
简介这份资源面向大气科学、遥感与气象观测方向的学习者和科研人员围绕CE318型太阳光度计的观测数据提供从原始数据读取到气溶胶光学厚度AOD与水汽含量WV反演的完整处理思路。资源包共5个文件全部为cpp源码压缩包约9KB涵盖数据读取、角度拟合、定标处理以及AOD与WV对比计算等核心模块便于读者直接研读算法实现或移植到自己的处理流程中。已有1082人学习下载说明其在同类数据处理资料中具有一定参考价值。通过阅读这些代码读者可以理解如何清洗CE318原始观测数据、应用气溶胶光学理论推算AOD并利用水汽吸收带反演WV同时接触定标、质量控制与结果对比等关键环节为后续开展大气环境监测和气候研究提供可复用的编程基础。1. 从一台太阳光度计到两张大气参数图CE318 反演 AOD 与 WV 到底在做什么手里有一台 CE318 太阳光度计连续跑了几周甚至几个月原始文件攒了一堆但真正能拿去做辐射传输订正、卫星产品验证或者气溶胶气候分析的其实只有两个核心量气溶胶光学厚度 AOD 和大气水汽含量 WV。很多人第一次接触这类数据时会以为「仪器自带软件点一下就能出结果」实际动手才发现原始文件里只有各通道的瞬时电压和对应的太阳几何角度AOD 和 WV 都得自己从比尔-朗伯定律一步步反演出来。这篇笔记就围绕 CE318 太阳光度计数据处理反演 AOD 和水汽含量 wv 这条链路把定标系数怎么用、Langley 定标和直接反演两条路怎么选、云 screening 怎么做、Angstrom 波长指数怎么配套算出来以及最常见的几个翻车点讲清楚。适合已经拿到 CE318 原始数据、想自己写脚本批量处理而不是依赖厂商黑匣子软件的人也适合做地基遥感与卫星对比、需要理解 AOD 和 WV 不确定度来源的从业者。2. CE318 原始数据长什么样文件结构、通道配置与定标系数2.1 原始文件里到底有哪些字段CE318 的原始数据通常按天或按测量序列存储常见格式是厂商自定义的文本或二进制打包文件解包后每条记录对应一次测量。一次完整测量包含的内容比想象中多测量时间戳UTC 或本地时必须确认、太阳天顶角 SZA、太阳方位角、各通道的瞬时信号值通常以数字计数或电压形式给出、以及仪器内部温度。不同固件版本字段顺序和分隔符会有差异所以第一步永远是拿几条记录对照厂商文档确认列含义而不是直接套用网上抄来的解析脚本。我一般会先把原始文件转成一张宽表每行一次测量列包括时间、SZA、各通道信号、温度。这样后续无论是做 Langley 定标还是直接反演都能在同一张表上操作。下面是一个最小化的解析与整理示例假设原始文件是分号分隔的文本前几列是时间和角度后面按波长顺序排列各通道信号。import pandas as pd import numpy as np # 读取原始文本假设无表头分号分隔 raw pd.read_csv(ce318_raw_20240101.txt, sep;, headerNone, names[date, time, sza, azimuth, temp, sig_340, sig_380, sig_440, sig_500, sig_675, sig_870, sig_940, sig_1020]) # 合并时间戳统一转成 UTC raw[datetime] pd.to_datetime(raw[date] raw[time], utcTrue) # 只保留 SZA 合理范围内的测量避免地平附近噪声 df raw[(raw[sza] 0) (raw[sza] 80)].copy() # 信号为 0 或负值的记录直接丢弃通常是遮挡或异常 signal_cols [c for c in df.columns if c.startswith(sig_)] df df[(df[signal_cols] 0).all(axis1)] print(df[[datetime, sza, sig_440, sig_675, sig_940]].head())这段代码做了三件事把日期和时间拼成统一时间戳、按太阳天顶角筛掉低仰角数据、剔除信号非正的异常记录。参数上SZA 上限设 80 度是常见做法因为再低太阳高度角会放大气mass误差和地面遮挡影响信号非正判断是为了排除云遮挡或仪器指向偏移导致的无效测量。注意时间戳一定要确认是 UTC 还是本地时后面查算日地距离和太阳几何都要用时区搞错会导致 AOD 系统性偏差。2.2 通道配置与定标系数怎么对应CE318 常见配置是 8 个通道左右覆盖 340 到 1020 纳米其中 940 纳米通道专门用于水汽反演其余通道用于 AOD 和 Angstrom 指数。每个通道都有自己的定标系数通常写成「仪器常数」或「校准系数」单位是把信号值转成大气外太阳辐照度对应的电压。定标系数不是永久不变的仪器经过维修、更换滤光片或长期使用后都需要重新定标所以拿到数据第一件事是确认定标系数对应的日期区间。常见做法是把定标系数整理成一张表每个波长一行包含定标值、定标日期、不确定度。反演时用插值或就近选取的方式匹配测量日期。如果定标系数缺失或明显过期AOD 的绝对值就不可信这时候只能做相对变化分析不能拿去和卫星产品做绝对对比。波长 nm定标系数 V0定标日期相对不确定度340待填待填待填380待填待填待填440待填待填待填500待填待填待填675待填待填待填870待填待填待填940待填待填待填1020待填待填待填表格里「待填」不是偷懒而是提醒你这些值必须来自你自己的定标报告不能从别处抄。不同仪器的 V0 差异可能很大抄错一个数量级AOD 直接变成负数。2.3 日地距离订正与大气质量计算反演 AOD 之前必须把测量信号归一到大气外太阳辐照度这一步涉及日地距离订正和大气质量计算。日地距离订正系数随日期变化常用近似公式计算大气质量在 SZA 小于 80 度时可以用 sec(SZA) 近似但更稳妥的是用考虑地球曲率和大气折射的经验公式。下面给出一个可直接用的计算函数。def earth_sun_distance_correction(doy): doy 为年积日返回日地距离订正因子 # 常用近似1 - 0.0167 * cos(2*pi*(doy-3)/365) return 1 - 0.0167 * np.cos(2 * np.pi * (doy - 3) / 365.0) def air_mass(sza_deg): SZA 小于 80 度时的大气质量近似 sza_rad np.deg2rad(sza_deg) # 考虑地球曲率的经验式比简单 sec 更稳 return 1.0 / (np.cos(sza_rad) 0.15 * (93.885 - sza_deg) ** -1.253) df[doy] df[datetime].dt.dayofyear df[esd] earth_sun_distance_correction(df[doy]) df[am] air_mass(df[sza])日地距离订正因子的物理含义是地球绕太阳公转导致不同日期接收到的太阳辐照度有约 ±3.3% 的变化不订正会直接进入 AOD 误差。大气质量描述太阳光穿过大气的等效路径长度SZA 越大路径越长AOD 信号越强但同时也越容易受云和地面影响。这两个量算错后面所有反演都是白做。3. 用 Langley 定标和直接反演两条路算 AOD3.1 Langley 定标法的适用条件与操作步骤Langley 定标是 AOD 反演的经典方法核心思路是在大气稳定、无云的清晨或傍晚测量信号的对数随大气质量线性变化把直线外推到大气质量为零截距就是大气外信号斜率就是 AOD。这个方法对天气条件要求极高必须是一整段稳定无云时段否则直线拟合会被云污染。常见做法是选早晨 SZA 从 75 度降到 60 度左右的一段或者傍晚反过来用最小二乘拟合。from scipy import stats def langley_calibrate(df, channel, am_min2.0, am_max5.0): 对指定通道做 Langley 定标返回截距和斜率 sub df[(df[am] am_min) (df[am] am_max)].copy() sub sub[sub[channel] 0] if len(sub) 10: return None, None, 0 ln_sig np.log(sub[channel] / sub[esd]) slope, intercept, r_value, p_value, std_err stats.linregress(sub[am], ln_sig) # 截距 exp 后就是该通道的 V0 v0 np.exp(intercept) return v0, slope, r_value ** 2 v0_440, tau_slope, r2 langley_calibrate(df, sig_440) print(f440nm V0{v0_440:.2f}, 斜率{tau_slope:.4f}, R2{r2:.4f})这段代码里am_min 和 am_max 控制参与拟合的大气质量范围一般取 2 到 5 之间对应 SZA 约 60 到 78 度。R2 是判断这次定标是否可信的关键指标低于 0.99 基本说明这段数据有云或大气不稳定定标结果不能用。斜率取负号后就是该通道的 AOD但注意这是定标时段的平均 AOD不是最终产品。Langley 定标最大的坑是「看起来直线很直但其实是云的影响被平均掉了」所以一定要画散点图肉眼确认不能只看 R2。3.2 直接反演法用已知定标系数算 AOD如果仪器有可靠的定标系数就不需要每次做 Langley直接用比尔-朗伯定律反演即可。公式是AOD ln(V0 / (V * esd)) / am其中 V 是测量信号V0 是定标系数esd 是日地距离订正am 是大气质量。这一步看起来简单但实际写脚本时要注意信号单位、定标系数单位、以及是否需要对信号做温度订正。def retrieve_aod(df, channel, v0): 用已知定标系数反演 AOD valid (df[channel] 0) (df[am] 0) aod np.full(len(df), np.nan) aod[valid] np.log(v0 / (df.loc[valid, channel] * df.loc[valid, esd])) / df.loc[valid, am] # 物理上 AOD 不应为负负值通常是云或定标问题 aod[aod 0] np.nan return aod df[aod_440] retrieve_aod(df, sig_440, v0_440) df[aod_675] retrieve_aod(df, sig_675, v0_675) df[aod_870] retrieve_aod(df, sig_870, v0_870)这里把负 AOD 直接置为 NaN是因为物理上气溶胶光学厚度不可能为负出现负值基本意味着云遮挡、定标系数偏大或信号异常。参数上v0 必须和测量日期匹配如果定标系数是几个月前的最好评估一下仪器漂移。另外 940 纳米通道不参与 AOD 反演它专门用于水汽因为水汽吸收带和气溶胶消光混在一起需要单独处理。3.3 Angstrom 波长指数与 AOD 质量控制有了多个波长的 AOD就可以算 Angstrom 波长指数它反映气溶胶粒子大小是判断气溶胶类型的重要参数。常见做法是用 440 和 870 两个波长的 AOD 做线性回归斜率取负就是 Angstrom 指数。同时要做质量控制比如检查 AOD 是否在合理范围通常 0 到 3 之间、同一时刻不同波长 AOD 是否满足平滑变化、以及是否被云 screening 标记。def angstrom_exponent(aod_short, aod_long, wl_short440, wl_long870): 计算 Angstrom 波长指数 valid (aod_short 0) (aod_long 0) ae np.full(len(aod_short), np.nan) ae[valid] -np.log(aod_short[valid] / aod_long[valid]) / np.log(wl_short / wl_long) return ae df[angstrom] angstrom_exponent(df[aod_440].values, df[aod_870].values) # 合理范围筛选 df.loc[(df[angstrom] 0) | (df[angstrom] 2.5), angstrom] np.nanAngstrom 指数小于 0 通常意味着测量误差或云污染大于 2.5 则可能是细粒子主导但数值偏大需要结合天气和后向轨迹判断。这一步不是必须的但做卫星对比或气溶胶类型分析时很有用。质量控制的核心原则是宁可少一些有效数据也不要让明显异常的值进入后续分析。4. 940 纳米通道反演水汽含量 WV从透过率到可降水量4.1 水汽反演的基本原理与通道选择CE318 的 940 纳米通道位于水汽吸收带内测量信号同时受气溶胶消光和 水汽吸收影响。反演水汽的思路是先用其他通道如 870 或 1020 纳米算出气溶胶在该波段的消光再从 940 纳米总消光中扣除气溶胶贡献剩下的就是水汽吸收光学厚度最后通过经验关系转换成水汽含量。这个经验关系通常写成「水汽含量 a * (ln(透过率))^b」的形式系数 a 和 b 来自仪器定标或文献不同仪器和滤光片特性会有差异。常见做法是用 870 和 1020 两个通道插值出 940 纳米的气溶胶 AOD然后计算 940 纳米的水汽透过率。插值方法可以用 Angstrom 关系也可以用线性插值。下面给出一个可复现的流程。def retrieve_wv(df, aod_870, aod_1020, sig_940, v0_940, coef_a0.65, coef_b0.6): 反演水汽含量coef_a 和 coef_b 来自定标或文献 # 用 Angstrom 关系插值 940nm 气溶胶 AOD wl_870, wl_940, wl_1020 870, 940, 1020 ae -np.log(aod_870 / aod_1020) / np.log(wl_870 / wl_1020) aod_940 aod_870 * (wl_940 / wl_870) ** (-ae) # 计算 940nm 总透过率 trans_total (sig_940 * df[esd]) / v0_940 trans_total np.clip(trans_total, 1e-6, 1.0) # 扣除气溶胶贡献 trans_aer np.exp(-aod_940 * df[am]) trans_wv trans_total / trans_aer trans_wv np.clip(trans_wv, 1e-6, 1.0) # 经验关系转水汽含量 wv coef_a * (-np.log(trans_wv)) ** coef_b return wv df[wv] retrieve_wv(df, df[aod_870].values, df[aod_1020].values, df[sig_940].values, v0_940)这段代码的关键参数是 coef_a 和 coef_b它们决定水汽含量的绝对量级。如果这两个系数不对WV 可能整体偏大或偏小但相对变化趋势仍然可用。实际项目中这两个系数最好用探空数据或微波辐射计做本地订正没有条件时用文献值并明确标注不确定度。另外 940 纳米通道信号对温度敏感如果仪器有温度记录最好做温度订正。4.2 水汽反演的误差来源与筛选条件水汽反演的误差来源比 AOD 更多气溶胶插值误差、云污染、温度变化、以及经验关系本身的局限。常见筛选条件包括SZA 小于 75 度、940 纳米信号在合理范围、AOD 插值结果为正且小于 1.5、以及同一时刻 870 和 1020 通道都有有效值。如果这些条件不满足WV 直接置为 NaN不要强行反演。# 水汽质量控制 valid_wv ( (df[sza] 75) (df[aod_870] 0) (df[aod_870] 1.5) (df[aod_1020] 0) (df[aod_1020] 1.5) (df[wv] 0) (df[wv] 10) ) df.loc[~valid_wv, wv] np.nan水汽含量合理范围一般在 0 到 10 厘米可降水量之间超出这个范围基本是反演失败。SZA 限制在 75 度以内是因为低仰角时大气质量大水汽吸收饱和反演灵敏度下降。这些阈值不是绝对的可以根据你的站点气候条件微调但调整后要重新评估不确定度。4.3 用探空或再分析数据做交叉验证反演出来的 WV 不能直接信最好用探空数据或再分析资料做交叉验证。常见做法是把同站点的探空可降水量和 CE318 反演值做散点图看斜率、截距和相关系数。如果斜率明显偏离 1说明经验系数需要本地订正如果相关系数低说明筛选条件或反演流程有问题。没有探空数据时可以用再分析资料的水汽场做粗略对比但要注意时空匹配误差。# 假设有探空可降水量 pwv_sonde 和对应时间 matched df.dropna(subset[wv]).merge(sonde_df, ondatetime, howinner) if len(matched) 10: slope, intercept, r, p, se stats.linregress(matched[pwv_sonde], matched[wv]) print(f斜率{slope:.3f}, 截距{intercept:.3f}, R{r:.3f})交叉验证不是一次性的每次仪器定标更新或反演流程调整后都应该重新做。如果发现系统性偏差优先检查定标系数和 940 纳米通道的系数而不是急着改筛选阈值。5. 避坑与排查CE318 反演 AOD 和水汽最常见的五个翻车点5.1 现象AOD 出现大量负值原因通常是定标系数 V0 偏大、信号未做日地距离订正、或者云遮挡导致信号异常偏低。解决方法是先检查 V0 是否和测量日期匹配再确认 esd 是否参与计算最后用云 screening 剔除异常时段。如果负值集中在某个时段大概率是云如果全天都有负值优先怀疑定标系数。5.2 现象Langley 定标 R2 很高但 AOD 仍然不准原因是 Langley 拟合时段内大气虽然稳定但可能存在薄卷云或气溶胶层变化导致截距偏移。解决方法是把拟合时段拆成更小的窗口分别定标比较不同窗口的 V0 差异差异大说明定标不可靠。另外要确认大气质量计算是否用了考虑地球曲率的公式简单 sec 近似在 SZA 大于 70 度时误差明显。5.3 现象水汽含量整体偏大或偏小原因是经验系数 coef_a 和 coef_b 不适用于你的仪器或站点。解决方法是用本地探空数据做最小二乘拟合重新标定这两个系数。如果没有探空数据至少用再分析资料做趋势对比确认相对变化合理。不要直接套用文献值就认为绝对量级正确。5.4 现象940 纳米通道信号饱和或接近零原因是水汽吸收太强或太弱信号超出仪器动态范围。解决方法是检查该通道的定标系数和信号范围如果长期饱和说明该通道可能不适合当前气候条件如果长期接近零检查滤光片是否老化或污染。这两种情况都需要仪器维护不是软件能解决的。5.5 现象不同波长 AOD 变化趋势不一致原因是某个通道的定标系数错误或滤光片特性漂移。解决方法是画各波长 AOD 的时间序列看是否有某个通道明显偏离。如果 440 和 675 趋势一致但 870 偏离优先检查 870 通道的定标和维护记录。Angstrom 指数异常大或异常小也是类似问题的信号。6. 把反演流程串成可复现的批处理脚本参数、日志与验证习惯把前面所有步骤串起来就是一个完整的批处理流程解析原始文件、计算日地距离和大气质量、反演各波长 AOD、计算 Angstrom 指数、反演水汽、质量控制、输出结果表。我一般会把整个流程写成一个函数输入是原始文件路径和定标系数表输出是包含时间、AOD、Angstrom、WV 的 DataFrame同时写一份日志记录每一步筛掉了多少条数据。def process_ce318(raw_path, calib_df, output_path): 完整反演流程返回结果 DataFrame 并写日志 log [] raw pd.read_csv(raw_path, sep;, headerNone) log.append(f原始记录数: {len(raw)}) # 解析、时间戳、筛选 df parse_and_filter(raw) log.append(f筛选后记录数: {len(df)}) # 日地距离和大气质量 df[esd] earth_sun_distance_correction(df[datetime].dt.dayofyear) df[am] air_mass(df[sza]) # AOD 反演 for wl in [340, 380, 440, 500, 675, 870, 1020]: v0 calib_df.loc[calib_df[wl] wl, v0].values[0] df[faod_{wl}] retrieve_aod(df, fsig_{wl}, v0) # Angstrom df[angstrom] angstrom_exponent(df[aod_440].values, df[aod_870].values) # 水汽 v0_940 calib_df.loc[calib_df[wl] 940, v0].values[0] df[wv] retrieve_wv(df, df[aod_870].values, df[aod_1020].values, df[sig_940].values, v0_940) # 质量控制 df apply_qc(df) log.append(f最终有效 AOD 记录数: {df[aod_440].notna().sum()}) log.append(f最终有效 WV 记录数: {df[wv].notna().sum()}) df.to_csv(output_path, indexFalse) with open(output_path.replace(.csv, .log), w) as f: f.write(\n.join(log)) return df这个脚本的关键设计是日志每一步筛掉多少条、最终有效数据量是多少都要记录。没有日志的批处理脚本就是黑匣子出了问题只能重跑。参数上calib_df 必须包含每个波长的 v0且和测量日期匹配输出路径建议按站点和月份分文件夹方便后续检索。验证习惯方面我一般会做三件事第一每天抽一条数据手工算一遍 AOD和脚本结果对比第二每周画一次 AOD 和 WV 的时间序列看有没有异常跳变第三每月和卫星产品做一次粗略对比确认量级和趋势一致。这三件事花不了多少时间但能提前发现大部分系统性问题。CE318 数据处理反演 AOD 和水汽含量 wv 这条链路难点不在公式而在细节定标系数、时间戳、云 screening、经验系数每一个环节出错都会让结果看起来「差不多但就是不对」。希望帮到你。本文还有配套的精品资源点击获取