SG2地震数据转DAT:segdat.m实现二进制到文本的格式转换
2026/9/17 2:44:36 网站建设 项目流程

简介:面向地震数据处理与分析人员,这份资源包聚焦地震记录由二进制采集格式向文本数据格式转换的需求,提供基于MATLAB的转换脚本。压缩包内仅包含1个m格式的MATLAB脚本文件,压缩包整体大小仅约1KB,结构单一轻量,便于直接查看和调试。脚本围绕格式转换的核心流程设计,依次实现SG2二进制数据解析、采样率与通道信息提取、按列整理地震参数、输出可读DAT文本文件等关键步骤,同时保留了必要的参数调整位置,可适配不同来源的地震记录。目前已有607人学习/下载,适合地震监测、工程地震及相关科研人员使用,能够快速打通两类数据之间的通道,减少重复编写底层解析代码的时间,帮助使用者更清晰地理解不同存储方式对后续分析的影响。

1. 从sg2到dat:segdat.m让地震数据转换不再卡壳

流动台站收回的原始记录是SG2二进制道文件,而震相分析脚本只认DAT文本,这种情况在临时观测和应急数据处理中几乎每周都会碰到。SG2保留了最完整的地震波形,适合采集端和存储端使用;但一旦进入跨软件流程交接,解析不了二进制格式,后面的分析工作就只能停下来。segdat.zip里的segdat.m就是为这个场景准备的:它读出SG2道头里的采样率、通道号、时间戳和采样点数,再把每个采样点按行写成DAT文本,同时附带可读的元数据注释。需要调整设备差异时,只改函数入口的几个字段即可,不必为每个台站重写脚本。它适合台站运维人员、数据分析工程师,以及刚进入地震数据处理方向的学生。

2. SG2与DAT格式的地震数据存储差异

2.1 SG2的二进制封装:道头、数据区与采样点排列

SG2本质上是按道组织的二进制记录。一个文件可以包含一道或多道波形,每道前面是固定长度的道头,后面紧接数据区。道头的长度在不同采集系统间并不统一,常见的规格有512字节、1024字节,数据区才是真正按采样率排列的波形序列。

道头里记录事件时间、采样点数、采样间隔、通道序号以及增益等参数。因为字段是排列在连续字节中的,解析时不能靠名字,只能靠偏移和变量类型。fread读回来的字节数组不会告诉你哪几个字节是采样点数,必须根据格式文档或者实际观测去验证。即便同一个厂家的设备,固件版本不同也可能调整字段位置,所以在segdat.m里我习惯把关键偏移放在文件开头集中管理。

数据区通常按定点数或浮点数排列。定点数常见的是int16或int32,浮点数则可能是single或double。选择哪种类型取决于采集设备的ADC位数。int16的优点是省空间,动态范围大约为96dB;int32能给出更大的动态范围,但文件体积翻倍。为了后续分析口径统一,segdat.m会先按原始类型读入,再统一转成double振幅。

2.2 DAT文本格式的行组织与常用字段布局

地震处理中的DAT文件没有全球统一标准,但多数情况下是纯文本,每行一个采样点,也可以用多列表示时间、幅值和多个分量。读取端只需要用readmatrix或load就能加载,这让DAT成为数据交换时的首选文本载体。

segdat.m输出的DAT采用注释头加数据区的结构。文件开头以井号开头记录事件时间和采样率,数据区每行一个双精度浮点数。如果要输出多个通道,最好为每个通道单独生成一个dat文件,而不是把几百个通道塞进同一个文本。单列布局的好处是后续做互相关或谱分析时按文件读取,不需要处理复杂的分隔符。

DAT格式的主要代价在体积和性能。实测中,int32的SG2文件写成文本后会膨胀4到6倍,读取时间也从毫秒级变成秒级。它更适合用于分析结果的归档和平台间交换,不适合做长期原始数据仓库。

2.3 转换的四项对齐关系与验证方法

把SG2转成DAT前,先要明确四项对齐信息:采样点数对齐、采样间隔对齐、通道号对齐、时间戳对齐。下表按segdat.m默认实现列了一组最常用的对应关系。

