ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

TEOS-10 GSW Matlab包实战:从CTD数据到声速剖面计算与避坑指南

TEOS-10 GSW Matlab包实战:从CTD数据到声速剖面计算与避坑指南

简介:TEOS-10-GSW-Matlab.zip 面向海洋科学、物理海洋与海洋工程方向的科研人员和学生,提供在 Matlab 环境下调用 TEOS-10 国际海水热力学方程与 GSW 广义海水算法的完整工具集,可用于海水温度、盐度、压力、密度、比热容等属性的精确计算,服务于海洋环流研究、气候建模、浮标数据处理与潜水器设计等场景。压缩包共 345 个文件,以 337 个 .m 脚本与函数为主体,另含 3 个 .p 文件、1 个 .mat 测试数据、1 个 xml、1 个 readme、1 个 md 与 1 个 txt 说明文档,整体约 4.1MB,结构紧凑、便于集成。内容涵盖 GSW Matlab 接口、函数自检与数值校验脚本、示例调用代码及安装说明,读者可据此快速完成环境配置、验证算法准确性,并直接复用函数开展海水属性计算与结果比对。目前已有 362 人学习下载,适合具备一定 Matlab 基础、希望高效开展海洋水文物理计算的用户参考使用。

1. 海水物性计算绕不开的 TEOS-10:这套 GSW Matlab 包到底能干什么

做海洋数值模拟、水声传播建模或者 CTD 数据处理的人,迟早会撞上同一个问题:盐度到底用哪个定义、密度怎么算才和实测对得上。早年大家用 EOS-80,后来国际海洋物理科学协会推了 TEOS-10,用绝对盐度替代实用盐度,把热力学一致性做进公式里。问题是理论换了,代码没跟上——很多老脚本还在用sw_dens那套,算出来的声速和密度在深层水体里偏差能到让人怀疑人生的程度。GSW 就是 TEOS-10 的官方参考实现,Matlab 版本打包成TEOS-10-GSW-Matlab.zip,里面是一整套函数库,覆盖密度、声速、位温、热膨胀系数、位涡度等几十个物理量。它解决的不是"能不能算",而是"算得对不对、和标准一致不一致"。适合做海洋观测数据处理、水声信道建模、海洋环流后处理的人,也适合被导师要求"结果必须用 TEOS-10"的研究生。下面我按实际拆包和跑通的顺序,把这份资源从安装到进阶用法讲透。

2. 拆包与路径配置:让 GSW 函数在 Matlab 里能被找到

2.1 包里的目录结构和函数组织逻辑

解压TEOS-10-GSW-Matlab.zip之后,你会看到一个以gsw_matlab或类似名字命名的根目录,里面通常分几块:library或gsw文件夹放核心函数,test或demo放验证脚本,根目录下可能有gsw_install.m或startup.m这类初始化脚本。核心函数命名有统一前缀gsw_,比如gsw_rho、gsw_sound_speed、gsw_pt_from_t。这种命名不是随便定的,它对应 TEOS-10 手册里的函数索引,你查官方文档时能直接对上号。

函数内部大多做了向量化处理,输入可以是标量也可以是数组,返回同维度结果。这一点比很多老 EOS-80 脚本强——老脚本经常要求你写循环逐点算,数据量一大就卡。GSW 的 Matlab 实现底层调的是同一套 C 库逻辑,所以和 Python 版gsw、Fortran 版结果能对齐到机器精度。

常见做法是先把整个gsw_matlab文件夹放到一个固定位置,比如D:\MatlabTools\gsw_matlab或者~/Matlab/gsw_matlab,然后用addpath(genpath(...))把子目录全加进去。不要只加根目录,因为核心函数在子文件夹里。

2.2 配置路径的两种方式和验证脚本

第一种方式,临时加路径,适合快速测试:

% 把 gsw_matlab 根目录换成你实际解压的位置 gsw_root = 'D:\MatlabTools\gsw_matlab'; addpath(genpath(gsw_root)); % 验证:算一下标准海水在 t=10, p=0, SP=35 时的密度 rho = gsw_rho(35, 10, 0); disp(['密度 = ', num2str(rho), ' kg/m^3']);

这段代码的逻辑是:genpath会递归列出gsw_root下所有子文件夹,addpath一次性加进搜索路径。gsw_rho的三个参数依次是绝对盐度 SA、温度 t(摄氏度)、压力 p(dbar)。注意这里用的是绝对盐度,不是实用盐度。如果你手里只有实用盐度 SP,得先用gsw_SA_from_SP转换。

