Skip to content

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. 三个核心场

cpp
// 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
cpp
// 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
cpp
// 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 env

3. 核心设计原则

3.1 纯函数对外,状态自包内

对外接口必须是 const 方法。这保证 env::* 类型可以在 sim::WorldEnv 中被多线程并发只读访问,无需锁。

cpp
// 反模式(禁止)
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_atenv::* 不做坐标转换。

3.3 不持有运行时状态

cpp
// 反模式(禁止)
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. 与其他模块的交互

mermaid
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 配置示例

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 本构)