地震参数SG2中的典型位置DAT输出位置转换注意点
采样点数道头内固定字偏移数据区行数读取时按字节类型转换
采样间隔道头内固定字偏移头部注释# sample_rate注意单位是毫秒还是微秒
通道号道头内固定字偏移头部注释# channel多道数据建议拆分为多个文件
事件时间道头ASCII区头部注释# event_time保留原始时区信息
数据体道头后的数据区每行一个采样值int16/int32转浮点时要标定

拿到一个陌生SG2文件时,可以先用MATLAB做一次快速检查:

fid = fopen('event_20240708.sg2', 'rb', 'ieee-le'); hdr = fread(fid, 64, '*uint16'); disp(hdr(1:20)); fclose(fid);

这里先用小端字节序读取前64个字,观察前20个数值是否符合设备已知参数。如果数值全都跳动得没有规律,或者采样点数是一个天文数字,多半是字节序选错,应将ieee-le换成ieee-be再试。fread中的'*uint16'表示按两个字节一个字读取,道头里很多字段是16位或32位,先按16位观察结构比较容易定位问题。

3. segdat.m 的实现与转换流程

3.1 入口参数设计:把字节序、道头长度和标定交给调用者

写转换脚本最容易犯的错误是把所有参数写死在代码里。道头长度、字节序、采样间隔单位这三个值,在不同采集器之间几乎没有规律可循。segdat.m把这三个值作为可选项暴露在函数签名里,调用者可以根据实测数据传参,不用每次编辑源码。

function datfile = segdat(sg2file, datfile, opts) arguments sg2file (1,1) string datfile (1,1) string = "output.dat" opts.byteOrder (1,1) string = "ieee-le" opts.headBytes (1,1) double = 1024 opts.scale (1,1) double = 1.0 end fid = fopen(sg2file, 'rb', opts.byteOrder); if fid < 0 error('segdat:OpenFailed', '无法打开文件: %s', sg2file); end % 后续道头解析、数据读取与写出逻辑 fclose(fid); end

代码中的arguments是MATLAB R2019b之后支持的输入校验语法。调用segdat('abc.sg2')会走默认参数;调用segdat('abc.sg2','out.dat','byteOrder','ieee-be')可以快速切换大小端。scale参数用于传感器标定,读到的整数计数乘以它才能变成物理单位。

为什么headBytes要独立设置?因为数据区起点不一定总是等于道头长度。有的设备在道头后面还会附加一段量纲说明,这时需要把headBytes设置成道头长度加额外偏移。提供一个可调参数,比把取数据区位置的逻辑写死在代码里要干净得多。

3.2 道头字段读取:typecast、fseek 与物理量换算

道头解析依赖fseektypecast。先定位道头开始位置,读取固定字节,再用typecast把字节组合成数值。MATLAB里不能直接把uint8数组当uint32用,必须显式做类型转换。

hdr = fread(fid, 2048, '*uint8'); % 以下偏移按常见SG2结构示例,真实文件需对照格式手册核对 npts = double(typecast(hdr(17:20), 'uint32')); dtMicro = double(typecast(hdr(21:24), 'uint32')); channelId = double(typecast(hdr(25:26), 'uint16')); % 定位到数据区并读取全部采样点 fseek(fid, opts.headBytes, 'bof'); raw = fread(fid, npts, '*int32'); phys = double(raw) * opts.scale;

这段代码假设采样点数记录在第17到20字节,采样间隔记录在第21到24字节,通道号记录在第25到26字节。这是很多SG2类文件通用的一套布局,但并非硬标准。实际使用时要拿真实文件验证偏移值。typecast(hdr(17:20),'uint32')会把第17字节作为低位,第20字节作为高位;在小端模式下正好得到正确数值,如果打开时指定了大端,字节顺序就要反过来理解。

phys的计算包含了scale。仪器响应标定值一般是一个极小的浮点数,例如1.6e-9 m/s/count,乘以整型计数后得到的是m/s单位的速度序列。如果只关心相对波形形态,把scale设为1即可。