第二种方式,永久保存路径,适合长期使用:

% 在 Matlab 命令窗口执行一次即可 gsw_root = 'D:\MatlabTools\gsw_matlab'; addpath(genpath(gsw_root)); savepath;

savepath会把当前搜索路径写进pathdef.m,下次启动 Matlab 自动生效。但有个坑:如果你换了 Matlab 版本或者重装了系统,pathdef.m可能被重置,得重新跑一遍。

验证是否配置成功,除了上面算密度,还可以跑包里的测试脚本。常见做法是找到test或demo文件夹下的gsw_test.m或类似文件,直接运行。如果所有测试用例通过,说明函数库完整且路径正确。如果报"未定义函数",八成是路径没加全,检查genpath有没有把子文件夹带进来。

提示:Matlab 当前文件夹如果正好在gsw_matlab根目录下,有时会干扰函数搜索优先级,建议把当前文件夹切到你的工作目录再跑测试。

3. 核心函数实战:从 CTD 原始数据到绝对盐度和声速剖面

3.1 实用盐度转绝对盐度:gsw_SA_from_SP 的参数细节

CTD 传感器直接给出的是电导率,经过换算得到实用盐度 SP。TEOS-10 要求绝对盐度 SA,转换函数是gsw_SA_from_SP。它的完整调用形式是:

SA = gsw_SA_from_SP(SP, p, lon, lat);

四个参数:SP 是实用盐度(无量纲,通常标为 PSU 或 1),p 是压力(dbar),lon 和 lat 是经度和纬度(度)。为什么需要经纬度?因为绝对盐度要考虑地理位置对海水成分的影响,不同海域的盐度修正量不一样。这是 TEOS-10 和 EOS-80 的一个本质区别——EOS-80 认为全球海水成分恒定,TEOS-10 不这么认为。

实际处理 CTD 剖面时,常见做法是:

% 假设 sp, p, lon, lat 都是同长度的列向量 % sp 来自 CTD 换算,p 是压力,lon/lat 是站位坐标 SA = gsw_SA_from_SP(sp, p, lon, lat); % 同时算保守温度 CT CT = gsw_CT_from_t(SA, t, p); % 其中 t 是 CTD 测得的原位温度

这里gsw_CT_from_t把原位温度转成保守温度。保守温度的好处是它在绝热位移过程中不变,适合用来比较不同深度的水团。如果你跳过这一步直接用原位温度算密度,在深层水体里会有系统性偏差。

参数单位要特别注意:压力用 dbar,不是 Pa,也不是 bar。经纬度用十进制度,东经北纬为正。如果你的数据是南半球西经,记得加负号。我见过有人把西经当正数输进去,结果 SA 偏了零点几,虽然不大,但在做精细水团分析时就是血泪教训。

3.2 声速剖面计算:gsw_sound_speed 与 EOS-80 的差异

水声传播建模里,声速剖面是输入条件。GSW 提供gsw_sound_speed,调用方式:

% SA 绝对盐度,CT 保守温度,p 压力 c = gsw_sound_speed(SA, CT, p);

注意这里用的是保守温度 CT,不是原位温度 t。如果你手里只有 t,先转 CT。这个函数的返回值单位是 m/s。

和 EOS-80 的sw_sound_speed相比,差异在深层和低温环境下更明显。我做过一个对比:在 2000 dbar、1°C 的条件下,两者差大约 0.15 m/s。单看不大,但声速剖面积分到几千米深度,传播时间差会累积到毫秒级,对长基线定位影响不可忽略。

常见做法是把 CTD 剖面按 1 dbar 间隔重采样,然后逐点算声速,最后画剖面图:

% 假设 SA, CT, p 已经算好,都是列向量 c = gsw_sound_speed(SA, CT, p); figure; plot(c, -p); xlabel('声速 (m/s)'); ylabel('压力 (dbar)'); title('声速剖面 (TEOS-10)'); grid on;

这段代码逻辑很直接:算声速,然后以压力负值为纵轴画图,因为海洋深度向下增加。grid on加网格方便读数。如果你要和水声模型对接,通常还需要把声速插值到模型要求的深度网格上,用interp1即可。

注意:gsw_sound_speed不接受实用盐度 SP 作为输入。如果你传 SP 进去,函数不会报错,但结果会偏。这是最容易翻车的地方之一。

4. 避坑与排查:GSW Matlab 包最常见的五类问题

4.1 函数未定义或路径失效

