Environment Fields
> Aligned with PCR Master Blueprint v1.0 — see Blueprint §2.3 / §7.15. > 职责:定义"普适物理场"——关于位置/时间的纯函数 f(x, t)。它们不属于任何具体物体,但作用于所有物体。 > C-Distillation note:所有 field 类只暴露 const 方法(pressure_at / density_at / gravity_at / wind_at)。内部持有的查表数据在蒸馏阶段会被替换为静态 PROGMEM 表。
1. 为什么 environment/ 必须独立
物理规律有两类,目录上必须分离:
| 类型 | 数学形式 | 可变性 profile | 归属 |
|---|---|---|---|
| 场(Field) | f(x, t) | 几乎不变(地球大气在地球任务里是常量) | environment/ |
| 本构关系(Constitutive) | g(state, params) | 每个火箭型号、每次任务都换 | plant/ |
如果把它们塞进同一个 plant/ 目录,迟早会因为"为了换一个气动表却被迫改动大气模型代码"而暴露耦合。详见 Blueprint §7.15。
2. 三个核心场
// src/environment/Atmosphere.h
namespace env {
struct Atmosphere {
// 输入:海拔高度(米,MSL = Mean Sea Level)
// 输出:USSA1976 标准大气或自定义查表
double pressure_at (double h_msl) const; // [Pa]
double density_at (double h_msl) const; // [kg/m^3]
double sound_speed_at(double h_msl) const; // [m/s]
double temperature_at(double h_msl) const; // [K]
private:
InterpTable1D table_; // 配置加载时从 YAML 注入
};
} // namespace env// src/environment/GravityField.h
namespace env {
struct GravityField {
// WGS84 椭球重力场(或更高阶模型)
Vec3_T<frame::ECF> gravity_at(Vec3_T<frame::ECF> pos_ecf) const;
private:
double mu_earth_; // 标准重力参数 GM
double J2_; // 椭球扁率引力项
double omega_e_; // 地球自转角速度(用于离心修正,可选)
};
} // namespace env// src/environment/WindField.h
namespace env {
struct WindField {
// 风速场:可以是常风、剖面风(高度相关)、或 4D 数据立方
Vec3_T<frame::ECF> wind_at(Vec3_T<frame::ECF> pos_ecf, Time t) const;
private:
// 实现可选:
// - 常风:单一 Vec3
// - 1D 剖面:InterpTable1D,输入高度
// - 3D / 4D:从 NWP 网格插值
WindModelVariant model_;
};
} // namespace env3. 核心设计原则
3.1 纯函数对外,状态自包内
对外接口必须是 const 方法。这保证 env::* 类型可以在 sim::WorldEnv 中被多线程并发只读访问,无需锁。
// 反模式(禁止)
struct Atmosphere {
double pressure_at(double h); // 没有 const → 可能修改内部
void set_time(Time t); // 时变状态→应该作为参数传入
};
// 正确
struct Atmosphere {
double pressure_at(double h) const;
};
struct WindField {
Vec3_T<frame::ECF> wind_at(Vec3_T<frame::ECF>, Time t) const; // 时间作为参数
};3.2 输入坐标系统一为 ECF
gravity_at / wind_at 都以 Vec3_T<frame::ECF> 为输入。原因:
- WGS84 重力模型天然在椭球地心系定义
- 气象数据(NWP 等)天然以经纬高网格存储,ECF 是最近转换
Atmosphere用海拔(标量)作为输入,因为标准大气只依赖高度
约定:调用方负责坐标转换。例如 probe_aero 拿到 body.spatial.pos_lic,自己用 frame::EcfLicTransform 转 ECF 后再传给 gravity_at。env::* 不做坐标转换。
3.3 不持有运行时状态
// 反模式(禁止)
struct Atmosphere {
double last_queried_h_; // ← 缓存 / 历史 ← 引入并发危险
double current_pressure_; // ← 当前值 ← 应改为参数
};env::Atmosphere 只持有查表数据(YAML 注入后只读),不持有"上次查到的值"之类的可变状态。
4. 替换的边界(地球 → 月球)
设想把项目从地球任务移植到月球任务:
| 模块 | 是否需要改 |
|---|---|
env::Atmosphere(月球数据源) | ✅ 改 YAML,重新加载 |
env::GravityField(月球 GM / 0 J2) | ✅ 改 YAML,重新加载 |
env::WindField(月球无大气,返回零) | ✅ 改 YAML,重新加载 |
plant/physics/compute_thrust | ❌ 不动(不知道大气是什么样的,只读 BodyEnv.aero.static_pressure) |
plant/physics/compute_drag | ❌ 不动(同上,读 BodyEnv.aero.density) |
sim::probe::probe_aero | ❌ 不动(接口不变) |
dynamics_core/ | ❌ 不动(universal) |
> 这就是把场与本构分离的回报:换星球只改 environment YAML + Assembler 装配规则。
5. 与其他模块的交互
graph LR
YAML[data/input/world/*.yaml] -->|Assembler 加载| Env
Env[env::Atmosphere<br/>env::GravityField<br/>env::WindField] -->|sim::WorldEnv 引用| WE[sim::WorldEnv]
WE -->|probe 读| Probe[sim::probe::*]
Probe -->|产出 BodyEnv.aero| BE[sim::BodyEnv]
BE -->|读 static_pressure / density| Phys[plant::physics::compute_*]注意:plant::physics::* 不直接 include environment/。它只通过 BodyEnv 的标量字段感受环境。这是"探测器降维"模式(详见 06_Simulation/Body_World_Tick.md 与 Blueprint §2.6.3 / §7.19)。
6. YAML 配置示例
# data/input/world/atmosphere.yaml
model: ussa1976
overrides:
surface_pressure_pa: 101325.0
surface_temperature_k: 288.15
# 可选:用 CSV 替换内置 USSA1976 表
custom_table: world/atmosphere_custom.csv
# data/input/world/gravity.yaml
model: wgs84
mu_earth: 3.986004418e14
j2: 1.08263e-3
include_centrifugal: false # 是否在 GravityField 中合并离心项
# data/input/world/wind.yaml
model: profile_1d
profile_csv: world/wind_profile.csv
# 或:
# model: constant
# vector_ecf: [10.0, 0.0, 0.0]加载逻辑由 runtime::Assembler::load_environment_() 实现(详见 07_Runtime/Assembler_and_Runner.md)。
7. 反模式
| 反模式 | 为什么不行 |
|---|---|
把 EngineSpec 放在 environment/ | EngineSpec 是对象专属本构,不是场 |
Atmosphere::set_time(Time t) | 时间应作为参数传入,不应改变内部状态 |
WindField 接收 Vec3_T<frame::LIC> 输入 | 跨 frame 不应由 env 层负责,调用方应先转 ECF |
在 plant/physics/compute_drag 中 #include <environment/Atmosphere.h> | 破坏"配方与流水线分离"——必须通过 BodyEnv |
在 env::* 内部持有 launch_point | 那是 frame::FrameConfig 的事,不属于场 |
8. Cross References
- 资产(对象本构)→
02_Physical_World/Plant_Model_Assets.md - 探测器降维 →
06_Simulation/Body_World_Tick.md§3 - 装配流程 →
07_Runtime/Assembler_and_Runner.md - Blueprint §7.15 设计裁定(场 vs 本构)