数据区如果是single浮点而不是int32,应把fread的数据类型改成'*single',随后再做一次double(raw)。判断依据是道头里的数据类型标识和每条采样点的字节宽度。

3.3 DAT写出与分块写盘机制

写出步骤建议分块。用一个较大的缓冲区把数据格式化后一次性fprintf,避免在循环里逐点调用造成写盘过慢。下面是最常用的单通道输出写法。

out = fopen(datfile, 'w'); fprintf(out, '# event_time: 2024-07-08T12:34:56\n'); fprintf(out, '# sample_rate: %.6f Hz\n', 1e6 / dtMicro); fprintf(out, '# channel: %d\n', channelId); fprintf(out, '# data_start\n'); block = 524288; for s = 1:block:npts e = min(s + block - 1, npts); fprintf(out, '%.8e\n', phys(s:e)); end fclose(out);

前三行fprintf负责写注释头,后续读取端可以用readmatrix('out.dat','CommentStyle','#')直接跳过井号行。采样率从dtMicro微秒换算成Hz,公式是1 / (dtMicro * 1e-6)。如果dtMicro实际单位是毫秒而不是微秒,换算因子需要改成1e-3。

分块写的作用是让长时间连续记录转成DAT时内存不会暴涨。每个block块取52万行左右即可,输出数据按行排列,每行一个科学计数法浮点数。%.8e保留8位有效数字,对地震观测数据来说已经足够,同时能让文件保持紧凑。

3.4 输出参数与文件体积预期

输出DAT的体积是这类文本转换绕不开的问题。下面按每秒采样1000点、float64文本格式估算,每行约15字节。一天连续记录会产生约130MB文本,虽然远小于同等时长的原始SG2,但在批量处理时仍要考虑磁盘空间。

输出参数建议值说明
precision%.8e8位有效数字浮点输出
blockSize524288每批最多写52万行
separateChannelstrue多道时按通道分别写文件
includeHeadertrue保留井号注释行

转换完成后用文本编辑器打开文件头部,核对注释行和数据行数是否匹配。如果行数比采样点数少很多,优先检查3.2里的headBytes是否偏大,把数据区起点定到了波形的中段。

4. 参数配置、边界处理与常见排错

4.1 字节序、采样间隔与道头偏移的排查

用segdat.m处理一批外部数据时,先跑一个样本文件,看输出的头部注释是否符合常识。采样率若在0.0001附近,大概率是采样间隔单位被误当成微秒,而原始数据里的单位其实是秒;采样率若变成几千万Hz,则多半是字节序颠倒。

症状可能原因排查动作
采样点数是一个巨大整数字节序或偏移错误打开十六进制查看前4字节
采样率接近0单位换算错确认dtMicro代表ms还是us
输出波形只有一条平线数据区偏移错误headBytes附近逐段试探
输出文件体积少一半数据区是int16但按int32读检查道头数据类型标识

排查时不要直接相信读出的头字段。把道头前256字节导出成十六进制文本,手动计算前几个字段的十进制值,和设备面板上显示的采样率做对比。若差2倍或4倍,基本是字节序或字宽选错;若差一个固定倍数,则多半是采样间隔单位换算错。

4.2 坏道、丢帧与饱和数据的统计方式

地震记录中因为采集器死机、雷击、传感器升压不到位,会出现坏道。转换时统计非有限值和满幅值比例,并写进注释,后续处理就能知道这段数据的真实质量。

% 统计NaN和Inf数量 bad = ~isfinite(phys); % 统计接近满幅的饱和样点 sat = abs(phys) >= abs(opts.scale) * 0.999; fprintf(out, '# bad_count: %d\n', nnz(bad)); fprintf(out, '# sat_count: %d\n', nnz(sat));

bad数组标记NaN和Inf点位。遇到大量NaN时,不建议直接删除,因为不知道这些位置原始采样值是否还有分析价值。segdat.m只统计并原样保留,后续拆帧或滤波时再决定补零还是做数据修补。饱和点同理,保留原值并标记,比插值更可靠,因为插值会引入虚假低频成分。