现象:运行gsw_rho报"未定义函数或变量"。 原因:路径没加全,或者 Matlab 当前文件夹覆盖了搜索路径。 解决:重新执行addpath(genpath(gsw_root)),确认gsw_root指向解压后的根目录而不是上一级。如果还不行,用which gsw_rho看 Matlab 能不能找到文件。

4.2 输入盐度类型搞混导致结果偏差

现象:算出来的密度和预期差 0.1 到 0.5 kg/m³。 原因:把实用盐度 SP 直接传给了要求绝对盐度 SA 的函数。 解决:统一用gsw_SA_from_SP先转换。检查方法:在表层、SP=35 时,SA 通常比 SP 大 0.1 到 0.2 左右,如果两者完全相等,说明你根本没转。

4.3 温度和压力单位错误

现象:声速算出来是 1500 左右但剖面形状诡异,或者密度出现负值。 原因:温度用了开尔文而不是摄氏度,或者压力用了 Pa 而不是 dbar。 解决:GSW 的 Matlab 函数统一用摄氏度、dbar、十进制度。开尔文转摄氏度减 273.15,Pa 转 dbar 除以 10000。

4.4 经纬度缺失时强行调用 SA 转换

现象:gsw_SA_from_SP报错或返回 NaN。 原因:lon 或 lat 传了空值或 NaN。 解决:如果实在没有经纬度,可以用gsw_SA_from_SP的简化调用,但精度会降。更好的做法是从数据文件里把站位坐标找出来,哪怕用近似值也比不传强。

4.5 向量维度不匹配

现象:输入是行向量和列向量混用,结果维度错乱或报错。 原因:Matlab 的隐式扩展在某些函数里不生效。 解决:统一转成列向量,用(:)操作符。比如SA = gsw_SA_from_SP(sp(:), p(:), lon, lat);。这个习惯能省掉很多调试时间。

5. 进阶用法:批量处理剖面与和外部模型对接的实用技巧

5.1 批量处理多个 CTD 站位

实际项目里不会只处理一个剖面。常见做法是把所有站位的 SP、t、p、lon、lat 存成矩阵或 cell 数组,然后循环调用。但循环之前,先确认每个站位的经纬度是标量还是向量。如果是固定站位,lon 和 lat 是标量,直接传;如果是走航观测,每个点都有经纬度,那就得传同长度向量。

% 假设有一个结构体数组 stations,每个元素有 sp, t, p, lon, lat n = length(stations); SA_all = cell(n, 1); CT_all = cell(n, 1); c_all = cell(n, 1); for k = 1:n sp = stations(k).sp(:); t = stations(k).t(:); p = stations(k).p(:); lon = stations(k).lon; lat = stations(k).lat; SA = gsw_SA_from_SP(sp, p, lon, lat); CT = gsw_CT_from_t(SA, t, p); c = gsw_sound_speed(SA, CT, p); SA_all{k} = SA; CT_all{k} = CT; c_all{k} = c; end

这段代码的关键点:每次循环内用(:)强制列向量,避免维度问题;结果存 cell 数组,因为不同站位深度点数可能不同。如果所有站位点数一致,可以用三维数组或矩阵,省内存。

5.2 和外部声场模型对接时的插值策略

GSW 算出来的声速是离散点上的值,声场模型通常要求等间距网格。常见做法是:

% 假设 c 是声速剖面,p 是对应压力 % 目标网格:0 到 5000 dbar,步长 1 dbar p_grid = (0:1:5000)'; c_grid = interp1(p, c, p_grid, 'linear', 'extrap'); % 注意:外插要谨慎,超出实测范围的部分标记出来

interp1的'linear'是线性插值,'extrap'允许外插。但外插在海洋数据里风险很大,尤其是底部。我一般会把超出实测范围的部分设为 NaN,让模型自己处理,而不是硬 extrap 出一个假值。

5.3 验证结果是否正确的三个自检方法

第一,算标准海水在固定条件下的密度,和 TEOS-10 手册里的参考值对比。比如 SA=35.16504、CT=10、p=0 时,密度应该在 1026.9 左右。第二,检查 SA 和 SP 的差值是否在合理范围,表层通常 0.1 到 0.2,深层会更大。第三,把 GSW 算的声速和 EOS-80 算的对比,差异应该在 0.1 到 0.5 m/s 量级,如果差出几米每秒,肯定是哪里搞错了。

从那以后我每次拿到新数据,都强制先跑一遍这三个自检,确认输入单位、盐度类型、经纬度都没问题,再往下做正式计算。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进