DiFX研究整体流程:

图 1:从 VDIF 输入到复可见度分析的完整流程
1. 本次完成了什么
完整流程如下:
源码安装
→ 生成 Synthetic VDIF
→ mpifxcorr 互相关
→ 生成 test-usb.difx
→ difx2fits 生成 test-usb.fits
→ Astropy 读取 UV_DATA
→ 计算幅度和相位
本次结果如下:
| 项目 | 结果 |
|---|---|
| DiFX 版本 | 2.9.0 |
| 输入文件 | TEST1.vdif、TEST2-usb.vdif |
| 模拟台站 | T1、T2 |
| 基线数量 | 1 条 |
| UV_DATA 记录数 | 30 |
| 频率通道数 | 4096 |
| FLUX 长度 | 8192 |
| 参考频率 | 200 MHz |
| 单通道带宽 | 976.5625 Hz |
| 总带宽 | 约 4 MHz |
本次测试证明了软件和数据处理链路已经跑通,但并不代表已经得到真实天体图像。
2. DiFX 安装与环境配置
2.1 下载并固定版本
mkdir -p ~/software
cd ~/software
git clone --recurse-submodules https://github.com/difx/difx.git
cd difx
git checkout V2.9.0
git submodule update --init --recursive
git describe --tags --always
正常情况下应显示:
v2.9.0
2.2 关键环境路径
| 配置 | 路径 |
|---|---|
DIFXROOT | $HOME/difx-2.9.0 |
MPICXX | /usr/bin/mpicxx |
IPPROOT | /opt/intel/oneapi/ipp/2022.3 |
PGPLOTDIR | /usr/lib |
加载环境:
source ~/software/difx/setup.bash
检查变量:
echo "$DIFXROOT"
echo "$MPICXX"
echo "$IPPROOT"
echo "$PGPLOTDIR"
2.3 编译安装
mkdir -p ~/build/difx-2.9.0
cd ~/build/difx-2.9.0
source ~/software/difx/setup.bash
~/software/difx/install-difx --ipp 2>&1 | tee install.log
检查核心相关器:
which mpifxcorr
test -x "$DIFXROOT/bin/mpifxcorr" \
&& echo "DiFX 核心相关器安装成功"
3. 生成 Synthetic 模拟数据
复制官方测试目录:
mkdir -p ~/difx_work
cp -a ~/software/difx/tests/Synthetic ~/difx_work/
cd ~/difx_work/Synthetic
为了缩短测试时间,将 createData.sh 中的:
export DURATION=1200
改为:
export DURATION=10
运行:
./createData.sh 2>&1 | tee createData.log
本次生成了 8 个 VDIF 文件,总大小约 154 MB,其中测试使用:
TEST1.vdif
TEST2-usb.vdif
VDIF 保存的是带时间标记的数字采样帧,不是图像,也不是最终科学结果。
4. 运行最小互相关测试
执行:
cd ~/difx_work/Synthetic
unset DISPLAY XAUTHORITY WAYLAND_DISPLAY
./run-usb.sh 2>&1 | tee run-usb.log
脚本会自动完成:
生成 test-usb.input / .calc / .threads
→ 调用 mpifxcorr
→ 生成 test-usb.difx
→ 调用 difx2fits
→ 生成 test-usb.fits
成功日志应包含:
Conversion successful
1 of 1 jobs converted to 1 FITS files
检查输入配置:
grep -niE "DATA FORMAT|DATA SOURCE|BASELINE|FILE" test-usb.input
本次关键结果为:
DATA FORMAT: VDIF
DATA SOURCE: FILE
BASELINE ENTRIES: 1
FILE 0/0: TEST1.vdif
FILE 1/0: TEST2-usb.vdif
这说明相关器确实读取了两路 VDIF,并完成了 T1—T2 一条基线的互相关。
5. 配置文件和输出文件
DiFX 不能只依靠 VDIF 文件运行,还需要配置文件说明台站、时间、频率、边带和时延模型。
| 文件 | 作用 |
|---|---|
.vex | 观测计划、台站、源、频率和扫描 |
.v2d | 数据路径与相关参数 |
.input | mpifxcorr 的核心执行配置 |
.calc | 几何时延和台站模型 |
.threads | 线程分配 |
machines | MPI 节点清单 |
主要输出文件:
| 输出 | 含义 |
|---|---|
test-usb.difx/ | mpifxcorr 原生复可见度结果 |
DIFX_* | .difx 内部的实际相关数据 |
test-usb.fits | FITS-IDI 标准数据容器 |
注意:FITS 文件包含二进制表,直接用文本编辑器打开出现乱码。
6. UV_DATA 和 FLUX 的结构
使用 Astropy 读取后,实验结果如下:

图 2:UV_DATA结构
本次 UV_DATA 包含:
- 30 条记录;
- 每条 13 个字段;
- 1 个极化产品;
- 1 个频带;
- 4096 个频率通道;
- 每条 FLUX 长度为 8192。
为什么通道数是 4096,而 FLUX 长度是 8192?
是由于$ N_{\mathrm{FLUX}} = 2\times1\times4096\times1\times1\times1 = 8192 $
其中 COMPLEX = 2,表示每个通道保存实部和虚部:
R₁, I₁, R₂, I₂, R₃, I₃, ...
因此第 (k) 个通道的复可见度为:
$ V_k=R_k+iI_k $
第一条记录如下:

图 3:第一条 UV_DATA 记录
| 字段 | 实际值 | 含义 |
|---|---|---|
BASELINE | 258 | $(256\times1+2)$,即 T1—T2 |
INTTIM | 1.048576 s | 单条记录积分时间 |
WEIGHT | 0.9892578 | 相关权重,不是准确率 |
UU/VV/WW | 秒 | 基线在空间频率坐标中的投影 |
7. 幅度、相位和带宽公式
幅度:
$ A_k=\lvert V_k\rvert =\sqrt{R_k^2+I_k^2} $
相位:
$ \phi_k=\operatorname{atan2}(I_k,R_k) $
总带宽:
$ B=N_{\mathrm{chan}}\Delta f $
代入本次参数:
$ B = 4096\times976.5625\ \mathrm{Hz} = 4\ \mathrm{MHz} $
由于当前 FLUX 单位为 UNCALIB,因此幅度只是未标定相关幅度,不能直接写成 Jy。
8. Python 读取与绘图
安装依赖:
python -m pip install astropy numpy matplotlib
核心代码:
from astropy.io import fits
import matplotlib.pyplot as plt
import numpy as np
with fits.open("test-usb.fits") as hdul:
row = hdul["UV_DATA"].data[0]
flux = np.asarray(row["FLUX"], dtype=float).ravel()
real = flux[0::2]
imag = flux[1::2]
visibility = real + 1j * imag
amplitude = np.abs(visibility)
phase_deg = np.angle(visibility, deg=True)
channel = np.arange(len(visibility))
plt.plot(channel, amplitude)
plt.xlabel("Channel number")
plt.ylabel("Visibility amplitude")
plt.grid()
plt.show()
plt.plot(channel, phase_deg)
plt.xlabel("Channel number")
plt.ylabel("Visibility phase (degrees)")
plt.grid()
plt.show()
完整脚本位于:
scripts/analyze_difx_fits.py
9. 幅度图分析
可见度幅度:

图 4:第一条记录的 4096 通道可见度幅度
主要特征:
- 频带两端逐渐下降,说明存在通带边缘滚降;
- 中间多数通道幅度约在 0.02~0.12 范围内波动;
- 约第 1020 通道出现最高窄峰,幅度约 0.44;
- 约第 1530 通道出现另一明显窄峰,幅度约 0.36。
这些峰只能称为较强窄带相关分量。由于当前是 Synthetic 数据,不能直接说成真实谱线、RFI 或某个天体信号。
这张图表示的是一条基线、一个时间积分内,不同频率通道上的相关强度。
10. 相位图分析
可见度相位:

图 5:第一条记录的 4096 通道原始包裹相位
相位范围为:
$ -180^\circ\leq\phi_k\leq180^\circ $
相位密集跳变的原因主要包括:
- 当前只使用第一条记录;
- 没有筛选高信噪比通道;
- 没有进行时间平均;
- 没有相位展开;
- 低幅度通道的相位更容易受到噪声影响;
- 相位跨过 (180^\circ) 后会跳回 (-180^\circ)。
因此不能说已经得到稳定条纹。
如果要估计残余时延,应先进行通道筛选、时间平均和相位展开,再拟合:
$ \phi(f)=\phi_0+2\pi f\tau_{\mathrm{res}} $
残余时延为:
$ \tau_{\mathrm{res}} = \frac{1}{2\pi} \frac{\mathrm{d}\phi}{\mathrm{d}f} $
11. 当前结论与下一步
11.1 已经证明
- DiFX 2.9.0 安装成功;
- Synthetic VDIF 数据生成成功;
- T1—T2 一条基线互相关成功;
.difx和 FITS-IDI 输出成功;- Astropy 能读取
UV_DATA; - 4096 通道复可见度能够拆分并绘制幅相图。
11.2 暂时不能证明
- 当前幅度已经是 Jy;
- 两个窄峰就是真实天文谱线或 RFI;
- 原始相位已经稳定;
- 已经完成条纹搜索和残余时延测量;
- 已经处理
/data/Difx_data中的真实数据。
真实数据阶段应先检查是否存在:
.input / .calc / .threads
或者:
.vex / .v2d
随后选择小规模任务运行:
vex2difx experiment.v2d
calcif2 experiment
mpirun -np 4 mpifxcorr experiment.input
difx2fits experiment
12. 总结
本次已经完整跑通:
VDIF 输入
→ mpifxcorr 互相关
→ .difx 输出
→ FITS 转换
→ UV_DATA 读取
→ FLUX 拆分
→ 幅度和相位提取
最终确认:
test-usb.fits中有 30 条UV_DATA记录;- 每条记录包含 4096 个频率通道;
- FLUX 中的 8192 个数对应 4096 组实部和虚部;
- 幅度图显示通带边缘滚降和两个明显窄带峰;
- 相位图显示原始包裹相位;
- 幅度尚未标定,相位尚未平均和展开。
最终结论: DiFX 2.9.0 的安装、Synthetic VDIF 生成、基线互相关、
.difx输出、FITS 转换、UV_DATA 读取和幅相提取流程均已正常运行。下一步重点是处理小规模真实数据,并完成配置核验、条纹搜索、相位展开、残余时延拟合和幅相标定。
