1100 lines
52 KiB
Python
1100 lines
52 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""
|
|||
|
|
事件模拟器核心类
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import numpy as np
|
|||
|
|
import logging
|
|||
|
|
from core.encoder import BitFieldEncoder
|
|||
|
|
|
|||
|
|
# 设置日志
|
|||
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
class SampleSpaceGenerator:
|
|||
|
|
"""样本空间生成器(能量单位:keV)"""
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def generate_from_distribution(dist_config: dict, size: int) -> np.ndarray:
|
|||
|
|
"""根据配置生成样本(能量单位:keV)"""
|
|||
|
|
dist_type = dist_config['type']
|
|||
|
|
params = {k: v for k, v in dist_config.items() if k != 'type'}
|
|||
|
|
|
|||
|
|
if dist_type == 'normal':
|
|||
|
|
return np.random.normal(params.get('mean', 0), params.get('std', 1), size)
|
|||
|
|
elif dist_type == 'uniform':
|
|||
|
|
return np.random.uniform(params.get('low', 0), params.get('high', 1), size)
|
|||
|
|
elif dist_type == 'exponential':
|
|||
|
|
# 指数分布:scale是尺度参数,loc是位置参数(延迟/偏移)
|
|||
|
|
return np.random.exponential(params.get('scale', 1), size) + params.get('loc', 0)
|
|||
|
|
elif dist_type == 'gamma':
|
|||
|
|
return np.random.gamma(params.get('shape', 1), params.get('scale', 1), size)
|
|||
|
|
elif dist_type == 'poisson':
|
|||
|
|
return np.random.poisson(params.get('lam', 1), size)
|
|||
|
|
elif dist_type == 'lognormal':
|
|||
|
|
return np.random.lognormal(params.get('mean', 0), params.get('sigma', 1), size)
|
|||
|
|
elif dist_type == 'constant':
|
|||
|
|
return np.full(size, params.get('value', 1000.0))
|
|||
|
|
else:
|
|||
|
|
raise ValueError(f"不支持的分布类型: {dist_type}")
|
|||
|
|
|
|||
|
|
def generate_sample_space(self, detector_config: dict) -> np.ndarray:
|
|||
|
|
"""生成样本空间(能量单位:keV)"""
|
|||
|
|
size = detector_config['sample_space_size']
|
|||
|
|
|
|||
|
|
# 生成能量样本 (keV)
|
|||
|
|
energies = self.generate_from_distribution(detector_config['energy_distribution'], size)
|
|||
|
|
energies = np.abs(energies)
|
|||
|
|
|
|||
|
|
# 生成时间戳样本 (us)
|
|||
|
|
timestamps = self.generate_from_distribution(detector_config['timestamp_distribution'], size)
|
|||
|
|
timestamps = np.abs(timestamps)
|
|||
|
|
|
|||
|
|
# 组合成二维向量
|
|||
|
|
sample_space = np.column_stack((energies, timestamps))
|
|||
|
|
|
|||
|
|
logger.info(f"为{detector_config['name']}生成了{size}个样本 (能量单位: keV)")
|
|||
|
|
return sample_space
|
|||
|
|
|
|||
|
|
class DetectorSampler:
|
|||
|
|
"""探测器采样器"""
|
|||
|
|
|
|||
|
|
def __init__(self, sample_space: np.ndarray, allow_replacement: bool = True):
|
|||
|
|
self.sample_space = sample_space
|
|||
|
|
self.size = len(sample_space)
|
|||
|
|
self.allow_replacement = allow_replacement
|
|||
|
|
|
|||
|
|
def sample(self, num_samples: int) -> np.ndarray:
|
|||
|
|
"""从样本空间抽样"""
|
|||
|
|
if num_samples <= 0:
|
|||
|
|
return np.array([])
|
|||
|
|
|
|||
|
|
if self.allow_replacement:
|
|||
|
|
indices = np.random.choice(self.size, num_samples, replace=True)
|
|||
|
|
else:
|
|||
|
|
if num_samples > self.size:
|
|||
|
|
logger.warning(f"请求样本数({num_samples})超过样本空间大小({self.size})")
|
|||
|
|
num_samples = self.size
|
|||
|
|
indices = np.random.choice(self.size, num_samples, replace=False)
|
|||
|
|
|
|||
|
|
return self.sample_space[indices].copy()
|
|||
|
|
|
|||
|
|
class EventSimulatorV4:
|
|||
|
|
"""事件模拟器 V4(支持三种事件生成模式)"""
|
|||
|
|
|
|||
|
|
def __init__(self, config: dict, pre_generated_sample_spaces: dict = None):
|
|||
|
|
self.config = config
|
|||
|
|
self.sample_generator = SampleSpaceGenerator()
|
|||
|
|
self.encoder = BitFieldEncoder()
|
|||
|
|
|
|||
|
|
# 获取事件生成模式
|
|||
|
|
self.event_generation_mode = config['simulation'].get('event_generation_mode', 'random')
|
|||
|
|
logger.info(f"使用事件生成模式: {self.event_generation_mode}")
|
|||
|
|
|
|||
|
|
# 获取能量转换参数
|
|||
|
|
energy_conversion = config['simulation'].get('energy_conversion', {})
|
|||
|
|
self.energy_K = energy_conversion.get('K', 1.0)
|
|||
|
|
self.energy_B = energy_conversion.get('B', 0.0)
|
|||
|
|
logger.info(f"能量转换参数: K={self.energy_K}, B={self.energy_B} (mV = keV * K + B)")
|
|||
|
|
|
|||
|
|
# 初始化探测器配置(所有模式都需要)
|
|||
|
|
self.detectors = self._initialize_detectors()
|
|||
|
|
|
|||
|
|
# 对于随机模式,需要样本空间和采样器
|
|||
|
|
if self.event_generation_mode == 'random':
|
|||
|
|
# 使用预生成的样本空间(如果提供),否则生成新的
|
|||
|
|
if pre_generated_sample_spaces:
|
|||
|
|
logger.info("使用预生成的样本空间")
|
|||
|
|
self.sample_spaces = pre_generated_sample_spaces
|
|||
|
|
else:
|
|||
|
|
logger.info("生成新的样本空间")
|
|||
|
|
self.sample_spaces = self._generate_sample_spaces()
|
|||
|
|
|
|||
|
|
self.samplers = {}
|
|||
|
|
for name, sample_space in self.sample_spaces.items():
|
|||
|
|
allow_replacement = self.detectors[name]['allow_replacement']
|
|||
|
|
self.samplers[name] = DetectorSampler(sample_space, allow_replacement)
|
|||
|
|
else:
|
|||
|
|
# 固定模式和脉冲模式不需要样本空间
|
|||
|
|
self.sample_spaces = {}
|
|||
|
|
self.samplers = {}
|
|||
|
|
|
|||
|
|
# 加载固定事件配置
|
|||
|
|
self.fixed_events_config = config.get('fixed_events', {})
|
|||
|
|
|
|||
|
|
# 加载脉冲信号配置
|
|||
|
|
self.pulse_signals_config = config.get('pulse_signals', {})
|
|||
|
|
|
|||
|
|
def _initialize_detectors(self) -> dict:
|
|||
|
|
"""初始化探测器配置"""
|
|||
|
|
detectors = {}
|
|||
|
|
|
|||
|
|
for det_name, det_config in self.config['detectors'].items():
|
|||
|
|
detectors[det_name] = det_config
|
|||
|
|
|
|||
|
|
return detectors
|
|||
|
|
|
|||
|
|
def _generate_sample_spaces(self) -> dict:
|
|||
|
|
"""为所有探测器生成样本空间(仅随机模式使用)"""
|
|||
|
|
sample_spaces = {}
|
|||
|
|
|
|||
|
|
for name, detector_config in self.detectors.items():
|
|||
|
|
sample_spaces[name] = self.sample_generator.generate_sample_space(detector_config)
|
|||
|
|
|
|||
|
|
return sample_spaces
|
|||
|
|
|
|||
|
|
def generate_event_random(self, event_id: int) -> list:
|
|||
|
|
"""
|
|||
|
|
随机模式:生成一个随机事件
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
事件信号列表
|
|||
|
|
"""
|
|||
|
|
sampling_config = self.config['simulation']['sampling']
|
|||
|
|
require_signal = sampling_config.get('require_signal', True)
|
|||
|
|
|
|||
|
|
# 为每个探测器随机确定信号数量
|
|||
|
|
num_signals = {
|
|||
|
|
name: np.random.randint(
|
|||
|
|
sampling_config['min_signals_per_detector'],
|
|||
|
|
sampling_config['max_signals_per_detector'] + 1
|
|||
|
|
)
|
|||
|
|
for name in self.detectors.keys()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 从每个探测器抽样
|
|||
|
|
detector_signals = {}
|
|||
|
|
for name, sampler in self.samplers.items():
|
|||
|
|
samples = sampler.sample(num_signals[name])
|
|||
|
|
|
|||
|
|
if len(samples) > 0:
|
|||
|
|
sorted_indices = np.argsort(samples[:, 1])
|
|||
|
|
detector_signals[name] = samples[sorted_indices]
|
|||
|
|
else:
|
|||
|
|
detector_signals[name] = np.array([])
|
|||
|
|
|
|||
|
|
# 合并所有探测器信号并编码为64位整数
|
|||
|
|
encoded_events = self._merge_and_encode_signals(detector_signals, require_signal)
|
|||
|
|
|
|||
|
|
# 获取最大时间戳用于事件结束标记
|
|||
|
|
max_timestamp = 0
|
|||
|
|
if encoded_events:
|
|||
|
|
for encoded in encoded_events:
|
|||
|
|
decoded = self.encoder.decode(encoded)
|
|||
|
|
if decoded['timestamp'] > max_timestamp:
|
|||
|
|
max_timestamp = decoded['timestamp']
|
|||
|
|
|
|||
|
|
# 设置最后一个信号的事件结束标志位
|
|||
|
|
if encoded_events:
|
|||
|
|
encoded_events[-1] = self.encoder.set_event_end_flag(encoded_events[-1])
|
|||
|
|
else:
|
|||
|
|
# 如果没有信号,创建一个结束标记信号
|
|||
|
|
end_signal = self.encoder.encode(max_timestamp, [0, 0, 0], event_end=True)
|
|||
|
|
encoded_events.append(end_signal)
|
|||
|
|
|
|||
|
|
return encoded_events
|
|||
|
|
|
|||
|
|
def generate_event_fixed(self, event_id: int) -> list:
|
|||
|
|
"""
|
|||
|
|
固定模式:生成一个固定模式的事件
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
事件信号列表
|
|||
|
|
"""
|
|||
|
|
config = self.fixed_events_config
|
|||
|
|
|
|||
|
|
# 获取配置参数
|
|||
|
|
num_signals = config.get('num_signals_per_event', 5)
|
|||
|
|
energy_levels = config.get('energy_levels', {})
|
|||
|
|
timestamps = config.get('timestamps', [])
|
|||
|
|
repeat_pattern = config.get('repeat_pattern', True)
|
|||
|
|
|
|||
|
|
# 准备能量数据
|
|||
|
|
det1_energies = energy_levels.get('detector1', [1000] * num_signals)
|
|||
|
|
det2_energies = energy_levels.get('detector2', [800] * num_signals)
|
|||
|
|
det3_energies = energy_levels.get('detector3', [500] * num_signals)
|
|||
|
|
|
|||
|
|
# 准备时间戳
|
|||
|
|
if repeat_pattern and len(timestamps) > 0:
|
|||
|
|
# 重复时间戳模式
|
|||
|
|
timestamps_cycle = timestamps * ((num_signals // len(timestamps)) + 1)
|
|||
|
|
timestamps_used = timestamps_cycle[:num_signals]
|
|||
|
|
else:
|
|||
|
|
# 使用递增时间戳
|
|||
|
|
if len(timestamps) >= num_signals:
|
|||
|
|
timestamps_used = timestamps[:num_signals]
|
|||
|
|
else:
|
|||
|
|
base_timestamp = 10
|
|||
|
|
interval = 50
|
|||
|
|
timestamps_used = [base_timestamp + i * interval for i in range(num_signals)]
|
|||
|
|
|
|||
|
|
# 确保数组长度一致
|
|||
|
|
det1_energies = self._ensure_length(det1_energies, num_signals, 1000)
|
|||
|
|
det2_energies = self._ensure_length(det2_energies, num_signals, 800)
|
|||
|
|
det3_energies = self._ensure_length(det3_energies, num_signals, 500)
|
|||
|
|
|
|||
|
|
# 编码信号
|
|||
|
|
encoded_signals = []
|
|||
|
|
for i in range(num_signals):
|
|||
|
|
timestamp = timestamps_used[i]
|
|||
|
|
energies = [det1_energies[i], det2_energies[i], det3_energies[i]]
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
encoded = self.encoder.encode(timestamp, energies, event_end=False)
|
|||
|
|
encoded_signals.append(encoded)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"固定模式编码信号时出错: {e}")
|
|||
|
|
# 创建一个默认信号
|
|||
|
|
default_signal = self.encoder.encode(timestamp, [0, 0, 0], event_end=False)
|
|||
|
|
encoded_signals.append(default_signal)
|
|||
|
|
|
|||
|
|
# 设置最后一个信号的事件结束标志位
|
|||
|
|
if encoded_signals:
|
|||
|
|
encoded_signals[-1] = self.encoder.set_event_end_flag(encoded_signals[-1])
|
|||
|
|
|
|||
|
|
logger.info(f"固定模式生成事件 {event_id+1}: {num_signals}个信号")
|
|||
|
|
return encoded_signals
|
|||
|
|
|
|||
|
|
def generate_event_pulse(self, event_id: int) -> list:
|
|||
|
|
"""
|
|||
|
|
脉冲模式:生成一个脉冲信号事件
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
事件信号列表
|
|||
|
|
"""
|
|||
|
|
config = self.pulse_signals_config
|
|||
|
|
|
|||
|
|
# 获取配置参数
|
|||
|
|
pulse_count = config.get('pulse_count', 10)
|
|||
|
|
pulse_interval = config.get('pulse_interval', 100)
|
|||
|
|
pulse_width = config.get('pulse_width', 5)
|
|||
|
|
energy_levels = config.get('energy_levels', {})
|
|||
|
|
jitter = config.get('jitter', 10.0)
|
|||
|
|
energy_noise = config.get('energy_noise', 100.0)
|
|||
|
|
|
|||
|
|
# 获取各探测器基础能量
|
|||
|
|
det1_energy = energy_levels.get('detector1', 1500)
|
|||
|
|
det2_energy = energy_levels.get('detector2', 1200)
|
|||
|
|
det3_energy = energy_levels.get('detector3', 900)
|
|||
|
|
|
|||
|
|
# 生成脉冲信号
|
|||
|
|
encoded_signals = []
|
|||
|
|
|
|||
|
|
for pulse_idx in range(pulse_count):
|
|||
|
|
# 计算脉冲起始时间
|
|||
|
|
base_time = pulse_idx * pulse_interval
|
|||
|
|
|
|||
|
|
# 生成脉冲内的多个信号
|
|||
|
|
for sub_idx in range(pulse_width):
|
|||
|
|
# 添加时间抖动
|
|||
|
|
time_jitter = np.random.uniform(-jitter, jitter) if jitter > 0 else 0
|
|||
|
|
timestamp = base_time + sub_idx * (pulse_interval / pulse_width) + time_jitter
|
|||
|
|
timestamp = max(0, timestamp)
|
|||
|
|
|
|||
|
|
# 添加能量噪声
|
|||
|
|
det1_noisy = det1_energy + np.random.uniform(-energy_noise, energy_noise) if energy_noise > 0 else det1_energy
|
|||
|
|
det2_noisy = det2_energy + np.random.uniform(-energy_noise, energy_noise) if energy_noise > 0 else det2_energy
|
|||
|
|
det3_noisy = det3_energy + np.random.uniform(-energy_noise, energy_noise) if energy_noise > 0 else det3_energy
|
|||
|
|
|
|||
|
|
# 确保能量非负
|
|||
|
|
det1_noisy = max(0, det1_noisy)
|
|||
|
|
det2_noisy = max(0, det2_noisy)
|
|||
|
|
det3_noisy = max(0, det3_noisy)
|
|||
|
|
|
|||
|
|
energies = [det1_noisy, det2_noisy, det3_noisy]
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
encoded = self.encoder.encode(int(timestamp), energies, event_end=False)
|
|||
|
|
encoded_signals.append(encoded)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"脉冲模式编码信号时出错: {e}")
|
|||
|
|
# 创建一个默认信号
|
|||
|
|
default_signal = self.encoder.encode(int(timestamp), [0, 0, 0], event_end=False)
|
|||
|
|
encoded_signals.append(default_signal)
|
|||
|
|
|
|||
|
|
# 设置最后一个信号的事件结束标志位
|
|||
|
|
if encoded_signals:
|
|||
|
|
encoded_signals[-1] = self.encoder.set_event_end_flag(encoded_signals[-1])
|
|||
|
|
|
|||
|
|
logger.info(f"脉冲模式生成事件 {event_id+1}: {len(encoded_signals)}个信号")
|
|||
|
|
return encoded_signals
|
|||
|
|
|
|||
|
|
def _ensure_length(self, array: list, target_length: int, default_value: float) -> list:
|
|||
|
|
"""确保数组达到目标长度,不足时用默认值填充"""
|
|||
|
|
if len(array) >= target_length:
|
|||
|
|
return array[:target_length]
|
|||
|
|
else:
|
|||
|
|
return array + [default_value] * (target_length - len(array))
|
|||
|
|
|
|||
|
|
def _merge_and_encode_signals(self, detector_signals: dict, require_signal: bool = True) -> list:
|
|||
|
|
"""
|
|||
|
|
合并探测器信号并编码为64位整数
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
detector_signals: 各探测器的信号数组
|
|||
|
|
require_signal: 是否只输出有信号的探测器
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
编码后的64位整数列表
|
|||
|
|
"""
|
|||
|
|
time_signal_map = {}
|
|||
|
|
|
|||
|
|
for det_idx, det_name in enumerate(['detector1', 'detector2', 'detector3']):
|
|||
|
|
signals = detector_signals.get(det_name, np.array([]))
|
|||
|
|
|
|||
|
|
for signal in signals:
|
|||
|
|
if len(signal) == 2:
|
|||
|
|
energy_kev = signal[0]
|
|||
|
|
timestamp_us = signal[1]
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 能量转换: mV = keV * K + B,结果取整
|
|||
|
|
energy_mv = int(round(energy_kev * self.energy_K + self.energy_B))
|
|||
|
|
|
|||
|
|
# 确保能量非负
|
|||
|
|
if energy_mv < 0:
|
|||
|
|
energy_mv = 0
|
|||
|
|
|
|||
|
|
# 检查溢出
|
|||
|
|
if energy_mv > ((1 << 14) - 1):
|
|||
|
|
logger.warning(f"能量溢出,放弃信号: {energy_mv}mV (原始: {energy_kev}keV)")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
if timestamp_us > ((1 << 18) - 1):
|
|||
|
|
logger.warning(f"时间戳溢出,放弃信号: {timestamp_us}us")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 添加到时间映射
|
|||
|
|
timestamp_int = int(round(timestamp_us))
|
|||
|
|
if timestamp_int not in time_signal_map:
|
|||
|
|
time_signal_map[timestamp_int] = {
|
|||
|
|
'timestamp': timestamp_int,
|
|||
|
|
'energies': [0, 0, 0],
|
|||
|
|
'has_signal': [False, False, False]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
time_signal_map[timestamp_int]['energies'][det_idx] = energy_mv
|
|||
|
|
time_signal_map[timestamp_int]['has_signal'][det_idx] = True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"编码信号时出错,放弃: {e}")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
sorted_timestamps = sorted(time_signal_map.keys())
|
|||
|
|
|
|||
|
|
encoded_events = []
|
|||
|
|
|
|||
|
|
for timestamp in sorted_timestamps:
|
|||
|
|
data = time_signal_map[timestamp]
|
|||
|
|
energies = data['energies']
|
|||
|
|
has_signal = data['has_signal']
|
|||
|
|
|
|||
|
|
if require_signal and not any(has_signal):
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
encoded = self.encoder.encode(timestamp, energies, event_end=False)
|
|||
|
|
encoded_events.append(encoded)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"编码时间点{timestamp}信号时出错,放弃: {e}")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
return encoded_events
|
|||
|
|
|
|||
|
|
def generate_event(self, event_id: int) -> list:
|
|||
|
|
"""
|
|||
|
|
根据当前模式生成一个事件
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
事件信号列表
|
|||
|
|
"""
|
|||
|
|
if self.event_generation_mode == 'random':
|
|||
|
|
return self.generate_event_random(event_id)
|
|||
|
|
elif self.event_generation_mode == 'fixed':
|
|||
|
|
return self.generate_event_fixed(event_id)
|
|||
|
|
elif self.event_generation_mode == 'pulse':
|
|||
|
|
return self.generate_event_pulse(event_id)
|
|||
|
|
else:
|
|||
|
|
logger.warning(f"未知的事件生成模式: {self.event_generation_mode},使用随机模式")
|
|||
|
|
return self.generate_event_random(event_id)
|
|||
|
|
|
|||
|
|
def simulate_events(self, num_events: int = None) -> list:
|
|||
|
|
"""
|
|||
|
|
模拟多个事件,根据配置的模式生成
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
num_events: 事件数量(覆盖配置)
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
所有事件的列表
|
|||
|
|
"""
|
|||
|
|
if num_events is None:
|
|||
|
|
num_events = self.config['simulation']['num_events']
|
|||
|
|
|
|||
|
|
all_events = []
|
|||
|
|
|
|||
|
|
print(f"开始模拟事件...")
|
|||
|
|
print(f" 模式: {self.event_generation_mode}")
|
|||
|
|
print(f" 数量: {num_events}个事件")
|
|||
|
|
|
|||
|
|
for i in range(num_events):
|
|||
|
|
if self.event_generation_mode == 'random':
|
|||
|
|
event = self.generate_event_random(i)
|
|||
|
|
valid_signals = sum(1 for sig in event if sig != 0)
|
|||
|
|
logger.info(f"已生成随机事件 {i + 1}/{num_events} - 有效信号: {valid_signals}")
|
|||
|
|
elif self.event_generation_mode == 'fixed':
|
|||
|
|
event = self.generate_event_fixed(i)
|
|||
|
|
logger.info(f"已生成固定事件 {i + 1}/{num_events} - 信号数: {len(event)}")
|
|||
|
|
elif self.event_generation_mode == 'pulse':
|
|||
|
|
event = self.generate_event_pulse(i)
|
|||
|
|
logger.info(f"已生成脉冲事件 {i + 1}/{num_events} - 信号数: {len(event)}")
|
|||
|
|
else:
|
|||
|
|
event = self.generate_event_random(i)
|
|||
|
|
valid_signals = sum(1 for sig in event if sig != 0)
|
|||
|
|
logger.info(f"已生成事件 {i + 1}/{num_events} - 有效信号: {valid_signals}")
|
|||
|
|
|
|||
|
|
all_events.append(event)
|
|||
|
|
|
|||
|
|
# 汇总统计信息
|
|||
|
|
total_signals = sum(len(event) for event in all_events)
|
|||
|
|
total_valid = sum(sum(1 for sig in event if sig != 0) for event in all_events)
|
|||
|
|
|
|||
|
|
print(f"\n模拟完成!")
|
|||
|
|
print(f" 总事件数: {len(all_events)}")
|
|||
|
|
print(f" 总信号数: {total_signals}")
|
|||
|
|
print(f" 有效信号数: {total_valid}")
|
|||
|
|
print(f" 平均每事件信号数: {total_signals/len(all_events):.1f}")
|
|||
|
|
|
|||
|
|
return all_events
|
|||
|
|
|
|||
|
|
class TimeSeriesGenerator:
|
|||
|
|
"""时间序列生成器 - 根据工作模式和同步脉冲配置生成时间序列"""
|
|||
|
|
|
|||
|
|
def __init__(self, work_mode: str, sync_pulses: list, sample_spaces: dict,
|
|||
|
|
samplers: dict, detectors: dict = None, energy_K: float = 1.0, energy_B: float = 0.0,
|
|||
|
|
sync_pulse_signals: dict = None):
|
|||
|
|
"""
|
|||
|
|
初始化时间序列生成器
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
work_mode: 工作模式 ("CO", "Sigma", "Combo")
|
|||
|
|
sync_pulses: 同步脉冲配置列表,每个元素为 {"count": int, "period": int}
|
|||
|
|
sample_spaces: 各探测器的样本空间
|
|||
|
|
samplers: 各探测器的采样器
|
|||
|
|
detectors: 各探测器的配置(包含每探测器信号数)
|
|||
|
|
energy_K: 能量转换系数K
|
|||
|
|
energy_B: 能量转换系数B
|
|||
|
|
sync_pulse_signals: 每个同步脉冲的探测器信号数配置
|
|||
|
|
格式: {"pulse1": {"detector1": {"min": 1, "max": 10}, ...}, ...}
|
|||
|
|
"""
|
|||
|
|
self.work_mode = work_mode
|
|||
|
|
self.sync_pulses = sync_pulses
|
|||
|
|
self.sample_spaces = sample_spaces
|
|||
|
|
self.samplers = samplers
|
|||
|
|
self.detectors = detectors or {}
|
|||
|
|
self.energy_K = energy_K
|
|||
|
|
self.energy_B = energy_B
|
|||
|
|
self.sync_pulse_signals = sync_pulse_signals or {}
|
|||
|
|
self.encoder = BitFieldEncoder()
|
|||
|
|
|
|||
|
|
logger.info(f"时间序列生成器初始化 - 工作模式: {work_mode}")
|
|||
|
|
for i, pulse in enumerate(sync_pulses):
|
|||
|
|
logger.info(f" 同步脉冲{i+1}: 个数={pulse['count']}, 周期={pulse['period']} (10ns)")
|
|||
|
|
|
|||
|
|
def generate_time_series(self) -> dict:
|
|||
|
|
"""
|
|||
|
|
生成时间序列
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
包含各探测器时间序列的字典
|
|||
|
|
{
|
|||
|
|
"detector1": [(timestamp, energy), ...],
|
|||
|
|
"detector2": [(timestamp, energy), ...],
|
|||
|
|
"detector3": [(timestamp, energy), ...],
|
|||
|
|
"total_duration": 总时长(10ns)
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
time_series = {
|
|||
|
|
"detector1": [],
|
|||
|
|
"detector2": [],
|
|||
|
|
"detector3": [],
|
|||
|
|
"total_duration": 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if self.work_mode == "CO":
|
|||
|
|
time_series = self._generate_co_mode()
|
|||
|
|
elif self.work_mode == "Sigma":
|
|||
|
|
time_series = self._generate_sigma_mode()
|
|||
|
|
elif self.work_mode == "Combo":
|
|||
|
|
time_series = self._generate_combo_mode()
|
|||
|
|
else:
|
|||
|
|
logger.warning(f"未知的工作模式: {self.work_mode},使用CO模式")
|
|||
|
|
time_series = self._generate_co_mode()
|
|||
|
|
|
|||
|
|
total_signals = sum(len(time_series[det]) for det in ["detector1", "detector2", "detector3"])
|
|||
|
|
logger.info(f"时间序列生成完成 - 总时长: {time_series['total_duration']} (10ns), 总信号数: {total_signals}")
|
|||
|
|
|
|||
|
|
return time_series
|
|||
|
|
|
|||
|
|
def _generate_co_mode(self) -> dict:
|
|||
|
|
"""生成CO模式时间序列"""
|
|||
|
|
time_series = {
|
|||
|
|
"detector1": [],
|
|||
|
|
"detector2": [],
|
|||
|
|
"detector3": [],
|
|||
|
|
"total_duration": 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pulse1_count = self.sync_pulses[0]["count"]
|
|||
|
|
pulse1_period = self.sync_pulses[0]["period"]
|
|||
|
|
pulse2_count = self.sync_pulses[1]["count"]
|
|||
|
|
pulse2_period = self.sync_pulses[1]["period"]
|
|||
|
|
pulse3_count = self.sync_pulses[2]["count"]
|
|||
|
|
pulse3_period = self.sync_pulses[2]["period"]
|
|||
|
|
pulse4_count = self.sync_pulses[3]["count"]
|
|||
|
|
pulse4_period = self.sync_pulses[3]["period"]
|
|||
|
|
|
|||
|
|
# 计算总时长:串行计算所有脉冲序列的总时间
|
|||
|
|
total_duration = (
|
|||
|
|
pulse1_count * pulse1_period +
|
|||
|
|
pulse2_count * pulse2_period +
|
|||
|
|
pulse3_count * pulse3_period +
|
|||
|
|
pulse4_count * pulse4_period
|
|||
|
|
)
|
|||
|
|
time_series["total_duration"] = total_duration
|
|||
|
|
|
|||
|
|
# 获取同步脉冲信号数配置
|
|||
|
|
def get_signal_count(pulse_key, det_name, default_min=1, default_max=10):
|
|||
|
|
"""获取指定同步脉冲和探测器的信号数配置"""
|
|||
|
|
if pulse_key in self.sync_pulse_signals:
|
|||
|
|
if det_name in self.sync_pulse_signals[pulse_key]:
|
|||
|
|
config = self.sync_pulse_signals[pulse_key][det_name]
|
|||
|
|
min_val = config.get("min", default_min)
|
|||
|
|
max_val = config.get("max", default_max)
|
|||
|
|
return np.random.randint(min_val, max_val + 1)
|
|||
|
|
return np.random.randint(default_min, default_max + 1)
|
|||
|
|
|
|||
|
|
# 对每个同步脉冲触发采样
|
|||
|
|
# 同步脉冲1
|
|||
|
|
for pulse_idx in range(pulse1_count):
|
|||
|
|
# 计算脉冲时间位置(10ns单位)
|
|||
|
|
pulse_start_time = pulse_idx * pulse1_period
|
|||
|
|
# 同步脉冲1的时间窗口:0-160微秒
|
|||
|
|
max_time_us = 160
|
|||
|
|
|
|||
|
|
# 用于记录当前同步脉冲内每个探测器已使用的时间戳(单个探测器内去重)
|
|||
|
|
det_timestamps = {
|
|||
|
|
"detector1": set(),
|
|||
|
|
"detector2": set(),
|
|||
|
|
"detector3": set()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for det_name in ["detector1", "detector2", "detector3"]:
|
|||
|
|
if det_name in self.samplers:
|
|||
|
|
# 在最小和最大信号数之间随机抽取(使用同步脉冲1的配置)
|
|||
|
|
num_signals = get_signal_count("pulse1", det_name)
|
|||
|
|
sampler = self.samplers[det_name]
|
|||
|
|
sample_space = self.sample_spaces[det_name]
|
|||
|
|
|
|||
|
|
if len(sample_space) > 0:
|
|||
|
|
# 循环抽样,确保在同一个同步脉冲内,同一个探测器的时间戳不重复
|
|||
|
|
signals_collected = 0
|
|||
|
|
max_attempts = num_signals * 10 # 最多尝试次数
|
|||
|
|
attempts = 0
|
|||
|
|
|
|||
|
|
while signals_collected < num_signals and attempts < max_attempts:
|
|||
|
|
# 从样本空间中抽取一个样本
|
|||
|
|
sample = sampler.sample(1)[0]
|
|||
|
|
energy_kev = sample[0]
|
|||
|
|
sample_time_us = sample[1]
|
|||
|
|
|
|||
|
|
# 检查时间戳是否在同步脉冲时间窗口内
|
|||
|
|
if sample_time_us <= max_time_us:
|
|||
|
|
# 转换为10ns单位,确保为整数
|
|||
|
|
sample_time_10ns = int(sample_time_us * 100) # us -> 10ns
|
|||
|
|
timestamp_10ns = pulse_start_time + sample_time_10ns
|
|||
|
|
|
|||
|
|
# 检查该时间戳是否已经被当前探测器在当前同步脉冲内使用(单个探测器内去重)
|
|||
|
|
if timestamp_10ns not in det_timestamps[det_name]:
|
|||
|
|
# 能量转换并确保非负
|
|||
|
|
energy_mv = int(energy_kev * self.energy_K + self.energy_B)
|
|||
|
|
energy_mv = max(0, energy_mv)
|
|||
|
|
time_series[det_name].append((timestamp_10ns, energy_mv))
|
|||
|
|
det_timestamps[det_name].add(timestamp_10ns)
|
|||
|
|
signals_collected += 1
|
|||
|
|
else:
|
|||
|
|
logger.debug(f"重复时间戳: 探测器={det_name}, 时间戳={timestamp_10ns}, 能量={energy_kev}keV")
|
|||
|
|
|
|||
|
|
attempts += 1
|
|||
|
|
|
|||
|
|
# 同步脉冲2
|
|||
|
|
for pulse_idx in range(pulse2_count):
|
|||
|
|
# 计算脉冲时间位置(10ns单位)
|
|||
|
|
# 同步脉冲2的起始时间是同步脉冲1的结束时间
|
|||
|
|
pulse_start_time = pulse1_count * pulse1_period + pulse_idx * pulse2_period
|
|||
|
|
# 同步脉冲2的时间窗口:0-1000微秒
|
|||
|
|
max_time_us = 1000
|
|||
|
|
|
|||
|
|
# 用于记录当前同步脉冲内每个探测器已使用的时间戳(单个探测器内去重)
|
|||
|
|
det_timestamps = {
|
|||
|
|
"detector1": set(),
|
|||
|
|
"detector2": set(),
|
|||
|
|
"detector3": set()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for det_name in ["detector1", "detector2", "detector3"]:
|
|||
|
|
if det_name in self.samplers:
|
|||
|
|
# 在最小和最大信号数之间随机抽取(使用同步脉冲2的配置)
|
|||
|
|
num_signals = get_signal_count("pulse2", det_name)
|
|||
|
|
sampler = self.samplers[det_name]
|
|||
|
|
sample_space = self.sample_spaces[det_name]
|
|||
|
|
|
|||
|
|
if len(sample_space) > 0:
|
|||
|
|
# 循环抽样,确保在同一个同步脉冲内,同一个探测器的时间戳不重复
|
|||
|
|
signals_collected = 0
|
|||
|
|
max_attempts = num_signals * 10 # 最多尝试次数
|
|||
|
|
attempts = 0
|
|||
|
|
|
|||
|
|
while signals_collected < num_signals and attempts < max_attempts:
|
|||
|
|
# 从样本空间中抽取一个样本
|
|||
|
|
sample = sampler.sample(1)[0]
|
|||
|
|
energy_kev = sample[0]
|
|||
|
|
sample_time_us = sample[1]
|
|||
|
|
|
|||
|
|
# 检查时间戳是否在同步脉冲时间窗口内
|
|||
|
|
if sample_time_us <= max_time_us:
|
|||
|
|
# 转换为10ns单位,确保为整数
|
|||
|
|
sample_time_10ns = int(sample_time_us * 100) # us -> 10ns
|
|||
|
|
timestamp_10ns = pulse_start_time + sample_time_10ns
|
|||
|
|
|
|||
|
|
# 检查该时间戳是否已经被当前探测器在当前同步脉冲内使用(单个探测器内去重)
|
|||
|
|
if timestamp_10ns not in det_timestamps[det_name]:
|
|||
|
|
# 能量转换并确保非负
|
|||
|
|
energy_mv = int(energy_kev * self.energy_K + self.energy_B)
|
|||
|
|
energy_mv = max(0, energy_mv)
|
|||
|
|
time_series[det_name].append((timestamp_10ns, energy_mv))
|
|||
|
|
det_timestamps[det_name].add(timestamp_10ns)
|
|||
|
|
signals_collected += 1
|
|||
|
|
|
|||
|
|
attempts += 1
|
|||
|
|
|
|||
|
|
# 同步脉冲3
|
|||
|
|
for pulse_idx in range(pulse3_count):
|
|||
|
|
# 计算脉冲时间位置(10ns单位)
|
|||
|
|
# 同步脉冲3的起始时间是同步脉冲2的结束时间
|
|||
|
|
pulse_start_time = pulse1_count * pulse1_period + pulse2_count * pulse2_period + pulse_idx * pulse3_period
|
|||
|
|
# 同步脉冲3的时间窗口:0-4000微秒
|
|||
|
|
max_time_us = 4000
|
|||
|
|
|
|||
|
|
# 用于记录当前同步脉冲内每个探测器已使用的时间戳(单个探测器内去重)
|
|||
|
|
det_timestamps = {
|
|||
|
|
"detector1": set(),
|
|||
|
|
"detector2": set(),
|
|||
|
|
"detector3": set()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for det_name in ["detector1", "detector2", "detector3"]:
|
|||
|
|
if det_name in self.samplers:
|
|||
|
|
# 在最小和最大信号数之间随机抽取(使用同步脉冲3的配置)
|
|||
|
|
num_signals = get_signal_count("pulse3", det_name)
|
|||
|
|
sampler = self.samplers[det_name]
|
|||
|
|
sample_space = self.sample_spaces[det_name]
|
|||
|
|
|
|||
|
|
if len(sample_space) > 0:
|
|||
|
|
# 循环抽样,确保在同一个同步脉冲内,同一个探测器的时间戳不重复
|
|||
|
|
signals_collected = 0
|
|||
|
|
max_attempts = num_signals * 10 # 最多尝试次数
|
|||
|
|
attempts = 0
|
|||
|
|
|
|||
|
|
while signals_collected < num_signals and attempts < max_attempts:
|
|||
|
|
# 从样本空间中抽取一个样本
|
|||
|
|
sample = sampler.sample(1)[0]
|
|||
|
|
energy_kev = sample[0]
|
|||
|
|
sample_time_us = sample[1]
|
|||
|
|
|
|||
|
|
# 检查时间戳是否在同步脉冲时间窗口内
|
|||
|
|
if sample_time_us <= max_time_us:
|
|||
|
|
# 转换为10ns单位,确保为整数
|
|||
|
|
sample_time_10ns = int(sample_time_us * 100) # us -> 10ns
|
|||
|
|
timestamp_10ns = pulse_start_time + sample_time_10ns
|
|||
|
|
|
|||
|
|
# 检查该时间戳是否已经被当前探测器在当前同步脉冲内使用(单个探测器内去重)
|
|||
|
|
if timestamp_10ns not in det_timestamps[det_name]:
|
|||
|
|
# 能量转换并确保非负
|
|||
|
|
energy_mv = int(energy_kev * self.energy_K + self.energy_B)
|
|||
|
|
energy_mv = max(0, energy_mv)
|
|||
|
|
time_series[det_name].append((timestamp_10ns, energy_mv))
|
|||
|
|
det_timestamps[det_name].add(timestamp_10ns)
|
|||
|
|
signals_collected += 1
|
|||
|
|
|
|||
|
|
attempts += 1
|
|||
|
|
|
|||
|
|
# 对每个探测器的信号按时间戳排序
|
|||
|
|
for det_name in ["detector1", "detector2", "detector3"]:
|
|||
|
|
time_series[det_name].sort(key=lambda x: x[0])
|
|||
|
|
|
|||
|
|
return time_series
|
|||
|
|
|
|||
|
|
def _generate_sigma_mode(self) -> dict:
|
|||
|
|
"""生成Sigma模式时间序列"""
|
|||
|
|
time_series = {
|
|||
|
|
"detector1": [],
|
|||
|
|
"detector2": [],
|
|||
|
|
"detector3": [],
|
|||
|
|
"total_duration": 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pulse1_count = self.sync_pulses[0]["count"]
|
|||
|
|
pulse1_period = self.sync_pulses[0]["period"]
|
|||
|
|
|
|||
|
|
total_duration = pulse1_count * pulse1_period
|
|||
|
|
time_series["total_duration"] = total_duration
|
|||
|
|
|
|||
|
|
# 对每个同步脉冲触发采样
|
|||
|
|
for pulse_idx in range(pulse1_count):
|
|||
|
|
# 计算脉冲时间位置(10ns单位)
|
|||
|
|
pulse_start_time = pulse_idx * pulse1_period
|
|||
|
|
|
|||
|
|
# 用于记录当前同步脉冲内每个探测器已使用的时间戳(单个探测器内去重)
|
|||
|
|
det_timestamps = {
|
|||
|
|
"detector1": set(),
|
|||
|
|
"detector2": set(),
|
|||
|
|
"detector3": set()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for det_name in ["detector1", "detector2", "detector3"]:
|
|||
|
|
if det_name in self.samplers:
|
|||
|
|
sampler = self.samplers[det_name]
|
|||
|
|
sample_space = self.sample_spaces[det_name]
|
|||
|
|
|
|||
|
|
if len(sample_space) > 0:
|
|||
|
|
# 循环抽样,确保在同一个同步脉冲内,同一个探测器的时间戳不重复
|
|||
|
|
num_samples = np.random.randint(1, 4)
|
|||
|
|
signals_collected = 0
|
|||
|
|
max_attempts = num_samples * 10 # 最多尝试次数
|
|||
|
|
attempts = 0
|
|||
|
|
|
|||
|
|
while signals_collected < num_samples and attempts < max_attempts:
|
|||
|
|
# 从样本空间中抽取一个样本
|
|||
|
|
sample = sampler.sample(1)[0]
|
|||
|
|
energy_kev = sample[0]
|
|||
|
|
sample_time_us = sample[1]
|
|||
|
|
|
|||
|
|
# 计算最终时间戳:脉冲时间 + 样本时间(使用10ns单位整数运算避免浮点精度问题)
|
|||
|
|
sample_time_10ns = int(sample_time_us * 100) # us -> 10ns
|
|||
|
|
timestamp_10ns = pulse_start_time + sample_time_10ns
|
|||
|
|
|
|||
|
|
# 检查该时间戳是否已经被当前探测器在当前同步脉冲内使用(单个探测器内去重)
|
|||
|
|
if timestamp_10ns not in det_timestamps[det_name]:
|
|||
|
|
# 能量转换并确保非负
|
|||
|
|
energy_mv = int(energy_kev * self.energy_K + self.energy_B)
|
|||
|
|
energy_mv = max(0, energy_mv)
|
|||
|
|
time_series[det_name].append((timestamp_10ns, energy_mv))
|
|||
|
|
det_timestamps[det_name].add(timestamp_10ns)
|
|||
|
|
signals_collected += 1
|
|||
|
|
else:
|
|||
|
|
logger.debug(f"重复时间戳: 探测器={det_name}, 脉冲={pulse_idx}, 时间戳={timestamp_10ns}, 样本时间={sample_time_us}us")
|
|||
|
|
|
|||
|
|
attempts += 1
|
|||
|
|
|
|||
|
|
return time_series
|
|||
|
|
|
|||
|
|
def _generate_combo_mode(self) -> dict:
|
|||
|
|
"""生成Combo模式时间序列"""
|
|||
|
|
time_series = {
|
|||
|
|
"detector1": [],
|
|||
|
|
"detector2": [],
|
|||
|
|
"detector3": [],
|
|||
|
|
"total_duration": 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pulse1_count = self.sync_pulses[0]["count"]
|
|||
|
|
pulse1_period = self.sync_pulses[0]["period"]
|
|||
|
|
pulse2_count = self.sync_pulses[1]["count"]
|
|||
|
|
pulse2_period = self.sync_pulses[1]["period"]
|
|||
|
|
pulse3_count = self.sync_pulses[2]["count"]
|
|||
|
|
pulse3_period = self.sync_pulses[2]["period"]
|
|||
|
|
|
|||
|
|
# 计算总时长:取最长的脉冲序列结束时间
|
|||
|
|
total_duration = max(
|
|||
|
|
pulse1_count * pulse1_period,
|
|||
|
|
pulse2_count * pulse2_period,
|
|||
|
|
pulse3_count * pulse3_period
|
|||
|
|
)
|
|||
|
|
time_series["total_duration"] = total_duration
|
|||
|
|
|
|||
|
|
# 对每个同步脉冲触发采样
|
|||
|
|
# 同步脉冲1
|
|||
|
|
for pulse_idx in range(pulse1_count):
|
|||
|
|
# 计算脉冲时间位置(10ns单位)
|
|||
|
|
pulse_start_time = pulse_idx * pulse1_period
|
|||
|
|
|
|||
|
|
# 用于记录当前同步脉冲内每个探测器已使用的时间戳(单个探测器内去重)
|
|||
|
|
det_timestamps = {
|
|||
|
|
"detector1": set(),
|
|||
|
|
"detector2": set(),
|
|||
|
|
"detector3": set()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for det_name in ["detector1", "detector2", "detector3"]:
|
|||
|
|
if det_name in self.samplers:
|
|||
|
|
sampler = self.samplers[det_name]
|
|||
|
|
sample_space = self.sample_spaces[det_name]
|
|||
|
|
|
|||
|
|
if len(sample_space) > 0:
|
|||
|
|
# 循环抽样,确保在同一个同步脉冲内,同一个探测器的时间戳不重复
|
|||
|
|
num_samples = np.random.randint(1, 4)
|
|||
|
|
signals_collected = 0
|
|||
|
|
max_attempts = num_samples * 10 # 最多尝试次数
|
|||
|
|
attempts = 0
|
|||
|
|
|
|||
|
|
while signals_collected < num_samples and attempts < max_attempts:
|
|||
|
|
# 从样本空间中抽取一个样本
|
|||
|
|
sample = sampler.sample(1)[0]
|
|||
|
|
energy_kev = sample[0]
|
|||
|
|
sample_time_us = sample[1]
|
|||
|
|
|
|||
|
|
# 计算最终时间戳:脉冲时间 + 样本时间(使用10ns单位整数运算避免浮点精度问题)
|
|||
|
|
sample_time_10ns = int(sample_time_us * 100) # us -> 10ns
|
|||
|
|
timestamp_10ns = pulse_start_time + sample_time_10ns
|
|||
|
|
|
|||
|
|
# 检查该时间戳是否已经被当前探测器在当前同步脉冲内使用(单个探测器内去重)
|
|||
|
|
if timestamp_10ns not in det_timestamps[det_name]:
|
|||
|
|
# 能量转换并确保非负
|
|||
|
|
energy_mv = int(energy_kev * self.energy_K + self.energy_B)
|
|||
|
|
energy_mv = max(0, energy_mv)
|
|||
|
|
time_series[det_name].append((timestamp_10ns, energy_mv))
|
|||
|
|
det_timestamps[det_name].add(timestamp_10ns)
|
|||
|
|
signals_collected += 1
|
|||
|
|
|
|||
|
|
attempts += 1
|
|||
|
|
|
|||
|
|
# 同步脉冲3
|
|||
|
|
for pulse_idx in range(pulse3_count):
|
|||
|
|
# 计算脉冲时间位置(10ns单位)
|
|||
|
|
pulse_start_time = pulse_idx * pulse3_period
|
|||
|
|
# 转换为微秒单位
|
|||
|
|
pulse_start_time_us = pulse_start_time / 100 # 10ns = 0.01us
|
|||
|
|
|
|||
|
|
# 用于记录当前同步脉冲内每个探测器已使用的时间戳(单个探测器内去重)
|
|||
|
|
det_timestamps = {
|
|||
|
|
"detector1": set(),
|
|||
|
|
"detector2": set(),
|
|||
|
|
"detector3": set()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for det_name in ["detector1", "detector2", "detector3"]:
|
|||
|
|
if det_name in self.samplers:
|
|||
|
|
sampler = self.samplers[det_name]
|
|||
|
|
sample_space = self.sample_spaces[det_name]
|
|||
|
|
|
|||
|
|
if len(sample_space) > 0:
|
|||
|
|
# 循环抽样,确保在同一个同步脉冲内,同一个探测器的时间戳不重复
|
|||
|
|
num_samples = np.random.randint(1, 4)
|
|||
|
|
signals_collected = 0
|
|||
|
|
max_attempts = num_samples * 10 # 最多尝试次数
|
|||
|
|
attempts = 0
|
|||
|
|
|
|||
|
|
while signals_collected < num_samples and attempts < max_attempts:
|
|||
|
|
# 从样本空间中抽取一个样本
|
|||
|
|
sample = sampler.sample(1)[0]
|
|||
|
|
energy_kev = sample[0]
|
|||
|
|
sample_time_us = sample[1]
|
|||
|
|
|
|||
|
|
# 计算最终时间戳:脉冲时间 + 样本时间(使用10ns单位整数运算避免浮点精度问题)
|
|||
|
|
sample_time_10ns = int(sample_time_us * 100) # us -> 10ns
|
|||
|
|
timestamp_10ns = pulse_start_time + sample_time_10ns
|
|||
|
|
|
|||
|
|
# 检查该时间戳是否已经被当前探测器在当前同步脉冲内使用(单个探测器内去重)
|
|||
|
|
if timestamp_10ns not in det_timestamps[det_name]:
|
|||
|
|
# 能量转换并确保非负
|
|||
|
|
energy_mv = int(energy_kev * self.energy_K + self.energy_B)
|
|||
|
|
energy_mv = max(0, energy_mv)
|
|||
|
|
time_series[det_name].append((timestamp_10ns, energy_mv))
|
|||
|
|
det_timestamps[det_name].add(timestamp_10ns)
|
|||
|
|
signals_collected += 1
|
|||
|
|
|
|||
|
|
attempts += 1
|
|||
|
|
|
|||
|
|
return time_series
|
|||
|
|
|
|||
|
|
def encode_time_series(self, time_series: dict) -> list:
|
|||
|
|
"""
|
|||
|
|
将时间序列编码为64位整数列表
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
time_series: 时间序列字典
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
编码后的64位整数列表
|
|||
|
|
"""
|
|||
|
|
# 计算同步脉冲时间点
|
|||
|
|
sync_pulse_times = []
|
|||
|
|
current_time = 0
|
|||
|
|
for pulse_config in self.sync_pulses:
|
|||
|
|
count = pulse_config["count"]
|
|||
|
|
period = pulse_config["period"]
|
|||
|
|
for i in range(count):
|
|||
|
|
sync_pulse_times.append(current_time)
|
|||
|
|
current_time += period
|
|||
|
|
|
|||
|
|
logger.info(f"同步脉冲时间点: {len(sync_pulse_times)}个")
|
|||
|
|
|
|||
|
|
# 为每个同步脉冲创建一个事件
|
|||
|
|
event_signals = []
|
|||
|
|
|
|||
|
|
# 标记已分配的信号
|
|||
|
|
assigned_timestamps = set()
|
|||
|
|
|
|||
|
|
for pulse_idx, pulse_time in enumerate(sync_pulse_times):
|
|||
|
|
# 找到该脉冲时间窗口内的信号
|
|||
|
|
# 时间范围:从当前脉冲时间到下一个脉冲时间
|
|||
|
|
next_pulse_time = sync_pulse_times[pulse_idx + 1] if pulse_idx + 1 < len(sync_pulse_times) else None
|
|||
|
|
|
|||
|
|
# 按相对时间戳分组信号(合并相同时间戳的信号)
|
|||
|
|
relative_time_signal_map = {}
|
|||
|
|
|
|||
|
|
# 收集当前脉冲时间窗口内的信号
|
|||
|
|
for det_idx, det_name in enumerate(['detector1', 'detector2', 'detector3']):
|
|||
|
|
signals = time_series.get(det_name, [])
|
|||
|
|
for signal in signals:
|
|||
|
|
if len(signal) == 2:
|
|||
|
|
timestamp = signal[0]
|
|||
|
|
energy = signal[1]
|
|||
|
|
|
|||
|
|
# 检查信号是否在当前脉冲时间窗口内且未被分配
|
|||
|
|
if timestamp >= pulse_time and timestamp not in assigned_timestamps:
|
|||
|
|
if next_pulse_time is None or timestamp < next_pulse_time:
|
|||
|
|
# 计算相对时间戳(相对于同步脉冲时间)
|
|||
|
|
relative_timestamp = timestamp - pulse_time
|
|||
|
|
# 转换为微秒单位(编码器期望的单位)
|
|||
|
|
relative_timestamp_us = relative_timestamp // 100 # 10ns -> us,使用整数除法避免浮点精度问题
|
|||
|
|
|
|||
|
|
# 按相对时间戳分组,确保同一事件内同一探测器的时间戳不重复
|
|||
|
|
if relative_timestamp_us not in relative_time_signal_map:
|
|||
|
|
relative_time_signal_map[relative_timestamp_us] = [0, 0, 0]
|
|||
|
|
|
|||
|
|
# 如果该探测器在这个相对时间戳上已经有信号,使用第一个信号(去重)
|
|||
|
|
if relative_time_signal_map[relative_timestamp_us][det_idx] == 0:
|
|||
|
|
relative_time_signal_map[relative_timestamp_us][det_idx] = energy
|
|||
|
|
else:
|
|||
|
|
logger.debug(f"重复相对时间戳: 事件={pulse_idx}, 时间戳={relative_timestamp_us}us, 探测器={det_name}, 旧能量={relative_time_signal_map[relative_timestamp_us][det_idx]}, 新能量={energy}")
|
|||
|
|
|
|||
|
|
# 统计合并后的信号
|
|||
|
|
merged_count = 0
|
|||
|
|
for timestamp, energies in relative_time_signal_map.items():
|
|||
|
|
active_dets = sum(1 for e in energies if e > 0)
|
|||
|
|
if active_dets > 1:
|
|||
|
|
merged_count += 1
|
|||
|
|
logger.debug(f"事件 {pulse_idx} - 时间戳 {timestamp}us 合并了 {active_dets} 个探测器的信号: {energies}")
|
|||
|
|
|
|||
|
|
# 按相对时间戳排序
|
|||
|
|
sorted_relative_timestamps = sorted(relative_time_signal_map.keys())
|
|||
|
|
|
|||
|
|
pulse_signals = []
|
|||
|
|
|
|||
|
|
for relative_timestamp_us in sorted_relative_timestamps:
|
|||
|
|
energies = relative_time_signal_map[relative_timestamp_us]
|
|||
|
|
try:
|
|||
|
|
encoded = self.encoder.encode(relative_timestamp_us, energies, event_end=False)
|
|||
|
|
pulse_signals.append(encoded)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"编码信号时出错: {e}")
|
|||
|
|
default_signal = self.encoder.encode(relative_timestamp_us, [0, 0, 0], event_end=False)
|
|||
|
|
pulse_signals.append(default_signal)
|
|||
|
|
|
|||
|
|
# 标记当前脉冲时间窗口内的信号为已分配
|
|||
|
|
for det_idx, det_name in enumerate(['detector1', 'detector2', 'detector3']):
|
|||
|
|
signals = time_series.get(det_name, [])
|
|||
|
|
for signal in signals:
|
|||
|
|
if len(signal) == 2:
|
|||
|
|
timestamp = signal[0]
|
|||
|
|
if timestamp >= pulse_time and timestamp not in assigned_timestamps:
|
|||
|
|
if next_pulse_time is None or timestamp < next_pulse_time:
|
|||
|
|
assigned_timestamps.add(timestamp)
|
|||
|
|
|
|||
|
|
# 如果有信号,设置最后一个信号为事件结束
|
|||
|
|
if pulse_signals:
|
|||
|
|
# 设置最后一个信号的事件结束标志
|
|||
|
|
original_val = pulse_signals[-1]
|
|||
|
|
pulse_signals[-1] = self.encoder.set_event_end_flag(pulse_signals[-1])
|
|||
|
|
logger.debug(f"事件 {pulse_idx}: 设置event_end, 原始值={hex(original_val)}, 新值={hex(pulse_signals[-1])}")
|
|||
|
|
event_signals.extend(pulse_signals)
|
|||
|
|
|
|||
|
|
logger.info(f"时间序列编码完成 - 编码信号数: {len(event_signals)}, 事件数: {len(sync_pulse_times)}")
|
|||
|
|
return event_signals
|
|||
|
|
|
|||
|
|
def _merge_and_encode_signals(self, detector_signals: dict, require_signal: bool = True) -> list:
|
|||
|
|
"""
|
|||
|
|
合并探测器信号并编码为64位整数
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
detector_signals: 各探测器的信号数组
|
|||
|
|
require_signal: 是否只输出有信号的探测器
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
编码后的64位整数列表
|
|||
|
|
"""
|
|||
|
|
time_signal_map = {}
|
|||
|
|
|
|||
|
|
for det_idx, det_name in enumerate(['detector1', 'detector2', 'detector3']):
|
|||
|
|
signals = detector_signals.get(det_name, [])
|
|||
|
|
|
|||
|
|
for signal in signals:
|
|||
|
|
if len(signal) == 2:
|
|||
|
|
# 注意:这里的信号来自sampler.sample(),时间戳是微秒单位
|
|||
|
|
energy_kev = signal[0]
|
|||
|
|
timestamp_us = signal[1]
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 能量转换: mV = keV * K + B,结果取整
|
|||
|
|
energy_mv = int(round(energy_kev * self.energy_K + self.energy_B))
|
|||
|
|
|
|||
|
|
# 确保能量非负
|
|||
|
|
if energy_mv < 0:
|
|||
|
|
energy_mv = 0
|
|||
|
|
|
|||
|
|
# 检查溢出
|
|||
|
|
if energy_mv > ((1 << 14) - 1):
|
|||
|
|
logger.warning(f"能量溢出,放弃信号: {energy_mv}mV (原始: {energy_kev}keV)")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
if timestamp_us > ((1 << 18) - 1):
|
|||
|
|
logger.warning(f"时间戳溢出,放弃信号: {timestamp_us}us")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
# 使用微秒单位的时间戳作为键
|
|||
|
|
timestamp_int = int(round(timestamp_us))
|
|||
|
|
if timestamp_int not in time_signal_map:
|
|||
|
|
time_signal_map[timestamp_int] = [0, 0, 0]
|
|||
|
|
|
|||
|
|
# 只保留第一个信号(去重)
|
|||
|
|
if time_signal_map[timestamp_int][det_idx] == 0:
|
|||
|
|
time_signal_map[timestamp_int][det_idx] = energy_mv
|
|||
|
|
else:
|
|||
|
|
logger.debug(f"重复时间戳: 时间戳={timestamp_int}us, 探测器={det_name}, 旧能量={time_signal_map[timestamp_int][det_idx]}, 新能量={energy_mv}")
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"编码信号时出错,放弃: {e}")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
encoded_signals = []
|
|||
|
|
sorted_timestamps = sorted(time_signal_map.keys())
|
|||
|
|
|
|||
|
|
for timestamp in sorted_timestamps:
|
|||
|
|
energies = time_signal_map[timestamp]
|
|||
|
|
|
|||
|
|
if require_signal:
|
|||
|
|
if all(e == 0 for e in energies):
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 直接使用微秒单位的时间戳(编码器期望的单位)
|
|||
|
|
encoded = self.encoder.encode(timestamp, energies, event_end=False)
|
|||
|
|
encoded_signals.append(encoded)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"编码时间序列信号时出错: {e}")
|
|||
|
|
default_signal = self.encoder.encode(timestamp, [0, 0, 0], event_end=False)
|
|||
|
|
encoded_signals.append(default_signal)
|
|||
|
|
|
|||
|
|
return encoded_signals
|