4.3 地震dat与其他同名格式的辨别

在网上一搜dat格式,会出现微信dat文件查看器、佳能dat文件恢复这样的内容。微信dat是聊天图片缓存,佳能dat文件恢复针对存储卡残留文件,它们与地震时间序列毫无关系。判断dat属于哪一类,不能看扩展名,要看文件内部结构。segdat.m生成的DAT应当是首行带井号注释、接下来每行一个浮点数的纯文本;而微信dat加密后是乱码,佳能dat文件恢复依赖文件系统上下文。拿地震脚本去读这些文件,会在fopen之后的fread阶段直接报错。

理解这个区别对运维场景有价值:存放数据时采用规范命名,把地震dat单独放在events目录,并让批量脚本自动生成日志。否则半年后看到一批.dat文件,很难定位哪一个对应哪条测线。

4.4 转换结果与原始波形的快速对比

转换结束后,用MATLAB把DAT读回,截取一段1000个采样点与SG2原始波形叠加对比。如果两条曲线重合,说明偏移和标定都正确。

d = readmatrix('output.dat', 'CommentStyle', '#'); % 从原始SG2中读取同一位置的数据段作对比 fid = fopen('event_20240708.sg2', 'rb', 'ieee-le'); fseek(fid, opts.headBytes, 'bof'); ref = fread(fid, 1000, '*int32'); fclose(fid); plot(1:1000, double(ref) * opts.scale, 'x'); hold on; plot(1:1000, d(1:1000, 2), '-');

这里使用readmatrix读取DAT时,默认会把第一列当作行号,因此数据值落在第二列d(:,2)中。若输出是单列数据,则需要用load('output.dat')或者readmatrix('output.dat','NumHeaderLines',4)来处理注释头。

5. 批量处理与结果验证的进阶技巧

5.1 一键批量转换台站目录

台站数据通常按天或按小时切成很多小文件,一次要跑几十个。用dir列出所有SG2文件,再循环调用segdat即可。

files = dir('raw/*.sg2'); for k = 1:numel(files) in = fullfile(files(k).folder, files(k).name); out = fullfile('converted', strrep(files(k).name, '.sg2', '.dat')); segdat(in, out, 'headBytes', 1024); end

这里strrep只替换文件名后缀,不会误改目录部分。如果某份数据的道头偏移不同,循环里要加一条日志记录失败文件,再单独处理。

5.2 把segdat扩展成dat转sg2回写接口

很多分析软件只接受DAT,但数据交换时对方又要求sg2。可以把segdat.m逆过来写,先从DAT注释头中读取采样率等元数据,再把采样值按整型编码写回。关键在于scale因子,写回前把振幅乘以合适系数转成int32,确保动态范围不浪费。

d = readmatrix('output.dat', 'CommentStyle', '#'); fid = fopen('restored.sg2', 'wb'); fwrite(fid, int32(d(:, 2) * opts.scale), 'int32'); fclose(fid);

这里没有重建道头,只适合实验验证。正式回写时还要把事件时间和通道号写进道头,不能用占位符糊弄。

5.3 把segdat.m接进数据质量日志

实际项目中,我会把segdat.m封装为批处理脚本,让它顺手输出每个文件的质量摘要,包括采样点数、坏道占比、饱和占比和输出文件大小。这样从采集端拿到原始SG2,经过一次转换就能同时得到DAT文件和可供后续分析的质量清单。

质量日志采用制表符分隔,每行一个文件,字段顺序固定:

file_name _ npts _ sample_rate _ bad_ratio _ sat_ratio _ dat_kb

fprintf(logFid, '%s\t%d\t%.1f\t%.4f\t%.4f\t%.1f\n', ... name, npts, sampleRateHz, badRatio, satRatio, fileSizeKb);

后续做频谱分析时,我会先加载这份质量日志,把坏道比例大于5%的文件过滤掉再进入计算流程。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询