diff --git a/README.md b/README.md index 3634be0..2fa12c7 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,9 @@ document means creating it under its layer and listing it in ## Regenerating the interface catalog The interface documentation is generated from the ApsisRS sources rather than -written by hand. Run the extractor from this directory when the sources +written by hand. The doc comments there are written in English and then in +Chinese, and the extractor splits the two halves, so both languages come from +the comment beside the code and neither can fall behind what it describes. Run the extractor from this directory when the sources change, then rebuild: ```sh @@ -174,8 +176,7 @@ or later. The Rust build does not run it and does not depend on Python. | `content/concepts//*.zh.md` | Chinese translations, named for the file they translate | | `content/graph/concepts.json` | The concept graph | | `content/flows/*.json` | The algorithm flow graphs | -| `content/api.json` | The generated interface catalog; do not edit by hand | -| `content/api.zh.json` | Chinese translations of the crate summaries and item doc comments | +| `content/api.json` | The generated interface catalog, in both languages; do not edit by hand | A document header declares `title`, `summary`, `tags`, and `prerequisites`. The identifier and the layer are not declared: the identifier is the file diff --git a/README.zh.md b/README.zh.md index 7b4a8af..031e510 100644 --- a/README.zh.md +++ b/README.zh.md @@ -134,7 +134,9 @@ curl -s http://127.0.0.1:8080/api/tools/hohmann-transfer/run \ ## 重新生成接口目录 -接口文档由 ApsisRS 源码生成,而非手工撰写。源码变化时在本目录运行提取脚本, +接口文档由 ApsisRS 源码生成,而非手工撰写。那里的文档注释先写英文、再写中文, +提取器把两半拆开,因此两种语言都来自代码旁的同一段注释,谁也不会落后于它所描述的 +代码。源码变化时在本目录运行提取脚本, 然后重新构建: ```sh @@ -154,8 +156,7 @@ cargo build --release | `content/concepts//*.zh.md` | 中文译文,文件名对应其所译的文件 | | `content/graph/concepts.json` | 概念图谱 | | `content/flows/*.json` | 算法流程图 | -| `content/api.json` | 生成的接口目录;不要手工编辑 | -| `content/api.zh.json` | crate 概要与条目文档注释的中文译文 | +| `content/api.json` | 生成的接口目录,含两种语言;不要手工编辑 | 文档头部声明 `title`、`summary`、`tags` 与 `prerequisites`。标识与层不由头部 声明:标识即文件名,层即目录,二者无法与目录结构相抵触。交叉引用写作 diff --git a/content/api.json b/content/api.json index 47a9b37..88b7bab 100644 --- a/content/api.json +++ b/content/api.json @@ -45,13 +45,15 @@ { "name": "apsis-core", "layer": "foundation", - "summary": "Shared foundation types for the ApsisRS workspace: a general purpose error type, result aliases, physical constants, and the unit marker trait. This crate sits at the bottom of the dependency graph. It has no dependency on any other ApsisRS crate, so every other crate may use it. Three concerns live here: - [`Error`] and [`Result`] are a general purpose error carrying a coarse [`ErrorKind`]. They exist so that a function whose failure has no interesting structure can report it without inventing a type, and so that the workspace has a shape to fall back on. They are deliberately *not* the error type of the layers: a layer with a domain specific failure defines its own `Error` and `ErrorKind`, whose variants name the concrete conditions a caller would want to match on. See `docs/design/errors.md` for why, and for the conventions the domain errors follow. - [`constant`] holds physical and astronomical constants expressed in SI units. - [`Quantity`] marks newtypes that carry a physical unit, so generic code can accept any quantity and still recover its unit type.", + "summary": "Shared foundation types for the ApsisRS workspace: a general purpose\nerror type, result aliases, physical constants, and the unit marker\ntrait.\n\nThis crate sits at the bottom of the dependency graph. It has no\ndependency on any other ApsisRS crate, so every other crate may use it.\n\nThree concerns live here:\n\n- [`Error`] and [`Result`] are a general purpose error carrying a coarse\n [`ErrorKind`]. They exist so that a function whose failure has no\n interesting structure can report it without inventing a type, and so\n that the workspace has a shape to fall back on. They are deliberately\n *not* the error type of the layers: a layer with a domain specific\n failure defines its own `Error` and `ErrorKind`, whose variants name\n the concrete conditions a caller would want to match on. See\n `docs/design/errors.md` for why, and for the conventions the domain\n errors follow.\n- [`constant`] holds physical and astronomical constants expressed in\n SI units.\n- [`Quantity`] marks newtypes that carry a physical unit, so generic\n code can accept any quantity and still recover its unit type.", + "summary_zh": "ApsisRS 工作区共享的基础类型:一个通用错误类型、结果别名、物理常量,以及\n单位标记 trait。\n\n本 crate 位于依赖图的底部。它不依赖任何其他 ApsisRS crate,故其余每个\ncrate 都可以使用它。\n\n这里有三件事:\n\n- [`Error`] 与 [`Result`] 是携带粗糙 [`ErrorKind`] 的通用错误。它们存在,\n 是为了让失败没有有趣结构的函数无需发明一个类型即可报告它,也是为了让\n 工作区有一个可退守的形状。它们有意地*不是*各层的错误类型:具有领域\n 特定失败的一层会定义自己的 `Error` 与 `ErrorKind`,其变体指名调用方\n 想要匹配的具体情形。原因以及各领域错误遵循的约定见\n `docs/design/errors.md`。\n- [`constant`] 保存以 SI 单位表达的物理与天文常量。\n- [`Quantity`] 标记携带有物理单位的 newtype,使泛型代码可以接受任何量\n 并仍能恢复其单位类型。", "items": [ { "kind": "const", "name": "SPEED_OF_LIGHT", "signature": "pub const SPEED_OF_LIGHT: f64 = 299_792_458.0;", "doc": "Speed of light in vacuum, in meters per second.\n\nExact by definition of the metre in the SI.", + "doc_zh": "真空光速,单位为米每秒。\n\n由 SI 对米的定义精确确定。", "associated": false }, { @@ -59,6 +61,7 @@ "name": "GRAVITATIONAL_CONSTANT", "signature": "pub const GRAVITATIONAL_CONSTANT: f64 = 6.674_30e-11;", "doc": "Newtonian constant of gravitation, in cubic meters per kilogram per\nsecond squared.\n\nCODATA 2018 value with its standard uncertainty of\n`1.5e-11 m^3 kg^-1 s^-2`.", + "doc_zh": "牛顿万有引力常数,单位为立方米每千克每二次方秒。\n\nCODATA 2018 推荐值,标准不确定度为 `1.5e-11 m^3 kg^-1 s^-2`。", "associated": false }, { @@ -66,6 +69,7 @@ "name": "BOLTZMANN", "signature": "pub const BOLTZMANN: f64 = 1.380_649e-23;", "doc": "Boltzmann constant, in joules per kelvin.\n\nExact by definition of the kelvin in the SI.", + "doc_zh": "玻尔兹曼常量,单位为焦耳每开尔文。\n\n由 SI 对开尔文的定义精确确定。", "associated": false }, { @@ -73,6 +77,7 @@ "name": "PLANCK", "signature": "pub const PLANCK: f64 = 6.626_070_15e-34;", "doc": "Planck constant, in joule seconds.\n\nExact by definition of the kilogram in the SI.", + "doc_zh": "普朗克常量,单位为焦耳秒。\n\n由 SI 对千克的定义精确确定。", "associated": false }, { @@ -80,6 +85,7 @@ "name": "ELEMENTARY_CHARGE", "signature": "pub const ELEMENTARY_CHARGE: f64 = 1.602_176_634e-19;", "doc": "Elementary charge, in coulombs.\n\nExact by definition of the ampere in the SI.", + "doc_zh": "elementary charge,单位为库仑。\n\n由 SI 对安培的定义精确确定。", "associated": false }, { @@ -87,6 +93,7 @@ "name": "ASTRONOMICAL_UNIT", "signature": "pub const ASTRONOMICAL_UNIT: f64 = 149_597_870_700.0;", "doc": "Astronomical unit, in meters.\n\nExact by IAU 2012 Resolution B2.", + "doc_zh": "天文单位,单位为米。\n\n由 IAU 2012 Resolution B2 精确确定。", "associated": false }, { @@ -94,6 +101,7 @@ "name": "STANDARD_GRAVITY", "signature": "pub const STANDARD_GRAVITY: f64 = 9.806_65;", "doc": "Standard gravity, in meters per second squared.\n\nExact by definition of the standard atmosphere.", + "doc_zh": "标准重力加速度,单位为米每二次方秒。\n\n由标准大气的定义精确确定。", "associated": false }, { @@ -101,6 +109,7 @@ "name": "MINUTE", "signature": "pub const MINUTE: f64 = 60.0;", "doc": "One minute, in seconds.", + "doc_zh": "一分钟,单位为秒。", "associated": false }, { @@ -108,6 +117,7 @@ "name": "HOUR", "signature": "pub const HOUR: f64 = 3_600.0;", "doc": "One hour, in seconds.", + "doc_zh": "一小时,单位为秒。", "associated": false }, { @@ -115,6 +125,7 @@ "name": "DAY", "signature": "pub const DAY: f64 = 86_400.0;", "doc": "One day of 24 hours, in seconds.\n\nThis is the SI day. It is not the mean solar day, which is slightly\nlonger, and not the Julian day of 86 400 seconds used in Julian date\narithmetic, which is identical to this value.", + "doc_zh": "一个 24 小时的日,单位为秒。\n\n这是 SI 日。它不是略长的平均太阳日,也不是儒略日运算中使用的 86 400 秒\n儒略日,后者与本值相同。", "associated": false }, { @@ -122,6 +133,7 @@ "name": "JULIAN_YEAR", "signature": "pub const JULIAN_YEAR: f64 = 31_557_600.0;", "doc": "One Julian year of 365.25 days, in seconds.\n\nExact by definition of the Julian year.", + "doc_zh": "一个 365.25 日的儒略年,单位为秒。\n\n由儒略年的定义精确确定。", "associated": false }, { @@ -129,6 +141,7 @@ "name": "JULIAN_CENTURY", "signature": "pub const JULIAN_CENTURY: f64 = 3_155_760_000.0;", "doc": "One Julian century of 36 525 days, in seconds.\n\nExact by definition of the Julian year, used by precession and secular\nrate expressions quoted per Julian century.", + "doc_zh": "一个 36 525 日的儒略世纪,单位为秒。\n\n由儒略年的定义精确确定;岁差与长期变化率表达式按儒略世纪引用时使用它。", "associated": false }, { @@ -136,6 +149,7 @@ "name": "JULIAN_DATE_UNIX_EPOCH", "signature": "pub const JULIAN_DATE_UNIX_EPOCH: f64 = 2_440_587.5;", "doc": "Julian date of the Unix epoch, 1970-01-01T00:00:00 UTC.\n\nThis is `2 440 587.5` in the Julian date system.", + "doc_zh": "Unix 历元的儒略日,1970-01-01T00:00:00 UTC。\n\n在儒略日体系中该值为 `2 440 587.5`。", "associated": false }, { @@ -143,6 +157,7 @@ "name": "JULIAN_DATE_J2000", "signature": "pub const JULIAN_DATE_J2000: f64 = 2_451_545.0;", "doc": "Julian date of the J2000.0 epoch, 2000-01-01T12:00:00 TT.\n\nExact by definition of the J2000.0 epoch.", + "doc_zh": "J2000.0 历元的儒略日,2000-01-01T12:00:00 TT。\n\n由 J2000.0 历元的定义精确确定。", "associated": false }, { @@ -150,6 +165,7 @@ "name": "SECONDS_J2000_TO_UNIX", "signature": "pub const SECONDS_J2000_TO_UNIX: f64 = (JULIAN_DATE_J2000 - JULIAN_DATE_UNIX_EPOCH) * DAY;", "doc": "The number of seconds between the J2000.0 epoch and the Unix epoch.\n\nDerived from [`JULIAN_DATE_J2000`] and [`JULIAN_DATE_UNIX_EPOCH`].", + "doc_zh": "J2000.0 历元与 Unix 历元之间的秒数。\n\n由 [`JULIAN_DATE_J2000`] 与 [`JULIAN_DATE_UNIX_EPOCH`] 导出。", "associated": false }, { @@ -157,6 +173,7 @@ "name": "EARTH_MU", "signature": "pub const EARTH_MU: f64 = 398_600.441_8;", "doc": "Earth gravitational parameter, in cubic kilometers per second squared.\n\nWGS 84 value, `GM = 398 600.4418 km^3/s^2`, with an uncertainty that is\ndominated by the uncertainty in the Newtonian constant of gravitation.\nOrbit propagation uses kilometers, so the value is given in those units\nrather than in SI.", + "doc_zh": "地球引力参数,单位为立方千米每二次方秒。\n\nWGS 84 取值 `GM = 398 600.4418 km^3/s^2`,其不确定度主要来自牛顿万有引力常数的\n不确定度。轨道传播使用千米,故该值以千米给出而非 SI 单位。", "associated": false }, { @@ -164,6 +181,7 @@ "name": "EARTH_EQUATORIAL_RADIUS", "signature": "pub const EARTH_EQUATORIAL_RADIUS: f64 = 6_378.137;", "doc": "Earth equatorial radius, in kilometers.\n\nWGS 84 semi-major axis, `a = 6 378.137 km`.", + "doc_zh": "地球赤道半径,单位为千米。\n\nWGS 84 半长轴,`a = 6 378.137 km`。", "associated": false }, { @@ -171,6 +189,7 @@ "name": "EARTH_FLATTENING", "signature": "pub const EARTH_FLATTENING: f64 = 1.0 / 298.257_223_563;", "doc": "Earth flattening, dimensionless.\n\nWGS 84 value, `f = 1 / 298.257 223 563`.", + "doc_zh": "地球扁率,无量纲。\n\nWGS 84 取值 `f = 1 / 298.257 223 563`。", "associated": false }, { @@ -178,6 +197,7 @@ "name": "EARTH_ROTATION_RATE", "signature": "pub const EARTH_ROTATION_RATE: f64 = 7.292_115_0e-5;", "doc": "Earth rotation rate, in radians per second.\n\nWGS 84 value. This is the nominal mean angular velocity of the Earth,\n`omega = 7.292 115e-5 rad/s`.", + "doc_zh": "WGS 84 取值,即地球的名义平均角速度,\n`omega = 7.292 115e-5 rad/s`。", "associated": false }, { @@ -185,6 +205,7 @@ "name": "EARTH_J2", "signature": "pub const EARTH_J2: f64 = 1.082_626_683e-3;", "doc": "Earth second zonal harmonic, dimensionless.\n\nWGS 84 value, `J2 = 1.082 626 683e-3`.", + "doc_zh": "WGS 84 取值,`J2 = 1.082 626 683e-3`。", "associated": false }, { @@ -192,6 +213,7 @@ "name": "STANDARD_ATMOSPHERE", "signature": "pub const STANDARD_ATMOSPHERE: f64 = 101_325.0;", "doc": "Standard atmosphere sea level pressure, in pascals.\n\nExact by definition of the standard atmosphere.", + "doc_zh": "标准大气海平面气压,单位为帕斯卡。\n\n由标准大气的定义精确确定。", "associated": false }, { @@ -199,6 +221,7 @@ "name": "ZERO_CELSIUS_IN_KELVIN", "signature": "pub const ZERO_CELSIUS_IN_KELVIN: f64 = 273.15;", "doc": "Zero degrees Celsius on the thermodynamic temperature scale, in kelvin.\n\nExact by definition of the Celsius scale.", + "doc_zh": "热力学温标上零摄氏度对应的温度,单位为开尔文。\n\n由摄氏标度的定义精确确定。", "associated": false }, { @@ -206,6 +229,7 @@ "name": "ErrorKind", "signature": "pub enum ErrorKind {", "doc": "The category of an [`Error`].\n\nThe variants are grouped by the layer that most often produces them, but\nany layer may return any variant. Callers should match on the variant\nthat is relevant to their own recovery logic and treat the rest as\nopaque.", + "doc_zh": "[`Error`] 的类别。\n\n各变体按最常产生它们的层分组,但任何层都可以返回任何变体。调用方应只匹配\n与自身恢复逻辑相关的变体,其余视为不透明。", "associated": false }, { @@ -213,6 +237,7 @@ "name": "Error", "signature": "pub struct Error {", "doc": "The error type used across the workspace.\n\n`Error` carries a machine matchable [`ErrorKind`] plus a human readable\nmessage. An optional source records the lower level error that caused\nthis one.", + "doc_zh": "工作区使用的错误类型。\n\n`Error` 携带可供机器匹配的 [`ErrorKind`] 与一条供人阅读的消息。可选的\nsource 记录导致该错误的底层错误。", "associated": false }, { @@ -220,6 +245,7 @@ "name": "new", "signature": "pub fn new(kind: ErrorKind) -> Self {", "doc": "Creates an error with the given kind and no message.", + "doc_zh": "用给定类别创建一个不带消息的错误。", "associated": true }, { @@ -227,6 +253,7 @@ "name": "with_message", "signature": "pub fn with_message(kind: ErrorKind, message: impl Into) -> Self {", "doc": "Creates an error with the given kind and message.\n\nThe message should describe the concrete cause of this specific\nfailure. It is meant for humans reading logs, not for matching.", + "doc_zh": "用给定类别与消息创建一个错误。\n\n该消息应描述这一具体失败的具体原因。它意在供人阅读日志,而非供匹配。", "associated": true }, { @@ -234,6 +261,7 @@ "name": "with_source", "signature": "pub fn with_source(kind: ErrorKind, source: E) -> Self where E: StdError + Send + Sync + 'static, {", "doc": "Creates an error with the given kind and a source error.", + "doc_zh": "用给定类别与来源错误创建一个错误。", "associated": true }, { @@ -241,6 +269,7 @@ "name": "with_context", "signature": "pub fn with_context(mut self, message: impl Into) -> Self {", "doc": "Attaches a message to this error, replacing any existing message.", + "doc_zh": "为该错误附加消息,替换已有消息。", "associated": true }, { @@ -248,6 +277,7 @@ "name": "caused_by", "signature": "pub fn caused_by(mut self, source: E) -> Self where E: StdError + Send + Sync + 'static, {", "doc": "Attaches a source error, replacing any existing source.", + "doc_zh": "为该错误附加来源错误,替换已有来源。", "associated": true }, { @@ -255,6 +285,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> ErrorKind {", "doc": "Returns the category of this error.", + "doc_zh": "返回该错误的类别。", "associated": true }, { @@ -262,13 +293,15 @@ "name": "message", "signature": "pub fn message(&self) -> Option<&str> {", "doc": "Returns the message attached to this error, if any.", + "doc_zh": "返回附加在该错误上的消息,若无则返回 `None`。", "associated": true }, { "kind": "type", "name": "Result", "signature": "pub type Result = core::result::Result;", - "doc": "A [`Result`](core::result::Result) whose error type is [`Error`].", + "doc": "A [`Result`](core::result::Result) whose error type is [`Error`]", + "doc_zh": "错误类型为 [`Error`] 的 [`Result`](core::result::Result).", "associated": false }, { @@ -276,6 +309,7 @@ "name": "Unit", "signature": "pub trait Unit: Copy + fmt::Debug + fmt::Display + 'static {", "doc": "A unit of a single physical dimension.\n\nImplementors are zero sized marker types. Two units that implement\n`Unit` for the same dimension convert through\n[`Quantity::to_unit`](crate::Quantity::to_unit).", + "doc_zh": "单一物理量纲的单位。\n\n实现者是零尺寸标记类型。对同一量纲实现 `Unit` 的两个单位经\n[`Quantity::to_unit`](crate::Quantity::to_unit) 换算。", "associated": false }, { @@ -283,6 +317,7 @@ "name": "Quantity", "signature": "pub trait Quantity: Copy {", "doc": "A value paired with a unit.\n\nImplementors are newtypes over `f64` and a [`Unit`]. The trait gives\ngeneric code access to the stored value and its unit without assuming\nwhich newtype it was handed.", + "doc_zh": "与单位配对的一个数值。\n\n实现者是包装 `f64` 与 [`Unit`] 的 newtype。该 trait 使泛型代码能访问所存的\n值与其单位,而不假定所接到的是哪个 newtype。", "associated": false } ] @@ -290,13 +325,15 @@ { "name": "apsis-units", "layer": "foundation", - "summary": "Strongly typed newtypes for physical quantities. Each quantity is a newtype over `f64` that carries its unit in the type system, so a length cannot be passed where a duration is expected. Units of the same dimension convert through [`Quantity::to_unit`], and each unit also offers direct conversions for the common cases. A newtype implements both [`Unit`] and [`Quantity`], with its own type as the associated unit. For example [`Meters`] is a [`Length`](dimension::Length) unit and a quantity whose unit is itself. The dimensions covered here are length, time, frequency, angle, and thermodynamic temperature. Temperature is the only affine scale in the set, so Celsius is handled by explicit methods on [`Kelvin`] rather than by the linear [`Unit`] conversion factor. ``` use apsis_core::Quantity; use apsis_units::{Kilometers, Meters}; let distance = Meters::new(2_500.0); let in_kilometers: Kilometers = distance.to_unit(); assert!((in_kilometers.value() - 2.5).abs() < 1e-12); ```", + "summary": "Strongly typed newtypes for physical quantities.\n\nEach quantity is a newtype over `f64` that carries its unit in the type\nsystem, so a length cannot be passed where a duration is expected. Units\nof the same dimension convert through\n[`Quantity::to_unit`], and each unit also\noffers direct conversions for the common cases.", + "summary_zh": "物理量的强类型 newtype。\n\n每个量都是 `f64` 上的 newtype,并把单位带进类型系统,因此长度无法被传到\n需要时长的位置。同一量纲的单位通过 [`Quantity::to_unit`] 换算,每个单位\n另外为常见情形提供直接换算。\n\nA newtype implements both [`Unit`] and [`Quantity`], with its own type as\nthe associated unit. For example [`Meters`] is a [`Length`](dimension::Length)\nunit and a quantity whose unit is itself.\n\nThe dimensions covered here are length, time, frequency, angle, and\nthermodynamic temperature. Temperature is the only affine scale in the\nset, so Celsius is handled by explicit methods on [`Kelvin`] rather than\nby the linear [`Unit`] conversion factor.\n\n一个 newtype 同时实现 [`Unit`] 与 [`Quantity`],并以自身类型作为关联单位。\n例如 [`Meters`] 既是一个 [`Length`](dimension::Length) 单位,也是单位为\n自身的量。\n\n此处覆盖的量纲为长度、时间、频率、角度与热力学温度。温度是集合中唯一的\n仿射标度,因此摄氏度由 [`Kelvin`] 上的显式方法处理,而不用 [`Unit`] 的\n线性换算因子。\n\n```\nuse apsis_core::Quantity;\nuse apsis_units::{Kilometers, Meters};\n\nlet distance = Meters::new(2_500.0);\nlet in_kilometers: Kilometers = distance.to_unit();\nassert!((in_kilometers.value() - 2.5).abs() < 1e-12);\n```", "items": [ { "kind": "struct", "name": "Radians", "signature": "pub struct Radians(f64);", "doc": "An angle in radians.", + "doc_zh": "以弧度表示的角度。", "associated": false }, { @@ -304,6 +341,7 @@ "name": "Degrees", "signature": "pub struct Degrees(f64);", "doc": "An angle in degrees.", + "doc_zh": "以度表示的角度。", "associated": false }, { @@ -311,6 +349,7 @@ "name": "new", "signature": "pub const fn new(value: f64) -> Self {", "doc": "Creates an angle from a value in radians.", + "doc_zh": "由弧度值创建角度。", "associated": true }, { @@ -318,6 +357,7 @@ "name": "value", "signature": "pub const fn value(self) -> f64 {", "doc": "Returns this angle in radians.", + "doc_zh": "返回该角度的弧度值。", "associated": true }, { @@ -325,6 +365,7 @@ "name": "to_degrees", "signature": "pub fn to_degrees(self) -> Degrees {", "doc": "Converts this angle to degrees.", + "doc_zh": "把该角度换算为度。", "associated": true }, { @@ -332,6 +373,7 @@ "name": "wrap", "signature": "pub fn wrap(self) -> Self {", "doc": "Returns this angle wrapped to the half-open interval `[-pi, pi)`.\n\nWrapping uses [`f64::rem_euclid`](f64::rem_euclid) so the result is\nwell defined for negative inputs as well as large ones.", + "doc_zh": "返回该角度归约到半开区间 `[-pi, pi)` 后的值。\n\n归约使用 [`f64::rem_euclid`](f64::rem_euclid),因此对负输入与很大输入\n都有良好定义。", "associated": true }, { @@ -339,6 +381,7 @@ "name": "to_radians", "signature": "pub fn to_radians(self) -> Radians {", "doc": "Converts this angle to radians.", + "doc_zh": "把该角度换算为弧度。", "associated": true }, { @@ -346,6 +389,7 @@ "name": "Length", "signature": "pub struct Length;", "doc": "Length.", + "doc_zh": "长度。", "associated": false }, { @@ -353,6 +397,7 @@ "name": "Time", "signature": "pub struct Time;", "doc": "Duration.", + "doc_zh": "时间。", "associated": false }, { @@ -360,6 +405,7 @@ "name": "Frequency", "signature": "pub struct Frequency;", "doc": "Frequency, the reciprocal of time.", + "doc_zh": "频率,时间的倒数。", "associated": false }, { @@ -367,6 +413,7 @@ "name": "Angle", "signature": "pub struct Angle;", "doc": "Plane angle.", + "doc_zh": "平面角。", "associated": false }, { @@ -374,6 +421,7 @@ "name": "Temperature", "signature": "pub struct Temperature;", "doc": "Thermodynamic temperature.", + "doc_zh": "热力学温度。", "associated": false }, { @@ -381,6 +429,7 @@ "name": "Hertz", "signature": "pub struct Hertz(f64);", "doc": "A frequency in hertz.", + "doc_zh": "以赫兹表示的频率。", "associated": false }, { @@ -388,6 +437,7 @@ "name": "period", "signature": "pub fn period(self) -> f64 {", "doc": "Returns the period corresponding to this frequency, in seconds.\n\nReturns `f64::INFINITY` when the frequency is zero, which is the\ncorrect limit of an infinite period.", + "doc_zh": "返回与该频率对应的周期,单位为秒。\n\n频率为零时返回 `f64::INFINITY`,这正是周期趋于无穷的正确极限。", "associated": true }, { @@ -395,6 +445,7 @@ "name": "Meters", "signature": "pub struct Meters(f64);", "doc": "A length in meters.", + "doc_zh": "以米为单位的长度。", "associated": false }, { @@ -402,6 +453,7 @@ "name": "Kilometers", "signature": "pub struct Kilometers(f64);", "doc": "A length in kilometers.", + "doc_zh": "以千米为单位的长度。", "associated": false }, { @@ -409,6 +461,7 @@ "name": "to_kilometers", "signature": "pub fn to_kilometers(self) -> Kilometers {", "doc": "Converts this length to kilometers.", + "doc_zh": "把该长度换算为千米。", "associated": true }, { @@ -416,13 +469,15 @@ "name": "to_meters", "signature": "pub fn to_meters(self) -> Meters {", "doc": "Converts this length to meters.", + "doc_zh": "把该长度换算为米。", "associated": true }, { "kind": "struct", "name": "Kelvin", "signature": "pub struct Kelvin(f64);", - "doc": "A thermodynamic temperature in kelvin.\n\nValues below absolute zero are not rejected, because intermediate\narithmetic may legitimately pass through them; callers that require a\nphysical temperature should check the value.", + "doc": "A thermodynamic temperature in kelvin.", + "doc_zh": "以开尔文表示的热力学温度。\n\nValues below absolute zero are not rejected, because intermediate\narithmetic may legitimately pass through them; callers that require a\nphysical temperature should check the value.\n\n不拒绝低于绝对零的值,因为中间运算可能合法地经过该区域;需要物理温度的调用方\n应自行检查。", "associated": false }, { @@ -430,6 +485,7 @@ "name": "from_celsius", "signature": "pub fn from_celsius(celsius: f64) -> Self {", "doc": "Creates a temperature from a value in degrees Celsius.\n\nUses the exact offset defined by the Celsius scale.", + "doc_zh": "由摄氏度值创建温度。\n\n使用摄氏标度所定义的精确偏移。", "associated": true }, { @@ -437,6 +493,7 @@ "name": "to_celsius", "signature": "pub fn to_celsius(self) -> f64 {", "doc": "Converts this temperature to degrees Celsius.", + "doc_zh": "把该温度换算为摄氏度。", "associated": true }, { @@ -444,6 +501,7 @@ "name": "Seconds", "signature": "pub struct Seconds(f64);", "doc": "A duration in seconds.", + "doc_zh": "以秒为单位的时长。", "associated": false } ] @@ -451,13 +509,15 @@ { "name": "apsis-math", "layer": "foundation", - "summary": "Dependency-free linear algebra for the ApsisRS workspace. The crate provides three types: - [`Vec3`], a three component column vector over `f64` - [`Quat`], a unit quaternion used for rotations - [`Mat3`], a three by three matrix stored in column-major order The types are plain data with no interior invariants beyond those documented on each method, so their fields are public and a value can be built by struct literal expression. Operations that are mathematically undefined for some inputs, such as normalizing the zero vector, return [`Option`] instead of panicking or producing a non-finite result. All rotations use the right-hand rule about the given axis, and all angles are in radians. Angles are not wrapped on input; methods that require a canonical range document that requirement. ``` use apsis_math::Vec3; let x = Vec3::new(1.0, 0.0, 0.0); let y = Vec3::new(0.0, 1.0, 0.0); let z = x.cross(y); assert_eq!(z, Vec3::new(0.0, 0.0, 1.0)); ```", + "summary": "Dependency-free linear algebra for the ApsisRS workspace.\n\nThe crate provides three types:\n\n- [`Vec3`], a three component column vector over `f64`\n- [`Quat`], a unit quaternion used for rotations\n- [`Mat3`], a three by three matrix stored in column-major order\n\nThe types are plain data with no interior invariants beyond those\ndocumented on each method, so their fields are public and a value can be\nbuilt by struct literal expression. Operations that are mathematically\nundefined for some inputs, such as normalizing the zero vector, return\n[`Option`] instead of panicking or producing a non-finite result.\n\nAll rotations use the right-hand rule about the given axis, and all\nangles are in radians. Angles are not wrapped on input; methods that\nrequire a canonical range document that requirement.\n\n```\nuse apsis_math::Vec3;\n\nlet x = Vec3::new(1.0, 0.0, 0.0);\nlet y = Vec3::new(0.0, 1.0, 0.0);\nlet z = x.cross(y);\nassert_eq!(z, Vec3::new(0.0, 0.0, 1.0));\n```", + "summary_zh": "ApsisRS 工作区无依赖的线性代数。\n\n本 crate 提供三种类型:\n\n- [`Vec3`],基于 `f64` 的三分量列矢量\n- [`Quat`],用于转动的单位四元数\n- [`Mat3`],按列主序存储的三阶矩阵\n\n这些类型是纯数据,除各方法上所述外没有内部不变量,故其字段是公开的,\n值可由结构体字面量表达式构建。对某些输入在数学上未定义的操作(如把零\n矢量归一化)返回 [`Option`],而不 panic 或产生非有限结果。\n\n所有转动都绕给定轴采用右手定则,所有角度都以弧度计。输入时角度不进行\n环绕;要求取规范区间的方法会说明该要求。", "items": [ { "kind": "struct", "name": "Mat3", "signature": "pub struct Mat3 {", "doc": "A three by three matrix stored in column-major order.\n\nThe field names follow the `m` convention, so `m01` is the\nentry in the first row and second column. The fields are listed in\ncolumn-major order, and the same order is used by [`Mat3::from_columns`]\nand [`Mat3::column`].", + "doc_zh": "按列主序存储的三乘三矩阵。\n\n字段名遵循 `m<行><列>` 约定,故 `m01` 是第一行第二列的元素。字段按列主序\n列出,[`Mat3::from_columns`] 与 [`Mat3::column`] 使用同样的顺序。", "associated": false }, { @@ -465,6 +525,7 @@ "name": "ZERO", "signature": "pub const ZERO: Self = Self::from_rows_raw([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);", "doc": "The zero matrix.", + "doc_zh": "零矩阵。", "associated": true }, { @@ -472,6 +533,7 @@ "name": "IDENTITY", "signature": "pub const IDENTITY: Self = Self::from_rows_raw([1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]);", "doc": "The identity matrix.", + "doc_zh": "单位矩阵。", "associated": true }, { @@ -479,6 +541,7 @@ "name": "from_rows", "signature": "pub const fn from_rows(row0: [f64; 3], row1: [f64; 3], row2: [f64; 3]) -> Self {", "doc": "Creates a matrix from its nine entries in row-major order.\n\nThe arguments are grouped by row: `row0`, then `row1`, then `row2`.", + "doc_zh": "由九个元素按行主序创建矩阵。\n\n参数按行分组:先 `row0`,再 `row1`,最后 `row2`。", "associated": true }, { @@ -486,6 +549,7 @@ "name": "from_columns", "signature": "pub const fn from_columns(c0: Vec3, c1: Vec3, c2: Vec3) -> Self {", "doc": "Creates a matrix from three column vectors.", + "doc_zh": "由三个列向量创建矩阵。", "associated": true }, { @@ -493,6 +557,7 @@ "name": "from_diagonal", "signature": "pub const fn from_diagonal(diagonal: Vec3) -> Self {", "doc": "Creates a diagonal matrix from a vector of diagonal entries.", + "doc_zh": "由对角元素向量创建对角矩阵。", "associated": true }, { @@ -500,6 +565,7 @@ "name": "from_quat", "signature": "pub fn from_quat(q: Quat) -> Self {", "doc": "Creates a rotation matrix from a unit quaternion.\n\nThe quaternion is assumed to have unit length. A non-unit quaternion\nproduces a scaled matrix, which [`Mat3::is_rotation`] then rejects.", + "doc_zh": "由单位四元数创建旋转矩阵。\n\n假定该四元数为单位长度。非单位四元数会得到一个带缩放的矩阵,随后被\n[`Mat3::is_rotation`] 拒绝。", "associated": true }, { @@ -507,6 +573,7 @@ "name": "from_axis_angle", "signature": "pub fn from_axis_angle(axis: Vec3, angle: f64) -> Option {", "doc": "Creates a rotation matrix of `angle` radians about the unit axis\n`axis`.\n\nReturns `None` when the axis has zero or non-finite length.", + "doc_zh": "创建绕单位轴 `axis` 旋转 `angle` 弧度的旋转矩阵。\n\n轴长度为零或非有限时返回 `None`。", "associated": true }, { @@ -514,6 +581,7 @@ "name": "from_rotation_x", "signature": "pub fn from_rotation_x(angle: f64) -> Self {", "doc": "Creates a rotation matrix of `angle` radians about the first axis.", + "doc_zh": "创建绕第一轴旋转 `angle` 弧度的旋转矩阵。", "associated": true }, { @@ -521,6 +589,7 @@ "name": "from_rotation_y", "signature": "pub fn from_rotation_y(angle: f64) -> Self {", "doc": "Creates a rotation matrix of `angle` radians about the second axis.", + "doc_zh": "创建绕第二轴旋转 `angle` 弧度的旋转矩阵。", "associated": true }, { @@ -528,6 +597,7 @@ "name": "from_rotation_z", "signature": "pub fn from_rotation_z(angle: f64) -> Self {", "doc": "Creates a rotation matrix of `angle` radians about the third axis.", + "doc_zh": "创建绕第三轴旋转 `angle` 弧度的旋转矩阵。", "associated": true }, { @@ -535,6 +605,7 @@ "name": "get", "signature": "pub const fn get(self, row: usize, column: usize) -> Option {", "doc": "Returns the entry in row `row` and column `column`.\n\nReturns `None` when either index is outside `0..=2`.", + "doc_zh": "返回第 `row` 行第 `column` 列的元素。\n\n任一下标超出 `0..=2` 时返回 `None`。", "associated": true }, { @@ -542,6 +613,7 @@ "name": "column", "signature": "pub const fn column(self, index: usize) -> Option {", "doc": "Returns column `index`.\n\nReturns `None` when `index` is outside `0..=2`.", + "doc_zh": "返回第 `index` 列。\n\n`index` 超出 `0..=2` 时返回 `None`。", "associated": true }, { @@ -549,6 +621,7 @@ "name": "row", "signature": "pub const fn row(self, index: usize) -> Option {", "doc": "Returns row `index`.\n\nReturns `None` when `index` is outside `0..=2`.", + "doc_zh": "返回第 `index` 行。\n\n`index` 超出 `0..=2` 时返回 `None`。", "associated": true }, { @@ -556,6 +629,7 @@ "name": "transpose", "signature": "pub const fn transpose(self) -> Self {", "doc": "Returns the transpose.", + "doc_zh": "返回转置。", "associated": true }, { @@ -563,6 +637,7 @@ "name": "trace", "signature": "pub const fn trace(self) -> f64 {", "doc": "Returns the sum of the diagonal entries.", + "doc_zh": "返回对角元素之和。", "associated": true }, { @@ -570,6 +645,7 @@ "name": "determinant", "signature": "pub fn determinant(self) -> f64 {", "doc": "Returns the determinant.", + "doc_zh": "返回行列式。", "associated": true }, { @@ -577,6 +653,7 @@ "name": "is_rotation", "signature": "pub fn is_rotation(self, tolerance: f64) -> bool {", "doc": "Returns `true` when this matrix is a rotation within `tolerance`.\n\nA matrix is a rotation when it is orthonormal with determinant one.\nThe check compares `M^T M` to the identity entry by entry and then\ncompares the determinant to one.", + "doc_zh": "当该矩阵在 `tolerance` 内为旋转矩阵时返回 `true`。\n\n正交且行列式为一的矩阵即旋转矩阵。该检查逐元素比较 `M^T M` 与单位\n矩阵,然后把行列式与一比较。", "associated": true }, { @@ -584,6 +661,7 @@ "name": "is_finite", "signature": "pub fn is_finite(self) -> bool {", "doc": "Returns `true` when all nine entries are finite.", + "doc_zh": "当九个元素都有限时返回 `true`。", "associated": true }, { @@ -591,6 +669,7 @@ "name": "thread_count", "signature": "pub fn thread_count() -> usize {", "doc": "The number of threads to use for a piece of work.\n\nThis is the machine's parallelism as reported by the standard library, at\nleast one. It is the number the callers use to decide whether a parallel\nrun is worth starting.", + "doc_zh": "某项工作应使用的线程数。\n\n这是标准库报告的机器并行度,至少为一。调用方据此决定并行运行是否值得启动。", "associated": false }, { @@ -598,6 +677,7 @@ "name": "threads_for", "signature": "pub fn threads_for(len: usize) -> usize {", "doc": "The number of threads that should run a loop over `len` items.\n\nA loop shorter than the thread count would leave most threads with nothing\nto do while still paying to start them, so this returns one thread until\nthere is enough work for a second. The threshold is a per item cost of\nroughly a microsecond, below which starting a thread cannot pay for\nitself.", + "doc_zh": "对 `len` 个元素的循环应使用的线程数。\n\n比线程数还短的循环会让多数线程无所事事,却仍要付出启动代价,因此在工作量\n足以分给第二个线程之前本函数返回一个线程。阈值是每项约一微秒的开销,低于\n此值启动线程无法收回成本。", "associated": false }, { @@ -605,6 +685,7 @@ "name": "for_each_index", "signature": "pub fn for_each_index(len: usize, operation: F) where F: Fn(usize) + Sync, {", "doc": "Runs a closure once for each index of a range, spread over several threads.\n\nThe range is split into one contiguous chunk per thread and each thread\nruns the closure for the indices in its chunk. The closure may be called\nfrom any thread, so it must be `Send` and `Sync`; anything it captures is\nshared rather than copied. The call returns only after every thread has\nfinished, so the data the closure wrote is safe to read afterwards.\n\nThe closure is handed the index rather than the element, which keeps the\nsignature independent of the collection: the caller reads the input and\nwrites the output itself, and the two never overlap.\n\n# Panics\n\nIf the closure panics, the panic is resumed on the calling thread after\nthe other threads have been joined, so a failure surfaces at the call\nrather than in a detached thread.", + "doc_zh": "对一段范围内的每个下标各调用一次闭包,分摊到多个线程上执行。\n\n该范围被切分为每线程一段连续区间,每个线程对落在自己区间内的下标调用闭包。\n闭包可能在任意线程上被调用,故必须是 `Send` 与 `Sync`;它捕获的数据是被共享\n而非复制的。本次调用只有在全部线程结束后才返回,因此闭包写入的数据此后可安全\n读取。\n\n闭包收到的是下标而非元素,这让签名与具体集合无关:调用方自行读取输入并\n写出输出,两者不会重叠。\n\n# Panics\n\n若闭包发生 panic,该 panic 会在其它线程 join 之后于调用线程上重新抛出,\n因此失败出现在调用处,而不是某个已分离的线程中。", "associated": false }, { @@ -612,6 +693,7 @@ "name": "map_index", "signature": "pub fn map_index(len: usize, operation: F) -> Vec where T: Send, F: Fn(usize) -> T + Sync, {", "doc": "Collects one value for each index of a range, spread over several threads.\n\nThis is [`for_each_index`] for the common case where the result of the\nwork is a value rather than a write into a shared array. The output is\nreturned in index order regardless of which thread produced which element,\nso it does not depend on the scheduling.\n\nThe closure is handed the index and returns the value for that index. As\nwith [`for_each_index`], it may run on any thread, so captured data must be\n`Sync`.", + "doc_zh": "为一段范围内的每个下标各收集一个值,分摊到多个线程上执行。\n\n当工作的结果是值而非写入共享数组时,本函数是 [`for_each_index`] 的对应形式。\n输出按下标顺序返回,与哪个线程产生了哪个元素无关,故不依赖调度。\n\n闭包收到下标并返回该下标对应的值。与 [`for_each_index`] 一样,它可能在任意\n线程上运行,故捕获的数据必须为 `Sync`。", "associated": false }, { @@ -619,6 +701,7 @@ "name": "Quat", "signature": "pub struct Quat {", "doc": "A quaternion `w + xi + yj + zk`.\n\nMethods that rotate vectors assume the quaternion has unit length.\nMultiplication and addition do not, because arbitrary quaternion\narithmetic is well defined without that assumption. Use\n[`Quat::normalize`] to enforce unit length, and\n[`Quat::is_normalized`] to check it.\n\nA rotation by angle `theta` about a unit axis `u` is\n`w = cos(theta / 2)`, `(x, y, z) = u * sin(theta / 2)`. Composition uses\nthe same convention as matrix multiplication: `(a * b)` applied to a\nvector applies `b` first, then `a`.", + "doc_zh": "四元数 `w + xi + yj + zk`。\n\n旋转向量的方法假定该四元数为单位长度。乘法与加法则不然,因为任意四元数\n运算在该假定下同样有良好定义。用 [`Quat::normalize`] 归一到单位长度,用\n[`Quat::is_normalized`] 检查。\n\n绕单位轴 `u` 旋转 `theta` 的四元数为\n`w = cos(theta / 2)`、`(x, y, z) = u * sin(theta / 2)`。复合采用与矩阵\n乘法相同的约定:`(a * b)` 作用于向量时先作用 `b`,再作用 `a`。", "associated": false }, { @@ -626,6 +709,7 @@ "name": "IDENTITY", "signature": "pub const IDENTITY: Self = Self::new(1.0, 0.0, 0.0, 0.0);", "doc": "The identity rotation.", + "doc_zh": "恒等旋转。", "associated": true }, { @@ -633,6 +717,7 @@ "name": "new", "signature": "pub const fn new(w: f64, x: f64, y: f64, z: f64) -> Self {", "doc": "Creates a quaternion from its four components.", + "doc_zh": "由四个分量创建四元数。", "associated": true }, { @@ -640,6 +725,7 @@ "name": "from_mat3", "signature": "pub fn from_mat3(m: crate::mat3::Mat3) -> Option {", "doc": "Creates a quaternion from the rotation matrix `m`.\n\nThe algorithm picks the largest diagonal entry to divide by, which\nkeeps the square root well conditioned for every input rotation.\nReturns `None` when `m` is not a rotation matrix, that is when it is\nnot orthonormal with determinant one within a tolerance of `1e-9`.", + "doc_zh": "由旋转矩阵 `m` 创建四元数。\n\n算法选取对角线上最大的元素作除数,从而对任何输入旋转都保持开方的良态。\n当 `m` 不是旋转矩阵时返回 `None`,即在 `1e-9` 的容差内不正交或行列式\n不为一。", "associated": true }, { @@ -647,6 +733,7 @@ "name": "vector", "signature": "pub const fn vector(self) -> Vec3 {", "doc": "Returns the vector part `(x, y, z)`.", + "doc_zh": "返回向量部分 `(x, y, z)`。", "associated": true }, { @@ -654,6 +741,7 @@ "name": "norm_squared", "signature": "pub fn norm_squared(self) -> f64 {", "doc": "Returns the squared length of this quaternion.", + "doc_zh": "返回该四元数的长度平方。", "associated": true }, { @@ -661,6 +749,7 @@ "name": "norm", "signature": "pub fn norm(self) -> f64 {", "doc": "Returns the length of this quaternion.", + "doc_zh": "返回该四元数的长度。", "associated": true }, { @@ -668,6 +757,7 @@ "name": "normalize", "signature": "pub fn normalize(self) -> Option {", "doc": "Returns this quaternion scaled to unit length.\n\nReturns `None` when the length is zero or not finite.", + "doc_zh": "返回该四元数缩放到单位长度后的结果。\n\n长度为零或非有限时返回 `None`。", "associated": true }, { @@ -675,6 +765,7 @@ "name": "conjugate", "signature": "pub const fn conjugate(self) -> Self {", "doc": "Returns the conjugate `w - xi - yj - zk`.\n\nFor a unit quaternion this is the inverse rotation.", + "doc_zh": "返回共轭 `w - xi - yj - zk`。\n\n对单位四元数,它即逆旋转。", "associated": true }, { @@ -682,6 +773,7 @@ "name": "inverse", "signature": "pub fn inverse(self) -> Option {", "doc": "Returns the inverse rotation.\n\nReturns `None` when the length is zero or not finite.", + "doc_zh": "返回逆旋转。\n\n长度为零或非有限时返回 `None`。", "associated": true }, { @@ -689,6 +781,7 @@ "name": "dot", "signature": "pub fn dot(self, rhs: Self) -> f64 {", "doc": "Returns the dot product of `self` and `rhs`, treating both as four\ncomponent vectors.", + "doc_zh": "把两者都视为四分量向量,返回 `self` 与 `rhs` 的点积。", "associated": true }, { @@ -696,6 +789,7 @@ "name": "rotate", "signature": "pub fn rotate(self, v: Vec3) -> Vec3 {", "doc": "Rotates `v` by this rotation.\n\nAssumes this quaternion has unit length. The rotation is computed as\n`v + 2 * cross(vector, cross(vector, v) + w * v)`, which avoids\nforming the quaternion product matrix twice.", + "doc_zh": "用该旋转对 `v` 施以旋转。\n\n假定该四元数为单位长度。旋转按\n`v + 2 * cross(vector, cross(vector, v) + w * v)` 计算,避免两次构造\n四元数乘积矩阵。", "associated": true }, { @@ -703,6 +797,7 @@ "name": "angle", "signature": "pub fn angle(self) -> f64 {", "doc": "Returns the rotation angle in radians, in `[0, pi]`.\n\nAssumes this quaternion has unit length.", + "doc_zh": "返回旋转角,单位为弧度,位于 `[0, pi]`。\n\n假定该四元数为单位长度。", "associated": true }, { @@ -710,6 +805,7 @@ "name": "axis", "signature": "pub fn axis(self) -> Option {", "doc": "Returns the rotation axis as a unit vector.\n\nReturns `None` for the identity rotation, where no axis is defined.\nAssumes this quaternion has unit length.", + "doc_zh": "返回旋转轴的单位向量。\n\n对恒等旋转返回 `None`,因为此时旋转轴未定义。假定该四元数为单位长度。", "associated": true }, { @@ -717,6 +813,7 @@ "name": "lerp", "signature": "pub fn lerp(self, rhs: Self, t: f64) -> Self {", "doc": "Returns the linear interpolation from `self` to `rhs`.\n\nThis is a chord interpolation, so the result is not a unit\nquaternion and the angular speed is not constant. Use\n[`Quat::slerp`] when the intermediate rotations must be uniform.", + "doc_zh": "返回从 `self` 到 `rhs` 的线性插值。\n\n这是弦插值,故结果不是单位四元数,角速度也不恒定。当中间旋转必须均匀时\n请用 [`Quat::slerp`]。", "associated": true }, { @@ -724,6 +821,7 @@ "name": "slerp", "signature": "pub fn slerp(self, rhs: Self, t: f64) -> Self {", "doc": "Returns the spherical linear interpolation from `self` to `rhs`.\n\nThe shortest of the two arcs is taken, so the result follows the\npath a rotation would take if the angle were kept minimal. Falls\nback to [`Quat::lerp`] when the inputs are nearly parallel, where\nthe sine in the denominator would otherwise lose precision.", + "doc_zh": "返回从 `self` 到 `rhs` 的球面线性插值。\n\n取两段圆弧中较短者,故结果沿角度最小的旋转路径。当输入近乎平行时退回\n[`Quat::lerp`],此时分母中的正弦会损失精度。", "associated": true }, { @@ -731,6 +829,7 @@ "name": "is_normalized", "signature": "pub fn is_normalized(self) -> bool {", "doc": "Returns `true` when the length is one within a tolerance of `1e-9`.", + "doc_zh": "当长度在 `1e-9` 容差内为一时返回 `true`。", "associated": true }, { @@ -738,6 +837,7 @@ "name": "quarter_turn_x", "signature": "pub fn quarter_turn_x() -> Self {", "doc": "Returns a rotation of a quarter turn about the first axis.", + "doc_zh": "返回绕第一轴旋转四分之一周的四元数。", "associated": true }, { @@ -745,6 +845,7 @@ "name": "half_turn_z", "signature": "pub fn half_turn_z() -> Self {", "doc": "Returns a rotation of a half turn about the third axis.", + "doc_zh": "返回绕第三轴旋转半周的四元数。", "associated": true }, { @@ -752,6 +853,7 @@ "name": "Vec3", "signature": "pub struct Vec3 {", "doc": "A three component column vector.\n\nThe components are named `x`, `y`, and `z`. No meaning is attached to\nthem here; the reference frame is defined by the crate that uses the\nvector.", + "doc_zh": "三分量列向量。\n\n分量名为 `x`、`y`、`z`。此处不赋予它们任何含义;参考系由使用该向量的\ncrate 定义。", "associated": false }, { @@ -759,6 +861,7 @@ "name": "ZERO", "signature": "pub const ZERO: Self = Self::new(0.0, 0.0, 0.0);", "doc": "The zero vector.", + "doc_zh": "零向量。", "associated": true }, { @@ -766,6 +869,7 @@ "name": "X", "signature": "pub const X: Self = Self::new(1.0, 0.0, 0.0);", "doc": "The unit vector along the first axis.", + "doc_zh": "沿第一轴的单位向量。", "associated": true }, { @@ -773,6 +877,7 @@ "name": "Y", "signature": "pub const Y: Self = Self::new(0.0, 1.0, 0.0);", "doc": "The unit vector along the second axis.", + "doc_zh": "沿第二轴的单位向量。", "associated": true }, { @@ -780,6 +885,7 @@ "name": "Z", "signature": "pub const Z: Self = Self::new(0.0, 0.0, 1.0);", "doc": "The unit vector along the third axis.", + "doc_zh": "沿第三轴的单位向量。", "associated": true }, { @@ -787,6 +893,7 @@ "name": "new", "signature": "pub const fn new(x: f64, y: f64, z: f64) -> Self {", "doc": "Creates a vector from its three components.", + "doc_zh": "由三个分量创建向量。", "associated": true }, { @@ -794,6 +901,7 @@ "name": "splat", "signature": "pub const fn splat(value: f64) -> Self {", "doc": "Creates a vector whose three components all equal `value`.", + "doc_zh": "创建三个分量都等于 `value` 的向量。", "associated": true }, { @@ -801,6 +909,7 @@ "name": "x", "signature": "pub const fn x(self) -> f64 {", "doc": "Returns the first component.", + "doc_zh": "返回第一分量。", "associated": true }, { @@ -808,6 +917,7 @@ "name": "y", "signature": "pub const fn y(self) -> f64 {", "doc": "Returns the second component.", + "doc_zh": "返回第二分量。", "associated": true }, { @@ -815,6 +925,7 @@ "name": "z", "signature": "pub const fn z(self) -> f64 {", "doc": "Returns the third component.", + "doc_zh": "返回第三分量。", "associated": true }, { @@ -822,6 +933,7 @@ "name": "get", "signature": "pub const fn get(self, index: usize) -> Option {", "doc": "Returns the component selected by `index`, where `0`, `1`, and `2`\nselect `x`, `y`, and `z` respectively.\n\nReturns `None` for an index outside `0..=2`.", + "doc_zh": "返回由 `index` 选定的分量,其中 `0`、`1`、`2` 分别对应 `x`、`y`、`z`。\n\n`index` 超出 `0..=2` 时返回 `None`。", "associated": true }, { @@ -829,6 +941,7 @@ "name": "cross", "signature": "pub fn cross(self, rhs: Self) -> Self {", "doc": "Returns the cross product `self x rhs`.\n\nThe result is perpendicular to both inputs and follows the\nright-hand rule, so `Vec3::X.cross(Vec3::Y)` equals `Vec3::Z`.", + "doc_zh": "返回叉积 `self x rhs`。\n\n结果同时垂直于两个输入并遵循右手定则,因此 `Vec3::X.cross(Vec3::Y)`\n等于 `Vec3::Z`。", "associated": true }, { @@ -836,6 +949,7 @@ "name": "distance", "signature": "pub fn distance(self, rhs: Self) -> f64 {", "doc": "Returns the Euclidean distance between `self` and `rhs`.", + "doc_zh": "返回 `self` 与 `rhs` 之间的欧几里得距离。", "associated": true }, { @@ -843,6 +957,7 @@ "name": "distance_squared", "signature": "pub fn distance_squared(self, rhs: Self) -> f64 {", "doc": "Returns the squared Euclidean distance between `self` and `rhs`.", + "doc_zh": "返回 `self` 与 `rhs` 之间的欧几里得距离平方。", "associated": true }, { @@ -850,6 +965,7 @@ "name": "angle_between", "signature": "pub fn angle_between(self, rhs: Self) -> Option {", "doc": "Returns the angle between `self` and `rhs`, in radians.\n\nThe result lies in `[0, pi]`. Returns `None` when either vector has\nzero or non-finite length, or when either vector has a non-finite\ncomponent.", + "doc_zh": "结果位于 `[0, pi]`。当任一向量长度为零或非有限,或任一向量含非有限\n分量时返回 `None`。\n\n返回 `self` 与 `rhs` 之间的夹角,单位为弧度。\n\n结果位于 `[0, pi]`。当任一向量长度为零或非有限,或任一向量含非有限分量\n时返回 `None`。", "associated": true }, { @@ -857,6 +973,7 @@ "name": "abs", "signature": "pub fn abs(self) -> Self {", "doc": "Returns a vector with each component replaced by its absolute value.", + "doc_zh": "返回各分量取绝对值后的向量。", "associated": true }, { @@ -864,6 +981,7 @@ "name": "max_component", "signature": "pub fn max_component(self) -> f64 {", "doc": "Returns the largest component.", + "doc_zh": "返回最大分量。", "associated": true }, { @@ -871,6 +989,7 @@ "name": "min_component", "signature": "pub fn min_component(self) -> f64 {", "doc": "Returns the smallest component.", + "doc_zh": "返回最小分量。", "associated": true } ] @@ -878,19 +997,22 @@ { "name": "apsis-macros", "layer": "foundation", - "summary": "Macros that support the ApsisRS workspace. This crate currently defines no macros. It exists so that the workspace has a single place for macro definitions if a need arises, and so that adding one later does not require a new crate. The decision to leave it empty follows from what the workspace actually needs. Rust's built in `macro_rules!` can already do everything the foundation layer requires: - Substituting a type and a literal to build a quantity, as `quantity!(Meters, 1.5)`. - Dispatching on a trailing token to accept a unit suffix, as `to_meters!(2.5 km)`. Both forms are demonstrated by the tests in `tests/probe.rs`. They can be written directly in the crate that uses them, so no shared macro crate is needed. Two limits are worth recording, because they explain why a macro based unit system is not provided: - A `macro_rules!` macro cannot be generic over a unit suffix that is not an identifier. `1.5 m` and `1.5 km` are accepted by separate rules, so the macro must enumerate every suffix it supports. The unit newtypes in `apsis-units` already give compile time unit safety without that list, so the macro would add surface area without adding checking. - A declarative macro cannot inspect the value of its arguments, so it cannot fold a unit conversion at compile time beyond what constant evaluation already does. A literal such as `2.5 * 1000.0` is folded by the compiler anyway. If the workspace later needs macros that generate items, such as deriving a set of conversions from one definition, that is the point at which this crate should gain its first macro.", + "summary": "Macros that support the ApsisRS workspace.\n\nThis crate currently defines no macros. It exists so that the workspace\nhas a single place for macro definitions if a need arises, and so that\nadding one later does not require a new crate.\n\nThe decision to leave it empty follows from what the workspace actually\nneeds. Rust's built in `macro_rules!` can already do everything the\nfoundation layer requires:\n\n- Substituting a type and a literal to build a quantity, as\n `quantity!(Meters, 1.5)`.\n- Dispatching on a trailing token to accept a unit suffix, as\n `to_meters!(2.5 km)`.\n\nBoth forms are demonstrated by the tests in `tests/probe.rs`. They can be\nwritten directly in the crate that uses them, so no shared macro crate is\nneeded.\n\nTwo limits are worth recording, because they explain why a macro based\nunit system is not provided:\n\n- A `macro_rules!` macro cannot be generic over a unit suffix that is not\n an identifier. `1.5 m` and `1.5 km` are accepted by separate rules, so\n the macro must enumerate every suffix it supports. The unit newtypes in\n `apsis-units` already give compile time unit safety without that list,\n so the macro would add surface area without adding checking.\n- A declarative macro cannot inspect the value of its arguments, so it\n cannot fold a unit conversion at compile time beyond what constant\n evaluation already does. A literal such as `2.5 * 1000.0` is folded by\n the compiler anyway.\n\nIf the workspace later needs macros that generate items, such as deriving\na set of conversions from one definition, that is the point at which this\ncrate should gain its first macro.", + "summary_zh": "支持 ApsisRS 工作区的宏。\n\n本 crate 当前不定义任何宏。它的存在是为了让工作区在需要时有存放宏定义的\n单一位置,也是为了日后再加一个宏时无需新建 crate。\n\n让它保持为空的决定源于工作区实际所需。Rust 内建的 `macro_rules!` 已能完成\n基础层所需的一切:\n\n- 替换一个类型与一个字面量以构建量,如 `quantity!(Meters, 1.5)`。\n- 依据尾随记号分派以接受单位后缀,如 `to_meters!(2.5 km)`。\n\n两种形式都由 `tests/probe.rs` 中的测试演示。它们可以直接写在用到它们的\ncrate 中,故无需共享的宏 crate。\n\n有两点限制值得记下,因为它们解释了为何不提供基于宏的单位系统:\n\n- `macro_rules!` 宏不能对不是标识符的单位后缀泛型化。`1.5 m` 与\n `1.5 km` 由不同的规则接受,故该宏必须枚举它所支持的每个后缀。\n `apsis-units` 中的单位 newtype 已在没有该列表的情况下给出编译期单位\n 安全,故该宏只会增加表面积而不增加检查。\n- 声明式宏无法检视其参数的值,故它无法在常量求值已做的之外于编译期折叠\n 单位换算。诸如 `2.5 * 1000.0` 这样的字面量反正会被编译器折叠。\n\n若工作区日后需要生成项(例如从一个定义派生一组换算)的宏,那便是本\ncrate 应获得其第一个宏之时。", "items": [] }, { "name": "apsis-time", "layer": "physics", - "summary": "Time scales, epochs, and calendar conversions. This crate provides the time model used by the rest of the workspace. An [`Epoch`] is an instant stored as a Modified Julian Date on the International Atomic Time scale, and [`TimeScale`] names the scale a number is expressed in. Storing a single uniform scale makes the arithmetic exact: the difference between two epochs is the elapsed SI time between them, independent of the scales they were created in. UTC is not uniform, because leap seconds are inserted to keep it near the rotation of the Earth, so UTC conversions are routed through the table in `apsis-leap`. ``` use apsis_time::{Calendar, Epoch, TimeScale}; // Build an instant from a UTC calendar date and read it back. let calendar = Calendar::from_ymd(2024, 6, 15); let epoch = Epoch::from_calendar(calendar, TimeScale::Utc).expect(\"covered by the leap table\"); let back = epoch.calendar(TimeScale::Utc).expect(\"covered by the leap table\"); assert_eq!(back.year, 2024); assert_eq!(back.month, 6); assert_eq!(back.day, 15); ``` Sources: - Julian date and Modified Julian date conventions: IAU, as described in the *Explanatory Supplement to the Astronomical Almanac*. - Terrestrial Time offset from TAI: IAU 2000 Resolution B1.9. - Leap seconds: IERS Bulletin C, embedded by `apsis-leap`. - Calendar algorithms: Howard Hinnant, *chrono-Compatible Low-Level Date Algorithms*, 2015.", + "summary": "Time scales, epochs, and calendar conversions.\n\nThis crate provides the time model used by the rest of the workspace.\nAn [`Epoch`] is an instant stored as a Modified Julian Date on the\nInternational Atomic Time scale, and [`TimeScale`] names the scale a\nnumber is expressed in.\n\nStoring a single uniform scale makes the arithmetic exact: the\ndifference between two epochs is the elapsed SI time between them,\nindependent of the scales they were created in. UTC is not uniform,\nbecause leap seconds are inserted to keep it near the rotation of the\nEarth, so UTC conversions are routed through the table in `apsis-leap`.\n\n```\nuse apsis_time::{Calendar, Epoch, TimeScale};\n\n// Build an instant from a UTC calendar date and read it back.\nlet calendar = Calendar::from_ymd(2024, 6, 15);\nlet epoch = Epoch::from_calendar(calendar, TimeScale::Utc).expect(\"covered by the leap table\");\nlet back = epoch.calendar(TimeScale::Utc).expect(\"covered by the leap table\");\nassert_eq!(back.year, 2024);\nassert_eq!(back.month, 6);\nassert_eq!(back.day, 15);\n```\n\nSources:\n\n- Julian date and Modified Julian date conventions: IAU, as described in\n the *Explanatory Supplement to the Astronomical Almanac*.\n- Terrestrial Time offset from TAI: IAU 2000 Resolution B1.9.\n- Leap seconds: IERS Bulletin C, embedded by `apsis-leap`.\n- Calendar algorithms: Howard Hinnant, *chrono-Compatible Low-Level Date\n Algorithms*, 2015.", + "summary_zh": "时标、历元与日历换算。\n\n本 crate 提供工作区其余部分所用的时间模型。[`Epoch`] 是以国际原子时时标\n上的约化儒略日存储的瞬时,[`TimeScale`] 指名某数所表达的时标。\n\n存储单一的均匀时标使算术精确:两个历元之差即它们之间流逝的 SI 时间,\n与它们创建时所处的时标无关。UTC 不均匀,因为插入闰秒以使其接近地球的\n转动,故 UTC 换算经由 `apsis-leap` 中的表格。\n\n出处:\n\n- 儒略日与约化儒略日约定:IAU,如 *Explanatory Supplement to the\n Astronomical Almanac* 所述。\n- 地球时相对 TAI 的偏移:IAU 2000 Resolution B1.9。\n- 闰秒:IERS Bulletin C,由 `apsis-leap` 内嵌。\n- 日历算法:Howard Hinnant,*chrono-Compatible Low-Level Date\n Algorithms*,2015。", "items": [ { "kind": "const", "name": "SECONDS_PER_DAY", "signature": "pub const SECONDS_PER_DAY: f64 = 86_400.0;", "doc": "The number of seconds in a day, not counting leap seconds.", + "doc_zh": "一天中的秒数,不计闰秒。", "associated": false }, { @@ -898,6 +1020,7 @@ "name": "Calendar", "signature": "pub struct Calendar {", "doc": "A calendar date and time of day.\n\nFields are the proleptic Gregorian calendar fields. `second` is a\nfloating point number in the range `[0, 60)`.\n\nThe second may not reach 60. A leap second is written as second 60 in\nUTC, but that label does not have a unique position on a continuous time\nscale: it occupies the same interval that the following midnight would\notherwise occupy. This crate therefore treats leap seconds only where\nthey are unambiguous, in the `TAI - UTC` offset lookup, and not as\ncalendar instants.", + "doc_zh": "日历日期与日内时刻。\n\n各字段为前推格里历的字段。`second` 是位于 `[0, 60)` 的浮点数。\n\n秒数不会达到 60。闰秒在 UTC 中写作第 60 秒,但该标记在连续时间标度上并没有\n唯一位置:它占据的区间正是随后午夜本应占据的区间。因此本 crate 只在无歧义的\n位置处理闰秒,即 `TAI - UTC` 偏移查表中,而不把它当作日历瞬时。", "associated": false }, { @@ -905,6 +1028,7 @@ "name": "new", "signature": "pub const fn new(year: i32, month: u32, day: u32, hour: u32, minute: u32, second: f64) -> Self {", "doc": "Creates a calendar date from its fields.", + "doc_zh": "由各字段创建日历日期。", "associated": true }, { @@ -912,6 +1036,7 @@ "name": "from_ymd", "signature": "pub const fn from_ymd(year: i32, month: u32, day: u32) -> Self {", "doc": "Creates a calendar date at the start of the given day.", + "doc_zh": "在给定日期的起点创建日历日期。", "associated": true }, { @@ -919,6 +1044,7 @@ "name": "seconds_of_day", "signature": "pub fn seconds_of_day(self) -> f64 {", "doc": "Returns the seconds elapsed since the start of the day.", + "doc_zh": "返回自该日起点已过去的秒数。", "associated": true }, { @@ -926,6 +1052,7 @@ "name": "is_valid", "signature": "pub fn is_valid(self) -> bool {", "doc": "Returns `true` when the fields form a date that exists in the\nproleptic Gregorian calendar.", + "doc_zh": "当各字段构成前推格里历中存在的日期时返回 `true`。", "associated": true }, { @@ -933,6 +1060,7 @@ "name": "is_leap_year", "signature": "pub const fn is_leap_year(year: i32) -> bool {", "doc": "Returns `true` when `year` is a leap year in the proleptic Gregorian\ncalendar.", + "doc_zh": "当 `year` 是前推格里历中的闰年时返回 `true`。", "associated": false }, { @@ -940,6 +1068,7 @@ "name": "days_in_month", "signature": "pub const fn days_in_month(year: i32, month: u32) -> u32 {", "doc": "Returns the number of days in the given month of the given year.\n\nReturns zero when `month` is outside `1..=12`.", + "doc_zh": "返回给定年给定月的天数。\n\n`month` 超出 `1..=12` 时返回零。", "associated": false }, { @@ -947,6 +1076,7 @@ "name": "mjd_from_ymd", "signature": "pub const fn mjd_from_ymd(year: i32, month: u32, day: u32) -> f64 {", "doc": "Converts a calendar date, ignoring the time of day, to a Modified Julian\nDate.\n\nThe result is the MJD of the start of the given day, so it is an\ninteger. The time of day is ignored; use [`Calendar::seconds_of_day`] to\naccount for it.", + "doc_zh": "把日历日期(忽略日内时刻)转换为修正儒略日(Modified Julian Date)。\n\n结果是该日起点的 MJD,故为整数。日内时刻被忽略;若需计入,请用\n[`Calendar::seconds_of_day`]。", "associated": false }, { @@ -954,6 +1084,7 @@ "name": "ymd_from_mjd", "signature": "pub fn ymd_from_mjd(mjd: f64) -> (i32, u32, u32) {", "doc": "Converts a Modified Julian Date to a calendar date.\n\nThe fractional part is ignored, so the result is the calendar day\ncontaining the instant.", + "doc_zh": "把修正儒略日(Modified Julian Date)转换为日历日期。\n\n小数部分被忽略,故结果是包含该瞬时的日历日。", "associated": false }, { @@ -961,6 +1092,7 @@ "name": "seconds_of_day_from_mjd", "signature": "pub fn seconds_of_day_from_mjd(mjd: f64) -> f64 {", "doc": "Keeps only the fractional part of an MJD as seconds since midnight.\n\nThe result lies in `[0, 86400)` for any finite input.", + "doc_zh": "只保留 MJD 的小数部分,表示为自午夜起的秒数。\n\n对任何有限输入,结果都位于 `[0, 86400)`。", "associated": false }, { @@ -968,6 +1100,7 @@ "name": "Epoch", "signature": "pub struct Epoch {", "doc": "An instant on the TAI time scale, stored as a Modified Julian Date.\n\nThe field is private so that the stored scale is guaranteed. Use\n[`Epoch::from_mjd`] or the named constructors to build one.", + "doc_zh": "TAI 时间尺度上的一个瞬时,以修正儒略日(Modified Julian Date)存储。\n\n字段私有以保证所存标度确定。用 [`Epoch::from_mjd`] 或具名构造函数创建。", "associated": false }, { @@ -975,6 +1108,7 @@ "name": "J2000", "signature": "pub const J2000: Self = Self::from_tai_mjd_const(51_544.5 - TT_MINUS_TAI / SECONDS_PER_DAY);", "doc": "The instant 2000-01-01T12:00:00 TT, which defines the J2000.0 epoch.\n\nThe value is `2451545.0` on the TT scale, offset back to TAI.", + "doc_zh": "瞬时 2000-01-01T12:00:00 TT,定义了 J2000.0 历元。\n\n该值在 TT 标度上为 `2451545.0`,并回退到 TAI。", "associated": true }, { @@ -982,6 +1116,7 @@ "name": "from_tai_mjd", "signature": "pub const fn from_tai_mjd(tai_mjd: f64) -> Self {", "doc": "Creates an epoch from a Modified Julian Date on the TAI scale.", + "doc_zh": "由 TAI 标度上的修正儒略日创建历元。", "associated": true }, { @@ -989,6 +1124,7 @@ "name": "from_mjd", "signature": "pub fn from_mjd(mjd: f64, scale: TimeScale) -> Option {", "doc": "Creates an epoch from a Modified Julian Date on the given scale.\n\nReturns `None` when the instant cannot be expressed on the requested\nscale. This happens for UTC before 1972, where the leap second table\nused to convert from UTC does not apply.", + "doc_zh": "由给定标度上的修正儒略日(Modified Julian Date)创建历元。\n\n当该瞬时无法在所请求的标度上表达时返回 `None`。这发生在 1972 年之前的\nUTC,因为用于从 UTC 换算的闰秒表不适用。", "associated": true }, { @@ -996,6 +1132,7 @@ "name": "from_calendar", "signature": "pub fn from_calendar(calendar: Calendar, scale: TimeScale) -> Option {", "doc": "Creates an epoch from a calendar date, interpreting it on the given\nscale.\n\nReturns `None` when the fields do not form a valid date, or when the\ninstant cannot be expressed on the requested scale.", + "doc_zh": "由日历日期创建历元,并按给定标度解释该日期。\n\n当各字段不构成有效日期,或该瞬时无法在所请求的标度上表达时返回 `None`。", "associated": true }, { @@ -1003,6 +1140,7 @@ "name": "tai_mjd", "signature": "pub const fn tai_mjd(self) -> f64 {", "doc": "Returns the instant as a Modified Julian Date on the TAI scale.", + "doc_zh": "返回该瞬时在 TAI 标度上的修正儒略日(Modified Julian Date)。", "associated": true }, { @@ -1010,6 +1148,7 @@ "name": "mjd", "signature": "pub fn mjd(self, scale: TimeScale) -> Option {", "doc": "Returns the instant as a Modified Julian Date on the given scale.\n\nReturns `None` when the instant precedes 1972 and the UTC scale is\nrequested, because the leap second table does not cover that range.", + "doc_zh": "返回该瞬时在给定标度上的修正儒略日(Modified Julian Date)。\n\n当该瞬时早于 1972 年且请求 UTC 标度时返回 `None`,因为闰秒表不覆盖该\n区间。", "associated": true }, { @@ -1017,6 +1156,7 @@ "name": "calendar", "signature": "pub fn calendar(self, scale: TimeScale) -> Option {", "doc": "Returns the instant as a calendar date on the given scale.\n\nReturns `None` when the scale conversion is not available for this\ninstant; see [`Epoch::mjd`].", + "doc_zh": "返回该瞬时在给定标度上的日历日期。\n\n当该瞬时不可进行标度换算时返回 `None`;参见 [`Epoch::mjd`]。", "associated": true }, { @@ -1024,6 +1164,7 @@ "name": "seconds_until", "signature": "pub fn seconds_until(self, other: Self) -> f64 {", "doc": "Returns the signed number of seconds from `self` to `other`.\n\nThe result is positive when `other` is later. Because both epochs\nare stored on the uniform TAI scale, the difference is exact and\ndoes not depend on the scales used to create either epoch.", + "doc_zh": "返回从 `self` 到 `other` 的带符号秒数。\n\n当 `other` 更晚时结果为正。由于两个历元都存于均匀的 TAI 标度,差值\n精确,且与创建二者时所用的标度无关。", "associated": true }, { @@ -1031,6 +1172,7 @@ "name": "add_seconds", "signature": "pub fn add_seconds(self, seconds: f64) -> Self {", "doc": "Returns an epoch shifted by `seconds`.\n\nThe shift is measured in SI seconds on the uniform TAI scale, so it\nadvances the instant by exactly that much elapsed time. This is the\nonly shift offered: a shift measured on the UTC clock would differ\nwhen the interval spans an inserted leap second, and the UTC clock\ndoes not have a single well defined numeric value across such an\ninstant. Callers that need calendar arithmetic on the UTC scale\nshould convert to a [`Calendar`], adjust it, and convert back.", + "doc_zh": "返回平移 `seconds` 后的历元。\n\n平移量以均匀 TAI 标度上的 SI 秒计,故瞬时恰好前进相同的流逝时间。这是\n唯一提供的平移方式:若按 UTC 时钟平移,当区间跨越插入的闰秒时结果会\n不同,而 UTC 时钟在该瞬时前后没有单一良定义的数值。需要按 UTC 标度做\n日历运算的调用方,应先转为 [`Calendar`]、调整后再转回。", "associated": true }, { @@ -1038,6 +1180,7 @@ "name": "is_finite", "signature": "pub const fn is_finite(self) -> bool {", "doc": "Returns `true` when the stored Modified Julian Date is finite.", + "doc_zh": "当所存修正儒略日(Modified Julian Date)有限时返回 `true`。", "associated": true }, { @@ -1045,6 +1188,7 @@ "name": "JULIAN_DATE_OFFSET", "signature": "pub const JULIAN_DATE_OFFSET: f64 = 2_400_000.5;", "doc": "The number of days between the start of the Julian Date scale and the\nstart of the Modified Julian Date scale.", + "doc_zh": "儒略日标度起点与修正儒略日标度起点之间的天数。", "associated": false }, { @@ -1052,6 +1196,7 @@ "name": "mjd_from_julian_date", "signature": "pub const fn mjd_from_julian_date(julian_date: f64) -> f64 {", "doc": "Converts a Julian Date to a Modified Julian Date.", + "doc_zh": "把儒略日(Julian Date)转换为修正儒略日(Modified Julian Date)。", "associated": false }, { @@ -1059,6 +1204,7 @@ "name": "julian_date_from_mjd", "signature": "pub const fn julian_date_from_mjd(mjd: f64) -> f64 {", "doc": "Converts a Modified Julian Date to a Julian Date.", + "doc_zh": "把修正儒略日(Modified Julian Date)转换为儒略日(Julian Date)。", "associated": false }, { @@ -1066,6 +1212,7 @@ "name": "TT_MINUS_TAI", "signature": "pub const TT_MINUS_TAI: f64 = 32.184;", "doc": "Terrestrial Time minus International Atomic Time, in seconds.\n\nExact by the definition of Terrestrial Time.", + "doc_zh": "地球时(TT)减国际原子时(TAI),单位为秒。\n\n由地球时的定义精确确定。", "associated": false }, { @@ -1073,6 +1220,7 @@ "name": "TAI_MINUS_GPS", "signature": "pub const TAI_MINUS_GPS: f64 = 19.0;", "doc": "International Atomic Time minus GPS time, in seconds.\n\nExact by the definition of GPS time.", + "doc_zh": "国际原子时(TAI)减 GPS 时,单位为秒。\n\n由 GPS 时的定义精确确定。", "associated": false }, { @@ -1080,6 +1228,7 @@ "name": "TimeScale", "signature": "pub enum TimeScale {", "doc": "A time scale.\n\nThe scale determines how a numeric instant is interpreted. It does not\nchange the instant itself; converting an [`Epoch`](crate::Epoch) between\nscales only changes the number used to display it.", + "doc_zh": "时间尺度。\n\n标度决定一个数值瞬时如何被解释,但并不改变瞬时本身;把\n[`Epoch`](crate::Epoch) 在标度之间换算,只改变用于显示它的数值。", "associated": false }, { @@ -1087,6 +1236,7 @@ "name": "is_uniform", "signature": "pub const fn is_uniform(self) -> bool {", "doc": "Returns `true` when the scale runs at a uniform rate.\n\nUTC is the only scale here that is not uniform, because leap seconds\nmake some of its minutes 61 seconds long.", + "doc_zh": "当该标度以均匀速率运行时返回 `true`。\n\n此处只有 UTC 不是均匀的,因为闰秒使其部分分钟长达 61 秒。", "associated": true }, { @@ -1094,6 +1244,7 @@ "name": "name", "signature": "pub const fn name(self) -> &'static str {", "doc": "Returns the short name of the scale, such as `TAI` or `UTC`.", + "doc_zh": "返回该标度的简称,例如 `TAI` 或 `UTC`。", "associated": true } ] @@ -1101,13 +1252,15 @@ { "name": "apsis-leap", "layer": "physics", - "summary": "Embedded leap second table for the UTC time scale. Coordinated Universal Time (UTC) is kept within 0.9 s of UT1 by inserting or removing leap seconds. Because UTC is not a uniform time scale, converting between UTC and the uniform scales TAI, TT, and GPS requires the offset `TAI - UTC` in force at the instant of interest. This crate embeds that offset as a compile time table and answers queries against it. Dates are handled as Modified Julian Date (MJD), which is the Julian Date minus 2 400 000.5. An MJD is a count of days since 1858-11-17T00:00:00, so it carries no time scale of its own; the caller states the scale by choosing which function to call. The table begins at 1972-01-01, when UTC adopted the current integer second offset scheme. Offsets before that date were fractional and followed a different rule, so queries earlier than the first entry return `None` rather than a misleading value. Source: IERS Bulletin C, which announces leap seconds after confirmation by the International Earth Rotation and Reference Systems Service. The most recent entry in this table is the leap second of 2017-01-01; no leap second has been announced since. ``` use apsis_leap::{tai_minus_utc_at_mjd, LEAP_SECONDS}; // The last entry in the embedded table fixes the current offset. let last = LEAP_SECONDS.last().expect(\"table is not empty\"); assert_eq!(last.tai_minus_utc, 37.0); assert_eq!(tai_minus_utc_at_mjd(60_000.0), Some(37.0)); ```", + "summary": "Embedded leap second table for the UTC time scale.\n\nCoordinated Universal Time (UTC) is kept within 0.9 s of UT1 by\ninserting or removing leap seconds. Because UTC is not a uniform time\nscale, converting between UTC and the uniform scales TAI, TT, and GPS\nrequires the offset `TAI - UTC` in force at the instant of interest.\n\nThis crate embeds that offset as a compile time table and answers\nqueries against it. Dates are handled as Modified Julian Date (MJD),\nwhich is the Julian Date minus 2 400 000.5. An MJD is a count of days\nsince 1858-11-17T00:00:00, so it carries no time scale of its own; the\ncaller states the scale by choosing which function to call.\n\nThe table begins at 1972-01-01, when UTC adopted the current integer\nsecond offset scheme. Offsets before that date were fractional and\nfollowed a different rule, so queries earlier than the first entry\nreturn `None` rather than a misleading value.\n\nSource: IERS Bulletin C, which announces leap seconds after\nconfirmation by the International Earth Rotation and Reference Systems\nService. The most recent entry in this table is the leap second of\n2017-01-01; no leap second has been announced since.\n\n```\nuse apsis_leap::{tai_minus_utc_at_mjd, LEAP_SECONDS};\n\n// The last entry in the embedded table fixes the current offset.\nlet last = LEAP_SECONDS.last().expect(\"table is not empty\");\nassert_eq!(last.tai_minus_utc, 37.0);\nassert_eq!(tai_minus_utc_at_mjd(60_000.0), Some(37.0));\n```", + "summary_zh": "为 UTC 时标内嵌的闰秒表。\n\n协调世界时(UTC)通过插入或删除闰秒保持在 UT1 的 0.9 秒以内。因为 UTC\n不是均匀时标,在 UTC 与均匀时标 TAI、TT、GPS 之间换算需要所关注瞬时\n生效的偏移 `TAI - UTC`。\n\n本 crate 把该偏移内嵌为编译期表格,并对其作答查询。日期按约化儒略日\n(MJD)处理,它是儒略日减去 2 400 000.5。MJD 是自 1858-11-17T00:00:00\n起的天数,故它自身不携带时标;调用方通过选择调用哪个函数来指明时标。\n\n该表始于 1972-01-01,那是 UTC 采用当前整数秒偏移方案的时刻。该日之前的\n偏移是小数并遵循不同规则,故早于首项的查询返回 `None` 而非误导性的值。\n\n出处:IERS Bulletin C,它在国际地球自转与参考系服务确认之后宣布闰秒。\n本表最近的一项是 2017-01-01 的闰秒;此后未再宣布闰秒。", "items": [ { "kind": "struct", "name": "LeapSecond", "signature": "pub struct LeapSecond {", "doc": "One leap second announcement.\n\nThe entry records the date on which a new value of `TAI - UTC` took\neffect. It does not record the size of the inserted second, because in\nevery announcement to date the inserted second was exactly one second\nand the new offset is one greater than the previous one.", + "doc_zh": "一条闰秒公告。\n\n该条目记录 `TAI - UTC` 新值生效的日期。它不记录所插入闰秒的长度,因为迄今\n每一次公告插入的闰秒都恰为一秒,且新偏移比前一个大一。", "associated": false }, { @@ -1115,6 +1268,7 @@ "name": "effective_mjd", "signature": "pub const fn effective_mjd(self) -> f64 {", "doc": "Returns the Modified Julian Date of the effective instant.\n\nLeap seconds take effect at 00:00:00 UTC, so the effective instant\nis the start of the given calendar day.", + "doc_zh": "返回生效瞬时的修正儒略日(Modified Julian Date)。\n\n闰秒在 00:00:00 UTC 生效,故生效瞬时即该日历日的起点。", "associated": true }, { @@ -1122,6 +1276,7 @@ "name": "LEAP_SECONDS", "signature": "pub const LEAP_SECONDS: &[LeapSecond] = &[ LeapSecond {", "doc": "The leap second table, ordered by effective date.\n\nSource: IERS Bulletin C. The first entry is 1972-01-01, when the\ncurrent scheme began with an initial offset of 10 s inherited from\nearlier adjustments.", + "doc_zh": "闰秒表,按生效日期排序。\n\n出处:IERS Bulletin C。首条为 1972-01-01,即现行方案开始之时,其初始偏移\n10 s 承自更早的调整。", "associated": false }, { @@ -1129,6 +1284,7 @@ "name": "TABLE_COVERS_FROM_MJD", "signature": "pub const TABLE_COVERS_FROM_MJD: f64 = mjd_from_ymd( LEAP_SECONDS[0].year, LEAP_SECONDS[0].month, LEAP_SECONDS[0].day, );", "doc": "The Modified Julian Date of the first table entry, 1972-01-01.\n\nQueries earlier than this instant are rejected because UTC did not yet\nuse integer second offsets.", + "doc_zh": "表中首条的修正儒略日(Modified Julian Date),1972-01-01。\n\n早于该瞬时的查询会被拒绝,因为那时的 UTC 尚未采用整数秒偏移。", "associated": false }, { @@ -1136,6 +1292,7 @@ "name": "TAI_MINUS_UTC_AFTER_LAST_ENTRY", "signature": "pub const TAI_MINUS_UTC_AFTER_LAST_ENTRY: f64 = LEAP_SECONDS[LEAP_SECONDS.len() - 1].tai_minus_utc;", "doc": "The value of `TAI - UTC` on and after the last table entry.\n\nThis is the offset that remains in force until a new leap second is\nannounced.", + "doc_zh": "自表中末条起生效的 `TAI - UTC` 值。\n\n在新的闰秒公布之前,该偏移持续有效。", "associated": false }, { @@ -1143,6 +1300,7 @@ "name": "mjd_from_ymd", "signature": "pub const fn mjd_from_ymd(year: i32, month: u32, day: u32) -> f64 {", "doc": "Converts a proleptic Gregorian calendar date to a Modified Julian Date.\n\nThe result is the MJD of 00:00:00 on the given day. The algorithm is the\nFliegel-Van Flandern integer conversion, which is exact for every date\nthe Gregorian calendar is defined on and does not need a lookup table.\n\nThe computation is done in `i64` and converted once at the end, so the\nresult is exact for the range of dates in which a leap second can\nplausibly occur.", + "doc_zh": "把前推格里历日期转换为修正儒略日(Modified Julian Date)。\n\n结果是给定日期 00:00:00 的 MJD。算法采用 Fliegel-Van Flandern 整数换算,\n对格里历定义范围内的每个日期都精确,且无需查表。\n\n计算在 `i64` 中进行并仅在末尾换算一次,故在闰秒可能发生的日期范围内结果\n精确。", "associated": false }, { @@ -1150,6 +1308,7 @@ "name": "MJD_AT_UNIX_EPOCH", "signature": "pub const MJD_AT_UNIX_EPOCH: f64 = 40_587.0;", "doc": "The Modified Julian Date of the Unix epoch, 1970-01-01T00:00:00.\n\nMJD carries no time scale of its own, so this constant is the same\nnumber whether the instant is read in UTC, TAI, or any other scale.", + "doc_zh": "Unix 历元的修正儒略日(Modified Julian Date),1970-01-01T00:00:00。\n\nMJD 本身不携带时间标度,故无论该瞬时按 UTC、TAI 还是其他标度读取,此常量\n都是同一个数。", "associated": false }, { @@ -1157,6 +1316,7 @@ "name": "tai_minus_utc_at_mjd", "signature": "pub fn tai_minus_utc_at_mjd(utc_mjd: f64) -> Option {", "doc": "Returns the value of `TAI - UTC` in seconds at the given UTC Modified\nJulian Date.\n\nThe offset is a step function: it changes at 00:00:00 UTC on the\neffective date of each leap second and is constant in between. A query\nexactly on an effective date returns the new value.\n\nReturns `None` when the instant precedes 1972-01-01, before which UTC\nused fractional offsets and this table does not apply.", + "doc_zh": "返回给定 UTC 修正儒略日(Modified Julian Date)处 `TAI - UTC` 的秒值。\n\n该偏移是阶跃函数:它在每个闰秒生效日的 00:00:00 UTC 改变,其间保持不变。\n恰在生效日上的查询返回新值。\n\n当该瞬时早于 1972-01-01 时返回 `None`,因为那之前 UTC 采用小数偏移,本表\n不适用。", "associated": false }, { @@ -1164,6 +1324,7 @@ "name": "tai_mjd_from_utc_mjd", "signature": "pub fn tai_mjd_from_utc_mjd(utc_mjd: f64) -> Option {", "doc": "Converts a UTC Modified Julian Date to the corresponding TAI Modified\nJulian Date.\n\nThe conversion adds the offset in force at that instant. Because the\noffset is a step function evaluated on the UTC side, the conversion is\ndefined for every UTC instant; the inverse conversion is not, because a\nleap second makes some TAI instants correspond to no UTC instant.\n\nReturns `None` when the instant precedes 1972-01-01.", + "doc_zh": "把 UTC 修正儒略日(Modified Julian Date)转换为对应的 TAI 修正儒略日。\n\n换算加上该瞬时生效的偏移。由于偏移是在 UTC 侧求值的阶跃函数,正向换算对每个\nUTC 瞬时都有定义;反向换算则不然,因为闰秒使某些 TAI 瞬时不对应任何 UTC\n瞬时。\n\n当该瞬时早于 1972-01-01 时返回 `None`。", "associated": false }, { @@ -1171,6 +1332,7 @@ "name": "utc_mjd_from_tai_mjd", "signature": "pub fn utc_mjd_from_tai_mjd(tai_mjd: f64) -> Option {", "doc": "Converts a TAI Modified Julian Date to the corresponding UTC Modified\nJulian Date.\n\nEach candidate offset from the table is tried in turn. A candidate is\naccepted when the offset in force at the resulting UTC instant is the\nsame offset that produced it. At most one offset can satisfy that\ncondition for a given input, so the search is unambiguous.\n\nReturns `None` in two cases. The first is an instant before the start of\nthe table, where UTC carried fractional offsets that this table does not\ndescribe. The second is an instant that falls strictly inside an\ninserted leap second. Such an instant has no UTC representation, because\nthe UTC clock labels that interval `23:59:60`, a value that does not\nexist on the continuous Modified Julian Date scale.", + "doc_zh": "把 TAI 修正儒略日(Modified Julian Date)转换为对应的 UTC 修正儒略日。\n\n表中每个候选偏移依次尝试;当所得 UTC 瞬时处生效的偏移与产生它的偏移相同时,\n该候选被接受。对给定输入至多有一个偏移满足该条件,故搜索无歧义。\n\n两种情形返回 `None`。其一是早于表中起点的瞬时,那里的 UTC 采用小数偏移,本表\n未予描述。其二是严格落在所插入闰秒内部的瞬时,此类瞬时没有 UTC 表示,因为\nUTC 时钟把该区间标记为 `23:59:60`,而该值在连续的修正儒略日标度上并不存在。", "associated": false }, { @@ -1178,6 +1340,7 @@ "name": "calendar_mjd", "signature": "pub const fn calendar_mjd(year: i32, month: u32, day: u32) -> f64 {", "doc": "Converts a proleptic Gregorian calendar date at 00:00:00 to a Modified\nJulian Date.\n\nThis is a convenience re-export of the calendar conversion used to place\nthe leap second table entries. It is provided so that callers do not\nhave to reproduce the conversion, and because the calibration scripts in\n`validation/` need the same function on both sides of a comparison.", + "doc_zh": "把前推格里历日期在 00:00:00 的瞬时转换为修正儒略日(Modified Julian Date)。\n\n这是用于定位闰秒表条目的日历换算之便捷再导出。提供它是为了免去调用方重复\n实现该换算,也因为 `validation/` 中的标定脚本在比较的两侧都需要同一函数。", "associated": false } ] @@ -1185,13 +1348,15 @@ { "name": "apsis-coord", "layer": "physics", - "summary": "Coordinate frames and the transformations between them. This crate defines positions that carry their reference frame in the type, so a position expressed in one frame cannot be passed where another is expected. The two frames are the Earth-centered inertial (ECI) frame and the Earth-centered Earth-fixed (ECEF) frame. Three position representations are offered: - [`Eci`], an inertial Cartesian position in meters - [`Ecef`], an Earth-fixed Cartesian position in meters - [`Geodetic`], a latitude, longitude, and height above the WGS 84 ellipsoid The transformation between [`Eci`] and [`Ecef`] is the rotation defined by the Earth Rotation Angle, which `apsis-frames` computes. The transformation between [`Ecef`] and [`Geodetic`] uses the WGS 84 ellipsoid from `apsis-geodesy`. ``` use apsis_coord::{Ecef, Eci, Geodetic, WGS84}; use apsis_frames::EarthRotationAngle; use apsis_math::Vec3; // A station on the equator at zero longitude. let station = Geodetic::new(0.0, 0.0, 0.0); let fixed = station.to_ecef(); assert!((fixed.vector().x - WGS84.semi_major_axis()).abs() < 1e-6); // Rotating it into the inertial frame preserves its length. let inertial = fixed.to_eci(EarthRotationAngle::from_ut1_days(0.0)); assert!((inertial.vector().norm() - fixed.vector().norm()).abs() < 1e-6); let _ = Eci::new(Vec3::X); ``` Sources: - Earth Rotation Angle: *IERS Conventions (2010)*, equation 8.32, as implemented by `apsis-frames`. - Geodetic conversions and the WGS 84 ellipsoid: NIMA TR8350.2, third edition, 2000, as implemented by `apsis-geodesy`.", + "summary": "Coordinate frames and the transformations between them.\n\nThis crate defines positions that carry their reference frame in the\ntype, so a position expressed in one frame cannot be passed where another\nis expected. The two frames are the Earth-centered inertial (ECI) frame\nand the Earth-centered Earth-fixed (ECEF) frame.\n\nThree position representations are offered:\n\n- [`Eci`], an inertial Cartesian position in meters\n- [`Ecef`], an Earth-fixed Cartesian position in meters\n- [`Geodetic`], a latitude, longitude, and height above the WGS 84\n ellipsoid\n\nThe transformation between [`Eci`] and [`Ecef`] is the rotation defined\nby the Earth Rotation Angle, which `apsis-frames` computes. The\ntransformation between [`Ecef`] and [`Geodetic`] uses the WGS 84\nellipsoid from `apsis-geodesy`.\n\n```\nuse apsis_coord::{Ecef, Eci, Geodetic, WGS84};\nuse apsis_frames::EarthRotationAngle;\nuse apsis_math::Vec3;\n\n// A station on the equator at zero longitude.\nlet station = Geodetic::new(0.0, 0.0, 0.0);\nlet fixed = station.to_ecef();\nassert!((fixed.vector().x - WGS84.semi_major_axis()).abs() < 1e-6);\n\n// Rotating it into the inertial frame preserves its length.\nlet inertial = fixed.to_eci(EarthRotationAngle::from_ut1_days(0.0));\nassert!((inertial.vector().norm() - fixed.vector().norm()).abs() < 1e-6);\nlet _ = Eci::new(Vec3::X);\n```\n\nSources:\n\n- Earth Rotation Angle: *IERS Conventions (2010)*, equation 8.32, as\n implemented by `apsis-frames`.\n- Geodetic conversions and the WGS 84 ellipsoid: NIMA TR8350.2, third\n edition, 2000, as implemented by `apsis-geodesy`.", + "summary_zh": "坐标系及它们之间的变换。\n\n本 crate 定义在类型中携带其参考系的各类位置,故在某坐标系中表达的位置\n无法传递到期望另一坐标系之处。这两个坐标系是地心惯性(ECI)系与地心\n地固(ECEF)系。\n\n提供三种位置表示:\n\n- [`Eci`],以米计的惯性直角坐标位置\n- [`Ecef`],以米计的地固直角坐标位置\n- [`Geodetic`],WGS 84 椭球之上的纬度、经度与高度\n\n[`Eci`] 与 [`Ecef`] 之间的变换是由地球自转角定义的转动,由\n`apsis-frames` 计算。[`Ecef`] 与 [`Geodetic`] 之间的变换使用来自\n`apsis-geodesy` 的 WGS 84 椭球。\n\n出处:\n\n- 地球自转角:*IERS Conventions(2010)*,式 8.32,由 `apsis-frames`\n 实现。\n- 大地坐标换算与 WGS 84 椭球:NIMA TR8350.2,第三版,2000,由\n `apsis-geodesy` 实现。", "items": [ { "kind": "struct", "name": "Eci", "signature": "pub struct Eci(Vec3);", "doc": "A Cartesian position in the Earth-centered inertial frame.\n\nThe frame is aligned with the mean equator and equinox of J2000.0 and is\ntreated as inertial, so it does not rotate with the Earth. Coordinates\nare in meters.", + "doc_zh": "地心惯性系中的笛卡尔位置。\n\n该系与 J2000.0 的平赤道平春分点对齐,并视为惯性系,故不随地球自转。\n坐标单位为米。", "associated": false }, { @@ -1199,6 +1364,7 @@ "name": "Ecef", "signature": "pub struct Ecef(Vec3);", "doc": "A Cartesian position in the Earth-centered Earth-fixed frame.\n\nThe frame rotates with the Earth, so a point fixed to the ground has\nconstant coordinates in it. Coordinates are in meters.", + "doc_zh": "地心地固系中的笛卡尔位置。\n\n该系随地球自转,故地面上固定的点在其中坐标恒定。坐标单位为米。", "associated": false }, { @@ -1206,6 +1372,7 @@ "name": "Geodetic", "signature": "pub struct Geodetic(EllipsoidalPosition);", "doc": "A position in the Earth-fixed frame given as latitude, longitude, and\nheight above the WGS 84 ellipsoid.\n\nLatitude and longitude are in radians and height is in meters. Latitude\nis geodetic, measured between the ellipsoid normal and the equatorial\nplane.", + "doc_zh": "以纬度、经度与 WGS 84 椭球高给出的地固系位置。\n\n纬度与经度用弧度,高用米。纬度为大地纬度,在椭球法线与赤道面之间量取。", "associated": false }, { @@ -1213,6 +1380,7 @@ "name": "new", "signature": "pub const fn new(vector: Vec3) -> Self {", "doc": "Creates an inertial position from a vector in meters.", + "doc_zh": "由米为单位的向量创建惯性位置。", "associated": true }, { @@ -1220,6 +1388,7 @@ "name": "vector", "signature": "pub const fn vector(self) -> Vec3 {", "doc": "Returns the position in meters.", + "doc_zh": "返回以米为单位的位置。", "associated": true }, { @@ -1227,6 +1396,7 @@ "name": "to_ecef", "signature": "pub fn to_ecef(self, angle: EarthRotationAngle) -> Ecef {", "doc": "Converts this inertial position to the Earth-fixed frame at the given\nEarth Rotation Angle.", + "doc_zh": "按给定的地球自转角把该惯性位置转换到地固系。", "associated": true }, { @@ -1234,6 +1404,7 @@ "name": "to_eci", "signature": "pub fn to_eci(self, angle: EarthRotationAngle) -> Eci {", "doc": "Converts this Earth-fixed position to the inertial frame at the given\nEarth Rotation Angle.", + "doc_zh": "按给定的地球自转角把该地固位置转换到惯性系。", "associated": true }, { @@ -1241,6 +1412,7 @@ "name": "to_geodetic", "signature": "pub fn to_geodetic(self, ellipsoid: Ellipsoid) -> Option {", "doc": "Converts this Earth-fixed position to latitude, longitude, and height\nabove the given ellipsoid.\n\nReturns `None` when the position lies on the polar axis, where no\nlongitude is defined.", + "doc_zh": "把该地固位置转换为给定椭球之上的纬度、经度与高。\n\n当位置落在极轴上时返回 `None`,因为那里经度未定义。", "associated": true }, { @@ -1248,6 +1420,7 @@ "name": "new", "signature": "pub const fn new(latitude: f64, longitude: f64, height: f64) -> Self {", "doc": "Creates a geodetic position from latitude, longitude, and height.\n\nLatitude and longitude are in radians and height is in meters.", + "doc_zh": "由纬度、经度与高创建大地位置。\n\n纬度与经度用弧度,高用米。", "associated": true }, { @@ -1255,6 +1428,7 @@ "name": "latitude", "signature": "pub const fn latitude(self) -> f64 {", "doc": "Returns the geodetic latitude in radians.", + "doc_zh": "返回大地纬度,单位为弧度。", "associated": true }, { @@ -1262,6 +1436,7 @@ "name": "longitude", "signature": "pub const fn longitude(self) -> f64 {", "doc": "Returns the longitude in radians, positive east.", + "doc_zh": "返回经度,单位为弧度,向东为正。", "associated": true }, { @@ -1269,6 +1444,7 @@ "name": "height", "signature": "pub const fn height(self) -> f64 {", "doc": "Returns the height above the ellipsoid in meters.", + "doc_zh": "返回椭球高,单位为米。", "associated": true }, { @@ -1276,6 +1452,7 @@ "name": "ellipsoidal", "signature": "pub const fn ellipsoidal(self) -> EllipsoidalPosition {", "doc": "Returns the underlying ellipsoidal position.", + "doc_zh": "返回底层的椭球位置。", "associated": true }, { @@ -1283,6 +1460,7 @@ "name": "to_ecef", "signature": "pub fn to_ecef(self) -> Ecef {", "doc": "Converts this position to the Earth-fixed frame on the WGS 84\nellipsoid.\n\nThis is the common case. Use [`Geodetic::to_ecef_on`] for a different\nellipsoid.", + "doc_zh": "把该位置转换到 WGS 84 椭球的地固系。\n\n这是常见情形。需用其他椭球时请用 [`Geodetic::to_ecef_on`]。", "associated": true }, { @@ -1290,6 +1468,7 @@ "name": "to_ecef_on", "signature": "pub fn to_ecef_on(self, ellipsoid: Ellipsoid) -> Ecef {", "doc": "Converts this position to the Earth-fixed frame on the given\nellipsoid.", + "doc_zh": "把该位置转换到给定椭球的地固系。", "associated": true } ] @@ -1297,13 +1476,15 @@ { "name": "apsis-geodesy", "layer": "physics", - "summary": "Reference ellipsoids and geodetic computations. A geodetic position is written as latitude, longitude, and height above an ellipsoid. Latitude here is always geodetic, the angle between the ellipsoid normal and the equatorial plane, not geocentric. The two differ by up to about 0.19 degrees at mid latitudes, so the distinction matters for pointing and ranging. [`WGS84`] is the ellipsoid used by GPS and by almost all satellite work. Its defining parameters are the semi-major axis and the inverse flattening; every other quantity is derived from those two, so the derived values agree with the ones published in the standard to the precision of the defining parameters. Source: National Imagery and Mapping Agency, *Department of Defense World Geodetic System 1984*, Technical Report TR8350.2, third edition, 2000. ``` use apsis_geodesy::{Geodetic, WGS84}; // The equator has zero height and a radius equal to the semi-major axis. let point = Geodetic::new(0.0, 0.0, 0.0); let cartesian = point.to_cartesian(WGS84); assert!((cartesian.norm() - WGS84.semi_major_axis()).abs() < 1e-9); ```", + "summary": "Reference ellipsoids and geodetic computations.\n\nA geodetic position is written as latitude, longitude, and height above\nan ellipsoid. Latitude here is always geodetic, the angle between the\nellipsoid normal and the equatorial plane, not geocentric. The two\ndiffer by up to about 0.19 degrees at mid latitudes, so the distinction\nmatters for pointing and ranging.\n\n[`WGS84`] is the ellipsoid used by GPS and by almost all satellite work.\nIts defining parameters are the semi-major axis and the inverse\nflattening; every other quantity is derived from those two, so the\nderived values agree with the ones published in the standard to the\nprecision of the defining parameters.\n\nSource: National Imagery and Mapping Agency, *Department of Defense World\nGeodetic System 1984*, Technical Report TR8350.2, third edition, 2000.\n\n```\nuse apsis_geodesy::{Geodetic, WGS84};\n\n// The equator has zero height and a radius equal to the semi-major axis.\nlet point = Geodetic::new(0.0, 0.0, 0.0);\nlet cartesian = point.to_cartesian(WGS84);\nassert!((cartesian.norm() - WGS84.semi_major_axis()).abs() < 1e-9);\n```", + "summary_zh": "参考椭球与大地计算。\n\n大地位置写为某椭球之上的纬度、经度与高度。此处的纬度总是大地纬度,即\n椭球法线与赤道面之间的角,而非地心纬度。在中纬度处二者相差至多约 0.19\n度,故该区别对指向与测距是要紧的。\n\n[`WGS84`] 是 GPS 以及几乎所有卫星工作所用的椭球。其定义参数是半长轴与\n扁率倒数;其余每个量都由这两个导出,故各导出值与标准中发布的值在定义\n参数的精度内一致。\n\n出处:National Imagery and Mapping Agency,*Department of Defense World\nGeodetic System 1984*,技术报告 TR8350.2,第三版,2000。", "items": [ { "kind": "struct", "name": "Ellipsoid", "signature": "pub struct Ellipsoid {", "doc": "A biaxial reference ellipsoid: an ellipse of revolution about its minor\naxis.\n\nThe shape is fixed by two numbers. This crate stores the semi-major axis\nand the inverse flattening, which are the two defining parameters of the\nWGS 84 ellipsoid in the standard. Every other quantity is derived, so\nthere is no risk of the parameters disagreeing with each other.\n\nLengths are in meters and angles are in radians.", + "doc_zh": "双轴参考椭球:绕短轴旋转所得的椭圆。\n\n形状由两个数确定。本 crate 存储半长轴与逆扁率,它们是标准中 WGS 84 椭球的\n两个定义参数。其余量都由二者导出,故各参数之间不会失配。\n\n长度单位为米,角度单位为弧度。", "associated": false }, { @@ -1311,6 +1492,7 @@ "name": "new", "signature": "pub const fn new(semi_major_axis: f64, inverse_flattening: f64) -> Self {", "doc": "Creates an ellipsoid from its semi-major axis and inverse\nflattening.\n\nLengths are in meters. The inverse flattening must be positive and\nfinite; a value of infinity describes a sphere.", + "doc_zh": "由半长轴与逆扁率创建椭球。\n\n长度单位为米。逆扁率必须为正且有限;取无穷大则描述一个球。", "associated": true }, { @@ -1318,6 +1500,7 @@ "name": "semi_major_axis", "signature": "pub const fn semi_major_axis(self) -> f64 {", "doc": "Returns the semi-major axis, in meters.", + "doc_zh": "返回半长轴,单位为米。", "associated": true }, { @@ -1325,6 +1508,7 @@ "name": "inverse_flattening", "signature": "pub const fn inverse_flattening(self) -> f64 {", "doc": "Returns the inverse flattening.", + "doc_zh": "返回逆扁率。", "associated": true }, { @@ -1332,6 +1516,7 @@ "name": "flattening", "signature": "pub fn flattening(self) -> f64 {", "doc": "Returns the flattening.\n\nThis is zero for a sphere.", + "doc_zh": "返回扁率。\n\n对球其值为零。", "associated": true }, { @@ -1339,6 +1524,7 @@ "name": "semi_minor_axis", "signature": "pub fn semi_minor_axis(self) -> f64 {", "doc": "Returns the semi-minor axis, in meters.", + "doc_zh": "返回半短轴,单位为米。", "associated": true }, { @@ -1346,6 +1532,7 @@ "name": "eccentricity_squared", "signature": "pub fn eccentricity_squared(self) -> f64 {", "doc": "Returns the first eccentricity squared, a dimensionless number in\n`[0, 1)`.\n\nIt is `2f - f^2` for flattening `f`.", + "doc_zh": "返回第一偏心率平方,为位于 `[0, 1)` 的无量纲数。\n\n对扁率 `f`,它等于 `2f - f^2`。", "associated": true }, { @@ -1353,6 +1540,7 @@ "name": "eccentricity", "signature": "pub fn eccentricity(self) -> f64 {", "doc": "Returns the first eccentricity, a dimensionless number in `[0, 1)`.", + "doc_zh": "返回第一偏心率,为位于 `[0, 1)` 的无量纲数。", "associated": true }, { @@ -1360,6 +1548,7 @@ "name": "second_eccentricity_squared", "signature": "pub fn second_eccentricity_squared(self) -> f64 {", "doc": "Returns the second eccentricity squared, a dimensionless number.\n\nIt is `e^2 / (1 - e^2)`, which appears in the closed form for\nconverting a geodetic position to Cartesian coordinates.", + "doc_zh": "返回第二偏心率平方,为无量纲数。\n\n它等于 `e^2 / (1 - e^2)`,出现在把大地位置转换为笛卡尔坐标的闭式解中。", "associated": true }, { @@ -1367,6 +1556,7 @@ "name": "polar_radius_of_curvature", "signature": "pub fn polar_radius_of_curvature(self) -> f64 {", "doc": "Returns the polar radius of curvature, in meters.\n\nThis is the radius of curvature in the meridian at the pole, equal\nto `a / sqrt(1 - e^2)`.", + "doc_zh": "返回极半径曲率,单位为米。\n\n它是极点处子午线上的曲率半径,等于 `a / sqrt(1 - e^2)`。", "associated": true }, { @@ -1374,6 +1564,7 @@ "name": "prime_vertical_radius", "signature": "pub fn prime_vertical_radius(self, latitude: f64) -> f64 {", "doc": "Returns the radius of curvature in the prime vertical at the given\ngeodetic latitude, in meters.\n\nThis is the distance from the surface to the polar axis measured\nalong the ellipsoid normal, and it grows from the semi-major axis at\nthe equator to [`Ellipsoid::polar_radius_of_curvature`] at the pole.\n\nThe latitude is in radians.", + "doc_zh": "返回给定大地纬度处卯酉圈上的曲率半径,单位为米。\n\n它是沿椭球法线从表面量到极轴的距离,由赤道处的半长轴增长到极点的\n[`Ellipsoid::polar_radius_of_curvature`]。\n\n纬度单位为弧度。", "associated": true }, { @@ -1381,6 +1572,7 @@ "name": "meridian_radius", "signature": "pub fn meridian_radius(self, latitude: f64) -> f64 {", "doc": "Returns the radius of curvature in the meridian at the given\ngeodetic latitude, in meters.\n\nThe latitude is in radians.", + "doc_zh": "返回给定大地纬度处子午线上的曲率半径,单位为米。\n\n纬度单位为弧度。", "associated": true }, { @@ -1388,6 +1580,7 @@ "name": "geocentric_latitude", "signature": "pub fn geocentric_latitude(self, geodetic_latitude: f64) -> f64 {", "doc": "Returns the geocentric latitude corresponding to a geodetic\nlatitude, in radians.\n\nThe geocentric latitude is measured from the equatorial plane to the\nradius vector, while the geodetic latitude is measured to the\nellipsoid normal. The geocentric value is the smaller of the two\naway from the equator and the poles.", + "doc_zh": "返回与给定大地纬度对应的地心纬度,单位为弧度。\n\n地心纬度自赤道面量到径向矢量,而大地纬度量到椭球法线。除赤道与极点外,\n地心纬度小于大地纬度。", "associated": true }, { @@ -1395,6 +1588,7 @@ "name": "WGS84", "signature": "pub const WGS84: Ellipsoid = Ellipsoid::new(6_378_137.0, 298.257_223_563);", "doc": "The WGS 84 reference ellipsoid.\n\nThe two defining parameters are the semi-major axis `a = 6 378 137 m` and\nthe inverse flattening `1 / f = 298.257 223 563`.\n\nSource: NIMA TR8350.2, third edition, 2000.", + "doc_zh": "WGS 84 参考椭球。\n\n两个定义参数为半长轴 `a = 6 378 137 m` 与逆扁率 `1 / f = 298.257 223 563`。\n\n出处:NIMA TR8350.2 第三版,2000。", "associated": false }, { @@ -1402,6 +1596,7 @@ "name": "WGS84_GRAVITATIONAL_PARAMETER", "signature": "pub const WGS84_GRAVITATIONAL_PARAMETER: f64 = 3.986_004_418e14;", "doc": "The Earth gravitational parameter for WGS 84, in cubic meters per second\nsquared.\n\nThis is held here rather than in `apsis-core` so that a caller working\nwith the ellipsoid can use the matching gravity field. The value is\n`3.986 004 418e14 m^3/s^2`.", + "doc_zh": "WGS 84 的地球引力参数,单位为立方米每二次方秒。\n\n该值置于此处而非 `apsis-core`,以便处理椭球的调用方使用与之匹配的重力场。\n其值为 `3.986 004 418e14 m^3/s^2`。", "associated": false }, { @@ -1409,6 +1604,7 @@ "name": "Geodetic", "signature": "pub struct Geodetic {", "doc": "A geodetic position: latitude, longitude, and height above an ellipsoid.\n\nLatitude and longitude are in radians and height is in meters. Latitude\nis geodetic, measured between the ellipsoid normal and the equatorial\nplane, and lies in `[-pi/2, pi/2]`. Longitude is measured east from the\nprime meridian. Height is the distance along the ellipsoid normal, taken\nas positive outside the ellipsoid.", + "doc_zh": "大地位置:纬度、经度与椭球高。\n\n纬度与经度用弧度,高用米。纬度为大地纬度,在椭球法线与赤道面之间量取,位于\n`[-pi/2, pi/2]`。经度自本初子午线向东量取。高是沿椭球法线的距离,椭球\n之外取正。", "associated": false }, { @@ -1416,6 +1612,7 @@ "name": "new", "signature": "pub const fn new(latitude: f64, longitude: f64, height: f64) -> Self {", "doc": "Creates a geodetic position from latitude, longitude, and height.", + "doc_zh": "由纬度、经度与高创建大地位置。", "associated": true }, { @@ -1423,6 +1620,7 @@ "name": "to_cartesian", "signature": "pub fn to_cartesian(self, ellipsoid: Ellipsoid) -> Vec3 {", "doc": "Converts this geodetic position to Earth-fixed Cartesian coordinates\non the given ellipsoid.\n\nThe frame has its third axis along the ellipsoid's minor axis through\nthe north pole, its first axis through the intersection of the\nequator and the prime meridian, and its second axis completing a\nright-handed set. The result is in meters.\n\nThe closed form is\n\n```text\nN = a / sqrt(1 - e^2 sin^2(lat))\nx = (N + h) cos(lat) cos(lon)\ny = (N + h) cos(lat) sin(lon)\nz = (N (1 - e^2) + h) sin(lat)\n```\n\nwhere `N` is the prime vertical radius of curvature. This form is\nexact everywhere, including at the poles, where the cosine factors\nvanish.", + "doc_zh": "把该大地位置转换为给定椭球上的地固笛卡尔坐标。\n\n该系的第三轴沿椭球短轴指向北极,第一轴过赤道与本初子午线之交,第二轴构成\n右手系。结果单位为米。\n\n闭式解为\n\n```text\nN = a / sqrt(1 - e^2 sin^2(lat))\nx = (N + h) cos(lat) cos(lon)\ny = (N + h) cos(lat) sin(lon)\nz = (N (1 - e^2) + h) sin(lat)\n```\n\n其中 `N` 为卯酉圈曲率半径。该形式处处精确,包括余弦因子为零的极点。", "associated": true }, { @@ -1430,6 +1628,7 @@ "name": "is_valid", "signature": "pub fn is_valid(self) -> bool {", "doc": "Returns `true` when every field is finite and the latitude and\nlongitude are inside their ranges.", + "doc_zh": "当各字段都有限,且纬度与经度位于其范围内时返回 `true`。", "associated": true }, { @@ -1437,6 +1636,7 @@ "name": "cartesian_from_geodetic", "signature": "pub fn cartesian_from_geodetic(geodetic: Geodetic, ellipsoid: Ellipsoid) -> Vec3 {", "doc": "Converts a geodetic position to Earth-fixed Cartesian coordinates.\n\nSee [`Geodetic::to_cartesian`] for the frame definition and the closed\nform.", + "doc_zh": "把大地位置转换为地固笛卡尔坐标。\n\n坐标系定义与闭式解见 [`Geodetic::to_cartesian`]。", "associated": false }, { @@ -1444,6 +1644,7 @@ "name": "geodetic_from_cartesian", "signature": "pub fn geodetic_from_cartesian(cartesian: Vec3, ellipsoid: Ellipsoid) -> Option {", "doc": "Converts Earth-fixed Cartesian coordinates to a geodetic position.\n\nThe latitude and height are recovered by Bowring's method, which solves\nthe meridian equation in closed form and then refines the result with a\nfew fixed point steps. The iteration converges to well below a\nmicrometer for positions from below the ellipsoid out to geostationary\nradius.\n\nReturns `None` when the position is not finite, or when it lies on the\npolar axis so that no longitude is defined.", + "doc_zh": "把地固笛卡尔坐标转换为大地位置。\n\n纬度与高由 Bowring 方法求得:先以闭式解解出子午线方程,再用若干不动点迭代\n精化。对从椭球下方到地球同步半径范围内的位置,该迭代收敛到远优于一微米。\n\n当位置非有限,或落在极轴上以致经度未定义时返回 `None`。", "associated": false } ] @@ -1451,13 +1652,15 @@ { "name": "apsis-frames", "layer": "physics", - "summary": "Reference frame definitions and the rotation between the inertial and Earth-fixed frames. A reference frame fixes the orientation and origin that a vector is expressed relative to. This crate names the two frames used for near-Earth work and computes the rotation between them. Concrete position types that carry a frame are defined in `apsis-coord`; this crate works with the rotation matrix and the angle that defines it. The inertial frame is the Earth-centered inertial (ECI) frame aligned with the mean equator and equinox of J2000.0. Its axes are fixed in inertial space, so an orbit propagated in it is not affected by the rotation of the Earth. The rotating frame is the Earth-centered Earth-fixed (ECEF) frame. Its third axis is the Earth's rotation axis and its first axis follows the prime meridian, so a point fixed to the ground has constant coordinates in it. The two are related by a rotation of [`EarthRotationAngle`] about the common third axis. This crate models the Earth's rotation with the Earth Rotation Angle, which is a function of UT1 only. It does not apply precession or nutation, because an angle accurate to a few hundredths of a degree is sufficient for pointing and coverage work and the full model needs the orientation of the celestial pole, which is a separate computation. Source: the Earth Rotation Angle expression is from the International Astronomical Union, as given in the *IERS Conventions (2010)*, equation 8.32. ``` use apsis_frames::{EarthRotationAngle, rotation_eci_to_ecef}; // The angle advances by roughly 360.9856 degrees per day. let first = EarthRotationAngle::from_ut1_days(0.0); let second = EarthRotationAngle::from_ut1_days(1.0); let advance = (second.radians() - first.radians()).to_degrees(); assert!((advance - 360.985_612_288_088).abs() < 1e-6); // The rotation is ready to apply to a vector. let _ = rotation_eci_to_ecef(first); ```", + "summary": "Reference frame definitions and the rotation between the inertial and\nEarth-fixed frames.\n\nA reference frame fixes the orientation and origin that a vector is\nexpressed relative to. This crate names the two frames used for\nnear-Earth work and computes the rotation between them. Concrete\nposition types that carry a frame are defined in `apsis-coord`; this\ncrate works with the rotation matrix and the angle that defines it.\n\nThe inertial frame is the Earth-centered inertial (ECI) frame aligned\nwith the mean equator and equinox of J2000.0. Its axes are fixed in\ninertial space, so an orbit propagated in it is not affected by the\nrotation of the Earth.\n\nThe rotating frame is the Earth-centered Earth-fixed (ECEF) frame. Its\nthird axis is the Earth's rotation axis and its first axis follows the\nprime meridian, so a point fixed to the ground has constant coordinates\nin it.\n\nThe two are related by a rotation of [`EarthRotationAngle`] about the\ncommon third axis. This crate models the Earth's rotation with the Earth\nRotation Angle, which is a function of UT1 only. It does not apply\nprecession or nutation, because an angle accurate to a few hundredths of\na degree is sufficient for pointing and coverage work and the full model\nneeds the orientation of the celestial pole, which is a separate\ncomputation.\n\nSource: the Earth Rotation Angle expression is from the International\nAstronomical Union, as given in the *IERS Conventions (2010)*, equation\n8.32.\n\n```\nuse apsis_frames::{EarthRotationAngle, rotation_eci_to_ecef};\n\n// The angle advances by roughly 360.9856 degrees per day.\nlet first = EarthRotationAngle::from_ut1_days(0.0);\nlet second = EarthRotationAngle::from_ut1_days(1.0);\nlet advance = (second.radians() - first.radians()).to_degrees();\nassert!((advance - 360.985_612_288_088).abs() < 1e-6);\n\n// The rotation is ready to apply to a vector.\nlet _ = rotation_eci_to_ecef(first);\n```", + "summary_zh": "参考系定义,以及惯性系与地固系之间的转动。\n\n参考系固定了矢量所相对表达的取向与原点。本 crate 为近地工作所用的两个\n坐标系命名,并计算它们之间的转动。携带坐标系的具位置类型定义在\n`apsis-coord` 中;本 crate 处理转动矩阵以及定义它的角。\n\n惯性系是与 J2000.0 平赤道与平春分点对齐的地心惯性(ECI)系。其各轴在\n惯性空间中固定,故在其中传播的轨道不受地球转动影响。\n\n转动坐标系是地心地固(ECEF)系。其第三轴是地球自转轴,第一轴随本初\n子午线,故固定于地面的点在其中具有恒定坐标。\n\n二者由绕公共第三轴转动 [`EarthRotationAngle`] 相联系。本 crate 用地球\n自转角为地球自转建模,它仅是 UT1 的函数。它不施加岁差或章动,因为精确\n到百分之几度的角对指向与覆盖工作已足够,而完整模型需要天极的取向,那是\n另一项单独的计算。\n\n出处:地球自转角表达式来自国际天文学联合会,如 *IERS Conventions\n(2010)* 式 8.32 所给出。", "items": [ { "kind": "const", "name": "ERA_J2000_TURNS", "signature": "pub const ERA_J2000_TURNS: f64 = 0.0;", "doc": "The Earth Rotation Angle at the J2000.0 epoch, in turns.\n\nThe epoch value is zero by the definition of the angle: the International\nAstronomical Union defines the Earth Rotation Angle to be zero at\n2000-01-01T12:00:00 UT1.", + "doc_zh": "J2000.0 历元处的地球自转角,以周计。\n\n按该角的定义,历元值就是零:国际天文学联合会把 2000-01-01T12:00:00 UT1\n处的地球自转角定义为零。", "associated": false }, { @@ -1465,6 +1668,7 @@ "name": "ERA_RATE_TURNS_PER_DAY", "signature": "pub const ERA_RATE_TURNS_PER_DAY: f64 = 1.002_737_811_911_354_6;", "doc": "The rate of change of the Earth Rotation Angle, in turns per day of UT1.\n\nThis is `1.002 737 811 911 354 6`, which is `360.985 612 288 088`\ndegrees per day. It exceeds one turn per day because the Earth turns once\nin a sidereal day, which is shorter than a solar day.\n\nSource: IERS Conventions (2010), equation 8.32.", + "doc_zh": "地球自转角的变化率,以每 UT1 日的周数计。\n\n该值为 `1.002 737 811 911 354 6`,即每天 `360.985 612 288 088` 度。它\n超过每天一周,因为地球在一个恒星日内转一周,而恒星日短于太阳日。\n\n出处:IERS Conventions(2010),式 8.32。", "associated": false }, { @@ -1472,6 +1676,7 @@ "name": "ut1_days_from_j2000", "signature": "pub fn ut1_days_from_j2000(ut1_julian_date: f64) -> f64 {", "doc": "The number of days from the J2000.0 epoch to the given Julian Date on the\nUT1 scale.\n\nThe J2000.0 epoch is Julian Date 2451545.0, which is noon on\n2000-01-01.", + "doc_zh": "从 J2000.0 历元到给定 UT1 时标儒略日的天数。\n\nJ2000.0 历元是儒略日 2451545.0,即 2000-01-01 的正午。", "associated": false }, { @@ -1479,6 +1684,7 @@ "name": "EarthRotationAngle", "signature": "pub struct EarthRotationAngle {", "doc": "The Earth Rotation Angle at a given instant.\n\nThe angle is the rotation from the celestial intermediate origin to the\nterrestrial intermediate origin, measured about the celestial\nintermediate pole. It replaces Greenwich Mean Sidereal Time in modern\npractice and, unlike sidereal time, is a linear function of UT1.\n\nThe value is held in turns. A turn is one full revolution, so the angle\nis periodic with a period of one. The value returned is not reduced to\nany interval, so differences between two angles are meaningful even when\nthe interval is longer than a day.", + "doc_zh": "某瞬时的地球自转角。\n\n该角是从天球中间原点到地球中间原点的转动,绕天球中间极量取。它在现代\n实践中取代格林尼治平恒星时,且与恒星时不同,它是 UT1 的线性函数。\n\n该值以周保存。一周即一整圈,故该角以 1 为周期。所返回的值不归约到任何\n区间,故即便区间长于一日,两角之差仍有意义。", "associated": false }, { @@ -1486,6 +1692,7 @@ "name": "from_turns", "signature": "pub const fn from_turns(turns: f64) -> Self {", "doc": "Creates an angle from a value in turns.", + "doc_zh": "由以周表示的值创建角。", "associated": true }, { @@ -1493,6 +1700,7 @@ "name": "from_radians", "signature": "pub fn from_radians(radians: f64) -> Self {", "doc": "Creates an angle from a value in radians.", + "doc_zh": "由以弧度表示的值创建角。", "associated": true }, { @@ -1500,6 +1708,7 @@ "name": "from_ut1_julian_date", "signature": "pub fn from_ut1_julian_date(ut1_julian_date: f64) -> Self {", "doc": "Returns the angle for a Julian Date on the UT1 scale.", + "doc_zh": "返回 UT1 时标上某儒略日对应的角。", "associated": true }, { @@ -1507,6 +1716,7 @@ "name": "from_ut1_days", "signature": "pub fn from_ut1_days(days_since_j2000: f64) -> Self {", "doc": "Returns the angle for a number of days of UT1 since the J2000.0\nepoch.\n\nThe expression is linear: `theta = 0.779 057 273 2640 + 1.002 737\n909 350 795 * d`, where `d` is the interval in days. The constant\nterm of `0.779 057` turns is the angle at the epoch itself, which is\nabout 280.46 degrees.\n\nSource: IERS Conventions (2010), equation 8.32.", + "doc_zh": "返回自 J2000.0 历元起的若干 UT1 天对应的角。\n\n该式为线性:`theta = 0.779 057 273 2640 + 1.002 737 909 350 795 * d`,\n其中 `d` 是以天计的间隔。`0.779 057` 周的常数项是历元本身的角,\n约为 280.46 度。\n\n出处:IERS Conventions(2010),式 8.32。", "associated": true }, { @@ -1514,6 +1724,7 @@ "name": "turns", "signature": "pub const fn turns(self) -> f64 {", "doc": "Returns the angle in turns.\n\nThe value is not reduced to `[0, 1)`.", + "doc_zh": "返回以周表示的角。\n\n该值不归约到 `[0, 1)`。", "associated": true }, { @@ -1521,6 +1732,7 @@ "name": "turns_positive", "signature": "pub fn turns_positive(self) -> f64 {", "doc": "Returns the angle in turns, reduced to `[0, 1)`.", + "doc_zh": "返回以周表示的角,归约到 `[0, 1)`。", "associated": true }, { @@ -1528,6 +1740,7 @@ "name": "radians", "signature": "pub fn radians(self) -> f64 {", "doc": "Returns the angle in radians.\n\nThe value is not reduced to `[0, 2 pi)`.", + "doc_zh": "返回以弧度表示的角。\n\n该值不归约到 `[0, 2 pi)`。", "associated": true }, { @@ -1535,6 +1748,7 @@ "name": "radians_positive", "signature": "pub fn radians_positive(self) -> f64 {", "doc": "Returns the angle in radians, reduced to `[0, 2 pi)`.", + "doc_zh": "返回以弧度表示的角,归约到 `[0, 2 pi)`。", "associated": true }, { @@ -1542,6 +1756,7 @@ "name": "rotation_eci_to_ecef", "signature": "pub fn rotation_eci_to_ecef(angle: EarthRotationAngle) -> Mat3 {", "doc": "Returns the rotation that maps a vector in the inertial frame to the\nEarth-fixed frame.\n\nThe rotation is a clockwise turn about the third axis by the Earth\nRotation Angle. Applying it to an inertial vector yields the Earth-fixed\nvector, so a vector that is fixed in inertial space acquires a negative\nrotation as the Earth turns beneath it.", + "doc_zh": "返回把惯性系中的矢量映射到地固系的转动。\n\n该转动是绕第三轴按地球自转角作顺时针转。把它施加于惯性矢量即得地固\n矢量,故在惯性空间固定的矢量会随地球在其下方转动而获得一个负的\n转动。", "associated": false }, { @@ -1549,6 +1764,7 @@ "name": "rotation_ecef_to_eci", "signature": "pub fn rotation_ecef_to_eci(angle: EarthRotationAngle) -> Mat3 {", "doc": "Returns the rotation that maps a vector in the Earth-fixed frame to the\ninertial frame.\n\nThis is the transpose of [`rotation_eci_to_ecef`], which for a rotation\nmatrix is also its inverse.", + "doc_zh": "返回把地固系中的矢量映射到惯性系的转动。\n\n它是 [`rotation_eci_to_ecef`] 的转置,对旋转矩阵而言也是其逆。", "associated": false }, { @@ -1556,6 +1772,7 @@ "name": "mean_anomaly_moon", "signature": "pub fn mean_anomaly_moon(t: f64) -> f64 {", "doc": "Returns the mean anomaly of the Moon, in radians.\n\nThis is the Delaunay argument `l`, from the IERS 2003 expressions.", + "doc_zh": "返回月球的平近点角,单位为弧度。\n\n这是 Delaunay 参数 `l`,取自 IERS 2003 表达式。", "associated": false }, { @@ -1563,6 +1780,7 @@ "name": "mean_anomaly_sun", "signature": "pub fn mean_anomaly_sun(t: f64) -> f64 {", "doc": "Returns the mean anomaly of the Sun, in radians.\n\nThis is the Delaunay argument `l'`. The reference evaluates it inline\nrather than through the general function, and the two differ slightly.", + "doc_zh": "返回太阳的平近点角,单位为弧度。\n\n这是 Delaunay 参数 `l'`。参考实现是内联求值而非经通用函数,二者略有\n差异。", "associated": false }, { @@ -1570,6 +1788,7 @@ "name": "mean_anomaly_sun_iers2003", "signature": "pub fn mean_anomaly_sun_iers2003(t: f64) -> f64 {", "doc": "Returns the mean anomaly of the Sun from the IERS 2003 expressions.\n\nThis differs from [`mean_anomaly_sun`] in the last digits of the constant\nand linear terms. The nutation series uses the inline form; the\ncomplementary terms of the equation of the equinoxes use this one.\n\nSource: as implemented by `eraFalp03`.", + "doc_zh": "返回取自 IERS 2003 表达式的太阳平近点角。\n\n它与 [`mean_anomaly_sun`] 在常数项与线性项的最后几位上不同。章动级数\n用内联形式;分点方程式的补充项用这一个。\n\n出处:按 `eraFalp03` 所实现。", "associated": false }, { @@ -1577,6 +1796,7 @@ "name": "mean_elongation_moon_sun_iers2003", "signature": "pub fn mean_elongation_moon_sun_iers2003(t: f64) -> f64 {", "doc": "Returns the mean elongation of the Moon from the Sun from the IERS 2003\nexpressions.\n\nThis differs from [`mean_elongation_moon_sun`] in the last digits of the\nconstant term. The nutation series uses the inline form; the\ncomplementary terms of the equation of the equinoxes use this one.\n\nSource: as implemented by `eraFad03`.", + "doc_zh": "返回取自 IERS 2003 表达式的月球相对太阳的平距角。\n\n它与 [`mean_elongation_moon_sun`] 在常数项的最后几位上不同。章动级数\n用内联形式;分点方程式的补充项用这一个。\n\n出处:按 `eraFad03` 所实现。", "associated": false }, { @@ -1584,6 +1804,7 @@ "name": "mean_latitude_moon", "signature": "pub fn mean_latitude_moon(t: f64) -> f64 {", "doc": "Returns the mean argument of latitude of the Moon, in radians.\n\nThis is the Delaunay argument `F`, the mean longitude of the Moon minus\nthe mean longitude of its ascending node.", + "doc_zh": "返回月球的平纬度参数,单位为弧度。\n\n这是 Delaunay 参数 `F`,即月球平黄经减去其升交点平黄经。", "associated": false }, { @@ -1591,6 +1812,7 @@ "name": "mean_elongation_moon_sun", "signature": "pub fn mean_elongation_moon_sun(t: f64) -> f64 {", "doc": "Returns the mean elongation of the Moon from the Sun, in radians.\n\nThis is the Delaunay argument `D`. The reference evaluates it inline.", + "doc_zh": "返回月球相对太阳的平距角,单位为弧度。\n\n这是 Delaunay 参数 `D`。参考实现是内联求值。", "associated": false }, { @@ -1598,6 +1820,7 @@ "name": "mean_longitude_node", "signature": "pub fn mean_longitude_node(t: f64) -> f64 {", "doc": "Returns the mean longitude of the Moon's ascending node, in radians.\n\nThis is the Delaunay argument `Om`.", + "doc_zh": "返回月球升交点的平黄经,单位为弧度。\n\n这是 Delaunay 参数 `Om`。", "associated": false }, { @@ -1605,6 +1828,7 @@ "name": "mean_longitude_mercury", "signature": "pub fn mean_longitude_mercury(t: f64) -> f64 {", "doc": "Returns the mean longitude of Mercury, in radians.", + "doc_zh": "返回水星的平黄经,单位为弧度。", "associated": false }, { @@ -1612,6 +1836,7 @@ "name": "mean_longitude_venus", "signature": "pub fn mean_longitude_venus(t: f64) -> f64 {", "doc": "Returns the mean longitude of Venus, in radians.", + "doc_zh": "返回金星的平黄经,单位为弧度。", "associated": false }, { @@ -1619,6 +1844,7 @@ "name": "mean_longitude_earth", "signature": "pub fn mean_longitude_earth(t: f64) -> f64 {", "doc": "Returns the mean longitude of the Earth, in radians.", + "doc_zh": "返回地球的平黄经,单位为弧度。", "associated": false }, { @@ -1626,6 +1852,7 @@ "name": "mean_longitude_mars", "signature": "pub fn mean_longitude_mars(t: f64) -> f64 {", "doc": "Returns the mean longitude of Mars, in radians.", + "doc_zh": "返回火星的平黄经,单位为弧度。", "associated": false }, { @@ -1633,6 +1860,7 @@ "name": "mean_longitude_jupiter", "signature": "pub fn mean_longitude_jupiter(t: f64) -> f64 {", "doc": "Returns the mean longitude of Jupiter, in radians.", + "doc_zh": "返回木星的平黄经,单位为弧度。", "associated": false }, { @@ -1640,6 +1868,7 @@ "name": "mean_longitude_saturn", "signature": "pub fn mean_longitude_saturn(t: f64) -> f64 {", "doc": "Returns the mean longitude of Saturn, in radians.", + "doc_zh": "返回土星的平黄经,单位为弧度。", "associated": false }, { @@ -1647,6 +1876,7 @@ "name": "mean_longitude_uranus", "signature": "pub fn mean_longitude_uranus(t: f64) -> f64 {", "doc": "Returns the mean longitude of Uranus, in radians.", + "doc_zh": "返回天王星的平黄经,单位为弧度。", "associated": false }, { @@ -1654,6 +1884,7 @@ "name": "mean_longitude_neptune", "signature": "pub fn mean_longitude_neptune(t: f64) -> f64 {", "doc": "Returns the mean longitude of Neptune, in radians.\n\nThe reference evaluates this inline, from the MHB2000 model rather than\nfrom the IERS 2003 expressions used for the other planets.", + "doc_zh": "返回海王星的平黄经,单位为弧度。\n\n参考实现是内联求值,取自 MHB2000 模型,而非用于其余行星的 IERS 2003\n表达式。", "associated": false }, { @@ -1661,6 +1892,7 @@ "name": "general_precession", "signature": "pub fn general_precession(t: f64) -> f64 {", "doc": "Returns the general accumulated precession in longitude, in radians.", + "doc_zh": "返回黄经上的一般累积岁差,单位为弧度。", "associated": false }, { @@ -1668,6 +1900,7 @@ "name": "DelaunayArguments", "signature": "pub struct DelaunayArguments {", "doc": "The five Delaunay arguments, in the order the luni-solar series uses.", + "doc_zh": "五个 Delaunay 参数,按日月级数所用的次序排列。", "associated": false }, { @@ -1675,6 +1908,7 @@ "name": "at", "signature": "pub fn at(t: f64) -> Self {", "doc": "Returns the five arguments for a time in Julian centuries.", + "doc_zh": "返回某儒略世纪时刻的这五个参数。", "associated": true }, { @@ -1682,6 +1916,7 @@ "name": "as_array", "signature": "pub const fn as_array(self) -> [f64; 5] {", "doc": "Returns the arguments as an array in series order.", + "doc_zh": "按级数次序把各参数作为数组返回。", "associated": true }, { @@ -1689,6 +1924,7 @@ "name": "PlanetaryArguments", "signature": "pub struct PlanetaryArguments {", "doc": "The thirteen arguments of the planetary series, in series order.\n\nThe order is `l`, `F`, `D`, `Om`, Mercury through Uranus, Neptune, and the\ngeneral accumulated precession. Note that `l'` does not appear; the\nplanetary series uses the Moon's mean anomaly, not the Sun's.", + "doc_zh": "行星级数的十三个参数,按级数次序排列。\n\n次序为 `l`、`F`、`D`、`Om`、水星到天王星、海王星,以及一般累积岁差。\n注意 `l'` 不出现;行星级数用的是月球的平近点角,而非太阳的。", "associated": false }, { @@ -1696,6 +1932,7 @@ "name": "as_array", "signature": "pub const fn as_array(self) -> [f64; 13] {", "doc": "Returns the arguments as an array in series order.", + "doc_zh": "按级数次序把各参数作为数组返回。", "associated": true }, { @@ -1703,6 +1940,7 @@ "name": "SeriesTerm", "signature": "pub struct SeriesTerm {", "doc": "One term of the luni-solar nutation series.\n\nThe five multipliers are applied to the Delaunay arguments in the order\n`l`, `l'`, `F`, `D`, `Om`. The six coefficients are the longitude sine,\nits time derivative, the longitude cosine, the obliquity cosine, its time\nderivative, and the obliquity sine, in units of 0.1 microarcsecond.", + "doc_zh": "日月章动级数的一项。\n\n五个乘数按 `l`、`l'`、`F`、`D`、`Om` 的次序施加于 Delaunay 参数。六个\n系数依次是黄经正弦、其时间导数、黄经余弦、黄赤交角余弦、其时间导数,\n以及黄赤交角正弦,单位为 0.1 微角秒。", "associated": false }, { @@ -1710,6 +1948,7 @@ "name": "ComplementaryTerm", "signature": "pub struct ComplementaryTerm {", "doc": "One term of the complementary terms of the equation of the equinoxes.\n\nThe eight multipliers apply to the arguments `l`, `l'`, `F`, `D`, `Om`,\nthe mean longitude of Venus, the mean longitude of the Earth, and the\ngeneral accumulated precession. The two coefficients are the sine and\ncosine amplitudes, in arcseconds.", + "doc_zh": "分点方程式补充项的一项。\n\n八个乘数施加于参数 `l`、`l'`、`F`、`D`、`Om`、金星的平黄经、地球的平\n黄经,以及一般累积岁差。两个系数是正弦与余弦幅度,单位为角秒。", "associated": false }, { @@ -1717,6 +1956,7 @@ "name": "PlanetaryTerm", "signature": "pub struct PlanetaryTerm {", "doc": "One term of the planetary nutation series.\n\nThe thirteen multipliers are applied to the planetary arguments in the\norder `l`, `F`, `D`, `Om`, Mercury through Uranus, Neptune, and the\ngeneral accumulated precession. The four coefficients are the longitude\nsine, the longitude cosine, the obliquity sine, and the obliquity cosine,\nin units of 0.1 microarcsecond.", + "doc_zh": "行星章动级数的一项。\n\n十三个乘数按 `l`、`F`、`D`、`Om`、水星到天王星、海王星,以及一般累积\n岁差的次序施加于行星参数。四个系数依次是黄经正弦、黄经余弦、黄赤交角\n正弦与黄赤交角余弦,单位为 0.1 微角秒。", "associated": false }, { @@ -1724,6 +1964,7 @@ "name": "Nutation", "signature": "pub struct Nutation {", "doc": "The nutation in longitude and obliquity, in radians.", + "doc_zh": "黄经与黄赤交角方向的章动,单位为弧度。", "associated": false }, { @@ -1731,6 +1972,7 @@ "name": "new", "signature": "pub const fn new(longitude: f64, obliquity: f64) -> Self {", "doc": "Creates a nutation from its two angles.", + "doc_zh": "由其两个角创建章动。", "associated": true }, { @@ -1738,6 +1980,7 @@ "name": "longitude_arcseconds", "signature": "pub fn longitude_arcseconds(self) -> f64 {", "doc": "Returns the nutation in longitude, in arcseconds.", + "doc_zh": "返回黄经方向的章动,单位为角秒。", "associated": true }, { @@ -1745,6 +1988,7 @@ "name": "obliquity_arcseconds", "signature": "pub fn obliquity_arcseconds(self) -> f64 {", "doc": "Returns the nutation in obliquity, in arcseconds.", + "doc_zh": "返回黄赤交角方向的章动,单位为角秒。", "associated": true }, { @@ -1752,6 +1996,7 @@ "name": "plus", "signature": "pub const fn plus(self, other: Self) -> Self {", "doc": "Returns the sum of two nutations.", + "doc_zh": "返回两个章动之和。", "associated": true }, { @@ -1759,6 +2004,7 @@ "name": "nutation_2000a", "signature": "pub fn nutation_2000a(t: f64) -> Nutation {", "doc": "Returns the nutation in longitude and obliquity for a time.\n\nThe time is in Julian centuries from J2000.0 on the Terrestrial Time\nscale. This is the IAU 2000A model, which includes the planetary terms.\n\nSource: IAU 2000A, as implemented by `eraNut00a`.", + "doc_zh": "返回某时刻的黄经与黄赤交角方向的章动。\n\n时间以 J2000.0 起的儒略世纪计,位于地球时(TT)标度。这是 IAU 2000A\n模型,含行星项。\n\n出处:IAU 2000A,按 `eraNut00a` 所实现。", "associated": false }, { @@ -1766,6 +2012,7 @@ "name": "nutation_2000a_with_2006_adjustment", "signature": "pub fn nutation_2000a_with_2006_adjustment(t: f64) -> Nutation {", "doc": "Returns the nutation with the IAU 2006 precession adjustments applied.\n\nThe IAU 2000A model was built on the IAU 1976 precession, and the IAU\n2006 precession changed the precession rates. The adjustment brings the\nnutation into consistency with the newer precession, which is the\ncombination used for the modern celestial-to-terrestrial transformation.\n\nSource: IAU 2006, as implemented by `eraNut06a`, which applies the\nexpressions of Wallace and Capitaine (2006), equations 5.", + "doc_zh": "返回施加 IAU 2006 岁差调整后的章动。\n\nIAU 2000A 模型建立在 IAU 1976 岁差之上,而 IAU 2006 岁差改变了岁差\n速率。该调整使章动与较新的岁差一致,这一组合正是现代天球到地球转换\n所用的。\n\n出处:IAU 2006,按 `eraNut06a` 所实现,它施加 Wallace 与\nCapitaine(2006)式 5 的表达式。", "associated": false }, { @@ -1773,6 +2020,7 @@ "name": "FRAME_BIAS_RIGHT_ASCENSION", "signature": "pub const FRAME_BIAS_RIGHT_ASCENSION: f64 = -0.014_6 * ARCSECOND_TO_RADIAN;", "doc": "The frame bias in right ascension, in radians.\n\nThe offset between the origin of the International Celestial Reference\nSystem and the dynamical equinox of J2000.0. The value is\nminus 0.0146 arcseconds.", + "doc_zh": "赤经方向的坐标架偏差,单位为弧度。\n\n国际天球参考系原点与 J2000.0 动力学春分点之间的偏移。该值为负\n0.0146 角秒。", "associated": false }, { @@ -1780,6 +2028,7 @@ "name": "FRAME_BIAS_DECLINATION", "signature": "pub const FRAME_BIAS_DECLINATION: f64 = -0.006_819_2 * ARCSECOND_TO_RADIAN;", "doc": "The frame bias in declination, in radians.\n\nThe offset between the pole of the International Celestial Reference\nSystem and the mean pole of J2000.0. The value is minus 0.0068192\narcseconds.", + "doc_zh": "赤纬方向的坐标架偏差,单位为弧度。\n\n国际天球参考系极与 J2000.0 平极之间的偏移。该值为负 0.0068192\n角秒。", "associated": false }, { @@ -1787,6 +2036,7 @@ "name": "FRAME_BIAS_LONGITUDE", "signature": "pub const FRAME_BIAS_LONGITUDE: f64 = -0.041_775 * ARCSECOND_TO_RADIAN;", "doc": "The frame bias in longitude, in radians.\n\nThe offset between the two origins measured along the equator. The value\nis minus 0.041775 arcseconds.", + "doc_zh": "黄经方向的坐标架偏差,单位为弧度。\n\n沿赤道量得的两个原点之间的偏移。该值为负 0.041775 角秒。", "associated": false }, { @@ -1794,6 +2044,7 @@ "name": "PrecessionAngles", "signature": "pub struct PrecessionAngles {", "doc": "The four angles that describe the precession from J2000.0 to a date.\n\nThey are applied in the order the reference uses: a rotation by\n`gamma_bar` about the third axis, then by `phi_bar` about the first, then\nby minus `psi_bar` about the third, then by minus `epsilon_a` about the\nfirst.", + "doc_zh": "描述从 J2000.0 到某日岁差的四个角。\n\n它们按参考实现所用的次序施加:绕第三轴转 `gamma_bar`,再绕第一轴转\n`phi_bar`,再绕第三轴转负的 `psi_bar`,再绕第一轴转负的\n`epsilon_a`。", "associated": false }, { @@ -1801,6 +2052,7 @@ "name": "mean_obliquity", "signature": "pub fn mean_obliquity(t: f64) -> f64 {", "doc": "Returns the mean obliquity of the ecliptic, in radians.\n\nThe polynomial is the IAU 2006 expression, evaluated for a time in Julian\ncenturies from J2000.0 on the Terrestrial Time scale. The constant term\nis 84381.406 arcseconds, which is the obliquity at J2000.0.\n\nSource: the IAU 2006 precession, as implemented by `eraObl06`.", + "doc_zh": "返回黄道的平黄赤交角,单位为弧度。\n\n该多项式是 IAU 2006 表达式,按地球时标、自 J2000.0 起的儒略世纪求值。\n常数项为 84381.406 角秒,即 J2000.0 处的黄赤交角。\n\n出处:IAU 2006 岁差,按 `eraObl06` 所实现。", "associated": false }, { @@ -1808,6 +2060,7 @@ "name": "precession_angles", "signature": "pub fn precession_angles(t: f64) -> PrecessionAngles {", "doc": "Returns the four precession angles for a time.\n\nThe time is in Julian centuries from J2000.0 on the Terrestrial Time\nscale.\n\nSource: the P03 precession, as implemented by `eraPfw06`.", + "doc_zh": "返回某时刻的四个岁差角。\n\n时间以 J2000.0 起的儒略世纪计,位于地球时(TT)标度。\n\n出处:P03 岁差,按 `eraPfw06` 所实现。", "associated": false }, { @@ -1815,6 +2068,7 @@ "name": "frame_bias_matrix", "signature": "pub fn frame_bias_matrix() -> Mat3 {", "doc": "Returns the frame bias rotation matrix.\n\nThe bias is a constant rotation from the International Celestial\nReference System to the dynamical mean equator and equinox of J2000.0.\nIts three angles are far below a tenth of an arcsecond, but omitting it\nwould leave a systematic error that is larger than the precision of the\nnutation series.\n\nSource: as implemented by `eraBi00`, which gives the bias as three small\nrotations rather than as a matrix.", + "doc_zh": "返回坐标架偏差旋转矩阵。\n\n该偏差是从国际天球参考系到 J2000.0 动力学平赤道与平春分点的常值\n转动。其三个角远小于十分之一角秒,但略去它会留下大于章动级数精度\n的系统误差。\n\n出处:按 `eraBi00` 所实现,它把偏差给出为三个小转动而非一个矩阵。", "associated": false }, { @@ -1822,6 +2076,7 @@ "name": "bias_precession_matrix", "signature": "pub fn bias_precession_matrix(t: f64) -> Mat3 {", "doc": "Returns the rotation from the celestial reference frame to the mean\nequator and equinox of date.\n\nThis is the bias-precession matrix. Nutation is applied on top of it to\nreach the true equator and equinox of date.\n\nThe time is in Julian centuries from J2000.0 on the Terrestrial Time\nscale.\n\nSource: as implemented by `eraPmat06`.", + "doc_zh": "返回从天球参考系到该日平赤道与平春分点的转动。\n\n这是偏差-岁差矩阵。章动施加于其上,以到达该日的真赤道与真春分点。\n\n时间以 J2000.0 起的儒略世纪计,位于地球时(TT)标度。\n\n出处:按 `eraPmat06` 所实现。", "associated": false }, { @@ -1829,6 +2084,7 @@ "name": "frame_to_mean_of_date", "signature": "pub fn frame_to_mean_of_date(angles: &PrecessionAngles) -> Mat3 {", "doc": "Returns the rotation from the celestial reference frame to the mean\nequator and equinox of date, given precomputed precession angles.\n\nSource: as implemented by `eraFw2m`, which applies the four elementary\nrotations in sequence. The reference helper composes each new rotation\non the left of the accumulator, so the product here is written with the\nlast rotation applied first, in the same order.", + "doc_zh": "在给定预先算出的岁差角时,返回从天球参考系到该日平赤道与平春分点的\n转动。\n\n出处:按 `eraFw2m` 所实现,它依次施加四个基本转动。参考辅助函数把\n每个新转动合成到累加器的左侧,故此处的乘积按最后施加的转动写在最前,\n次序一致。", "associated": false }, { @@ -1836,6 +2092,7 @@ "name": "rotation_z", "signature": "pub fn rotation_z(angle: f64) -> Mat3 {", "doc": "Returns a rotation of `angle` about the third axis, in radians.\n\nThe sign convention matches the other axis rotations in this module.", + "doc_zh": "返回绕第三轴转 `angle` 的旋转,单位为弧度。\n\n符号约定与本模块中其余各轴转动一致。", "associated": false }, { @@ -1843,6 +2100,7 @@ "name": "greenwich_mean_sidereal_time_1982", "signature": "pub fn greenwich_mean_sidereal_time_1982(day: f64, fraction: f64) -> f64 {", "doc": "Returns Greenwich mean sidereal time from the 1982 expression, in\nradians.\n\nThe expression is a polynomial in the number of Julian centuries from\nJ2000.0 plus the fraction of the day in seconds of time. It is the\nsidereal time the SGP4 theory was fitted against, which is why it is used\nhere rather than the modern expression: the element sets are consistent\nwith it.\n\nThe Julian date is taken in two parts, a whole number of days and a\nfraction, because a single `f64` near 2 461 000 has a resolution of about\n40 microseconds and the sidereal time is wanted to better than that. The\ncaller that has only one value may pass the whole part and the fraction\nseparately, or pass a value twice: `(jd, 0.0)`, `(2400000.5, mjd)`, and\n`(jd - 0.5, 0.5)` are all accepted, but the split with the smaller whole\npart preserves more of the fraction.\n\nSource: as implemented by `eraGmst82`.", + "doc_zh": "返回 1982 表达式给出的格林尼治平恒星时,单位为弧度。\n\n该表达式是自 J2000.0 起儒略世纪数加上当日以时间秒计的零头所构成的多项\n式。它是 SGP4 理论所拟合的恒星时,故此处用它而非现代表达式:根数集与\n它一致。\n\n儒略日按两部分取,一为整数天数、一为零头,因为接近 2 461 000 的单个\n`f64` 分辨率约为 40 微秒,而所要求的恒星时优于该值。只有一个值的调用\n方可以分别传入整数部分与零头,或把同一个值传两次:`(jd, 0.0)`、\n`(2400000.5, mjd)` 与 `(jd - 0.5, 0.5)` 都可接受,但整数部分更小的\n拆分能保留更多零头。", "associated": false }, { @@ -1850,6 +2108,7 @@ "name": "teme_to_terrestrial_matrix", "signature": "pub fn teme_to_terrestrial_matrix( ut1_day: f64, ut1_fraction: f64, polar_motion: PolarMotion, ) -> Mat3 {", "doc": "Returns the rotation from the True Equator Mean Equinox frame to the\nEarth-fixed frame.\n\nThe argument is the UT1 Julian date, because the rotation is the Earth's\nrotation, and the polar motion is supplied by the caller because it is\nmeasured rather than modelled.\n\nThis is the transformation to apply to the position and velocity that\n`apsis-sgp4` produces.", + "doc_zh": "返回从真赤道平春分点坐标系到地固系的转动。\n\n参数是 UT1 儒略日,因为该转动即地球自转;极移由调用方提供,因为它是\n实测而非建模的。\n\n这是施加于 `apsis-sgp4` 所产生的位置与速度的变换。", "associated": false }, { @@ -1857,6 +2116,7 @@ "name": "bias_precession_nutation_matrix", "signature": "pub fn bias_precession_nutation_matrix(t: f64) -> Mat3 {", "doc": "Returns the bias-precession-nutation matrix for a time.\n\nThe matrix carries a vector from the celestial reference frame to the\ntrue equator and equinox of date. It is built from the four precession\nangles with the nutation added to the last two, which is the form the\nreference uses.\n\nSource: as implemented by `eraPnm06a` and `eraFw2m`.", + "doc_zh": "返回某时刻的偏差-岁差-章动矩阵。\n\n该矩阵把矢量从天球参考系带到该日的真赤道与真春分点。它由四个岁差角\n构成,其中最后两个角加上章动,这正是参考实现所用的形式。\n\n出处:按 `eraPnm06a` 与 `eraFw2m` 所实现。", "associated": false }, { @@ -1864,6 +2124,7 @@ "name": "equation_of_the_equinoxes_complementary_terms", "signature": "pub fn equation_of_the_equinoxes_complementary_terms(t: f64) -> f64 {", "doc": "Returns the complementary terms of the equation of the equinoxes.\n\nThe equation of the equinoxes has, besides the projection of the nutation\nin longitude onto the true equator, a set of small periodic terms from\nthe same theory. They reach about twice the constant term of the\nosculating nutation, which is a millisecond of time, so they matter for a\nsidereal time that is quoted to that precision.\n\nSource: the MHB2000 complementary terms, as implemented by `eraEect00`.", + "doc_zh": "返回分点方程式的补充项。\n\n分点方程式除黄经章动在真赤道上的投影外,还有来自同一理论的一组小\n周期项。它们达到密切章动常数项的两倍左右,即一毫秒的时间,故对以该\n精度给出的恒星时是重要的。\n\n出处:MHB2000 补充项,按 `eraEect00` 所实现。", "associated": false }, { @@ -1871,6 +2132,7 @@ "name": "equation_of_the_equinoxes", "signature": "pub fn equation_of_the_equinoxes(t: f64) -> f64 {", "doc": "Returns the equation of the equinoxes for a time.\n\nThe equation of the equinoxes is the difference between the true and mean\nequinoxes measured along the true equator. It is the quantity a sidereal\ntime must be corrected by to become the hour angle of the true equinox.\n\nThe value is the projection of the nutation in longitude onto the true\nequator, `dpsi cos(eps)`, plus the complementary terms of the theory.", + "doc_zh": "返回某时刻的分点方程式。\n\n分点方程式是沿真赤道量得的真春分点与平春分点之差。它是恒星时须用以\n改正才能成为真春分点时角的量。\n\n该值是黄经章动在真赤道上的投影 `dpsi cos(eps)`,加上该理论的补充项。", "associated": false }, { @@ -1878,6 +2140,7 @@ "name": "PolarMotion", "signature": "pub struct PolarMotion {", "doc": "The polar motion of the Earth, as two angles and the rotation.\n\nThe polar motion is the motion of the rotation pole within the Earth. It\nis measured daily and published by the International Earth Rotation and\nReference Systems Service, so it cannot be computed from a model and is\nsupplied by the caller.", + "doc_zh": "地球的极移,以两个角与转动表示。\n\n极移是自转极在地球内部的运动。它由国际地球自转与参考系服务每日测量\n并发布,故无法由模型算出,而由调用方提供。", "associated": false }, { @@ -1885,6 +2148,7 @@ "name": "NONE", "signature": "pub const NONE: Self = Self {", "doc": "The zero polar motion, which is the approximation used when the\npublished values are not available.\n\nThe error from omitting the polar motion is under a tenth of an\narcsecond, well below the nutation but above the precision of a\nmodern transformation.", + "doc_zh": "零极移,即无法获得已发布值时所采用的近似。\n\n略去极移的误差不到十分之一角秒,远低于章动,但高于现代转换的\n精度。", "associated": true }, { @@ -1892,6 +2156,7 @@ "name": "new", "signature": "pub const fn new(x: f64, y: f64, s: f64) -> Self {", "doc": "Creates a polar motion from its three angles.", + "doc_zh": "由其三个角创建极移。", "associated": true }, { @@ -1899,6 +2164,7 @@ "name": "is_valid", "signature": "pub fn is_valid(self) -> bool {", "doc": "Returns `true` when the angles are usable.", + "doc_zh": "当各角可用时返回 `true`。", "associated": true }, { @@ -1906,6 +2172,7 @@ "name": "matrix", "signature": "pub fn matrix(self) -> Mat3 {", "doc": "Returns the rotation matrix of the polar motion.\n\nThe rotation is a turn about the third axis by the locator, then\nabout the second by minus the `x` displacement, then about the first\nby minus the `y` displacement. The signs are the ones the reference\nuses, which carry the terrestrial frame into alignment with the\ncelestial pole.\n\nSource: as implemented by `eraPom00`, which applies the rotations in\nsequence by composing each on the left of the accumulator.", + "doc_zh": "返回极移的旋转矩阵。\n\n该转动是绕第三轴转定位角,再绕第二轴转负的 `x` 位移,再绕第一轴\n转负的 `y` 位移。符号是参考实现所用者,它们把地球坐标系带到与\n天极对齐。\n\n出处:按 `eraPom00` 所实现,它依次施加各转动,每次都把新转动合成\n到累加器的左侧。", "associated": true }, { @@ -1913,6 +2180,7 @@ "name": "intermediate_to_terrestrial_matrix", "signature": "pub fn intermediate_to_terrestrial_matrix(ut1_julian_date: f64, tt_julian_date: f64) -> Mat3 {", "doc": "Returns the intermediate-to-terrestrial matrix for a time.\n\nThe intermediate frame is the true equator and equinox of date, and the\nterrestrial frame is the Earth-fixed frame at the same date. This uses\nthe equinox-based approach, in which the rotation from one to the other\nis Greenwich apparent sidereal time.\n\nThe time arguments are the UT1 and TT Julian dates.\n\nSource: the composition is the one `eraC2t06a` produces for the equinox\nbased chain, with the polar motion applied separately by the caller of\nthe full transformation.", + "doc_zh": "返回某时刻的中间坐标系到地球坐标系的矩阵。\n\n中间坐标系是该日的真赤道与真春分点,地球坐标系是同一日的地固系。这里\n用基于春分点的方法,其中从一个到另一个的转动是格林尼治视恒星时。\n\n时间参数为 UT1 与 TT 儒略日。\n\n出处:该合成即 `eraC2t06a` 对基于春分点的链条所产生者,极移由完整\n变换的调用方单独施加。", "associated": false }, { @@ -1920,6 +2188,7 @@ "name": "greenwich_apparent_sidereal_time", "signature": "pub fn greenwich_apparent_sidereal_time(ut1_julian_date: f64, tt_julian_date: f64) -> f64 {", "doc": "Returns Greenwich apparent sidereal time for a UT1 and a TT Julian date.\n\nThis is the mean sidereal time plus the equation of the equinoxes, so it\nis the hour angle of the true equinox.\n\nSource: as implemented by `eraGst06a`.", + "doc_zh": "返回某 UT1 与 TT 儒略日的格林尼治视恒星时。\n\n它是平恒星时加分点方程式,故即真春分点的时角。\n\n出处:按 `eraGst06a` 所实现。", "associated": false }, { @@ -1927,6 +2196,7 @@ "name": "greenwich_mean_sidereal_time", "signature": "pub fn greenwich_mean_sidereal_time(ut1_julian_date: f64, tt_julian_date: f64) -> f64 {", "doc": "Returns Greenwich mean sidereal time for a UT1 and a TT Julian date.\n\nThe relation is the Earth rotation angle plus the accumulated precession\nin right ascension, which is the quantity that relates the equinox to the\nnon-rotating origin.\n\nSource: as implemented by `eraGmst06`.", + "doc_zh": "返回某 UT1 与 TT 儒略日的格林尼治平恒星时。\n\n关系为地球自转角加上赤经上的累积岁差,后者是联系春分点与非旋转原点\n的量。\n\n出处:按 `eraGmst06` 所实现。", "associated": false }, { @@ -1934,6 +2204,7 @@ "name": "earth_rotation_angle", "signature": "pub fn earth_rotation_angle(ut1_julian_date: f64) -> f64 {", "doc": "Returns the Earth rotation angle for a UT1 Julian date.\n\nThis is the linear expression of the International Astronomical Union,\nwhich is exact to the precision the angle is known.\n\nSource: IERS Conventions (2010), equation 8.32.", + "doc_zh": "返回某 UT1 儒略日的地球自转角。\n\n这是国际天文学联合会的线性表达式,它在该角已知的精度内是精确的。\n\n出处:IERS Conventions(2010),式 8.32。", "associated": false }, { @@ -1941,6 +2212,7 @@ "name": "celestial_to_terrestrial_matrix", "signature": "pub fn celestial_to_terrestrial_matrix( ut1_julian_date: f64, tt_julian_date: f64, polar_motion: PolarMotion, ) -> Mat3 {", "doc": "Returns the celestial-to-terrestrial matrix for a time.\n\nThis composes the whole chain: the frame bias, the precession, the\nnutation, the Earth rotation, and the polar motion.\n\nThe time arguments are the UT1 and TT Julian dates, and the polar motion\nis supplied by the caller because it is measured rather than modelled.\n\nSource: the composition is the one `eraC2t06a` produces.", + "doc_zh": "返回某时刻的天球到地球坐标系矩阵。\n\n它合成整条链:坐标架偏差、岁差、章动、地球自转与极移。\n\n时间参数为 UT1 与 TT 儒略日,极移由调用方提供,因为它是实测而非建模\n的。\n\n出处:该合成即 `eraC2t06a` 所产生者。", "associated": false } ] @@ -1948,13 +2220,15 @@ { "name": "apsis-tle", "layer": "orbit", - "summary": "Parsing of two-line element sets. A two-line element set (TLE) describes a satellite orbit in the form used by the North American Aerospace Defense Command and distributed by Space-Track. The name of the satellite is carried on an optional first line, and the two following lines carry the orbital data. The format is fixed width. Every field occupies a fixed range of columns and the checksum of each line is the sum of its digits modulo ten, where a minus sign counts as one. This crate parses a TLE into structured fields and verifies the line checksums and the matching element set number. It does not interpret the fields; the mean elements and their units are the concern of `apsis-sgp4`. Column positions are one based, matching the documentation of the format. Two conventions from the format are worth stating explicitly, because they are easy to misread: - An exponent field is written as a signed single digit followed by the magnitude. For example ` 12345-3` means `0.12345e-3`, not `12345e-3`. - An assumed decimal point is written without a decimal point. For example the eccentricity field `0006703` means `0.0006703`. Source: the field layout follows the format description published by CelesTrak, *FAQs: Two-Line Element Set Format*, which documents the columns defined by the original NORAD specification. The checksum rule is the one stated there and used by Space-Track. ``` use apsis_tle::TwoLineElement; let text = concat!( \"1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927\\r\\n\", \"2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537\\r\\n\", ); let element = TwoLineElement::parse(text).expect(\"sample is well formed\"); assert_eq!(element.satellite_number, 25544); assert!((element.eccentricity - 0.0006703).abs() < 1e-12); ```", + "summary": "Parsing of two-line element sets.\n\nA two-line element set (TLE) describes a satellite orbit in the form\nused by the North American Aerospace Defense Command and distributed by\nSpace-Track. The name of the satellite is carried on an optional first\nline, and the two following lines carry the orbital data.\n\nThe format is fixed width. Every field occupies a fixed range of columns\nand the checksum of each line is the sum of its digits modulo ten, where\na minus sign counts as one. This crate parses a TLE into structured\nfields and verifies the line checksums and the matching element set\nnumber. It does not interpret the fields; the mean elements and their\nunits are the concern of `apsis-sgp4`.\n\nColumn positions are one based, matching the documentation of the format.\nTwo conventions from the format are worth stating explicitly, because\nthey are easy to misread:\n\n- An exponent field is written as a signed single digit followed by the\n magnitude. For example ` 12345-3` means `0.12345e-3`, not `12345e-3`.\n- An assumed decimal point is written without a decimal point. For\n example the eccentricity field `0006703` means `0.0006703`.\n\nSource: the field layout follows the format description published by\nCelesTrak, *FAQs: Two-Line Element Set Format*, which documents the\ncolumns defined by the original NORAD specification. The checksum rule\nis the one stated there and used by Space-Track.\n\n```\nuse apsis_tle::TwoLineElement;\n\nlet text = concat!(\n \"1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927\\r\\n\",\n \"2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537\\r\\n\",\n);\nlet element = TwoLineElement::parse(text).expect(\"sample is well formed\");\nassert_eq!(element.satellite_number, 25544);\nassert!((element.eccentricity - 0.0006703).abs() < 1e-12);\n```", + "summary_zh": "两行根数集的解析。\n\n两行根数集(TLE)以北美防空司令部所用、由 Space-Track 分发的形式描述\n卫星轨道。卫星名称携带在可选的第一行,其后两行携带轨道数据。\n\n该格式是定宽的。每个字段占据固定的列范围,每行的校验和是其各位数字之和\n模十,其中负号计为一。本 crate 把 TLE 解析为结构化字段,并校验各行校验和\n与相匹配的根数集编号。它不解释各字段;平根数及其单位是 `apsis-sgp4` 的\n事。\n\n列位置以 1 为基,与该格式的文档一致。该格式有两条约定值得明确写出,因为\n它们容易误读:\n\n- 指数字段写为一个带符号的单数字后接幅度。例如 ` 12345-3` 意为\n `0.12345e-3`,而非 `12345e-3`。\n- 隐含小数点不写小数点。例如偏心率字段 `0006703` 意为 `0.0006703`。\n\n出处:字段布局遵循 CelesTrak 发布的格式说明,*FAQs: Two-Line Element Set\nFormat*,它记录了原 NORAD 规范所定义的各列。校验和规则即其中所述、\nSpace-Track 所用者。", "items": [ { "kind": "fn", "name": "line_checksum", "signature": "pub fn line_checksum(line: &str) -> u32 {", "doc": "Returns the checksum of the first 68 columns of a line.\n\nThe input may include the trailing checksum digit, which is ignored\nbecause only the first 68 columns contribute to the sum. A line shorter\nthan 68 characters contributes only the columns it has.\n\nA minus sign contributes one, a decimal digit contributes its value, and\nevery other character contributes nothing.", + "doc_zh": "返回某行前 68 列的校验和。\n\n输入可以包含末尾的校验和数字,该位被忽略,因为只有前 68 列参与求和。短于\n68 字符的行只贡献其实际具有的列。\n\n负号贡献 1,十进制数字贡献其数值,其余字符不贡献。", "associated": false }, { @@ -1962,6 +2236,7 @@ "name": "verify_line_checksum", "signature": "pub fn verify_line_checksum(line: &str, line_number: usize) -> Result<()> {", "doc": "Verifies that the last character of a line is its checksum.\n\nThe line must be at least 69 characters long, because the checksum\noccupies column 69. The expected value is compared against\n[`line_checksum`] of the same line.\n\n`line_number` is the one based number used in any error that is\nreturned, so that a caller parsing a multi-line input can report which\nline failed.", + "doc_zh": "校验某行末位字符是否为其校验和。\n\n该行至少须有 69 个字符,因为校验和位于第 69 列。期望值与同一行的\n[`line_checksum`] 比较。\n\n`line_number` 是返回错误时使用的从一开始的行号,使解析多行输入的调用方\n能指出是哪一行失败。", "associated": false }, { @@ -1969,6 +2244,7 @@ "name": "Classification", "signature": "pub enum Classification {", "doc": "The security classification of an element set.", + "doc_zh": "根数集的安全密级。", "associated": false }, { @@ -1976,6 +2252,7 @@ "name": "as_char", "signature": "pub const fn as_char(self) -> char {", "doc": "Returns the character used in the element set.", + "doc_zh": "返回根数集中所用的字符。", "associated": true }, { @@ -1983,6 +2260,7 @@ "name": "TwoLineElement", "signature": "pub struct TwoLineElement {", "doc": "A parsed two-line element set.\n\nThe fields are the mean elements and identification data exactly as\nwritten in the element set. Units are the ones the format uses, named in\neach field's documentation. Converting them to a state vector is the\nconcern of `apsis-sgp4`.", + "doc_zh": "已解析的两线根数集。\n\n各字段即根数集中原样写下的平均根数与标识信息。单位沿用格式所用者,在各字段\n文档中注明。把它们转换为状态矢量是 `apsis-sgp4` 的职责。", "associated": false }, { @@ -1990,6 +2268,7 @@ "name": "expand_two_digit_year", "signature": "pub const fn expand_two_digit_year(two_digit: u32) -> i32 {", "doc": "Expands a two digit year using the convention that `57` through `99` are\nin the twentieth century and `00` through `56` are in the twenty-first.\n\nThe boundary sits before the start of the satellite catalogue, so no\ncatalogue entry is placed a century away from its true date.", + "doc_zh": "按如下约定展开两位数年份:`57` 至 `99` 属二十世纪,`00` 至 `56` 属\n二十一世纪。\n\n该分界位于卫星编目开始之前,故不会有编目条目被放到距真实日期一个世纪之处。", "associated": false }, { @@ -1997,6 +2276,7 @@ "name": "parse", "signature": "pub fn parse(text: &str) -> Result {", "doc": "Parses a two-line element set from one, two, or three lines.\n\nLine endings may be `\\n`, `\\r\\n`, or a mix; blank lines and lines\nthat are entirely whitespace are ignored. A leading line that does\nnot begin with `1 ` is taken as the satellite name, which is how the\noptional three line form is written.\n\nEach line is checked for the expected line number in column one, for\nits length, and for its checksum. The element set number of the two\nlines must agree.\n\nReturns an error describing the first failure and the line on which\nit occurred.", + "doc_zh": "从一行、两行或三行文本解析两线根数集。\n\n行结束符可以是 `\\n`、`\\r\\n` 或二者混用;空行与全为空白字符的行会被\n忽略。不以 `1 ` 开头的首行被取作卫星名称,这正是可选三行形式的写法。\n\n每一行都检查第一列的行号、长度与校验和。两行的根数集编号必须一致。\n\n返回描述首个失败及其所在行的错误。", "associated": true }, { @@ -2004,6 +2284,7 @@ "name": "ElementErrorKind", "signature": "pub enum ElementErrorKind {", "doc": "The category of a parse failure.", + "doc_zh": "解析失败的类别。", "associated": false }, { @@ -2011,6 +2292,7 @@ "name": "ElementError", "signature": "pub struct ElementError {", "doc": "A failure while parsing a two-line element set.\n\nThe error records which line failed and, where applicable, which columns\nwere being read, so that a caller can point at the offending text. Line\nnumbers are one based and column ranges are the one based inclusive\nrange from the format definition.", + "doc_zh": "解析两线根数集时的失败。\n\n该错误记录哪一行失败,并在适用时记录当时正在读取的列,使调用方能指出有问题的\n文本。行号从一开始,列区间为格式定义中从一开始的含两端区间。", "associated": false }, { @@ -2018,6 +2300,7 @@ "name": "new", "signature": "pub fn new(kind: ElementErrorKind, line: usize, columns: Option<(usize, usize)>) -> Self {", "doc": "Creates an error with the given kind, line number, and columns.\n\nThe line number is one based. The column range is the one based\ninclusive range that was being read, if the failure is tied to a\nfield.", + "doc_zh": "用给定类别、行号与列区间创建错误。\n\n行号从一开始。列区间为当时正在读取的从一开始的含两端区间,当失败与某个\n字段相关时给出。", "associated": true }, { @@ -2025,6 +2308,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the offending text.", + "doc_zh": "附加对有问题的文本的说明。", "associated": true }, { @@ -2032,6 +2316,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> ElementErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -2039,6 +2324,7 @@ "name": "line", "signature": "pub fn line(&self) -> usize {", "doc": "Returns the one based number of the line that failed.", + "doc_zh": "返回失败行的从一而行号。", "associated": true }, { @@ -2046,6 +2332,7 @@ "name": "columns", "signature": "pub fn columns(&self) -> Option<(usize, usize)> {", "doc": "Returns the one based inclusive column range that failed, if any.", + "doc_zh": "返回失败的从一开始的含两端列区间,若有。", "associated": true }, { @@ -2053,6 +2340,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -2060,6 +2348,7 @@ "name": "Result", "signature": "pub type Result = core::result::Result;", "doc": "A [`Result`](core::result::Result) whose error type is [`ElementError`].", + "doc_zh": "错误类型为 [`ElementError`] 的 [`Result`](core::result::Result)。", "associated": false } ] @@ -2067,13 +2356,15 @@ { "name": "apsis-sgp4", "layer": "orbit", - "summary": "The SGP4 and SDP4 analytical orbit propagator. The propagator takes the mean orbital elements of a two-line element set and returns the position and velocity of the satellite at a requested instant. It is the model that the element sets are fitted to, so it is the correct way to use them; a general purpose numerical integrator given the same elements would drift away from the published positions. Two branches exist and the propagator selects between them by orbital period. A satellite with a period below 225 minutes is propagated with SGP4, which models the secular effect of the Earth's oblateness and a drag term. A satellite at or above that period is propagated with SDP4, which adds the resonance and the luni-solar perturbations that dominate at that altitude. Inputs and outputs: - Elements are the mean elements from [`apsis_tle::TwoLineElement`]. Angles in the element set are in degrees and mean motion is in revolutions per day; this crate converts to radians and radians per minute internally. - The output is expressed in the True Equator Mean Equinox (TEME) frame, with position in kilometers and velocity in kilometers per second. TEME is the frame the model defines; converting it to an Earth-fixed frame requires the Earth rotation and the equation of the equinoxes and is not part of this crate. - Time is measured from the element set epoch, in minutes. Source: the algorithm follows Vallado et al., *Revisiting Spacetrack Report #3*, AIAA 2006-6753, 2006, and the revised implementation distributed with that paper. ``` use apsis_sgp4::{GravityModel, Satellite}; use apsis_tle::TwoLineElement; let text = concat!( \"1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927\\r\\n\", \"2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537\\r\\n\", ); let element = TwoLineElement::parse(text).expect(\"sample is well formed\"); let satellite = Satellite::from_element(&element, GravityModel::WGS72) .expect(\"sample elements are propagatable\"); // Propagate to the epoch of the elements. let state = satellite.propagate_minutes(0.0).expect(\"epoch state exists\"); // The position of a low Earth satellite is a few thousand kilometers // from the center of the Earth. assert!(state.position.norm() > 6_600.0); assert!(state.position.norm() < 7_200.0); ```", + "summary": "The SGP4 and SDP4 analytical orbit propagator.\n\nThe propagator takes the mean orbital elements of a two-line element set\nand returns the position and velocity of the satellite at a requested\ninstant. It is the model that the element sets are fitted to, so it is\nthe correct way to use them; a general purpose numerical integrator\ngiven the same elements would drift away from the published positions.\n\nTwo branches exist and the propagator selects between them by orbital\nperiod. A satellite with a period below 225 minutes is propagated with\nSGP4, which models the secular effect of the Earth's oblateness and a\ndrag term. A satellite at or above that period is propagated with SDP4,\nwhich adds the resonance and the luni-solar perturbations that dominate\nat that altitude.\n\nInputs and outputs:\n\n- Elements are the mean elements from [`apsis_tle::TwoLineElement`].\n Angles in the element set are in degrees and mean motion is in\n revolutions per day; this crate converts to radians and radians per\n minute internally.\n- The output is expressed in the True Equator Mean Equinox (TEME) frame,\n with position in kilometers and velocity in kilometers per second. TEME\n is the frame the model defines; converting it to an Earth-fixed frame\n requires the Earth rotation and the equation of the equinoxes and is\n not part of this crate.\n- Time is measured from the element set epoch, in minutes.\n\nSource: the algorithm follows Vallado et al., *Revisiting Spacetrack\nReport #3*, AIAA 2006-6753, 2006, and the revised implementation\ndistributed with that paper.\n\n```\nuse apsis_sgp4::{GravityModel, Satellite};\nuse apsis_tle::TwoLineElement;\n\nlet text = concat!(\n \"1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927\\r\\n\",\n \"2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537\\r\\n\",\n);\nlet element = TwoLineElement::parse(text).expect(\"sample is well formed\");\nlet satellite = Satellite::from_element(&element, GravityModel::WGS72)\n .expect(\"sample elements are propagatable\");\n\n// Propagate to the epoch of the elements.\nlet state = satellite.propagate_minutes(0.0).expect(\"epoch state exists\");\n// The position of a low Earth satellite is a few thousand kilometers\n// from the center of the Earth.\nassert!(state.position.norm() > 6_600.0);\nassert!(state.position.norm() < 7_200.0);\n```", + "summary_zh": "SGP4 与 SDP4 解析轨道传播器。\n\n该传播器接收两行根数集的平轨道根数,返回卫星在所请求瞬时的位置与速度。\n它是各根数集所拟合的模型,故是使用它们的正确方式;给同一个根数集一个\n通用数值积分器会偏离已发布的位置。\n\n有两条分支,传播器按轨道周期在二者之间选择。周期低于 225 分钟的卫星用\nSGP4 传播,它建模地球扁率的长期效应与一个阻力项。周期不低于该值的卫星\n用 SDP4 传播,它加入在该高度占主导的共振与日月摄动。\n\n输入与输出:\n\n- 根数是来自 [`apsis_tle::TwoLineElement`] 的平根数。根数集中的角度以度\n 计,平运动以每天圈数计;本 crate 内部换算为弧度与每分钟弧度。\n- 输出表达在真赤道平春分点(TEME)坐标系中,位置以千米计、速度以千米\n 每秒计。TEME 是该模型所定义的坐标系;把它换算到地固系需要地球自转与\n 分点方程式,不属于本 crate。\n- 时间自根数集历元起以分钟计。\n\n出处:该算法遵循 Vallado 等,*Revisiting Spacetrack Report #3*,\nAIAA 2006-6753,2006,以及随该论文分发的修订实现。", "items": [ { "kind": "fn", "name": "propagate_minutes_at", "signature": "pub fn propagate_minutes_at(satellite: &Satellite, minutes: &[f64]) -> Vec> {", "doc": "Propagates a satellite at a list of minutes from its epoch, in order.\n\nThe sequential form, provided for comparison and for small lists where\nstarting a thread would cost more than the work.", + "doc_zh": "按顺序在历元后若干分钟列表处传播该卫星。\n\n顺序形式,供比较以及线程开销高于工作时的小列表使用。", "associated": false }, { @@ -2081,6 +2372,7 @@ "name": "propagate_minutes_at_parallel", "signature": "pub fn propagate_minutes_at_parallel(satellite: &Satellite, minutes: &[f64]) -> Vec> {", "doc": "Propagates a satellite at a list of minutes from its epoch, across several\nthreads.\n\nThe result matches [`propagate_minutes_at`] element for element and in the\nsame order. A minute at which the model reports a decay yields `None`, as\nit does in the sequential form; the threads do not stop at the first such\nminute, because a caller usually wants the whole track and will decide for\nitself what a decay means.\n\nPropagation is deterministic and depends only on the minutes, so the\nresult does not depend on how the work was split.\n\nThe gain depends on how much work the list holds, because starting the\nthreads costs a fixed amount that a short list cannot repay. A day at a\none minute step is fourteen hundred states and gains little; a month is\nforty three thousand and gains several fold. A caller with many short\ntracks is better served by the per satellite form below, which takes the\nwhole list in one call.", + "doc_zh": "跨多个线程在历元后若干分钟列表处传播该卫星。\n\n结果与 [`propagate_minutes_at`] 逐元素相同且顺序一致。模型报告衰减的\n分钟给出 `None`,与顺序形式相同;线程不会在首个这样的分钟处停下,因为\n调用方通常需要整条轨迹,自行判断衰减的含义。\n\n传播是确定性的且只依赖分钟,故结果与工作如何划分无关。\n\n收益取决于列表所含的工作量,因为启动线程有固定开销,短列表无法收回。一\n天按一分钟步长是一千四百个状态,收益很小;一个月是四万三千个,可获数倍\n提升。拥有许多短轨迹的调用方更适合用下方的按卫星形式,它一次调用处理\n整个列表。", "associated": false }, { @@ -2088,6 +2380,7 @@ "name": "propagate_at", "signature": "pub fn propagate_at(satellites: &[Satellite], tai_mjd: f64) -> Vec> {", "doc": "Propagates several satellites at one instant, in order.\n\nThe per satellite work is independent, so a shell's state at a single\ninstant is a natural batch. The satellites are read only, so they are\nshared across the threads by reference.", + "doc_zh": "按顺序在同一瞬时传播多颗卫星。\n\n各卫星的工作彼此独立,故整壳在同一瞬时的状态是天然的批处理单元。卫星\n只读,因此在各线程间按引用共享。", "associated": false }, { @@ -2095,6 +2388,7 @@ "name": "propagate_at_parallel", "signature": "pub fn propagate_at_parallel(satellites: &[Satellite], tai_mjd: f64) -> Vec> {", "doc": "Propagates several satellites at one instant, across several threads.\n\nThe result matches [`propagate_at`] element for element and in the same\norder.\n\nThis is the form a coverage run wants: it holds the whole shell in one\ncall, so the threads start once for every satellite rather than once per\nsatellite, and even a shell of a few hundred gains.", + "doc_zh": "跨多个线程在同一瞬时传播多颗卫星。\n\n结果与 [`propagate_at`] 逐元素相同且顺序一致。\n\n覆盖计算需要的形式:它一次调用持有整壳,故线程为所有卫星启动一次而非\n每颗一次,即便几百颗的壳也能获益。", "associated": false }, { @@ -2102,6 +2396,7 @@ "name": "PropagationErrorKind", "signature": "pub enum PropagationErrorKind {", "doc": "The category of a propagation failure.", + "doc_zh": "传播失败的类别。", "associated": false }, { @@ -2109,6 +2404,7 @@ "name": "PropagationError", "signature": "pub struct PropagationError {", "doc": "A failure while initialising or propagating a satellite.", + "doc_zh": "初始化或传播卫星时发生的失败。", "associated": false }, { @@ -2116,6 +2412,7 @@ "name": "new", "signature": "pub fn new(kind: PropagationErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -2123,6 +2420,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the value that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -2130,6 +2428,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> PropagationErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -2137,6 +2436,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -2144,6 +2444,7 @@ "name": "Elements", "signature": "pub struct Elements {", "doc": "The elements of a satellite in the units the propagator uses.\n\nAngles are in radians and mean motion is in radians per minute. Helpers\nconvert a [`TwoLineElement`] into this form.", + "doc_zh": "卫星根数在传播器所用单位下的形式。\n\n角度用弧度,平运动用弧度每分钟。辅助函数把 [`TwoLineElement`] 转为该形式。", "associated": false }, { @@ -2151,13 +2452,15 @@ "name": "from_tle", "signature": "pub fn from_tle(element: &TwoLineElement) -> Option {", "doc": "Converts a parsed element set into the units the propagator uses.\n\nThe epoch is taken as the UTC Modified Julian Date exactly as written\nin the element set. The element set epoch is a UTC calendar instant,\nbut the propagator treats it as a uniform time origin, which is the\nconvention the theory was fitted under. Converting it to a uniform\nscale first would shift the epoch by the leap second offset, about\n33 seconds, and that shift propagates into the solar and lunar series\nof the deep space branch.", + "doc_zh": "把已解析的根数集转换为传播器所用单位。\n\n历元直接取根数集中原样写下的 UTC 修正儒略日(Modified Julian Date)。\n根数集历元是 UTC 日历瞬时,但传播器把它当作均匀时间原点,这正是理论\n拟合所依据的约定。若先换算到均匀标度,历元会偏移一个闰秒量(约 33\n秒),而该偏移会传播进深空分支的日月级数。", "associated": true }, { "kind": "fn", "name": "unkozai_mean_motion", "signature": "pub fn unkozai_mean_motion( mean_motion: f64, eccentricity: f64, inclination: f64, model: GravityModel, ) -> f64 {", - "doc": "Applies the Kozai to Brouwer mean motion correction.\n\nThe mean motion in an element set is a Kozai mean motion, which the\ntheory must convert to a Brouwer mean motion before use. The relation is\n\n```text\nn0 = n / (1 + 1.5 k2 (3 cos^2 i - 1) / (a^2 sqrt(1 - e^2)) / a^2)\n```\n\nwritten in the form used here as a fixed point iteration, because `a`\ndepends on `n0` through Kepler's third law. The iteration converges from\nthe unmodified mean motion in a few steps for every orbit that appears in\nan element set.\nApplies the Kozai to Brouwer mean motion correction.\n\nThe mean motion in an element set is a Kozai mean motion, which the\ntheory must convert to a Brouwer mean motion before use. Writing `no` for\nthe element set value and `xke` for the Kepler constant, the reference\nimplementation evaluates\n\n```text\nak = (xke / no)^(2/3)\nd1 = 0.75 j2 (3 cos^2 i - 1) / (sqrt(1 - e^2) (1 - e^2))\ndel = d1 / ak^2\nadel = ak (1 - del^2 - del (1/3 + 134 del^2 / 81))\ndel = d1 / adel^2\nn0 = no / (1 + del)\n```\n\nThe second evaluation of `del` uses `adel` rather than `ak`, which is\nwhat makes the correction consistent with the semi-major axis implied by\nthe corrected mean motion.\n\nSource: Vallado et al., *Revisiting Spacetrack Report #3*, AIAA\n2006-6753, 2006.", + "doc": "Applies the Kozai to Brouwer mean motion correction.\n\nThe mean motion in an element set is a Kozai mean motion, which the\ntheory must convert to a Brouwer mean motion before use. Writing `no` for\nthe element set value and `xke` for the Kepler constant, the reference\nimplementation evaluates\n\n```text\nak = (xke / no)^(2/3)\nd1 = 0.75 j2 (3 cos^2 i - 1) / (sqrt(1 - e^2) (1 - e^2))\ndel = d1 / ak^2\nadel = ak (1 - del^2 - del (1/3 + 134 del^2 / 81))\ndel = d1 / adel^2\nn0 = no / (1 + del)\n```\n\nThe second evaluation of `del` uses `adel` rather than `ak`, which is\nwhat makes the correction consistent with the semi-major axis implied by\nthe corrected mean motion.\n\nSource: Vallado et al., *Revisiting Spacetrack Report #3*, AIAA\n2006-6753, 2006.", + "doc_zh": "施加从 Kozai 平运动到 Brouwer 平运动的修正。\n\n根数集中的平运动是 Kozai 平运动,理论须先把它换算为 Brouwer 平运动方能\n使用。以 `no` 表示根数集给出的值、`xke` 表示开普勒常数,参考实现按下式\n求值:\n\n```text\nak = (xke / no)^(2/3)\nd1 = 0.75 j2 (3 cos^2 i - 1) / (sqrt(1 - e^2) (1 - e^2))\ndel = d1 / ak^2\nadel = ak (1 - del^2 - del (1/3 + 134 del^2 / 81))\ndel = d1 / adel^2\nn0 = no / (1 + del)\n```\n\n第二次求 `del` 用的是 `adel` 而非 `ak`,这正是该修正与修正后平运动所隐含\n的半长轴相一致的原因。\n\n出处:Vallado 等,*Revisiting Spacetrack Report #3*,AIAA 2006-6753,2006。", "associated": false }, { @@ -2165,6 +2468,7 @@ "name": "validate", "signature": "pub fn validate(elements: &Elements) -> Result<(), PropagationError> {", "doc": "Validates the mean elements before initialisation.\n\nReturns an error when the orbit cannot be propagated, for example when\nthe eccentricity does not describe a closed orbit or the mean motion is\nnot positive.", + "doc_zh": "在初始化前校验平均根数。\n\n当轨道不可传播时返回错误,例如偏心率不描述闭合轨道,或平运动不为正。", "associated": false }, { @@ -2172,6 +2476,7 @@ "name": "is_deep_space", "signature": "pub fn is_deep_space(mean_motion: f64) -> bool {", "doc": "Returns `true` when the elements describe a deep space orbit.\n\nThe test is on the orbital period, which follows from the mean motion.", + "doc_zh": "当这些根数描述一条深空轨道时返回 `true`。\n\n判据是轨道周期,它由平运动推出。", "associated": false }, { @@ -2179,6 +2484,7 @@ "name": "GravityModel", "signature": "pub struct GravityModel {", "doc": "A gravity model: the Earth constants used by the propagator.", + "doc_zh": "重力模型:传播器所用的地球常数。", "associated": false }, { @@ -2186,6 +2492,7 @@ "name": "WGS72", "signature": "pub const WGS72: Self = Self {", "doc": "The WGS 72 gravity model.\n\nThis is the model the SGP4 theory was fitted to and the one that\nmatches the standard element sets.", + "doc_zh": "WGS 72 重力模型。\n\n这是 SGP4 理论拟合所依据、且与标准根数集相符的模型。", "associated": true }, { @@ -2193,6 +2500,7 @@ "name": "WGS84", "signature": "pub const WGS84: Self = Self {", "doc": "The WGS 84 gravity model.\n\nUse this only with element sets fitted against WGS 84.", + "doc_zh": "WGS 84 重力模型。\n\n仅对按 WGS 84 拟合的根数集使用。", "associated": true }, { @@ -2200,6 +2508,7 @@ "name": "reciprocal_sqrt_mu", "signature": "pub fn reciprocal_sqrt_mu(self) -> f64 {", "doc": "Returns the reciprocal of the square root of the gravitational\nparameter, in seconds per kilometer to the three halves.", + "doc_zh": "返回引力参数平方根的倒数,单位为秒每千米的三分之二次方。", "associated": true }, { @@ -2207,6 +2516,7 @@ "name": "sqrt_mu", "signature": "pub fn sqrt_mu(self) -> f64 {", "doc": "Returns the square root of the gravitational parameter, in kilometers\nto the three halves per second.", + "doc_zh": "返回引力参数的平方根,单位为千米的三分之二次方每秒。", "associated": true }, { @@ -2214,6 +2524,7 @@ "name": "xke", "signature": "pub fn xke(self) -> f64 {", "doc": "Returns `xke`, the Kepler constant, in units of `er^1.5 / min`.\n\nThe propagator works in minutes and Earth radii, so the gravitational\nparameter is converted by\n\n```text\nxke = 60 / sqrt(radius^3 / mu)\n```\n\nwhich is the angular rate, in radians per minute, of a circular orbit\nof radius one Earth radius.", + "doc_zh": "返回 `xke`(开普勒常数),单位为 `er^1.5 / min`。\n\n传播器以分钟与地球半径为单位,故引力参数按下式换算:\n\n```text\nxke = 60 / sqrt(radius^3 / mu)\n```\n\n它是一个地球半径圆轨道的角速率,单位为弧度每分钟。", "associated": true }, { @@ -2221,6 +2532,7 @@ "name": "tumin", "signature": "pub fn tumin(self) -> f64 {", "doc": "Returns the reciprocal of `xke`, in minutes.\n\nThis is the time unit of the model: the period of the reference\ncircular orbit divided by two pi.", + "doc_zh": "返回 `xke` 的倒数,单位为分钟。\n\n它是模型的时间单位:参考圆轨道的周期除以二 pi。", "associated": true }, { @@ -2228,13 +2540,15 @@ "name": "j3_over_j2", "signature": "pub fn j3_over_j2(self) -> f64 {", "doc": "Returns `j3 / j2`, which appears as a single ratio in the theory.", + "doc_zh": "返回 `j3 / j2`,理论中以单一比值形式出现。", "associated": true }, { "kind": "fn", "name": "x2o3", "signature": "pub fn x2o3(self) -> f64 {", - "doc": "Returns `2 * xke^2`, a term that appears in the mean motion\ncorrection.", + "doc": "Returns `2 / 3`, the exponent the model uses in the mean motion and\ndrag corrections.\n\nIt appears as a power rather than a factor, in terms such as\n`(xke / mean_motion)^(2/3)`, so it is a constant and does not depend\non this model.", + "doc_zh": "返回 `2 / 3`,即模型在平运动与阻力修正中使用的指数。\n\n它以幂而非因子出现,例如 `(xke / mean_motion)^(2/3)`,故为常数,与\n本模型无关。", "associated": true }, { @@ -2242,6 +2556,7 @@ "name": "Satellite", "signature": "pub struct Satellite {", "doc": "A satellite prepared for propagation.\n\nConstruct one with [`Satellite::from_element`], then call\n[`Satellite::propagate_minutes`] or [`Satellite::propagate_at`] for each\ninstant of interest. The initialisation work, including the mean motion\ncorrection and every coefficient that depends only on the elements, is\ndone once at construction.", + "doc_zh": "为传播而准备好的卫星。\n\n用 [`Satellite::from_element`] 构造一个,然后对每个关心的瞬时调用\n[`Satellite::propagate_minutes`] 或 [`Satellite::propagate_at`]。初始化工作\n——包括平运动修正以及所有只依赖根数的系数——在构造时完成一次。", "associated": false }, { @@ -2249,6 +2564,7 @@ "name": "from_element", "signature": "pub fn from_element(element: &TwoLineElement, model: GravityModel) -> Option {", "doc": "Initialises a satellite from a parsed element set.\n\nReturns `None` when the mean elements do not describe an orbit the\nmodel can propagate, for example when the eccentricity is at least\none. A caller that needs the reason should use\n[`Satellite::try_from_element`].", + "doc_zh": "由已解析的根数集初始化卫星。\n\n当平均根数不描述模型可传播的轨道时返回 `None`,例如偏心率不小于一\n的情形。需要知道原因的调用方应使用 [`Satellite::try_from_element`]。", "associated": true }, { @@ -2256,6 +2572,7 @@ "name": "try_from_element", "signature": "pub fn try_from_element( element: &TwoLineElement, model: GravityModel, ) -> Result {", "doc": "Initialises a satellite from a parsed element set.\n\nReturns an error describing which element is not propagatable when\nthe elements are rejected.", + "doc_zh": "由已解析的根数集初始化卫星。\n\n当根数被拒绝时,返回一个说明哪个根数不可传播的错误。", "associated": true }, { @@ -2263,6 +2580,7 @@ "name": "try_from_elements", "signature": "pub fn try_from_elements( elements: &Elements, model: GravityModel, ) -> Result {", "doc": "Initialises a satellite from mean elements already in model units.", + "doc_zh": "由已按模型单位表示的平均根数初始化卫星。", "associated": true }, { @@ -2270,6 +2588,7 @@ "name": "epoch_mjd", "signature": "pub fn epoch_mjd(&self) -> f64 {", "doc": "Returns the epoch of the elements as a Modified Julian Date on the\nTAI scale.", + "doc_zh": "返回根数历元,为 TAI 标度上的修正儒略日(Modified Julian Date)。", "associated": true }, { @@ -2277,6 +2596,7 @@ "name": "is_deep_space", "signature": "pub fn is_deep_space(&self) -> bool {", "doc": "Returns `true` when the deep space branch applies.", + "doc_zh": "当深空分支适用时返回 `true`。", "associated": true }, { @@ -2284,6 +2604,7 @@ "name": "minutes_since_epoch", "signature": "pub fn minutes_since_epoch(&self, tai_mjd: f64) -> f64 {", "doc": "Returns the number of minutes from the element set epoch to the\ngiven instant.\n\nThe instant is given as a Modified Julian Date on the TAI scale, the\nsame scale as [`Satellite::epoch_mjd`].", + "doc_zh": "返回自根数集历元到给定瞬时的分钟数。\n\n该瞬时以 TAI 标度上的修正儒略日(Modified Julian Date)给出,与\n[`Satellite::epoch_mjd`] 同一标度。", "associated": true }, { @@ -2291,6 +2612,7 @@ "name": "propagate_at", "signature": "pub fn propagate_at(&self, tai_mjd: f64) -> Result {", "doc": "Returns the state of the satellite at a given instant.\n\nThe instant is a Modified Julian Date on the TAI scale.", + "doc_zh": "返回卫星在某瞬时的状态。\n\n该瞬时是 TAI 时标上的约化儒略日。", "associated": true }, { @@ -2298,6 +2620,7 @@ "name": "propagate_minutes", "signature": "pub fn propagate_minutes(&self, minutes: f64) -> Result {", "doc": "Returns the state of the satellite at a number of minutes from the\nelement set epoch.\n\nNegative values propagate backwards. The result is in the True\nEquator Mean Equinox frame, with position in kilometers and velocity\nin kilometers per second.\n\nReturns [`PropagationErrorKind::Decayed`](crate::PropagationErrorKind::Decayed)\nwhen the propagated radius falls below one Earth radius, which the\nmodel treats as the satellite having re-entered.", + "doc_zh": "返回卫星距根数集历元若干分钟处的状态。\n\n负值向后传播。结果在真赤道平春分点坐标系中,位置以千米计、速度以千米\n每秒计。\n\n当传播所得半径低于一个地球半径时返回\n[`PropagationErrorKind::Decayed`](crate::PropagationErrorKind::Decayed),\n模型将其视为卫星已经再入。", "associated": true }, { @@ -2305,6 +2628,7 @@ "name": "xke", "signature": "pub fn xke(&self) -> f64 {", "doc": "Returns the Kepler constant of the model, in radians per minute per\nEarth radius to the three halves.", + "doc_zh": "返回模型的开普勒常数,单位为弧度每分每地球半径的三分之二次方。", "associated": true }, { @@ -2312,6 +2636,7 @@ "name": "State", "signature": "pub struct State {", "doc": "The position and velocity of a satellite in the True Equator Mean Equinox\nframe.\n\nPosition is in kilometers and velocity is in kilometers per second. The\nframe is the one the model defines, with its first axis toward the mean\nequinox, its third axis along the true equator pole, and the second\ncompleting a right-handed set.", + "doc_zh": "真赤道平春分点系中卫星的位置与速度。\n\n位置单位为千米,速度单位为千米每秒。该系是模型所定义者:第一轴指向平春分点,\n第三轴沿真赤道极,第二轴构成右手系。", "associated": false }, { @@ -2319,6 +2644,7 @@ "name": "altitude_radius", "signature": "pub fn altitude_radius(self) -> f64 {", "doc": "Returns the distance from the center of the Earth, in kilometers.", + "doc_zh": "返回到地心的距离,单位为千米。", "associated": true }, { @@ -2326,6 +2652,7 @@ "name": "speed", "signature": "pub fn speed(self) -> f64 {", "doc": "Returns the speed relative to the Earth's center, in kilometers per\nsecond.", + "doc_zh": "返回相对地心的速率,单位为千米每秒。", "associated": true } ] @@ -2333,13 +2660,15 @@ { "name": "apsis-kepler", "layer": "orbit", - "summary": "Keplerian orbital elements and two-body propagation. A Keplerian element set describes an orbit as an ellipse, a parabola, or a hyperbola with a fixed orientation in space. From the six elements and the gravitational parameter, this crate computes the position and velocity at any time under the two-body assumption, and converts in the other direction. The two-body model includes only the point mass gravity of the central body. It omits the oblateness of the Earth, drag, and third body perturbations, so it drifts from reality over time. It is the right model for analysing an orbit whose elements are already known, for example to find the radius at a true anomaly, and the wrong model for propagating a two-line element set, which is the job of `apsis-sgp4`. Angles are in radians. Lengths are kilometers and the gravitational parameter is in cubic kilometers per second squared, matching the rest of the orbit layer. ``` use apsis_kepler::{GravityParameter, KeplerianElements}; // A circular orbit at the Earth's equatorial radius has the speed // sqrt(mu / r), about 7.9 km/s. let elements = KeplerianElements { semi_major_axis: 6_378.137, eccentricity: 0.0, inclination: 0.0, right_ascension: 0.0, argument_of_perigee: 0.0, true_anomaly: 0.0, }; let state = elements.to_state(GravityParameter::WGS84).expect(\"valid elements\"); assert!((state.velocity.norm() - 7.909).abs() < 0.01); ```", + "summary": "Keplerian orbital elements and two-body propagation.\n\nA Keplerian element set describes an orbit as an ellipse, a parabola, or\na hyperbola with a fixed orientation in space. From the six elements and\nthe gravitational parameter, this crate computes the position and\nvelocity at any time under the two-body assumption, and converts in the\nother direction.\n\nThe two-body model includes only the point mass gravity of the central\nbody. It omits the oblateness of the Earth, drag, and third body\nperturbations, so it drifts from reality over time. It is the right model\nfor analysing an orbit whose elements are already known, for example to\nfind the radius at a true anomaly, and the wrong model for propagating a\ntwo-line element set, which is the job of `apsis-sgp4`.\n\nAngles are in radians. Lengths are kilometers and the gravitational\nparameter is in cubic kilometers per second squared, matching the rest of\nthe orbit layer.\n\n```\nuse apsis_kepler::{GravityParameter, KeplerianElements};\n\n// A circular orbit at the Earth's equatorial radius has the speed\n// sqrt(mu / r), about 7.9 km/s.\nlet elements = KeplerianElements {\n semi_major_axis: 6_378.137,\n eccentricity: 0.0,\n inclination: 0.0,\n right_ascension: 0.0,\n argument_of_perigee: 0.0,\n true_anomaly: 0.0,\n};\nlet state = elements.to_state(GravityParameter::WGS84).expect(\"valid elements\");\nassert!((state.velocity.norm() - 7.909).abs() < 0.01);\n```", + "summary_zh": "开普勒轨道根数与二体传播。\n\n开普勒根数集把轨道描述为空间中具有固定取向的椭圆、抛物线或双曲线。由\n六个根数与引力参数,本 crate 在二体假定下计算任意时刻的位置与速度,并\n作反方向换算。\n\n二体模型只包含中心天体的质点引力。它略去地球扁率、阻力与第三体摄动,故\n随时间偏离现实。对分析已知根数的轨道(例如求真近点角处的半径),它是恰当\n的模型;对传播两行根数集,它是错误的模型,后者是 `apsis-sgp4` 的事。\n\n角度以弧度计。长度以千米计,引力参数以立方千米每平方秒计,与 orbit 层\n其余部分一致。", "items": [ { "kind": "fn", "name": "wrap_to_pi", "signature": "pub fn wrap_to_pi(angle: f64) -> f64 {", "doc": "Reduces an angle into `[-pi, pi)`.", + "doc_zh": "把角度归约到 `[-pi, pi)`。", "associated": false }, { @@ -2347,6 +2676,7 @@ "name": "true_from_eccentric", "signature": "pub fn true_from_eccentric(eccentric_anomaly: f64, eccentricity: f64) -> f64 {", "doc": "Returns the true anomaly for an eccentric anomaly and eccentricity.\n\nThe relation follows from the geometry of the auxiliary circle:\n\n```text\ntan(nu / 2) = sqrt((1 + e) / (1 - e)) tan(E / 2)\n```\n\nThe half angle form is used rather than the direct cosine relation so\nthat the result is correct in all four quadrants without a quadrant\ncheck.", + "doc_zh": "由偏近点角与偏心率返回真近点角。\n\n该关系来自辅助圆的几何性质:\n\n```text\ntan(nu / 2) = sqrt((1 + e) / (1 - e)) tan(E / 2)\n```\n\n采用半角形式而非直接的余弦关系,使结果在四个象限中都正确,无需判别象限。", "associated": false }, { @@ -2354,6 +2684,7 @@ "name": "eccentric_from_true", "signature": "pub fn eccentric_from_true(true_anomaly: f64, eccentricity: f64) -> f64 {", "doc": "Returns the eccentric anomaly for a true anomaly and eccentricity.\n\nThis is the inverse of [`true_from_eccentric`].", + "doc_zh": "由真近点角与偏心率返回偏近点角。\n\n这是 [`true_from_eccentric`] 的逆运算。", "associated": false }, { @@ -2361,6 +2692,7 @@ "name": "eccentric_from_mean", "signature": "pub fn eccentric_from_mean(mean_anomaly: f64, eccentricity: f64) -> f64 {", "doc": "Returns the eccentric anomaly for a mean anomaly and eccentricity.\n\nThis solves Kepler's equation\n\n```text\nM = E - e sin E\n```\n\nby Newton's method. The starting guess is the mean anomaly plus a term\nproportional to the eccentricity, which keeps the iteration stable for\neccentricities approaching one. The iteration always terminates, and the\nresult is the value reached after at most a fixed number of steps.", + "doc_zh": "由平近点角与偏心率返回偏近点角。\n\n用 Newton 法求解开普勒方程\n\n```text\nM = E - e sin E\n```\n\n初值取平近点角加上一个正比于偏心率的项,使迭代在偏心率趋近一时保持稳定。\n迭代总会终止,结果是至多固定步数后达到的值。", "associated": false }, { @@ -2368,6 +2700,7 @@ "name": "mean_from_eccentric", "signature": "pub fn mean_from_eccentric(eccentric_anomaly: f64, eccentricity: f64) -> f64 {", "doc": "Returns the mean anomaly for an eccentric anomaly and eccentricity.\n\nThis is Kepler's equation evaluated directly, `M = E - e sin E`.", + "doc_zh": "由偏近点角与偏心率返回平近点角。\n\n这是直接求值的开普勒方程,`M = E - e sin E`。", "associated": false }, { @@ -2375,6 +2708,7 @@ "name": "mean_from_true", "signature": "pub fn mean_from_true(true_anomaly: f64, eccentricity: f64) -> f64 {", "doc": "Returns the mean anomaly for a true anomaly and eccentricity.\n\nThe true anomaly is first converted to the eccentric anomaly, and the\nmean anomaly then follows from Kepler's equation. Going through the\neccentric anomaly avoids the separate closed form for the swept area and\nkeeps a single expression responsible for the relation.", + "doc_zh": "由真近点角与偏心率返回平近点角。\n\n先把真近点角转为偏近点角,再由开普勒方程得平近点角。绕经偏近点角避免了\n另立扫过面积的闭式解,使该关系只由一个表达式负责。", "associated": false }, { @@ -2382,6 +2716,7 @@ "name": "true_from_mean", "signature": "pub fn true_from_mean(mean_anomaly: f64, eccentricity: f64) -> f64 {", "doc": "Returns the true anomaly for a mean anomaly and eccentricity.", + "doc_zh": "由平近点角与偏心率返回真近点角。", "associated": false }, { @@ -2389,6 +2724,7 @@ "name": "KeplerianElements", "signature": "pub struct KeplerianElements {", "doc": "The six Keplerian elements of an orbit.\n\nThe shape and size are given by the semi-major axis and the eccentricity,\nthe orientation by the inclination, the right ascension of the ascending\nnode, and the argument of perigee, and the position along the orbit by\nthe true anomaly.\n\nAngles are in radians and the semi-major axis is in kilometers. The orbit\nis elliptical, so the eccentricity must be below one.", + "doc_zh": "轨道的六个开普勒根数。\n\n形状与大小由半长轴与偏心率给出,定向由倾角、升交点赤经与近地点幅角给出,\n轨道上的位置由真近点角给出。\n\n角度单位为弧度,半长轴单位为千米。轨道为椭圆,故偏心率必须小于一。", "associated": false }, { @@ -2396,6 +2732,7 @@ "name": "State", "signature": "pub struct State {", "doc": "The position and velocity of a satellite in the inertial frame the\nelements are referred to.\n\nPosition is in kilometers and velocity is in kilometers per second. The\nframe has its first axis toward the reference direction, its third axis\nalong the orbit normal, and its second completing a right-handed set.", + "doc_zh": "卫星在根数所参照惯性系中的位置与速度。\n\n位置单位为千米,速度单位为千米每秒。该系第一轴指向参考方向,第三轴沿轨道法线,\n第二轴构成右手系。", "associated": false }, { @@ -2403,6 +2740,7 @@ "name": "is_valid", "signature": "pub fn is_valid(self) -> bool {", "doc": "Returns `true` when the elements describe an elliptical orbit.", + "doc_zh": "当这些根数描述一条椭圆轨道时返回 `true`。", "associated": true }, { @@ -2410,6 +2748,7 @@ "name": "semi_latus_rectum", "signature": "pub fn semi_latus_rectum(self) -> f64 {", "doc": "Returns the semi-latus rectum, in kilometers.\n\nThis is `a (1 - e^2)`, the distance from the focus to the orbit\nmeasured perpendicular to the major axis.", + "doc_zh": "返回半通径,单位为千米。\n\n它等于 `a (1 - e^2)`,即从焦点沿垂直于长轴方向量到轨道的距离。", "associated": true }, { @@ -2417,6 +2756,7 @@ "name": "semi_minor_axis", "signature": "pub fn semi_minor_axis(self) -> f64 {", "doc": "Returns the semi-minor axis, in kilometers.", + "doc_zh": "返回半短轴,单位为千米。", "associated": true }, { @@ -2424,6 +2764,7 @@ "name": "perigee_radius", "signature": "pub fn perigee_radius(self) -> f64 {", "doc": "Returns the perigee radius, in kilometers.\n\nThis is the closest approach to the focus, `a (1 - e)`.", + "doc_zh": "返回近地点半径,单位为千米。\n\n它是到焦点的最近距离 `a (1 - e)`。", "associated": true }, { @@ -2431,6 +2772,7 @@ "name": "apogee_radius", "signature": "pub fn apogee_radius(self) -> f64 {", "doc": "Returns the apogee radius, in kilometers.\n\nThis is the farthest point from the focus, `a (1 + e)`.", + "doc_zh": "返回远地点半径,单位为千米。\n\n它是离焦点最远的点 `a (1 + e)`。", "associated": true }, { @@ -2438,6 +2780,7 @@ "name": "period", "signature": "pub fn period(self, parameter: GravityParameter) -> f64 {", "doc": "Returns the orbital period, in seconds.\n\nThis is Kepler's third law, `T = 2 pi sqrt(a^3 / mu)`.", + "doc_zh": "返回轨道周期,单位为秒。\n\n这是开普勒第三定律,`T = 2 pi sqrt(a^3 / mu)`。", "associated": true }, { @@ -2445,6 +2788,7 @@ "name": "eccentric_anomaly", "signature": "pub fn eccentric_anomaly(self) -> f64 {", "doc": "Returns the eccentric anomaly corresponding to the true anomaly.", + "doc_zh": "返回与真近点角对应的偏近点角。", "associated": true }, { @@ -2452,6 +2796,7 @@ "name": "mean_anomaly", "signature": "pub fn mean_anomaly(self) -> f64 {", "doc": "Returns the mean anomaly corresponding to the true anomaly.", + "doc_zh": "返回与真近点角对应的平近点角。", "associated": true }, { @@ -2459,6 +2804,7 @@ "name": "to_state", "signature": "pub fn to_state(self, parameter: GravityParameter) -> Option {", "doc": "Converts these elements to a position and velocity.\n\nReturns `None` when the elements are not a valid elliptical orbit.\n\nThe conversion places the satellite in the perifocal frame, where the\nfirst axis points to perigee and the third along the orbit normal,\n\n```text\nr = p / (1 + e cos nu)\nx = r cos nu, y = r sin nu, z = 0\nvx = -sqrt(mu / p) sin nu\nvy = sqrt(mu / p) (e + cos nu)\n```\n\nand then rotates by the argument of perigee, the inclination, and the\nright ascension of the ascending node.", + "doc_zh": "把这些根数转换为位置与速度。\n\n当根数不是有效椭圆轨道时返回 `None`。\n\n换算先在近点坐标系中定位卫星,该系第一轴指向近地点、第三轴沿轨道法线,\n\n```text\nr = p / (1 + e cos nu)\nx = r cos nu, y = r sin nu, z = 0\nvx = -sqrt(mu / p) sin nu\nvy = sqrt(mu / p) (e + cos nu)\n```\n\n再依次按近地点幅角、倾角与升交点赤经旋转。", "associated": true }, { @@ -2466,6 +2812,7 @@ "name": "from_state", "signature": "pub fn from_state(state: &State, parameter: GravityParameter) -> Option {", "doc": "Returns the elements for a position and velocity.\n\nReturns `None` when the state is not finite, when the radius is zero,\nor when the orbit is not elliptical. This conversion assumes the\norbit is bound, so a parabolic or hyperbolic state is rejected rather\nthan returned with an eccentricity at or above one.", + "doc_zh": "由位置与速度返回对应的根数。\n\n当状态非有限、半径为零或轨道非椭圆时返回 `None`。该换算假定轨道受束缚,\n因此抛物线或双曲线状态被拒绝,而不是返回偏心率大于等于一的结果。", "associated": true }, { @@ -2473,6 +2820,7 @@ "name": "with_true_anomaly", "signature": "pub fn with_true_anomaly(self, true_anomaly: f64) -> Self {", "doc": "Returns a copy of these elements with a different true anomaly.", + "doc_zh": "返回真近点角不同的这些根数的副本。", "associated": true }, { @@ -2480,6 +2828,7 @@ "name": "with_mean_anomaly", "signature": "pub fn with_mean_anomaly(self, mean_anomaly: f64) -> Self {", "doc": "Returns a copy of these elements at a mean anomaly.\n\nThe true anomaly is recovered from the mean anomaly through Kepler's\nequation.", + "doc_zh": "返回给定平近点角处这些根数的副本。\n\n真近点角由平近点角经开普勒方程求得。", "associated": true }, { @@ -2487,6 +2836,7 @@ "name": "to_mean_anomaly", "signature": "pub fn to_mean_anomaly(self) -> f64 {", "doc": "Returns the mean anomaly, which is the linear measure of time along\nthe orbit.\n\nThis is the same value as [`KeplerianElements::mean_anomaly`], named\nfor symmetry with [`KeplerianElements::with_mean_anomaly`].", + "doc_zh": "返回平近点角,即沿轨道的时间线性度量。\n\n其值与 [`KeplerianElements::mean_anomaly`] 相同,此处命名是为了与\n[`KeplerianElements::with_mean_anomaly`] 对称。", "associated": true }, { @@ -2494,6 +2844,7 @@ "name": "perifocal_to_inertial", "signature": "pub fn perifocal_to_inertial( argument_of_perigee: f64, inclination: f64, right_ascension: f64, ) -> apsis_math::Mat3 {", "doc": "Returns the rotation from the perifocal frame to the inertial frame.\n\nThe rotation is a product of three elementary rotations: by the argument\nof perigee about the third axis, then the inclination about the first,\nthen the right ascension about the third.", + "doc_zh": "返回从近点坐标系到惯性系的旋转。\n\n该旋转是三个基本旋转之积:先绕第三轴转近地点幅角,再绕第一轴转倾角,最后\n绕第三轴转赤经。", "associated": false }, { @@ -2501,6 +2852,7 @@ "name": "GravityParameter", "signature": "pub struct GravityParameter(pub f64);", "doc": "The gravitational parameter of the central body, in cubic kilometers per\nsecond squared.\n\nTwo values are offered because the workspace supports both gravity\nmodels, and the correct choice is the one that matches the source of the\norbital elements. For elements derived from a two-line element set, the\nWGS 72 value is the one the SGP4 theory was fitted to.", + "doc_zh": "中心天体的引力参数,单位为立方千米每二次方秒。\n\n提供两个值是因为工作区支持两种重力模型,正确选择取决于轨道根数的来源。对\n由两线根数集导出的根数,应取 WGS 72 值,即 SGP4 理论拟合所用的那一个。", "associated": false }, { @@ -2508,6 +2860,7 @@ "name": "WGS72", "signature": "pub const WGS72: Self = Self(398_600.8);", "doc": "The WGS 72 gravitational parameter, `398 600.8 km^3/s^2`.", + "doc_zh": "WGS 72 引力参数,`398 600.8 km^3/s^2`。", "associated": true }, { @@ -2515,6 +2868,7 @@ "name": "WGS84", "signature": "pub const WGS84: Self = Self(398_600.5);", "doc": "The WGS 84 gravitational parameter, `398 600.5 km^3/s^2`.", + "doc_zh": "WGS 84 引力参数,`398 600.5 km^3/s^2`。", "associated": true }, { @@ -2522,6 +2876,7 @@ "name": "value", "signature": "pub const fn value(self) -> f64 {", "doc": "Returns the value in cubic kilometers per second squared.", + "doc_zh": "返回该值,单位为立方千米每二次方秒。", "associated": true }, { @@ -2529,6 +2884,7 @@ "name": "sqrt", "signature": "pub fn sqrt(self) -> f64 {", "doc": "Returns the square root of the parameter, in kilometers to the three\nhalves per second.", + "doc_zh": "返回该参数的平方根,单位为千米的三分之二次方每秒。", "associated": true } ] @@ -2536,13 +2892,15 @@ { "name": "apsis-ephemeris", "layer": "orbit", - "summary": "Ephemeris sampling and interpolation. A propagator gives the state of a satellite at any instant, but calling it millions of times is wasteful when the result is only needed to draw a track or to search for a crossing. This crate records the state at a grid of times, called samples, and answers queries between them by interpolation. The two halves are deliberately independent: - [`Ephemeris`] is a table of times and states. It knows nothing about where the states came from, so a table propagated by SGP4, computed by two-body theory, or read from a file all work the same way. - [`Sampler`] fills a table by calling a function that produces a state from a time. In this workspace that function is normally `apsis_sgp4::Satellite::propagate_at`, but the sampler only requires a closure, so a test can use an analytic function. Interpolation is Hermite cubic, which uses both the position and the velocity at each end of an interval. Using the velocity is what makes it accurate: a cubic through two positions alone has no information about how fast the satellite is moving, while Hermite reconstruction matches the slope at both ends and so reproduces a smooth orbit far better for the same sample spacing. On a circular orbit the error is of order the fourth power of the step, so halving the step cuts the error about sixteen fold. Times are in seconds from an origin chosen by the caller, matching the rest of the orbit layer. Positions are in kilometers and velocities in kilometers per second. ``` use apsis_ephemeris::{Sample, Sampler}; use apsis_kepler::{GravityParameter, KeplerianElements}; // A circular orbit at the Earth's equatorial radius. let elements = KeplerianElements { semi_major_axis: 7_000.0, eccentricity: 0.0, inclination: 0.9, right_ascension: 0.0, argument_of_perigee: 0.0, true_anomaly: 0.0, }; let state_at = |seconds: f64| { let mean = 2.0 * core::f64::consts::PI * seconds / elements.period(GravityParameter::WGS84); let state = elements .with_mean_anomaly(mean) .to_state(GravityParameter::WGS84) .expect(\"the orbit is valid\"); Sample::new(seconds, state.position, state.velocity) }; let sampler = Sampler::new(0.0, 600.0, 60.0); let ephemeris = sampler.sample(state_at).expect(\"the grid is usable\"); assert_eq!(ephemeris.len(), 11); // A query between two samples is interpolated. let state = ephemeris.state_at(30.0).expect(\"the time is covered\"); assert!(state.position.norm() > 6_900.0); ```", + "summary": "Ephemeris sampling and interpolation.\n\nA propagator gives the state of a satellite at any instant, but calling\nit millions of times is wasteful when the result is only needed to draw a\ntrack or to search for a crossing. This crate records the state at a\ngrid of times, called samples, and answers queries between them by\ninterpolation.\n\nThe two halves are deliberately independent:\n\n- [`Ephemeris`] is a table of times and states. It knows nothing about\n where the states came from, so a table propagated by SGP4, computed by\n two-body theory, or read from a file all work the same way.\n- [`Sampler`] fills a table by calling a function that produces a state\n from a time. In this workspace that function is normally\n `apsis_sgp4::Satellite::propagate_at`, but the sampler only requires\n a closure, so a test can use an analytic function.\n\nInterpolation is Hermite cubic, which uses both the position and the\nvelocity at each end of an interval. Using the velocity is what makes it\naccurate: a cubic through two positions alone has no information about\nhow fast the satellite is moving, while Hermite reconstruction matches\nthe slope at both ends and so reproduces a smooth orbit far better for\nthe same sample spacing. On a circular orbit the error is of order the\nfourth power of the step, so halving the step cuts the error about\nsixteen fold.\n\nTimes are in seconds from an origin chosen by the caller, matching the\nrest of the orbit layer. Positions are in kilometers and velocities in\nkilometers per second.\n\n```\nuse apsis_ephemeris::{Sample, Sampler};\nuse apsis_kepler::{GravityParameter, KeplerianElements};\n\n// A circular orbit at the Earth's equatorial radius.\nlet elements = KeplerianElements {\n semi_major_axis: 7_000.0,\n eccentricity: 0.0,\n inclination: 0.9,\n right_ascension: 0.0,\n argument_of_perigee: 0.0,\n true_anomaly: 0.0,\n};\nlet state_at = |seconds: f64| {\n let mean = 2.0 * core::f64::consts::PI * seconds\n / elements.period(GravityParameter::WGS84);\n let state = elements\n .with_mean_anomaly(mean)\n .to_state(GravityParameter::WGS84)\n .expect(\"the orbit is valid\");\n Sample::new(seconds, state.position, state.velocity)\n};\n\nlet sampler = Sampler::new(0.0, 600.0, 60.0);\nlet ephemeris = sampler.sample(state_at).expect(\"the grid is usable\");\nassert_eq!(ephemeris.len(), 11);\n\n// A query between two samples is interpolated.\nlet state = ephemeris.state_at(30.0).expect(\"the time is covered\");\nassert!(state.position.norm() > 6_900.0);\n```", + "summary_zh": "星历采样与插值。\n\n传播器给出卫星在任意瞬时的状态,但当结果只用于绘制轨迹或搜索穿越时,调用\n它数百万次是浪费的。本 crate 在一组时刻网格上记录状态(称为样本),并用\n插值回答其间的查询。\n\n两半是有意独立的:\n\n- [`Ephemeris`] 是时刻与状态的表。它对状态来自何处一无所知,故由 SGP4\n 传播、由二体理论计算,或从文件读取的表都以相同方式工作。\n- [`Sampler`] 通过调用一个由时刻产生状态的函数来填充表。在本工作区中该\n 函数通常是 `apsis_sgp4::Satellite::propagate_at`,但采样器只需要一个\n 闭包,故测试可用解析函数。\n\n插值是 Hermite 三次插值,它同时使用区间两端的位与速度。使用速度正是它\n准确的原因:只过两个位置的三次式没有卫星移动多快的信息,而 Hermite 重构\n匹配两端的斜率,故在相同采样间距下能好得多地复现光滑轨道。在圆轨道上误差\n是步长的四阶量,故步长减半使误差约减为十六分之一。\n\n时间以距调用方所选原点的秒数计,与 orbit 层其余部分一致。位置以千米计,\n速度以千米每秒计。", "items": [ { "kind": "struct", "name": "Sample", "signature": "pub struct Sample {", "doc": "One sampled state.\n\nA sample is a position and velocity at a time, which is the form every\npropagator in the workspace produces and the form the interpolation\nneeds.", + "doc_zh": "一个采样状态。\n\n样本是某时刻的位置与速度,这既是工作区中每个传播器产出的形式,也是插值所需\n的形式。", "associated": false }, { @@ -2550,6 +2908,7 @@ "name": "new", "signature": "pub const fn new(seconds: f64, position: Vec3, velocity: Vec3) -> Self {", "doc": "Creates a sample.", + "doc_zh": "创建一个样本。", "associated": true }, { @@ -2557,6 +2916,7 @@ "name": "is_valid", "signature": "pub fn is_valid(&self) -> bool {", "doc": "Returns `true` when the time, position, and velocity are finite.", + "doc_zh": "当时间、位置与速度都有限时返回 `true`。", "associated": true }, { @@ -2564,6 +2924,7 @@ "name": "radius", "signature": "pub fn radius(&self) -> f64 {", "doc": "Returns the distance from the origin, in kilometers.", + "doc_zh": "返回到原点的距离,单位为千米。", "associated": true }, { @@ -2571,6 +2932,7 @@ "name": "speed", "signature": "pub fn speed(&self) -> f64 {", "doc": "Returns the speed, in kilometers per second.", + "doc_zh": "返回速率,单位为千米每秒。", "associated": true }, { @@ -2578,6 +2940,7 @@ "name": "hermite_position", "signature": "pub fn hermite_position(p0: Vec3, v0: Vec3, p1: Vec3, v1: Vec3, dt: f64, s: f64) -> Vec3 {", "doc": "Evaluates the Hermite cubic for one component.\n\n`p0` and `p1` are the endpoint values, `v0` and `v1` the endpoint\nderivatives with respect to time, `dt` the interval length, and `s` the\nfraction along the interval in `[0, 1]`.\n\nThe four terms are the standard cubic Hermite basis: the first and third\ncarry the endpoints, the second and fourth the slopes. The derivative\nterms are scaled by `dt` because the slopes are per unit time while `s`\nis per unit interval.", + "doc_zh": "对一个分量求 Hermite 三次式的值。\n\n`p0` 与 `p1` 为端点值,`v0` 与 `v1` 为端点处对时间的导数,`dt` 为区间长度,\n`s` 为区间内位置比例,位于 `[0, 1]`。\n\n四项即标准三次 Hermite 基:第一、三项承载端点值,第二、四项承载斜率。导数项\n乘以 `dt`,因为斜率以单位时间计而 `s` 以单位区间计。", "associated": false }, { @@ -2585,6 +2948,7 @@ "name": "hermite_velocity", "signature": "pub fn hermite_velocity(p0: Vec3, v0: Vec3, p1: Vec3, v1: Vec3, dt: f64, s: f64) -> Vec3 {", "doc": "Evaluates the derivative of the Hermite cubic for one component.\n\nThis is the velocity that goes with [`hermite_position`]. It is the exact\nderivative of that cubic, so the reconstructed state is internally\nconsistent: a caller that differentiates the position gets this velocity.", + "doc_zh": "对一个分量求 Hermite 三次式的导数。\n\n这是与 [`hermite_position`] 配对的速度,且是该三次式的精确导数,因此重建\n的状态内部自洽:调用方对位置求导会得到这个速度。", "associated": false }, { @@ -2592,6 +2956,7 @@ "name": "hermite_state", "signature": "pub fn hermite_state(p0: Vec3, v0: Vec3, p1: Vec3, v1: Vec3, dt: f64, s: f64) -> (Vec3, Vec3) {", "doc": "Evaluates both the Hermite position and velocity.", + "doc_zh": "同时求 Hermite 位置与速度。", "associated": false }, { @@ -2599,6 +2964,7 @@ "name": "Ephemeris", "signature": "pub struct Ephemeris {", "doc": "A table of sampled states that answers queries by interpolation.\n\nThe table holds at least two samples in strictly increasing time order.\nA query at a sample time returns that sample exactly; a query between two\nsamples returns the Hermite cubic through the four endpoint values. A\nquery outside the covered interval is refused rather than extrapolated,\nbecause the interpolation has no information there and an extrapolated\norbit can diverge quickly.", + "doc_zh": "以插值回答查询的采样状态表。\n\n该表至少含两个样本,时间严格递增。在样本时刻查询会精确返回该样本;两个样本\n之间的查询返回穿过四个端点值的 Hermite 三次式。落在覆盖区间之外的查询会被\n拒绝而非外推,因为插值在那里没有信息,而外推的轨道可能迅速发散。", "associated": false }, { @@ -2606,6 +2972,7 @@ "name": "from_samples", "signature": "pub fn from_samples(samples: Vec) -> Result {", "doc": "Builds a table from samples in increasing time order.\n\nReturns an error when there are fewer than two samples, when a sample\nis not finite, or when the times do not strictly increase.", + "doc_zh": "由时间递增的样本构建该表。\n\n当样本少于两个、某个样本非有限,或时间未严格递增时返回错误。", "associated": true }, { @@ -2613,6 +2980,7 @@ "name": "samples", "signature": "pub fn samples(&self) -> &[Sample] {", "doc": "Returns the samples in time order.", + "doc_zh": "返回按时间排列的样本。", "associated": true }, { @@ -2620,6 +2988,7 @@ "name": "len", "signature": "pub fn len(&self) -> usize {", "doc": "Returns the number of samples.", + "doc_zh": "返回样本数。", "associated": true }, { @@ -2627,6 +2996,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when the table is empty.\n\nA table built through [`Ephemeris::from_samples`] is never empty, so\nthis is always `false` for one that exists; it is provided so that\ngeneric code that accepts a slice can use the usual pair of methods.", + "doc_zh": "当该表为空时返回 `true`。\n\n经 [`Ephemeris::from_samples`] 构建的表不会为空,故已存在的表恒返回\n`false`;提供它是为了让接受切片的泛型代码可以配上惯常的一对方法。", "associated": true }, { @@ -2634,6 +3004,7 @@ "name": "start_time", "signature": "pub fn start_time(&self) -> f64 {", "doc": "Returns the first sample time, in seconds.", + "doc_zh": "返回首个样本时刻,单位为秒。", "associated": true }, { @@ -2641,6 +3012,7 @@ "name": "end_time", "signature": "pub fn end_time(&self) -> f64 {", "doc": "Returns the last sample time, in seconds.", + "doc_zh": "返回末个样本时刻,单位为秒。", "associated": true }, { @@ -2648,6 +3020,7 @@ "name": "duration", "signature": "pub fn duration(&self) -> f64 {", "doc": "Returns the time the table covers, in seconds.", + "doc_zh": "返回该表覆盖的时间,单位为秒。", "associated": true }, { @@ -2655,6 +3028,7 @@ "name": "covers", "signature": "pub fn covers(&self, seconds: f64) -> bool {", "doc": "Returns `true` when a time falls within the covered interval.\n\nThe interval is closed, so its endpoints are covered.", + "doc_zh": "当某时刻落在覆盖区间内时返回 `true`。\n\n该区间是闭的,故其端点也被覆盖。", "associated": true }, { @@ -2662,6 +3036,7 @@ "name": "uniform_step", "signature": "pub fn uniform_step(&self) -> Option {", "doc": "Returns the nominal step, when the samples are evenly spaced.\n\nReturns `None` when the spacing varies, which a caller checks before\nindexing samples by arithmetic.", + "doc_zh": "当样本等间隔时返回标称步长。\n\n间隔不一致时返回 `None`;调用方应先检查这一点,再按算术方式索引样本。", "associated": true }, { @@ -2669,6 +3044,7 @@ "name": "is_uniform", "signature": "pub fn is_uniform(&self) -> bool {", "doc": "Returns `true` when the samples are evenly spaced.", + "doc_zh": "当样本等间隔时返回 `true`。", "associated": true }, { @@ -2676,6 +3052,7 @@ "name": "state_at", "signature": "pub fn state_at(&self, seconds: f64) -> Option {", "doc": "Returns the state at a time, interpolating between samples.\n\nA time equal to a sample time returns that sample. Returns `None`\nwhen the time is outside the covered interval.", + "doc_zh": "返回某时刻的状态,在样本之间插值。\n\n时刻与某样本相同时返回该样本。落在覆盖区间之外时返回 `None`。", "associated": true }, { @@ -2683,6 +3060,7 @@ "name": "clamped_state_at", "signature": "pub fn clamped_state_at(&self, seconds: f64) -> Option {", "doc": "Returns the state at a time, or the nearest endpoint when the time is\noutside the covered interval.\n\nThis is for a caller that must produce a value for any time and is\ncontent to hold the last known state rather than fail.", + "doc_zh": "返回某时刻的状态;时刻落在覆盖区间之外时返回最近的端点。\n\n适用于必须对任意时刻给出取值、且宁可保持最近已知状态而不愿失败的调用方。", "associated": true }, { @@ -2690,6 +3068,7 @@ "name": "nearest_sample", "signature": "pub fn nearest_sample(&self, seconds: f64) -> Option {", "doc": "Returns the sample nearest to a time, without interpolating.", + "doc_zh": "返回距某时刻最近的样本,不作插值。", "associated": true }, { @@ -2697,6 +3076,7 @@ "name": "times", "signature": "pub fn times(&self) -> Vec {", "doc": "Returns the times of every sample, in order.", + "doc_zh": "返回各样本时刻,按顺序排列。", "associated": true }, { @@ -2704,6 +3084,7 @@ "name": "all_samples", "signature": "pub fn all_samples(&self, predicate: F) -> bool where F: Fn(&Sample) -> bool, {", "doc": "Returns `true` when every state satisfies a predicate.\n\nThis is a convenience for a caller that wants to check a property of\nthe whole table, such as every radius being above the surface, once.", + "doc_zh": "当每个状态都满足给定谓词时返回 `true`。\n\n这是给需要一次性检查整表性质的调用方的便利方法,例如检查所有半径都在\n表面之上。", "associated": true }, { @@ -2711,6 +3092,7 @@ "name": "EphemerisErrorKind", "signature": "pub enum EphemerisErrorKind {", "doc": "The category of an ephemeris error.", + "doc_zh": "星历错误的类别。", "associated": false }, { @@ -2718,6 +3100,7 @@ "name": "EphemerisError", "signature": "pub struct EphemerisError {", "doc": "A failure while building or querying an ephemeris.", + "doc_zh": "构建或查询星历时发生的失败。", "associated": false }, { @@ -2725,6 +3108,7 @@ "name": "new", "signature": "pub fn new(kind: EphemerisErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -2732,6 +3116,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the value that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -2739,6 +3124,7 @@ "name": "kind", "signature": "pub const fn kind(&self) -> EphemerisErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -2746,6 +3132,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -2753,6 +3140,7 @@ "name": "Sampler", "signature": "pub struct Sampler {", "doc": "A uniform time grid that a state function is evaluated on.\n\nA sampler describes when to sample and how finely. The grid is defined by\na start time, an end time, and a step, and the number of samples follows\nfrom them: the samples are placed at the start and at every step up to\nthe end, with the end itself included whether or not it falls on the\ngrid. Including the end matters, because a caller that asks for coverage\nup to a time expects the table to reach it.", + "doc_zh": "状态函数求值的均匀时间网格。\n\n采样器说明何时采样以及采样多密。网格由起始时刻、结束时刻与步长定义,样本数\n随之而定:样本置于起点及此后每隔一步处,并始终包含结束时刻,无论它是否落在\n网格上。包含结束时刻很重要,因为要求覆盖到某时刻的调用方期望表格确实延伸到\n那里。", "associated": false }, { @@ -2760,6 +3148,7 @@ "name": "new", "signature": "pub const fn new(start_seconds: f64, end_seconds: f64, step_seconds: f64) -> Self {", "doc": "Creates a sampler over an interval with a step.", + "doc_zh": "按给定步长在某区间上创建采样器。", "associated": true }, { @@ -2767,6 +3156,7 @@ "name": "sample_count", "signature": "pub fn sample_count(&self) -> Option {", "doc": "Returns the number of samples the grid produces.\n\nThis is the number of whole steps that fit in the interval plus one\nfor the start and one for the end when the end is off the grid.\nReturns `None` when the sampler is not usable.", + "doc_zh": "返回该网格产生的样本数。\n\n它等于区间内容纳的整步数,加上起点那一个;当结束时刻不在网格上时再加\n结束那一个。采样器不可用时返回 `None`。", "associated": true }, { @@ -2774,6 +3164,7 @@ "name": "times", "signature": "pub fn times(&self) -> Option> {", "doc": "Returns the times of the grid, in order.\n\nReturns `None` when the sampler is not usable. The last time is the\nend when the end does not fall on the grid, and the last grid point\notherwise.", + "doc_zh": "返回网格上的各时刻,按顺序排列。\n\n采样器不可用时返回 `None`。结束时刻不落在网格上时最后一项为结束时刻,\n否则为最后一个网格点。", "associated": true }, { @@ -2781,6 +3172,7 @@ "name": "sample", "signature": "pub fn sample(&self, mut state_at: F) -> Result where F: FnMut(f64) -> Sample, {", "doc": "Fills an ephemeris by evaluating a state function on the grid.\n\nThe function maps a time in seconds to a position and velocity. In\nthis workspace it is normally a closure over\n`apsis_sgp4::Satellite::propagate_at`, but any function works, so\nthe sampler can be tested against an analytic orbit.\n\nReturns an error when the sampler is not usable or when the function\nproduces a state that is not finite.", + "doc_zh": "在网格上求值状态函数以填充星历。\n\n该函数把以秒计的时刻映射为位置与速度。在本工作区它通常是\n`apsis_sgp4::Satellite::propagate_at` 上的闭包,但任何函数都可以,因此\n采样器也可针对解析轨道测试。\n\n采样器不可用或函数产生非有限状态时返回错误。", "associated": true }, { @@ -2788,6 +3180,7 @@ "name": "sample_positions", "signature": "pub fn sample_positions(&self, mut position_at: F) -> Result where F: FnMut(f64) -> Vec3, {", "doc": "Fills an ephemeris from a function that returns only a position.\n\nThe velocity is reconstructed from neighbouring positions by a\ncentral difference. This is for a position-only source, such as a\ntabulated file; where a velocity is available it should be used, so\nprefer [`Sampler::sample`].", + "doc_zh": "用只返回位置的函数填充星历。\n\n速度由相邻位置用中心差分重建。这适用于只有位置的数据源,例如制表文件;\n若可得速度应予使用,故优先选用 [`Sampler::sample`]。", "associated": true } ] @@ -2795,13 +3188,15 @@ { "name": "apsis-maneuver", "layer": "orbit", - "summary": "Impulsive and finite orbital maneuver models. A maneuver changes an orbit by applying thrust. The two models here are the two ends of the spectrum. An impulsive maneuver, [`Impulse`], applies a velocity change instantaneously, so the position is unchanged and only the velocity jumps. This is the model for a chemical burn, whose duration is short compared with the orbital period, and it is the model every textbook delta-v budget is written in. It is exact in the limit of zero burn time and the source of the closed form results for transfers between two orbits. A finite maneuver, [`FiniteBurn`], applies a constant acceleration over a duration, so the position drifts during the burn and the achieved velocity change is less than the thrust would give in free space when the thrust has to fight gravity. It is the model for an electric thruster, whose burn may last a large fraction of an orbit, and for any burn where the short duration approximation is not good enough. The two agree in the limit: a finite burn of acceleration `a` over a duration `t` delivers the impulse `a t`, so a [`FiniteBurn`] with a vanishing duration converges on the matching [`Impulse`]. The tests check that limit, which is what ties the two models together. The impulsive model also provides the classic two impulse transfer between two coplanar circular orbits, [`hohmann_transfer`], which is the reference every low thrust result is compared against. It gives the two burns and the total delta-v for a transfer between two circular orbits of given radii, together with the transfer time, the half period of the transfer ellipse. Conventions match the rest of the orbit layer: lengths are in kilometers, velocities in kilometers per second, and angles in radians. ``` use apsis_maneuver::{Impulse, hohmann_transfer}; use apsis_kepler::GravityParameter; use apsis_math::Vec3; // An impulsive burn straight along the velocity. let impulse = Impulse::new(Vec3::new(0.0, 1.0, 0.0)).expect(\"the change is usable\"); assert!((impulse.magnitude() - 1.0).abs() < 1e-12); // The two burns that raise a circular orbit from 7000 to 8000 km. let transfer = hohmann_transfer(7_000.0, 8_000.0, GravityParameter::WGS84) .expect(\"the radii are usable\"); assert!(transfer.first_burn > 0.0); assert!(transfer.total_delta_v() > 0.0); ```", + "summary": "Impulsive and finite orbital maneuver models.\n\nA maneuver changes an orbit by applying thrust. The two models here are\nthe two ends of the spectrum.\n\nAn impulsive maneuver, [`Impulse`], applies a velocity change\ninstantaneously, so the position is unchanged and only the velocity\njumps. This is the model for a chemical burn, whose duration is short\ncompared with the orbital period, and it is the model every textbook\ndelta-v budget is written in. It is exact in the limit of zero burn time\nand the source of the closed form results for transfers between two\norbits.\n\nA finite maneuver, [`FiniteBurn`], applies a constant acceleration over a\nduration, so the position drifts during the burn and the achieved\nvelocity change is less than the thrust would give in free space when\nthe thrust has to fight gravity. It is the model for an electric\nthruster, whose burn may last a large fraction of an orbit, and for any\nburn where the short duration approximation is not good enough.\n\nThe two agree in the limit: a finite burn of acceleration `a` over a\nduration `t` delivers the impulse `a t`, so a [`FiniteBurn`] with a\nvanishing duration converges on the matching [`Impulse`]. The tests check\nthat limit, which is what ties the two models together.\n\nThe impulsive model also provides the classic two impulse transfer\nbetween two coplanar circular orbits, [`hohmann_transfer`], which is the\nreference every low thrust result is compared against. It gives the two\nburns and the total delta-v for a transfer between two circular orbits of\ngiven radii, together with the transfer time, the half period of the\ntransfer ellipse.\n\nConventions match the rest of the orbit layer: lengths are in kilometers,\nvelocities in kilometers per second, and angles in radians.\n\n```\nuse apsis_maneuver::{Impulse, hohmann_transfer};\nuse apsis_kepler::GravityParameter;\nuse apsis_math::Vec3;\n\n// An impulsive burn straight along the velocity.\nlet impulse = Impulse::new(Vec3::new(0.0, 1.0, 0.0)).expect(\"the change is usable\");\nassert!((impulse.magnitude() - 1.0).abs() < 1e-12);\n\n// The two burns that raise a circular orbit from 7000 to 8000 km.\nlet transfer = hohmann_transfer(7_000.0, 8_000.0, GravityParameter::WGS84)\n .expect(\"the radii are usable\");\nassert!(transfer.first_burn > 0.0);\nassert!(transfer.total_delta_v() > 0.0);\n```", + "summary_zh": "脉冲式与有限式轨道机动模型。\n\n机动通过施加推力改变轨道。此处的两个模型是谱系的两端。\n\n脉冲机动 [`Impulse`] 瞬时施加速度变化,故位置不变、只有速度跃变。这是\n化学推进点火所用的模型,其持续时间相对轨道周期很短,也是每本教科书\ndelta-v 预算所写的模型。它在点火时间为零的极限下精确,也是两轨道之间\n转移各闭式结果的来源。\n\n有限机动 [`FiniteBurn`] 在一段时长上施加恒定加速度,故位置在点火期间\n漂移,且当推力须对抗引力时,所达到的速度变化小于推力在自由空间中会给出\n的。这是电推进器所用的模型,其点火可能持续轨道的很大一部分,也适用于\n任何短时近似不够好的点火。\n\n二者在极限下一致:加速度为 `a`、时长为 `t` 的有限点火给出冲量 `a t`,\n故时长趋于零的 [`FiniteBurn`] 收敛到相应的 [`Impulse`]。测试检查该极限,\n正是它把两个模型联系起来。\n\n脉冲模型还提供两个共面圆轨道之间经典的双脉冲转移\n[`hohmann_transfer`],它是每个低推力结果所比较的参照。它给出给定半径的\n两个圆轨道之间转移的两次点火与总 delta-v,以及转移时间,即转移椭圆的\n半个周期。\n\n约定与 orbit 层其余部分一致:长度以千米计,速度以千米每秒计,角度以\n弧度计。", "items": [ { "kind": "struct", "name": "FiniteBurn", "signature": "pub struct FiniteBurn {", "doc": "A maneuver that applies a constant acceleration for a duration.\n\nA finite burn is the model for a thruster that is on for long enough that\nits duration cannot be ignored: an electric thruster, whose burn may last\na large fraction of an orbit, or any burn where the spacecraft moves\nappreciably while thrusting.\n\nThe acceleration is held constant in an inertial frame chosen by the\ncaller. That is the simplification this model makes: a real thruster\nfires along a body axis that rotates as the spacecraft turns to hold a\ndirection, and modelling that requires the attitude history, which is not\npart of this crate.", + "doc_zh": "在一段时间内施加恒定加速度的机动。\n\n有限推力点火适用于推力器工作时间足够长、其时长无法忽略的情形:电推力器的\n点火可能持续轨道相当大一部分,任何推力期间航天器有明显位移的点火亦属此类。\n\n加速度在调用方选定的惯性系中保持恒定。这是本模型所作的简化:真实推力器\n沿随航天器转动以保持方向的本体轴喷焰,建模这一点需要姿态历史,而它不属\n于本 crate 的范围。", "associated": false }, { @@ -2809,6 +3204,7 @@ "name": "new", "signature": "pub fn new(acceleration: Vec3, duration: f64) -> Result {", "doc": "Creates a burn from an acceleration and a duration.\n\nReturns an error when a value is not finite, when the duration is not\npositive, or when the acceleration is zero.", + "doc_zh": "由加速度与时长创建一次点火。\n\n当某个值非有限、时长不为正或加速度为零时返回错误。", "associated": true }, { @@ -2816,6 +3212,7 @@ "name": "along", "signature": "pub fn along(direction: Vec3, acceleration: f64, duration: f64) -> Result {", "doc": "Creates a burn of a given acceleration magnitude along a direction.\n\nThe direction is normalized, so the acceleration magnitude is fixed\nwhatever the length of the direction vector.", + "doc_zh": "沿给定方向创建指定大小加速度的点火。\n\n方向被归一化,故无论方向矢量多长,加速度大小都固定不变。", "associated": true }, { @@ -2823,6 +3220,7 @@ "name": "acceleration", "signature": "pub const fn acceleration(self) -> Vec3 {", "doc": "Returns the acceleration vector, in kilometers per second squared.", + "doc_zh": "返回加速度矢量,单位为千米每二次方秒。", "associated": true }, { @@ -2830,6 +3228,7 @@ "name": "acceleration_magnitude", "signature": "pub fn acceleration_magnitude(self) -> f64 {", "doc": "Returns the magnitude of the acceleration, in kilometers per second\nsquared.", + "doc_zh": "返回加速度大小,单位为千米每二次方秒。", "associated": true }, { @@ -2837,6 +3236,7 @@ "name": "duration", "signature": "pub const fn duration(self) -> f64 {", "doc": "Returns the duration, in seconds.", + "doc_zh": "返回时长,单位为秒。", "associated": true }, { @@ -2844,6 +3244,7 @@ "name": "nominal_delta_v", "signature": "pub fn nominal_delta_v(self) -> f64 {", "doc": "Returns the velocity change the acceleration would give in free space,\nin kilometers per second.\n\nThis is the acceleration times the duration, the delta-v the burn\nwould deliver without gravity acting during it. Under gravity the\nachieved change is close to this for a short burn and differs from it\nfor a long one, which is the difference the finite model exists to\ncapture.", + "doc_zh": "返回该加速度在自由空间中会造成的速度变化,单位为千米每秒。\n\n它是加速度乘以时长,即点火在不受引力作用时会提供的 delta-v。在引力\n作用下,短点火的实际变化与之接近,长点火则不同,这正是有限推力模型\n得以存在的意义。", "associated": true }, { @@ -2851,6 +3252,7 @@ "name": "equivalent_impulse", "signature": "pub fn equivalent_impulse(self) -> Result {", "doc": "Returns the impulse equivalent to the nominal change.\n\nThis is the impulsive burn the finite one approximates as its\nduration shrinks, and it is what [`FiniteBurn::apply`] returns in the\nlimit of a constant gravity field.", + "doc_zh": "返回与标称变化等效的冲量。\n\n它是有限点火在时长趋零时所逼近的脉冲点火,也是 [`FiniteBurn::apply`]\n在恒定引力场极限下给出的量。", "associated": true }, { @@ -2858,6 +3260,7 @@ "name": "apply", "signature": "pub fn apply( self, state: State, parameter: GravityParameter, steps: usize, ) -> Result {", "doc": "Applies the burn to a state under two-body gravity.\n\nThe position and velocity are integrated forward over the duration\nwith the constant acceleration added to gravity. The exact solution\nof constant acceleration plus point mass gravity has no closed form,\nso the integration is a fixed step fourth order Runge-Kutta, which is\naccurate for the short arcs a burn occupies.\n\nThe achieved velocity change is measured against a coast of the same\nduration, not against the initial velocity. That distinction matters:\ngravity changes the velocity during the burn even with no thrust, so\nthe difference between the final and the initial velocity includes\nthe natural rotation of the velocity vector. Comparing against the\ncoast isolates the contribution of the thrust, which is what the\nburn bought; it also makes the result converge on the nominal change\nas the duration shrinks, which the difference from the initial\nvelocity would not.\n\nReturns the state at the end of the burn together with the achieved\nvelocity change.", + "doc_zh": "在二体引力下把该点火作用于一个状态。\n\n位置与速度在时长内积分前进,积分时把恒定加速度叠加到引力上。恒定加速度\n加质点引力的精确解没有闭式,故用定步长四阶 Runge-Kutta 积分,对点火所占\n的短弧段是精确的。\n\n实际达到的速度变化以同时长的滑行作为比较基准,而非以初始速度。区分这\n一点很重要:即使无推力,引力在点火期间也会改变速度,故末速度与初速度\n之差含速度矢量的自然转动。与滑行比较才能隔离出推力的贡献,即该次点火\n实际买到的东西;这也使结果在时长趋零时收敛到标称变化,而与初速度\n作差则不然。\n\n返回点火结束时的状态及实际达到的速度变化。", "associated": true }, { @@ -2865,6 +3268,7 @@ "name": "apply_to_elements", "signature": "pub fn apply_to_elements( self, elements: KeplerianElements, parameter: GravityParameter, steps: usize, ) -> Result {", "doc": "Applies the burn to a set of Keplerian elements.\n\nThe elements are converted to a state, the burn is applied to it, and\nthe result is converted back. Returns an error when the elements are\nnot valid, when the integration fails, or when the resulting state is\nno longer an elliptical orbit, which happens when the burn is large\nenough to escape.", + "doc_zh": "把该点火作用于一组开普勒根数。\n\n先把根数转为状态,对状态施加点火,再转回根数。当根数无效、积分失败,或\n结果状态不再是椭圆轨道时返回错误;后者在点火足够大以致逃逸时发生。", "associated": true }, { @@ -2872,6 +3276,7 @@ "name": "BurnOutcome", "signature": "pub struct BurnOutcome {", "doc": "What a finite burn produced.\n\nThe outcome keeps the final state, the change the burn achieved, and the\nchange the acceleration would have given in free space, so a caller can\nsee how much gravity cost or added during the burn.", + "doc_zh": "一次有限推力点火的产物。\n\n结果保留末状态、点火实际达到的变化,以及该加速度在自由空间中会造成的\n变化,使调用方可以看出点火期间引力造成或增加了多少。", "associated": false }, { @@ -2879,6 +3284,7 @@ "name": "achieved_magnitude", "signature": "pub fn achieved_magnitude(&self) -> f64 {", "doc": "Returns the magnitude of the achieved velocity change.", + "doc_zh": "返回实际达到的速度变化的大小。", "associated": true }, { @@ -2886,6 +3292,7 @@ "name": "gravity_loss", "signature": "pub fn gravity_loss(&self) -> f64 {", "doc": "Returns the difference between the achieved and nominal change, in\nkilometers per second.\n\nA negative value means gravity turned the velocity against the thrust\nduring the burn, so the burn bought less than its free space delta-v.", + "doc_zh": "返回实际变化与标称变化之差,单位为千米每秒。\n\n负值表示点火期间引力把速度转向了推力的反方向,使该次点火的收获小于其\n自由空间 delta-v。", "associated": true }, { @@ -2893,6 +3300,7 @@ "name": "elements", "signature": "pub fn elements(&self, parameter: GravityParameter) -> Option {", "doc": "Returns the elements at the end of the burn.\n\nReturns `None` when the final state is not an elliptical orbit, which\nhappens when the burn is large enough to escape.", + "doc_zh": "返回点火结束时的根数。\n\n当末状态不是椭圆轨道时返回 `None`;这在点火足够大以致逃逸时发生。", "associated": true }, { @@ -2900,6 +3308,7 @@ "name": "ManeuverErrorKind", "signature": "pub enum ManeuverErrorKind {", "doc": "The category of a maneuver error.", + "doc_zh": "机动错误的类别。", "associated": false }, { @@ -2907,6 +3316,7 @@ "name": "ManeuverError", "signature": "pub struct ManeuverError {", "doc": "A failure while building or applying a maneuver.", + "doc_zh": "构建或应用机动时发生的失败。", "associated": false }, { @@ -2914,6 +3324,7 @@ "name": "new", "signature": "pub fn new(kind: ManeuverErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -2921,6 +3332,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the value that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -2928,6 +3340,7 @@ "name": "kind", "signature": "pub const fn kind(&self) -> ManeuverErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -2935,6 +3348,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -2942,6 +3356,7 @@ "name": "HohmannTransfer", "signature": "pub struct HohmannTransfer {", "doc": "A two impulse transfer between two coplanar circular orbits.\n\nThe transfer is the classic Hohmann maneuver: a burn onto an ellipse\nwhose perigee touches the lower orbit and whose apogee touches the upper\none, a coast for half the ellipse's period, and a second burn onto the\ndestination circle. It is the cheapest two impulse transfer between two\ncoplanar circular orbits in the impulsive model, which is why it is the\nreference every other transfer is measured against.\n\nThe burns are signed along the direction of motion: a positive value\nraises the orbit and a negative one lowers it, so the same type covers a\nclimb and a descent. The total is the sum of the magnitudes, which is the\nquantity a propellant budget uses.", + "doc_zh": "两个共面圆轨道之间的双脉冲转移。\n\n该转移即经典的 Hohmann 机动:一次点火进入椭圆,其近地点相切于较低轨道、\n远地点相切于较高轨道,沿该椭圆滑行半个周期,再一次点火进入目标圆轨道。它\n是脉冲模型中两个共面圆轨道之间最省的双脉冲转移,因此成为衡量其他转移的\n基准。\n\n两次点火沿运动方向带符号:正值抬升轨道,负值降低轨道,故同一类型同时\n表达上升与下降。总和取两者模之和,即推进剂预算所用的量。", "associated": false }, { @@ -2949,6 +3364,7 @@ "name": "total_delta_v", "signature": "pub fn total_delta_v(&self) -> f64 {", "doc": "Returns the total velocity change, in kilometers per second.\n\nThis is the sum of the magnitudes of the two burns, which is what the\ntransfer costs whatever its direction.", + "doc_zh": "返回总速度变化,单位为千米每秒。\n\n它是两次点火大小之和,即无论方向如何该转移所需的代价。", "associated": true }, { @@ -2956,6 +3372,7 @@ "name": "is_raising", "signature": "pub fn is_raising(&self) -> bool {", "doc": "Returns `true` when the transfer raises the orbit.", + "doc_zh": "当该转移抬升轨道时返回 `true`。", "associated": true }, { @@ -2963,6 +3380,7 @@ "name": "first_burn_magnitude", "signature": "pub fn first_burn_magnitude(&self) -> f64 {", "doc": "Returns the velocity change of the first burn, unsigned.", + "doc_zh": "返回第一次点火的速度变化,不带符号。", "associated": true }, { @@ -2970,6 +3388,7 @@ "name": "second_burn_magnitude", "signature": "pub fn second_burn_magnitude(&self) -> f64 {", "doc": "Returns the velocity change of the second burn, unsigned.", + "doc_zh": "返回第二次点火的速度变化,不带符号。", "associated": true }, { @@ -2977,6 +3396,7 @@ "name": "mean_radius", "signature": "pub fn mean_radius(&self) -> f64 {", "doc": "Returns the mean radius the transfer is symmetric about, in\nkilometers.\n\nThis is the semi-major axis of the ellipse, which is the arithmetic\nmean of the two orbit radii.", + "doc_zh": "返回该转移所对称的平均半径,单位为千米。\n\n它即该椭圆的半长轴,为两个轨道半径的算术平均。", "associated": true }, { @@ -2984,6 +3404,7 @@ "name": "hohmann_transfer", "signature": "pub fn hohmann_transfer( from_radius: f64, to_radius: f64, parameter: GravityParameter, ) -> Result {", "doc": "Returns the two impulse transfer between two circular orbits.\n\nThe radii are in kilometers and must be finite and positive. The two may\nbe given in either order: a larger second radius is a climb and a smaller\none a descent.\n\nReturns an error when a radius is not usable or when the two are equal,\nbecause a transfer between an orbit and itself is not a maneuver and is\nalmost always a mistake in a plan.\n\nThe burns follow from the vis-viva equation. At the first radius the\ncircular speed is `sqrt(mu / r1)` and the speed on the transfer ellipse\nis `sqrt(mu (2 / r1 - 1 / a))`, where `a` is the semi-major axis of the\nellipse, so the first burn is the difference. The second burn is the\ndifference between the circular speed at the destination and the speed on\nthe ellipse there, taken the other way round because the ellipse arrives\nslower than the circle it must join. The coast time is half the period of\nthe ellipse.", + "doc_zh": "返回两个圆轨道之间的双脉冲转移。\n\n半径单位为千米,须有限且为正。二者可按任意次序给出:第二个半径较大者为\n上升,较小者为下降。\n\n当某半径不可用或二者相等时返回错误,因为轨道到其自身的转移不是机动,且\n几乎总是计划中的笔误。\n\n两次点火由活力公式导出。在第一个半径处圆轨道速度为 `sqrt(mu / r1)`,而\n转移椭圆上的速度为 `sqrt(mu (2 / r1 - 1 / a))`,其中 `a` 为椭圆半长轴,\n第一次点火即二者之差。第二次点火是目标处圆轨道速度与椭圆上速度之差,取\n相反方向,因为椭圆到达时比它必须汇入的圆轨道更慢。滑行时间为该椭圆\n周期的一半。", "associated": false }, { @@ -2991,6 +3412,7 @@ "name": "Impulse", "signature": "pub struct Impulse {", "doc": "An instantaneous change in velocity.\n\nAn impulse is a velocity change applied in zero time, so it changes the\nvelocity of a state but not its position. That is the model for a burn\nshort compared with the orbital period, and it is how a delta-v budget is\nwritten.\n\nThe change is stored as a vector in the same frame as the state it will\nbe applied to, so the caller is responsible for expressing it in that\nframe. The magnitude is the delta-v, the quantity a propellant budget is\nmeasured in.", + "doc_zh": "瞬时速度变化。\n\n冲量是在零时间内施加的速度变化,故只改变状态的速度而不改变其位置。这是相对\n轨道周期很短的点火的模型,也是编写 delta-v 预算所用的模型。\n\n该变化以矢量形式存储在与待施加状态相同的坐标系中,因此调用方负责把它表达\n在该系中。其模即 delta-v,推进剂预算以此计量。", "associated": false }, { @@ -2998,6 +3420,7 @@ "name": "new", "signature": "pub fn new(delta_v: Vec3) -> Result {", "doc": "Creates an impulse from a velocity change vector, in kilometers per\nsecond.\n\nReturns an error when a component is not finite or when the vector is\nzero, because a zero change is not a maneuver and is almost always a\nmistake in a plan.", + "doc_zh": "由速度变化矢量创建冲量,单位为千米每秒。\n\n当某个分量非有限或矢量为零时返回错误,因为零变化不是机动,且几乎总是\n计划中的笔误。", "associated": true }, { @@ -3005,6 +3428,7 @@ "name": "along", "signature": "pub fn along(direction: Vec3, magnitude: f64) -> Result {", "doc": "Creates an impulse of a given magnitude along a direction.\n\nThe direction is normalized, so the magnitude is the delta-v whatever\nthe length of the direction vector. Returns an error when the\nmagnitude is not positive and finite or when the direction is zero or\nnot finite.", + "doc_zh": "沿给定方向创建指定大小的冲量。\n\n方向被归一化,故无论方向矢量多长,其大小即 delta-v。当大小非正或非有限,\n或方向为零或非有限时返回错误。", "associated": true }, { @@ -3012,6 +3436,7 @@ "name": "delta_v", "signature": "pub const fn delta_v(self) -> Vec3 {", "doc": "Returns the velocity change vector, in kilometers per second.", + "doc_zh": "返回速度变化矢量,单位为千米每秒。", "associated": true }, { @@ -3019,6 +3444,7 @@ "name": "magnitude", "signature": "pub fn magnitude(self) -> f64 {", "doc": "Returns the magnitude of the change, in kilometers per second.\n\nThis is the delta-v the burn costs.", + "doc_zh": "返回该变化的大小,单位为千米每秒。\n\n这就是该次点火所需的 delta-v。", "associated": true }, { @@ -3026,6 +3452,7 @@ "name": "scaled", "signature": "pub fn scaled(self, factor: f64) -> Result {", "doc": "Returns a copy of the change scaled by a factor.\n\nA factor of zero produces a zero impulse, which [`Impulse::new`]\nrejects, so this returns an error for it rather than a value that\ncannot be built.", + "doc_zh": "返回该变化按某因子缩放后的副本。\n\n因子为零会得到零冲量,而 [`Impulse::new`] 拒绝这种情况,故本函数对该\n因子返回错误,而不是给出一个无法构造的值。", "associated": true }, { @@ -3033,6 +3460,7 @@ "name": "reversed", "signature": "pub fn reversed(self) -> Self {", "doc": "Returns a copy of the change reversed, which undoes the burn.", + "doc_zh": "返回取反后的变化副本,即撤销该次点火。", "associated": true }, { @@ -3040,6 +3468,7 @@ "name": "apply_to_velocity", "signature": "pub fn apply_to_velocity(self, velocity: Vec3) -> Vec3 {", "doc": "Applies the impulse to a velocity, returning the new velocity.", + "doc_zh": "把该冲量作用于一个速度,返回新的速度。", "associated": true } ] @@ -3047,27 +3476,31 @@ { "name": "apsis-orbit", "layer": "orbit", - "summary": "Orbital state facade that integrates the orbit layer. This crate is the single entry point to the orbit layer. It re-exports the element parsing, the analytical propagator, and the two-body model, and adds one type that ties them together. [`Orbit`] holds a satellite prepared for propagation together with the gravity model it was initialised against, and offers the conversions between the two ways of describing an orbit that the layer supports: - Mean elements propagated by SGP4, which is the model a two-line element set is written for and the only way to reproduce the published positions of a catalogued satellite. - Instantaneous Keplerian elements, which describe the osculating two-body orbit at one instant and are useful for inspection and for planning a maneuver. The two descriptions are not interchangeable. Propagating mean elements with the two-body model, or osculating elements with SGP4, gives a different and wrong answer; the conversions here are explicit about which direction they go. Two further operations are built on the propagation and exposed here because they need the same gravity parameter to stay consistent: - [`Orbit::sample_ephemeris`] records the track on a time grid so that it can be queried by interpolation instead of by propagation, which is what a coverage or access search wants. - [`Orbit::apply_burn`] applies a finite maneuver to the osculating state and returns the resulting elements, which is what a maneuver plan produces. ``` use apsis_orbit::{GravityModel, Orbit}; use apsis_tle::TwoLineElement; let text = concat!( \"1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927\\r\\n\", \"2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537\\r\\n\", ); let element = TwoLineElement::parse(text).expect(\"sample is well formed\"); let orbit = Orbit::from_element(&element, GravityModel::WGS72) .expect(\"sample elements are propagatable\"); let state = orbit.propagate_minutes(60.0).expect(\"state exists\"); assert!(state.speed() > 7.0); // The osculating elements at that instant. let osculating = orbit.osculating_elements(60.0).expect(\"state exists\"); assert!(osculating.eccentricity < 0.01); ```", + "summary": "Orbital state facade that integrates the orbit layer.\n\nThis crate is the single entry point to the orbit layer. It re-exports\nthe element parsing, the analytical propagator, and the two-body model,\nand adds one type that ties them together.\n\n[`Orbit`] holds a satellite prepared for propagation together with the\ngravity model it was initialised against, and offers the conversions\nbetween the two ways of describing an orbit that the layer supports:\n\n- Mean elements propagated by SGP4, which is the model a two-line\n element set is written for and the only way to reproduce the published\n positions of a catalogued satellite.\n- Instantaneous Keplerian elements, which describe the osculating\n two-body orbit at one instant and are useful for inspection and for\n planning a maneuver.\n\nThe two descriptions are not interchangeable. Propagating mean elements\nwith the two-body model, or osculating elements with SGP4, gives a\ndifferent and wrong answer; the conversions here are explicit about which\ndirection they go.\n\nTwo further operations are built on the propagation and exposed here\nbecause they need the same gravity parameter to stay consistent:\n\n- [`Orbit::sample_ephemeris`] records the track on a time grid so that it\n can be queried by interpolation instead of by propagation, which is\n what a coverage or access search wants.\n- [`Orbit::apply_burn`] applies a finite maneuver to the osculating state\n and returns the resulting elements, which is what a maneuver plan\n produces.\n\n```\nuse apsis_orbit::{GravityModel, Orbit};\nuse apsis_tle::TwoLineElement;\n\nlet text = concat!(\n \"1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927\\r\\n\",\n \"2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537\\r\\n\",\n);\nlet element = TwoLineElement::parse(text).expect(\"sample is well formed\");\nlet orbit = Orbit::from_element(&element, GravityModel::WGS72)\n .expect(\"sample elements are propagatable\");\n\nlet state = orbit.propagate_minutes(60.0).expect(\"state exists\");\nassert!(state.speed() > 7.0);\n\n// The osculating elements at that instant.\nlet osculating = orbit.osculating_elements(60.0).expect(\"state exists\");\nassert!(osculating.eccentricity < 0.01);\n```", + "summary_zh": "整合 orbit 层的轨道状态门面。\n\n本 crate 是 orbit 层的单一入口。它重导出根数解析、解析传播器与二体模型,\n并加入一个把它们联系起来的类型。\n\n[`Orbit`] 保存为传播准备好的卫星,连同其初始化所用的引力模型,并提供该层\n所支持的两种轨道描述之间的换算:\n\n- 由 SGP4 传播的平根数,这是两行根数集所针对的模型,也是复现已编目卫星\n 已发布位置的唯一方式。\n- 瞬时开普勒根数,它描述某一瞬时的密切二体轨道,对检视与规划机动有用。\n\n两种描述不可互换。用二体模型传播平根数,或用 SGP4 传播密切根数,会给出\n不同且错误的答案;此处的换算明确说明它们朝哪个方向进行。\n\n另有两项操作建立在传播之上并在此公开,因为它们需要同一引力参数以保持\n一致:\n\n- [`Orbit::sample_ephemeris`] 在时间网格上记录轨迹,使其可经插值而非\n 传播查询,这正是覆盖或可见性搜索所需的。\n- [`Orbit::apply_burn`] 对密切状态施加有限机动并返回所得根数,这正是\n 机动规划所产生的。", "items": [ { "kind": "struct", "name": "Orbit", "signature": "pub struct Orbit {", "doc": "A satellite orbit, held as mean elements ready for propagation.\n\nThe type pairs a prepared SGP4 satellite with the gravity model it was\ninitialised against, because the osculating element conversion needs the\nsame gravitational parameter to be consistent with the propagation.", + "doc_zh": "以平均根数形式持有、随时可传播的卫星轨道。\n\n该类型把准备好的 SGP4 卫星与其初始化时所用的重力模型配对,因为密切根数\n换算需要同一个引力参数才能与传播保持一致。", "associated": false }, { "kind": "fn", "name": "from_element", "signature": "pub fn from_element(element: &TwoLineElement, model: GravityModel) -> Option {", - "doc": "Prepares an orbit from a parsed element set and a gravity model.\n\nThe gravity model must match the one the element set was fitted\nagainst, which for a catalogued satellite means\n[`GravityModel::WGS72`]. Returns `None` when the elements are not\npropagatable.", + "doc": "Prepares an orbit from a parsed element set and a gravity model.", + "doc_zh": "由已解析的根数集与重力模型准备轨道。\n\nThe gravity model must match the one the element set was fitted\nagainst, which for a catalogued satellite means\n[`GravityModel::WGS72`]. Returns `None` when the elements are not\npropagatable.\n\n由已解析的根数集与重力模型准备轨道。\n\n重力模型须与该根数集拟合时所用者一致;对编目卫星即 [`GravityModel::WGS72`]。\n当根数不可传播时返回 `None`。", "associated": true }, { "kind": "fn", "name": "try_from_element", "signature": "pub fn try_from_element( element: &TwoLineElement, model: GravityModel, ) -> Result {", - "doc": "Prepares an orbit from a parsed element set and a gravity model.\n\nReturns an error describing which element is not propagatable.", + "doc": "Prepares an orbit from a parsed element set and a gravity model.", + "doc_zh": "由已解析的根数集与重力模型准备轨道。\n\nReturns an error describing which element is not propagatable.\n\n由已解析的根数集与重力模型准备轨道。\n\n返回一个说明哪个根数不可传播的错误。", "associated": true }, { @@ -3075,6 +3508,7 @@ "name": "from_elements", "signature": "pub fn from_elements( elements: &Elements, model: GravityModel, ) -> Result {", "doc": "Prepares an orbit from mean elements already in model units.", + "doc_zh": "由已按模型单位表示的平均根数准备轨道。", "associated": true }, { @@ -3082,6 +3516,7 @@ "name": "satellite", "signature": "pub fn satellite(&self) -> &Satellite {", "doc": "Returns the underlying propagator.", + "doc_zh": "返回底层的传播器。", "associated": true }, { @@ -3089,6 +3524,7 @@ "name": "gravity_parameter", "signature": "pub fn gravity_parameter(&self) -> GravityParameter {", "doc": "Returns the gravitational parameter paired with the gravity model.\n\nThe value is the one used for the osculating element conversion, so\nthat the conversion is consistent with the propagation.", + "doc_zh": "返回与该重力模型配对的引力参数。\n\n该值即密切根数换算所用者,从而使换算与传播保持一致。", "associated": true }, { @@ -3096,6 +3532,7 @@ "name": "epoch_mjd", "signature": "pub fn epoch_mjd(&self) -> f64 {", "doc": "Returns the epoch of the elements as a Modified Julian Date on the\nUTC scale.\n\nThe epoch is the value written in the element set, which is the\norigin the propagator measures time from.", + "doc_zh": "返回根数历元,为 UTC 标度上的修正儒略日(Modified Julian Date)。\n\n该历元即根数集中写下的值,也是传播器度量的时间原点。", "associated": true }, { @@ -3103,6 +3540,7 @@ "name": "is_deep_space", "signature": "pub fn is_deep_space(&self) -> bool {", "doc": "Returns `true` when the propagator uses the deep space branch.", + "doc_zh": "当传播器使用深空分支时返回 `true`。", "associated": true }, { @@ -3110,6 +3548,7 @@ "name": "propagate_minutes", "signature": "pub fn propagate_minutes(&self, minutes: f64) -> Result {", "doc": "Returns the state at a number of minutes from the element epoch.\n\nPosition is in kilometers and velocity in kilometers per second, in\nthe True Equator Mean Equinox frame.", + "doc_zh": "返回距根数历元若干分钟处的状态。\n\n位置单位为千米,速度单位为千米每秒,位于真赤道平春分点系。", "associated": true }, { @@ -3117,6 +3556,7 @@ "name": "propagate_at", "signature": "pub fn propagate_at(&self, utc_mjd: f64) -> Result {", "doc": "Returns the state at a Modified Julian Date on the UTC scale.", + "doc_zh": "返回 UTC 标度上某修正儒略日(Modified Julian Date)处的状态。", "associated": true }, { @@ -3124,6 +3564,7 @@ "name": "osculating_elements", "signature": "pub fn osculating_elements(&self, minutes: f64) -> Result {", "doc": "Returns the osculating Keplerian elements at a number of minutes from\nthe element epoch.\n\nThe osculating elements describe the two-body orbit that matches the\nposition and velocity at that instant. They are not the mean elements\nof the element set, and they change along the orbit because SGP4\nincludes perturbations that the two-body model does not.\n\nReturns an error when the state cannot be propagated, or when the\ninstantaneous orbit is not elliptical, which can happen for a state\nthe propagation has pushed onto an escape trajectory.", + "doc_zh": "返回距根数历元若干分钟处的密切开普勒根数。\n\n密切根数描述与该瞬时位置和速度相匹配的二体轨道。它不是根数集的平均根数,\n且沿轨道变化,因为 SGP4 含有二体模型所没有的摄动。\n\n当状态无法传播,或瞬时轨道非椭圆时返回错误;后者可发生于传播把状态推上\n逃逸轨迹的情形。", "associated": true }, { @@ -3131,6 +3572,7 @@ "name": "radius_at", "signature": "pub fn radius_at(&self, minutes: f64) -> Result {", "doc": "Returns the magnitude of the state vector.\n\nThis is a convenience for the common case of wanting only the\ndistance from the center of the Earth.", + "doc_zh": "返回状态矢量的模。\n\n这为只需到地心距离的常见情形提供便利。", "associated": true }, { @@ -3138,6 +3580,7 @@ "name": "position_at", "signature": "pub fn position_at(&self, minutes: f64) -> Result {", "doc": "Returns the position at a number of minutes from the element epoch.", + "doc_zh": "返回距根数历元若干分钟处的位置。", "associated": true }, { @@ -3145,13 +3588,15 @@ "name": "sample_ephemeris", "signature": "pub fn sample_ephemeris( &self, start_minutes: f64, end_minutes: f64, step_minutes: f64, ) -> Result {", "doc": "Samples the orbit into an ephemeris over a range of minutes.\n\nThe propagation is the expensive part of a track, so a table built\nonce and queried many times is worth the memory. The sample times are\nminutes from the element epoch, matching the rest of this type, while\nthe returned ephemeris keeps seconds, matching `apsis-ephemeris`.\n\nThe table is built with position and velocity from the propagator,\nso the Hermite interpolation has exact endpoint slopes and the error\nbetween samples is tiny; there is no need to use the position only\nroute except for a source that has no velocity.\n\nReturns an error when the grid is not usable or when a sample cannot\nbe propagated, which happens when the elements have decayed or the\nrange leaves the region where the model is defined.", + "doc_zh": "把该轨道在若干分钟的区间上采样为星历。\n\n传播是生成轨迹中开销最大的部分,故一次建表、多次查询值得占用内存。采样\n时刻以距根数历元的分钟计,与本类型其余部分一致;返回的星历则用秒,与\n`apsis-ephemeris` 一致。\n\n该表用传播器给出的位置与速度建立,故 Hermite 插值具有精确的端点斜率,\n样本间误差极小;除数据源没有速度外,无需使用仅位置的路径。\n\n当网格不可用或某样本无法传播时返回错误;后者发生于根数已衰减,或区间\n超出模型定义范围的情形。", "associated": true }, { "kind": "fn", "name": "apply_burn", "signature": "pub fn apply_burn( &self, minutes: f64, burn: apsis_maneuver::FiniteBurn, steps: usize, ) -> Result {", - "doc": "Applies a finite burn and returns the resulting osculating elements.\n\nThe burn is applied to the osculating state at the given time, so the\nmanoeuvre is described relative to the orbit the satellite is\nactually on rather than the mean elements. The resulting elements are\nthe osculating elements of the post burn state, which is what a\nfollowing propagation needs. Because the underlying propagator is\nbuilt for one element set, this is a planning result rather than a\nnew propagator; a caller that wants to propagate the new orbit starts\na fresh `Orbit` from the returned elements.", + "doc": "Applies a finite burn and returns the resulting osculating elements.\n\nThe burn is applied to the oscillating state at the given time, so the\nmanoeuvre is described relative to the orbit the satellite is\nactually on rather than the mean elements. The resulting elements are\nthe osculating elements of the post burn state, which is what a\nfollowing propagation needs. Because the underlying propagator is\nbuilt for one element set, this is a planning result rather than a\nnew propagator; a caller that wants to propagate the new orbit starts\na fresh `Orbit` from the returned elements.", + "doc_zh": "施加有限推力点火并返回所得的密切根数。\n\n点火施加于给定时刻的密切状态,故该机动是相对卫星实际所在轨道而非平均\n根数描述的。所得根数是点火后状态的密切根数,正是后续传播所需的。由于\n底层传播器是为一个根数集构建的,这给出的是规划结果而非新的传播器;\n需要传播新轨道的调用方应据此创建新的 `Orbit`。\n\n持续时间可由 30 天星历采样路径支撑。", "associated": true }, { @@ -3159,6 +3604,7 @@ "name": "EphemerisSamplingError", "signature": "pub enum EphemerisSamplingError {", "doc": "A failure while sampling an orbit into an ephemeris.", + "doc_zh": "把轨道采样为星历时发生的失败。", "associated": false }, { @@ -3166,6 +3612,7 @@ "name": "ManeuverApplicationError", "signature": "pub enum ManeuverApplicationError {", "doc": "A failure while applying a burn to an orbit.", + "doc_zh": "把点火作用于轨道时发生的失败。", "associated": false } ] @@ -3173,13 +3620,15 @@ { "name": "apsis-constellation", "layer": "network", - "summary": "Constellation definition and instantiation. A constellation is a set of satellites described by a small number of parameters rather than as a list of individual orbits. This crate models two forms of construction that together cover the constellations in operational use: - A **Walker constellation**, in which satellites are spread evenly over a set of orbital planes that share an inclination and a semi-major axis, with a fixed phase relationship between adjacent planes. - A **custom constellation**, where each shell is described separately so that a design that does not follow the Walker pattern can still be instantiated. Both forms produce a list of [`KeplerianElements`] at a reference time, which the orbit layer can propagate. The constellations in use for satellite internet are Walker patterns at several altitudes, so the Walker constructor is the one most callers need. The Walker notation is written `i: t/p/f`, where `i` is the inclination in degrees, `t` the total number of satellites, `p` the number of planes, and `f` the phase factor. The satellites are placed so that `t / p` satellites share each plane and adjacent planes are offset in mean anomaly by `f * 360 / t` degrees. Source: the Walker notation and the phasing rule are the ones defined in Walker, J. G., *Circular Orbit Patterns Providing Continuous Whole Earth Coverage*, Royal Aircraft Establishment Technical Report 70211, 1970. ``` use apsis_constellation::{Constellation, WalkerPattern}; // A Walker 53 degree pattern with 24 satellites in 3 planes. let pattern = WalkerPattern { total_satellites: 24, planes: 3, phase_factor: 1, inclination: 53.0_f64.to_radians(), semi_major_axis: 6_878.137, }; let constellation = Constellation::walker(pattern, 0.0).expect(\"the pattern is consistent\"); assert_eq!(constellation.satellites().len(), 24); ```", + "summary": "Constellation definition and instantiation.\n\nA constellation is a set of satellites described by a small number of\nparameters rather than as a list of individual orbits. This crate models\ntwo forms of construction that together cover the constellations in\noperational use:\n\n- A **Walker constellation**, in which satellites are spread evenly over\n a set of orbital planes that share an inclination and a semi-major\n axis, with a fixed phase relationship between adjacent planes.\n- A **custom constellation**, where each shell is described separately so\n that a design that does not follow the Walker pattern can still be\n instantiated.", + "summary_zh": "- **Walker 星座**:卫星均匀分布在若干轨道面上,各面共用同一倾角与同一半长轴,\n 相邻轨道面之间有固定的相位关系。\n- **自定义星座**:每个壳层单独描述,使不遵循 Walker 构型的设计同样可以\n 实例化。\n\nBoth forms produce a list of [`KeplerianElements`] at a reference time,\nwhich the orbit layer can propagate. The constellations in use for\nsatellite internet are Walker patterns at several altitudes, so the\nWalker constructor is the one most callers need.\n\n两种形式都在参考时刻产出一组 [`KeplerianElements`],可由 orbit 层传播。\n当前用于卫星互联网的星座是在若干高度上的 Walker 构型,因此多数调用方需要的\n是 Walker 构造函数。\n\nThe Walker notation is written `i: t/p/f`, where `i` is the inclination\nin degrees, `t` the total number of satellites, `p` the number of planes,\nand `f` the phase factor. The satellites are placed so that\n`t / p` satellites share each plane and adjacent planes are offset in\nmean anomaly by `f * 360 / t` degrees.\n\n卫星的分布方式为:每个轨道面分到 `t / p` 颗卫星,相邻轨道面的平近点角相差\n`f * 360 / t` 度。\n\nSource: the Walker notation and the phasing rule are the ones defined in\nWalker, J. G., *Circular Orbit Patterns Providing Continuous Whole Earth\nCoverage*, Royal Aircraft Establishment Technical Report 70211, 1970.\n\n出处:Walker 记法与相位规则取自 Walker, J. G., *Circular Orbit Patterns\nProviding Continuous Whole Earth Coverage*, Royal Aircraft Establishment\nTechnical Report 70211, 1970。\n\n```\nuse apsis_constellation::{Constellation, WalkerPattern};\n\n// A Walker 53 degree pattern with 24 satellites in 3 planes.\nlet pattern = WalkerPattern {\n total_satellites: 24,\n planes: 3,\n phase_factor: 1,\n inclination: 53.0_f64.to_radians(),\n semi_major_axis: 6_878.137,\n};\nlet constellation =\n Constellation::walker(pattern, 0.0).expect(\"the pattern is consistent\");\nassert_eq!(constellation.satellites().len(), 24);\n```", "items": [ { "kind": "enum", "name": "ConstellationErrorKind", "signature": "pub enum ConstellationErrorKind {", "doc": "The category of a constellation error.", + "doc_zh": "星座错误的类别。", "associated": false }, { @@ -3187,6 +3636,7 @@ "name": "ConstellationError", "signature": "pub struct ConstellationError {", "doc": "A failure while building a constellation.", + "doc_zh": "构建星座时的失败。", "associated": false }, { @@ -3194,6 +3644,7 @@ "name": "new", "signature": "pub fn new(kind: ConstellationErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -3201,6 +3652,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the values that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -3208,6 +3660,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> ConstellationErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -3215,13 +3668,15 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。\n\n返回附加在该错误上的说明文本,若无则返回 `None`。", "associated": true }, { "kind": "struct", "name": "WalkerPattern", "signature": "pub struct WalkerPattern {", - "doc": "A Walker delta pattern.\n\nA Walker pattern places a number of satellites evenly around the Earth in\na number of planes that share an inclination and a semi-major axis. The\nrelative phasing of adjacent planes is set by the phase factor, so the\nwhole constellation is described by five numbers.\n\nThe pattern is written `i: t/p/f`, where `i` is the inclination, `t` the\ntotal number of satellites, `p` the number of planes, and `f` the phase\nfactor. This structure stores the same quantities, with the inclination\nin radians and the semi-major axis in kilometers.", + "doc": "A Walker delta pattern.\n\nA Walker pattern places a number of satellites evenly around the Earth in\na number of planes that share an inclination and a semi-major axis. The\nrelative phasing of adjacent planes is set by the phase factor, so the\nwhole constellation is described by five numbers.", + "doc_zh": "Walker 构型把若干卫星均匀分布在绕地球的若干轨道面上,各面共用同一倾角与\n同一半长轴。相邻轨道面之间的相对相位由相位因子决定,因此整个星座由五个数\n描述。\n\nThe pattern is written `i: t/p/f`, where `i` is the inclination, `t` the\ntotal number of satellites, `p` the number of planes, and `f` the phase\nfactor. This structure stores the same quantities, with the inclination\nin radians and the semi-major axis in kilometers.\n\n构型写作 `i: t/p/f`,其中 `i` 为倾角、`t` 为卫星总数、`p` 为轨道面数、\n`f` 为相位因子。本结构体存储同样的量,倾角用弧度,半长轴用千米。", "associated": false }, { @@ -3229,20 +3684,23 @@ "name": "satellites_per_plane", "signature": "pub fn satellites_per_plane(self) -> Option {", "doc": "Returns the number of satellites in each plane.\n\nReturns `None` when the satellite count is not a multiple of the\nplane count.", + "doc_zh": "返回每个轨道面上的卫星数。\n\n当卫星总数不是轨道面数的整数倍时返回 `None`。", "associated": true }, { "kind": "fn", "name": "plane_spacing", "signature": "pub fn plane_spacing(self) -> Option {", - "doc": "Returns the spacing between adjacent planes in right ascension, in\nradians.\n\nReturns the spacing regardless of whether the pattern is consistent,\nso that a caller can report the value it would use.", + "doc": "Returns the spacing between adjacent planes in right ascension, in\nradians.\n\nReturns `None` when the plane count is zero, which is the only case\nin which the spacing is undefined.\n\nThe spacing does not depend on the pattern being consistent, so a\ncaller can report the value it would use even for a pattern that\ncannot be instantiated.", + "doc_zh": "返回相邻轨道面之间的赤经间隔,单位为弧度,即 `360 / planes` 度。\n\n当轨道面数为零时返回 `None`,这是该间隔未定义的唯一情形。\n\n该间隔不依赖构型是否自洽,因此即使构型无法实例化,调用方也能报告它本会\n使用的值。", "associated": true }, { "kind": "fn", "name": "phase_offset", "signature": "pub fn phase_offset(self) -> Option {", - "doc": "Returns the phase offset applied between adjacent planes, in radians.\n\nThis is `f * 360 / t` degrees, where `f` is the phase factor and `t`\nthe total satellite count.", + "doc": "Returns the phase offset applied between adjacent planes, in radians.\n\nThis is `f * 360 / t` degrees, where `f` is the phase factor and `t`\nthe total satellite count.\n\nReturns `None` when the satellite count is zero, which is the only\ncase in which the offset is undefined.", + "doc_zh": "返回相邻轨道面之间施加的相位偏移,单位为弧度。\n\n即 `f * 360 / t` 度,其中 `f` 为相位因子,`t` 为卫星总数。\n\n当卫星总数为零时返回 `None`,这是该偏移未定义的唯一情形。", "associated": true }, { @@ -3250,13 +3708,15 @@ "name": "is_valid", "signature": "pub fn is_valid(self) -> bool {", "doc": "Returns `true` when the pattern can be instantiated.", + "doc_zh": "当该构型可以被实例化时返回 `true`。", "associated": true }, { "kind": "fn", "name": "notation", "signature": "pub fn notation(self) -> String {", - "doc": "Returns the Walker notation for this pattern, such as `53: 24/3/1`.\n\nThe inclination is rounded to one decimal place, which is the\nprecision the notation is normally quoted to.", + "doc": "Returns the Walker notation for this pattern, such as `53: 24/3/1`.\n\nThe inclination is printed at full precision, so a value such as\n53.25 degrees survives the round trip. The notation is an identifier\nrather than a rounded quotation, so truncating it would lose\ninformation for no gain.", + "doc_zh": "返回该构型的 Walker 记法,例如 `53: 24/3/1`。\n\n倾角按全精度打印,因此 53.25 度这样的值可以原样往返。该记法用作标识而\n非取整后的引用值,截断它只会丢失信息而没有收益。", "associated": true }, { @@ -3264,6 +3724,7 @@ "name": "instantiate", "signature": "pub fn instantiate(self) -> Result, ConstellationError> {", "doc": "Instantiates the pattern as a list of orbital elements.\n\nThe mean anomaly of the reference satellite of the first plane is\nzero. Each further satellite in a plane is advanced by\n`360 / (t / p)` degrees, and each further plane is advanced by the\nphase offset.\n\nReturns an error describing why the pattern cannot be instantiated.\n\nSource: the phasing rule is the one defined in Walker, J. G.,\n*Circular Orbit Patterns Providing Continuous Whole Earth Coverage*,\nRoyal Aircraft Establishment Technical Report 70211, 1970.", + "doc_zh": "把该构型实例化为一组轨道根数。\n\n第一个轨道面的参考卫星的平近点角为零。同一面内每增加一颗卫星,平近点角\n前进 `360 / (t / p)` 度;每增加一个轨道面,前进一个相位偏移。\n\n当该构型无法实例化时返回一个说明原因的错误。\n\n出处:相位规则取自 Walker, J. G., *Circular Orbit Patterns Providing\nContinuous Whole Earth Coverage*, Royal Aircraft Establishment\nTechnical Report 70211, 1970。", "associated": true }, { @@ -3271,6 +3732,7 @@ "name": "Satellite", "signature": "pub struct Satellite {", "doc": "One satellite in an instantiated constellation.", + "doc_zh": "已实例化星座中的一颗卫星。", "associated": false }, { @@ -3278,6 +3740,7 @@ "name": "Constellation", "signature": "pub struct Constellation {", "doc": "A constellation instantiated at a reference time.\n\nThe elements are the ones at the reference time passed to the\nconstructor. Advancing them in time is the job of the orbit layer, which\npropagates each satellite independently.", + "doc_zh": "在参考时刻实例化的星座。\n\n根数即构造函数所给参考时刻的那一组。随时间推进由 orbit 层负责,该层对每颗\n卫星独立传播。", "associated": false }, { @@ -3285,6 +3748,7 @@ "name": "walker", "signature": "pub fn walker(pattern: WalkerPattern, reference_mjd: f64) -> Result {", "doc": "Instantiates a Walker pattern at a reference time.\n\nThe reference time is a Modified Julian Date on the TAI scale. It is\nstored with the constellation and returned unchanged; the elements\nare the ones at that instant.\n\nReturns an error when the pattern cannot be instantiated.", + "doc_zh": "在参考时刻实例化一个 Walker 构型。\n\n参考时刻为 TAI 时间尺度上的修正儒略日(Modified Julian Date)。它随\n星座一并保存并原样返回;根数即该时刻的根数。\n\n当该构型无法实例化时返回一个错误。", "associated": true }, { @@ -3292,6 +3756,7 @@ "name": "from_satellites", "signature": "pub fn from_satellites( elements: &[KeplerianElements], reference_mjd: f64, ) -> Result {", "doc": "Builds a constellation from a list of satellites.\n\nThis is the constructor for a constellation that does not follow the\nWalker pattern. The plane index of each satellite is derived from the\nright ascension of its elements, so satellites sharing a plane are\ngrouped.\n\nReturns an error when the list is empty, when the reference time is\nnot finite, or when any element set is not a valid orbit.", + "doc_zh": "由一组卫星根数构建星座。\n\n这是为不遵循 Walker 构型的星座提供的构造函数。每颗卫星的轨道面序号由其根数\n的赤经推得,因此共用同一轨道面的卫星会被归为一组。\n\n当列表为空、参考时刻非有限,或任一根数集不是有效轨道时返回一个错误。", "associated": true }, { @@ -3299,6 +3764,7 @@ "name": "satellites", "signature": "pub fn satellites(&self) -> &[Satellite] {", "doc": "Returns the satellites in the constellation.", + "doc_zh": "返回星座中的卫星。", "associated": true }, { @@ -3306,6 +3772,7 @@ "name": "len", "signature": "pub fn len(&self) -> usize {", "doc": "Returns the number of satellites.", + "doc_zh": "返回卫星数。", "associated": true }, { @@ -3313,6 +3780,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when the constellation is empty.\n\nA constellation built through either constructor is never empty, so\nthis always returns `false`; it exists because a collection type\nshould offer it alongside [`Constellation::len`].", + "doc_zh": "经任一构造函数构建的星座都不会为空,故此函数恒返回 `false`;它存在是因为\n集合类型应当与 [`Constellation::len`] 一并提供。", "associated": true }, { @@ -3320,6 +3788,7 @@ "name": "plane_count", "signature": "pub fn plane_count(&self) -> usize {", "doc": "Returns the number of orbital planes.", + "doc_zh": "返回轨道面数。", "associated": true }, { @@ -3327,6 +3796,7 @@ "name": "pattern", "signature": "pub fn pattern(&self) -> Option {", "doc": "Returns the Walker pattern, when the constellation was built from\none.", + "doc_zh": "若星座由 Walker 构型构建则返回该构型,否则返回 `None`。", "associated": true }, { @@ -3334,6 +3804,7 @@ "name": "reference_mjd", "signature": "pub fn reference_mjd(&self) -> f64 {", "doc": "Returns the reference time as a Modified Julian Date on the TAI\nscale.", + "doc_zh": "返回参考时刻,为 TAI 时间尺度上的修正儒略日(Modified Julian Date)。", "associated": true }, { @@ -3341,6 +3812,7 @@ "name": "plane", "signature": "pub fn plane(&self, plane: usize) -> Vec {", "doc": "Returns the satellites of one plane.\n\nReturns an empty slice when the plane index is out of range.", + "doc_zh": "返回某个轨道面上的卫星。\n\n当轨道面序号越界时返回空切片。", "associated": true }, { @@ -3348,6 +3820,7 @@ "name": "altitude_above", "signature": "pub fn altitude_above(&self, body_radius_km: f64) -> Option {", "doc": "Returns the altitude of the constellation above the given radius, in\nkilometers.\n\nEvery satellite of a Walker pattern shares a semi-major axis, so a\nsingle altitude describes the whole constellation. For a custom\nconstellation this is the altitude of the first satellite.\n\nReturns `None` when the constellation is empty.", + "doc_zh": "Walker 构型中所有卫星共用同一半长轴,故单一高度即可描述整个星座;对自定义\n星座,返回的是第一颗卫星的高度。\n\n星座为空时返回 `None`。", "associated": true }, { @@ -3355,6 +3828,7 @@ "name": "plane_populations", "signature": "pub fn plane_populations(&self) -> Vec {", "doc": "Returns the count of satellites in each plane.\n\nThe result has one entry per plane, in plane order.", + "doc_zh": "返回的向量每个轨道面一项,按轨道面顺序排列。", "associated": true } ] @@ -3362,13 +3836,15 @@ { "name": "apsis-coverage", "layer": "network", - "summary": "Ground coverage analysis. A satellite covers the part of the Earth's surface from which it can be seen above a minimum elevation angle. That part is a circle, called the footprint, whose size depends on the satellite altitude and on the elevation mask. This crate computes the footprint geometry, tests whether a ground point lies inside a footprint, and measures how much of a region is covered. The spherical model in [`geometry`](self) treats the Earth as a sphere. That is accurate enough for constellation design, where the quantities wanted are the footprint radius to a few parts in ten thousand and the number of satellites in view. The exact line-of-sight test in [`line_of_sight`] uses the actual positions, so it accounts for the flattening and for a ground point above the ellipsoid. The geometry of the satellite, the ground point, and the centre of the Earth is a triangle. Its three angles are the elevation at the ground point, the nadir angle at the satellite, and the central angle at the centre, and they sum to a right angle: ```text elevation + nadir + central = 90 degrees ``` Source: the relations used here are the standard satellite geometry, as given for example in Vallado, D. A., *Fundamentals of Astrodynamics and Applications*, 4th edition, microcosm Press, 2013, section 5.3. ``` use apsis_coverage::{footprint_central_angle, slant_range}; // A satellite at 550 km altitude sees to about fifteen degrees of // central angle at a ten degree elevation mask, which is a footprint // radius of some 1665 km along the surface. let central = footprint_central_angle(6_928.137, 6_378.137, 10.0_f64.to_radians()) .expect(\"valid geometry\"); assert!((central.to_degrees() - 14.96).abs() < 0.01); let range = slant_range(6_928.137, 6_378.137, 10.0_f64.to_radians()) .expect(\"valid geometry\"); assert!((range - 1_815.65).abs() < 0.01); ```", + "summary": "Ground coverage analysis.\n\nA satellite covers the part of the Earth's surface from which it can be\nseen above a minimum elevation angle. That part is a circle, called the\nfootprint, whose size depends on the satellite altitude and on the\nelevation mask. This crate computes the footprint geometry, tests whether\na ground point lies inside a footprint, and measures how much of a region\nis covered.\n\nThe spherical model in [`geometry`](self) treats the Earth as a sphere.\nThat is accurate enough for constellation design, where the quantities\nwanted are the footprint radius to a few parts in ten thousand and the\nnumber of satellites in view. The exact line-of-sight test in\n[`line_of_sight`] uses the actual positions, so it accounts for the\nflattening and for a ground point above the ellipsoid.\n\nThe geometry of the satellite, the ground point, and the centre of the\nEarth is a triangle. Its three angles are the elevation at the ground\npoint, the nadir angle at the satellite, and the central angle at the\ncentre, and they sum to a right angle:\n\n```text\nelevation + nadir + central = 90 degrees\n```\n\nSource: the relations used here are the standard satellite geometry, as\ngiven for example in Vallado, D. A., *Fundamentals of Astrodynamics and\nApplications*, 4th edition, microcosm Press, 2013, section 5.3.\n\n```\nuse apsis_coverage::{footprint_central_angle, slant_range};\n\n// A satellite at 550 km altitude sees to about fifteen degrees of\n// central angle at a ten degree elevation mask, which is a footprint\n// radius of some 1665 km along the surface.\nlet central = footprint_central_angle(6_928.137, 6_378.137, 10.0_f64.to_radians())\n .expect(\"valid geometry\");\nassert!((central.to_degrees() - 14.96).abs() < 0.01);\n\nlet range = slant_range(6_928.137, 6_378.137, 10.0_f64.to_radians())\n .expect(\"valid geometry\");\nassert!((range - 1_815.65).abs() < 0.01);\n```", + "summary_zh": "地面覆盖分析。\n\n卫星覆盖地表上能在最小仰角以上看到它的那部分。那部分是一个圆,称为覆盖区,\n其大小取决于卫星高度与仰角遮蔽。本 crate 计算覆盖区几何、判定某地面点是否落在\n覆盖区内,并度量某区域被覆盖的比例。\n\n[`geometry`](self) 中的球面模型把地球当作一个球。对星座设计已足够准确,那里\n所要的量是覆盖区半径到万分之几以及可见卫星数。[`line_of_sight`] 中的精确视线\n判定使用实际位置,故它计入了扁率以及高于椭球的地面点。\n\n卫星、地面点与地心构成的几何是一个三角形。其三个角是地面点处的仰角、卫星处的\n天底角,以及地心处的中心角,它们相加为直角:\n\n```text\nelevation + nadir + central = 90 degrees\n```\n\n出处:此处所用的关系是标准的卫星几何,例如见 Vallado, D. A.,*Fundamentals of\nAstrodynamics and Applications*,第 4 版,microcosm Press,2013,第 5.3 节。", "items": [ { "kind": "struct", "name": "Footprint", "signature": "pub struct Footprint {", "doc": "A circular ground footprint.\n\nThe footprint is the part of the Earth's surface from which a satellite\nis visible above a given elevation mask. On a sphere it is an exact\ncircle, described here by the central angle from the sub-satellite point\nand the radius of the body.", + "doc_zh": "圆形地面覆盖区。\n\n覆盖区是能在地平遮蔽以上看见某卫星的那部分地表。在球面上它是精确\n的圆,此处以自星下点的中心角和天体半径描述。", "associated": false }, { @@ -3376,6 +3852,7 @@ "name": "new", "signature": "pub const fn new(central_angle: f64, body_radius: f64) -> Self {", "doc": "Creates a footprint from its central angle and body radius.", + "doc_zh": "由其中心角与天体半径创建覆盖区。", "associated": true }, { @@ -3383,6 +3860,7 @@ "name": "surface_radius", "signature": "pub fn surface_radius(&self) -> f64 {", "doc": "Returns the footprint radius measured along the surface, in\nkilometers.", + "doc_zh": "返回沿地表量得的覆盖区半径,单位为千米。", "associated": true }, { @@ -3390,6 +3868,7 @@ "name": "area", "signature": "pub fn area(&self) -> f64 {", "doc": "Returns the area of the footprint, in square kilometers.\n\nThe area is the part of a sphere cut off by a cone,\n\n```text\nA = 2 pi R^2 (1 - cos(central))\n```\n\nwhich is exact for a spherical body.", + "doc_zh": "返回覆盖区的面积,单位为平方千米。\n\n该面积是被圆锥截去的球面部分,\n\n```text\nA = 2 pi R^2 (1 - cos(central))\n```\n\n对球形天体这是精确的。", "associated": true }, { @@ -3397,6 +3876,7 @@ "name": "fraction_of_surface", "signature": "pub fn fraction_of_surface(&self) -> f64 {", "doc": "Returns the fraction of the whole sphere covered by the footprint.\n\nThe value is between zero and one half, because a footprint from a\npoint above the surface can never cover more than a hemisphere.", + "doc_zh": "返回覆盖区占整个球面的比例。\n\n该值介于零与二分之一之间,因为来自地表之上某点的覆盖区覆盖范围\n绝不会超过一个半球。", "associated": true }, { @@ -3404,6 +3884,7 @@ "name": "contains_central_angle", "signature": "pub fn contains_central_angle(&self, central_angle: f64) -> bool {", "doc": "Returns `true` when a ground point at the given central angle from\nthe sub-satellite point lies inside the footprint.\n\nThe comparison is inclusive, so a point exactly on the edge is\ninside.", + "doc_zh": "当距星下点给定中心角的地面点位于覆盖区内时返回 `true`。\n\n比较是含端点的,故恰在边缘上的点算在内部。", "associated": true }, { @@ -3411,6 +3892,7 @@ "name": "footprint_central_angle", "signature": "pub fn footprint_central_angle( satellite_radius: f64, body_radius: f64, elevation_mask: f64, ) -> Option {", "doc": "Returns the central half angle of the footprint, in radians.\n\nThis is the largest central angle at which the satellite is still above\nthe elevation mask.\n\nReturns `None` when the geometry is not usable.", + "doc_zh": "返回覆盖区的中心半角,单位为弧度。\n\n这是卫星仍高于仰角遮蔽时的最大中心角。\n\n几何不可用时返回 `None`。", "associated": false }, { @@ -3418,6 +3900,7 @@ "name": "footprint", "signature": "pub fn footprint( satellite_radius: f64, body_radius: f64, elevation_mask: f64, ) -> Option {", "doc": "Returns the footprint for a satellite and an elevation mask.\n\nReturns `None` when the geometry is not usable.", + "doc_zh": "返回卫星与仰角遮蔽所对应的覆盖区。\n\n几何不可用时返回 `None`。", "associated": false }, { @@ -3425,6 +3908,7 @@ "name": "footprint_area", "signature": "pub fn footprint_area(satellite_radius: f64, body_radius: f64, elevation_mask: f64) -> Option {", "doc": "Returns the area of a footprint, in square kilometers.\n\nThis is a free function form of [`Footprint::area`].\n\nReturns `None` when the geometry is not usable.", + "doc_zh": "返回覆盖区的面积,单位为平方千米。\n\n这是 [`Footprint::area`] 的自由函数形式。\n\n几何不可用时返回 `None`。", "associated": false }, { @@ -3432,6 +3916,7 @@ "name": "footprint_fraction", "signature": "pub fn footprint_fraction( satellite_radius: f64, body_radius: f64, elevation_mask: f64, ) -> Option {", "doc": "Returns the fraction of the Earth's surface covered by a footprint.\n\nReturns `None` when the geometry is not usable.", + "doc_zh": "返回覆盖区所占地球表面的比例。\n\n几何不可用时返回 `None`。", "associated": false }, { @@ -3439,6 +3924,7 @@ "name": "central_angle_between", "signature": "pub fn central_angle_between(first: Vec3, second: Vec3) -> Option {", "doc": "Returns the angle between two surface points, in radians.\n\nThe vectors must be positions relative to the centre of the body, in the\nsame frame. The angle is the central angle between them, as seen from the\ncentre.\n\nReturns `None` when either vector has zero length or a non-finite\ncomponent.", + "doc_zh": "返回两个地表点之间的角度,单位为弧度。\n\n两矢量必须是相对天体中心、在同一坐标系中的位置。该角度是自中心看\n它们之间的中心角。\n\n任一矢量为零长或含非有限分量时返回 `None`。", "associated": false }, { @@ -3446,6 +3932,7 @@ "name": "point_in_footprint", "signature": "pub fn point_in_footprint( footprint: &Footprint, point: Vec3, sub_satellite_point: Vec3, ) -> Option {", "doc": "Returns `true` when a ground point lies inside a footprint.\n\nThe point and the sub-satellite point are positions relative to the\ncentre of the body, in kilometers. The footprint supplies the body radius\nand the central half angle.\n\nReturns `None` when either vector is degenerate or not finite.", + "doc_zh": "当某地面点位于某覆盖区内时返回 `true`。\n\n该点与星下点都是相对天体中心的位置,单位为千米。覆盖区提供天体半径\n与中心半角。\n\n任一矢量退化或非有限时返回 `None`。", "associated": false }, { @@ -3453,6 +3940,7 @@ "name": "footprint_edge_elevation", "signature": "pub fn footprint_edge_elevation(satellite_radius: f64, footprint: &Footprint) -> Option {", "doc": "Returns the elevation at which a footprint edge is seen from the ground,\nin radians.\n\nThis is the inverse of [`footprint_central_angle`], useful when a\nfootprint is known and the mask it corresponds to is wanted.\n\nReturns `None` when the geometry is not usable.", + "doc_zh": "返回自地面看覆盖区边缘的仰角,单位为弧度。\n\n它是 [`footprint_central_angle`] 的逆,当已知覆盖区而想求它对应的遮蔽时\n有用。\n\n几何不可用时返回 `None`。", "associated": false }, { @@ -3460,6 +3948,7 @@ "name": "nadir_angle", "signature": "pub fn nadir_angle(satellite_radius: f64, body_radius: f64, elevation: f64) -> Option {", "doc": "Returns the nadir angle at the satellite, in radians.\n\nThe nadir angle is measured at the satellite between the direction to the\ncentre of the Earth and the direction to the ground point. It is zero when\nthe ground point is directly below the satellite and grows as the point\nmoves toward the horizon.\n\nReturns `None` when the radii are not usable, when the body radius\nexceeds the satellite radius, or when the elevation is not finite.", + "doc_zh": "返回卫星处的天底角,单位为弧度。\n\n天底角在卫星处量取,介于到地心的方向与到地面点的方向之间。当地面点\n恰在卫星正下方时它为零,并随该点移向地平而增大。\n\n半径不可用、天体半径超过卫星半径,或仰角非有限时返回 `None`。", "associated": false }, { @@ -3467,6 +3956,7 @@ "name": "earth_central_angle", "signature": "pub fn earth_central_angle(satellite_radius: f64, body_radius: f64, elevation: f64) -> Option {", "doc": "Returns the earth central angle between the sub-satellite point and the\nground point, in radians.\n\nThe central angle is the angle at the centre of the Earth between the two\nradial directions. It is the distance along the surface, divided by the\nbody radius.\n\nReturns `None` when the geometry is not usable.", + "doc_zh": "返回星下点与地面点之间的地心中心角,单位为弧度。\n\n中心角是地心处两个径向方向之间的角。它是沿地表的距离除以天体半径。\n\n几何不可用时返回 `None`。", "associated": false }, { @@ -3474,6 +3964,7 @@ "name": "slant_range", "signature": "pub fn slant_range(satellite_radius: f64, body_radius: f64, elevation: f64) -> Option {", "doc": "Returns the slant range between the satellite and the ground point, in\nkilometers.\n\nThe range is the straight line distance, not the distance along the\nsurface. It is the quantity a link budget needs, because it sets the free\nspace path loss.\n\nThe relation is the law of cosines applied to the triangle formed by the\nsatellite, the ground point, and the centre of the Earth,\n\n```text\nrange^2 = satellite_radius^2 + body_radius^2\n - 2 satellite_radius body_radius cos(central)\n```\n\nThe law of cosines is used rather than the equivalent form\n`Rs sin(central) / cos(elevation)` because the latter is singular at the\nzenith, where the elevation is a right angle and its cosine is zero,\neven though the range there is simply the altitude.\n\nReturns `None` when the geometry is not usable.", + "doc_zh": "返回卫星与地面点之间的斜距,单位为千米。\n\n该距离是直线距离,而非沿地表的距离。它是链路预算所需的量,因为它决定自由\n空间路径损耗。\n\n该关系是余弦定理施加于卫星、地面点与地心构成的三角形:\n\n```text\nrange^2 = satellite_radius^2 + body_radius^2\n - 2 satellite_radius body_radius cos(central)\n```\n\n此处用余弦定理而非等价形式 `Rs sin(central) / cos(elevation)`,因为后者在\n天顶处奇异,那里仰角是直角、其余弦为零,尽管那里的距离恰是高度。\n\n几何不可用时返回 `None`。", "associated": false }, { @@ -3481,6 +3972,7 @@ "name": "elevation_from_central_angle", "signature": "pub fn elevation_from_central_angle( satellite_radius: f64, body_radius: f64, central_angle: f64, ) -> Option {", "doc": "Returns the elevation angle for a central angle, in radians.\n\nThis inverts [`earth_central_angle`]. It is used when the position of a\nground point relative to the sub-satellite point is known and the\nelevation at that point is wanted.\n\nThe relation is\n\n```text\ntan(elevation) = (cos(central) - body_radius / satellite_radius)\n / sin(central)\n```\n\nReturns `None` when the radii are not usable or when the central angle is\nnot finite.", + "doc_zh": "返回某中心角对应的仰角,单位为弧度。\n\n它反解 [`earth_central_angle`]。当已知地面点相对星下点的位置而要求\n该点处仰角时使用它。\n\n关系为\n\n```text\ntan(elevation) = (cos(central) - body_radius / satellite_radius)\n / sin(central)\n```\n\n半径不可用或中心角非有限时返回 `None`。", "associated": false }, { @@ -3488,6 +3980,7 @@ "name": "GroundPoint", "signature": "pub struct GroundPoint {", "doc": "A ground station, given as a geodetic position on an ellipsoid.", + "doc_zh": "一个地面站,以椭球上的大地位置给出。", "associated": false }, { @@ -3495,6 +3988,7 @@ "name": "new", "signature": "pub fn new(position: Geodetic, elevation_mask: f64) -> Self {", "doc": "Creates a ground point on the WGS 84 ellipsoid with a given mask.", + "doc_zh": "在 WGS 84 椭球上以给定遮蔽创建地面点。", "associated": true }, { @@ -3502,6 +3996,7 @@ "name": "ecef", "signature": "pub fn ecef(&self) -> Vec3 {", "doc": "Returns the position of the station in the Earth-fixed Cartesian\nframe, in kilometers.\n\nThe geodetic conversion yields meters, so the result is divided by a\nthousand to match the frame the orbit layer uses.", + "doc_zh": "返回该站在地固直角坐标系中的位置,单位为千米。\n\n大地转换得到的是米,故结果除以一千,以与 orbit 层所用的坐标系\n一致。", "associated": true }, { @@ -3509,6 +4004,7 @@ "name": "radius", "signature": "pub fn radius(&self) -> f64 {", "doc": "Returns the distance from the centre of the Earth, in kilometers.\n\nThis is the local radius of the ellipsoid at the station's latitude\nplus its height, so it includes the effect of the flattening.", + "doc_zh": "返回距地心的距离,单位为千米。\n\n这是该站纬度处椭球的当地半径加上其高度,故包含了扁率的影响。", "associated": true }, { @@ -3516,6 +4012,7 @@ "name": "is_valid", "signature": "pub fn is_valid(&self) -> bool {", "doc": "Returns `true` when the station and its mask describe a usable\nsensor.", + "doc_zh": "当该站及其遮蔽描述一个可用传感器时返回 `true`。", "associated": true }, { @@ -3523,6 +4020,7 @@ "name": "Sensor", "signature": "pub struct Sensor {", "doc": "A sensor at a ground point, with a pointing direction.\n\nThe direction is a unit vector in the Earth-fixed frame. It is optional,\nbecause a station that can point anywhere with sufficient gain is\nmodelled by its elevation mask alone; the direction matters only when the\nstation has a fixed beam.", + "doc_zh": "位于地面点、具有指向方向的传感器。\n\n方向是地固系中的单位矢量。它是可选的,因为能以足够增益指向任何\n方向的站仅以其仰角遮蔽建模即可;只有当站具有固定波束时方向才要紧。", "associated": false }, { @@ -3530,6 +4028,7 @@ "name": "omnidirectional", "signature": "pub const fn omnidirectional(ground: GroundPoint) -> Self {", "doc": "Creates a station that can point anywhere above its mask.", + "doc_zh": "创建能在其遮蔽之上指向任何方向的站。", "associated": true }, { @@ -3537,6 +4036,7 @@ "name": "directional", "signature": "pub const fn directional(ground: GroundPoint, pointing: Vec3) -> Self {", "doc": "Creates a station with a fixed pointing direction.\n\nThe direction is normalized when it is used, so the caller may pass\nan unnormalized vector.", + "doc_zh": "创建具有固定指向方向的站。\n\n该方向在使用时被归一化,故调用方可以传入未归一化的矢量。", "associated": true }, { @@ -3544,6 +4044,7 @@ "name": "SubSatellitePoint", "signature": "pub struct SubSatellitePoint {", "doc": "The point on the Earth's surface directly below a satellite.", + "doc_zh": "卫星正下方的地表点。", "associated": false }, { @@ -3551,6 +4052,7 @@ "name": "sub_satellite_point", "signature": "pub fn sub_satellite_point( position_ecef_km: Vec3, ellipsoid: Ellipsoid, ) -> Option {", "doc": "Returns the sub-satellite point for a position in the Earth-fixed frame.\n\nThe satellite position is in kilometers. The sub-satellite point is the\nintersection of the line from the centre of the Earth through the\nsatellite with the ellipsoid, which is the geodetic position of the\nsatellite with its height set to zero at the same latitude and longitude.\n\nThis is the geocentric projection, not the point at which the ellipsoid\nnormal through the satellite meets the surface. The difference is at most\nabout twenty kilometers for a low Earth satellite, and the geocentric\nform is the one the footprint geometry uses.\n\nReturns `None` when the position is not finite or lies on the polar axis,\nwhere no longitude is defined.", + "doc_zh": "返回地固系中某位置的星下点。\n\n卫星位置单位为千米。星下点是从地心过卫星的直线与椭球的交点,即把\n卫星高度置零而纬经度不变所得的大地位置。\n\n这是地心投影,而非过卫星的椭球法线与地表相交的点。对低轨卫星两者\n相差至多约二十千米,而覆盖区几何用的是地心形式。\n\n位置非有限,或位于未定义经度的极轴上时返回 `None`。", "associated": false }, { @@ -3558,6 +4060,7 @@ "name": "elevation_angle", "signature": "pub fn elevation_angle(satellite_ecef_km: Vec3, ground: &GroundPoint) -> Option {", "doc": "Returns the elevation of a satellite as seen from a station, in radians.\n\nThe satellite position is in the Earth-fixed frame, in kilometers. The\nelevation is measured from the local horizon, which is the plane\nperpendicular to the ellipsoid normal at the station.\n\nA positive value means the satellite is above the horizon, a value of a\nright angle means it is at the zenith, and a negative value means it is\nbelow the horizon.\n\nReturns `None` when either position is not usable.", + "doc_zh": "返回自某站看某卫星的仰角,单位为弧度。\n\n卫星位置在地固系中,单位为千米。仰角自当地地平量起,后者是垂直于该\n站处椭球法线的平面。\n\n正值表示卫星在地平之上,直角值表示在天顶,负值表示在地平之下。\n\n任一位置不可用时返回 `None`。", "associated": false }, { @@ -3565,6 +4068,7 @@ "name": "local_up", "signature": "pub fn local_up(ground: &GroundPoint) -> Vec3 {", "doc": "Returns the unit vector along the ellipsoid normal at a station.\n\nThis is the local vertical, the direction a plumb line points. For a\ngeodetic position it follows from the latitude and longitude, because the\nnormal of a biaxial ellipsoid lies in the meridian plane and makes the\ngeodetic latitude with the equatorial plane.", + "doc_zh": "返回沿某站处椭球法线的单位矢量。\n\n这是当地垂线,即铅垂线所指的方向。对大地位置而言,它由纬度与经度\n得出,因为双轴椭球的法线位于子午面内,并与赤道面夹大地纬度。", "associated": false }, { @@ -3572,6 +4076,7 @@ "name": "line_of_sight", "signature": "pub fn line_of_sight(satellite_ecef_km: Vec3, ground: &GroundPoint) -> Option {", "doc": "Returns the elevation of a satellite as seen from a station, in radians.\n\nThis is the same as [`elevation_angle`], named for symmetry with the\nother visibility functions.\n\nReturns `None` when either position is not usable.", + "doc_zh": "返回自某站看某卫星的仰角,单位为弧度。\n\n这与 [`elevation_angle`] 相同,其命名是为与其余可见性函数对称。\n\n任一位置不可用时返回 `None`。", "associated": false }, { @@ -3579,6 +4084,7 @@ "name": "visible_from", "signature": "pub fn visible_from(satellite_ecef_km: Vec3, sensor: &Sensor) -> Option {", "doc": "Returns `true` when a satellite is visible from a sensor.\n\nThe satellite is visible when its elevation at the station is at least\nthe sensor's mask, and, for a directional sensor, when the direction to\nthe satellite is within a right angle of the pointing direction, so that\nthe satellite is in the forward hemisphere of the beam.\n\nReturns `None` when the satellite position or the sensor is not usable.", + "doc_zh": "当从某传感器可见某卫星时返回 `true`。\n\n当卫星在该站处的仰角不低于该传感器的遮蔽,且(对定向传感器)到卫星\n的方向与指向方向的夹角在一个直角以内,即卫星处于波束的前半球时,\n卫星可见。\n\n卫星位置或传感器不可用时返回 `None`。", "associated": false }, { @@ -3586,6 +4092,7 @@ "name": "visible_from_geodetic", "signature": "pub fn visible_from_geodetic( satellite_ecef_km: Vec3, position: Geodetic, elevation_mask: f64, ) -> Option {", "doc": "Returns the elevation of a satellite as seen from a geodetic station.\n\nThis is a convenience for a station given directly as a geodetic\nposition, without building a [`GroundPoint`].\n\nReturns `None` when the satellite position or the station is not usable.", + "doc_zh": "返回自某大地站看某卫星的仰角。\n\n这是为直接把站作为大地位置给出、而不构建 [`GroundPoint`] 的便利\n函数。\n\n卫星位置或该站不可用时返回 `None`。", "associated": false }, { @@ -3593,6 +4100,7 @@ "name": "central_angle_to_station", "signature": "pub fn central_angle_to_station( satellite_ecef_km: Vec3, ground: &GroundPoint, elevation: f64, ) -> Option {", "doc": "Returns the central angle between a satellite and a ground station, in\nradians.\n\nThis uses the spherical relation between the two radii and the elevation,\nso it is consistent with the footprint geometry. The satellite position\nis used only for its radius.\n\nReturns `None` when the geometry is not usable.", + "doc_zh": "返回卫星与地面站之间的中心角,单位为弧度。\n\n它使用两个半径与仰角之间的球面关系,故与覆盖区几何一致。卫星位置只用于其\n半径。\n\n几何不可用时返回 `None`。", "associated": false } ] @@ -3600,13 +4108,15 @@ { "name": "apsis-access", "layer": "network", - "summary": "Ground station access windows. A satellite is accessible from a ground station during the intervals when it rises above the station's elevation mask. Each such interval is a pass, or access window, and the sequence of windows over a period is the access schedule. This crate finds those windows from a propagated orbit and reports their start, end, duration, and the geometry at their peak. The windows are found by sampling the elevation angle at a fixed step and refining each crossing where the elevation passes the mask. The sampling step sets the shortest pass that can be found, and the refinement gives the crossing times to a tolerance independent of the step. The elevation is evaluated from the satellite position in the Earth-fixed frame, so the caller supplies the transformation from the orbit frame. In this workspace that is the conversion in `apsis-coord` with the Earth rotation angle from `apsis-frames`. ``` use apsis_access::{AccessFinder, Pass}; use apsis_coverage::GroundPoint; use apsis_geodesy::Geodetic; // A station on the equator with a ten degree mask. let station = GroundPoint::new(Geodetic::new(0.0, 0.0, 0.0), 10.0_f64.to_radians()); let finder = AccessFinder { station, sample_step_seconds: 60.0, crossing_tolerance_seconds: 0.001, }; assert!(finder.is_valid()); ```", + "summary": "Ground station access windows.\n\nA satellite is accessible from a ground station during the intervals when\nit rises above the station's elevation mask. Each such interval is a\npass, or access window, and the sequence of windows over a period is the\naccess schedule. This crate finds those windows from a propagated orbit\nand reports their start, end, duration, and the geometry at their peak.\n\nThe windows are found by sampling the elevation angle at a fixed step and\nrefining each crossing where the elevation passes the mask. The sampling\nstep sets the shortest pass that can be found, and the refinement gives\nthe crossing times to a tolerance independent of the step.\n\nThe elevation is evaluated from the satellite position in the\nEarth-fixed frame, so the caller supplies the transformation from the\norbit frame. In this workspace that is the conversion in `apsis-coord`\nwith the Earth rotation angle from `apsis-frames`.\n\n```\nuse apsis_access::{AccessFinder, Pass};\nuse apsis_coverage::GroundPoint;\nuse apsis_geodesy::Geodetic;\n\n// A station on the equator with a ten degree mask.\nlet station = GroundPoint::new(Geodetic::new(0.0, 0.0, 0.0), 10.0_f64.to_radians());\nlet finder = AccessFinder {\n station,\n sample_step_seconds: 60.0,\n crossing_tolerance_seconds: 0.001,\n};\nassert!(finder.is_valid());\n```", + "summary_zh": "地面站可见性窗口。\n\n当卫星升到地面站仰角遮蔽之上时,它在那段时间内可从该站访问。每个这样的区间\n是一次过境,或称可见性窗口,而一段时间上的窗口序列即可见性调度。本 crate 从\n传播后的轨道找出这些窗口,并报告其开始、结束、时长以及峰值处的几何。\n\n窗口通过对仰角以固定步长采样、并对仰角穿过遮蔽的每个穿越加以细化来找到。采样\n步长设定所能找到的最短过境,而细化给出独立于步长的穿越时刻精度。\n\n仰角由地固系中的卫星位置求值,故调用方提供从轨道坐标系来的变换。在本工作区中\n那是 `apsis-coord` 中的换算,配以来自 `apsis-frames` 的地球自转角。", "items": [ { "kind": "fn", "name": "find_all", "signature": "pub fn find_all( finder: &AccessFinder, tracks: &[Vec], ) -> Vec> {", "doc": "Searches access for many tracks, in order.\n\nThe sequential form, provided for comparison and for small sets where\nstarting a thread would cost more than the search.", + "doc_zh": "按次序为许多轨迹搜索可见性。\n\n顺序形式,供比较以及线程启动代价高于搜索本身的小规模情形使用。", "associated": false }, { @@ -3614,6 +4124,7 @@ "name": "find_all_parallel", "signature": "pub fn find_all_parallel( finder: &AccessFinder, tracks: &[Vec], ) -> Vec> {", "doc": "Searches access for many tracks, across several threads.\n\nThe result matches [`find_all`] element for element and in the same order.\nA track that cannot be searched yields its error in the same position the\nsequential form would put it, so one bad track does not hide the rest.", + "doc_zh": "跨多个线程为许多轨迹搜索可见性。\n\n结果与 [`find_all`] 逐元素、逐次序相同。无法搜索的轨迹在顺序形式会\n放置它的同一位置给出其错误,故一条坏轨迹不会掩盖其余。", "associated": false }, { @@ -3621,6 +4132,7 @@ "name": "AccessErrorKind", "signature": "pub enum AccessErrorKind {", "doc": "The category of an access error.", + "doc_zh": "接入错误的类别。", "associated": false }, { @@ -3628,6 +4140,7 @@ "name": "AccessError", "signature": "pub struct AccessError {", "doc": "A failure while finding access windows.", + "doc_zh": "查找可见性窗口时发生的失败。", "associated": false }, { @@ -3635,6 +4148,7 @@ "name": "new", "signature": "pub fn new(kind: AccessErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -3642,6 +4156,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the values that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -3649,6 +4164,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> AccessErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -3656,6 +4172,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -3663,6 +4180,7 @@ "name": "OrbitSample", "signature": "pub struct OrbitSample {", "doc": "One sample of a satellite track, as the finder consumes it.\n\nThe position is in the Earth-fixed frame, in kilometers, and the time is\nin seconds from the caller's origin. The caller supplies a sequence of\nthese, which decouples the finder from the propagation and the frame\ntransformation.", + "doc_zh": "卫星轨迹的一个样本,按其被查找器消费的形式。\n\n位置在地固系中,单位为千米;时间以距调用方原点的秒数表示。调用方\n提供这样的一串样本,使查找器与传播及坐标系变换解耦。", "associated": false }, { @@ -3670,6 +4188,7 @@ "name": "new", "signature": "pub const fn new(seconds: f64, position_ecef_km: Vec3) -> Self {", "doc": "Creates a sample from a time and a position.", + "doc_zh": "由时间与位置创建样本。", "associated": true }, { @@ -3677,6 +4196,7 @@ "name": "is_valid", "signature": "pub fn is_valid(&self) -> bool {", "doc": "Returns `true` when the sample is usable.", + "doc_zh": "当该样本可用时返回 `true`。", "associated": true }, { @@ -3684,6 +4204,7 @@ "name": "AccessFinder", "signature": "pub struct AccessFinder {", "doc": "Finds access windows for one station.\n\nThe finder holds the station and the numerical settings of the search.\nThe settings are separated from the search so that the same finder can be\napplied to many satellites.", + "doc_zh": "为一个地面站查找可见性窗口。\n\n查找器保存地面站与搜索的数值设置。设置与搜索分开,使同一个查找器可\n应用于许多卫星。", "associated": false }, { @@ -3691,13 +4212,15 @@ "name": "elevation_of", "signature": "pub fn elevation_of(&self, sample: &OrbitSample) -> Option {", "doc": "Returns the elevation of a sample, in radians.\n\nReturns `None` when the sample or the station is not usable.", + "doc_zh": "返回某样本的仰角,单位为弧度。\n\n样本或地面站不可用时返回 `None`。", "associated": true }, { "kind": "fn", "name": "find", "signature": "pub fn find(&self, samples: &[OrbitSample]) -> Result {", - "doc": "Finds the access windows in an ordered sequence of samples.\n\nThe samples must be ordered by time and cover the interval of\ninterest. The finder assumes the elevation is continuous between\nsamples, so the step must be short enough that the satellite does not\nrise and set within one step. A low Earth satellite is above a\nten degree mask for several minutes, so a step of a minute or less\nsatisfies that.\n\nThe crossing times are found by interpolating the satellite position\nlinearly between the two samples that straddle the crossing. The\nchord between two samples departs from the true arc by an amount of\nthe second order in the step, so a ten second step gives a crossing\naccurate to about a tenth of a second and a one second step to about\na millisecond. Use [`AccessFinder::find_with`] when the propagator is\navailable and the crossing is wanted to the full tolerance.\n\nA pass that is already in progress at the first sample is reported\nwith its rise at the first sample time, and one still in progress at\nthe last sample is reported with its set at the last sample time.\nThat convention means the pass is clipped to the interval searched\nrather than extrapolated beyond it.\n\nReturns an error when the settings are not usable, when the sample\nsequence has fewer than two entries, or when a sample is not usable.", + "doc": "Finds the access windows in an ordered sequence of samples.\n\nThe samples must be ordered by time and cover the interval of\ninterest. The finder assumes the elevation is continuous between\nsamples, so the step must be short enough that the satellite does not\nrise and set within one step. A low Earth satellite is above a\nten degree mask for several minutes, so a step of a minute or less\nsatisfies that.\n\nThe crossing times are found by interpolating the satellite position\nlinearly between the two samples that straddle the crossing. The\nchord between two samples departs from the true arc by an amount of\nthe second order in the step, so a ten second step gives a crossing\naccurate to about a tenth of a second and a one second step to about\na millisecond. Use [`AccessFinder::find_with`] when the propagator is\navailable and the crossing is wanted to the full tolerance.\n\nA pass that is already in progress at the first sample is reported\nwith its rise at the first sample time, and one still in progress at\nthe last sample is reported with its set at the last sample time.\nThat convention means the pass is clipped to the interval searched\nrather than extrapolated beyond it.\n\nReturns an error when the settings are not usable, when the sample\nsequence has fewer than two entries, when a sample is not usable, or\nwhen the sequence is not in increasing time order. The order is\nchecked rather than assumed, because the interpolation between two\nsamples binary searches the sequence and a binary search over an\nunordered list returns a wrong index without failing.", + "doc_zh": "在有序的样本序列中查找可见性窗口。\n\n各样本必须按时间排序并覆盖所关注的区间。查找器假定仰角在样本\n之间连续,故步长必须短到卫星不会在一个步长内升起又降落。低轨\n卫星在十度遮蔽以上持续数分钟,故一分钟或更短的步长即满足。\n\n穿越时刻通过在跨越该穿越的两个样本之间线性插值卫星位置求得。\n两样本之间的弦与真实弧的偏差为步长的二阶量,故十秒步长给出的\n穿越精度约为十分之一秒,一秒步长约为毫秒。当传播器可用且希望\n穿越达到完整容差时,用 [`AccessFinder::find_with`]。\n\n在第一个样本处已进行中的过境,其升起时刻报告为第一个样本时间;\n在最后一个样本处仍在进行中的过境,其降落时刻报告为最后一个\n样本时间。该约定意味着过境被裁剪到所搜索的区间,而不外推超出\n它。\n\n设置不可用、样本序列少于两项、某样本不可用,或序列未按时间\n递增排列时返回错误。次序是被检查而非假定的,因为两样本之间的\n插值会对序列做二分查找,而对无序列表的二分查找会返回错误的下标\n而不报错。", "associated": true }, { @@ -3705,6 +4228,7 @@ "name": "find_with", "signature": "pub fn find_with( &self, start_seconds: f64, end_seconds: f64, mut propagator: F, ) -> Result where F: FnMut(f64) -> Option, {", "doc": "Finds the access windows over an interval, calling a propagator for\neach sample.\n\nThe propagator receives a time in seconds from the origin of the\nsearch and returns the satellite position in the Earth-fixed frame.\nThis adapts the finder to any orbit source without this crate\ndepending on one.\n\nReturns an error when the settings are not usable, when the interval\nis not ordered, or when the propagator fails at a sample.", + "doc_zh": "在某个区间上查找可见性窗口,对每个样本调用一次传播器。\n\n传播器接收以距搜索原点的秒数表示的时间,返回地固系中的卫星\n位置。这使查找器可适配任何轨道来源,而本 crate 不依赖于其一。\n\n设置不可用、区间次序不对,或传播器在某样本处失败时返回错误。", "associated": true }, { @@ -3712,6 +4236,7 @@ "name": "Pass", "signature": "pub struct Pass {", "doc": "One continuous interval during which a satellite is above a station's\nelevation mask.\n\nTimes are seconds from an origin chosen by the caller, so the same\nstructure serves a search over a scenario clock and one over elapsed\ntime.", + "doc_zh": "卫星高于某地面站仰角遮蔽的一个连续区间。\n\n时间是以距调用方所选原点的秒数表示,故同一结构既服务于按场景时钟的\n搜索,也服务于按流逝时间的搜索。", "associated": false }, { @@ -3719,6 +4244,7 @@ "name": "duration", "signature": "pub fn duration(&self) -> f64 {", "doc": "Returns the duration of the pass, in seconds.", + "doc_zh": "返回该过境的时长,单位为秒。", "associated": true }, { @@ -3726,6 +4252,7 @@ "name": "rise_to_peak", "signature": "pub fn rise_to_peak(&self) -> f64 {", "doc": "Returns the time from the rise to the peak, in seconds.", + "doc_zh": "返回从升起到峰值的时间,单位为秒。", "associated": true }, { @@ -3733,6 +4260,7 @@ "name": "contains", "signature": "pub fn contains(&self, seconds: f64) -> bool {", "doc": "Returns the elevation at a time within the pass, when the caller can\nsupply it.\n\nThe pass stores only the peak, so this helper reports the stored\nvalues at the three defining times and defers to the caller's\nelevation function otherwise.", + "doc_zh": "返回过境内某时刻的仰角,当调用方能提供它时。\n\n过境只保存峰值,故该辅助函数在三个界定时刻报告所存的值,其余\n情形交由调用方的仰角函数。", "associated": true }, { @@ -3740,6 +4268,7 @@ "name": "maximum_elevation", "signature": "pub fn maximum_elevation(&self) -> f64 {", "doc": "Returns the maximum elevation of the pass, in radians.\n\nThis is the same as the peak elevation, named for readability at a\ncall site that compares passes.", + "doc_zh": "返回该过境的最大仰角,单位为弧度。\n\n这与峰值仰角相同,其命名是为在比较过境的调用处更具可读性。", "associated": true }, { @@ -3747,6 +4276,7 @@ "name": "PassSummary", "signature": "pub struct PassSummary {", "doc": "A summary of the access windows over an interval.", + "doc_zh": "某区间上可见性窗口的汇总。", "associated": false }, { @@ -3754,6 +4284,7 @@ "name": "new", "signature": "pub fn new(passes: Vec, interval_seconds: f64) -> Self {", "doc": "Creates a summary from a list of passes and the interval length.", + "doc_zh": "由过境列表与区间长度创建汇总。", "associated": true }, { @@ -3761,6 +4292,7 @@ "name": "count", "signature": "pub fn count(&self) -> usize {", "doc": "Returns the number of passes.", + "doc_zh": "返回过境的数量。", "associated": true }, { @@ -3768,6 +4300,7 @@ "name": "total_access_seconds", "signature": "pub fn total_access_seconds(&self) -> f64 {", "doc": "Returns the total access time, in seconds.\n\nThe passes are disjoint, so the durations add.", + "doc_zh": "返回总可见时间,单位为秒。\n\n各过境互不相交,故时长相加。", "associated": true }, { @@ -3775,6 +4308,7 @@ "name": "access_fraction", "signature": "pub fn access_fraction(&self) -> Option {", "doc": "Returns the fraction of the interval during which the satellite was\naccessible.\n\nReturns `None` when the interval length is not positive.", + "doc_zh": "返回卫星可见所占该区间的比例。\n\n区间长度非正时返回 `None`。", "associated": true }, { @@ -3782,6 +4316,7 @@ "name": "mean_duration_seconds", "signature": "pub fn mean_duration_seconds(&self) -> Option {", "doc": "Returns the mean pass duration, in seconds.\n\nReturns `None` when there are no passes.", + "doc_zh": "返回过境的平均时长,单位为秒。\n\n没有过境时返回 `None`。", "associated": true }, { @@ -3789,6 +4324,7 @@ "name": "longest", "signature": "pub fn longest(&self) -> Option<&Pass> {", "doc": "Returns the longest pass, if there is one.", + "doc_zh": "返回最长的过境(若存在)。", "associated": true }, { @@ -3796,6 +4332,7 @@ "name": "highest", "signature": "pub fn highest(&self) -> Option<&Pass> {", "doc": "Returns the highest pass, if there is one.", + "doc_zh": "返回仰角最高的过境(若存在)。", "associated": true }, { @@ -3803,6 +4340,7 @@ "name": "gap_seconds", "signature": "pub fn gap_seconds(&self, first: usize, second: usize) -> Option {", "doc": "Returns the gap between two consecutive passes, in seconds.\n\nReturns `None` when either index is out of range or the two indices\nare not consecutive and increasing.", + "doc_zh": "返回两个相邻过境之间的间隙,单位为秒。\n\n任一下标越界,或两个下标不相邻且递增时返回 `None`。", "associated": true }, { @@ -3810,6 +4348,7 @@ "name": "longest_gap_seconds", "signature": "pub fn longest_gap_seconds(&self) -> Option {", "doc": "Returns the largest gap between consecutive passes, in seconds.\n\nThis is the longest time a station waits between contacts.\nReturns `None` when there are fewer than two passes.", + "doc_zh": "返回相邻过境之间的最大间隙,单位为秒。\n\n这是一个地面站在两次通信之间等待的最长时间。过境少于两个时返回\n`None`。", "associated": true } ] @@ -3817,13 +4356,15 @@ { "name": "apsis-topology", "layer": "network", - "summary": "Time-varying network topology. A satellite network is not a single graph. The set of usable links changes as the satellites move, so the network is a sequence of graphs indexed by time, and this crate provides the data structures for that sequence. The model is deliberately simple. A [`Topology`] is a graph with a fixed set of nodes, which are satellites or ground stations, and a set of edges that may be present or absent. A [`TopologySeries`] is a sequence of topologies at increasing times, together with the interval each one applies to. The series is a step function: a topology holds from its own time until the time of the next entry. That is the standard discrete time model for a time-varying graph, and it is what routing over the series expects. Edges carry a cost as well as their presence, so that a routing algorithm has something to minimise. The cost is a plain number, so a caller may put a delay, a hop count, or anything else in it, as long as the routing algorithm and the cost agree on the meaning. ``` use apsis_topology::{Topology, TopologySample, TopologySeries}; // Two satellites linked to each other. let mut topology = Topology::new(2); topology.add_edge(0, 1, 1.0).expect(\"the nodes exist\"); assert!(topology.has_edge(0, 1)); let series = TopologySeries::new(vec![TopologySample::new(0.0, topology)]) .expect(\"the series is not empty\"); assert_eq!(series.len(), 1); assert!(series.topology_at(0.0).expect(\"a topology exists\").has_edge(0, 1)); ```", + "summary": "Time-varying network topology.\n\nA satellite network is not a single graph. The set of usable links\nchanges as the satellites move, so the network is a sequence of graphs\nindexed by time, and this crate provides the data structures for that\nsequence.\n\nThe model is deliberately simple. A [`Topology`] is a graph with a fixed\nset of nodes, which are satellites or ground stations, and a set of edges\nthat may be present or absent. A [`TopologySeries`] is a sequence of\ntopologies at increasing times, together with the interval each one\napplies to.\n\nThe series is a step function: a topology holds from its own time until\nthe time of the next entry. That is the standard discrete time model for\na time-varying graph, and it is what routing over the series expects.\n\nEdges carry a cost as well as their presence, so that a routing algorithm\nhas something to minimise. The cost is a plain number, so a caller may\nput a delay, a hop count, or anything else in it, as long as the routing\nalgorithm and the cost agree on the meaning.\n\n```\nuse apsis_topology::{Topology, TopologySample, TopologySeries};\n\n// Two satellites linked to each other.\nlet mut topology = Topology::new(2);\ntopology.add_edge(0, 1, 1.0).expect(\"the nodes exist\");\nassert!(topology.has_edge(0, 1));\n\nlet series = TopologySeries::new(vec![TopologySample::new(0.0, topology)])\n .expect(\"the series is not empty\");\nassert_eq!(series.len(), 1);\nassert!(series.topology_at(0.0).expect(\"a topology exists\").has_edge(0, 1));\n```", + "summary_zh": "时变网络拓扑。\n\n卫星网络不是单一的图。可用链路的集合随卫星移动而改变,故该网络是按时间索引的\n一个图序列,而本 crate 提供该序列的数据结构。\n\n模型有意简单。一个 [`Topology`] 是节点集合固定、边集合可有可无的图,节点是卫星\n或地面站。一个 [`TopologySeries`] 是递增时刻上一串拓扑,连同各自适用的区间。\n\n该序列是阶跃函数:一个拓扑从其自身时间起保持到下一项的时间。这是时变图的标准\n离散时间模型,也是对该序列路由所期望的。\n\n边除存在与否外还携带代价,使路由算法有可最小化之物。该代价是一个普通数,故\n调用方可以放一个时延、一个跳数,或任何别的东西,只要路由算法与代价在含义上\n一致。", "items": [ { "kind": "enum", "name": "TopologyErrorKind", "signature": "pub enum TopologyErrorKind {", "doc": "The category of a topology error.", + "doc_zh": "拓扑错误的类别。", "associated": false }, { @@ -3831,6 +4372,7 @@ "name": "TopologyError", "signature": "pub struct TopologyError {", "doc": "A failure while building or querying a topology.", + "doc_zh": "构建或查询拓扑时发生的失败。", "associated": false }, { @@ -3838,6 +4380,7 @@ "name": "new", "signature": "pub fn new(kind: TopologyErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -3845,6 +4388,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the values that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -3852,6 +4396,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> TopologyErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -3859,6 +4404,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -3866,6 +4412,7 @@ "name": "Edge", "signature": "pub struct Edge {", "doc": "One link in a topology.", + "doc_zh": "拓扑中的一条链路。", "associated": false }, { @@ -3873,6 +4420,7 @@ "name": "new", "signature": "pub const fn new(from: usize, to: usize, cost: f64) -> Self {", "doc": "Creates an edge.", + "doc_zh": "创建一条边。", "associated": true }, { @@ -3880,6 +4428,7 @@ "name": "reversed", "signature": "pub const fn reversed(self) -> Self {", "doc": "Returns the same edge with its endpoints exchanged.\n\nA link between two satellites carries traffic both ways, so most\ntopologies add both directions.", + "doc_zh": "返回端点互换后的同一条边。\n\n两颗卫星之间的链路双向承载业务,故多数拓扑会添加两个方向。", "associated": true }, { @@ -3887,6 +4436,7 @@ "name": "Topology", "signature": "pub struct Topology {", "doc": "A graph of nodes and links at one instant.\n\nThe node count is fixed at construction and nodes are identified by their\nindex. Edges are directed: adding an edge from one node to another does\nnot add the reverse, so a caller that wants a bidirectional link adds\nboth directions. This is deliberate, because an inter-satellite link may\nhave different capacity in each direction and a ground link is often\nasymmetric.", + "doc_zh": "某一瞬时的节点与链路图。\n\n节点数在构造时固定,节点以其下标标识。边是有向的:从一节点向另一\n节点添加边不会添加反向边,故需要双向链路的调用方要添加两个方向。\n这是有意的,因为星间链路每个方向的容量可能不同,而地面链路往往\n不对称。", "associated": false }, { @@ -3894,6 +4444,7 @@ "name": "new", "signature": "pub fn new(node_count: usize) -> Self {", "doc": "Creates an empty topology with the given number of nodes.\n\nThis is not a `const fn` because it sizes the adjacency index, which\nallocates.", + "doc_zh": "以给定节点数创建空拓扑。\n\n它不是 `const fn`,因为它要为邻接索引确定大小,而这会分配。", "associated": true }, { @@ -3901,6 +4452,7 @@ "name": "node_count", "signature": "pub const fn node_count(&self) -> usize {", "doc": "Returns the number of nodes.", + "doc_zh": "返回节点的数量。", "associated": true }, { @@ -3908,6 +4460,7 @@ "name": "edge_count", "signature": "pub fn edge_count(&self) -> usize {", "doc": "Returns the number of edges.", + "doc_zh": "返回边的数量。", "associated": true }, { @@ -3915,6 +4468,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when there are no edges.", + "doc_zh": "当没有边时返回 `true`。", "associated": true }, { @@ -3922,6 +4476,7 @@ "name": "edges", "signature": "pub fn edges(&self) -> &[Edge] {", "doc": "Returns the edges.", + "doc_zh": "返回各条边。", "associated": true }, { @@ -3929,6 +4484,7 @@ "name": "add_edge", "signature": "pub fn add_edge(&mut self, from: usize, to: usize, cost: f64) -> Result<(), TopologyError> {", "doc": "Adds a directed edge.\n\nReturns an error when either node index is out of range, when the\ncost is not finite and positive, or when the edge already exists.\nRejecting a duplicate rather than replacing it keeps a topology free\nof silent contradictions about the cost of a link.", + "doc_zh": "添加一条有向边。\n\n任一端点下标越界、代价非有限且为正,或该边已存在时返回错误。拒绝\n重复边而非替换它,使拓扑不会对某条链路的代价产生无声的矛盾。", "associated": true }, { @@ -3936,6 +4492,7 @@ "name": "add_bidirectional_edge", "signature": "pub fn add_bidirectional_edge( &mut self, first: usize, second: usize, cost: f64, ) -> Result<(), TopologyError> {", "doc": "Adds an edge in both directions with the same cost.\n\nIf the second direction fails after the first succeeds, the first is\nrolled back, so the topology is left unchanged when the call reports\nan error.", + "doc_zh": "以相同代价在两个方向添加边。\n\n若第一个方向成功后第二个方向失败,则回滚第一个,故调用报告错误\n时拓扑保持不变。", "associated": true }, { @@ -3943,6 +4500,7 @@ "name": "has_edge", "signature": "pub fn has_edge(&self, from: usize, to: usize) -> bool {", "doc": "Returns `true` when an edge exists from one node to another.", + "doc_zh": "当存在从一节点到另一节点的边时返回 `true`。", "associated": true }, { @@ -3950,6 +4508,7 @@ "name": "cost", "signature": "pub fn cost(&self, from: usize, to: usize) -> Option {", "doc": "Returns the cost of the edge between two nodes.\n\nReturns `None` when there is no such edge.", + "doc_zh": "返回两节点之间边的代价。\n\n不存在该边时返回 `None`。", "associated": true }, { @@ -3957,6 +4516,7 @@ "name": "remove_edge", "signature": "pub fn remove_edge(&mut self, from: usize, to: usize) -> Result<(), TopologyError> {", "doc": "Removes the edge between two nodes.\n\nReturns an error when there is no such edge.", + "doc_zh": "移除两节点之间的边。\n\n不存在该边时返回错误。", "associated": true }, { @@ -3964,6 +4524,7 @@ "name": "neighbours", "signature": "pub fn neighbours(&self, node: usize) -> Vec {", "doc": "Returns the indices of the nodes reachable from a node in one hop.\n\nThe result is in the order the edges were added, so it is\ndeterministic for a given construction sequence.", + "doc_zh": "返回自某节点一跳可达的节点下标。\n\n结果按边被添加的次序排列,故对给定的构建序列是确定的。", "associated": true }, { @@ -3971,6 +4532,7 @@ "name": "out_degree", "signature": "pub fn out_degree(&self, node: usize) -> usize {", "doc": "Returns the number of edges leaving a node.", + "doc_zh": "返回离开某节点的边数。", "associated": true }, { @@ -3978,6 +4540,7 @@ "name": "in_degree", "signature": "pub fn in_degree(&self, node: usize) -> usize {", "doc": "Returns the number of edges arriving at a node.", + "doc_zh": "返回到达某节点的边数。", "associated": true }, { @@ -3985,6 +4548,7 @@ "name": "adjacency", "signature": "pub fn adjacency(&self) -> Vec> {", "doc": "Returns the adjacency of every node.\n\nThe result has one entry per node, in node order, each listing the\nneighbours of that node.", + "doc_zh": "返回每个节点的邻接。\n\n结果每个节点一项,按节点次序排列,各自列出该节点的邻居。", "associated": true }, { @@ -3992,6 +4556,7 @@ "name": "connected_components", "signature": "pub fn connected_components(&self) -> usize {", "doc": "Returns the number of connected components, treating the edges as\nundirected.\n\nThis answers how many separate networks the topology forms, which is\nthe first thing to check about a constellation snapshot: a\nconstellation that has split into pieces can still carry traffic\nwithin each piece but not between them.", + "doc_zh": "把边视为无向时返回连通分量的数量。\n\n它回答该拓扑构成多少个独立网络,这是检查星座快照时要看的第一件\n事:已裂成数块的星座仍可在各块内承载业务,但块与块之间不行。", "associated": true }, { @@ -3999,6 +4564,7 @@ "name": "is_connected", "signature": "pub fn is_connected(&self) -> bool {", "doc": "Returns `true` when every node can reach every other node, treating\nthe edges as undirected.", + "doc_zh": "把边视为无向时,当每个节点都能到达其余每个节点,返回 `true`。", "associated": true }, { @@ -4006,6 +4572,7 @@ "name": "total_cost", "signature": "pub fn total_cost(&self) -> f64 {", "doc": "Returns the total cost of every edge.\n\nThis is a crude measure of the network's extent, useful for comparing\nsnapshots rather than for routing.", + "doc_zh": "返回所有边的代价之和。\n\n这是对网络规模的一个粗略度量,用于比较快照而非用于路由。", "associated": true }, { @@ -4013,6 +4580,7 @@ "name": "TopologySample", "signature": "pub struct TopologySample {", "doc": "One topology and the time from which it applies.", + "doc_zh": "一个拓扑及其开始适用的时间。", "associated": false }, { @@ -4020,6 +4588,7 @@ "name": "new", "signature": "pub const fn new(seconds: f64, topology: Topology) -> Self {", "doc": "Creates a sample.", + "doc_zh": "创建一个样本。", "associated": true }, { @@ -4027,6 +4596,7 @@ "name": "TopologySeries", "signature": "pub struct TopologySeries {", "doc": "A sequence of topologies over time.\n\nThe sequence is a step function. The entry at index `i` applies from its\nown time until the time of the entry at index `i + 1`, and the last entry\napplies for all later times. Querying a time before the first entry\nreturns the first topology, so the series behaves as if it extends\nbackwards as well as forwards.\n\nThe times must be strictly increasing. Two entries at the same time would\nmake the topology at that instant ambiguous.", + "doc_zh": "拓扑随时间变化的序列。\n\n该序列是阶跃函数。下标 `i` 处的项自其自身时间起适用,直到下标\n`i + 1` 处项的时间;最后一项适用于其后所有时间。查询首项之前的时间\n返回第一个拓扑,故该序列的行为像是向前向后都延伸。\n\n各时间必须严格递增。同一时间的两项会使该瞬时的拓扑含糊。", "associated": false }, { @@ -4034,6 +4604,7 @@ "name": "new", "signature": "pub fn new(samples: Vec) -> Result {", "doc": "Creates a series from a list of samples.\n\nReturns an error when the list is empty, when a time is not finite,\nor when the times are not strictly increasing.", + "doc_zh": "由样本列表创建序列。\n\n列表为空、某时间非有限,或各时间非严格递增时返回错误。", "associated": true }, { @@ -4041,6 +4612,7 @@ "name": "from_parts", "signature": "pub fn from_parts(times: Vec, topologies: Vec) -> Result {", "doc": "Creates a series from times and topologies as separate lists.\n\nThe two lists must have the same length.\n\nReturns an error when the lists differ in length or when the series\nitself is not valid.", + "doc_zh": "由分别给出的时间列表与拓扑列表创建序列。\n\n两个列表必须长度相同。\n\n列表长度不同或序列自身无效时返回错误。", "associated": true }, { @@ -4048,6 +4620,7 @@ "name": "samples", "signature": "pub fn samples(&self) -> &[TopologySample] {", "doc": "Returns the samples.", + "doc_zh": "返回各样本。", "associated": true }, { @@ -4055,6 +4628,7 @@ "name": "len", "signature": "pub fn len(&self) -> usize {", "doc": "Returns the number of samples.", + "doc_zh": "返回样本的数量。", "associated": true }, { @@ -4062,6 +4636,7 @@ "name": "start_seconds", "signature": "pub fn start_seconds(&self) -> f64 {", "doc": "Returns the first time in the series.", + "doc_zh": "返回序列中的第一个时间。", "associated": true }, { @@ -4069,6 +4644,7 @@ "name": "end_seconds", "signature": "pub fn end_seconds(&self) -> f64 {", "doc": "Returns the last time in the series.", + "doc_zh": "返回序列中的最后一个时间。", "associated": true }, { @@ -4076,6 +4652,7 @@ "name": "index_at", "signature": "pub fn index_at(&self, seconds: f64) -> Option {", "doc": "Returns the index of the sample that applies at a time.\n\nThe index is the last entry whose time is at or before the query, so\na time before the series returns zero and a time after it returns the\nlast index.\n\nReturns `None` when the time is not finite or the series is empty.", + "doc_zh": "返回某时间处适用的样本下标。\n\n该下标是时间不晚于查询的最后一个项,故序列之前的时间返回零,\n序列之后的时间返回最后的下标。\n\n时间非有限或序列为空时返回 `None`。", "associated": true }, { @@ -4083,6 +4660,7 @@ "name": "topology_at", "signature": "pub fn topology_at(&self, seconds: f64) -> Option<&Topology> {", "doc": "Returns the topology in force at a time.\n\nReturns `None` when the time is not finite or the series is empty.", + "doc_zh": "返回某时间处生效的拓扑。\n\n时间非有限或序列为空时返回 `None`。", "associated": true }, { @@ -4090,6 +4668,7 @@ "name": "interval_of", "signature": "pub fn interval_of(&self, index: usize) -> Option<(f64, f64)> {", "doc": "Returns the interval over which the sample at an index applies.\n\nThe upper bound is the time of the next sample, or positive infinity\nfor the last entry.\n\nReturns `None` when the index is out of range.", + "doc_zh": "返回某下标处样本适用的区间。\n\n上界是下一样本的时间,对最后一项为正无穷。\n\n下标越界时返回 `None`。", "associated": true }, { @@ -4097,6 +4676,7 @@ "name": "change_count", "signature": "pub fn change_count(&self) -> usize {", "doc": "Returns the number of times the topology changes.\n\nThis is the number of samples less one, since each change introduces\na new entry. It measures how dynamic the network is: a series with\nmany changes needs routing that accounts for them.", + "doc_zh": "返回拓扑变化的次数。\n\n它是样本数减一,因为每次变化引入一个新项。它衡量网络的动态\n程度:变化多的序列需要能考虑到这些变化的路由。", "associated": true }, { @@ -4104,6 +4684,7 @@ "name": "mean_interval_seconds", "signature": "pub fn mean_interval_seconds(&self) -> Option {", "doc": "Returns the mean interval between changes, in seconds.\n\nThe last entry has unbounded duration, so it is excluded from the\nmean and the result covers the changes that have a measured interval.\n\nReturns `None` when there is only one sample.", + "doc_zh": "返回变化之间的平均间隔,单位为秒。\n\n最后一项的持续时间无界,故它被排除在均值之外,结果涵盖有可测\n间隔的那些变化。\n\n只有一个样本时返回 `None`。", "associated": true }, { @@ -4111,6 +4692,7 @@ "name": "edge_changes", "signature": "pub fn edge_changes(&self) -> usize {", "doc": "Returns the number of edge changes across the series.\n\nEach topology is compared with the one before it, counting the edges\nthat appear and the edges that disappear. A topology that differs by\none link contributes two, since one link changed.\n\nReturns zero for a series of one sample.", + "doc_zh": "返回整个序列中边变化的次数。\n\n每个拓扑与其前一个比较,统计出现与消失的边。相差一条链路的拓扑\n贡献二,因为有一条链路发生了变化。\n\n单样本序列返回零。", "associated": true }, { @@ -4118,6 +4700,7 @@ "name": "node_count", "signature": "pub fn node_count(&self) -> usize {", "doc": "Returns the number of nodes in the series.\n\nEvery entry is expected to describe the same set of nodes, so the\nvalue is taken from the first entry. A caller that changes the node\ncount between entries is comparing two different networks and should\nbuild two series instead.", + "doc_zh": "返回序列中的节点数。\n\n每个项都应描述同一组节点,故该值取自第一项。在各项之间改变节点\n数的调用方是在比较两个不同的网络,应改为构建两个序列。", "associated": true } ] @@ -4125,13 +4708,15 @@ { "name": "apsis-isl", "layer": "network", - "summary": "Inter-satellite link geometry and visibility. Two satellites can form a link when the straight line between them does not pass through the Earth. This crate decides that condition, gives the geometry a link budget needs, and reports the range and the angles at which each satellite must point. The blocking test is whether the line segment between the two positions has any point inside the body. For a spherical body that is a perpendicular distance test, which is exact and cheap, so it is the one used here. An optical link adds a second condition: the Sun must not be within the field of view of either terminal, because the background would swamp the signal. That is a separate test, offered as [`sun_blocks_link`], which takes the direction to the Sun. Lengths are in kilometers and angles in radians. The positions are in a frame in which the Earth is a sphere of the given radius, which for link geometry is the same frame the orbit layer produces. ``` use apsis_isl::{LinkGeometry, link_geometry, mutual_visibility}; use apsis_math::Vec3; // Two satellites on the same side of the Earth see each other. let first = Vec3::new(6_928.137, 0.0, 0.0); let second = Vec3::new(6_868.137, 2_500.0, 0.0); assert_eq!(mutual_visibility(first, second, 6_378.137), Some(true)); // Two satellites on opposite sides do not. let opposite = Vec3::new(-6_928.137, 0.0, 0.0); assert_eq!(mutual_visibility(first, opposite, 6_378.137), Some(false)); ```", + "summary": "Inter-satellite link geometry and visibility.\n\nTwo satellites can form a link when the straight line between them does\nnot pass through the Earth. This crate decides that condition, gives the\ngeometry a link budget needs, and reports the range and the angles at\nwhich each satellite must point.\n\nThe blocking test is whether the line segment between the two positions\nhas any point inside the body. For a spherical body that is a\nperpendicular distance test, which is exact and cheap, so it is the one\nused here.\n\nAn optical link adds a second condition: the Sun must not be within the\nfield of view of either terminal, because the background would swamp the\nsignal. That is a separate test, offered as [`sun_blocks_link`], which\ntakes the direction to the Sun.\n\nLengths are in kilometers and angles in radians. The positions are in a\nframe in which the Earth is a sphere of the given radius, which for link\ngeometry is the same frame the orbit layer produces.\n\n```\nuse apsis_isl::{LinkGeometry, link_geometry, mutual_visibility};\nuse apsis_math::Vec3;\n\n// Two satellites on the same side of the Earth see each other.\nlet first = Vec3::new(6_928.137, 0.0, 0.0);\nlet second = Vec3::new(6_868.137, 2_500.0, 0.0);\nassert_eq!(mutual_visibility(first, second, 6_378.137), Some(true));\n\n// Two satellites on opposite sides do not.\nlet opposite = Vec3::new(-6_928.137, 0.0, 0.0);\nassert_eq!(mutual_visibility(first, opposite, 6_378.137), Some(false));\n```", + "summary_zh": "星间链路几何与可见性。\n\n当两颗卫星之间的直线不穿过地球时,它们可以形成一条链路。本 crate 判定该条件、\n给出链路预算所需的几何,并报告距离以及每颗卫星必须指向的角度。\n\n遮挡判据是两点之间的线段是否有点位于天体内部。对球形天体而言这是一个垂距判定,\n它精确且廉价,故此处采用它。\n\n光链路还加一个条件:太阳不得位于任一终端的视场内,因为背景会淹没信号。那是一个\n单独的判定,作为 [`sun_blocks_link`] 提供,它接收太阳方向。\n\n长度以千米计,角度以弧度计。各位置所在的坐标系中地球是给定半径的球,对链路\n几何而言它与 orbit 层所产生的是同一坐标系。", "items": [ { "kind": "fn", "name": "visibility_matrix", "signature": "pub fn visibility_matrix(positions: &[Vec3], body_radius: f64) -> Vec {", "doc": "Returns the visibility of every ordered pair of positions, in order.\n\nThe result is row major: the entry at `row * positions.len() + column` is\nthe visibility from `positions[row]` to `positions[column]`. A satellite is\nalways visible to itself, which is the caller's cue to skip the diagonal.\n\nA pair whose test is undefined, because a position is not finite or the\nbody radius is not positive, yields `false`.", + "doc_zh": "按顺序返回各位置有序对的可见性。\n\n结果按行主序:位于 `row * positions.len() + column` 的元素是\n`positions[row]` 对 `positions[column]` 的可见性。卫星对自身总可见,\n这提示调用方跳过对角线。\n\n测试未定义的点对(位置非有限或天体半径非正)给出 `false`。", "associated": false }, { @@ -4139,6 +4724,7 @@ "name": "visibility_matrix_parallel", "signature": "pub fn visibility_matrix_parallel(positions: &[Vec3], body_radius: f64) -> Vec {", "doc": "Returns the visibility of every ordered pair of positions, across several\nthreads.\n\nThe result matches [`visibility_matrix`] element for element and in the\nsame order. The pairs are independent, so splitting the flat index space\nacross threads needs no coordination and produces the same matrix however\nthe work is divided.\n\nThe gain from threading is modest here and shrinks as the matrix shrinks.\nA single visibility test is a few nanoseconds, so the cost of starting the\nthreads is only repaid by a large matrix: on a sixteen core machine a few\nhundred satellites already gain, while a few dozen lose. The sequential\nform is the better choice for a small shell, and this one exists for the\nlarge ones where the quadratic sweep dominates.", + "doc_zh": "跨多个线程返回各位置有序对的可见性。\n\n结果与 [`visibility_matrix`] 逐元素、逐次序相同。各对相互独立,故把扁平的\n下标空间跨线程划分无需协调,且无论工作如何划分都产生相同的矩阵。\n\n此处的线程收益不大,并随矩阵变小而缩小。单次可见性判定只有几纳秒,故启动\n线程的代价只有大矩阵才回收:在十六核机器上,几百颗卫星已有收益,而几十颗则\n亏损。顺序形式是小壳层的更好选择,而这个存在是为了二次扫描占主导的大壳层。", "associated": false }, { @@ -4146,6 +4732,7 @@ "name": "closest_approach_to_centre", "signature": "pub fn closest_approach_to_centre(first: Vec3, second: Vec3) -> Option {", "doc": "Returns the closest distance from the origin to the line segment between\ntwo points.\n\nThis is the perpendicular distance when the closest point lies between\nthe two ends, and the distance to the nearer end otherwise. For an\ninter-satellite link the first case is the one that matters, because the\nblocking body lies between the terminals.\n\nReturns `None` when either point is not finite.", + "doc_zh": "返回原点到两点连线的最短距离。\n\n最近点落在两端之间时它是垂距,否则是到较近一端的距离。对星间链路\n而言前一种情形是要紧的,因为遮挡天体位于两端之间。\n\n任一点非有限时返回 `None`。", "associated": false }, { @@ -4153,6 +4740,7 @@ "name": "mutual_visibility", "signature": "pub fn mutual_visibility(first: Vec3, second: Vec3, body_radius: f64) -> Option {", "doc": "Returns `true` when the two satellites can see each other past the body.\n\nThe test is whether the line segment between the two positions stays\noutside a sphere of the given radius. A link that grazes the surface\nexactly is treated as clear, because the limit is the geometric horizon.\n\nReturns `None` when either position is not finite or the body radius is\nnot positive.", + "doc_zh": "当两颗卫星能越过该天体互相可见时返回 `true`。\n\n判据是两位置之间的线段是否保持在给定半径的球体之外。恰好擦过表面\n的链路被视为通畅,因为该极限即几何地平线。\n\n任一位置非有限或天体半径非正时返回 `None`。", "associated": false }, { @@ -4160,6 +4748,7 @@ "name": "link_geometry", "signature": "pub fn link_geometry(first: Vec3, second: Vec3, body_radius: f64) -> Option {", "doc": "Returns the full geometry of a link between two satellites.\n\nThe directions are unit vectors from each satellite toward the other. The\nclosest approach to the centre is included so that the caller can judge\nhow much margin the link has.\n\nReturns `None` when either position is not finite, when the two coincide,\nor when the body radius is not usable. A cell that reports its own\nposition for both ends has no link, so that case is rejected rather than\nreturned with a zero range.", + "doc_zh": "返回两颗卫星之间链路的完整几何。\n\n各方向是自每颗卫星指向对方的单位矢量。其中含对中心的最接近距离,使\n调用方能判断该链路有多少余量。\n\n任一位置非有限、两者重合或天体半径不可用时返回 `None`。两端回报\n自身位置的单元没有链路,故该情形被拒绝而非以零距离返回。", "associated": false }, { @@ -4167,6 +4756,7 @@ "name": "sun_blocks_link", "signature": "pub fn sun_blocks_link( link: &LinkGeometry, sun_direction: Vec3, field_of_view: f64, ) -> Option {", "doc": "Returns `true` when the Sun is within the field of view of an optical\nlink.\n\nThe test is the angle between the link direction and the direction to the\nSun. If that angle is within the field of view at either terminal, the\nSun is in the beam and the link cannot be used.\n\nThe field of view is given as a half angle in radians, which is the\nacceptance cone of the telescope. A typical optical terminal has a half\nangle of a few microradians.\n\nThe direction to the Sun is a unit vector in the same frame as the\npositions. This crate does not compute it, because that needs an\nephemeris of the Sun, which the orbit layer does not currently provide; a\ncaller with such an ephemeris supplies the direction.\n\nReturns `None` when the directions or the field of view are not usable.", + "doc_zh": "当太阳位于某光链路的视场内时返回 `true`。\n\n判据是链路方向与太阳方向之间的夹角。若该夹角落在任一终端的视场内,则太阳进入\n波束,该链路不可用。\n\n视场以半角(弧度)给出,即望远镜的接受锥角。典型光终端的半角为若干微弧度。\n\n太阳方向是与位置同系的单位矢量。本 crate 不计算它,因为这需要太阳星历,而\norbit 层当前不提供;拥有该星历的调用方自行提供该方向。\n\n方向或视场不可用时返回 `None`。", "associated": false }, { @@ -4174,6 +4764,7 @@ "name": "LinkGeometry", "signature": "pub struct LinkGeometry {", "doc": "The geometry of a link between two satellites.\n\nThe range is the straight line distance, which a link budget uses for the\nfree space path loss. The two pointing vectors are the directions each\nterminal must aim, expressed in the frame the positions were given in.", + "doc_zh": "两颗卫星之间链路的几何。\n\n距离是直线距离,链路预算以之计算自由空间路径损耗。两个指向矢量是各\n终端须瞄准的方向,表达在位置所给的坐标系中。", "associated": false }, { @@ -4181,6 +4772,7 @@ "name": "surface_clearance", "signature": "pub fn surface_clearance(&self, body_radius: f64) -> f64 {", "doc": "Returns the grazing margin of the link, in kilometers.\n\nThis is how far the line between the two satellites clears the\nsurface, measured at the closest point. A negative value means the\nlink is blocked.", + "doc_zh": "返回该链路的擦地余量,单位为千米。\n\n它是两点连线在最接近处越过地表的量度。负值表示该链路被遮挡。", "associated": true }, { @@ -4188,6 +4780,7 @@ "name": "is_clear", "signature": "pub fn is_clear(&self, body_radius: f64) -> bool {", "doc": "Returns `true` when the link clears the body.", + "doc_zh": "当该链路越过天体时返回 `true`。", "associated": true }, { @@ -4195,6 +4788,7 @@ "name": "LinkState", "signature": "pub enum LinkState {", "doc": "The state of a link at one instant.\n\nA link is either carrying traffic or not. The reasons a link can be\nunavailable are kept separate, because routing across a constellation has\nto distinguish a link that is geometrically blocked from one that is\nmerely not established yet.", + "doc_zh": "某一瞬时链路的状态。\n\n链路或承载业务或否。链路不可用的各种原因分开保留,因为在星座上路由\n须区分被几何遮挡的链路与尚未建立的链路。", "associated": false }, { @@ -4202,6 +4796,7 @@ "name": "is_usable", "signature": "pub const fn is_usable(self) -> bool {", "doc": "Returns `true` when the link may carry traffic.", + "doc_zh": "当该链路可承载业务时返回 `true`。", "associated": true }, { @@ -4209,6 +4804,7 @@ "name": "name", "signature": "pub const fn name(self) -> &'static str {", "doc": "Returns a short name for the state.", + "doc_zh": "返回该状态的简称。", "associated": true } ] @@ -4216,13 +4812,15 @@ { "name": "apsis-routing", "layer": "network", - "summary": "Routing over time-varying satellite topologies. The topology of a satellite network changes as the satellites move, so a route is only valid over an interval. This crate computes least cost routes through a [`Topology`] and reports the route together with the time it remains valid. Two forms of routing are provided: - [`shortest_path`] finds the least cost route through the topology in force at one instant. - [`route_over_series`] finds a route that is valid for a whole interval, by taking the intersection of the topologies across that interval. The second form is the one a store and forward network needs. A route through a topology that changes halfway along it may not exist in the new topology, so a route is only useful if its every link survives the interval it is planned for. The algorithm is Dijkstra's, with a binary heap. Costs must be positive, which the topology layer enforces, so the algorithm is exact: the first time a node is removed from the heap its distance is final. Source: Dijkstra, E. W., *A note on two problems in connexion with graphs*, Numerische Mathematik 1, 1959, and the binary heap formulation as given in Cormen et al., *Introduction to Algorithms*, 3rd edition, MIT Press, 2009, chapter 24. ``` use apsis_routing::shortest_path; use apsis_topology::Topology; // A three node line: 0 - 1 - 2, with the direct link 0 - 2 absent. let mut topology = Topology::new(3); topology.add_edge(0, 1, 1.0).expect(\"the nodes exist\"); topology.add_edge(1, 2, 1.0).expect(\"the nodes exist\"); let route = shortest_path(&topology, 0, 2).expect(\"a route exists\"); assert_eq!(route.nodes, vec![0, 1, 2]); assert!((route.cost - 2.0).abs() < 1e-12); ```", + "summary": "Routing over time-varying satellite topologies.\n\nThe topology of a satellite network changes as the satellites move, so a\nroute is only valid over an interval. This crate computes least cost\nroutes through a [`Topology`] and reports the\nroute together with the time it remains valid.\n\nTwo forms of routing are provided:\n\n- [`shortest_path`] finds the least cost route through the topology in\n force at one instant.\n- [`route_over_series`] finds a route that is valid for a whole interval,\n by taking the intersection of the topologies across that interval.\n\nThe second form is the one a store and forward network needs. A route\nthrough a topology that changes halfway along it may not exist in the new\ntopology, so a route is only useful if its every link survives the\ninterval it is planned for.\n\nThe algorithm is Dijkstra's, with a binary heap. Costs must be positive,\nwhich the topology layer enforces, so the algorithm is exact: the first\ntime a node is removed from the heap its distance is final.\n\nSource: Dijkstra, E. W., *A note on two problems in connexion with\ngraphs*, Numerische Mathematik 1, 1959, and the binary heap formulation\nas given in Cormen et al., *Introduction to Algorithms*, 3rd edition,\nMIT Press, 2009, chapter 24.\n\n```\nuse apsis_routing::shortest_path;\nuse apsis_topology::Topology;\n\n// A three node line: 0 - 1 - 2, with the direct link 0 - 2 absent.\nlet mut topology = Topology::new(3);\ntopology.add_edge(0, 1, 1.0).expect(\"the nodes exist\");\ntopology.add_edge(1, 2, 1.0).expect(\"the nodes exist\");\n\nlet route = shortest_path(&topology, 0, 2).expect(\"a route exists\");\nassert_eq!(route.nodes, vec![0, 1, 2]);\nassert!((route.cost - 2.0).abs() < 1e-12);\n```", + "summary_zh": "时变卫星拓扑上的路由。\n\n卫星网络的拓扑随卫星移动而改变,故一条路由只在一个区间上有效。本 crate 计算\n穿过 [`Topology`] 的最小代价路由,并把该路由连同其保持有效的时间一起报告。\n\n提供两种路由形式:\n\n- [`shortest_path`] 找出在某一瞬时生效的拓扑中的最小代价路由。\n- [`route_over_series`] 找出对整个区间都有效的路由,方法是取该区间内各拓扑的\n 交集。\n\n第二种形式是存储转发网络所需者。穿过一条中途改变的拓扑的路由,在新拓扑中可能\n不存在,故一条路由只有在其每条链路都存活于它所规划的那个区间时才有用。\n\n该算法是 Dijkstra 的,配以二叉堆。代价必须为正,这由拓扑层强制,故该算法精确:\n某节点首次从堆中取出时其距离即为最终。\n\n出处:Dijkstra, E. W.,*A note on two problems in connexion with graphs*,\nNumerische Mathematik 1,1959,以及二叉堆的表述,见 Cormen 等,\n*Introduction to Algorithms*,第 3 版,MIT Press,2009,第 24 章。", "items": [ { "kind": "struct", "name": "PathTree", "signature": "pub struct PathTree {", "doc": "The result of a single source shortest path computation.\n\nThe distances are to every node reachable from the source, and the\npredecessor map records the edge by which each node was reached. Both are\nwhat a routing table needs, and a single computation fills a whole row of\nit.", + "doc_zh": "单源最短路计算的结果。\n\n各距离是自源到每个可达节点的距离,而前驱图记录每个节点是经哪条边到达的。二者\n都是路由表所需的,且一次计算即填满它的一整行。", "associated": false }, { @@ -4230,6 +4828,7 @@ "name": "distance", "signature": "pub fn distance(&self, node: usize) -> Option {", "doc": "Returns the distance to a node.\n\nReturns `None` when the node index is out of range or the node is\nunreachable.", + "doc_zh": "返回到某节点的距离。\n\n当节点下标越界或节点不可达时返回 `None`。", "associated": true }, { @@ -4237,6 +4836,7 @@ "name": "is_reachable", "signature": "pub fn is_reachable(&self, node: usize) -> bool {", "doc": "Returns `true` when the node is reachable from the source.", + "doc_zh": "当该节点自源点可达时返回 `true`。", "associated": true }, { @@ -4244,20 +4844,23 @@ "name": "reachable_count", "signature": "pub fn reachable_count(&self) -> usize {", "doc": "Returns the number of reachable nodes, including the source itself.", + "doc_zh": "返回可达节点的数量,含源点自身。", "associated": true }, { "kind": "fn", "name": "route_to", "signature": "pub fn route_to(&self, destination: usize, topology: &Topology) -> Result {", - "doc": "Reconstructs the route to a node.\n\nReturns an error when the node is out of range or unreachable.", + "doc": "Reconstructs the route to a node.", + "doc_zh": "重建到某节点的路由。\n\nReturns an error when the node is out of range or unreachable.", "associated": true }, { "kind": "fn", "name": "shortest_paths_from", "signature": "pub fn shortest_paths_from(topology: &Topology, source: usize) -> Result {", - "doc": "Computes the shortest paths from one source to every reachable node.\n\nReturns an error when the source node is out of range.\n\nSource: Dijkstra, E. W., *A note on two problems in connexion with\ngraphs*, Numerische Mathematik 1, 1959.", + "doc": "Computes the shortest paths from one source to every reachable node.\n\nReturns an error when the source node is out of range.", + "doc_zh": "计算自一个源点到每个可达节点的最短距离。\n\n源节点超出取值范围时返回错误。\n\nSource: Dijkstra, E. W., *A note on two problems in connexion with\ngraphs*, Numerische Mathematik 1, 1959.", "associated": false }, { @@ -4265,6 +4868,7 @@ "name": "shortest_path", "signature": "pub fn shortest_path( topology: &Topology, source: usize, destination: usize, ) -> Result {", "doc": "Returns the shortest route between two nodes.\n\nReturns an error when either node is out of range, when they are the\nsame node, or when no route exists.", + "doc_zh": "返回两节点之间的最短路由。\n\n任一节点越界、两者相同,或不存在路由时返回错误。", "associated": false }, { @@ -4272,6 +4876,7 @@ "name": "route_over_series", "signature": "pub fn route_over_series( series: &TopologySeries, source: usize, destination: usize, start_seconds: f64, end_seconds: f64, ) -> Result {", "doc": "Returns the shortest route valid across an interval of a topology series.\n\nThe route is computed over the links that are present throughout the\ninterval, so it remains valid for the whole of it. The interval is taken\nfrom the start of the sample in force at `start_seconds` to the start of\nthe sample in force at `end_seconds`, and the links of every sample in\nthat span are intersected.\n\nThe returned leg records the interval the route is valid for, which is\nbounded by the next topology change after `end_seconds`.\n\nReturns an error when either node is out of range or the same, when the\ninterval is not ordered, or when no route survives the whole interval.\n\nSource: the store and forward routing model over a time-varying graph is\nthe one described in Fraire, J. A. et al., *Routing in Delay-Tolerant\nNetworks Comprising Heterogeneous Populations of Deterministic Link\nDynamics*, IEEE Transactions on Mobile Computing, 2020.", + "doc_zh": "返回在拓扑序列某区间上有效的最短路由。\n\n该路由在贯穿整个区间都存在的链路上计算,故它在整个区间上保持有效。\n该区间取自 `start_seconds` 处生效样本的起点,到 `end_seconds` 处生效\n样本的起点,并对该跨度内每个样本的链路求交。\n\n所返回的路段记录该路由有效的区间,它以 `end_seconds` 之后的下一处\n拓扑变化为界。\n\n任一节点越界或两者相同、区间次序不对,或没有路由能存活整个区间时\n返回错误。\n\n出处:时变图上的存储转发路由模型见 Fraire, J. A. 等,*Routing in\nDelay-Tolerant Networks Comprising Heterogeneous Populations of\nDeterministic Link Dynamics*,IEEE Transactions on Mobile Computing,\n2020。", "associated": false }, { @@ -4279,6 +4884,7 @@ "name": "RoutingErrorKind", "signature": "pub enum RoutingErrorKind {", "doc": "The category of a routing error.", + "doc_zh": "路由错误的类别。", "associated": false }, { @@ -4286,6 +4892,7 @@ "name": "RoutingError", "signature": "pub struct RoutingError {", "doc": "A failure while routing.", + "doc_zh": "路由时发生的失败。", "associated": false }, { @@ -4293,6 +4900,7 @@ "name": "new", "signature": "pub fn new(kind: RoutingErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -4300,6 +4908,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the values that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -4307,6 +4916,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> RoutingErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -4314,6 +4924,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -4321,6 +4932,7 @@ "name": "RouteStep", "signature": "pub struct RouteStep {", "doc": "One hop of a route.\n\nA step records the link traversed and the cost of that link, so a caller\ncan attribute a route's total cost to its parts.", + "doc_zh": "路由的一跳。\n\n一个步骤记录所穿越的链路及其代价,使调用方能把路由的总代价归因于\n各部分。", "associated": false }, { @@ -4328,6 +4940,7 @@ "name": "new", "signature": "pub const fn new(from: usize, to: usize, cost: f64) -> Self {", "doc": "Creates a step.", + "doc_zh": "创建一个步骤。", "associated": true }, { @@ -4335,6 +4948,7 @@ "name": "Route", "signature": "pub struct Route {", "doc": "A route between two nodes.\n\nThe node list includes both endpoints, so a route of one hop has two\nnodes and one step, and a route from a node to itself has one node and no\nsteps.", + "doc_zh": "两个节点之间的路由。\n\n节点列表包含两个端点,故一跳的路由有两个节点和一个步骤,自某节点到\n其自身的路由有一个节点且没有步骤。", "associated": false }, { @@ -4342,6 +4956,7 @@ "name": "new", "signature": "pub fn new(nodes: Vec, steps: Vec) -> Self {", "doc": "Creates a route from its nodes and steps.\n\nThe cost is taken as the sum of the step costs, so a caller cannot\nmake the total disagree with the parts.", + "doc_zh": "由其节点与步骤创建路由。\n\n代价取各步骤代价之和,故调用方无法使总计与各部分不一致。", "associated": true }, { @@ -4349,6 +4964,7 @@ "name": "hop_count", "signature": "pub fn hop_count(&self) -> usize {", "doc": "Returns the number of hops.", + "doc_zh": "返回跳数。", "associated": true }, { @@ -4356,6 +4972,7 @@ "name": "source", "signature": "pub fn source(&self) -> Option {", "doc": "Returns the source node.\n\nReturns `None` when the route has no nodes.", + "doc_zh": "返回源节点。\n\n路由没有节点时返回 `None`。", "associated": true }, { @@ -4363,6 +4980,7 @@ "name": "destination", "signature": "pub fn destination(&self) -> Option {", "doc": "Returns the destination node.\n\nReturns `None` when the route has no nodes.", + "doc_zh": "返回目的地节点。\n\n路由没有节点时返回 `None`。", "associated": true }, { @@ -4370,6 +4988,7 @@ "name": "reversed", "signature": "pub fn reversed(&self) -> Self {", "doc": "Returns the route traversed in the opposite direction.\n\nThe costs are unchanged, because a route reversed traverses the same\nlinks. This is only correct when every link is present in both\ndirections with the same cost, which a caller must ensure.", + "doc_zh": "返回按相反方向穿越的路由。\n\n各代价不变,因为反转后的路由穿越同样的链路。仅当每条链路都以\n相同代价双向存在时才正确,这一点须由调用方保证。", "associated": true }, { @@ -4377,6 +4996,7 @@ "name": "passes_through", "signature": "pub fn passes_through(&self, node: usize) -> bool {", "doc": "Returns `true` when the route passes through a node.", + "doc_zh": "当该路由经过某节点时返回 `true`。", "associated": true }, { @@ -4384,6 +5004,7 @@ "name": "position_of", "signature": "pub fn position_of(&self, node: usize) -> Option {", "doc": "Returns the position of a node in the route, if it is on it.", + "doc_zh": "返回某节点在路由中的位置(若在该路由上)。", "associated": true }, { @@ -4391,6 +5012,7 @@ "name": "RouteLeg", "signature": "pub struct RouteLeg {", "doc": "A route with the interval over which it is valid.", + "doc_zh": "一条路由及其有效的时段。", "associated": false }, { @@ -4398,6 +5020,7 @@ "name": "new", "signature": "pub const fn new(route: Route, start_seconds: f64, end_seconds: f64) -> Self {", "doc": "Creates a route leg.", + "doc_zh": "创建一段路由。", "associated": true }, { @@ -4405,6 +5028,7 @@ "name": "duration_seconds", "signature": "pub fn duration_seconds(&self) -> f64 {", "doc": "Returns the duration for which the route holds, in seconds.\n\nReturns infinity when the route is valid for all later times.", + "doc_zh": "返回该路由保持的时长,单位为秒。\n\n该路由对其后所有时间都有效时返回无穷。", "associated": true } ] @@ -4412,13 +5036,15 @@ { "name": "apsis-handover", "layer": "network", - "summary": "Handover decision logic for mobile terminals. A terminal that can be served by more than one satellite must choose one, and it must change its choice when the current server becomes unsuitable. This crate makes that decision. It does not compute access windows, which belong to the access layer; it takes a list of candidate satellites with their quality and decides whether to hand over. Two effects oppose each other and both must be in the policy: - **Handing over too late** loses the link. If the current server drops below the terminal's minimum, the link fails before the switch happens. - **Handing over too often** wastes capacity and time. Each handover costs signalling and interrupts the data flow, so a terminal that switches on every small fluctuation performs poorly even though each individual choice looked better. The policy therefore has three parts: a threshold at which the current server is considered unusable, a margin by which a candidate must beat the current server before it is worth switching to, and a minimum time between handovers. The margin is what suppresses oscillation, and it is the hysteresis of the decision. Source: the threshold, margin, and dwell time structure is the one used for handover in cellular and satellite networks, as described for example in ITU-R Recommendation M.1184 on the technical and operational characteristics of mobile satellite systems, and in the general treatment of handover hysteresis in mobile radio. ``` use apsis_handover::{Candidate, HandoverPolicy, HandoverDecision, decide}; let policy = HandoverPolicy { minimum_quality: 0.2, handover_margin: 0.1, minimum_dwell_seconds: 5.0, }; // The current server is fine and the alternative is only slightly // better, so nothing happens. let candidates = [ Candidate::new(1, 0.8, 100.0), Candidate::new(2, 0.85, 90.0), ]; let decision = decide(&policy, Some(1), &candidates).expect(\"valid input\"); assert_eq!(decision, HandoverDecision::Stay); ```", + "summary": "Handover decision logic for mobile terminals.\n\nA terminal that can be served by more than one satellite must choose one,\nand it must change its choice when the current server becomes unsuitable.\nThis crate makes that decision. It does not compute access windows, which\nbelong to the access layer; it takes a list of candidate satellites with\ntheir quality and decides whether to hand over.\n\nTwo effects oppose each other and both must be in the policy:\n\n- **Handing over too late** loses the link. If the current server drops\n below the terminal's minimum, the link fails before the switch happens.\n- **Handing over too often** wastes capacity and time. Each handover costs\n signalling and interrupts the data flow, so a terminal that switches on\n every small fluctuation performs poorly even though each individual\n choice looked better.\n\nThe policy therefore has three parts: a threshold at which the current\nserver is considered unusable, a margin by which a candidate must beat\nthe current server before it is worth switching to, and a minimum time\nbetween handovers. The margin is what suppresses oscillation, and it is\nthe hysteresis of the decision.\n\nSource: the threshold, margin, and dwell time structure is the one used\nfor handover in cellular and satellite networks, as described for example\nin ITU-R Recommendation M.1184 on the technical and operational\ncharacteristics of mobile satellite systems, and in the general treatment\nof handover hysteresis in mobile radio.\n\n```\nuse apsis_handover::{Candidate, HandoverPolicy, HandoverDecision, decide};\n\nlet policy = HandoverPolicy {\n minimum_quality: 0.2,\n handover_margin: 0.1,\n minimum_dwell_seconds: 5.0,\n};\n\n// The current server is fine and the alternative is only slightly\n// better, so nothing happens.\nlet candidates = [\n Candidate::new(1, 0.8, 100.0),\n Candidate::new(2, 0.85, 90.0),\n];\nlet decision = decide(&policy, Some(1), &candidates).expect(\"valid input\");\nassert_eq!(decision, HandoverDecision::Stay);\n```", + "summary_zh": "移动终端的切换决策逻辑。\n\n能被不止一颗卫星服务的终端必须选择其一,并在当前服务星变得不合适时改变其\n选择。本 crate 作出该决策。它不计算可见性窗口,那属于可见性层;它接收一份带有\n各自质量的候选卫星列表,并决定是否切换。\n\n有两种效果相互对抗,二者都必须纳入策略:\n\n- **切换太迟**会掉链。若当前服务星跌到终端的最低值以下,链路在切换发生之前即\n 失效。\n- **切换太频繁**浪费容量与时间。每次切换都要付出信令代价并中断数据流,故在每个\n 小起伏上都切换的终端表现很差,即便每个单独的选择看起来更好。\n\n故该策略有三部分:当前服务星被视为不可用的阈值、候选在值得切换之前必须超出\n当前服务星的余量,以及两次切换之间的最短时间。该余量正是抑制振荡者,也是该\n决策的滞回。\n\n出处:阈值、余量与驻留时间的结构即蜂窝网与卫星网中切换所用者,例如见 ITU-R\n建议书 M.1184 关于移动卫星系统技术特性的内容,以及移动无线电中切换滞回的一般\n论述。", "items": [ { "kind": "struct", "name": "Candidate", "signature": "pub struct Candidate {", "doc": "A satellite that could serve the terminal.", + "doc_zh": "一颗可能服务终端的卫星。", "associated": false }, { @@ -4426,6 +5052,7 @@ "name": "new", "signature": "pub const fn new(satellite: usize, quality: f64, remaining_seconds: f64) -> Self {", "doc": "Creates a candidate.", + "doc_zh": "创建一个候选。", "associated": true }, { @@ -4433,6 +5060,7 @@ "name": "is_valid", "signature": "pub fn is_valid(&self) -> bool {", "doc": "Returns `true` when the candidate is usable.", + "doc_zh": "当该候选可用时返回 `true`。", "associated": true }, { @@ -4440,6 +5068,7 @@ "name": "HandoverDecision", "signature": "pub enum HandoverDecision {", "doc": "What the terminal should do at one instant.", + "doc_zh": "终端在某一瞬时应做的动作。", "associated": false }, { @@ -4447,6 +5076,7 @@ "name": "is_change", "signature": "pub const fn is_change(self) -> bool {", "doc": "Returns `true` when the decision changes the serving satellite.", + "doc_zh": "当该决策改变服务卫星时返回 `true`。", "associated": true }, { @@ -4454,6 +5084,7 @@ "name": "resulting_satellite", "signature": "pub const fn resulting_satellite(self) -> Option {", "doc": "Returns the satellite the terminal serves after the decision.\n\nReturns `None` for [`HandoverDecision::Drop`] and for\n[`HandoverDecision::Stay`] with no current server.", + "doc_zh": "返回决策之后终端所服务的卫星。\n\n对 [`HandoverDecision::Drop`] 以及没有当前服务星的\n[`HandoverDecision::Stay`] 返回 `None`。", "associated": true }, { @@ -4461,6 +5092,7 @@ "name": "name", "signature": "pub const fn name(self) -> &'static str {", "doc": "Returns a short name for the decision.", + "doc_zh": "返回该决策的简称。", "associated": true }, { @@ -4468,6 +5100,7 @@ "name": "decide", "signature": "pub fn decide( policy: &HandoverPolicy, current: Option, candidates: &[Candidate], ) -> Result {", "doc": "Returns the decision implied by the policy, without a dwell time.\n\nThis is the form to use when the terminal makes an independent decision\nat each instant and the caller tracks the history itself. Use\n[`decide_with_history`] when the dwell time must be enforced.\n\nThe rules, in the order they are applied:\n\n1. With no current server, acquire the best candidate, or drop when none\n meets the minimum quality.\n2. When the current server is not among the candidates, it has become\n unavailable, so switch to the best candidate or drop when none meets\n the minimum quality.\n3. When the current server is below the minimum quality, abandon it for\n the best candidate, or drop when none meets the minimum quality.\n4. Otherwise stay, unless a candidate exceeds the current server's\n quality by more than the margin.\n\nReturns an error when the candidates or the policy are not usable.", + "doc_zh": "返回策略在不设驻留时间时所蕴含的决策。\n\n这是终端在每一瞬时独立决策、而调用方自行跟踪历史时所用的形式。当必须强制\n驻留时间时,用 [`decide_with_history`]。\n\n各条规则,按其施加次序:\n\n1. 没有当前服务星时,获取最优候选;若没有一个满足最低质量,则放弃。\n2. 当前服务星不在候选中时,它已不可用,故切换到最优候选;若没有一个满足\n 最低质量,则放弃。\n3. 当前服务星低于最低质量时,放弃它而取最优候选;若没有一个满足最低质量,\n 则放弃。\n4. 否则保持,除非某候选超出当前服务星的质量多于余量。\n\n候选或策略不可用时返回错误。", "associated": false }, { @@ -4475,6 +5108,7 @@ "name": "decide_with_history", "signature": "pub fn decide_with_history( policy: &HandoverPolicy, current: Option, last_handover_seconds: Option, candidates: &[Candidate], seconds: f64, ) -> Result {", "doc": "Returns the decision implied by the policy, enforcing the dwell time.\n\nThis adds one rule to those of [`decide`]. When the time since the last\nhandover is less than the dwell time, a handover that would otherwise\nhappen for a quality gain is refused and the terminal stays. A handover\nthat is forced, because the current server has become unavailable or has\nfallen below the minimum quality, is allowed regardless: refusing it\nwould drop the link, which is worse than the churn the dwell time\nprevents.\n\nReturns an error when the candidates or the policy are not usable.", + "doc_zh": "返回策略在强制驻留时间时所蕴含的决策。\n\n它在 [`decide`] 的各条规则之上再加一条。当距上次切换的时间少于驻留时间时,\n本会因质量增益而发生的切换被拒绝,终端保持。被强制的切换——因为当前服务星\n已不可用或已跌至最低质量以下——无论如何都允许:拒绝它会掉链,这比驻留时间\n所要防止的频繁切换更糟。\n\n候选或策略不可用时返回错误。", "associated": false }, { @@ -4482,6 +5116,7 @@ "name": "HandoverErrorKind", "signature": "pub enum HandoverErrorKind {", "doc": "The category of a handover error.", + "doc_zh": "切换错误的类别。", "associated": false }, { @@ -4489,6 +5124,7 @@ "name": "HandoverError", "signature": "pub struct HandoverError {", "doc": "A failure while making a handover decision.", + "doc_zh": "作出切换决策时发生的失败。", "associated": false }, { @@ -4496,6 +5132,7 @@ "name": "new", "signature": "pub fn new(kind: HandoverErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -4503,6 +5140,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the values that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -4510,6 +5148,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> HandoverErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -4517,6 +5156,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -4524,6 +5164,7 @@ "name": "HandoverPolicy", "signature": "pub struct HandoverPolicy {", "doc": "The parameters of a handover policy.\n\nThe three parameters work together. The minimum quality decides when the\ncurrent server must be abandoned, the margin decides when a better\ncandidate is worth the switch, and the dwell time limits how often\nswitches may happen at all.", + "doc_zh": "切换策略的参数。\n\n三个参数协同工作。最低质量决定当前服务星何时必须被放弃,余量决定\n更优候选何时值得切换,驻留时间则限制切换至多能多频繁发生。", "associated": false }, { @@ -4531,6 +5172,7 @@ "name": "new", "signature": "pub const fn new( minimum_quality: f64, handover_margin: f64, minimum_dwell_seconds: f64, ) -> Self {", "doc": "Creates a policy from its three parameters.", + "doc_zh": "由其三个参数创建策略。", "associated": true }, { @@ -4538,6 +5180,7 @@ "name": "validate", "signature": "pub fn validate(&self) -> Result<(), HandoverError> {", "doc": "Validates the policy, returning the reason when it is not usable.", + "doc_zh": "校验策略,不可用时返回原因。", "associated": true }, { @@ -4545,6 +5188,7 @@ "name": "best", "signature": "pub fn best<'a>(&self, candidates: &'a [Candidate]) -> Option<&'a Candidate> {", "doc": "Returns the best candidate under the policy.\n\nThe best candidate is the one with the greatest quality, with ties\nbroken by the lower satellite identifier so that the result is\ndeterministic.", + "doc_zh": "返回该策略下的最优候选。\n\n最优候选是质量最大者,并列时以较小的卫星标识符打破,使结果确定。", "associated": true }, { @@ -4552,6 +5196,7 @@ "name": "HandoverRecord", "signature": "pub struct HandoverRecord {", "doc": "A record of one handover that has occurred.", + "doc_zh": "一次已发生切换的记录。", "associated": false }, { @@ -4559,6 +5204,7 @@ "name": "new", "signature": "pub const fn new( from: usize, to: usize, seconds: f64, from_quality: f64, to_quality: f64, ) -> Self {", "doc": "Creates a record.", + "doc_zh": "创建一条记录。", "associated": true }, { @@ -4566,6 +5212,7 @@ "name": "quality_gain", "signature": "pub fn quality_gain(&self) -> f64 {", "doc": "Returns the gain in quality that the handover achieved.", + "doc_zh": "返回该切换所取得的质量增益。", "associated": true }, { @@ -4573,6 +5220,7 @@ "name": "HandoverTracker", "signature": "pub struct HandoverTracker {", "doc": "Tracks the serving satellite and the history of handovers.\n\nThe tracker owns the policy and the decision history, so a caller only\nhas to supply the candidates at each step. It is the form a simulation or\na control loop uses, where the decision made at one step affects the one\nat the next through the dwell time.", + "doc_zh": "跟踪服务卫星与切换历史。\n\n跟踪器拥有策略与决策历史,故调用方只需在每一步提供候选。这是仿真或\n控制回路所用的形式——其中一步的决策经驻留时间影响下一步。", "associated": false }, { @@ -4580,6 +5228,7 @@ "name": "new", "signature": "pub fn new(policy: HandoverPolicy) -> Self {", "doc": "Creates a tracker with no serving satellite.", + "doc_zh": "创建没有服务卫星的跟踪器。", "associated": true }, { @@ -4587,6 +5236,7 @@ "name": "serving", "signature": "pub fn serving(policy: HandoverPolicy, satellite: usize) -> Self {", "doc": "Creates a tracker that is already serving a satellite.", + "doc_zh": "创建已在服务某卫星的跟踪器。", "associated": true }, { @@ -4594,6 +5244,7 @@ "name": "policy", "signature": "pub fn policy(&self) -> HandoverPolicy {", "doc": "Returns the policy.", + "doc_zh": "返回策略。", "associated": true }, { @@ -4601,6 +5252,7 @@ "name": "current", "signature": "pub fn current(&self) -> Option {", "doc": "Returns the satellite currently being served, if any.", + "doc_zh": "返回当前正在服务的卫星(若有)。", "associated": true }, { @@ -4608,6 +5260,7 @@ "name": "last_handover_seconds", "signature": "pub fn last_handover_seconds(&self) -> Option {", "doc": "Returns the time of the last handover, if there has been one.", + "doc_zh": "返回最后一次切换的时间(若曾发生)。", "associated": true }, { @@ -4615,6 +5268,7 @@ "name": "history", "signature": "pub fn history(&self) -> &[HandoverRecord] {", "doc": "Returns the handover history, in time order.", + "doc_zh": "按时间次序返回切换历史。", "associated": true }, { @@ -4622,6 +5276,7 @@ "name": "handover_count", "signature": "pub fn handover_count(&self) -> usize {", "doc": "Returns the number of handovers that have occurred.", + "doc_zh": "返回已发生的切换次数。", "associated": true }, { @@ -4629,6 +5284,7 @@ "name": "step", "signature": "pub fn step( &mut self, seconds: f64, candidates: &[Candidate], ) -> Result {", "doc": "Applies the policy to a set of candidates at a time.\n\nThe tracker updates its state according to the decision and returns\nthat decision, so a caller can both react to it and rely on the\ntracker having recorded it.\n\nReturns an error when the candidates or the policy are not usable.", + "doc_zh": "在某一时刻把策略施加于一组候选。\n\n跟踪器按该决策更新其状态并返回该决策,使调用方既能对它作出反应,也能\n依赖跟踪器已记录它。\n\n候选或策略不可用时返回错误。", "associated": true }, { @@ -4636,6 +5292,7 @@ "name": "mean_interval_seconds", "signature": "pub fn mean_interval_seconds(&self) -> Option {", "doc": "Returns the mean time between handovers, in seconds.\n\nReturns `None` when fewer than two handovers have occurred, because a\nsingle interval is not a mean.", + "doc_zh": "返回两次切换之间的平均时间,单位为秒。\n\n发生的切换少于两次时返回 `None`,因为单个间隔算不上均值。", "associated": true }, { @@ -4643,6 +5300,7 @@ "name": "handover_rate_per_second", "signature": "pub fn handover_rate_per_second(&self) -> Option {", "doc": "Returns the number of handovers per unit time over the history.\n\nThe rate is measured across the whole history, from the first\nhandover to the last. Returns `None` when there are fewer than two\nhandovers or when they all occur at the same instant.", + "doc_zh": "返回整个历史上单位时间的切换次数。\n\n速率跨越整个历史度量,从首次切换到末次。切换少于两次或它们都\n发生在同一瞬时时返回 `None`。", "associated": true }, { @@ -4650,6 +5308,7 @@ "name": "total_quality_gain", "signature": "pub fn total_quality_gain(&self) -> f64 {", "doc": "Returns the total quality gained by handing over.\n\nThe value is the sum over the history, which can be negative when a\nhandover was forced by a falling quality rather than chosen for a\ngain.", + "doc_zh": "返回切换所获得的质量总和。\n\n该值是对整个历史求和,当切换是被下降的质量所迫而非为增益所选择\n时可为负。", "associated": true } ] @@ -4657,13 +5316,15 @@ { "name": "apsis-mesh", "layer": "network", - "summary": "Mesh connectivity abstractions. A satellite mesh is a set of terminals that reach each other through links, some of which are satellite links and some of which are ground links. This crate provides the abstractions that a mesh application needs on top of a topology: which terminals can reach which others, how many independent ways there are to do so, and which links are critical. The three questions this crate answers: - **Reachability**: the set of terminals reachable from a given one. - **Redundancy**: how many independent paths join two terminals, which is the number of links that can fail independently. This is the edge-connectivity of the graph, by Menger's theorem. - **Criticality**: the links whose removal would separate the mesh, which are the ones a network operator must protect. The redundancy computation is exact for the small graphs a satellite mesh presents. It uses the edge version of Menger's theorem, computing the connectivity from a sequence of maximum flow problems, each of unit capacity. Source: Menger's theorem, and the reduction of edge connectivity to maximum flow, as given in Cormen et al., *Introduction to Algorithms*, 3rd edition, MIT Press, 2009, chapter 26. ``` use apsis_mesh::Mesh; use apsis_topology::Topology; // Three terminals in a ring, so each pair has two independent paths. let mut topology = Topology::new(3); topology.add_bidirectional_edge(0, 1, 1.0).expect(\"the nodes exist\"); topology.add_bidirectional_edge(1, 2, 1.0).expect(\"the nodes exist\"); topology.add_bidirectional_edge(2, 0, 1.0).expect(\"the nodes exist\"); let mesh = Mesh::new(topology); assert!(mesh.is_connected()); assert_eq!(mesh.edge_connectivity(0, 1), Some(2)); ```", + "summary": "Mesh connectivity abstractions.\n\nA satellite mesh is a set of terminals that reach each other through\nlinks, some of which are satellite links and some of which are ground\nlinks. This crate provides the abstractions that a mesh application needs\non top of a topology: which terminals can reach which others, how many\nindependent ways there are to do so, and which links are critical.\n\nThe three questions this crate answers:\n\n- **Reachability**: the set of terminals reachable from a given one.\n- **Redundancy**: how many independent paths join two terminals, which is\n the number of links that can fail independently. This is the\n edge-connectivity of the graph, by Menger's theorem.\n- **Criticality**: the links whose removal would separate the mesh, which\n are the ones a network operator must protect.\n\nThe redundancy computation is exact for the small graphs a satellite mesh\npresents. It uses the edge version of Menger's theorem, computing the\nconnectivity from a sequence of maximum flow problems, each of unit\ncapacity.\n\nSource: Menger's theorem, and the reduction of edge connectivity to\nmaximum flow, as given in Cormen et al., *Introduction to Algorithms*,\n3rd edition, MIT Press, 2009, chapter 26.\n\n```\nuse apsis_mesh::Mesh;\nuse apsis_topology::Topology;\n\n// Three terminals in a ring, so each pair has two independent paths.\nlet mut topology = Topology::new(3);\ntopology.add_bidirectional_edge(0, 1, 1.0).expect(\"the nodes exist\");\ntopology.add_bidirectional_edge(1, 2, 1.0).expect(\"the nodes exist\");\ntopology.add_bidirectional_edge(2, 0, 1.0).expect(\"the nodes exist\");\n\nlet mesh = Mesh::new(topology);\nassert!(mesh.is_connected());\nassert_eq!(mesh.edge_connectivity(0, 1), Some(2));\n```", + "summary_zh": "网格连通性抽象。\n\n卫星网格是一组经链路互相到达的终端,其中一些是卫星链路、一些是地面链路。本\ncrate 提供网格应用在拓扑之上所需的抽象:哪些终端能到达哪些其他终端、有多少条\n独立路径可以做到,以及哪些链路是关键。\n\n本 crate 回答的三个问题:\n\n- **可达性**:自某给定终端可达的终端集合。\n- **冗余度**:连接两个终端的独立路径条数,即可独立失效的链路数。依 Menger 定理,\n 它是该图的边连通度。\n- **关键性**:移除后会切开网格的那些链路,即网络运营者必须保护的链路。\n\n冗余度计算对卫星网格所呈现的小图是精确的。它使用 Menger 定理的边版本,从一\n系列单位容量的最大流问题计算连通度。\n\n出处:Menger 定理,以及把边连通度归约为最大流,见 Cormen 等,\n*Introduction to Algorithms*,第 3 版,MIT Press,2009,第 26 章。", "items": [ { "kind": "fn", "name": "reachable_from", "signature": "pub fn reachable_from(topology: &Topology, source: usize) -> Vec {", "doc": "Returns the set of nodes reachable from a node, following edges in their\ndirection.\n\nThe start node is included, since a node reaches itself by a path of no\nhops. The result is in node order.\n\nReturns an empty vector when the node is out of range.", + "doc_zh": "沿边的方向返回自某节点可达的节点集合。\n\n起点节点被包含在内,因为节点经零跳路径可达自身。结果按节点次序\n排列。\n\n节点越界时返回空向量。", "associated": false }, { @@ -4671,6 +5332,7 @@ "name": "edge_connectivity", "signature": "pub fn edge_connectivity(topology: &Topology, source: usize, destination: usize) -> Option {", "doc": "Returns the number of independent paths between two nodes.\n\nTwo paths are independent when they share no edge. By Menger's theorem\nthat number is the minimum number of edges whose removal disconnects the\ntwo nodes, so it is the number of link failures the connection survives.\n\nThe value is computed as a maximum flow of unit capacity between the two\nnodes, using the Edmonds-Karp variant of the Ford-Fulkerson method, which\naugments along a shortest path at each step. For the small graphs of a\nsatellite mesh this is fast, and it is exact.\n\nReturns `Some(0)` when no path exists, and `None` when either node is out\nof range or the two nodes are the same, for which the question is not\ndefined.", + "doc_zh": "返回两节点之间相互独立的路径条数。\n\n两条路径在不含公共边时相互独立。依 Menger 定理,该条数即使这两节点\n断连所需移除的最少边数,故即该连接可承受的链路失效次数。\n\n该值以两节点之间单位容量的最大流计算,采用 Ford-Fulkerson 方法的\nEdmonds-Karp 变体,每步沿最短增广。对卫星网这样的小图它既快又\n精确。\n\n不存在路径时返回 `Some(0)`;任一节点越界或两节点相同(此时该问题\n无定义)时返回 `None`。", "associated": false }, { @@ -4678,6 +5340,7 @@ "name": "strongly_connected_components", "signature": "pub fn strongly_connected_components(topology: &Topology) -> usize {", "doc": "Returns the number of strongly connected components.\n\nA component is strongly connected when every node in it can reach every\nother following the edges in their direction. This is the count of\nseparate networks that can route internally, which for a satellite mesh\nwith directed inter-satellite links is the right measure of how many\npieces it has.\n\nThe algorithm is Kosaraju's: a depth first search records the order in\nwhich nodes finish, and a second search over the reversed edges, taking\nthe nodes in reverse finishing order, emits the components.\n\nSource: Kosaraju's algorithm as described in Cormen et al.,\n*Introduction to Algorithms*, 3rd edition, MIT Press, 2009, chapter 22.", + "doc_zh": "返回强连通分量的数量。\n\n一个分量内每个节点都能沿边的方向到达其余每个节点时,它即强连通。\n该计数是各自内部可路由的独立网络数,对含定向星间链路的卫星网而\n言,它正是衡量该网由几部分构成的恰当度量。\n\n算法为 Kosaraju:一次深度优先搜索记录各节点的完成次序,再在反向\n边上按完成次序的逆序做第二次搜索,输出各分量。\n\n出处:Kosaraju 算法,见 Cormen 等,*Introduction to Algorithms*,\n第 3 版,MIT Press,2009,第 22 章。", "associated": false }, { @@ -4685,6 +5348,7 @@ "name": "CriticalLink", "signature": "pub struct CriticalLink {", "doc": "A link whose removal would change the connectivity of the mesh.", + "doc_zh": "一条移除后会改变网格连通性的链路。", "associated": false }, { @@ -4692,6 +5356,7 @@ "name": "new", "signature": "pub const fn new(from: usize, to: usize, cost: f64, components_without: usize) -> Self {", "doc": "Creates a critical link record.", + "doc_zh": "创建关键链路记录。", "associated": true }, { @@ -4699,6 +5364,7 @@ "name": "MeshSummary", "signature": "pub struct MeshSummary {", "doc": "A summary of the connectivity of a mesh.", + "doc_zh": "网格连通性的摘要。", "associated": false }, { @@ -4706,6 +5372,7 @@ "name": "Mesh", "signature": "pub struct Mesh {", "doc": "A mesh of terminals joined by links.\n\nThe mesh wraps a topology and adds the connectivity measures a mesh\napplication needs. It does not own a schedule or a traffic model; those\nbelong to the caller.", + "doc_zh": "由链路连接的终端构成的网格。\n\n网格封装一个拓扑并加入网格应用所需的连通性度量。它不拥有调度或业务\n模型;那些属于调用方。", "associated": false }, { @@ -4713,6 +5380,7 @@ "name": "new", "signature": "pub const fn new(topology: Topology) -> Self {", "doc": "Creates a mesh over a topology.", + "doc_zh": "在某拓扑之上创建网格。", "associated": true }, { @@ -4720,6 +5388,7 @@ "name": "topology", "signature": "pub const fn topology(&self) -> &Topology {", "doc": "Returns the underlying topology.", + "doc_zh": "返回底层拓扑。", "associated": true }, { @@ -4727,6 +5396,7 @@ "name": "node_count", "signature": "pub fn node_count(&self) -> usize {", "doc": "Returns the number of terminals.", + "doc_zh": "返回终端的数量。", "associated": true }, { @@ -4734,6 +5404,7 @@ "name": "edge_count", "signature": "pub fn edge_count(&self) -> usize {", "doc": "Returns the number of links.", + "doc_zh": "返回链路的数量。", "associated": true }, { @@ -4741,6 +5412,7 @@ "name": "is_connected", "signature": "pub fn is_connected(&self) -> bool {", "doc": "Returns `true` when every terminal reaches every other, treating the\nlinks as undirected.", + "doc_zh": "把链路视为无向时,当每个终端都能到达其余每个终端,返回 `true`。", "associated": true }, { @@ -4748,6 +5420,7 @@ "name": "reachable_from", "signature": "pub fn reachable_from(&self, source: usize) -> Vec {", "doc": "Returns the terminals reachable from a terminal.", + "doc_zh": "返回自某终端可达的各终端。", "associated": true }, { @@ -4755,6 +5428,7 @@ "name": "edge_connectivity", "signature": "pub fn edge_connectivity(&self, source: usize, destination: usize) -> Option {", "doc": "Returns the number of independent paths between two terminals.\n\nReturns `None` when either terminal is out of range or the two are\nthe same.", + "doc_zh": "返回两个终端之间独立路径的条数。\n\n任一终端越界或两者相同时返回 `None`。", "associated": true }, { @@ -4762,6 +5436,7 @@ "name": "minimum_connectivity", "signature": "pub fn minimum_connectivity(&self) -> Option {", "doc": "Returns the smallest number of independent paths between any two\ndistinct terminals.\n\nReturns `None` when there are fewer than two terminals.", + "doc_zh": "返回任意两个不同终端之间独立路径条数的最小值。\n\n终端少于两个时返回 `None`。", "associated": true }, { @@ -4769,6 +5444,7 @@ "name": "critical_links", "signature": "pub fn critical_links(&self) -> Vec {", "doc": "Returns the links whose removal would increase the number of\nconnected components, treating each link as undirected.\n\nThese are the bridges of the undirected graph underlying the mesh. A\nlink is reported once, even when it is present in both directions,\nbecause a failure takes out the whole link rather than one direction\nof it.\n\nEach candidate is found by removing the link in both directions and\ncomparing the component count, which is exact but costs one traversal\nper link. For the size of graph a satellite mesh presents that is\nacceptable, and it is easier to verify than a linear time bridge\nfinding algorithm.\n\nLinks that are not bridges are omitted, so an empty result means\nevery link lies on a cycle and the mesh survives any single link\nfailure without splitting.", + "doc_zh": "返回移除后会增加连通分量数的那些链路,把每条链路视为无向。\n\n它们是网格底层无向图的桥。一条链路只报告一次,即便它有双向,\n因为一次失效是整条链路失效而非其中一个方向。\n\n每个候选通过在两方向移除该链路并比较分量数来发现,这精确但每条\n链路要花一次遍历。对卫星网所呈现的图规模而言这是可接受的,且比\n线性时间的桥查找算法更易验证。\n\n非桥的链路被省略,故空结果意味着每条链路都在某个环上,网格在\n任何单条链路失效下都不会裂开。", "associated": true }, { @@ -4776,6 +5452,7 @@ "name": "survives_single_link_failure", "signature": "pub fn survives_single_link_failure(&self) -> bool {", "doc": "Returns `true` when the mesh survives the failure of any single link\nwithout splitting.\n\nThis is the condition for the mesh to have a single link failure\ntolerance, which is the minimum redundancy a network carrying traffic\nwithout interruption should have.", + "doc_zh": "当网格在任何单条链路失效下都不裂开时返回 `true`。\n\n这是网格具备单链路失效容错的条件,而这是不中断地承载业务的网络\n应有的最低冗余。", "associated": true }, { @@ -4783,6 +5460,7 @@ "name": "strongly_connected_components", "signature": "pub fn strongly_connected_components(&self) -> usize {", "doc": "Returns the number of strongly connected components.\n\nFor a mesh of directed links this counts the pieces that can route\ninternally. A value above one means some terminals cannot be reached\nfrom some others.", + "doc_zh": "返回强连通分量的数量。\n\n对有向链路的网格而言,它统计可各自内部路由的块数。大于一的值\n意味着有些终端无法从另一些到达。", "associated": true }, { @@ -4790,6 +5468,7 @@ "name": "summarise", "signature": "pub fn summarise(&self) -> MeshSummary {", "doc": "Returns a summary of the mesh connectivity.", + "doc_zh": "返回网格连通性的摘要。", "associated": true } ] @@ -4797,13 +5476,15 @@ { "name": "apsis-rf", "layer": "rf", - "summary": "Core radio frequency primitives. This crate holds the quantities that the rest of the radio frequency layer is written in: power, gain, and the decibel scale that both are usually quoted on, together with the two relations that every link calculation starts from, the wavelength of a frequency and the free space path loss. Units are SI throughout. Power is in watts, frequency in hertz, distance in meters, and temperature in kelvin. Decibel values are power ratios, computed as ten times the base ten logarithm of the ratio. Two conventions are worth stating because they are easy to mix up: - A decibel is a ratio between two powers. A gain expressed in `dBi` is a ratio to an isotropic radiator and one in `dBd` is a ratio to a half wave dipole, which is 2.15 dB more directive than an isotropic radiator. [`Gain`] carries the ratio and both named forms. - The free space path loss increases with frequency as well as with distance, because both the effective aperture of a fixed gain antenna and the solid angle it subtends shrink as the wavelength shortens. ``` use apsis_rf::{Power, wavelength}; // A noise power of -204 dBW is about 4e-21 W. let noise = Power::from_dbw(-204.0).expect(\"the reading is finite\"); assert!(noise.watts() > 0.0); // A one meter wavelength corresponds to a frequency of 299.792 MHz. let lambda = wavelength(299_792_458.0).expect(\"the frequency is positive\"); assert!((lambda - 1.0).abs() < 1e-12); ```", + "summary": "Core radio frequency primitives.\n\nThis crate holds the quantities that the rest of the radio frequency\nlayer is written in: power, gain, and the decibel scale that both are\nusually quoted on, together with the two relations that every link\ncalculation starts from, the wavelength of a frequency and the free\nspace path loss.\n\nUnits are SI throughout. Power is in watts, frequency in hertz, distance\nin meters, and temperature in kelvin. Decibel values are power ratios,\ncomputed as ten times the base ten logarithm of the ratio.\n\nTwo conventions are worth stating because they are easy to mix up:\n\n- A decibel is a ratio between two powers. A gain expressed in `dBi` is a\n ratio to an isotropic radiator and one in `dBd` is a ratio to a half\n wave dipole, which is 2.15 dB more directive than an isotropic\n radiator. [`Gain`] carries the ratio and both named forms.\n- The free space path loss increases with frequency as well as with\n distance, because both the effective aperture of a fixed gain antenna\n and the solid angle it subtends shrink as the wavelength shortens.\n\n```\nuse apsis_rf::{Power, wavelength};\n\n// A noise power of -204 dBW is about 4e-21 W.\nlet noise = Power::from_dbw(-204.0).expect(\"the reading is finite\");\nassert!(noise.watts() > 0.0);\n\n// A one meter wavelength corresponds to a frequency of 299.792 MHz.\nlet lambda = wavelength(299_792_458.0).expect(\"the frequency is positive\");\nassert!((lambda - 1.0).abs() < 1e-12);\n```", + "summary_zh": "核心射频原语。\n\n本 crate 保存射频层其余部分据以书写的各量:功率、增益,以及二者通常所\n用的分贝尺度,连同每次链路计算所起的两条关系——某频率的波长与自由空间\n路径损耗。\n\n全程使用 SI 单位。功率以瓦特计,频率以赫兹计,距离以米计,温度以开尔文\n计。分贝值是功率比,按该比的十进对数的十倍计算。\n\n有两条约定值得写出,因为它们容易混淆:\n\n- 分贝是两个功率之比。以 `dBi` 表示的增益是相对各向同性辐射体的比值,\n 以 `dBd` 表示的则是相对半波振子的比值,后者比各向同性辐射体更具方向性\n 2.15 dB。[`Gain`] 携带该比值以及两种具名形式。\n- 自由空间路径损耗既随频率也随距离增大,因为固定增益天线的有效孔径与其\n 所张立体角都随波长变短而缩小。", "items": [ { "kind": "struct", "name": "Decibel", "signature": "pub struct Decibel(f64);", "doc": "A ratio expressed in decibels.\n\nA decibel is ten times the base ten logarithm of a power ratio, so a\nvalue of zero is unity gain or loss, a value of three is very close to a\ndoubling, and a value of minus three is very close to a halving.\n\nThe type stores the decibel value rather than the ratio, because a chain\nof gains and losses is then a sum, which is both faster and less prone to\noverflow than a product of ratios.", + "doc_zh": "以分贝表示的比值。\n\n分贝是功率比的十进对数的十倍,故值为零是单位增益或损耗,值为三非常接近加倍,\n值为负三非常接近减半。\n\n该类型存储分贝值而非比值,因为一串增益与损耗于是成为求和,这比比值之积既更快\n也更不易溢出。", "associated": false }, { @@ -4811,6 +5492,7 @@ "name": "UNITY", "signature": "pub const UNITY: Self = Self(0.0);", "doc": "The value for a ratio of one, which is zero decibels.", + "doc_zh": "比值为一时的取值,即零分贝。", "associated": true }, { @@ -4818,6 +5500,7 @@ "name": "DOUBLING", "signature": "pub const DOUBLING: Self = Self(3.010_299_956_639_812);", "doc": "The value for a ratio of two, which is about 3.0103 decibels.", + "doc_zh": "比值为二时的取值,约为 3.0103 分贝。", "associated": true }, { @@ -4825,6 +5508,7 @@ "name": "from_db", "signature": "pub const fn from_db(decibels: f64) -> Self {", "doc": "Creates a value from a decibel reading.", + "doc_zh": "由分贝读数创建一个取值。", "associated": true }, { @@ -4832,13 +5516,15 @@ "name": "from_ratio", "signature": "pub fn from_ratio(ratio: f64) -> Option {", "doc": "Creates a value from a power ratio.\n\nReturns `None` when the ratio is not positive and finite, because the\nlogarithm is not defined there.", + "doc_zh": "由功率比创建一个取值。\n\n比值非正且非有限时返回 `None`,因为那里没有定义对数。", "associated": true }, { "kind": "fn", "name": "from_amplitude_ratio", "signature": "pub fn from_amplitude_ratio(ratio: f64) -> Option {", - "doc": "Creates a value from an amplitude ratio.\n\nAn amplitude, unlike a power, scales the decibel value by twenty\nrather than ten, because power is proportional to amplitude squared.\nUse this for a voltage or field ratio.\n\nReturns `None` when the ratio is not positive and finite.", + "doc": "Creates a value from an amplitude ratio.", + "doc_zh": "由幅度比创建一个取值。\n\nAn amplitude, unlike a power, scales the decibel value by twenty\nrather than ten, because power is proportional to amplitude squared.\nUse this for a voltage or field ratio.\n\nReturns `None` when the ratio is not positive and finite.\n\n比值非正或非有限时返回 `None`。", "associated": true }, { @@ -4846,6 +5532,7 @@ "name": "db", "signature": "pub const fn db(self) -> f64 {", "doc": "Returns the reading in decibels.", + "doc_zh": "返回该分贝读数。", "associated": true }, { @@ -4853,6 +5540,7 @@ "name": "ratio", "signature": "pub fn ratio(self) -> f64 {", "doc": "Returns the power ratio this reading represents.", + "doc_zh": "返回该读数所表示的功率比。", "associated": true }, { @@ -4860,6 +5548,7 @@ "name": "amplitude_ratio", "signature": "pub fn amplitude_ratio(self) -> f64 {", "doc": "Returns the amplitude ratio this reading represents.", + "doc_zh": "返回该读数所表示的幅度比。", "associated": true }, { @@ -4867,6 +5556,7 @@ "name": "plus", "signature": "pub const fn plus(self, other: Self) -> Self {", "doc": "Returns the sum of two readings.\n\nAdding decibel values multiplies the ratios they represent, which is\nhow a cascade of gains and losses is combined.", + "doc_zh": "返回两个读数之和。\n\n分贝值相加即其所表示的比值相乘,这正是级联增益与损耗的合成\n方式。", "associated": true }, { @@ -4874,6 +5564,7 @@ "name": "minus", "signature": "pub const fn minus(self, other: Self) -> Self {", "doc": "Returns the difference of two readings.\n\nSubtracting decibel values divides the ratios they represent, which\nis how a loss is removed from a gain.", + "doc_zh": "返回两个读数之差。\n\n分贝值相减即其所表示的比值相除,这正是从增益中去掉损耗的方式。", "associated": true }, { @@ -4881,6 +5572,7 @@ "name": "negated", "signature": "pub const fn negated(self) -> Self {", "doc": "Returns this reading negated, which inverts the ratio.", + "doc_zh": "返回该读数的相反数,即比值的倒数。", "associated": true }, { @@ -4888,6 +5580,7 @@ "name": "is_finite", "signature": "pub const fn is_finite(self) -> bool {", "doc": "Returns `true` when the reading is finite.", + "doc_zh": "当该读数有限时返回 `true`。", "associated": true }, { @@ -4895,6 +5588,7 @@ "name": "wavelength", "signature": "pub fn wavelength(frequency: f64) -> Option {", "doc": "Returns the wavelength of a frequency in vacuum, in meters.\n\nThe relation is `lambda = c / f`, with `c` exact by the definition of the\nmetre.\n\nReturns `None` when the frequency is not positive and finite.", + "doc_zh": "返回某频率在真空中的波长,单位为米。\n\n关系为 `lambda = c / f`,其中 `c` 依米的定义是精确的。\n\n频率非正且非有限时返回 `None`。", "associated": false }, { @@ -4902,6 +5596,7 @@ "name": "free_space_path_loss", "signature": "pub fn free_space_path_loss(distance: f64, frequency: f64) -> Option {", "doc": "Returns the free space path loss between two isotropic antennas, as a\nratio in decibels.\n\nThe ratio is positive, so it represents a loss: the received power is the\ntransmitted power divided by this ratio. The relation is\n\n```text\nL = (4 pi d / lambda)^2\n```\n\nwhich is the spreading loss of a spherical wave over a distance `d`\ncombined with the effective aperture of an isotropic receive antenna.\n\nThe loss grows with frequency at a fixed distance, because the effective\naperture of the receive antenna falls as the square of the wavelength.\n\nThe distance must be positive. A zero distance is not a case this\nexpression covers: the ratio would be zero, which is infinite attenuation\nin decibels, and the underlying far field assumption does not hold.\n\nReturns `None` when the distance is not positive, or when either argument\nis not finite.", + "doc_zh": "返回两个各向同性天线之间的自由空间路径损耗,以分贝比值表示。\n\n该比值为正,故表示一种损耗:接收功率是发射功率除以该比值。关系为\n\n```text\nL = (4 pi d / lambda)^2\n```\n\n它是球面波在距离 `d` 上的扩散损耗与各向同性接收天线有效孔径的\n结合。\n\n固定距离下损耗随频率增大,因为接收天线的有效孔径按波长的平方下降。\n\n距离必须为正。零距离不是本式所涵盖的情形:比值会为零,即以分贝计的\n无穷衰减,且其背后的远场假定不成立。\n\n距离非正,或任一参数非有限时返回 `None`。", "associated": false }, { @@ -4909,6 +5604,7 @@ "name": "free_space_received_ratio", "signature": "pub fn free_space_received_ratio( distance: f64, frequency: f64, transmitter: Gain, receiver: Gain, ) -> Option {", "doc": "Returns the received power ratio between two antennas in free space.\n\nThe received power is the transmitted power multiplied by the two antenna\ngains and divided by the free space path loss, which in decibels is the\nsum\n\n```text\nP_r / P_t = G_t G_r / L\n```\n\nThe result is the ratio in decibels, so a positive value means the\nreceive antenna collects more than was transmitted, which happens when\nthe two gains exceed the spreading loss.\n\nReturns `None` when the distance or frequency is not usable.", + "doc_zh": "返回自由空间两个天线之间的接收功率比。\n\n接收功率是发射功率乘以两个天线增益、再除以自由空间路径损耗,以分贝\n表示即\n\n```text\nP_r / P_t = G_t G_r / L\n```\n\n结果是分贝比值,故正值意味着接收天线收集到的多于所发射的,这在两个\n增益超过扩散损耗时发生。\n\n距离或频率不可用时返回 `None`。", "associated": false }, { @@ -4916,6 +5612,7 @@ "name": "DIPOLE_OVER_ISOTROPIC_DB", "signature": "pub const DIPOLE_OVER_ISOTROPIC_DB: f64 = 2.15;", "doc": "The gain of a half wave dipole relative to an isotropic radiator, in\ndecibels.\n\nThe exact directivity of an ideal half wave dipole is 1.6409, which is\n2.1509 dB. Engineering practice rounds this to 2.15 dB, and that rounded\nvalue is what the `dBd` and `dBi` conventions are defined against. This\ncrate uses the conventional value so that a conversion between the two\nnamed forms is exact, and it derives the linear ratio from it.", + "doc_zh": "半波振子相对各向同性辐射体的增益,单位为分贝。\n\n理想半波振子的精确方向性为 1.6409,即 2.1509 dB。工程实践将其取整为\n2.15 dB,`dBd` 与 `dBi` 两种约定即以该取整值定义。本 crate 采用\n该约定值,使两种具名形式之间的换算精确,并由其导出线性比值。", "associated": false }, { @@ -4923,6 +5620,7 @@ "name": "HALF_WAVE_DIPOLE_RATIO", "signature": "pub const HALF_WAVE_DIPOLE_RATIO: f64 = 1.640_589_773_199_539_4;", "doc": "The linear gain ratio of a half wave dipole, `10^(2.15 / 10)`.\n\nThis is about 1.64059, within 0.02 percent of the exact directivity of an\nideal dipole, which is 1.6409.", + "doc_zh": "半波振子的线性增益比值,`10^(2.15 / 10)`。\n\n该值约为 1.64059,与理想振子 1.6409 的精确方向性相差在 0.02% 以内。", "associated": false }, { @@ -4930,6 +5628,7 @@ "name": "Gain", "signature": "pub struct Gain(f64);", "doc": "An antenna gain expressed as a ratio to an isotropic radiator.\n\nThe type stores the linear ratio. The two named decibel forms are offered\nas conversions, because the choice is a matter of convention rather than\nof the gain itself:\n\n- `dBi` is the ratio to an isotropic radiator, which radiates equally in\n every direction.\n- `dBd` is the ratio to a half wave dipole, which is\n [`DIPOLE_OVER_ISOTROPIC_DB`] decibels above an isotropic radiator.", + "doc_zh": "以相对各向同性辐射体的比值表示的天线增益。\n\n该类型以线性比值存储。两种具名分贝形式以换算形式提供,因为这一\n选择关乎约定而非增益本身:\n\n- `dBi` 是相对各向同性辐射体的比值,后者向各方向均匀辐射。\n- `dBd` 是相对半波振子的比值,后者比各向同性辐射体高\n [`DIPOLE_OVER_ISOTROPIC_DB`] 分贝。", "associated": false }, { @@ -4937,6 +5636,7 @@ "name": "ISOTROPIC", "signature": "pub const ISOTROPIC: Self = Self(1.0);", "doc": "Unit gain, as an isotropic radiator has.", + "doc_zh": "单位增益,如各向同性辐射体所有。", "associated": true }, { @@ -4944,6 +5644,7 @@ "name": "HALF_WAVE_DIPOLE", "signature": "pub const HALF_WAVE_DIPOLE: Self = Self(HALF_WAVE_DIPOLE_RATIO);", "doc": "The gain of a half wave dipole, about 1.6406.", + "doc_zh": "半波振子的增益,约为 1.6406。", "associated": true }, { @@ -4951,6 +5652,7 @@ "name": "from_ratio", "signature": "pub const fn from_ratio(ratio: f64) -> Self {", "doc": "Creates a gain from a linear ratio.", + "doc_zh": "由线性比值创建增益。", "associated": true }, { @@ -4958,6 +5660,7 @@ "name": "from_dbi", "signature": "pub fn from_dbi(dbi: f64) -> Option {", "doc": "Creates a gain from a value in decibels relative to an isotropic\nradiator.\n\nA gain ratio is never negative, so any finite reading is accepted.", + "doc_zh": "由相对各向同性辐射体的分贝值创建增益。\n\n增益比值不会为负,故任何有限读数都被接受。", "associated": true }, { @@ -4965,6 +5668,7 @@ "name": "from_dbd", "signature": "pub fn from_dbd(dbd: f64) -> Option {", "doc": "Creates a gain from a value in decibels relative to a half wave\ndipole.\n\nReturns `None` when the reading is not finite.", + "doc_zh": "由相对半波振子的分贝值创建增益。\n\n该读数非有限时返回 `None`。", "associated": true }, { @@ -4972,6 +5676,7 @@ "name": "ratio", "signature": "pub const fn ratio(self) -> f64 {", "doc": "Returns the linear ratio to an isotropic radiator.", + "doc_zh": "返回相对各向同性辐射体的线性比值。", "associated": true }, { @@ -4979,6 +5684,7 @@ "name": "dbi", "signature": "pub fn dbi(self) -> Option {", "doc": "Returns this gain in decibels relative to an isotropic radiator.\n\nReturns `None` when the ratio is not positive.", + "doc_zh": "返回该增益,相对各向同性辐射体以分贝计。\n\n比值非正时返回 `None`。", "associated": true }, { @@ -4986,6 +5692,7 @@ "name": "dbd", "signature": "pub fn dbd(self) -> Option {", "doc": "Returns this gain in decibels relative to a half wave dipole.\n\nReturns `None` when the ratio is not positive.", + "doc_zh": "返回该增益,相对半波振子以分贝计。\n\n比值非正时返回 `None`。", "associated": true }, { @@ -4993,6 +5700,7 @@ "name": "as_decibel", "signature": "pub fn as_decibel(self) -> Option {", "doc": "Returns this gain as a ratio in decibels.\n\nReturns `None` when the ratio is not positive.", + "doc_zh": "返回该增益,以分贝比表示。\n\n比值非正时返回 `None`。", "associated": true }, { @@ -5000,6 +5708,7 @@ "name": "effective_aperture", "signature": "pub fn effective_aperture(self, wavelength: f64) -> Option {", "doc": "Returns the effective aperture of an antenna with this gain at the\ngiven wavelength, in square meters.\n\nThe relation is `A = G lambda^2 / (4 pi)`. It follows from the\nrequirement that an antenna in a thermal bath receive as much power\nas it radiates.\n\nReturns `None` when the wavelength is not positive and finite.", + "doc_zh": "返回具有该增益的天线在给定波长处的有效孔径,单位为平方米。\n\n关系为 `A = G lambda^2 / (4 pi)`。它由热浴中的天线接收功率须等于\n其辐射功率这一要求得出。\n\n波长非正且非有限时返回 `None`。", "associated": true }, { @@ -5007,6 +5716,7 @@ "name": "from_effective_aperture", "signature": "pub fn from_effective_aperture(aperture: f64, wavelength: f64) -> Option {", "doc": "Returns the gain of an antenna with this effective aperture at the\ngiven wavelength.\n\nThis is the inverse of [`Gain::effective_aperture`].\n\nReturns `None` when the wavelength is not positive and finite, or\nwhen the aperture is negative.", + "doc_zh": "返回具有该有效孔径的天线在给定波长处的增益。\n\n它是 [`Gain::effective_aperture`] 的逆。\n\n波长非正且非有限,或孔径为负时返回 `None`。", "associated": true }, { @@ -5014,6 +5724,7 @@ "name": "DBW_REFERENCE", "signature": "pub const DBW_REFERENCE: f64 = 1.0;", "doc": "The reference power of the decibel watt, one watt.\n\nA power quoted in dBW is a ratio to this value.", + "doc_zh": "分贝瓦的参考功率,即一瓦。\n\n以 dBW 表示的功率是相对该值的比值。", "associated": false }, { @@ -5021,6 +5732,7 @@ "name": "DBM_REFERENCE", "signature": "pub const DBM_REFERENCE: f64 = 1.0e-3;", "doc": "The reference power of the decibel milliwatt, one milliwatt.\n\nA power quoted in dBm is a ratio to this value, so a value in dBm is\nthirty decibels above the same value in dBW.", + "doc_zh": "分贝毫瓦的参考功率,即一毫瓦。\n\n以 dBm 表示的功率是相对该值的比值,故同一数值以 dBm 表示比以 dBW\n表示高三十分贝。", "associated": false }, { @@ -5028,6 +5740,7 @@ "name": "Power", "signature": "pub struct Power(f64);", "doc": "A power level in watts.\n\nThe type stores watts, which is the unit the decibel references are\ndefined against, and offers conversion to and from the two decibel forms\nused in link budgets.", + "doc_zh": "以瓦特表示的功率电平。\n\n该类型以瓦特存储,这是分贝参考所依据的单位,并提供与链路预算中常用\n的两种分贝形式之间的换算。", "associated": false }, { @@ -5035,6 +5748,7 @@ "name": "WATT", "signature": "pub const WATT: Self = Self(1.0);", "doc": "The reference power of zero dBW, one watt.", + "doc_zh": "零 dBW 的参考功率,即一瓦。", "associated": true }, { @@ -5042,6 +5756,7 @@ "name": "MILLIWATT", "signature": "pub const MILLIWATT: Self = Self(1.0e-3);", "doc": "The reference power of zero dBm, one milliwatt.", + "doc_zh": "零 dBm 的参考功率,即一毫瓦。", "associated": true }, { @@ -5049,6 +5764,7 @@ "name": "from_watts", "signature": "pub const fn from_watts(watts: f64) -> Self {", "doc": "Creates a power level from a value in watts.\n\nA power may be zero, which represents no power, but not negative.", + "doc_zh": "以瓦特值创建功率电平。\n\n功率可以为零,表示没有功率,但不可为负。", "associated": true }, { @@ -5056,6 +5772,7 @@ "name": "from_milliwatts", "signature": "pub const fn from_milliwatts(milliwatts: f64) -> Self {", "doc": "Creates a power level from a value in milliwatts.", + "doc_zh": "以毫瓦值创建功率电平。", "associated": true }, { @@ -5063,6 +5780,7 @@ "name": "from_dbw", "signature": "pub fn from_dbw(decibel_watts: f64) -> Option {", "doc": "Creates a power level from a value in decibel watts.\n\nA zero or negative reading cannot be expressed as a finite power, so\nno validation is needed.", + "doc_zh": "以分贝瓦值创建功率电平。\n\n零或负的读数无法表示为有限功率,故无需校验。", "associated": true }, { @@ -5070,6 +5788,7 @@ "name": "from_dbm", "signature": "pub fn from_dbm(decibel_milliwatts: f64) -> Option {", "doc": "Creates a power level from a value in decibel milliwatts.\n\nReturns `None` when the reading is not finite.", + "doc_zh": "以分贝毫瓦值创建功率电平。\n\n该读数非有限时返回 `None`。", "associated": true }, { @@ -5077,6 +5796,7 @@ "name": "watts", "signature": "pub const fn watts(self) -> f64 {", "doc": "Returns this power in watts.", + "doc_zh": "返回该功率的瓦特值。", "associated": true }, { @@ -5084,6 +5804,7 @@ "name": "milliwatts", "signature": "pub const fn milliwatts(self) -> f64 {", "doc": "Returns this power in milliwatts.", + "doc_zh": "返回该功率的毫瓦值。", "associated": true }, { @@ -5091,6 +5812,7 @@ "name": "dbw", "signature": "pub fn dbw(self) -> Option {", "doc": "Returns this power in decibel watts.\n\nReturns `None` when the power is not positive, because the logarithm\nis not defined there.", + "doc_zh": "返回该功率的分贝瓦值。\n\n功率非正时返回 `None`,因为对数在那里没有定义。", "associated": true }, { @@ -5098,6 +5820,7 @@ "name": "dbm", "signature": "pub fn dbm(self) -> Option {", "doc": "Returns this power in decibel milliwatts.\n\nReturns `None` when the power is not positive.", + "doc_zh": "返回该功率的分贝毫瓦值。\n\n功率非正时返回 `None`。", "associated": true }, { @@ -5105,6 +5828,7 @@ "name": "ratio_in_decibels", "signature": "pub fn ratio_in_decibels(self, rhs: Self) -> Option {", "doc": "Returns the ratio of `self` to `rhs`, in decibels.\n\nThis is the form used in a link budget to express a loss or a gain\nbetween two power levels.\n\nReturns `None` when either power is not positive.", + "doc_zh": "返回 `self` 与 `rhs` 之比,以分贝计。\n\n这是链路预算中用来表达两个功率电平之间损耗或增益的形式。\n\n两个功率之一非正时返回 `None`。", "associated": true }, { @@ -5112,6 +5836,7 @@ "name": "scaled", "signature": "pub fn scaled(self, ratio: Decibel) -> Option {", "doc": "Returns this power scaled by a ratio in decibels.\n\nReturns `None` when the resulting power is not finite, which happens\nfor a very large gain.", + "doc_zh": "返回该功率按某个分贝比值缩放后的值。\n\n结果功率非有限时返回 `None`;增益极大时即如此。", "associated": true }, { @@ -5119,6 +5844,7 @@ "name": "is_valid", "signature": "pub const fn is_valid(self) -> bool {", "doc": "Returns `true` when the power is finite and not negative.", + "doc_zh": "当该功率有限且非负时返回 `true`。", "associated": true } ] @@ -5126,13 +5852,15 @@ { "name": "apsis-antenna", "layer": "rf", - "summary": "Antenna radiation patterns and gain models. An antenna does not radiate equally in every direction. This crate models how the gain varies with angle for the two shapes that appear in almost every satellite link: a parabolic reflector, whose beam is a narrow pencil, and an idealised element with a broad pattern. Three beam width conventions are used in practice and they are not interchangeable: - The half power beam width is the angle between the two directions where the gain has fallen to half its peak, which is three decibels down. - The null to null beam width is the angle between the first two zeros on either side of the main lobe. It is roughly twice the half power width for a uniformly illuminated aperture. - The first sidelobe level is the peak gain outside the main lobe. For a uniformly illuminated circular aperture it is about minus 17.6 dB. Every function here takes or returns angles in radians and gains as [`apsis_rf::Gain`], so a result can be fed straight into a link budget. Sources: - Parabolic aperture gain: `G = eta (pi D / lambda)^2`, where `eta` is the aperture efficiency, typically between 0.55 and 0.70 for a well adjusted dish. - Half power beam width of a circular aperture: `theta = k lambda / D` with `k` about 1.02 for uniform illumination and close to 1.2 for typical illumination tapers. - The co-polar reference pattern of a simple Earth station antenna, which falls as the square of the off-axis angle down to a sidelobe floor. This is the form used by ITU-R Recommendation S.465 to bound interference from one satellite system into another. ``` use apsis_antenna::ParabolicAperture; // A three meter dish at twelve gigahertz has a peak gain near 50 dBi. let dish = ParabolicAperture { diameter: 3.0, efficiency: 0.65, }; let peak = dish.peak_gain(0.025).expect(\"valid wavelength\"); assert!(peak.dbi().expect(\"positive\") > 45.0); assert!(peak.dbi().expect(\"positive\") < 50.0); ```", + "summary": "Antenna radiation patterns and gain models.\n\nAn antenna does not radiate equally in every direction. This crate models\nhow the gain varies with angle for the two shapes that appear in almost\nevery satellite link: a parabolic reflector, whose beam is a narrow\npencil, and an idealised element with a broad pattern.\n\nThree beam width conventions are used in practice and they are not\ninterchangeable:\n\n- The half power beam width is the angle between the two directions where\n the gain has fallen to half its peak, which is three decibels down.\n- The null to null beam width is the angle between the first two zeros on\n either side of the main lobe. It is roughly twice the half power width\n for a uniformly illuminated aperture.\n- The first sidelobe level is the peak gain outside the main lobe. For a\n uniformly illuminated circular aperture it is about minus 17.6 dB.\n\nEvery function here takes or returns angles in radians and gains as\n[`apsis_rf::Gain`], so a result can be fed straight into a link budget.\n\nSources:\n\n- Parabolic aperture gain: `G = eta (pi D / lambda)^2`, where `eta` is\n the aperture efficiency, typically between 0.55 and 0.70 for a\n well adjusted dish.\n- Half power beam width of a circular aperture: `theta = k lambda / D`\n with `k` about 1.02 for uniform illumination and close to 1.2 for\n typical illumination tapers.\n- The co-polar reference pattern of a simple Earth station antenna, which\n falls as the square of the off-axis angle down to a sidelobe floor. This\n is the form used by ITU-R Recommendation S.465 to bound interference\n from one satellite system into another.\n\n```\nuse apsis_antenna::ParabolicAperture;\n\n// A three meter dish at twelve gigahertz has a peak gain near 50 dBi.\nlet dish = ParabolicAperture {\n diameter: 3.0,\n efficiency: 0.65,\n};\nlet peak = dish.peak_gain(0.025).expect(\"valid wavelength\");\nassert!(peak.dbi().expect(\"positive\") > 45.0);\nassert!(peak.dbi().expect(\"positive\") < 50.0);\n```", + "summary_zh": "天线辐射方向图与增益模型。\n\n天线并非向各个方向均匀辐射。本 crate 为几乎每条卫星链路都会出现的两种\n形状建模增益随角度的变化:抛物面反射体,其波束是窄笔形;以及具有宽方向图\n的理想化单元。\n\n实践中有三种波束宽度约定,它们不可互换:\n\n- 半功率波束宽度是增益降到峰值一半(即低三分贝)的两个方向之间的角。\n- 零点到零点波束宽度是主瓣两侧头两个零点之间的角。对均匀照射孔径,它约\n 为半功率宽度的两倍。\n- 第一旁瓣电平是主瓣之外的峰值增益。对均匀照射的圆孔径,它约为负\n 17.6 dB。\n\n此处的每个函数都以弧度接收或返回角度,以 [`apsis_rf::Gain`] 接收或返回\n增益,故结果可直接送入链路预算。\n\n出处:\n\n- 抛物面孔径增益:`G = eta (pi D / lambda)^2`,其中 `eta` 是孔径效率,\n 对调准良好的天线通常在 0.55 与 0.70 之间。\n- 圆孔径的半功率波束宽度:`theta = k lambda / D`,均匀照射时 `k` 约为\n 1.02,典型照射锥削时接近 1.2。\n- 简单地球站天线的同极化参考方向图,其随偏轴角的平方下降到旁瓣底限。\n 这是 ITU-R 建议书 S.465 为限定从一个卫星系统到另一个的干扰所用的\n 形式。", "items": [ { "kind": "struct", "name": "ParabolicAperture", "signature": "pub struct ParabolicAperture {", "doc": "A parabolic reflector described by its diameter and aperture efficiency.\n\nThe aperture efficiency collects every loss between the power delivered\nto the feed and the power that leaves the aperture as a uniform plane\nwave: illumination taper, spillover, blockage by the feed and its\nsupports, and surface error. A well adjusted dish reaches 0.65 to 0.70,\nand a small or simply built one is nearer 0.55.", + "doc_zh": "由其直径与孔径效率描述的抛物面反射体。\n\n孔径效率汇集了从送到馈源的功率到以均匀平面波离开孔径的功率之间的\n每一项损耗:照射锥削、溢出、馈源及其支撑的遮挡,以及表面误差。调准\n良好的天线达到 0.65 至 0.70,小而简易的接近 0.55。", "associated": false }, { @@ -5140,6 +5868,7 @@ "name": "area", "signature": "pub fn area(self) -> f64 {", "doc": "Returns the physical area of the aperture, in square meters.", + "doc_zh": "返回孔径的物理面积,单位为平方米。", "associated": true }, { @@ -5147,6 +5876,7 @@ "name": "effective_area", "signature": "pub fn effective_area(self) -> f64 {", "doc": "Returns the effective area, in square meters.\n\nThis is the physical area scaled by the aperture efficiency. It is\nthe area that, if it collected uniformly, would deliver the observed\npower.", + "doc_zh": "返回有效面积,单位为平方米。\n\n这是物理面积按孔径效率缩放后的值。它是这样一块面积:若均匀收集,\n将给出所观测到的功率。", "associated": true }, { @@ -5154,6 +5884,7 @@ "name": "is_valid", "signature": "pub fn is_valid(self) -> bool {", "doc": "Returns `true` when the aperture describes a usable dish.", + "doc_zh": "当该孔径描述一个可用天线时返回 `true`。", "associated": true }, { @@ -5161,6 +5892,7 @@ "name": "peak_gain", "signature": "pub fn peak_gain(self, wavelength: f64) -> Option {", "doc": "Returns the peak gain on boresight at the given wavelength.\n\nThe relation is\n\n```text\nG = eta (pi D / lambda)^2\n```\n\nwhich follows from the effective area through\n[`Gain::from_effective_aperture`]. Returns `None` for an invalid\naperture or wavelength.", + "doc_zh": "返回给定波长处视轴上的峰值增益。\n\n关系为\n\n```text\nG = eta (pi D / lambda)^2\n```\n\n它由有效面积经 [`Gain::from_effective_aperture`] 得出。孔径或波长\n无效时返回 `None`。", "associated": true }, { @@ -5168,6 +5900,7 @@ "name": "uniform_beam_width", "signature": "pub fn uniform_beam_width(self, wavelength: f64) -> Option {", "doc": "Returns the half power beam width at the given wavelength, in\nradians.\n\nThis uses the uniform illumination relation, which is the narrowest\na dish of this diameter can be. A real dish with an illumination\ntaper is wider; use [`ParabolicAperture::half_power_beam_width`] to\ninclude the taper.\n\nReturns `None` for an invalid aperture or wavelength.", + "doc_zh": "返回给定波长处的半功率波束宽度,单位为弧度。\n\n这里用的是均匀照射关系,即该直径天线所能达到的最窄。带照射锥削\n的实际天线更宽;要计入锥削请用\n[`ParabolicAperture::half_power_beam_width`]。\n\n孔径或波长无效时返回 `None`。", "associated": true }, { @@ -5175,6 +5908,7 @@ "name": "half_power_beam_width", "signature": "pub fn half_power_beam_width(self, wavelength: f64) -> Option {", "doc": "Returns the half power beam width at the given wavelength, in\nradians, allowing for an illumination taper.\n\nA tapered illumination reduces the sidelobes at the cost of a wider\nmain lobe. The factor `k` in `theta = k lambda / D` rises from about\n1.02 for uniform illumination to about 1.2 for the taper that\nminimises sidelobes. A value of 1.15 is representative of a\nwell designed dish.\n\nReturns `None` for an invalid aperture or wavelength.", + "doc_zh": "返回给定波长处的半功率波束宽度(弧度),并计入照射锥削。\n\n锥削照射以加宽主瓣为代价降低旁瓣。`theta = k lambda / D` 中的因子\n`k` 从均匀照射的约 1.02 升到最小化旁瓣的锥削的约 1.2。1.15 是\n设计良好的天线的代表值。\n\n孔径或波长无效时返回 `None`。", "associated": true }, { @@ -5182,6 +5916,7 @@ "name": "beam_width", "signature": "pub fn beam_width(diameter: f64, wavelength: f64, k: f64) -> Option {", "doc": "Returns the half power beam width of a circular aperture, in radians.\n\nThe relation is `theta = k lambda / D`, where `k` depends on how the\naperture is illuminated. Uniform illumination gives the narrowest beam,\nwith `k` about 1.02, and the taper that suppresses sidelobes gives the\nwidest, with `k` about 1.2.\n\nThe result is the full width between the two three decibel points, not\nthe half width. It is a small angle approximation, so the caller should\nconfirm that the resulting width is small compared to a radian.\n\nReturns `None` when the diameter or wavelength is not positive, or when\n`k` is not positive.", + "doc_zh": "返回圆孔径的半功率波束宽度,单位为弧度。\n\n关系为 `theta = k lambda / D`,其中 `k` 取决于孔径如何被照射。均匀\n照射给出最窄波束,`k` 约为 1.02;抑制旁瓣的锥削给出最宽的,`k` 约为\n1.2。\n\n结果是两个三分贝点之间的全宽,而非半宽。它是小角近似,故调用方应\n确认所得宽度相对一弧度很小。\n\n直径或波长非正,或 `k` 非正时返回 `None`。", "associated": false }, { @@ -5189,6 +5924,7 @@ "name": "floor_angle", "signature": "pub fn floor_angle(beam_width: f64, floor_db: f64) -> Option {", "doc": "Returns the angle at which the squared angle law of\n[`off_axis_attenuation`](crate::off_axis_attenuation) reaches the\nsidelobe floor.\n\nBeyond this angle the model holds the gain at the floor rather than\nletting it fall without bound. The angle follows from setting the\nattenuation `12 (theta / theta_0)^2` equal to the floor, which gives\n`theta = theta_0 sqrt(floor / 12)`.\n\nReturns `None` when the beam width or floor is not positive and finite.", + "doc_zh": "返回 [`off_axis_attenuation`](crate::off_axis_attenuation) 的角平方律\n到达旁瓣底限的角度。\n\n超过该角度后,模型把增益保持在底限,而非任其无界下降。该角度由令\n衰减 `12 (theta / theta_0)^2` 等于底限得出,即\n`theta = theta_0 sqrt(floor / 12)`。\n\n波束宽度或底限非正且非有限时返回 `None`。", "associated": false }, { @@ -5196,6 +5932,7 @@ "name": "off_axis_attenuation", "signature": "pub fn off_axis_attenuation( peak: Gain, beam_width: f64, off_axis_angle: f64, floor_db: f64, ) -> Option {", "doc": "Returns the gain of an antenna at an angle from boresight, given a peak\ngain and a half power beam width.\n\nThis is the squared angle model that several interference assessments\nuse, expressed in terms of the half power beam width so that any antenna\ncan be described by the two numbers a link budget has to hand. In\ndecibels,\n\n```text\nG(theta) = G_peak - min(12 (theta / theta_0)^2, G_floor)\n```\n\nwhere `theta_0` is the half power beam width and `G_floor` is the\nsidelobe level to fall to, in decibels below the peak. The squared angle\nlaw comes from the first term of the Airy pattern of a uniformly\nilluminated aperture, which is a good approximation down to the first\nsidelobe.\n\nThe coefficient twelve is chosen so that the attenuation reaches three\ndecibels at `theta_0 / 2`, which is the definition of the half power beam\nwidth. A pattern taken from a recommendation should be used in place of\nthis one where the recommendation applies: see\n[`reference_pattern_gain`](crate::reference_pattern_gain) for the ITU-R\nS.465 pattern, which differs from this model in the main lobe and also in\nthe shape of its sidelobe region.\n\nReturns `None` when the peak gain has no decibel form, or when the beam\nwidth or floor is not positive and finite.", + "doc_zh": "返回给定峰值增益与半功率波束宽度时,天线在偏离视轴某角度处的增益。\n\n这是若干干扰评估所用的角平方模型,以半功率波束宽度表达,使任何天线\n都能由链路预算手头的两个数描述。以分贝计,\n\n```text\nG(theta) = G_peak - min(12 (theta / theta_0)^2, G_floor)\n```\n\n其中 `theta_0` 是半功率波束宽度,`G_floor` 是所要降到的旁瓣电平,\n相对峰值以分贝计。角平方律来自均匀照射孔径 Airy 图案的首项,直到\n第一旁瓣都是良好近似。\n\n系数十二的选取使衰减在 `theta_0 / 2` 处达到三分贝,这正是半功率\n波束宽度的定义。在建议书适用之处,应改用取自建议书的图案:ITU-R\nS.465 图案见 [`reference_pattern_gain`](crate::reference_pattern_gain),\n它在主瓣上以及旁瓣区的形状上都与本模型不同。\n\n峰值增益没有分贝形式,或波束宽度、底限非正且非有限时返回 `None`。", "associated": false }, { @@ -5203,6 +5940,7 @@ "name": "mask_gain", "signature": "pub fn mask_gain(diameter_over_wavelength: f64, off_axis_angle: f64) -> Option {", "doc": "Returns the gain that an antenna is required not to exceed at an angle\nfrom boresight, in decibels relative to an isotropic radiator.\n\nThis is the reference pattern of the Radio Regulations Appendix 8, used\nto check whether an Earth station meets the off-axis emission limits that\nprotect neighbouring satellites. The pattern is piecewise:\n\n```text\nfor D/lambda >= 100:\n G = 29 - 25 log10(theta) for 1 <= theta < 36 degrees\n G = -10 for 36 <= theta <= 180 degrees\nfor D/lambda < 100:\n G = 32 - 25 log10(theta) for 1 <= theta < 48 degrees\n G = -10 for 48 <= theta <= 180 degrees\n G = 32 - 25 log10(48) for theta < 1 degree\n```\n\nThe angle is in radians and the diameter to wavelength ratio decides\nwhich branch applies, because a small antenna has a broader main lobe\nand therefore a wider range over which the pattern must be controlled.\n\nThis function returns the limit, that is the largest gain permitted. An\nantenna is compliant when its actual off-axis gain is at or below it.\n\nReturns `None` when the angle is negative, when the ratio is not\npositive, or when the angle is not finite.\n\nSource: ITU, *Radio Regulations*, Appendix 8, which reproduces the\nreference pattern of ITU-R Recommendation S.465.", + "doc_zh": "返回天线在偏离视轴某角度处不得超出的增益,相对各向同性辐射体以分贝\n计。\n\n这是《无线电规则》附录 8 的参考方向图,用于检查地球站是否满足保护\n邻近卫星的偏轴发射限值。该方向图是分段的:\n\n```text\nfor D/lambda >= 100:\n G = 29 - 25 log10(theta) for 1 <= theta < 36 degrees\n G = -10 for 36 <= theta <= 180 degrees\nfor D/lambda < 100:\n G = 32 - 25 log10(theta) for 1 <= theta < 48 degrees\n G = -10 for 48 <= theta <= 180 degrees\n G = 32 - 25 log10(48) for theta < 1 degree\n```\n\n角度以弧度计,口径与波长之比决定适用哪一支,因为小天线主瓣更宽,\n故须受控的角度范围也更大。\n\n本函数返回限值,即允许的最大增益。当实际偏轴增益不高于它时天线即\n合规。\n\n角度为负、比值为非正,或角度非有限时返回 `None`。\n\n出处:ITU,*Radio Regulations*,附录 8,其中再现了 ITU-R 建议书\nS.465 的参考方向图。", "associated": false }, { @@ -5210,6 +5948,7 @@ "name": "Pattern", "signature": "pub enum Pattern {", "doc": "The shape of an antenna pattern.\n\nA pattern is either described by its geometry, in which case the peak\ngain and beam width are derived from the aperture, or given directly as\nmeasured or published values. The enum lets a link budget accept either.", + "doc_zh": "天线方向图的形状。\n\n方向图或由其几何描述(此时峰值增益与波束宽度由孔径导出),或作为\n实测或已发表的值直接给出。该枚举使链路预算能接受二者。", "associated": false }, { @@ -5217,6 +5956,7 @@ "name": "beam_width", "signature": "pub fn beam_width(self, wavelength: f64) -> Option {", "doc": "Returns the half power beam width of this pattern at the given\nwavelength, in radians.", + "doc_zh": "返回本方向图在给定波长处的半功率波束宽度,单位为弧度。", "associated": true }, { @@ -5224,6 +5964,7 @@ "name": "ElementPattern", "signature": "pub struct ElementPattern {", "doc": "A gain pattern constructed from a peak gain and a half power beam width.\n\nThis is the form a link budget needs: the two numbers that describe the\nbeam, plus the model that gives the gain at any angle from boresight.", + "doc_zh": "由峰值增益与半功率波束宽度构造的增益方向图。\n\n这是链路预算所需的形式:描述波束的两个数,加上给出偏离视轴任意角度\n处增益的模型。", "associated": false }, { @@ -5231,6 +5972,7 @@ "name": "new", "signature": "pub const fn new(peak: Gain, beam_width: f64) -> Self {", "doc": "Creates a pattern from a peak gain and a half power beam width.\n\nThe sidelobe floor is set to the value of the Radio Regulations\nreference pattern, 32 decibels below the peak, which is a common\nworst case for a small Earth station.", + "doc_zh": "由峰值增益与半功率波束宽度创建方向图。\n\n旁瓣底限被设为《无线电规则》参考方向图的值,即相对峰值低\n32 分贝,这是小型地球站常见的坏情形。", "associated": true }, { @@ -5238,6 +5980,7 @@ "name": "from_parabolic", "signature": "pub fn from_parabolic(aperture: ParabolicAperture, wavelength: f64) -> Option {", "doc": "Creates a pattern from a parabolic aperture at a wavelength.\n\nReturns `None` when the aperture or wavelength is not usable.", + "doc_zh": "由某波长处的抛物面孔径创建方向图。\n\n孔径或波长不可用时返回 `None`。", "associated": true }, { @@ -5245,6 +5988,7 @@ "name": "with_floor", "signature": "pub const fn with_floor(mut self, floor_db: f64) -> Self {", "doc": "Returns this pattern with a different sidelobe floor.", + "doc_zh": "返回本方向图的一个具有不同旁瓣底限的副本。", "associated": true }, { @@ -5252,6 +5996,7 @@ "name": "gain_at", "signature": "pub fn gain_at(self, off_axis_angle: f64) -> Option {", "doc": "Returns the gain at an angle from boresight.\n\nReturns `None` when the pattern is not usable or the angle is not\nfinite.", + "doc_zh": "返回偏离视轴某角度处的增益。\n\n方向图不可用或角度非有限时返回 `None`。", "associated": true }, { @@ -5259,6 +6004,7 @@ "name": "PEAK_GAIN_OFFSET_DB", "signature": "pub const PEAK_GAIN_OFFSET_DB: f64 = 7.7;", "doc": "The peak gain offset of the reference pattern, in decibels.\n\nThe recommendation gives the peak gain as `20 log10(D / lambda) + 7.7`,\nwhich corresponds to an aperture efficiency near 0.6. It is slightly\nbelow what a well adjusted dish reaches, because the reference pattern\nrepresents a typical rather than an optimal antenna.", + "doc_zh": "参考方向图的峰值增益偏移,单位为分贝。\n\n建议书把峰值增益给出为 `20 log10(D / lambda) + 7.7`,对应约 0.6 的\n孔径效率。它略低于调准良好的天线所达到者,因为参考方向图代表的是\n典型天线而非最优天线。", "associated": false }, { @@ -5266,6 +6012,7 @@ "name": "reference_peak_gain", "signature": "pub fn reference_peak_gain(diameter_over_wavelength: f64) -> Option {", "doc": "Returns the peak gain of the reference pattern, in decibels relative to an\nisotropic radiator.\n\nThe expression is `20 log10(D / lambda) + 7.7`.\n\nReturns `None` when the ratio is not positive and finite.", + "doc_zh": "返回参考方向图的峰值增益,相对各向同性辐射体以分贝计。\n\n表达式为 `20 log10(D / lambda) + 7.7`。\n\n比值非正或非有限时返回 `None`。", "associated": false }, { @@ -5273,6 +6020,7 @@ "name": "main_lobe_break_angle", "signature": "pub fn main_lobe_break_angle(diameter_over_wavelength: f64) -> Option {", "doc": "Returns the angle at which the main lobe meets the sidelobe region, in\ndegrees.\n\nThe two expressions intersect at this angle, and there is no closed form,\nso the value is found by bisection. The main lobe falls as the square of\nthe angle while the sidelobe region falls as its logarithm, so the\ndifference has a single zero in the interval searched, which makes\nbisection reliable.\n\nThe result lies between zero and the floor angle of the sidelobe region.", + "doc_zh": "返回主瓣与旁瓣区相接的角度,单位为度。\n\n两个表达式在该角度处相交,且没有闭式解,故该值以二分法求得。主瓣按\n角度的平方下降,而旁瓣区按其对数下降,故二者之差在所搜索区间内只有\n一个零点,这使二分法可靠。\n\n结果落在零与旁瓣区底限角之间。", "associated": false }, { @@ -5280,6 +6028,7 @@ "name": "reference_pattern_gain", "signature": "pub fn reference_pattern_gain(diameter_over_wavelength: f64, off_axis_angle: f64) -> Option {", "doc": "Returns the gain of the S.465 reference pattern at an off-axis angle.\n\nThe angle is in radians. The three regions are\n\n```text\nG = G_max - 2.5e-3 (D/lambda phi_deg)^2 for 0 <= phi < phi_break\nG = head - 25 log10(phi_deg) for phi_break <= phi < phi_floor\nG = -10 for phi_floor <= phi <= 180\n```\n\nwhere `phi_break` is the angle returned by [`main_lobe_break_angle`], the\nhead is 29 for a large aperture and 32 for a small one, and the floor\nangle is 36 or 48 degrees accordingly.\n\nThe falling sidelobe expression does not reach exactly minus ten decibels\nat the floor angle, so the pattern has a step of a few hundredths of a\ndecibel there. This is a property of the recommendation, which defines\nthe regions separately, and it is reproduced here rather than smoothed\nover.\n\nReturns `None` when the ratio or the angle is not usable.", + "doc_zh": "返回 S.465 参考方向图在偏轴某角度处的增益。\n\n角度以弧度计。三个区是\n\n```text\nG = G_max - 2.5e-3 (D/lambda phi_deg)^2 for 0 <= phi < phi_break\nG = head - 25 log10(phi_deg) for phi_break <= phi < phi_floor\nG = -10 for phi_floor <= phi <= 180\n```\n\n其中 `phi_break` 是 [`main_lobe_break_angle`] 返回的角度,head 对大\n孔径为 29、对小孔径为 32,底限角相应地是 36 或 48 度。\n\n下降的旁瓣表达式在底限角处并不精确达到负十分贝,故方向图在那里有\n几百分之一分贝的台阶。这是建议书的性质,它把各区分开定义;此处照实\n再现而非加以平滑。\n\n比值或角度不可用时返回 `None`。", "associated": false }, { @@ -5287,6 +6036,7 @@ "name": "reference_pattern_decibel", "signature": "pub fn reference_pattern_decibel( diameter_over_wavelength: f64, off_axis_angle: f64, ) -> Option {", "doc": "Returns the gain of the S.465 reference pattern as a decibel value.\n\nReturns `None` when the ratio or the angle is not usable.", + "doc_zh": "返回 S.465 参考方向图的增益,以分贝值表示。\n\n比值或角度不可用时返回 `None`。", "associated": false }, { @@ -5294,6 +6044,7 @@ "name": "diameter_over_wavelength_from_peak_gain", "signature": "pub fn diameter_over_wavelength_from_peak_gain(peak_gain_dbi: f64) -> Option {", "doc": "Returns the diameter to wavelength ratio of an antenna with a given peak\ngain.\n\nThis inverts the peak gain expression, which is useful when a station is\nspecified by its gain rather than its size.\n\nReturns `None` when the gain is not finite.", + "doc_zh": "返回某峰值增益天线的口径与波长之比。\n\n它反解峰值增益表达式,当某站以其增益而非尺寸规定时有用。\n\n增益非有限时返回 `None`。", "associated": false }, { @@ -5301,6 +6052,7 @@ "name": "reference_beam_width", "signature": "pub fn reference_beam_width(diameter_over_wavelength: f64) -> Option {", "doc": "Returns the half power beam width of the reference pattern main lobe, in\nradians.\n\nThe main lobe falls to three decibels at\n`phi = sqrt(3 / 2.5e-3) / (D / lambda)` degrees from boresight, so the\nfull width between the two three decibel points is twice that value.\n\nReturns `None` when the ratio is not positive and finite.", + "doc_zh": "返回参考方向图主瓣的半功率波束宽度,单位为弧度。\n\n主瓣在距视轴 `phi = sqrt(3 / 2.5e-3) / (D / lambda)` 度处降到三分贝,故\n两个三分贝点之间的全宽是该值的两倍。\n\n比值非正或非有限时返回 `None`。", "associated": false } ] @@ -5308,13 +6060,15 @@ { "name": "apsis-doppler", "layer": "rf", - "summary": "Doppler shift computation. A satellite and a ground station move relative to each other, so the frequency that arrives at the receiver differs from the frequency that left the transmitter. This crate computes that shift from the relative velocity. Two effects are present and they are separate. The classical shift is proportional to the range rate divided by the speed of light, and it is what a Doppler tracker compensates. The relativistic time dilation between the two clocks is a second order correction that is far smaller for orbital velocities, and it is offered separately so that a caller can include it when the application needs it. The sign convention used here is that a positive range rate, meaning the two terminals are moving apart, produces a negative shift and therefore a lower received frequency. This matches the everyday observation that a receding source sounds lower. ``` use apsis_doppler::{DopplerShift, range_rate_from_velocity}; use apsis_math::Vec3; // A satellite at the first axis moving away along it. let range_rate = range_rate_from_velocity( Vec3::new(7_000_000.0, 0.0, 0.0), Vec3::new(1_000.0, 0.0, 0.0), ); assert_eq!(range_rate, Some(1_000.0)); let shift = DopplerShift::from_range_rate(12.0e9, 1_000.0).expect(\"valid\"); assert!(shift.shifted_frequency() < 12.0e9); ```", + "summary": "Doppler shift computation.\n\nA satellite and a ground station move relative to each other, so the\nfrequency that arrives at the receiver differs from the frequency that\nleft the transmitter. This crate computes that shift from the relative\nvelocity.\n\nTwo effects are present and they are separate. The classical shift is\nproportional to the range rate divided by the speed of light, and it is\nwhat a Doppler tracker compensates. The relativistic time dilation\nbetween the two clocks is a second order correction that is far smaller\nfor orbital velocities, and it is offered separately so that a caller can\ninclude it when the application needs it.\n\nThe sign convention used here is that a positive range rate, meaning the\ntwo terminals are moving apart, produces a negative shift and therefore a\nlower received frequency. This matches the everyday observation that a\nreceding source sounds lower.\n\n```\nuse apsis_doppler::{DopplerShift, range_rate_from_velocity};\nuse apsis_math::Vec3;\n\n// A satellite at the first axis moving away along it.\nlet range_rate = range_rate_from_velocity(\n Vec3::new(7_000_000.0, 0.0, 0.0),\n Vec3::new(1_000.0, 0.0, 0.0),\n);\nassert_eq!(range_rate, Some(1_000.0));\n\nlet shift = DopplerShift::from_range_rate(12.0e9, 1_000.0).expect(\"valid\");\nassert!(shift.shifted_frequency() < 12.0e9);\n```", + "summary_zh": "多普勒频移计算。\n\n卫星与地面站相对运动,故到达接收机的频率不同于离开发射机的频率。本 crate\n由相对速度计算该频移。\n\n存在两种效应,它们是分开的。经典频移与距离变化率除以光速成正比,它是\n多普勒跟踪器所补偿的。两个时钟之间的相对论时间膨胀是二阶改正,对轨道\n速度而言小得多,它被分开提供,使调用方在应用需要时可以计入它。\n\n此处所用的符号约定是:正的距离变化率(即两终端正在远离)产生负频移,\n因而接收频率更低。这与日常观察——远离的声源听起来更低——一致。", "items": [ { "kind": "fn", "name": "range_rate_from_velocity", "signature": "pub fn range_rate_from_velocity(relative_position: Vec3, relative_velocity: Vec3) -> Option {", "doc": "Returns the range rate between two terminals, in meters per second.\n\nThe range rate is the component of the relative velocity along the line\nbetween the terminals. A positive value means the separation is\nincreasing.\n\nThe first argument is the position of the remote terminal relative to the\nlocal one, so that its magnitude is the separation. The second is the\nvelocity of the remote terminal relative to the local one. Both are in\nSI units.\n\nReturns `None` when the separation is zero, because no line of sight is\ndefined there, or when either argument is not finite.", + "doc_zh": "返回两终端之间的距离变化率,单位为米每秒。\n\n距离变化率是相对速度沿两终端连线的分量。正值表示间距在增大。\n\n第一个参数是远端终端相对本地终端的位置,故其模即间距。第二个是远端\n终端相对本地终端的速度。两者均为 SI 单位。\n\n间距为零时返回 `None`,因为那里没有定义视线;或任一参数非有限时\n返回 `None`。", "associated": false }, { @@ -5322,6 +6076,7 @@ "name": "classical_shift", "signature": "pub fn classical_shift(carrier_frequency: f64, range_rate: f64) -> Option {", "doc": "Returns the classical Doppler shift of a frequency for a range rate, in\nhertz.\n\nThe relation is\n\n```text\ndelta f = -f_0 (v_r / c)\n```\n\nwhere `v_r` is the range rate, positive when receding. The shift is\nnegative for a receding terminal, so the received frequency is lower than\nthe transmitted one.\n\nThe magnitude of the shift is small compared to the carrier: a range rate\nof 7 km/s at 12 GHz gives about 280 kHz, which is two parts in a hundred\nthousand. The caller must therefore treat the frequency as a double, and\nthe shifted frequency is returned separately rather than by adding a\nsmall shift to a large number.\n\nReturns `None` when the frequency is not positive and finite, or when the\nrange rate is not finite.", + "doc_zh": "返回某频率对应某距离变化率的经典多普勒频移,单位为赫兹。\n\n关系为\n\n```text\ndelta f = -f_0 (v_r / c)\n```\n\n其中 `v_r` 是距离变化率,远离时为正。远离的终端其频移为负,故接收\n频率低于发射频率。\n\n该频移的幅度相对载波很小:12 GHz 下 7 km/s 的距离变化率约给出\n280 kHz,即十万分之二。故调用方必须把频率当作双精度处理,且移位后的\n频率是单独返回,而非把一个小频移加到一个大数上。\n\n频率非正且非有限,或距离变化率非有限时返回 `None`。", "associated": false }, { @@ -5329,6 +6084,7 @@ "name": "relativistic_doppler_factor", "signature": "pub fn relativistic_doppler_factor(range_rate: f64) -> Option {", "doc": "Returns the relativistic Doppler factor for a range rate.\n\nThe factor is `sqrt((1 - beta) / (1 + beta))` with `beta = v_r / c`.\nMultiplying the transmitted frequency by it gives the received frequency\nto all orders in `beta`, so it is not a small correction to the classical\nshift but the quantity the classical shift approximates.\n\nIts expansion is `1 - beta + beta^2 / 2 - ...`. The first two terms are\nthe classical result, which is `1 - beta` to within a factor that is\nitself second order, and the remaining terms are the difference between\nthe two treatments. For a satellite at orbital velocity, `beta` is about\n`2.3e-5` and the second order term is about `2.7e-10` of the carrier, so\nthe two agree to a few parts in ten to the tenth.\n\nReturns `None` when the range rate is not finite, or when its magnitude\nreaches the speed of light, where the expression has no real value.", + "doc_zh": "返回某距离变化率的相对论多普勒因子。\n\n该因子为 `sqrt((1 - beta) / (1 + beta))`,其中 `beta = v_r / c`。把\n发射频率乘以它便给出对 `beta` 各阶都正确的接收频率,故它不是对经典\n频移的小修正,而是经典频移所近似的量。\n\n其展开为 `1 - beta + beta^2 / 2 - ...`。前两项是经典结果,它在本身为\n二阶的因子之内即 `1 - beta`;其余项是两种处理之差。对轨道速度的\n卫星,`beta` 约为 `2.3e-5`,二阶项约为载波的 `2.7e-10`,故两者相符\n到十的十次方分之几。\n\n距离变化率非有限,或其幅度达到光速(此时该式无实值)时返回 `None`。", "associated": false }, { @@ -5336,6 +6092,7 @@ "name": "DopplerShift", "signature": "pub struct DopplerShift {", "doc": "A Doppler shift applied to a carrier.\n\nThe type carries the carrier frequency and the range rate, so the\ntransmitted and received frequencies and the shift itself are all\navailable without ambiguity about which sign convention was used.", + "doc_zh": "施加于载波的多普勒频移。\n\n该类型带有载波频率与距离变化率,故发射频率、接收频率以及频移本身都\n可得,而不必对所用符号约定存疑。", "associated": false }, { @@ -5343,6 +6100,7 @@ "name": "from_range_rate", "signature": "pub fn from_range_rate(carrier_frequency: f64, range_rate: f64) -> Option {", "doc": "Creates a shift from a carrier frequency and a range rate.\n\nReturns `None` when the frequency is not positive and finite, or when\nthe range rate is not finite.", + "doc_zh": "由载波频率与距离变化率创建频移。\n\n频率非正且非有限,或距离变化率非有限时返回 `None`。", "associated": true }, { @@ -5350,6 +6108,7 @@ "name": "carrier_frequency", "signature": "pub const fn carrier_frequency(self) -> f64 {", "doc": "Returns the transmitted frequency, in hertz.", + "doc_zh": "返回发射频率,单位为赫兹。", "associated": true }, { @@ -5357,6 +6116,7 @@ "name": "range_rate", "signature": "pub const fn range_rate(self) -> f64 {", "doc": "Returns the range rate, in meters per second, positive when receding.", + "doc_zh": "返回距离变化率,单位为米每秒,远离时为正。", "associated": true }, { @@ -5364,6 +6124,7 @@ "name": "shift", "signature": "pub fn shift(self) -> f64 {", "doc": "Returns the shift, in hertz.\n\nThe value is negative when the terminals are receding.", + "doc_zh": "返回频移,单位为赫兹。\n\n两终端远离时该值为负。", "associated": true }, { @@ -5371,6 +6132,7 @@ "name": "shifted_frequency", "signature": "pub fn shifted_frequency(self) -> f64 {", "doc": "Returns the received frequency, in hertz.", + "doc_zh": "返回接收频率,单位为赫兹。", "associated": true }, { @@ -5378,6 +6140,7 @@ "name": "shift_including_relativity", "signature": "pub fn shift_including_relativity(self) -> Option {", "doc": "Returns the shift including the relativistic clock correction, in\nhertz.\n\nThe received frequency is the carrier multiplied by the relativistic\nfactor, so the shift is `f_0 (factor - 1)`.\n\nThis is not the classical shift plus a small correction: the factor\nalready contains the first order term, and its expansion is\n`1 - beta + beta^2 / 2 - ...`, whose leading term is exactly the\nclassical shift. The classical value is the first order\napproximation to this one, and the two differ only by the second and\nhigher order terms, of order `beta^2`, which for a satellite is a few\nparts in ten to the tenth of the carrier.\n\nThe factor is evaluated first and the carrier multiplied afterwards,\nbecause the difference from one is tiny and would lose precision if\nthe shift were added to the carrier before the correction.\n\nReturns `None` when the range rate reaches the speed of light.", + "doc_zh": "返回含相对论钟改正的频移,单位为赫兹。\n\n接收频率是载波乘以相对论因子,故频移为 `f_0 (factor - 1)`。\n\n这不是经典频移加一个小修正:该因子已含一阶项,其展开为\n`1 - beta + beta^2 / 2 - ...`,其首项恰是经典频移。经典值是对它的\n一阶近似,两者只差二阶及更高阶项,量级为 `beta^2`,对卫星而言是\n载波的十的十次方分之几。\n\n先求因子、后乘载波,因为它与一之差极小,若在改正之前把频移加到\n载波上会损失精度。\n\n距离变化率达到光速时返回 `None`。", "associated": true }, { @@ -5385,6 +6148,7 @@ "name": "shift_in_ppm", "signature": "pub fn shift_in_ppm(self) -> f64 {", "doc": "Returns the shift in parts per million of the carrier.\n\nThis is a convenient way to compare the shift of links on different\ncarriers.", + "doc_zh": "返回频移,以载波的百万分率计。\n\n这是比较位于不同载波上的链路频移的便利方式。", "associated": true }, { @@ -5392,6 +6156,7 @@ "name": "shift_rate", "signature": "pub fn shift_rate(self, range_acceleration: f64) -> Option {", "doc": "Returns the rate of change of the shift, in hertz per second.\n\nThis is what a Doppler tracker must follow, and it follows from the\nrange acceleration.\n\nReturns `None` when the acceleration is not finite.", + "doc_zh": "返回频移的变化率,单位为赫兹每秒。\n\n这是多普勒跟踪器必须跟随的量,它由距离加速度得出。\n\n加速度非有限时返回 `None`。", "associated": true }, { @@ -5399,6 +6164,7 @@ "name": "is_receding", "signature": "pub fn is_receding(self) -> bool {", "doc": "Returns `true` when the shift is a redshift, meaning the terminals\nare receding and the received frequency is lower.", + "doc_zh": "当该频移是红移(即两终端远离、接收频率更低)时返回 `true`。", "associated": true }, { @@ -5406,6 +6172,7 @@ "name": "angle_from_range_rate", "signature": "pub fn angle_from_range_rate(velocity: Vec3, range_rate: f64) -> Option {", "doc": "Returns the angle between the line of sight and the velocity, in radians.\n\nThis is the complement of the usual elevation angle within the plane\ncontaining both, and it is useful when the range rate is known and the\nvelocity from the orbit is known: the angle follows from the ratio of the\ntwo.\n\nThe result lies in `[0, pi]`.\n\nReturns `None` when the velocity has zero length or either argument is\nnot finite.", + "doc_zh": "返回视线与速度之间的夹角,单位为弧度。\n\n它是二者所在平面内通常仰角的余角,当已知距离变化率且已知来自轨道的\n速度时有用:该角由二者之比得出。\n\n结果落在 `[0, pi]` 内。\n\n速度为零长或任一参数非有限时返回 `None`。", "associated": false }, { @@ -5413,6 +6180,7 @@ "name": "maximum_shift", "signature": "pub fn maximum_shift(carrier_frequency: f64, speed: f64) -> Option {", "doc": "Returns the largest possible shift magnitude for a carrier and a speed,\nin hertz.\n\nThis is the shift when the motion is entirely along the line of sight,\nwhich is the width of the acquisition range a receiver must cover. It is\nhalf the total span between the approach and the recession extremes.\n\nThe result is returned as a positive magnitude. Returns `None` when the\ncarrier or speed is not positive and finite.", + "doc_zh": "返回某载波与某速度下可能的最大频移幅度,单位为赫兹。\n\n这是运动完全沿视线时的频移,即接收机必须覆盖的捕获范围宽度。它是在\n接近与远离两个极端之间的总跨度的一半。\n\n结果以正幅度返回。载波或速度非正且非有限时返回 `None`。", "associated": false }, { @@ -5420,6 +6188,7 @@ "name": "acquisition_span", "signature": "pub fn acquisition_span(carrier_frequency: f64, speed: f64) -> Option {", "doc": "Returns the total span of frequencies a receiver must accept to cover\nevery range rate up to the given speed.\n\nApproach and recession give shifts of opposite sign, so the span is twice\nthe maximum shift.\n\nReturns `None` when the carrier or speed is not usable.", + "doc_zh": "返回接收机为覆盖直到给定速度的每个距离变化率所必须接受的总频率跨度。\n\n接近与远离给出符号相反的频移,故该跨度是最大频移的两倍。\n\n载波或速度不可用时返回 `None`。", "associated": false } ] @@ -5427,13 +6196,15 @@ { "name": "apsis-atmosphere", "layer": "rf", - "summary": "Atmospheric attenuation and refraction models. A satellite signal passing through the atmosphere is weakened by two mechanisms that are treated separately because they behave differently with frequency, elevation, and weather. Gaseous absorption comes from the absorption lines of oxygen and water vapour. Oxygen has a dense group of lines between 50 and 70 GHz and an isolated line at 118.75 GHz, and water vapour has a strong line at 22.235 GHz and a much stronger one at 183.31 GHz. The attenuation is the line-by-line sum over those lines, which makes it negligible below about 10 GHz and severe in the absorption bands. Rain attenuation is caused by scattering and absorption in precipitation. It is negligible below about 4 GHz, grows as a power of the rain rate, and depends strongly on elevation and polarisation. Both are functions of the elevation angle through the same path geometry: a signal at a low elevation crosses more atmosphere. Sources: - Gaseous specific attenuation and equivalent heights: ITU-R Recommendation P.676-12, *Attenuation by atmospheric gases*. - Rain specific attenuation coefficients: ITU-R Recommendation P.838-3, *Specific attenuation model for rain for use in prediction methods*. - Rain path and elevation dependence: ITU-R Recommendation P.618-13, *Propagation data and prediction methods required for the design of Earth-space telecommunication systems*. ``` use apsis_atmosphere::{Atmosphere, slant_path_attenuation, zenith_attenuation}; // At the reference atmosphere the 60 GHz oxygen complex is heavily // attenuated while a Ku band link is barely affected. let ku = zenith_attenuation(12.0, Atmosphere::REFERENCE).expect(\"valid\"); let oxygen = zenith_attenuation(60.0, Atmosphere::REFERENCE).expect(\"valid\"); assert!(ku < 0.1); assert!(oxygen > 100.0); // A lower elevation crosses more atmosphere. let zenith = zenith_attenuation(12.0, Atmosphere::REFERENCE).expect(\"valid\"); let low = slant_path_attenuation(12.0, 0.2, Atmosphere::REFERENCE).expect(\"valid\"); assert!(low > zenith); ```", + "summary": "Atmospheric attenuation and refraction models.\n\nA satellite signal passing through the atmosphere is weakened by two\nmechanisms that are treated separately because they behave differently\nwith frequency, elevation, and weather.\n\nGaseous absorption comes from the absorption lines of oxygen and water\nvapour. Oxygen has a dense group of lines between 50 and 70 GHz and an\nisolated line at 118.75 GHz, and water vapour has a strong line at\n22.235 GHz and a much stronger one at 183.31 GHz. The attenuation is the\nline-by-line sum over those lines, which makes it negligible below about\n10 GHz and severe in the absorption bands.\n\nRain attenuation is caused by scattering and absorption in\nprecipitation. It is negligible below about 4 GHz, grows as a power of\nthe rain rate, and depends strongly on elevation and polarisation.\n\nBoth are functions of the elevation angle through the same path\ngeometry: a signal at a low elevation crosses more atmosphere.\n\nSources:\n\n- Gaseous specific attenuation and equivalent heights: ITU-R\n Recommendation P.676-12, *Attenuation by atmospheric gases*.\n- Rain specific attenuation coefficients: ITU-R Recommendation P.838-3,\n *Specific attenuation model for rain for use in prediction methods*.\n- Rain path and elevation dependence: ITU-R Recommendation P.618-13,\n *Propagation data and prediction methods required for the design of\n Earth-space telecommunication systems*.\n\n```\nuse apsis_atmosphere::{Atmosphere, slant_path_attenuation, zenith_attenuation};\n\n// At the reference atmosphere the 60 GHz oxygen complex is heavily\n// attenuated while a Ku band link is barely affected.\nlet ku = zenith_attenuation(12.0, Atmosphere::REFERENCE).expect(\"valid\");\nlet oxygen = zenith_attenuation(60.0, Atmosphere::REFERENCE).expect(\"valid\");\nassert!(ku < 0.1);\nassert!(oxygen > 100.0);\n\n// A lower elevation crosses more atmosphere.\nlet zenith = zenith_attenuation(12.0, Atmosphere::REFERENCE).expect(\"valid\");\nlet low = slant_path_attenuation(12.0, 0.2, Atmosphere::REFERENCE).expect(\"valid\");\nassert!(low > zenith);\n```", + "summary_zh": "大气衰减与折射模型。\n\n穿过大气的卫星信号被两种机制削弱,它们被分开处理,因为其行为随频率、\n仰角与天气而异。\n\n气体吸收来自氧与水汽的吸收谱线。氧在 50 与 70 GHz 之间有一组密集谱线,\n并在 118.75 GHz 处有一条孤立线;水汽在 22.235 GHz 处有一条强线,在\n183.31 GHz 处有一条强得多的线。衰减即对上述各线的逐线求和,这使它在\n约 10 GHz 以下可忽略,而在各吸收带中很严重。\n\n降雨衰减由降水中的散射与吸收造成。它在约 4 GHz 以下可忽略,随降雨率的\n幂次增大,并强烈依赖仰角与极化。\n\n二者都经相同的路径几何依赖仰角:低仰角的信号穿过更多大气。\n\n出处:\n\n- 气体比衰减与等效高度:ITU-R 建议书 P.676-12,*Attenuation by\n atmospheric gases*。\n- 降雨比衰减系数:ITU-R 建议书 P.838-3,*Specific attenuation model for\n rain for use in prediction methods*。\n- 降雨路径与仰角依赖:ITU-R 建议书 P.618-13,*Propagation data and\n prediction methods required for the design of Earth-space\n telecommunication systems*。", "items": [ { "kind": "struct", "name": "Atmosphere", "signature": "pub struct Atmosphere {", "doc": "The atmospheric state that the gaseous attenuation depends on.\n\nThe pressure is the dry air pressure, as the recommendation specifies. The\nwater vapour density is separate because the partial pressure of water\nvapour is derived from it.", + "doc_zh": "气体衰减所依赖的大气状态。\n\n气压是干空气气压,如建议书所规定。水汽密度单列,因为水汽分压由它\n导出。", "associated": false }, { @@ -5441,6 +6212,7 @@ "name": "REFERENCE", "signature": "pub const REFERENCE: Self = Self {", "doc": "The ITU-R reference atmosphere at mean sea level.\n\nThe values are the ones the recommendation uses for its reference\ncurves: a dry air pressure of 1013.25 hPa, a water vapour density of\n7.5 g/m^3, and a temperature of 288.15 K.", + "doc_zh": "平均海平面处的 ITU-R 参考大气。\n\n各值即该建议书用于其参考曲线者:干空气气压 1013.25 hPa,水汽\n密度 7.5 g/m^3,温度 288.15 K。", "associated": true }, { @@ -5448,6 +6220,7 @@ "name": "new", "signature": "pub const fn new(pressure_hpa: f64, water_vapour_density: f64, temperature_k: f64) -> Self {", "doc": "Creates an atmosphere from its three parameters.", + "doc_zh": "由其三个参数创建大气。", "associated": true }, { @@ -5455,6 +6228,7 @@ "name": "water_vapour_partial_pressure", "signature": "pub fn water_vapour_partial_pressure(self) -> f64 {", "doc": "Returns the partial pressure of water vapour, in hectopascals.\n\nThe relation is `e = rho T / 216.7`, which follows from the ideal gas\nlaw with the molar masses of water and dry air.", + "doc_zh": "返回水汽分压,以百帕计。\n\n关系为 `e = rho T / 216.7`,它由理想气体定律连同水与干空气的摩尔\n质量得出。", "associated": true }, { @@ -5462,6 +6236,7 @@ "name": "is_valid", "signature": "pub fn is_valid(self) -> bool {", "doc": "Returns `true` when the state describes a physical atmosphere.", + "doc_zh": "当该状态描述一个物理大气时返回 `true`。", "associated": true }, { @@ -5469,6 +6244,7 @@ "name": "specific_attenuation_dry_air", "signature": "pub fn specific_attenuation_dry_air(frequency_ghz: f64, atmosphere: Atmosphere) -> Option {", "doc": "Returns the specific attenuation of dry air, in decibels per kilometer.\n\nThis is the oxygen contribution, computed by the line-by-line sum of\nequation 1 of ITU-R P.676-12, plus the non-resonant Debye term that\naccounts for the far wings of all the lines together.\n\nThe frequency is in gigahertz.\n\nReturns `None` when the frequency or atmosphere is not usable.", + "doc_zh": "返回干空气的比衰减,单位为分贝每千米。\n\n这是氧的贡献,由 ITU-R P.676-12 式 1 的逐线求和计算,加上计入所有谱线远翼\n合在一起的非共振 Debye 项。\n\n频率单位为吉赫。\n\n频率或大气不可用时返回 `None`。", "associated": false }, { @@ -5476,6 +6252,7 @@ "name": "specific_attenuation_water_vapour", "signature": "pub fn specific_attenuation_water_vapour( frequency_ghz: f64, atmosphere: Atmosphere, ) -> Option {", "doc": "Returns the specific attenuation of water vapour, in decibels per\nkilometer.\n\nThis is the water vapour contribution, computed by the line-by-line sum of\nequation 1 of ITU-R P.676-12.\n\nThe frequency is in gigahertz.\n\nReturns `None` when the frequency or atmosphere is not usable.", + "doc_zh": "返回水汽的比衰减,单位为分贝每千米。\n\n这是水汽的贡献,由 ITU-R P.676-12 式 1 的逐线求和计算。\n\n频率单位为吉赫。\n\n频率或大气不可用时返回 `None`。", "associated": false }, { @@ -5483,6 +6260,7 @@ "name": "specific_attenuation", "signature": "pub fn specific_attenuation(frequency_ghz: f64, atmosphere: Atmosphere) -> Option {", "doc": "Returns the total specific attenuation of the atmosphere, in decibels per\nkilometer.\n\nThis is the sum of the dry air and water vapour contributions.\n\nReturns `None` when the frequency or atmosphere is not usable.", + "doc_zh": "返回大气的总比衰减,单位为分贝每千米。\n\n这是干空气与水汽贡献之和。\n\n频率或大气不可用时返回 `None`。", "associated": false }, { @@ -5490,20 +6268,23 @@ "name": "EquivalentHeights", "signature": "pub struct EquivalentHeights {", "doc": "The equivalent height of dry air and of water vapour, in kilometers.", + "doc_zh": "干空气与水汽的等效高度,单位为千米。", "associated": false }, { "kind": "fn", "name": "equivalent_heights", "signature": "pub fn equivalent_heights(frequency_ghz: f64, atmosphere: Atmosphere) -> Option {", - "doc": "Returns the equivalent heights of dry air and water vapour.\n\nThe heights are the lengths that, multiplied by the surface specific\nattenuation, give the zenith attenuation. The dry air height is limited\nto a pressure dependent bound below 70 GHz, where the oxygen complex is\nnarrow enough that the height would otherwise be overestimated.\n\nThe frequency is in gigahertz.\n\nReturns `None` when the frequency or atmosphere is not usable.\n\nSource: ITU-R Recommendation P.676-12, equations 30 through 38.", + "doc": "Returns the equivalent heights of dry air and water vapour.\n\nThe heights are the lengths that, multiplied by the surface specific\nattenuation, give the zenith attenuation. The dry air height is limited\nto a pressure dependent bound below 70 GHz, where the oxygen complex is\nnarrow enough that the height would otherwise be overestimated.\n\nThe frequency is in gigahertz.", + "doc_zh": "频率单位为吉赫。\n\nReturns `None` when the frequency or atmosphere is not usable.\n\n返回干空气与水汽的等效高度。\n\n各高度是这样的长度:乘以地表比衰减即给出天顶衰减。干空气高度在 70 GHz\n以下被限制到一个依赖气压的界,那里氧复合体足够窄,否则该高度会被高估。\n\n频率单位为吉赫。\n\n频率或大气不可用时返回 `None`。\n\n出处:ITU-R 建议书 P.676-12,式 30 至式 38。", "associated": false }, { "kind": "fn", "name": "zenith_attenuation", "signature": "pub fn zenith_attenuation(frequency_ghz: f64, atmosphere: Atmosphere) -> Option {", - "doc": "Returns the zenith attenuation of the atmosphere, in decibels.\n\nThis is the specific attenuation at the surface multiplied by the\nequivalent height, summed over the two constituents.\n\nThe frequency is in gigahertz.\n\nReturns `None` when the frequency or atmosphere is not usable.", + "doc": "Returns the zenith attenuation of the atmosphere, in decibels.\n\nThis is the specific attenuation at the surface multiplied by the\nequivalent height, summed over the two constituents.\n\nThe frequency is in gigahertz.", + "doc_zh": "频率单位为吉赫。\n\nReturns `None` when the frequency or atmosphere is not usable.\n\n返回大气的天顶衰减,单位为分贝。\n\n这是地表比衰减乘以等效高度,并对两个成分求和。\n\n频率单位为吉赫。\n\n频率或大气不可用时返回 `None`。", "associated": false }, { @@ -5511,6 +6292,7 @@ "name": "slant_path_attenuation", "signature": "pub fn slant_path_attenuation( frequency_ghz: f64, elevation: f64, atmosphere: Atmosphere, ) -> Option {", "doc": "Returns the attenuation along a slant path at an elevation angle, in\ndecibels.\n\nThe zenith attenuation is divided by the sine of the elevation. The\nplane parallel approximation this uses is the one the recommendation\napplies for the approximate method; it is accurate above about five\ndegrees of elevation.\n\nThe elevation is in radians.\n\nReturns `None` when the frequency or atmosphere is not usable, or when\nthe elevation is at or below the horizon.", + "doc_zh": "返回某仰角下沿斜路径的衰减,单位为分贝。\n\n天顶衰减除以仰角的正弦。其使用的平面平行近似即建议书用于近似方法\n者;在约五度仰角以上是准确的。\n\n仰角以弧度计。\n\n频率或大气不可用,或仰角在地平及其以下时返回 `None`。", "associated": false }, { @@ -5518,6 +6300,7 @@ "name": "slant_path_attenuation_exact", "signature": "pub fn slant_path_attenuation_exact( frequency_ghz: f64, elevation: f64, atmosphere: Atmosphere, ) -> Option {", "doc": "Returns the attenuation along a slant path by the exact method of\nRecommendation P.676.\n\nThe exact method divides the atmosphere into a stack of layers whose\nthickness grows exponentially with height, evaluates the specific\nattenuation in each, and sums the contribution of each layer weighted by\nthe ray path length through it. Because the ray is refracted, the path\nlength is found by ray tracing with Snell's law applied to the ratio of\nthe refractive indices of adjacent layers, which the approximate method\nignores.\n\nThis is the form to use at low elevation, where the plane parallel\napproximation that [`slant_path_attenuation`] uses is inaccurate. It is\nfar more expensive, so the approximate form remains available for the\nhigh elevation case where it is adequate.\n\nThe frequency is in gigahertz, the elevation in radians, and the surface\natmosphere supplies the pressure, the water vapour density, and the\ntemperature at ground level. The surface water vapour density scales the\nexponential vapour profile, so the atmosphere defines the whole stack.\n\nReturns `None` when the frequency, elevation, or atmosphere is not\nusable.\n\nSource: ITU-R Recommendation P.676-12, equations 13 through 19, with the\nlayered atmosphere of ITU-R Recommendation P.835-6.", + "doc_zh": "以建议书 P.676 的精确方法返回沿斜路径的衰减。\n\n精确方法把大气分成厚度随高度指数增长的一叠层,计算每层的比衰减,\n并按射线穿过该层的路径长度对每层的贡献加权求和。由于射线发生折射,\n路径长度以射线追踪求得,其中对相邻层折射率之比施加 Snell 定律,而\n近似方法忽略这一点。\n\n这是低仰角下宜用的形式,此时 [`slant_path_attenuation`] 所用的平面\n平行近似不准确。它的代价高得多,故近似形式仍可供其足够胜任的高仰角\n情形使用。\n\n频率以吉赫兹计,仰角以弧度计,地表大气提供地面处的气压、水汽密度与\n温度。地表水汽密度缩放指数水汽廓线,故该大气界定了整叠层。\n\n频率、仰角或大气不可用时返回 `None`。\n\n出处:ITU-R 建议书 P.676-12,式 13 至式 19,分层大气取自 ITU-R\n建议书 P.835-6。", "associated": false }, { @@ -5525,6 +6308,7 @@ "name": "elevation_from_zenith", "signature": "pub fn elevation_from_zenith(zenith_angle: f64) -> f64 {", "doc": "Returns the elevation angle for a zenith angle.\n\nThe elevation is measured from the horizon and the zenith angle from the\nvertical, so the two add to a right angle.", + "doc_zh": "返回某天顶角对应的仰角。\n\n仰角自地平量起,天顶角自垂线量起,故两者相加为直角。", "associated": false }, { @@ -5532,6 +6316,7 @@ "name": "zenith_to_elevation", "signature": "pub fn zenith_to_elevation(elevation: f64) -> f64 {", "doc": "Returns the zenith angle for an elevation angle.", + "doc_zh": "返回某仰角对应的天顶角。", "associated": false }, { @@ -5539,6 +6324,7 @@ "name": "air_mass", "signature": "pub fn air_mass(elevation: f64) -> f64 {", "doc": "Returns the air mass along a slant path at the given elevation angle.\n\nThe air mass is the ratio of the path length through a flat atmosphere to\nthe vertical path length. For a plane parallel atmosphere it is the\ncosecant of the elevation,\n\n```text\nm = 1 / sin(elevation)\n```\n\nso the value is one at the zenith and grows without bound at the horizon.\nThe plane parallel assumption fails below about ten degrees elevation,\nwhere the curvature of the Earth shortens the path compared with this\nexpression and the true air mass is smaller.\n\nThe value is not clamped at low elevation. A caller that needs a finite\nbound near the horizon should impose one; this function reports the plane\nparallel geometry it models.\n\nReturns infinity at or below the horizon, where no direct path exists.", + "doc_zh": "返回给定仰角下沿斜路径的大气质量。\n\n大气质量是穿过平坦大气的路径长度与垂直路径长度之比。对平面平行\n大气它是仰角的余割,\n\n```text\nm = 1 / sin(elevation)\n```\n\n故在天顶处为一,在地平处无界增长。平面平行假定在约十度仰角以下\n失效,那里地球曲率使路径比该式更短,真实大气质量更小。\n\n该值在低仰角处不被截断。需要在地平附近取有限上界的调用方应自行\n施加;本函数报告它所建模的平面平行几何。\n\n在地平及其以下不存在直接路径,返回无穷。", "associated": false }, { @@ -5546,6 +6332,7 @@ "name": "geopotential_height", "signature": "pub fn geopotential_height(geometric_height_km: f64) -> f64 {", "doc": "Returns the geopotential height for a geometric height, in kilometers.\n\nThe relation is\n\n```text\nh_p = R H / (R + H)\n```\n\nwith `R` the fixed Earth radius of the standard atmosphere and `H` the\ngeometric height. The geopotential height is always the smaller of the\ntwo.", + "doc_zh": "返回某几何高度对应的势高,单位为千米。\n\n关系为\n\n```text\nh_p = R H / (R + H)\n```\n\n其中 `R` 是标准大气固定的地球半径,`H` 是几何高度。势高总是两者中较\n小者。", "associated": false }, { @@ -5553,6 +6340,7 @@ "name": "standard_temperature", "signature": "pub fn standard_temperature(geometric_height_km: f64) -> f64 {", "doc": "Returns the temperature of the reference atmosphere, in kelvin.\n\nThe profile is piecewise linear in geopotential height up to 84.852 km\nand is then defined by two further segments that extend to 100 km. Above\n100 km the recommendation does not define a global profile and this\nfunction returns the value at 100 km.\n\nThe geometric height is in kilometers.", + "doc_zh": "返回参考大气的温度,单位为开尔文。\n\n该廓线在势高度 84.852 km 以下分段线性,其后由延伸至 100 km 的两段定义。\n100 km 以上该建议书不定义全球廓线,本函数返回 100 km 处的值。\n\n几何高度单位为千米。", "associated": false }, { @@ -5560,6 +6348,7 @@ "name": "standard_pressure", "signature": "pub fn standard_pressure(geometric_height_km: f64) -> f64 {", "doc": "Returns the pressure of the reference atmosphere, in hectopascals.\n\nThe profile is piecewise, continuous at the segment boundaries, and\nfollows from the hydrostatic relation applied to each temperature\nsegment.\n\nThe geometric height is in kilometers.", + "doc_zh": "返回参考大气的气压,单位为百帕。\n\n该廓线是分段的,在各分段边界处连续,由静力关系施加于每个温度分段得出。\n\n几何高度单位为千米。", "associated": false }, { @@ -5567,6 +6356,7 @@ "name": "standard_water_vapour_density", "signature": "pub fn standard_water_vapour_density(geometric_height_km: f64, surface_density: f64) -> f64 {", "doc": "Returns the water vapour density of the reference atmosphere, in grams\nper cubic meter.\n\nThe profile is exponential with a scale height of two kilometers, scaled\nso that the density at the surface is the value given.\n\nThe geometric height is in kilometers.", + "doc_zh": "返回参考大气的水汽密度,单位为克每立方米。\n\n该廓线是指数式的,标高两千米,并缩放使地表的密度为所给值。\n\n几何高度单位为千米。", "associated": false }, { @@ -5574,6 +6364,7 @@ "name": "radio_refractive_index", "signature": "pub fn radio_refractive_index( dry_pressure_hpa: f64, vapour_pressure_hpa: f64, temperature_k: f64, ) -> f64 {", "doc": "Returns the radio refractive index of moist air, dimensionless.\n\nThe relation is\n\n```text\nN = 77.6 P_d / T + 72 e / T + 3.75e5 e / T^2\nn = 1 + N 1e-6\n```\n\nwith `P_d` the dry air pressure and `e` the water vapour partial\npressure, both in hectopascals, and `T` the temperature in kelvin. The\nfirst two terms are the non-dispersive refractivity and the third is the\ndipole term that gives water vapour its strong effect.\n\nSource: ITU-R Recommendation P.453, which gives this expression for the\nrefractive index of moist air.", + "doc_zh": "返回湿空气的无线电折射率,无量纲。\n\n关系为\n\n```text\nN = 77.6 P_d / T + 72 e / T + 3.75e5 e / T^2\nn = 1 + N 1e-6\n```\n\n其中 `P_d` 是干空气气压,`e` 是水汽分压,两者均以百帕计;`T` 是\n温度,以开尔文计。前两项是非色散折射率,第三项是使水汽产生强烈效应\n的偶极项。\n\n出处:ITU-R 建议书 P.453,它给出湿空气折射率的这一表达式。", "associated": false }, { @@ -5581,6 +6372,7 @@ "name": "RAIN_HEIGHT_OFFSET_KM", "signature": "pub const RAIN_HEIGHT_OFFSET_KM: f64 = 0.36;", "doc": "The offset added to the isotherm height to obtain the rain height, in\nkilometers.\n\nThe recommendation states that the rain height is the isotherm height\nplus 0.36 km, which represents the depth of the melting layer below the\nisotherm.", + "doc_zh": "加到等温线高度上以获得雨顶高度的偏移,单位为千米。\n\n建议书指出雨顶高度是等温线高度加 0.36 km,它代表等温线以下融化层的\n深度。", "associated": false }, { @@ -5588,6 +6380,7 @@ "name": "zero_isotherm_height", "signature": "pub fn zero_isotherm_height(latitude: f64, longitude: f64) -> Option {", "doc": "Returns the height of the zero degree isotherm at a location, in\nkilometers above mean sea level.\n\nThe latitude is in degrees, positive north, and the longitude in degrees,\npositive east. The value is interpolated bilinearly from the tabulated\ngrid, and longitudes are reduced into `[0, 360)` first so that any\nconvention is accepted.\n\nReturns `None` when either coordinate is not finite, or when the latitude\nis outside the range of the grid.\n\nSource: ITU-R Recommendation P.839-4, equation 1 and the tabulated\nisotherm height.", + "doc_zh": "返回某位置零度等温线的高度,以平均海平面以上千米计。\n\n纬度以度计、北正;经度以度计、东正。该值由列表网格双线性插值得到,\n经度先归约到 `[0, 360)`,故任何约定都被接受。\n\n任一坐标非有限,或纬度超出网格范围时返回 `None`。\n\n出处:ITU-R 建议书 P.839-4,式 1 及所列等温线高度。", "associated": false }, { @@ -5595,6 +6388,7 @@ "name": "rain_height", "signature": "pub fn rain_height(latitude: f64, longitude: f64) -> Option {", "doc": "Returns the rain height at a location, in kilometers above mean sea level.\n\nThis is the zero degree isotherm height plus\n[`RAIN_HEIGHT_OFFSET_KM`], as the recommendation defines it.\n\nReturns `None` when the coordinates are not usable.", + "doc_zh": "返回某位置的雨顶高度,以平均海平面以上千米计。\n\n它是零度等温线高度加 [`RAIN_HEIGHT_OFFSET_KM`],如建议书所定义。\n\n坐标不可用时返回 `None`。", "associated": false }, { @@ -5602,6 +6396,7 @@ "name": "grid_step_degrees", "signature": "pub const fn grid_step_degrees() -> f64 {", "doc": "Returns the grid resolution of the tabulated isotherm height, in degrees.", + "doc_zh": "返回列表等温线高度的网格分辨率,单位为度。", "associated": false }, { @@ -5609,6 +6404,7 @@ "name": "DEFAULT_RAIN_HEIGHT_KM", "signature": "pub const DEFAULT_RAIN_HEIGHT_KM: f64 = 3.0;", "doc": "A mid latitude rain height, in kilometers above sea level.\n\nRecommendation P.618 takes the rain height from the zero degree isotherm\nheight of Recommendation P.839, which is tabulated as a function of\nlatitude and longitude. Prefer [`rain_attenuation_at`], which looks the\nvalue up, over [`rain_attenuation`] with this constant.", + "doc_zh": "中纬度雨顶高度,以海拔千米计。\n\n建议书 P.618 从建议书 P.839 的零度等温线高度取雨顶高度,后者按纬度\n与经度列表给出。相较用本常量调用 [`rain_attenuation`],更宜用会查表\n的 [`rain_attenuation_at`]。", "associated": false }, { @@ -5616,6 +6412,7 @@ "name": "RainAttenuation", "signature": "pub struct RainAttenuation {", "doc": "A rain attenuation result at a given elevation.", + "doc_zh": "给定仰角下的降雨衰减结果。", "associated": false }, { @@ -5623,6 +6420,7 @@ "name": "attenuation", "signature": "pub fn attenuation(self) -> Decibel {", "doc": "Returns the total attenuation.", + "doc_zh": "返回总衰减。", "associated": true }, { @@ -5630,6 +6428,7 @@ "name": "rain_coefficients", "signature": "pub fn rain_coefficients(frequency: f64, polarisation: f64, elevation: f64) -> Option<(f64, f64)> {", "doc": "Returns the P.838-3 coefficients of the specific attenuation power law.\n\nThe polarisation is an angle in radians between the electric field and\nthe horizontal, so zero is horizontal and a right angle is vertical. The\nelevation is in radians.\n\nThe result is a pair of the coefficient `k` and the exponent `alpha` of\nthe relation `gamma = k R^alpha`, with the rain rate in millimeters per\nhour and the result in decibels per kilometer.\n\nReturns `None` when the frequency, polarisation, or elevation is not\nusable. The recommendation is stated for frequencies from 1 GHz to\n1000 GHz.\n\nSource: ITU-R Recommendation P.838-3, equations 2 and 3.", + "doc_zh": "返回比衰减幂律的 P.838-3 系数。\n\n极化是电场与水平方向之间以弧度计的角度,故零为水平、直角为垂直。\n仰角以弧度计。\n\n结果是关系 `gamma = k R^alpha` 中系数 `k` 与指数 `alpha` 的一对值,\n其中降雨率以毫米每小时计,结果以分贝每千米计。\n\n频率、极化或仰角不可用时返回 `None`。该建议书针对 1 GHz 至 1000 GHz\n的频率陈述。\n\n出处:ITU-R 建议书 P.838-3,式 2 与式 3。", "associated": false }, { @@ -5637,6 +6436,7 @@ "name": "specific_rain_attenuation", "signature": "pub fn specific_rain_attenuation( rain_rate: f64, frequency: f64, polarisation: f64, elevation: f64, ) -> Option {", "doc": "Returns the specific attenuation in rain, in decibels per kilometer.\n\nThe rain rate is in millimeters per hour.\n\nReturns `None` when the rain rate is negative, or when the coefficients\nare not available.", + "doc_zh": "返回雨中的比衰减,单位为分贝每千米。\n\n降雨率以毫米每小时计。\n\n降雨率为负,或系数不可用时返回 `None`。", "associated": false }, { @@ -5644,6 +6444,7 @@ "name": "rain_attenuation", "signature": "pub fn rain_attenuation( rain_rate: f64, frequency: f64, polarisation: f64, elevation: f64, station_height_km: f64, rain_height_km: f64, latitude: f64, ) -> Option {", "doc": "Returns the total rain attenuation along a slant path.\n\nThe rain rate is the value exceeded for the percentage of time of\ninterest, in millimeters per hour. The station height above sea level and\nthe rain layer height are in kilometers; pass [`DEFAULT_RAIN_HEIGHT_KM`]\nfor the latter when no local value is available.\n\nThe method is the one of Recommendation P.618. The slant path length\nfollows from the rain height, the station height, and the elevation, with\na correction for the curvature of the Earth. Two reduction factors then\naccount for the non-uniformity of the rain: a horizontal factor that\ndepends on the specific attenuation, the frequency, and the horizontal\nprojection of the path, and a vertical factor that depends on the\nlatitude through the angle at which the path crosses the rain layer.\n\nReturns `None` when the rain rate, frequency, or elevation is not usable,\nwhen the station is above the rain layer, or when the elevation is at or\nbelow the horizon.\n\nSource: ITU-R Recommendation P.618-13, equations 1 through 10.", + "doc_zh": "返回沿斜路径的总降雨衰减。\n\n降雨率是在所关注时间百分比上被超过的值,以毫米每小时计。站的海拔\n高度与雨层高度以千米计;没有本地值时,后者传\n[`DEFAULT_RAIN_HEIGHT_KM`]。\n\n方法即建议书 P.618 所述。斜路径长度由雨顶高度、站高与仰角得出,\n并作地球曲率修正。随后两个缩减因子计入降雨的不均匀性:一个水平\n因子,取决于比衰减、频率以及路径的水平投影;一个垂直因子,通过\n路径穿越雨层的角度取决于纬度。\n\n降雨率、频率或仰角不可用,站高于雨层,或仰角在地平及其以下时返回\n`None`。\n\n出处:ITU-R 建议书 P.618-13,式 1 至式 10。", "associated": false }, { @@ -5651,6 +6452,7 @@ "name": "polarisation_tilt", "signature": "pub const fn polarisation_tilt(degrees_from_horizontal: f64) -> f64 {", "doc": "Returns the angle in radians for a polarisation described by a tilt from\nhorizontal.\n\nThis is a convenience for the two cases that are quoted by name.", + "doc_zh": "返回以相对水平倾角描述的极化所对应的弧度角。\n\n这是为两种常被具名引用的情形提供的便利。", "associated": false }, { @@ -5658,6 +6460,7 @@ "name": "rain_attenuation_at", "signature": "pub fn rain_attenuation_at( rain_rate: f64, frequency: f64, polarisation: f64, elevation: f64, station_height_km: f64, latitude: f64, longitude: f64, ) -> Option {", "doc": "Returns the total rain attenuation along a slant path, taking the rain\nheight from the Recommendation P.839 model.\n\nThis is the same calculation as [`rain_attenuation`], with the rain layer\nheight looked up from the tabulated zero degree isotherm height at the\ngiven latitude and longitude rather than supplied by the caller. It is\nthe form to use unless a locally measured isotherm height is available.\n\nThe station height must be given; pass zero for a station at sea level.\n\nReturns `None` when the coordinates are not usable, or when any of the\nother arguments is rejected by [`rain_attenuation`].\n\nSource: the rain height is from ITU-R Recommendation P.839-4 and the path\nmethod from ITU-R Recommendation P.618-13.", + "doc_zh": "返回沿斜路径的总降雨衰减,雨顶高度取自建议书 P.839 模型。\n\n这与 [`rain_attenuation`] 是同一计算,只是雨层高度由给定纬经度处列表\n给出的零度等温线高度查得,而非由调用方提供。除非有本地实测的等温线\n高度,否则宜用此形式。\n\n站高必须给出;海拔为零的站传零。\n\n坐标不可用,或其他任何参数被 [`rain_attenuation`] 拒绝时返回 `None`。\n\n出处:雨顶高度来自 ITU-R 建议书 P.839-4,路径方法来自 ITU-R 建议书\nP.618-13。", "associated": false } ] @@ -5665,13 +6468,15 @@ { "name": "apsis-noise", "layer": "rf", - "summary": "Noise temperature and noise figure models. Noise enters a receiver from three places: the source, the antenna and its feed, and the receiver itself. They are combined by adding noise temperatures rather than powers, because a temperature is a power referred to a fixed bandwidth through the Boltzmann relation, and a cascade of stages at different gains does not simply add powers. The central relations are: - Thermal noise power in a bandwidth, `P = k T B`, with `k` the Boltzmann constant. - Noise figure as a temperature, `T = T_0 (F - 1)`, with `T_0` the reference temperature of 290 K. - The Friis cascade, which weights each stage's noise by the gain that precedes it. Sources: - The reference temperature of 290 K and the noise figure definition are from the IEEE standard definition of noise figure, IEEE Std 161. - The cascade is the Friis formula for noise, as given in any receiver design text. ``` use apsis_noise::{NoiseTemperature, thermal_noise_power}; // A 36 MHz bandwidth at 290 K carries about -128 dBW of thermal noise. let temperature = NoiseTemperature::from_kelvin(290.0); let power = thermal_noise_power(temperature, 36.0e6).expect(\"valid arguments\"); assert!((power.dbw().expect(\"positive\") + 128.0).abs() < 1.0); ```", + "summary": "Noise temperature and noise figure models.\n\nNoise enters a receiver from three places: the source, the antenna and\nits feed, and the receiver itself. They are combined by adding noise\ntemperatures rather than powers, because a temperature is a power\nreferred to a fixed bandwidth through the Boltzmann relation, and a\ncascade of stages at different gains does not simply add powers.\n\nThe central relations are:\n\n- Thermal noise power in a bandwidth, `P = k T B`, with `k` the Boltzmann\n constant.\n- Noise figure as a temperature, `T = T_0 (F - 1)`, with `T_0` the\n reference temperature of 290 K.\n- The Friis cascade, which weights each stage's noise by the gain that\n precedes it.\n\nSources:\n\n- The reference temperature of 290 K and the noise figure definition are\n from the IEEE standard definition of noise figure, IEEE Std 161.\n- The cascade is the Friis formula for noise, as given in any receiver\n design text.\n\n```\nuse apsis_noise::{NoiseTemperature, thermal_noise_power};\n\n// A 36 MHz bandwidth at 290 K carries about -128 dBW of thermal noise.\nlet temperature = NoiseTemperature::from_kelvin(290.0);\nlet power = thermal_noise_power(temperature, 36.0e6).expect(\"valid arguments\");\nassert!((power.dbw().expect(\"positive\") + 128.0).abs() < 1.0);\n```", + "summary_zh": "噪声温度与噪声系数模型。\n\n噪声从三处进入接收机:源、天线及其馈源,以及接收机自身。它们通过相加噪声\n温度而非功率来合并,因为温度是经玻尔兹曼关系折算到固定带宽的功率,而不同\n增益各级的级联并不能简单地把功率相加。\n\n核心关系是:\n\n- 某带宽内的热噪声功率 `P = k T B`,其中 `k` 是玻尔兹曼常量。\n- 噪声系数作为温度 `T = T_0 (F - 1)`,其中 `T_0` 是 290 K 的参考温度。\n- Friis 级联,它按本级之前的增益对每级的噪声加权。\n\n出处:\n\n- 290 K 的参考温度与噪声系数定义来自 IEEE 噪声系数标准定义,IEEE Std\n 161。\n- 级联即噪声的 Friis 公式,见任何接收机设计教材。", "items": [ { "kind": "struct", "name": "Stage", "signature": "pub struct Stage {", "doc": "One stage of a receiver chain.\n\nA stage is described by the noise it adds, referred to its own input, and\nthe gain it applies. The noise may be given as a temperature or as a\nnoise figure; both are converted to a temperature before the cascade is\nevaluated, because temperatures add across the chain while decibel\nfigures do not.", + "doc_zh": "接收链的一级。\n\n一级由其折算到自身输入所加的噪声与其所施加的增益描述。噪声可以温度或\n噪声系数给出;两者都在评估级联之前换算为温度,因为温度在链中相加而\n分贝系数不然。", "associated": false }, { @@ -5679,6 +6484,7 @@ "name": "new", "signature": "pub const fn new(temperature: NoiseTemperature, gain: Gain) -> Self {", "doc": "Creates a stage from a noise temperature and a gain.", + "doc_zh": "由噪声温度与增益创建一级。", "associated": true }, { @@ -5686,6 +6492,7 @@ "name": "from_noise_figure", "signature": "pub fn from_noise_figure(noise_figure_db: f64, gain_dbi: f64) -> Option {", "doc": "Creates a stage from a noise figure in decibels and a gain in\ndecibels.\n\nReturns `None` when either value is not finite.", + "doc_zh": "由以分贝表示的噪声系数与以分贝表示的增益创建一级。\n\n任一值非有限时返回 `None`。", "associated": true }, { @@ -5693,6 +6500,7 @@ "name": "ideal", "signature": "pub const fn ideal(gain: Gain) -> Self {", "doc": "Creates a noiseless stage with the given gain.\n\nAn ideal amplifier adds no noise, so it contributes nothing to the\ncascade except its gain.", + "doc_zh": "以给定增益创建无噪声的一级。\n\n理想放大器不加噪声,故除其增益外它对级联别无贡献。", "associated": true }, { @@ -5700,6 +6508,7 @@ "name": "is_valid", "signature": "pub fn is_valid(self) -> bool {", "doc": "Returns `true` when the stage is usable.", + "doc_zh": "当该级可用时返回 `true`。", "associated": true }, { @@ -5707,6 +6516,7 @@ "name": "cascade_temperature", "signature": "pub fn cascade_temperature(stages: &[Stage]) -> Option {", "doc": "Returns the noise temperature of a cascade of stages, referred to the\ninput of the first stage.\n\nThe Friis formula weights each stage's contribution by the gain that\nprecedes it:\n\n```text\nT = T_1 + T_2 / G_1 + T_3 / (G_1 G_2) + ...\n```\n\nThe consequence is that the first stage dominates: a noisy second stage\nmatters much less when the first stage has gain. The total temperature is\nthat of the chain, so it can be compared directly with the available\ngain to find the noise figure of the chain.\n\nReturns `None` when any stage is not usable, which includes a gain ratio\nthat is not positive.", + "doc_zh": "返回各级级联的噪声温度,折算到第一级的输入。\n\nFriis 公式按本级之前的增益对每级的贡献加权:\n\n```text\nT = T_1 + T_2 / G_1 + T_3 / (G_1 G_2) + ...\n```\n\n其结果是第一级占主导:当第一级有增益时,噪声大的第二级影响小得多。\n总温度即整链的温度,故可直接与可用增益比较以求得整链的噪声系数。\n\n任一级不可用(包括增益比非正)时返回 `None`。", "associated": false }, { @@ -5714,6 +6524,7 @@ "name": "noise_figure_to_temperature", "signature": "pub fn noise_figure_to_temperature(noise_figure_db: f64) -> Option {", "doc": "Returns the noise temperature that corresponds to a noise figure in\ndecibels, using the standard reference temperature.\n\nThis is a free function form of\n[`NoiseTemperature::from_noise_figure_db`](crate::NoiseTemperature::from_noise_figure_db).\n\nReturns `None` when the figure is not finite.", + "doc_zh": "使用标准参考温度,返回与某分贝噪声系数对应的噪声温度。\n\n这是\n[`NoiseTemperature::from_noise_figure_db`](crate::NoiseTemperature::from_noise_figure_db)\n的自由函数形式。\n\n该系数非有限时返回 `None`。", "associated": false }, { @@ -5721,6 +6532,7 @@ "name": "cascade_noise_figure_db", "signature": "pub fn cascade_noise_figure_db(stages: &[Stage]) -> Option {", "doc": "Returns the noise figure of a cascade, in decibels.\n\nThe cascade is evaluated as a temperature and then converted to a figure\nusing the standard reference temperature. This is the form in which a\nreceiver chain is usually specified.\n\nReturns `None` when any stage is not usable.", + "doc_zh": "返回级联的噪声系数,以分贝计。\n\n级联先按温度求值,再用标准参考温度换算为系数。这是接收链通常规定的\n形式。\n\n任一级不可用时返回 `None`。", "associated": false }, { @@ -5728,6 +6540,7 @@ "name": "reference_temperature", "signature": "pub const fn reference_temperature() -> f64 {", "doc": "Returns the reference temperature used by the noise figure definition.", + "doc_zh": "返回噪声系数定义所用的参考温度。", "associated": false }, { @@ -5735,6 +6548,7 @@ "name": "REFERENCE_TEMPERATURE_K", "signature": "pub const REFERENCE_TEMPERATURE_K: f64 = 290.0;", "doc": "The reference temperature used to define the noise figure, in kelvin.\n\nThe value is 290 K, which is the standard reference temperature of the\nIEEE definition of noise figure. It is a convention rather than a\nphysical temperature, and the choice affects the noise figure quoted for\na given noise temperature.", + "doc_zh": "用于定义噪声系数的参考温度,单位为开尔文。\n\n该值为 290 K,即 IEEE 噪声系数定义的标准参考温度。它是约定而非物理\n温度,这一选择会影响给定噪声温度所对应的噪声系数。", "associated": false }, { @@ -5742,6 +6556,7 @@ "name": "NoiseTemperature", "signature": "pub struct NoiseTemperature(f64);", "doc": "A noise temperature in kelvin.\n\nA noise temperature is the temperature of a resistor that would deliver\nthe same noise power in the same bandwidth. It is a convenient way to\nexpress any noise source, including one that is not thermal in origin,\nbecause temperatures add across a cascade while powers do not.", + "doc_zh": "以开尔文表示的噪声温度。\n\n噪声温度是在同一带宽内会给出相同噪声功率的电阻的温度。它是表达任何\n噪声源(包括非热起源者)的便利方式,因为温度在级联中相加而功率\n不然。", "associated": false }, { @@ -5749,6 +6564,7 @@ "name": "REFERENCE", "signature": "pub const REFERENCE: Self = Self(REFERENCE_TEMPERATURE_K);", "doc": "The standard reference temperature of 290 K.", + "doc_zh": "290 K 的标准参考温度。", "associated": true }, { @@ -5756,6 +6572,7 @@ "name": "from_kelvin", "signature": "pub const fn from_kelvin(kelvin: f64) -> Self {", "doc": "Creates a noise temperature from a value in kelvin.", + "doc_zh": "由以开尔文表示的值创建噪声温度。", "associated": true }, { @@ -5763,6 +6580,7 @@ "name": "from_noise_figure_db", "signature": "pub fn from_noise_figure_db(decibels: f64) -> Option {", "doc": "Creates a noise temperature from a noise figure in decibels.\n\nThe relation is `T = T_0 (F - 1)` with `F` the linear noise factor,\nso a noise figure of zero decibels gives a temperature of zero.\n\nReturns `None` when the figure is not finite.", + "doc_zh": "由以分贝表示的噪声系数创建噪声温度。\n\n关系为 `T = T_0 (F - 1)`,其中 `F` 是线性噪声因子,故零分贝的\n噪声系数给出零温度。\n\n该系数非有限时返回 `None`。", "associated": true }, { @@ -5770,6 +6588,7 @@ "name": "from_noise_factor", "signature": "pub fn from_noise_factor(factor: f64) -> Option {", "doc": "Creates a noise temperature from a linear noise factor.\n\nReturns `None` when the factor is not finite or is below one, which\nwould describe a stage that removes noise.", + "doc_zh": "由线性噪声因子创建噪声温度。\n\n该因子非有限或小于一时返回 `None`,那将描述一个去除噪声的级。", "associated": true }, { @@ -5777,6 +6596,7 @@ "name": "kelvin", "signature": "pub const fn kelvin(self) -> f64 {", "doc": "Returns this temperature in kelvin.", + "doc_zh": "返回该温度的开尔文值。", "associated": true }, { @@ -5784,6 +6604,7 @@ "name": "noise_factor", "signature": "pub fn noise_factor(self) -> f64 {", "doc": "Returns this temperature as a linear noise factor.\n\nThe factor is `T / T_0 + 1`, which is one for zero noise.", + "doc_zh": "返回该温度的线性噪声因子。\n\n该因子为 `T / T_0 + 1`,零噪声时为一。", "associated": true }, { @@ -5791,6 +6612,7 @@ "name": "noise_figure_db", "signature": "pub fn noise_figure_db(self) -> Option {", "doc": "Returns this temperature as a noise figure in decibels.\n\nReturns `None` when the temperature is not finite or is negative,\nwhich would give a factor below one.", + "doc_zh": "返回该温度的噪声系数,以分贝计。\n\n温度非有限或为负(会给出小于一的因子)时返回 `None`。", "associated": true }, { @@ -5798,6 +6620,7 @@ "name": "plus", "signature": "pub const fn plus(self, other: Self) -> Self {", "doc": "Returns the sum of two temperatures.\n\nNoise temperatures of independent sources add, which is the reason\nthe quantity is useful.", + "doc_zh": "返回两个温度之和。\n\n独立源的噪声温度相加,这正是该量有用的原因。", "associated": true }, { @@ -5805,6 +6628,7 @@ "name": "scaled", "signature": "pub const fn scaled(self, factor: f64) -> Self {", "doc": "Returns this temperature scaled by a gain ratio.\n\nA source followed by a gain contributes its temperature divided by\nthat gain when referred to the input, so the scaling for a\ncontribution referred to the input is the reciprocal.", + "doc_zh": "返回该温度按某增益比缩放后的值。\n\n某源之后接一增益时,折算到输入其贡献为该温度除以该增益,故折算到\n输入的贡献其缩放取倒数。", "associated": true }, { @@ -5812,6 +6636,7 @@ "name": "is_valid", "signature": "pub const fn is_valid(self) -> bool {", "doc": "Returns `true` when the temperature is finite and not negative.", + "doc_zh": "当该温度有限且非负时返回 `true`。", "associated": true }, { @@ -5819,6 +6644,7 @@ "name": "thermal_noise_power", "signature": "pub fn thermal_noise_power(temperature: NoiseTemperature, bandwidth: f64) -> Option {", "doc": "Returns the thermal noise power in a bandwidth at a noise temperature.\n\nThe relation is `P = k T B`, with `k` the Boltzmann constant. This is the\npower that a matched resistor at the given temperature would deliver to a\nload over the stated bandwidth.\n\nThe bandwidth must be positive. Returns `None` for an invalid\ntemperature or bandwidth.", + "doc_zh": "返回某噪声温度下某带宽内的热噪声功率。\n\n关系为 `P = k T B`,其中 `k` 是玻尔兹曼常量。这是给定温度下的匹配\n电阻在所给带宽内向负载输送的功率。\n\n带宽必须为正。温度或带宽无效时返回 `None`。", "associated": false } ] @@ -5826,13 +6652,15 @@ { "name": "apsis-interference", "layer": "rf", - "summary": "Interference and carrier-to-interference computation. A wanted signal rarely arrives alone. This crate combines the wanted carrier with one or more interfering carriers and with noise, and reports the ratios that decide whether the wanted link is usable. The figures computed here are: - The carrier to interference ratio, over one interferer or several. - The carrier to noise plus interference ratio, which is what a receiver actually sees, because it cannot separate the two. - The equivalent noise temperature of an interferer, which lets an interference contribution be folded into the link budget as if it were noise. Interference from a second satellite sharing the same frequency is described by the angle between the two satellites as seen from the ground station. The receive antenna has a sidelobe response in that direction, and the ratio of the interferer's power to the wanted power follows from the two gains and the two path losses. This crate takes the two gains as inputs, so it composes with the pattern models in `apsis-antenna`. Source: the definitions of carrier to interference and carrier to noise plus interference, and the practice of expressing interference as an equivalent noise temperature, are standard in satellite link design and appear for example in the ITU-R Handbook on Satellite Communications. ``` use apsis_interference::{Interferer, carrier_to_interference, combine}; use apsis_rf::{Decibel, Power}; let wanted = Power::from_dbw(-130.0).expect(\"finite\"); let interferer = Interferer { power: Power::from_dbw(-140.0).expect(\"finite\"), }; // One interferer ten decibels below the carrier leaves a C/I of 10 dB. let ratio = carrier_to_interference(wanted, &[interferer]).expect(\"valid\"); assert!((ratio.db() - 10.0).abs() < 1e-9); // Combining with noise can only lower the result. let combined = combine(ratio, Decibel::from_db(15.0)).expect(\"valid\"); assert!(combined.db() < ratio.db()); ```", + "summary": "Interference and carrier-to-interference computation.\n\nA wanted signal rarely arrives alone. This crate combines the wanted\ncarrier with one or more interfering carriers and with noise, and reports\nthe ratios that decide whether the wanted link is usable.\n\nThe figures computed here are:\n\n- The carrier to interference ratio, over one interferer or several.\n- The carrier to noise plus interference ratio, which is what a receiver\n actually sees, because it cannot separate the two.\n- The equivalent noise temperature of an interferer, which lets an\n interference contribution be folded into the link budget as if it were\n noise.\n\nInterference from a second satellite sharing the same frequency is\ndescribed by the angle between the two satellites as seen from the ground\nstation. The receive antenna has a sidelobe response in that direction,\nand the ratio of the interferer's power to the wanted power follows from\nthe two gains and the two path losses. This crate takes the two gains as\ninputs, so it composes with the pattern models in `apsis-antenna`.\n\nSource: the definitions of carrier to interference and carrier to noise\nplus interference, and the practice of expressing interference as an\nequivalent noise temperature, are standard in satellite link design and\nappear for example in the ITU-R Handbook on Satellite Communications.\n\n```\nuse apsis_interference::{Interferer, carrier_to_interference, combine};\nuse apsis_rf::{Decibel, Power};\n\nlet wanted = Power::from_dbw(-130.0).expect(\"finite\");\nlet interferer = Interferer {\n power: Power::from_dbw(-140.0).expect(\"finite\"),\n};\n\n// One interferer ten decibels below the carrier leaves a C/I of 10 dB.\nlet ratio = carrier_to_interference(wanted, &[interferer]).expect(\"valid\");\nassert!((ratio.db() - 10.0).abs() < 1e-9);\n\n// Combining with noise can only lower the result.\nlet combined = combine(ratio, Decibel::from_db(15.0)).expect(\"valid\");\nassert!(combined.db() < ratio.db());\n```", + "summary_zh": "干扰与载干比计算。\n\n有用信号很少独自到达。本 crate 把有用载波与一个或多个干扰载波以及噪声\n合并,并报告决定该有用链路是否可用的各比值。\n\n此处计算的数字是:\n\n- 载干比,对一个或若干干扰源。\n- 载波与噪声加干扰之比,这是接收机实际所见,因为它无法把二者分开。\n- 某干扰源的等效噪声温度,它使干扰贡献可以像噪声一样折算进链路预算。\n\n来自共享同一频率的第二颗卫星的干扰,由自地面站看两颗卫星之间的角描述。\n接收天线在该方向上有旁瓣响应,而干扰源功率与有用功率之比由两个增益与\n两个路径损耗得出。本 crate 把两个增益作为输入,故它与 `apsis-antenna`\n中的方向图模型可组合。\n\n出处:载干比与载波噪声加干扰比的定义,以及把干扰表达为等效噪声温度的\n做法,都是卫星链路设计中的标准内容,例如见于 ITU-R Handbook on Satellite\nCommunications。", "items": [ { "kind": "struct", "name": "Interferer", "signature": "pub struct Interferer {", "doc": "One interfering carrier seen at the receiver input.\n\nThe power is the interfering power that reaches the receiver, already\nincluding the transmit power, the transmit gain, the path loss, the\nreceive antenna gain in the direction of the interferer, and any\npropagation loss. This crate combines the powers; it does not compute the\ngeometry.", + "doc_zh": "在接收机输入端看到的一个干扰载波。\n\n功率是到达接收机的干扰功率,其中已含发射功率、发射增益、路径损耗、\n干扰源方向上的接收天线增益,以及任何传播损耗。本 crate 只合并功率;\n它不计算几何。", "associated": false }, { @@ -5840,6 +6668,7 @@ "name": "new", "signature": "pub const fn new(power: Power) -> Self {", "doc": "Creates an interferer from a power at the receiver input.", + "doc_zh": "由接收机输入端的功率创建干扰源。", "associated": true }, { @@ -5847,6 +6676,7 @@ "name": "InterferenceSummary", "signature": "pub struct InterferenceSummary {", "doc": "The combined result of a wanted carrier and its interference.", + "doc_zh": "有用载波与其干扰合并所得的结果。", "associated": false }, { @@ -5854,6 +6684,7 @@ "name": "interference_to_carrier", "signature": "pub fn interference_to_carrier(self) -> Decibel {", "doc": "Returns the interference to carrier ratio.\n\nThis is the reciprocal of the carrier to interference ratio and is\nthe form some specifications use.", + "doc_zh": "返回干扰与载波之比。\n\n它是载干比的倒数,是某些规范所用的形式。", "associated": true }, { @@ -5861,6 +6692,7 @@ "name": "total_interference", "signature": "pub fn total_interference(interferers: &[Interferer]) -> Option {", "doc": "Returns the total interfering power from a set of interferers.\n\nInterfering powers add, because they are independent signals and their\npower is what matters, not their amplitudes. Two equal interferers\ntherefore give twice the power of one, which is three decibels and not\nsix.\n\nReturns `None` when any interferer has an invalid power.", + "doc_zh": "返回一组干扰源的总干扰功率。\n\n各干扰功率相加,因为它们是独立信号,要紧的是其功率而非幅度。故两个\n相等的干扰源给出单个的两倍功率,即三分贝而非六分贝。\n\n任一干扰源的功率无效时返回 `None`。", "associated": false }, { @@ -5868,6 +6700,7 @@ "name": "carrier_to_interference", "signature": "pub fn carrier_to_interference(carrier: Power, interferers: &[Interferer]) -> Option {", "doc": "Returns the carrier to interference ratio for a carrier and a set of\ninterferers.\n\nThe ratio is the wanted carrier power divided by the sum of the\ninterfering powers. A large ratio means the interferers are weak.\n\nReturns `None` when the carrier is not positive, when any interferer is\ninvalid, or when the total interference is zero, which would give an\ninfinite ratio.", + "doc_zh": "返回某载波与一组干扰源的载干比。\n\n该比是有用载波功率除以各干扰功率之和。比大意味着干扰源弱。\n\n载波非正、任一干扰源无效,或总干扰为零(会给出无穷的比)时返回\n`None`。", "associated": false }, { @@ -5875,6 +6708,7 @@ "name": "carrier_to_interference_single", "signature": "pub fn carrier_to_interference_single(carrier: Power, interferer: Interferer) -> Option {", "doc": "Returns the carrier to interference ratio for a single interferer.\n\nThis is a convenience for the common case of one dominant interferer.\n\nReturns `None` when either power is not positive.", + "doc_zh": "返回单个干扰源下的载干比。\n\n这是为一个占主导的干扰源这一常见情形提供的便利。\n\n两个功率之一非正时返回 `None`。", "associated": false }, { @@ -5882,6 +6716,7 @@ "name": "combine", "signature": "pub fn combine(first: Decibel, second: Decibel) -> Option {", "doc": "Combines a ratio with another ratio of the same kind.\n\nWhen two independent impairments act together, their effects add as\nreciprocal ratios, which in decibels means\n\n```text\n1 / R = 1 / R_1 + 1 / R_2\n```\n\nThe result is always below the smaller of the two, so adding a second\nimpairment can only lower the combined ratio. This is how a carrier to\nnoise ratio and a carrier to interference ratio are combined into a\ncarrier to noise plus interference ratio.\n\nReturns `None` when either value is not finite.", + "doc_zh": "把一个比与另一个同类之比合并。\n\n当两种独立损伤共同作用时,它们的效果按倒数之比相加,以分贝表示即\n\n```text\n1 / R = 1 / R_1 + 1 / R_2\n```\n\n结果总低于两者中较小者,故加入第二种损伤只会降低合并之比。载噪比与\n载干比正是这样合并为载波与噪声加干扰之比的。\n\n任一值非有限时返回 `None`。", "associated": false }, { @@ -5889,6 +6724,7 @@ "name": "carrier_to_noise_plus_interference", "signature": "pub fn carrier_to_noise_plus_interference( carrier_to_noise: Decibel, carrier_to_interference: Decibel, ) -> Option {", "doc": "Returns the carrier to noise plus interference ratio.\n\nThis combines a carrier to noise ratio and a carrier to interference\nratio through [`combine`]. It is the ratio a receiver actually\nexperiences, because it cannot separate thermal noise from a competing\ncarrier.\n\nReturns `None` when either ratio is not finite.", + "doc_zh": "返回载波与噪声加干扰之比。\n\n它通过 [`combine`] 把载噪比与载干比合并。这是接收机实际经历的比,因为\n它无法把热噪声与竞争载波分开。\n\n任一比非有限时返回 `None`。", "associated": false }, { @@ -5896,6 +6732,7 @@ "name": "interference_to_noise_ratio", "signature": "pub fn interference_to_noise_ratio(interference: Power, noise: Power) -> Option {", "doc": "Returns the interference to noise ratio.\n\nThis compares the interfering power with the thermal noise power in the\nreceiver. A value well below zero decibels means the interference is\nnegligible against the noise and can be ignored; a value near or above\nzero means it must be accounted for.\n\nReturns `None` when either power is not positive.", + "doc_zh": "返回干扰与噪声之比。\n\n它把干扰功率与接收机中的热噪声功率相比较。远低于零分贝的值意味着干扰相对\n噪声可忽略、可以不计;接近或高于零的值意味着必须计入它。\n\n两个功率之一非正时返回 `None`。", "associated": false }, { @@ -5903,6 +6740,7 @@ "name": "equivalent_noise_temperature", "signature": "pub fn equivalent_noise_temperature(interference: Power, bandwidth: f64) -> Option {", "doc": "Returns the noise temperature equivalent to an interfering power.\n\nAn interferer that a receiver treats as noise can be folded into the link\nbudget as an increase in system noise temperature. In temperature the\nrelation is\n\n```text\nT = P / (k B)\n```\n\nwith `P` the interfering power, `k` the Boltzmann constant, and `B` the\nreceiver noise bandwidth. The temperature is the value that, added to the\nsystem temperature, would raise the noise by the same amount as the\ninterferer.\n\nReturns `None` when the power is not positive or the bandwidth is not\npositive and finite.", + "doc_zh": "返回与某干扰功率等效的噪声温度。\n\n接收机当作噪声处理的干扰源可以折算为系统噪声温度的增量计入链路\n预算。在温度上关系为\n\n```text\nT = P / (k B)\n```\n\n其中 `P` 是干扰功率,`k` 是玻尔兹曼常量,`B` 是接收机噪声带宽。该\n温度即加到系统温度上会使噪声上升与干扰源相同量的值。\n\n功率非正,或带宽非正且非有限时返回 `None`。", "associated": false }, { @@ -5910,6 +6748,7 @@ "name": "summarise", "signature": "pub fn summarise( carrier: Power, interferers: &[Interferer], noise: Option, ) -> Option {", "doc": "Returns a summary of a carrier with its interference.\n\nWhen a noise power is given, the carrier to noise plus interference ratio\nis computed as well. Pass `None` for the noise power when only the\ninterference is of interest.\n\nReturns `None` when the carrier is not positive, when any interferer is\ninvalid, or when the total interference is zero.", + "doc_zh": "返回某载波及其干扰的摘要。\n\n当给出噪声功率时,载波与噪声加干扰之比也一并算出。当只关注干扰时,\n噪声功率传 `None`。\n\n载波非正、任一干扰源无效,或总干扰为零时返回 `None`。", "associated": false } ] @@ -5917,13 +6756,15 @@ { "name": "apsis-link", "layer": "rf", - "summary": "Link budget computation. A link budget adds up every gain and loss between a transmitter and a receiver and compares the resulting carrier power with the noise that competes with it. This crate computes that sum and the figures derived from it: the carrier to noise ratio, the energy per bit to noise density ratio, and the link margin. The order of operations matters, so the chain is stated explicitly: 1. The transmitter power is reduced by the transmit losses and increased by the transmit antenna gain, giving the equivalent isotropically radiated power. 2. That is divided by the free space path loss and increased by the receive antenna gain, giving the carrier power at the receiver input. 3. The system noise temperature is converted to a noise density by multiplying by the Boltzmann constant. 4. The carrier to noise density ratio is the carrier power divided by that density, and the carrier to noise ratio divides it further by the receiver bandwidth. Sources: - The link equation in this form is the one used throughout satellite communications practice, as described for example in the ITU-R Handbook on Satellite Communications. - The Boltzmann constant and the speed of light come from the SI definitions, as re-exported by `apsis-rf`. ``` use apsis_link::{LinkBudget, LinkGeometry}; use apsis_rf::{Gain, Power}; let budget = LinkBudget { transmit_power: Power::from_dbw(10.0).expect(\"finite\"), transmit_gain: Gain::from_dbi(30.0).expect(\"finite\"), transmit_loss: apsis_rf::Decibel::from_db(1.0), receive_gain: Gain::from_dbi(35.0).expect(\"finite\"), receive_loss: apsis_rf::Decibel::from_db(0.5), system_temperature: apsis_noise::NoiseTemperature::from_kelvin(150.0), bandwidth: 36.0e6, geometry: LinkGeometry { distance: 38_000_000.0, frequency: 12.0e9, }, }; // The carrier to noise density ratio is available directly. For this // budget it is about 74.7 dB, which is typical of a Ku band downlink. let ratio = budget.carrier_to_noise_density().expect(\"valid budget\"); assert!(ratio.db() > 70.0); assert!(ratio.db() < 80.0); ```", + "summary": "Link budget computation.\n\nA link budget adds up every gain and loss between a transmitter and a\nreceiver and compares the resulting carrier power with the noise that\ncompetes with it. This crate computes that sum and the figures derived\nfrom it: the carrier to noise ratio, the energy per bit to noise density\nratio, and the link margin.\n\nThe order of operations matters, so the chain is stated explicitly:\n\n1. The transmitter power is reduced by the transmit losses and increased\n by the transmit antenna gain, giving the equivalent isotropically\n radiated power.\n2. That is divided by the free space path loss and increased by the\n receive antenna gain, giving the carrier power at the receiver input.\n3. The system noise temperature is converted to a noise density by\n multiplying by the Boltzmann constant.\n4. The carrier to noise density ratio is the carrier power divided by\n that density, and the carrier to noise ratio divides it further by the\n receiver bandwidth.\n\nSources:\n\n- The link equation in this form is the one used throughout satellite\n communications practice, as described for example in the ITU-R\n Handbook on Satellite Communications.\n- The Boltzmann constant and the speed of light come from the SI\n definitions, as re-exported by `apsis-rf`.\n\n```\nuse apsis_link::{LinkBudget, LinkGeometry};\nuse apsis_rf::{Gain, Power};\n\nlet budget = LinkBudget {\n transmit_power: Power::from_dbw(10.0).expect(\"finite\"),\n transmit_gain: Gain::from_dbi(30.0).expect(\"finite\"),\n transmit_loss: apsis_rf::Decibel::from_db(1.0),\n receive_gain: Gain::from_dbi(35.0).expect(\"finite\"),\n receive_loss: apsis_rf::Decibel::from_db(0.5),\n system_temperature: apsis_noise::NoiseTemperature::from_kelvin(150.0),\n bandwidth: 36.0e6,\n geometry: LinkGeometry {\n distance: 38_000_000.0,\n frequency: 12.0e9,\n },\n};\n\n// The carrier to noise density ratio is available directly. For this\n// budget it is about 74.7 dB, which is typical of a Ku band downlink.\nlet ratio = budget.carrier_to_noise_density().expect(\"valid budget\");\nassert!(ratio.db() > 70.0);\nassert!(ratio.db() < 80.0);\n```", + "summary_zh": "链路预算计算。\n\n链路预算把发射机与接收机之间的每项增益与损耗相加,并把所得载波功率与同\n它竞争的噪声比较。本 crate 计算该和以及由它导出的各数字:载噪比、每比特\n能量与噪声密度之比,以及链路余量。\n\n运算次序要紧,故该链被明确写出:\n\n1. 发射机功率减去发射损耗、加上发射天线增益,给出等效全向辐射功率。\n2. 它除以自由空间路径损耗、加上接收天线增益,给出接收机输入端的载波\n 功率。\n3. 系统噪声温度乘以玻尔兹曼常量,换算为噪声密度。\n4. 载噪密度比是载波功率除以该密度,载噪比则进一步除以接收机带宽。\n\n出处:\n\n- 此形式的链路方程是卫星通信实践中通篇所用的,例如见 ITU-R Handbook on\n Satellite Communications。\n- 玻尔兹曼常量与光速来自 SI 定义,由 `apsis-rf` 重导出。", "items": [ { "kind": "struct", "name": "LinkGeometry", "signature": "pub struct LinkGeometry {", "doc": "The geometric part of a link: how far apart the terminals are and at what\nfrequency they operate.", + "doc_zh": "链路的几何部分:两端相距多远,以及在什么频率上工作。", "associated": false }, { @@ -5931,6 +6772,7 @@ "name": "path_loss", "signature": "pub fn path_loss(self) -> Option {", "doc": "Returns the free space path loss for this geometry.\n\nReturns `None` when the distance or frequency is not usable.", + "doc_zh": "返回该几何的自由空间路径损耗。\n\n距离或频率不可用时返回 `None`。", "associated": true }, { @@ -5938,6 +6780,7 @@ "name": "is_valid", "signature": "pub fn is_valid(self) -> bool {", "doc": "Returns `true` when the geometry is usable.", + "doc_zh": "当该几何可用时返回 `true`。", "associated": true }, { @@ -5945,6 +6788,7 @@ "name": "LinkBudget", "signature": "pub struct LinkBudget {", "doc": "A complete link budget for one direction of a radio link.\n\nThe losses are given as positive decibel values that are subtracted,\nwhich is the convention in link budgets: a three decibel loss is entered\nas three decibels, not as minus three.", + "doc_zh": "无线电链路一个方向的完整链路预算。\n\n各损耗以被减去的正分贝值给出,这是链路预算中的约定:三分贝的损耗记\n为三,而非负三。", "associated": false }, { @@ -5952,6 +6796,7 @@ "name": "eirp", "signature": "pub fn eirp(&self) -> Option {", "doc": "Returns the equivalent isotropically radiated power, in watts.\n\nThis is the transmit power reduced by the transmit losses and\nincreased by the transmit antenna gain. It is the power that an\nisotropic radiator would have to radiate to produce the same flux\ndensity in the direction of the receiver.\n\nReturns `None` when any input is not usable.", + "doc_zh": "返回等效全向辐射功率,单位为瓦特。\n\n这是发射功率减去发射损耗、加上发射天线增益。它是一个各向同性辐射体为\n在接收机方向上产生相同通量密度所必须辐射的功率。\n\n任一输入不可用时返回 `None`。", "associated": true }, { @@ -5959,6 +6804,7 @@ "name": "received_power", "signature": "pub fn received_power(&self) -> Option {", "doc": "Returns the carrier power at the receiver input, in watts.\n\nReturns `None` when any input is not usable.", + "doc_zh": "返回接收机输入端的载波功率,单位为瓦特。\n\n任一输入不可用时返回 `None`。", "associated": true }, { @@ -5966,6 +6812,7 @@ "name": "noise_density", "signature": "pub fn noise_density(&self) -> Option {", "doc": "Returns the noise power density at the receiver input, in watts per\nhertz.\n\nThis is `k T`, the noise temperature converted to a power per unit\nbandwidth. It does not depend on the receiver bandwidth, which is\nwhat makes the carrier to noise density ratio a property of the link\nrather than of the receiver.\n\nReturns `None` when the system temperature is not usable.", + "doc_zh": "返回接收机输入端的噪声功率密度,单位为瓦特每赫兹。\n\n这是 `k T`,即把噪声温度换算为单位带宽的功率。它不依赖于接收机\n带宽,这正是载噪密度比成为链路属性而非接收机属性的原因。\n\n系统温度不可用时返回 `None`。", "associated": true }, { @@ -5973,6 +6820,7 @@ "name": "carrier_to_noise_density", "signature": "pub fn carrier_to_noise_density(&self) -> Option {", "doc": "Returns the carrier to noise density ratio.\n\nThis is the carrier power divided by the noise power in one hertz.\nIt is the form used to compare a link against the energy per bit\nrequirement of a modulation, because the data rate is accounted for\nseparately.\n\nReturns `None` when any input is not usable.", + "doc_zh": "返回载噪密度比。\n\n这是载波功率除以一赫兹内的噪声功率。它是把链路与某调制的每比特能量要求\n相比较所用的形式,因为数据速率被分开计入。\n\n任一输入不可用时返回 `None`。", "associated": true }, { @@ -5980,6 +6828,7 @@ "name": "carrier_to_noise", "signature": "pub fn carrier_to_noise(&self) -> Option {", "doc": "Returns the carrier to noise ratio in the receiver bandwidth.\n\nThis is the carrier to noise density ratio reduced by the bandwidth.\nUnlike the density ratio it depends on the receiver, which is why the\nsame link can show different carrier to noise ratios in receivers of\ndifferent bandwidth.\n\nReturns `None` when any input is not usable.", + "doc_zh": "返回接收机带宽内的载噪比。\n\n这是载噪密度比减去带宽的贡献。与密度比不同,它依赖于接收机,这正是同一\n链路在不同带宽的接收机中可显示不同载噪比的原因。\n\n任一输入不可用时返回 `None`。", "associated": true }, { @@ -5987,6 +6836,7 @@ "name": "energy_per_bit_over_noise", "signature": "pub fn energy_per_bit_over_noise(&self, data_rate: f64) -> Option {", "doc": "Returns the energy per bit to noise density ratio for a data rate.\n\nThis is the carrier to noise density ratio reduced by the data rate,\nbecause energy per bit is carrier power divided by bit rate.\n\nReturns `None` when the data rate is not positive, or when the\ncarrier to noise density ratio is not usable.", + "doc_zh": "返回某数据速率下每比特能量与噪声密度之比。\n\n它是载噪密度比减去数据速率,因为每比特能量是载波功率除以比特\n速率。\n\n数据速率非正,或载噪密度比不可用时返回 `None`。", "associated": true }, { @@ -5994,6 +6844,7 @@ "name": "with_added_noise", "signature": "pub fn with_added_noise(&self, added: NoiseTemperature) -> Option {", "doc": "Returns the rise in system noise caused by the receive antenna\nviewing a noise source of the given temperature.\n\nThe antenna noise is added to the system temperature in power, which\nin temperature is a sum. This is the form used to account for the\nEarth, the Sun, or a rain cell in the beam.\n\nReturns `None` when the added temperature is not usable.", + "doc_zh": "返回接收天线看到给定温度的噪声源所引起的系统噪声上升。\n\n天线噪声按功率加到系统温度上,在温度上即求和。这是用于计入波束中\n的地球、太阳或雨胞的形式。\n\n所加温度不可用时返回 `None`。", "associated": true }, { @@ -6001,6 +6852,7 @@ "name": "carrier_wavelength", "signature": "pub fn carrier_wavelength(&self) -> Option {", "doc": "Returns the wavelength of the carrier, in meters.\n\nReturns `None` when the frequency is not usable.", + "doc_zh": "返回载波的波长,单位为米。\n\n频率不可用时返回 `None`。", "associated": true }, { @@ -6008,6 +6860,7 @@ "name": "is_valid", "signature": "pub fn is_valid(&self) -> bool {", "doc": "Returns `true` when every input describes a usable link.", + "doc_zh": "当每个输入都描述一条可用链路时返回 `true`。", "associated": true }, { @@ -6015,6 +6868,7 @@ "name": "LinkMargin", "signature": "pub struct LinkMargin {", "doc": "The result of comparing a link against a requirement.", + "doc_zh": "把链路与某项要求比较所得的结果。", "associated": false }, { @@ -6022,6 +6876,7 @@ "name": "margin", "signature": "pub fn margin(self) -> Decibel {", "doc": "Returns the margin, in decibels.\n\nA positive margin means the link closes with room to spare; a\nnegative margin means it does not.", + "doc_zh": "返回余量,单位为分贝。\n\n正余量意味着链路闭合并有余,负余量意味着不能闭合。", "associated": true }, { @@ -6029,6 +6884,7 @@ "name": "closes", "signature": "pub fn closes(self) -> bool {", "doc": "Returns `true` when the margin is not negative.", + "doc_zh": "当余量非负时返回 `true`。", "associated": true }, { @@ -6036,6 +6892,7 @@ "name": "link_margin", "signature": "pub fn link_margin(achieved: Decibel, required: Decibel) -> LinkMargin {", "doc": "Returns the margin between an achieved ratio and a required one.\n\nThis is the comparison that decides whether a link closes.", + "doc_zh": "返回所达到的比与所要求的比之间的余量。\n\n这一比较决定链路是否闭合。", "associated": false }, { @@ -6043,6 +6900,7 @@ "name": "carrier_to_noise_density", "signature": "pub fn carrier_to_noise_density(budget: &LinkBudget) -> Option {", "doc": "Returns the carrier to noise density ratio for a budget.\n\nThis is a free function form of\n[`LinkBudget::carrier_to_noise_density`].\n\nReturns `None` when the budget is not usable.", + "doc_zh": "返回某预算的载噪密度比。\n\n这是 [`LinkBudget::carrier_to_noise_density`] 的自由函数形式。\n\n预算不可用时返回 `None`。", "associated": false }, { @@ -6050,6 +6908,7 @@ "name": "gain_to_temperature", "signature": "pub fn gain_to_temperature(gain: Gain, temperature: NoiseTemperature) -> Option {", "doc": "Returns the gain to system temperature ratio of a receive chain, in\nreciprocal kelvin.\n\nThis figure of merit is written `G / T` and is the standard way to\ncompare receive stations. It combines the receive antenna gain with the\nsystem noise temperature, so a station with a bigger dish and a quieter\nreceiver scores higher.\n\nExpressed in decibels, `G / T` is the receive gain in dBi minus ten times\nthe logarithm of the temperature, which is the form usually quoted.\n\nReturns `None` when the gain or temperature is not usable.", + "doc_zh": "返回接收链路的增益与系统温度之比,单位为每开尔文。\n\n该品质因数写作 `G / T`,是比较接收站的标准方式。它把接收天线增益与\n系统噪声温度结合起来,故天线更大、接收机更安静的站得分更高。\n\n以分贝表示时,`G / T` 是接收增益(以 dBi 计)减去温度的十倍对数,\n这是通常引用的形式。\n\n增益或温度不可用时返回 `None`。", "associated": false } ] @@ -6057,13 +6916,15 @@ { "name": "apsis-resource", "layer": "ops", - "summary": "Resource modeling for satellite operations. A resource is anything a task can occupy: a satellite, a ground station, a transmitter, an onboard recorder. This crate models the two things a scheduler needs to know about one: when it is available, and how much of it a task consumes. Availability is expressed as a set of disjoint intervals on a single time axis, called windows. A resource is usable at an instant when that instant falls inside one of its windows. The windows may come from anywhere: the access analysis in `apsis-access` produces them for a ground station, and an operator enters maintenance periods by hand. Capacity is a positive number whose meaning is the caller's. A satellite that can run one observation at a time has a capacity of one; a recorder has a capacity in megabits; a power bus has one in watts. Two tasks conflict only when their demands together exceed the capacity, which lets the same type express exclusive and shared resources. Times are in seconds from an origin chosen by the caller, so the same model serves a scenario clock, an elapsed time, and a Julian date offset. ``` use apsis_resource::{Resource, ResourceId, Window}; // A station available from 100 to 200 seconds. let mut resource = Resource::new(ResourceId::new(0), 1.0); resource.add_window(Window::new(100.0, 200.0)).expect(\"the window is usable\"); assert!(resource.is_available_at(150.0)); assert!(!resource.is_available_at(250.0)); assert!(resource.is_available_over(120.0, 80.0)); assert!(!resource.is_available_over(150.0, 100.0)); ```", + "summary": "Resource modeling for satellite operations.\n\nA resource is anything a task can occupy: a satellite, a ground station, a\ntransmitter, an onboard recorder. This crate models the two things a\nscheduler needs to know about one: when it is available, and how much of\nit a task consumes.\n\nAvailability is expressed as a set of disjoint intervals on a single time\naxis, called windows. A resource is usable at an instant when that instant\nfalls inside one of its windows. The windows may come from anywhere: the\naccess analysis in `apsis-access` produces them for a ground station, and\nan operator enters maintenance periods by hand.\n\nCapacity is a positive number whose meaning is the caller's. A satellite\nthat can run one observation at a time has a capacity of one; a recorder\nhas a capacity in megabits; a power bus has one in watts. Two tasks\nconflict only when their demands together exceed the capacity, which lets\nthe same type express exclusive and shared resources.\n\nTimes are in seconds from an origin chosen by the caller, so the same\nmodel serves a scenario clock, an elapsed time, and a Julian date offset.\n\n```\nuse apsis_resource::{Resource, ResourceId, Window};\n\n// A station available from 100 to 200 seconds.\nlet mut resource = Resource::new(ResourceId::new(0), 1.0);\nresource.add_window(Window::new(100.0, 200.0)).expect(\"the window is usable\");\n\nassert!(resource.is_available_at(150.0));\nassert!(!resource.is_available_at(250.0));\nassert!(resource.is_available_over(120.0, 80.0));\nassert!(!resource.is_available_over(150.0, 100.0));\n```", + "summary_zh": "卫星运行的资源建模。\n\n资源是任务可以占用的任何东西:一颗卫星、一座地面站、一台发射机、一台星载\n记录器。本 crate 为调度器需要了解的关于它的两件事建模:它何时可用,以及一个\n任务消耗它多少。\n\n可用性表达为单一时间轴上互不相交的区间集合,称为窗口。在某瞬时有窗口包含该\n瞬时,资源即可用。这些窗口可来自任何地方:`apsis-access` 中的可见性分析为\n地面站产生它们,操作员则手工录入维护时段。\n\n容量是一个正数,其含义由调用方定义。一次只能运行一项观测的卫星容量为一;记录\n器的容量以兆比特计;功率母线的以瓦特计。两个任务只有在它们的需求合起来超过\n容量时才冲突,这使同一个类型既能表达独占资源也能表达共享资源。\n\n时间以距调用方所选原点的秒数计,故同一模型可服务于场景时钟、流逝时间与儒略日\n偏移。", "items": [ { "kind": "enum", "name": "ResourceErrorKind", "signature": "pub enum ResourceErrorKind {", "doc": "The category of a resource error.", + "doc_zh": "资源错误的类别。", "associated": false }, { @@ -6071,6 +6932,7 @@ "name": "ResourceError", "signature": "pub struct ResourceError {", "doc": "A failure while building or querying a resource.", + "doc_zh": "构建或查询资源时发生的失败。", "associated": false }, { @@ -6078,6 +6940,7 @@ "name": "new", "signature": "pub fn new(kind: ResourceErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -6085,6 +6948,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the values that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -6092,6 +6956,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> ResourceErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -6099,6 +6964,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -6106,6 +6972,7 @@ "name": "ResourceId", "signature": "pub struct ResourceId(pub usize);", "doc": "An identifier for a resource.\n\nThe identifier is an index chosen by the caller. It is deliberately opaque\nto this crate so that a caller can use whatever numbering its own model\nprovides.", + "doc_zh": "资源的标识符。\n\n标识符是由调用方选定的下标。它对本 crate 刻意保持不透明,以便调用方\n使用其自身模型所提供的任何编号方式。", "associated": false }, { @@ -6113,6 +6980,7 @@ "name": "new", "signature": "pub const fn new(index: usize) -> Self {", "doc": "Creates an identifier.", + "doc_zh": "创建一个标识符。", "associated": true }, { @@ -6120,6 +6988,7 @@ "name": "index", "signature": "pub const fn index(self) -> usize {", "doc": "Returns the index.", + "doc_zh": "返回该下标。", "associated": true }, { @@ -6127,6 +6996,7 @@ "name": "Resource", "signature": "pub struct Resource {", "doc": "A resource with availability windows and a capacity.\n\nThe capacity is a positive number whose unit the caller chooses. A task\nthat occupies more than the capacity of a resource cannot be scheduled on\nit at all; two tasks conflict when their demands together exceed the\ncapacity at some instant.", + "doc_zh": "带有可用窗口与容量的资源。\n\n容量是由调用方选定单位的正数。占用量超过某资源容量的任务根本无法在其上\n调度;当两个任务的需求之和在某瞬超出容量时,二者冲突。", "associated": false }, { @@ -6134,6 +7004,7 @@ "name": "new", "signature": "pub const fn new(id: ResourceId, capacity: f64) -> Self {", "doc": "Creates a resource with no availability and the given capacity.\n\nA resource with no windows is never available, so a caller adds\nwindows before using it. That is the safe default: a resource whose\navailability was forgotten is unavailable rather than always\navailable.", + "doc_zh": "创建无可用性、容量给定的资源。\n\n没有窗口的资源永不可用,故调用方须先添加窗口再使用。这是安全的\n默认值:忘记设置可用性的资源是不可用,而非始终可用。", "associated": true }, { @@ -6141,6 +7012,7 @@ "name": "capacity", "signature": "pub const fn capacity(&self) -> f64 {", "doc": "Returns the capacity.", + "doc_zh": "返回该容量。", "associated": true }, { @@ -6148,6 +7020,7 @@ "name": "set_capacity", "signature": "pub fn set_capacity(&mut self, capacity: f64) -> Result<(), ResourceError> {", "doc": "Sets the capacity.\n\nReturns an error when the capacity is not positive and finite.", + "doc_zh": "设置容量。\n\n当容量非正或非有限时返回错误。", "associated": true }, { @@ -6155,6 +7028,7 @@ "name": "has_valid_capacity", "signature": "pub fn has_valid_capacity(&self) -> bool {", "doc": "Returns `true` when the capacity is usable.", + "doc_zh": "当该容量可用时返回 `true`。", "associated": true }, { @@ -6162,6 +7036,7 @@ "name": "windows", "signature": "pub fn windows(&self) -> &[Window] {", "doc": "Returns the availability windows, merged and in time order.", + "doc_zh": "返回可用窗口,已合并且按时间有序。", "associated": true }, { @@ -6169,6 +7044,7 @@ "name": "window_count", "signature": "pub fn window_count(&self) -> usize {", "doc": "Returns the number of availability windows.", + "doc_zh": "返回可用窗口的数量。", "associated": true }, { @@ -6176,6 +7052,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when the resource has no availability at all.", + "doc_zh": "当该资源完全没有可用性时返回 `true`。", "associated": true }, { @@ -6183,6 +7060,7 @@ "name": "set_windows", "signature": "pub fn set_windows(&mut self, windows: &[Window]) -> Result<(), ResourceError> {", "doc": "Replaces the availability with a list of windows.\n\nThe windows are merged, so the stored form is canonical whatever order\nthe caller supplies them in.\n\nReturns an error when a window is not usable.", + "doc_zh": "用一组窗口替换可用性。\n\n窗口会被合并,故无论调用方以何种顺序给出,存储形式都是规范的。\n\n当某个窗口不可用时返回错误。", "associated": true }, { @@ -6190,6 +7068,7 @@ "name": "add_window", "signature": "pub fn add_window(&mut self, window: Window) -> Result<(), ResourceError> {", "doc": "Adds one availability window.\n\nThe window is merged into the existing set.\n\nReturns an error when the window is not usable.", + "doc_zh": "添加一个可用窗口。\n\n该窗口被合并进已有集合。\n\n当该窗口不可用时返回错误。", "associated": true }, { @@ -6197,6 +7076,7 @@ "name": "remove_window", "signature": "pub fn remove_window(&mut self, removed: Window) -> Result<(), ResourceError> {", "doc": "Removes an availability window.\n\nThe window is subtracted from the existing set, which may split a\nstored window in two.\n\nReturns an error when the window is not usable.", + "doc_zh": "移除一个可用窗口。\n\n该窗口从已有集合中被减去,可能把某个已存窗口一分为二。\n\n当该窗口不可用时返回错误。", "associated": true }, { @@ -6204,6 +7084,7 @@ "name": "is_available_at", "signature": "pub fn is_available_at(&self, time: f64) -> bool {", "doc": "Returns `true` when the resource is available at an instant.\n\nAn instantaneous query is a query of zero duration, which is satisfied\nwhen the instant is inside a window. At the end of a window the\nresource is not available, because the window is half-open.", + "doc_zh": "当该资源在某瞬可用时返回 `true`。\n\n瞬时查询即零时长查询,当该瞬落在某个窗口内即得到满足。在窗口\n终点处该资源不可用,因为窗口是半开的。", "associated": true }, { @@ -6211,6 +7092,7 @@ "name": "is_available_over", "signature": "pub fn is_available_over(&self, start: f64, duration: f64) -> bool {", "doc": "Returns `true` when the resource is available for a whole interval.\n\nA resource can satisfy a request of zero duration whenever it is\navailable at the requested instant, so a zero duration is checked as\nan instant.", + "doc_zh": "当该资源在整段区间上可用时返回 `true`。\n\n只要资源在所请求瞬时可用,即可满足零时长请求,故零时长按瞬时检查。", "associated": true }, { @@ -6218,6 +7100,7 @@ "name": "availability_within", "signature": "pub fn availability_within(&self, query: Interval) -> Vec {", "doc": "Returns the availability that falls inside an interval.\n\nThe result is the intersection of the stored windows with the query,\nwhich is what a scheduler needs to know when it asks what part of its\nplanning horizon a resource is usable for.", + "doc_zh": "返回落在某区间内的可用性。\n\n结果是已存窗口与该查询的交集,这正是调度器在询问资源的规划时域中\n哪一段可用时所需的信息。", "associated": true }, { @@ -6225,6 +7108,7 @@ "name": "total_availability", "signature": "pub fn total_availability(&self) -> f64 {", "doc": "Returns the total availability, in seconds.\n\nThe windows are disjoint, so the durations add.", + "doc_zh": "返回总可用时长,单位为秒。\n\n各窗口互不相交,故时长可直接相加。", "associated": true }, { @@ -6232,6 +7116,7 @@ "name": "earliest_fit", "signature": "pub fn earliest_fit(&self, duration: f64) -> Option {", "doc": "Returns the first window that can hold a request.\n\nThis is the earliest time at which the resource can satisfy a request\nof the given duration, which is what a greedy scheduler asks. Returns\n`None` when no window is long enough.", + "doc_zh": "返回第一个能容纳某请求的窗口。\n\n这是该资源能满足给定时长请求的最早时刻,即贪心调度器所询问的量。\n没有足够长的窗口时返回 `None`。", "associated": true }, { @@ -6239,6 +7124,7 @@ "name": "earliest_fit_after", "signature": "pub fn earliest_fit_after(&self, not_before: f64, duration: f64) -> Option {", "doc": "Returns the first window that can hold a request starting no earlier\nthan a given time.\n\nReturns `None` when no window is both late enough and long enough.", + "doc_zh": "返回第一个能容纳请求的窗口,且该请求的起点不早于给定时刻。\n\n没有既够晚又够长的窗口时返回 `None`。", "associated": true }, { @@ -6246,6 +7132,7 @@ "name": "busy_until", "signature": "pub fn busy_until(&self, time: f64) -> Option {", "doc": "Returns the time at which the resource stops being available.\n\nReturns `None` when the resource is not available at the given\ninstant, and infinity when the window that contains it is unbounded.", + "doc_zh": "返回该资源不再可用的时刻。\n\n该资源在所给瞬时不可用时返回 `None`;包含该瞬的窗口无界时返回无穷。", "associated": true }, { @@ -6253,6 +7140,7 @@ "name": "ResourceSet", "signature": "pub struct ResourceSet {", "doc": "A collection of resources, indexed by identifier.\n\nThe set keeps the resources in the order they were added and provides the\nlookups a scheduler needs: by identifier, and by the resource that has a\nwindow covering an instant.", + "doc_zh": "以标识符为索引的资源集合。\n\n该集合按添加顺序保存资源,并提供调度器所需的查找:按标识符,以及按\n某个瞬时上有窗口覆盖的资源。", "associated": false }, { @@ -6260,6 +7148,7 @@ "name": "new", "signature": "pub const fn new() -> Self {", "doc": "Creates an empty set.", + "doc_zh": "创建一个空集合。", "associated": true }, { @@ -6267,6 +7156,7 @@ "name": "from_resources", "signature": "pub fn from_resources(resources: Vec) -> Result {", "doc": "Creates a set from a list of resources.\n\nReturns an error when two resources share an identifier, because a\nlookup would then be ambiguous.", + "doc_zh": "由一组资源创建集合。\n\n两个资源标识符相同时返回错误,因为查找将有歧义。", "associated": true }, { @@ -6274,6 +7164,7 @@ "name": "insert", "signature": "pub fn insert(&mut self, resource: Resource) -> Result<(), ResourceError> {", "doc": "Inserts a resource.\n\nReturns an error when the identifier is already present.", + "doc_zh": "插入一个资源。\n\n该标识符已存在时返回错误。", "associated": true }, { @@ -6281,6 +7172,7 @@ "name": "get", "signature": "pub fn get(&self, id: ResourceId) -> Option<&Resource> {", "doc": "Returns the resource with the given identifier.", + "doc_zh": "返回具有所给标识符的资源。", "associated": true }, { @@ -6288,6 +7180,7 @@ "name": "get_mut", "signature": "pub fn get_mut(&mut self, id: ResourceId) -> Option<&mut Resource> {", "doc": "Returns the resource with the given identifier for modification.", + "doc_zh": "返回具有所给标识符的资源以供修改。", "associated": true }, { @@ -6295,6 +7188,7 @@ "name": "remove", "signature": "pub fn remove(&mut self, id: ResourceId) -> Result {", "doc": "Removes a resource.\n\nReturns an error when no resource has the identifier.", + "doc_zh": "移除一个资源。\n\n没有资源具有该标识符时返回错误。", "associated": true }, { @@ -6302,6 +7196,7 @@ "name": "resources", "signature": "pub fn resources(&self) -> &[Resource] {", "doc": "Returns the resources in the order they were added.", + "doc_zh": "返回按添加顺序排列的资源。", "associated": true }, { @@ -6309,6 +7204,7 @@ "name": "len", "signature": "pub fn len(&self) -> usize {", "doc": "Returns the number of resources.", + "doc_zh": "返回资源数量。", "associated": true }, { @@ -6316,6 +7212,7 @@ "name": "available_at", "signature": "pub fn available_at(&self, time: f64) -> Vec {", "doc": "Returns the identifiers of the resources available at an instant.\n\nThe result is in the order the resources were added.", + "doc_zh": "返回在某瞬可用的资源标识符。\n\n结果按资源添加顺序排列。", "associated": true }, { @@ -6323,6 +7220,7 @@ "name": "available_over", "signature": "pub fn available_over(&self, start: f64, duration: f64) -> Vec {", "doc": "Returns the identifiers of the resources available for a whole\ninterval.", + "doc_zh": "返回在整段区间上可用的资源标识符。", "associated": true }, { @@ -6330,6 +7228,7 @@ "name": "Interval", "signature": "pub struct Interval {", "doc": "A half-open interval of time, `[start, end)`.\n\nThe half-open convention is the one that makes intervals tile without\noverlap or gap: an interval that ends where another begins shares only the\ninstant at the boundary, which belongs to the second. Two intervals are\ntherefore disjoint when the end of the first is at or before the start of\nthe second.", + "doc_zh": "半开时间区间 `[start, end)`。\n\n采用半开约定正是为了使区间既不重叠也无空隙地铺满:一个在某处结束、\n另一处从此开始的区间只在边界那一瞬相交,而该瞬归后者。故当第一个区\n间的终点不晚于第二个区间的起点时,二者不相交。", "associated": false }, { @@ -6337,6 +7236,7 @@ "name": "new", "signature": "pub const fn new(start: f64, end: f64) -> Self {", "doc": "Creates an interval.", + "doc_zh": "创建一个区间。", "associated": true }, { @@ -6344,6 +7244,7 @@ "name": "duration", "signature": "pub fn duration(&self) -> f64 {", "doc": "Returns the duration of the interval, in seconds.\n\nThe value is zero for an empty interval and positive otherwise.", + "doc_zh": "返回该区间的时长,单位为秒。\n\n对空区间该值为零,其余情形为正。", "associated": true }, { @@ -6351,6 +7252,7 @@ "name": "is_valid", "signature": "pub fn is_valid(&self) -> bool {", "doc": "Returns `true` when the interval is finite and ordered.", + "doc_zh": "当该区间有限且次序正确时返回 `true`。", "associated": true }, { @@ -6358,6 +7260,7 @@ "name": "contains", "signature": "pub fn contains(&self, time: f64) -> bool {", "doc": "Returns `true` when an instant lies inside the interval.\n\nThe start is included and the end is excluded, so a boundary instant\nbelongs to the interval that begins there.", + "doc_zh": "当某时刻落在该区间内时返回 `true`。\n\n含起点不含终点,故边界时刻归由此处开始的区间。", "associated": true }, { @@ -6365,6 +7268,7 @@ "name": "contains_interval", "signature": "pub fn contains_interval(&self, other: &Self) -> bool {", "doc": "Returns `true` when the interval fully contains another.\n\nAn empty interval is contained by any interval that contains its\ninstant, so a zero length query inside the range is satisfied.", + "doc_zh": "当该区间完全包含另一区间时返回 `true`。\n\n空区间被任何包含其瞬时的区间所包含,故范围内的零长度查询得到满足。", "associated": true }, { @@ -6372,6 +7276,7 @@ "name": "overlaps", "signature": "pub fn overlaps(&self, other: &Self) -> bool {", "doc": "Returns `true` when two intervals share any time.\n\nTwo intervals that meet at a point do not overlap, because the point\nbelongs to only one of them under the half-open convention.", + "doc_zh": "当两个区间共享任一时刻时返回 `true`。\n\n在一点相接的两个区间不重叠,因为在半开约定下该点只属于其中之一。", "associated": true }, { @@ -6379,6 +7284,7 @@ "name": "intersection", "signature": "pub fn intersection(&self, other: &Self) -> Option {", "doc": "Returns the intersection of two intervals, or `None` when they do not\noverlap.", + "doc_zh": "返回两个区间的交集;二者不重叠时返回 `None`。", "associated": true }, { @@ -6386,6 +7292,7 @@ "name": "hull", "signature": "pub fn hull(&self, other: &Self) -> Self {", "doc": "Returns the shortest interval containing both.", + "doc_zh": "返回包含二者的最短区间。", "associated": true }, { @@ -6393,6 +7300,7 @@ "name": "is_adjacent_to", "signature": "pub fn is_adjacent_to(&self, other: &Self) -> bool {", "doc": "Returns `true` when the two intervals touch or overlap, so that they\ncan be merged without leaving a gap.", + "doc_zh": "当两个区间相接或重叠时返回 `true`,此时合并它们不会留下空隙。", "associated": true }, { @@ -6400,6 +7308,7 @@ "name": "Window", "signature": "pub struct Window {", "doc": "An availability window of a resource.\n\nThis is an [`Interval`] under a name that reads better where the caller is\ndescribing when a resource can be used rather than doing interval algebra.", + "doc_zh": "资源的可用窗口。\n\n它就是一个 [`Interval`],只是当调用方描述资源何时可用、而非进行区间运算\n时,这个名字读起来更贴切。", "associated": false }, { @@ -6407,6 +7316,7 @@ "name": "from_interval", "signature": "pub const fn from_interval(interval: Interval) -> Self {", "doc": "Creates a window from an interval.", + "doc_zh": "由区间创建窗口。", "associated": true }, { @@ -6414,6 +7324,7 @@ "name": "start", "signature": "pub const fn start(&self) -> f64 {", "doc": "Returns the start of the window, in seconds.", + "doc_zh": "返回窗口起点,单位为秒。", "associated": true }, { @@ -6421,6 +7332,7 @@ "name": "end", "signature": "pub const fn end(&self) -> f64 {", "doc": "Returns the end of the window, in seconds.", + "doc_zh": "返回窗口终点,单位为秒。", "associated": true }, { @@ -6428,6 +7340,7 @@ "name": "validate", "signature": "pub fn validate(&self) -> Result<(), ResourceError> {", "doc": "Validates the window, returning the reason when it is not usable.", + "doc_zh": "校验该窗口;不可用时返回原因。", "associated": true }, { @@ -6435,6 +7348,7 @@ "name": "merge_windows", "signature": "pub fn merge_windows(windows: &[Window]) -> Result, ResourceError> {", "doc": "Merges a list of windows into a list of disjoint windows in time order.\n\nWindows that overlap or touch are combined, and empty windows are dropped.\nThe result is the canonical form of a set of availability windows: sorted,\ndisjoint, and non-empty, so that two sets can be compared directly.\n\nReturns an error when a window is not usable.", + "doc_zh": "把一组窗口合并为按时间排序的互不相交窗口。\n\n重叠或相接的窗口被合并,空窗口被丢弃。结果是一组可用窗口的规范形式:\n有序、互不相交且无空项,故两组可直接比较。\n\n当某个窗口不可用时返回错误。", "associated": false }, { @@ -6442,6 +7356,7 @@ "name": "subtract_window", "signature": "pub fn subtract_window(windows: &[Window], removed: Window) -> Result, ResourceError> {", "doc": "Removes a window from each of a list of windows.\n\nThe result is the same set of availability with the removed window taken\nout. Windows that are entirely inside the removed one disappear, and one\nthat contains it is split in two.\n\nReturns an error when any input window is not usable.", + "doc_zh": "从一组窗口中逐个减去某个窗口。\n\n结果是同一组可用性去掉该窗口之后的情形。完全落在被减窗口内部的窗口\n消失,包含它的窗口被一分为二。\n\n当任一输入窗口不可用时返回错误。", "associated": false } ] @@ -6449,13 +7364,15 @@ { "name": "apsis-task", "layer": "ops", - "summary": "Task modeling for satellite operations. A task is a unit of work that a scheduler places on a set of resources. It carries everything a scheduler needs to decide whether and where to run it: how long it takes, when it may run, which resources it needs and in what amount, and what it is worth. A task requires all of its resources at the same time, for its whole duration. That is the model an observation has: the satellite and the ground station must both be available while the data is taken. A task that needs resources in sequence is expressed as two tasks joined by a precedence constraint in a [`PrecedenceGraph`], which says that one task must finish before the other starts. Tasks are immutable once built. The state a scheduler assigns to one is kept separately, in the schedule, so that the same task can be considered for several placements without being modified. ``` use apsis_resource::ResourceId; use apsis_task::{Requirement, Task, TaskId}; // An observation of sixty seconds that needs a satellite and a station. let task = Task::new(TaskId::new(0), 60.0) .with_window(100.0, 1_000.0) .with_priority(5.0) .try_require(ResourceId::new(0), 1.0) .expect(\"the satellite is not yet required\") .try_require(ResourceId::new(1), 1.0) .expect(\"the station is not yet required\"); assert_eq!(task.duration, 60.0); assert_eq!(task.requirements.len(), 2); assert!(task.is_valid()); ```", + "summary": "Task modeling for satellite operations.\n\nA task is a unit of work that a scheduler places on a set of resources. It\ncarries everything a scheduler needs to decide whether and where to run\nit: how long it takes, when it may run, which resources it needs and in\nwhat amount, and what it is worth.\n\nA task requires all of its resources at the same time, for its whole\nduration. That is the model an observation has: the satellite and the\nground station must both be available while the data is taken. A task that\nneeds resources in sequence is expressed as two tasks joined by a\nprecedence constraint in a [`PrecedenceGraph`], which says that one task\nmust finish before the other starts.\n\nTasks are immutable once built. The state a scheduler assigns to one is\nkept separately, in the schedule, so that the same task can be considered\nfor several placements without being modified.\n\n```\nuse apsis_resource::ResourceId;\nuse apsis_task::{Requirement, Task, TaskId};\n\n// An observation of sixty seconds that needs a satellite and a station.\nlet task = Task::new(TaskId::new(0), 60.0)\n .with_window(100.0, 1_000.0)\n .with_priority(5.0)\n .try_require(ResourceId::new(0), 1.0)\n .expect(\"the satellite is not yet required\")\n .try_require(ResourceId::new(1), 1.0)\n .expect(\"the station is not yet required\");\n\nassert_eq!(task.duration, 60.0);\nassert_eq!(task.requirements.len(), 2);\nassert!(task.is_valid());\n```", + "summary_zh": "卫星运行的任务建模。\n\n任务是调度器放置到一组资源上的工作单元。它携带调度器决定是否运行它、在哪里\n运行它所需的一切:耗时多久、何时可以运行、需要哪些资源及多少量,以及它值多少。\n\n一个任务在其整个时长内同时需要它的全部资源。这是观测所具有的模型:取数期间\n卫星与地面站必须都可用。需要按序使用资源的任务,表达为两个由\n[`PrecedenceGraph`] 中先后约束连接的任务,该约束说一个任务必须在另一个开始\n之前完成。\n\n任务一旦构建便不可变。调度器赋给它的状态分开保存在调度中,使同一个任务可以\n被考虑用于多处放置而不被修改。", "items": [ { "kind": "enum", "name": "TaskErrorKind", "signature": "pub enum TaskErrorKind {", "doc": "The category of a task error.", + "doc_zh": "任务错误的类别。", "associated": false }, { @@ -6463,6 +7380,7 @@ "name": "TaskError", "signature": "pub struct TaskError {", "doc": "A failure while building or querying a task.", + "doc_zh": "构建或查询任务时发生的失败。", "associated": false }, { @@ -6470,6 +7388,7 @@ "name": "new", "signature": "pub fn new(kind: TaskErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -6477,6 +7396,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the values that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -6484,6 +7404,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> TaskErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -6491,6 +7412,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -6498,6 +7420,7 @@ "name": "PrecedenceGraph", "signature": "pub struct PrecedenceGraph {", "doc": "A directed acyclic graph of precedence constraints.\n\nAn edge from one task to another means the first must finish before the\nsecond starts. The order of the tasks themselves is the caller's: the\ngraph only records which pairs are related.", + "doc_zh": "优先约束构成的有向无环图。\n\n从一个任务指向另一个任务的边表示前者必须先于后者完成。任务本身的次序\n由调用方决定:该图只记录哪些任务对之间存在关系。", "associated": false }, { @@ -6505,6 +7428,7 @@ "name": "new", "signature": "pub fn new() -> Self {", "doc": "Creates an empty graph.", + "doc_zh": "创建一个空图。", "associated": true }, { @@ -6512,6 +7436,7 @@ "name": "edge_count", "signature": "pub fn edge_count(&self) -> usize {", "doc": "Returns the number of edges.", + "doc_zh": "返回边的数量。", "associated": true }, { @@ -6519,6 +7444,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when there are no edges.", + "doc_zh": "当没有边时返回 `true`。", "associated": true }, { @@ -6526,6 +7452,7 @@ "name": "has_constraint", "signature": "pub fn has_constraint(&self, before: TaskId, after: TaskId) -> bool {", "doc": "Returns `true` when the constraint from `before` to `after` exists.", + "doc_zh": "当存在从 `before` 到 `after` 的约束时返回 `true`。", "associated": true }, { @@ -6533,6 +7460,7 @@ "name": "add", "signature": "pub fn add(&mut self, before: TaskId, after: TaskId) -> Result<(), TaskError> {", "doc": "Adds a constraint that `before` must finish before `after` starts.\n\nReturns an error when the two identifiers are the same, because a task\ncannot precede itself, or when the constraint would close a cycle.\n\nAdding a constraint that already exists is accepted and has no effect,\nso a caller may assemble a graph from overlapping sources without\nhaving to deduplicate.", + "doc_zh": "添加一条约束:`before` 必须在 `after` 开始之前完成。\n\n两个标识符相同时返回错误,因为任务不能先于自身;该约束会闭成环时\n亦返回错误。\n\n添加已存在的约束被接受且不起作用,故调用方可以从多个有重叠的数据源\n汇集一个图,无须先去重。", "associated": true }, { @@ -6540,6 +7468,7 @@ "name": "is_reachable", "signature": "pub fn is_reachable(&self, from: TaskId, to: TaskId) -> bool {", "doc": "Returns `true` when `to` is reachable from `from` following the\nconstraints.", + "doc_zh": "当沿约束可自 `from` 到达 `to` 时返回 `true`。", "associated": true }, { @@ -6547,6 +7476,7 @@ "name": "predecessors_of", "signature": "pub fn predecessors_of(&self, task: TaskId) -> Vec {", "doc": "Returns the tasks that must finish before a task starts.\n\nThe result is in the order the constraints were added. It is the\ndirect predecessors only, not the transitive closure.", + "doc_zh": "返回必须在某任务开始前完成的任务。\n\n结果按约束添加顺序排列。它只含直接前驱,不含传递闭包。", "associated": true }, { @@ -6554,6 +7484,7 @@ "name": "successors_of", "signature": "pub fn successors_of(&self, task: TaskId) -> Vec {", "doc": "Returns the tasks that must wait for a task to finish.\n\nThe result is in the order the constraints were added. It is the\ndirect successors only, not the transitive closure.", + "doc_zh": "返回必须等待某任务完成的任务。\n\n结果按约束添加顺序排列。它只含直接后继,不含传递闭包。", "associated": true }, { @@ -6561,6 +7492,7 @@ "name": "roots", "signature": "pub fn roots(&self) -> Vec {", "doc": "Returns the tasks with no predecessor.", + "doc_zh": "返回没有前驱的任务。", "associated": true }, { @@ -6568,6 +7500,7 @@ "name": "leaves", "signature": "pub fn leaves(&self) -> Vec {", "doc": "Returns the tasks with no successor.", + "doc_zh": "返回没有后继的任务。", "associated": true }, { @@ -6575,6 +7508,7 @@ "name": "topological_order", "signature": "pub fn topological_order(&self, tasks: &[TaskId]) -> Result, TaskError> {", "doc": "Returns a topological order of the given tasks.\n\nThe order respects every constraint: a task appears after all of its\npredecessors. Ties are broken by the position of the task in the\nsupplied list, so that a caller can impose its own preference among\ntasks that are otherwise unordered. That is what lets a planner place\nthe more valuable tasks first within the freedom the constraints\nallow.\n\nTasks in the list that have no constraints still appear.\n\nReturns an error when the graph contains a cycle among the given\ntasks. The graph rejects cycles as they are added, so this can only\nhappen when a caller builds the graph from one task set and orders\nanother, or when the tasks are not all in the graph.", + "doc_zh": "返回所给任务的拓扑序。\n\n该次序满足每一条约束:任务出现在其所有前驱之后。平局按该任务在所给\n列表中的位置打破,使调用方可在本来无序的任务间表达自己的偏好。这\n正是让规划器在约束允许的自由度内先放置更有价值的任务。\n\n列表中无任何约束的任务同样出现。\n\n当图在所给任务之间含环时返回错误。该图在添加时就拒绝成环,故这只\n可能发生在调用方以一组任务建图却对另一组排序,或任务未全在图中时。", "associated": true }, { @@ -6582,6 +7516,7 @@ "name": "task_count", "signature": "pub fn task_count(&self) -> usize {", "doc": "Returns the number of tasks in the graph.", + "doc_zh": "返回图中的任务数。", "associated": true }, { @@ -6589,6 +7524,7 @@ "name": "TaskId", "signature": "pub struct TaskId(pub usize);", "doc": "An identifier for a task.", + "doc_zh": "任务的标识符。", "associated": false }, { @@ -6596,6 +7532,7 @@ "name": "new", "signature": "pub const fn new(index: usize) -> Self {", "doc": "Creates an identifier.", + "doc_zh": "创建一个标识符。", "associated": true }, { @@ -6603,6 +7540,7 @@ "name": "index", "signature": "pub const fn index(self) -> usize {", "doc": "Returns the index.", + "doc_zh": "返回该下标。", "associated": true }, { @@ -6610,6 +7548,7 @@ "name": "Requirement", "signature": "pub struct Requirement {", "doc": "A demand that a task makes on one resource.\n\nThe demand is the amount of the resource's capacity the task consumes\nwhile it runs. A demand that exceeds the resource's capacity makes the\ntask impossible to schedule there, which a scheduler discovers by\ncomparing the two.", + "doc_zh": "任务对某个资源的需求。\n\n需求是任务运行期间消耗的资源容量大小。超出资源容量的需求使该任务无法在\n此处调度,调度器通过比较二者即可发现。", "associated": false }, { @@ -6617,6 +7556,7 @@ "name": "new", "signature": "pub const fn new(resource: ResourceId, demand: f64) -> Self {", "doc": "Creates a requirement.", + "doc_zh": "创建一条需求。", "associated": true }, { @@ -6624,6 +7564,7 @@ "name": "is_valid", "signature": "pub fn is_valid(&self) -> bool {", "doc": "Returns `true` when the demand is positive and finite.", + "doc_zh": "当该需求为正且有限时返回 `true`。", "associated": true }, { @@ -6631,6 +7572,7 @@ "name": "Task", "signature": "pub struct Task {", "doc": "A unit of work to be scheduled.\n\nA task is immutable after construction. The builder methods take `self` by\nvalue and return a new task, so a caller can share a base task and derive\nvariants from it without the variants interfering.", + "doc_zh": "待调度的一份工作。\n\n任务在构造后不可变。各构建方法按值接收 `self` 并返回新任务,故调用方\n可以共享一个基础任务并由它派生出若干变体,而变体之间互不干扰。", "associated": false }, { @@ -6638,6 +7580,7 @@ "name": "new", "signature": "pub fn new(id: TaskId, duration: f64) -> Self {", "doc": "Creates a task with the given duration and no requirements.\n\nThe task may start at any finite time, because it has no window. It\nhas a priority of zero and is not mandatory.", + "doc_zh": "以给定时长、无资源需求创建任务。\n\n该任务没有窗口,故可在任意有限时刻开始。其优先级为零,且非强制。", "associated": true }, { @@ -6645,6 +7588,7 @@ "name": "with_window", "signature": "pub fn with_window(mut self, start: f64, end: f64) -> Self {", "doc": "Returns a copy of this task with a time window.\n\nThe window is the interval in which the whole task must fit, so the\ntask may start no earlier than the window start and must end no later\nthan the window end.", + "doc_zh": "返回带时间窗口的本任务副本。\n\n该窗口是整个任务必须容纳其中的区间,故任务起点不早于窗口起点、终点\n不晚于窗口终点。", "associated": true }, { @@ -6652,6 +7596,7 @@ "name": "with_priority", "signature": "pub fn with_priority(mut self, priority: f64) -> Self {", "doc": "Returns a copy of this task with a priority.", + "doc_zh": "返回带给定优先级的本任务副本。", "associated": true }, { @@ -6659,6 +7604,7 @@ "name": "with_mandatory", "signature": "pub fn with_mandatory(mut self, mandatory: bool) -> Self {", "doc": "Returns a copy of this task marked mandatory or not.", + "doc_zh": "返回标记为强制或非强制的本任务副本。", "associated": true }, { @@ -6666,6 +7612,7 @@ "name": "require", "signature": "pub fn require(mut self, resource: ResourceId, demand: f64) -> Self {", "doc": "Returns a copy of this task with an additional resource requirement.\n\nAdding the same resource twice is allowed at this point, because a\nbuilder chain cannot report an error. [`Task::validate`] rejects it,\nand [`Task::try_require`] reports it immediately.", + "doc_zh": "返回附加一条资源需求的本任务副本。\n\n此处允许重复加入同一资源,因为构建者链无法报告错误。\n[`Task::validate`] 会拒绝它,[`Task::try_require`] 则立即报告。", "associated": true }, { @@ -6673,6 +7620,7 @@ "name": "try_require", "signature": "pub fn try_require(mut self, resource: ResourceId, demand: f64) -> Result {", "doc": "Returns a copy of this task with an additional resource requirement,\nreporting a duplicate or an invalid demand immediately.", + "doc_zh": "返回附加一条资源需求的本任务副本,并立即报告重复或无效需求。", "associated": true }, { @@ -6680,6 +7628,7 @@ "name": "demand_for", "signature": "pub fn demand_for(&self, resource: ResourceId) -> Option {", "doc": "Returns the demand the task makes on a resource.\n\nReturns `None` when the task does not require the resource.", + "doc_zh": "返回该任务对某资源的需求。\n\n该任务不要求该资源时返回 `None`。", "associated": true }, { @@ -6687,6 +7636,7 @@ "name": "requires", "signature": "pub fn requires(&self, resource: ResourceId) -> bool {", "doc": "Returns `true` when the task requires a resource.", + "doc_zh": "当该任务要求某资源时返回 `true`。", "associated": true }, { @@ -6694,6 +7644,7 @@ "name": "requirement_count", "signature": "pub fn requirement_count(&self) -> usize {", "doc": "Returns the number of resources the task requires.", + "doc_zh": "返回该任务要求的资源数量。", "associated": true }, { @@ -6701,6 +7652,7 @@ "name": "resources", "signature": "pub fn resources(&self) -> Vec {", "doc": "Returns the identifiers of the required resources.", + "doc_zh": "返回所要求资源的标识符。", "associated": true }, { @@ -6708,6 +7660,7 @@ "name": "window_length", "signature": "pub fn window_length(&self) -> f64 {", "doc": "Returns the length of the window in which the task must fit, in\nseconds.\n\nReturns infinity when the task has no restriction at either end, and\nzero or a negative value when the window cannot hold the task at all.", + "doc_zh": "返回该任务必须容纳其中的窗口长度,单位为秒。\n\n任务两端均无限制时返回无穷;窗口完全容不下该任务时返回零或负值。", "associated": true }, { @@ -6715,6 +7668,7 @@ "name": "window_is_too_short", "signature": "pub fn window_is_too_short(&self) -> bool {", "doc": "Returns `true` when the window cannot hold the task.", + "doc_zh": "当窗口容不下该任务时返回 `true`。", "associated": true }, { @@ -6722,6 +7676,7 @@ "name": "earliest_possible_end", "signature": "pub fn earliest_possible_end(&self) -> f64 {", "doc": "Returns the earliest time the task could end, given its window.\n\nReturns infinity when the task has no upper bound, and negative\ninfinity when it has no lower bound.", + "doc_zh": "返回在其窗口下该任务可能结束的最早时刻。\n\n任务无上界时返回无穷,无下界时返回负无穷。", "associated": true }, { @@ -6729,6 +7684,7 @@ "name": "fits_window", "signature": "pub fn fits_window(&self) -> bool {", "doc": "Returns `true` when the task fits its window.", + "doc_zh": "当该任务能容纳于其窗口时返回 `true`。", "associated": true }, { @@ -6736,6 +7692,7 @@ "name": "has_duplicate_requirements", "signature": "pub fn has_duplicate_requirements(&self) -> bool {", "doc": "Returns `true` when the task uses any resource more than once, or\ndemands a non-positive amount.", + "doc_zh": "当该任务重复使用某资源,或需求非正时返回 `true`。", "associated": true }, { @@ -6743,6 +7700,7 @@ "name": "validate", "signature": "pub fn validate(&self) -> Result<(), TaskError> {", "doc": "Validates the task, returning the reason when it is not usable.\n\nA task is usable when its duration is finite and positive, its window\nis finite at the ends it states and holds the duration, every demand\nis positive and finite and names a distinct resource, and the weights\nare finite.", + "doc_zh": "校验该任务;不可用时返回原因。\n\n任务可用当且仅当:时长有限且为正;窗口在其给出的两端有限并容得下该\n时长;每条需求为正且有限并指向互不相同的资源;各权值有限。", "associated": true }, { @@ -6750,6 +7708,7 @@ "name": "weighted_value", "signature": "pub fn weighted_value(&self) -> f64 {", "doc": "Returns the value of the task, which is its priority weighted by its\nduration.\n\nThis is the quantity a scheduler maximises when it is asked to make\nthe most of the available time: a long task of high priority is worth\nmore than a short one of the same priority.", + "doc_zh": "返回该任务的价值,即其优先级与时长的乘积。\n\n当调度器被要求充分利用可用时间时,最大化的正是该量:同等优先级下\n时长较长的任务价值更高。", "associated": true }, { @@ -6757,6 +7716,7 @@ "name": "TaskState", "signature": "pub enum TaskState {", "doc": "The state a scheduler has assigned to a task.\n\nThe state is kept apart from the task so that the same task can be\nconsidered for several placements without being modified. A task that has\nbeen placed records when and where, so that a schedule can be examined\nwithout a second lookup.", + "doc_zh": "调度器为某任务指定的状态。\n\n状态与任务分开保存,故同一任务可被考虑多次放置而不被修改。已放置的\n任务记录了何时何地,从而无需二次查找即可检查一份计划。", "associated": false }, { @@ -6764,6 +7724,7 @@ "name": "is_scheduled", "signature": "pub const fn is_scheduled(&self) -> bool {", "doc": "Returns `true` when the task has been placed.", + "doc_zh": "当该任务已被放置时返回 `true`。", "associated": true }, { @@ -6771,6 +7732,7 @@ "name": "start", "signature": "pub const fn start(&self) -> Option {", "doc": "Returns the start time of the placement, if there is one.", + "doc_zh": "返回放置的起始时刻,若有。", "associated": true }, { @@ -6778,6 +7740,7 @@ "name": "end", "signature": "pub fn end(&self, duration: f64) -> Option {", "doc": "Returns the end time of the placement, if there is one.", + "doc_zh": "返回放置的结束时刻,若有。", "associated": true }, { @@ -6785,6 +7748,7 @@ "name": "name", "signature": "pub const fn name(&self) -> &'static str {", "doc": "Returns a short name for the state.", + "doc_zh": "返回该状态的简称。", "associated": true }, { @@ -6792,6 +7756,7 @@ "name": "TaskSet", "signature": "pub struct TaskSet {", "doc": "A collection of tasks, indexed by identifier.", + "doc_zh": "以标识符为索引的任务集合。", "associated": false }, { @@ -6799,6 +7764,7 @@ "name": "new", "signature": "pub const fn new() -> Self {", "doc": "Creates an empty set.", + "doc_zh": "创建一个空集合。", "associated": true }, { @@ -6806,6 +7772,7 @@ "name": "from_tasks", "signature": "pub fn from_tasks(tasks: Vec) -> Result {", "doc": "Creates a set from a list of tasks.\n\nReturns an error when two tasks share an identifier or when any task\nis not usable.", + "doc_zh": "由一组任务创建集合。\n\n两个任务标识符相同或任一任务不可用时返回错误。", "associated": true }, { @@ -6813,6 +7780,7 @@ "name": "insert", "signature": "pub fn insert(&mut self, task: Task) -> Result<(), TaskError> {", "doc": "Inserts a task.\n\nReturns an error when the identifier is already present or when the\ntask is not usable.", + "doc_zh": "插入一个任务。\n\n该标识符已存在或该任务不可用时返回错误。", "associated": true }, { @@ -6820,6 +7788,7 @@ "name": "get", "signature": "pub fn get(&self, id: TaskId) -> Option<&Task> {", "doc": "Returns the task with the given identifier.", + "doc_zh": "返回具有所给标识符的任务。", "associated": true }, { @@ -6827,6 +7796,7 @@ "name": "remove", "signature": "pub fn remove(&mut self, id: TaskId) -> Result {", "doc": "Removes a task.\n\nReturns an error when no task has the identifier.", + "doc_zh": "移除一个任务。\n\n没有任务具有该标识符时返回错误。", "associated": true }, { @@ -6834,6 +7804,7 @@ "name": "tasks", "signature": "pub fn tasks(&self) -> &[Task] {", "doc": "Returns the tasks in the order they were added.", + "doc_zh": "返回按添加顺序排列的任务。", "associated": true }, { @@ -6841,6 +7812,7 @@ "name": "len", "signature": "pub fn len(&self) -> usize {", "doc": "Returns the number of tasks.", + "doc_zh": "返回任务数量。", "associated": true }, { @@ -6848,6 +7820,7 @@ "name": "total_duration", "signature": "pub fn total_duration(&self) -> f64 {", "doc": "Returns the total duration of every task, in seconds.\n\nThis is the load the tasks would place on the schedule if each used\none unit of resource for its whole duration.", + "doc_zh": "返回所有任务的总时长,单位为秒。\n\n这是各任务在整个时长内各占一个单位资源时,会施加到计划上的负载。", "associated": true }, { @@ -6855,6 +7828,7 @@ "name": "total_weighted_value", "signature": "pub fn total_weighted_value(&self) -> f64 {", "doc": "Returns the total weighted value of every task.", + "doc_zh": "返回所有任务的总加权价值。", "associated": true }, { @@ -6862,6 +7836,7 @@ "name": "tasks_requiring", "signature": "pub fn tasks_requiring(&self, resource: ResourceId) -> Vec {", "doc": "Returns the identifiers of the tasks that require a resource.", + "doc_zh": "返回要求某资源的任务标识符。", "associated": true }, { @@ -6869,6 +7844,7 @@ "name": "mandatory", "signature": "pub fn mandatory(&self) -> Vec {", "doc": "Returns the identifiers of the mandatory tasks.", + "doc_zh": "返回强制任务的标识符。", "associated": true }, { @@ -6876,6 +7852,7 @@ "name": "task_order", "signature": "pub fn task_order(tasks: &[Task]) -> Vec {", "doc": "Returns a task ordering suitable for a greedy scheduler.\n\nThe order is by decreasing weighted value, which is an efficient\nheuristic: placing the tasks that contribute most first leaves the least\nvaluable ones to fill the gaps. Ties are broken by the earlier deadline\nand then by the identifier, so that the order is deterministic for a given\nset.\n\nThis is not guaranteed to find the best possible schedule. Scheduling\nwith resource constraints is intractable in general, so a heuristic is the\npractical choice and the caller may supply its own order instead.", + "doc_zh": "返回适合贪心调度器的任务次序。\n\n该次序按加权价值递减排列,这是一个有效启发式:先放置贡献最大的任务,\n把价值最小的留给填补空隙。平局先按更早的截止时刻、再按标识符打破,故\n对给定集合次序是确定的。\n\n它不保证找到最优计划。带资源约束的调度在一般情况下不可解,故启发式\n是现实的选择,调用方亦可自行提供次序。", "associated": false } ] @@ -6883,13 +7860,15 @@ { "name": "apsis-scheduler", "layer": "ops", - "summary": "Scheduling algorithms for satellite task planning. A scheduler takes a list of tasks and a set of resources and produces a schedule. This crate provides a greedy scheduler and the pieces needed to build others. Scheduling with resource constraints is intractable in general: deciding whether every task fits is a bin packing problem, which is NP-hard. A satellite planning problem is small, with hundreds of tasks rather than millions, but an exact search is still impractical for a routine planning cycle. The scheduler here is therefore a greedy heuristic. The algorithm sorts the tasks and places each in the earliest time at which all of its resources are free together. Tasks are considered in decreasing order of weighted value, which is a good heuristic: the most valuable work is placed first and the least valuable fills the remaining gaps. Mandatory tasks are placed first regardless of their value, because a task that must run is worth more than any task that may. The result is not guaranteed to be optimal, and the crate reports what it could not place rather than dropping it silently, so that an operator can see the cost of the plan. ``` use apsis_resource::{Resource, ResourceId, ResourceSet, Window}; use apsis_scheduler::Planner; use apsis_task::{Task, TaskId}; let mut station = Resource::new(ResourceId::new(0), 1.0); station.set_windows(&[Window::new(0.0, 1_000.0)]).expect(\"usable\"); let resources = ResourceSet::from_resources(vec![station]).expect(\"distinct\"); let tasks = vec![Task::new(TaskId::new(0), 60.0) .try_require(ResourceId::new(0), 1.0) .expect(\"the resource is new\")]; let planner = Planner::new(); let result = planner.schedule(&tasks, &resources); assert_eq!(result.schedule.len(), 1); assert!(result.is_complete()); ```", + "summary": "Scheduling algorithms for satellite task planning.\n\nA scheduler takes a list of tasks and a set of resources and produces a\nschedule. This crate provides a greedy scheduler and the pieces needed to\nbuild others.\n\nScheduling with resource constraints is intractable in general: deciding\nwhether every task fits is a bin packing problem, which is NP-hard. A\nsatellite planning problem is small, with hundreds of tasks rather than\nmillions, but an exact search is still impractical for a routine planning\ncycle. The scheduler here is therefore a greedy heuristic.\n\nThe algorithm sorts the tasks and places each in the earliest time at\nwhich all of its resources are free together. Tasks are considered in\ndecreasing order of weighted value, which is a good heuristic: the most\nvaluable work is placed first and the least valuable fills the remaining\ngaps. Mandatory tasks are placed first regardless of their value, because\na task that must run is worth more than any task that may.\n\nThe result is not guaranteed to be optimal, and the crate reports what it\ncould not place rather than dropping it silently, so that an operator can\nsee the cost of the plan.\n\n```\nuse apsis_resource::{Resource, ResourceId, ResourceSet, Window};\nuse apsis_scheduler::Planner;\nuse apsis_task::{Task, TaskId};\n\nlet mut station = Resource::new(ResourceId::new(0), 1.0);\nstation.set_windows(&[Window::new(0.0, 1_000.0)]).expect(\"usable\");\nlet resources = ResourceSet::from_resources(vec![station]).expect(\"distinct\");\n\nlet tasks = vec![Task::new(TaskId::new(0), 60.0)\n .try_require(ResourceId::new(0), 1.0)\n .expect(\"the resource is new\")];\n\nlet planner = Planner::new();\nlet result = planner.schedule(&tasks, &resources);\nassert_eq!(result.schedule.len(), 1);\nassert!(result.is_complete());\n```", + "summary_zh": "卫星任务规划的调度算法。\n\n调度器接收任务列表与资源集合,产生一个调度。本 crate 提供一个贪心调度器以及\n构建其他调度器所需的构件。\n\n带资源约束的调度一般是难解的:判定是否每个任务都放得下是一个装箱问题,它是\nNP 难的。卫星规划问题规模小,有数百个任务而非数百万个,但对例行的规划周期而言,\n精确搜索仍不切实际。故此处的调度器是贪心启发式。\n\n该算法把任务排序,并把每个放到其全部资源同时空闲的最早时间。任务按加权价值递减\n的次序考虑,这是一个好的启发式:最有价值的工作先被放置,最不有价值的填充剩余\n空隙。强制任务不论其价值都先放置,因为一个必须运行的任务比任何可以运行的任务\n都值更多。\n\n结果不保证最优,且本 crate 报告它无法放置的内容而非无声丢弃,使操作员能看到该\n计划的代价。", "items": [ { "kind": "fn", "name": "resources_have_capacity", "signature": "pub fn resources_have_capacity(task: &Task, resources: &ResourceSet) -> bool {", "doc": "Returns `true` when every resource of a task has the capacity to hold its\ndemand.\n\nA task that demands more of a resource than the resource has can never be\nscheduled on it, so this is checked before the search for a time. Returns\n`false` when a required resource does not exist.", + "doc_zh": "当任务的每个资源都有容量容纳其需求时返回 `true`。\n\n需求超过某资源容量的任务永远无法在其上调度,故在搜索时刻之前先检查\n此点。所需资源不存在时返回 `false`。", "associated": false }, { @@ -6897,6 +7876,7 @@ "name": "demand_at", "signature": "pub fn demand_at( schedule: &Schedule, tasks: &[Task], resources: &ResourceSet, time: f64, ) -> Vec<(ResourceId, f64)> {", "doc": "Returns the total demand placed on each resource at an instant, given a\nschedule and the tasks it contains.\n\nThe result is in the order the resources appear in the set. A resource\nthat is not in the set is omitted.", + "doc_zh": "给定一份计划及其任务集合,返回某瞬时各资源上的总需求。\n\n结果按资源在集合中的次序排列。不在集合中的资源被省略。", "associated": false }, { @@ -6904,6 +7884,7 @@ "name": "resources_free_at", "signature": "pub fn resources_free_at( schedule: &Schedule, tasks: &[Task], resources: &ResourceSet, time: f64, ) -> Vec {", "doc": "Returns the identifiers of the resources that are free at an instant.\n\nA resource is free when its availability windows cover the instant and\nthe demand already placed on it leaves room for one more unit. This is\nthe query a watermark scheduler makes when it looks for a satellite with\nspare capacity.", + "doc_zh": "返回在某瞬空闲的资源标识符。\n\n资源空闲当且仅当其可用窗口覆盖该瞬,且其上已放置的需求还留有一个单\n位的余量。这正是水位调度器寻找有余量卫星时所作的查询。", "associated": false }, { @@ -6911,6 +7892,7 @@ "name": "feasible_at", "signature": "pub fn feasible_at( task: &Task, tasks: &[Task], schedule: &Schedule, resources: &ResourceSet, start: f64, ) -> bool {", "doc": "Returns `true` when a task placed at a time is feasible.\n\nThe check is that every required resource is available for the whole\nduration and that the demand already placed on it, plus the new demand,\nstays within the capacity at every instant of the placement.", + "doc_zh": "当某任务在某时刻的放置可行时返回 `true`。\n\n检查内容为:每个所需资源在整个时长上可用,且其上已放置的需求加上\n新需求,在放置的每个瞬时都不超过容量。", "associated": false }, { @@ -6918,6 +7900,7 @@ "name": "infeasible_tasks", "signature": "pub fn infeasible_tasks(tasks: &[Task], resources: &ResourceSet) -> Vec {", "doc": "Returns the identifiers of the tasks that a set of resources cannot\naccommodate at all.\n\nA task is infeasible when it demands more of a resource than the resource\nhas, when a resource it requires does not exist, or when its window is\nshorter than its duration. These cannot be repaired by rescheduling and\nare worth reporting separately from the tasks that were merely left out.", + "doc_zh": "返回某组资源完全无法容纳的任务标识符。\n\n任务不可行的情形有:需求超过某资源容量;所需资源不存在;窗口短于其\n时长。这些无法通过重排修复,值得与那些只是被排除的任务分开报告。", "associated": false }, { @@ -6925,6 +7908,7 @@ "name": "ImprovementSettings", "signature": "pub struct ImprovementSettings {", "doc": "The settings of the improvement pass.", + "doc_zh": "改进轮的设置。", "associated": false }, { @@ -6932,6 +7916,7 @@ "name": "new", "signature": "pub const fn new(max_attempts_per_task: usize) -> Self {", "doc": "Creates settings with the given bound.", + "doc_zh": "以给定界创建设置。", "associated": true }, { @@ -6939,6 +7924,7 @@ "name": "disabled", "signature": "pub const fn disabled() -> Self {", "doc": "Returns settings that do not run the pass.", + "doc_zh": "返回不运行该轮的设置。", "associated": true }, { @@ -6946,6 +7932,7 @@ "name": "ImprovementOutcome", "signature": "pub struct ImprovementOutcome {", "doc": "What the improvement pass changed.", + "doc_zh": "改进轮所作的改变。", "associated": false }, { @@ -6953,6 +7940,7 @@ "name": "placed_count", "signature": "pub fn placed_count(&self) -> usize {", "doc": "Returns the number of tasks the pass placed.", + "doc_zh": "返回该轮所放置的任务数。", "associated": true }, { @@ -6960,6 +7948,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when the pass changed nothing.", + "doc_zh": "当该轮未作任何改变时返回 `true`。", "associated": true }, { @@ -6967,6 +7956,7 @@ "name": "improve", "signature": "pub fn improve( tasks: &[Task], resources: &ResourceSet, deferred: &[TaskId], schedule: &mut Schedule, states: &mut [TaskState], settings: ImprovementSettings, ) -> ImprovementOutcome {", "doc": "Tries to place deferred tasks by relocating the placements that block them.\n\n`deferred` are the tasks the greedy pass could not place, in the order to\nconsider them. `states` holds the state of every task indexed by\nidentifier and is updated in place, so the caller sees the final\nassignment. The schedule is modified in place.\n\nReturns a record of what the pass placed and moved.", + "doc_zh": "通过搬迁阻塞它们的放置,尝试安排被推迟的任务。\n\n`deferred` 是贪心轮无法放置的任务,按考虑顺序排列。`states` 按标识符\n索引保存每个任务的状态并就地更新,故调用方看到的是最终指派。该计划\n亦被就地修改。\n\n返回记录该轮所放置与所移动内容的报告。", "associated": false }, { @@ -6974,6 +7964,7 @@ "name": "PlanOutcome", "signature": "pub struct PlanOutcome {", "doc": "What happened to one task during planning.\n\nThe outcome pairs a task with the state the planner assigned, which is the\ninformation an operator needs to see. It is separate from the schedule\nbecause a task that was not placed has no placement but still has an\noutcome worth reporting.", + "doc_zh": "规划过程中某个任务的结果。\n\n它把任务与规划器所指定的状态配对,这是操作者需要看到的信息。它与\n计划分开,因为未放置的任务虽无放置,却仍有一份值得报告的结果。", "associated": false }, { @@ -6981,6 +7972,7 @@ "name": "is_scheduled", "signature": "pub fn is_scheduled(&self) -> bool {", "doc": "Returns `true` when the task was placed.", + "doc_zh": "当该任务已被放置时返回 `true`。", "associated": true }, { @@ -6988,6 +7980,7 @@ "name": "is_infeasible", "signature": "pub fn is_infeasible(&self) -> bool {", "doc": "Returns `true` when the task could not be placed for a structural\nreason, rather than being left out for want of time.", + "doc_zh": "当该任务因结构性原因无法放置、而非因时间不足被排除时返回 `true`。", "associated": true }, { @@ -6995,6 +7988,7 @@ "name": "SchedulingResult", "signature": "pub struct SchedulingResult {", "doc": "The result of planning.", + "doc_zh": "规划的结果。", "associated": false }, { @@ -7002,6 +7996,7 @@ "name": "outcome_of", "signature": "pub fn outcome_of(&self, task: TaskId) -> Option<&PlanOutcome> {", "doc": "Returns the outcome of a task.", + "doc_zh": "返回某任务的结果。", "associated": true }, { @@ -7009,6 +8004,7 @@ "name": "scheduled", "signature": "pub fn scheduled(&self) -> Vec {", "doc": "Returns the identifiers of the tasks that were placed.", + "doc_zh": "返回已放置任务的标识符。", "associated": true }, { @@ -7016,6 +8012,7 @@ "name": "deferred", "signature": "pub fn deferred(&self) -> Vec {", "doc": "Returns the identifiers of the tasks that were left out.", + "doc_zh": "返回被排除任务的标识符。", "associated": true }, { @@ -7023,6 +8020,7 @@ "name": "infeasible", "signature": "pub fn infeasible(&self) -> Vec {", "doc": "Returns the identifiers of the tasks that cannot be placed at all.", + "doc_zh": "返回完全无法放置的任务的标识符。", "associated": true }, { @@ -7030,6 +8028,7 @@ "name": "is_complete", "signature": "pub fn is_complete(&self) -> bool {", "doc": "Returns `true` when every offered task was placed.", + "doc_zh": "当所有提交任务均被放置时返回 `true`。", "associated": true }, { @@ -7037,6 +8036,7 @@ "name": "honours_mandatory", "signature": "pub fn honours_mandatory(&self, tasks: &[Task]) -> bool {", "doc": "Returns `true` when every mandatory task was placed.\n\nA plan that leaves a mandatory task unplaced is not usable, however\nmuch optional work it found room for.", + "doc_zh": "当所有强制任务均被放置时返回 `true`。\n\n留下强制任务未放置的计划不可用,无论它为多少可选工作腾出了\n空间。", "associated": true }, { @@ -7044,6 +8044,7 @@ "name": "honours_precedence", "signature": "pub fn honours_precedence(&self, graph: &PrecedenceGraph) -> bool {", "doc": "Returns `true` when every precedence constraint is satisfied.\n\nA constraint is satisfied when the second task starts at or after the\nend of the first. A constraint whose tasks are not both placed is\ntrivially satisfied, because nothing has been promised about them.", + "doc_zh": "当所有优先约束都得到满足时返回 `true`。\n\n约束被满足,指第二个任务的起点不早于第一个的终点。两个任务未\n同时放置的约束平凡地满足,因为对它们未作任何承诺。", "associated": true }, { @@ -7051,6 +8052,7 @@ "name": "Planner", "signature": "pub struct Planner {", "doc": "A greedy planner.\n\nThe planner holds the settings of the search. The settings are separated\nfrom the schedule so that the same planner can be applied to many task\nsets with the same policy.", + "doc_zh": "贪心规划器。\n\n该规划器持有搜索的设置。设置与计划分开,故同一个规划器可按同一策略\n应用于许多任务集。", "associated": false }, { @@ -7058,6 +8060,7 @@ "name": "new", "signature": "pub const fn new() -> Self {", "doc": "Creates a planner with the default settings.\n\nThe defaults are the ones [`Planner::default`] uses: mandatory tasks\nfirst and the improvement pass enabled with four attempts per task.", + "doc_zh": "以默认设置创建规划器。\n\n默认值即 [`Planner::default`] 所用者:先放置强制任务,改进轮\n启用且每任务尝试四次。", "associated": true }, { @@ -7065,6 +8068,7 @@ "name": "with_mandatory_first", "signature": "pub const fn with_mandatory_first(mut self, mandatory_first: bool) -> Self {", "doc": "Returns a copy of this planner with the mandatory placement rule set.", + "doc_zh": "返回设置了强制放置规则的规划器副本。", "associated": true }, { @@ -7072,6 +8076,7 @@ "name": "with_improvement", "signature": "pub const fn with_improvement(mut self, improvement: ImprovementSettings) -> Self {", "doc": "Returns a copy of this planner with the improvement pass configured.", + "doc_zh": "返回配置了改进轮的规划器副本。", "associated": true }, { @@ -7079,6 +8084,7 @@ "name": "order", "signature": "pub fn order(&self, tasks: &[Task]) -> Vec {", "doc": "Returns the order in which the planner considers the tasks.\n\nWithout a precedence graph the order is mandatory tasks first, when\nthe planner is configured that way, and then decreasing weighted\nvalue with ties broken by deadline and identifier.\n\nWith a precedence graph the order is a topological order of the\ngraph, using the value order to break ties. That places a task as\nearly as its constraints allow and the most valuable one first among\nthe tasks that are free.", + "doc_zh": "返回规划器考虑任务的顺序。\n\n无优先关系图时,顺序是先强制任务(若规划器如此配置),再按加权\n价值递减,平局按截止时刻与标识符打破。\n\n有优先关系图时,顺序是该图的拓扑序,并以价值次序打破平局。这\n使任务在其约束允许的最早时刻被放置,而在自由的任务中优先放置\n价值最高者。", "associated": true }, { @@ -7086,6 +8092,7 @@ "name": "order_with_precedence", "signature": "pub fn order_with_precedence( &self, tasks: &[Task], precedence: Option<&PrecedenceGraph>, ) -> Vec {", "doc": "Returns the order in which the planner considers the tasks, honouring\na precedence graph.\n\nA graph whose constraints contain a cycle produces the value order\nunchanged, because the constraints then have no satisfiable\ninterpretation. The caller should validate the graph before\nplanning; the order is a fallback so that planning still produces\nsomething rather than failing outright.", + "doc_zh": "返回规划器考虑任务的顺序,并遵从给定的优先关系图。\n\n约束含环的图会原样产生价值次序,因为这些约束此时没有可满足的\n解释。调用方应在规划前校验该图;此处的次序是一个回退,使规划\n仍能产出结果而非直接失败。", "associated": true }, { @@ -7093,6 +8100,7 @@ "name": "schedule", "signature": "pub fn schedule(&self, tasks: &[Task], resources: &ResourceSet) -> SchedulingResult {", "doc": "Plans a set of tasks on a set of resources.\n\nEvery task is considered once, in the order the planner chooses. A\ntask is placed at the earliest time at which all of its resources are\nfree together. A task that cannot be placed is reported as deferred,\nor as infeasible when no placement could ever exist.\n\nThe schedule that is returned is feasible by construction, because\neach placement is checked against the capacity before it is made. The\ncaller may confirm that with `apsis-conflict`.", + "doc_zh": "在一组资源上规划一组任务。\n\n每个任务按规划器所选的顺序被考虑一次,被放置在其全部资源恰好同时\n空闲的最早时刻。无法放置的任务被报告为被推迟;不存在任何可行\n放置的则报告为不可行。\n\n返回的计划按构造即可行,因为每处放置在做下之前都对照容量校验\n过。调用方可用 `apsis-conflict` 复核。", "associated": true }, { @@ -7100,6 +8108,7 @@ "name": "schedule_with_precedence", "signature": "pub fn schedule_with_precedence( &self, tasks: &[Task], resources: &ResourceSet, precedence: &PrecedenceGraph, ) -> SchedulingResult {", "doc": "Plans a set of tasks, honouring a precedence graph.\n\nA task is placed only after every task that must precede it has been\nplaced, so the search for its start begins no earlier than the latest\nend among its predecessors. A predecessor that could not be placed\nmakes the successor deferred as well, because the constraint cannot be\nmet.\n\nThe improvement pass is not applied to a plan with precedence\nconstraints, because relocating a placement could break a constraint\nthat the pass does not consider. A caller that needs both should\nimprove the plan and then verify it with\n[`SchedulingResult::honours_precedence`].", + "doc_zh": "遵从优先关系图规划一组任务。\n\n任务仅在其所有前驱均已放置之后才被放置,故其起点搜索不早于\n前驱中最晚的结束时刻。无法放置的前驱会使其后继同样被推迟,\n因为该约束无法满足。\n\n改进轮不作用于带优先约束的计划,因为移动某处放置可能破坏该轮\n并不考虑的约束。两者皆需的调用方应先改进计划,再用\n[`SchedulingResult::honours_precedence`] 复核。", "associated": true }, { @@ -7107,6 +8116,7 @@ "name": "earliest_feasible_start", "signature": "pub fn earliest_feasible_start( task: &Task, tasks: &[Task], schedule: &Schedule, resources: &ResourceSet, ) -> Option {", "doc": "Returns the earliest time at which a task can be placed.\n\nThe earliest feasible start is always one of a small set of candidate\ntimes: the start of the task's window, the start of an availability\nwindow of one of its resources, or the end of a placement already on one\nof those resources. Feasibility cannot change at any other instant,\nbecause the demand on every resource is constant between those events and\nthe availability is constant between them too.\n\nReturns `None` when the task cannot be placed anywhere in its window.", + "doc_zh": "返回某任务可被放置的最早时刻。\n\n最早可行起点总是某个小候选集合之一:任务窗口的起点、其某资源可用窗口\n的起点,或其某资源上已有放置的结束时刻。可行性在其他任何瞬时都不会\n改变,因为各资源的需求在这些事件之间恒定,可用性亦然。\n\n该任务在其窗口内无处可放时返回 `None`。", "associated": false } ] @@ -7114,13 +8124,15 @@ { "name": "apsis-conflict", "layer": "ops", - "summary": "Resource and time conflict detection. A schedule can be wrong in several ways, and this crate finds them. It reports conflicts rather than refusing them, so that a plan loaded from a file can be checked in full and every problem listed at once instead of one per attempt. Five kinds of conflict are detected: - **Capacity**: two placements need more of a resource at the same instant than the resource has. - **Window**: a placement starts before its task may, or ends after it must. - **Requirement**: a placement's task is not known, or its duration does not match the schedule. - **Availability**: a placement falls outside the availability windows of a resource it needs, so the resource would not actually be there. - **Precedence**: a task that must follow another starts before it ends. The distinction between a capacity conflict and an availability conflict matters in practice: the first says the plan is over committed, the second says the plan asks for a resource at a time when it is switched off or out of view, which is a different repair. ``` use apsis_resource::{Resource, ResourceId, ResourceSet, Window}; use apsis_schedule::Schedule; use apsis_task::{Task, TaskId}; use apsis_conflict::{ConflictKind, detect}; let mut station = Resource::new(ResourceId::new(0), 1.0); station.set_windows(&[Window::new(0.0, 1_000.0)]).expect(\"usable\"); let resources = ResourceSet::from_resources(vec![station]).expect(\"distinct\"); let task = Task::new(TaskId::new(0), 60.0) .try_require(ResourceId::new(0), 1.0) .expect(\"the resource is new\"); let mut schedule = Schedule::new(); schedule.place(&task, 100.0).expect(\"the task fits\"); let report = detect(&schedule, &[task], &resources); assert!(report.is_clear()); assert_eq!(report.conflicts().len(), 0); let _ = ConflictKind::Capacity; ```", + "summary": "Resource and time conflict detection.\n\nA schedule can be wrong in several ways, and this crate finds them. It\nreports conflicts rather than refusing them, so that a plan loaded from a\nfile can be checked in full and every problem listed at once instead of\none per attempt.\n\nFive kinds of conflict are detected:\n\n- **Capacity**: two placements need more of a resource at the same instant\n than the resource has.\n- **Window**: a placement starts before its task may, or ends after it\n must.\n- **Requirement**: a placement's task is not known, or its duration does\n not match the schedule.\n- **Availability**: a placement falls outside the availability windows of\n a resource it needs, so the resource would not actually be there.\n- **Precedence**: a task that must follow another starts before it ends.\n\nThe distinction between a capacity conflict and an availability conflict\nmatters in practice: the first says the plan is over committed, the second\nsays the plan asks for a resource at a time when it is switched off or out\nof view, which is a different repair.\n\n```\nuse apsis_resource::{Resource, ResourceId, ResourceSet, Window};\nuse apsis_schedule::Schedule;\nuse apsis_task::{Task, TaskId};\nuse apsis_conflict::{ConflictKind, detect};\n\nlet mut station = Resource::new(ResourceId::new(0), 1.0);\nstation.set_windows(&[Window::new(0.0, 1_000.0)]).expect(\"usable\");\nlet resources = ResourceSet::from_resources(vec![station]).expect(\"distinct\");\n\nlet task = Task::new(TaskId::new(0), 60.0)\n .try_require(ResourceId::new(0), 1.0)\n .expect(\"the resource is new\");\nlet mut schedule = Schedule::new();\nschedule.place(&task, 100.0).expect(\"the task fits\");\n\nlet report = detect(&schedule, &[task], &resources);\nassert!(report.is_clear());\nassert_eq!(report.conflicts().len(), 0);\nlet _ = ConflictKind::Capacity;\n```", + "summary_zh": "资源与时间冲突检测。\n\n一份调度可能以若干方式出错,本 crate 找出它们。它报告冲突而非拒绝它们,使从\n文件加载的计划能被完整检查,并一次列出每个问题,而不是每次尝试报一个。\n\n检测五类冲突:\n\n- **容量**:两处放置在同一瞬时需要的某资源超过该资源所有。\n- **窗口**:一处放置在任务可开始之前开始,或在它必须结束之后结束。\n- **需求**:一处放置的任务未知,或其时长与调度不符。\n- **可用性**:一处放置落在它所需某资源的可用性窗口之外,故该资源实际上不在场。\n- **先后**:一个必须跟随另一任务的任务在其结束之前开始。\n\n容量冲突与可用性冲突之别在实践中要紧:前者说明计划超额承诺,后者说明计划在资源\n被关闭或不可见之时索求它,二者是不同的修复。", "items": [ { "kind": "enum", "name": "ConflictKind", "signature": "pub enum ConflictKind {", "doc": "The kind of problem a conflict describes.", + "doc_zh": "冲突所描述的问题类别。", "associated": false }, { @@ -7128,6 +8140,7 @@ "name": "Conflict", "signature": "pub struct Conflict {", "doc": "One problem found in a schedule.\n\nThe conflict names the tasks involved and, where it applies, the resource\nand the instant at which the problem occurs. That is enough for an\noperator to locate the placement without re-deriving the analysis.", + "doc_zh": "计划中发现的单个问题。\n\n冲突指明所涉任务,并在适用时指明资源及问题发生的时刻。这足以让操作\n者定位放置,而无须重新推导分析。", "associated": false }, { @@ -7135,6 +8148,7 @@ "name": "new", "signature": "pub fn new(kind: ConflictKind, task: TaskId, at: f64, detail: impl Into) -> Self {", "doc": "Creates a conflict.", + "doc_zh": "创建一个冲突。", "associated": true }, { @@ -7142,6 +8156,7 @@ "name": "with_other", "signature": "pub fn with_other(mut self, other: TaskId) -> Self {", "doc": "Returns a copy of this conflict involving another task.", + "doc_zh": "返回涉及另一任务的本冲突副本。", "associated": true }, { @@ -7149,6 +8164,7 @@ "name": "with_resource", "signature": "pub fn with_resource(mut self, resource: ResourceId) -> Self {", "doc": "Returns a copy of this conflict involving a resource.", + "doc_zh": "返回涉及某资源的本冲突副本。", "associated": true }, { @@ -7156,6 +8172,7 @@ "name": "is_movable", "signature": "pub const fn is_movable(&self) -> bool {", "doc": "Returns `true` when the conflict concerns a placement that should be\nmoved rather than one that should be dropped.\n\nA capacity or availability conflict can often be repaired by moving\nthe placement, while a requirement conflict means the placement does\nnot belong in the schedule at all.", + "doc_zh": "当该冲突涉及一处应当移动而非丢弃的放置时返回 `true`。\n\n容量或可用性冲突常可通过移动放置来修复,而需求冲突意味着该\n放置本就不应出现在计划中。", "associated": true }, { @@ -7163,6 +8180,7 @@ "name": "ConflictReport", "signature": "pub struct ConflictReport {", "doc": "The conflicts found in a schedule.\n\nThe report is ordered by time and then by kind, so that reading it\nfollows the plan. The counts by kind are provided because a planner\nusually wants to know how many of each problem there are before deciding\nwhat to do.", + "doc_zh": "计划中发现的所有冲突。\n\n报告按时间、再按类别排序,故阅读它即是在阅读该计划。各类计数一并\n提供,因为规划器通常希望先知道每类问题各有若干,再决定对策。", "associated": false }, { @@ -7170,6 +8188,7 @@ "name": "new", "signature": "pub fn new(mut conflicts: Vec, offered_tasks: usize, placed_tasks: usize) -> Self {", "doc": "Creates a report from a list of conflicts and the task counts.\n\nThe conflicts are sorted by time, then by kind, then by task, so that\nthe order does not depend on the order they were found.", + "doc_zh": "由一组冲突与任务计数创建报告。\n\n冲突按时间、类别、任务依次排序,故其次序不取决于发现它们的\n顺序。", "associated": true }, { @@ -7177,6 +8196,7 @@ "name": "conflicts", "signature": "pub fn conflicts(&self) -> &[Conflict] {", "doc": "Returns the conflicts, in time order.", + "doc_zh": "返回各冲突,按时间排序。", "associated": true }, { @@ -7184,6 +8204,7 @@ "name": "len", "signature": "pub fn len(&self) -> usize {", "doc": "Returns the number of conflicts.", + "doc_zh": "返回冲突数量。", "associated": true }, { @@ -7191,6 +8212,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when there are no conflicts.\n\nThis is the conventional spelling of [`ConflictReport::is_clear`], and\nthe two always agree.", + "doc_zh": "当没有冲突时返回 `true`。\n\n这是 [`ConflictReport::is_clear`] 的惯用拼写,二者始终一致。", "associated": true }, { @@ -7198,6 +8220,7 @@ "name": "is_clear", "signature": "pub fn is_clear(&self) -> bool {", "doc": "Returns `true` when there are no conflicts.\n\nA clear report means the schedule is feasible with respect to every\ncheck this crate performs, not that it is optimal.", + "doc_zh": "当没有冲突时返回 `true`。\n\n报告无冲突意味着该计划就本 crate 执行的每项检查而言是可行的,\n并不意味着它最优。", "associated": true }, { @@ -7205,6 +8228,7 @@ "name": "has_conflicts", "signature": "pub fn has_conflicts(&self) -> bool {", "doc": "Returns `true` when there is at least one conflict.", + "doc_zh": "当至少存在一个冲突时返回 `true`。", "associated": true }, { @@ -7212,6 +8236,7 @@ "name": "count_of", "signature": "pub fn count_of(&self, kind: ConflictKind) -> usize {", "doc": "Returns the number of conflicts of one kind.", + "doc_zh": "返回某一类别的冲突数量。", "associated": true }, { @@ -7219,6 +8244,7 @@ "name": "of_kind", "signature": "pub fn of_kind(&self, kind: ConflictKind) -> Vec<&Conflict> {", "doc": "Returns the conflicts of one kind, in time order.", + "doc_zh": "返回某一类别的冲突,按时间排序。", "associated": true }, { @@ -7226,6 +8252,7 @@ "name": "conflicting_tasks", "signature": "pub fn conflicting_tasks(&self) -> Vec {", "doc": "Returns the tasks involved in at least one conflict.\n\nA task appears once even when it has several conflicts, which is what\na repair pass wants: the set of tasks to reconsider.", + "doc_zh": "返回至少卷入一个冲突的任务。\n\n同一任务即使有多个冲突也只出现一次,这正是一个修复轮所需:待重新\n考虑的任务集合。", "associated": true }, { @@ -7233,6 +8260,7 @@ "name": "offered_tasks", "signature": "pub fn offered_tasks(&self) -> usize {", "doc": "Returns the number of tasks that were offered to the schedule.", + "doc_zh": "返回提交给该计划的任务数量。", "associated": true }, { @@ -7240,6 +8268,7 @@ "name": "placed_tasks", "signature": "pub fn placed_tasks(&self) -> usize {", "doc": "Returns the number of tasks that were placed.", + "doc_zh": "返回已被放置的任务数量。", "associated": true }, { @@ -7247,6 +8276,7 @@ "name": "summary", "signature": "pub fn summary(&self) -> String {", "doc": "Returns a one line summary of the report.", + "doc_zh": "返回该报告的单行摘要。", "associated": true }, { @@ -7254,6 +8284,7 @@ "name": "Precedence", "signature": "pub struct Precedence {", "doc": "A precedence constraint: one task must finish before another starts.", + "doc_zh": "一条优先约束:一个任务必须先于另一个开始而完成。", "associated": false }, { @@ -7261,6 +8292,7 @@ "name": "new", "signature": "pub const fn new(before: TaskId, after: TaskId) -> Self {", "doc": "Creates a precedence constraint.", + "doc_zh": "创建一条优先约束。", "associated": true }, { @@ -7268,6 +8300,7 @@ "name": "placements_of", "signature": "pub fn placements_of(schedule: &Schedule, task: TaskId) -> Option<&apsis_schedule::Placement> {", "doc": "Returns the placements of a task, which is at most one.", + "doc_zh": "返回某任务的放置,至多一处。", "associated": false }, { @@ -7275,6 +8308,7 @@ "name": "detect", "signature": "pub fn detect(schedule: &Schedule, tasks: &[Task], resources: &ResourceSet) -> ConflictReport {", "doc": "Returns every conflict in a schedule.\n\nThe checks are performed in full: the report contains every problem found,\nnot just the first. That is deliberate, because a planner that is handed a\nplan usually wants to know how bad it is before deciding whether to repair\nor rebuild.\n\nThe checks are:\n\n- every placement's task exists in the list, and the durations agree;\n- every placement falls inside its task's window;\n- no resource exceeds its capacity at any instant;\n- every placement falls inside the availability windows of each resource\n it requires.\n\nThe resource sweep examines the boundaries of the placements, because the\ntotal demand on a resource is constant between them, so the check is exact\nand takes time proportional to the number of placements rather than to the\nlength of the plan.", + "doc_zh": "返回计划中的所有冲突。\n\n检查是完整执行的:报告包含所有发现的问题,而非只有首个。这是有意\n的,因为拿到一份计划的规划器通常希望先知道它有多糟,再决定修复还是\n重建。\n\n检查项为:\n\n- 每处放置的任务都在列表中,且时长一致;\n- 每处放置都落在其任务窗口内;\n- 任何瞬时没有资源超出其容量;\n- 每处放置都落在其所要求的每个资源的可用窗口内。\n\n资源扫掠检查各放置的边界,因为资源上的总需求在相邻边界之间恒定,\n故该检查是精确的,耗时正比于放置数而非计划长度。", "associated": false }, { @@ -7282,6 +8316,7 @@ "name": "detect_precedence", "signature": "pub fn detect_precedence(schedule: &Schedule, constraints: &[Precedence]) -> Vec {", "doc": "Returns the precedence conflicts among a set of constraints.\n\nA precedence conflict occurs when the task that must come second starts\nbefore the one that must come first has finished. A constraint whose\ntasks are not both placed is not a conflict, because nothing has been\npromised about them.\n\nThe constraints are checked as given, so they must be acyclic. A cycle is\na contradiction in the caller's own model and is not detected here.", + "doc_zh": "返回一组约束中的优先冲突。\n\n当本应居次的任务在应居首者完成之前开始,即为优先冲突。若某约束的两\n个任务未被同时放置,则不构成冲突,因为对它们未作任何承诺。\n\n约束按给定形式检查,故必须无环。环是调用方自身模型中的矛盾,此处\n不予检测。", "associated": false } ] @@ -7289,13 +8324,15 @@ { "name": "apsis-schedule", "layer": "ops", - "summary": "Schedule representation and evaluation. A schedule is the answer a scheduler produces: for each task, when it runs and on which resources. This crate holds that answer and evaluates it. It does not decide the placements, which is the job of `apsis-scheduler`; keeping the two apart lets a schedule be read from a file, produced by a scheduler, or written by hand, and evaluated the same way. A placement records the task, its start time, and the resources it holds. The resources are checked against the task's requirements when the placement is made, so a schedule cannot describe a task running on a resource it did not ask for. The evaluation reports what an operator asks of a finished plan: how many tasks were placed, how much of each resource is used at its busiest, and how much of the available time was actually spent. Those figures are what a planner compares between candidate schedules. ``` use apsis_resource::ResourceId; use apsis_schedule::Schedule; use apsis_task::{Task, TaskId}; let task = Task::new(TaskId::new(0), 60.0) .try_require(ResourceId::new(0), 1.0) .expect(\"the resource is not yet required\"); let mut schedule = Schedule::new(); schedule.place(&task, 100.0).expect(\"the task fits\"); assert_eq!(schedule.len(), 1); assert!(schedule.is_placed(TaskId::new(0))); ```", + "summary": "Schedule representation and evaluation.\n\nA schedule is the answer a scheduler produces: for each task, when it runs\nand on which resources. This crate holds that answer and evaluates it.\nIt does not decide the placements, which is the job of `apsis-scheduler`;\nkeeping the two apart lets a schedule be read from a file, produced by a\nscheduler, or written by hand, and evaluated the same way.\n\nA placement records the task, its start time, and the resources it holds.\nThe resources are checked against the task's requirements when the\nplacement is made, so a schedule cannot describe a task running on a\nresource it did not ask for.\n\nThe evaluation reports what an operator asks of a finished plan: how many\ntasks were placed, how much of each resource is used at its busiest, and\nhow much of the available time was actually spent. Those figures are what\na planner compares between candidate schedules.\n\n```\nuse apsis_resource::ResourceId;\nuse apsis_schedule::Schedule;\nuse apsis_task::{Task, TaskId};\n\nlet task = Task::new(TaskId::new(0), 60.0)\n .try_require(ResourceId::new(0), 1.0)\n .expect(\"the resource is not yet required\");\n\nlet mut schedule = Schedule::new();\nschedule.place(&task, 100.0).expect(\"the task fits\");\nassert_eq!(schedule.len(), 1);\nassert!(schedule.is_placed(TaskId::new(0)));\n```", + "summary_zh": "调度的表示与评估。\n\n调度是调度器所产生的答案:对每个任务,它何时运行以及在哪些资源上运行。本\ncrate 保存该答案并评估它。它不决定放置,那是 `apsis-scheduler` 的事;把二者\n分开,使一个调度可以从文件读取、由调度器产生,或手工书写,并以相同方式评估。\n\n一处放置记录任务、其开始时间,以及它所持有的资源。放置时各资源会针对任务的需求\n检查,故调度无法描述一个运行在它并未请求的资源上的任务。\n\n评估报告操作员对已完成计划所问的:放置了多少任务、每种资源在其最繁忙时被用去\n多少,以及可用时间实际花去多少。这些数字正是规划器在候选调度之间比较的。", "items": [ { "kind": "enum", "name": "ScheduleErrorKind", "signature": "pub enum ScheduleErrorKind {", "doc": "The category of a schedule error.", + "doc_zh": "计划错误的类别。", "associated": false }, { @@ -7303,6 +8340,7 @@ "name": "ScheduleError", "signature": "pub struct ScheduleError {", "doc": "A failure while building or evaluating a schedule.", + "doc_zh": "构建或评估计划时发生的失败。", "associated": false }, { @@ -7310,6 +8348,7 @@ "name": "new", "signature": "pub fn new(kind: ScheduleErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -7317,6 +8356,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the values that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -7324,6 +8364,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> ScheduleErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -7331,6 +8372,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -7338,6 +8380,7 @@ "name": "Placement", "signature": "pub struct Placement {", "doc": "One task placed on the schedule.", + "doc_zh": "计划上放置的一个任务。", "associated": false }, { @@ -7345,6 +8388,7 @@ "name": "new", "signature": "pub const fn new(task: TaskId, start: f64, duration: f64) -> Self {", "doc": "Creates a placement.", + "doc_zh": "创建一次放置。", "associated": true }, { @@ -7352,6 +8396,7 @@ "name": "end", "signature": "pub fn end(&self) -> f64 {", "doc": "Returns the end time of the placement, in seconds.", + "doc_zh": "返回放置的结束时刻,单位为秒。", "associated": true }, { @@ -7359,6 +8404,7 @@ "name": "covers", "signature": "pub fn covers(&self, time: f64) -> bool {", "doc": "Returns `true` when the placement covers an instant.\n\nThe interval is half-open, so a placement covers its start and not its\nend. That convention lets two back to back placements share a boundary\nwithout overlapping.", + "doc_zh": "当该放置覆盖某瞬时返回 `true`。\n\n区间是半开的,故放置含其起点而不含其终点。该约定使两个首尾相接的\n放置可共享边界而不重叠。", "associated": true }, { @@ -7366,6 +8412,7 @@ "name": "overlaps", "signature": "pub fn overlaps(&self, other: &Self) -> bool {", "doc": "Returns `true` when the placement overlaps another.", + "doc_zh": "当该放置与另一个放置重叠时返回 `true`。", "associated": true }, { @@ -7373,6 +8420,7 @@ "name": "is_valid", "signature": "pub fn is_valid(&self) -> bool {", "doc": "Returns `true` when the placement is usable.", + "doc_zh": "当该放置可用时返回 `true`。", "associated": true }, { @@ -7380,6 +8428,7 @@ "name": "ResourceUsage", "signature": "pub struct ResourceUsage {", "doc": "How much of one resource a schedule uses.", + "doc_zh": "一份计划对某个资源的使用情况。", "associated": false }, { @@ -7387,6 +8436,7 @@ "name": "utilisation", "signature": "pub fn utilisation(&self, interval_seconds: f64) -> Option {", "doc": "Returns the utilisation of the resource over an interval.\n\nThe value is the busy time divided by the length of the interval. It\nis a time utilisation, not a capacity utilisation: a resource that is\nonly half occupied by demand still counts as busy.\n\nReturns `None` when the interval is not positive.", + "doc_zh": "返回该资源在某区间上的利用率。\n\n其值为忙碌时长除以区间长度。这是时间利用率而非容量利用率:仅被\n需求占据一半的资源同样算作忙碌。\n\n区间非正时返回 `None`。", "associated": true }, { @@ -7394,6 +8444,7 @@ "name": "Evaluation", "signature": "pub struct Evaluation {", "doc": "The evaluation of a whole schedule.", + "doc_zh": "整份计划的评估结果。", "associated": false }, { @@ -7401,6 +8452,7 @@ "name": "makespan", "signature": "pub fn makespan(&self) -> f64 {", "doc": "Returns the length of the planning horizon the schedule covers, in\nseconds.\n\nThis is the time from the earliest start to the latest end, which is\nthe makespan of the plan.", + "doc_zh": "返回该计划覆盖的规划时域长度,单位为秒。\n\n它是从最早开始到最晚结束的时间,即该计划的 makespan。", "associated": true }, { @@ -7408,6 +8460,7 @@ "name": "fill_rate", "signature": "pub fn fill_rate(&self) -> Option {", "doc": "Returns the fraction of the offered tasks that were placed.\n\nReturns `None` when no tasks were offered.", + "doc_zh": "返回所提交任务中被放置的比例。\n\n没有提交任务时返回 `None`。", "associated": true }, { @@ -7415,6 +8468,7 @@ "name": "is_complete", "signature": "pub fn is_complete(&self) -> bool {", "doc": "Returns `true` when every offered task was placed.", + "doc_zh": "当所有提交任务均被放置时返回 `true`。", "associated": true }, { @@ -7422,6 +8476,7 @@ "name": "usage_of", "signature": "pub fn usage_of(&self, resource: ResourceId) -> Option<&ResourceUsage> {", "doc": "Returns the usage of one resource.", + "doc_zh": "返回某个资源的使用情况。", "associated": true }, { @@ -7429,6 +8484,7 @@ "name": "Schedule", "signature": "pub struct Schedule {", "doc": "A set of task placements.\n\nThe schedule stores placements in the order they were made and keeps an\nindex from task to placement, so that a lookup by task is direct. A task\nmay be placed at most once: placing it again is an error rather than a\nsilent overwrite, because a schedule that contains two placements for one\ntask is not a schedule.", + "doc_zh": "一组任务放置。\n\n该计划按放置发生的顺序保存各项,并维护从任务到放置的索引,故按任务查找\n是直接的。一个任务至多放置一次:重复放置是错误而非静默覆盖,因为含有\n同一任务两处放置的集合已不是一份计划。", "associated": false }, { @@ -7436,6 +8492,7 @@ "name": "new", "signature": "pub fn new() -> Self {", "doc": "Creates an empty schedule.", + "doc_zh": "创建一份空计划。", "associated": true }, { @@ -7443,6 +8500,7 @@ "name": "placements", "signature": "pub fn placements(&self) -> &[Placement] {", "doc": "Returns the placements in the order they were made.", + "doc_zh": "返回按放置顺序排列的各项。", "associated": true }, { @@ -7450,6 +8508,7 @@ "name": "len", "signature": "pub fn len(&self) -> usize {", "doc": "Returns the number of placements.", + "doc_zh": "返回放置的数量。", "associated": true }, { @@ -7457,6 +8516,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when there are no placements.", + "doc_zh": "当没有放置时返回 `true`。", "associated": true }, { @@ -7464,6 +8524,7 @@ "name": "is_placed", "signature": "pub fn is_placed(&self, task: TaskId) -> bool {", "doc": "Returns `true` when a task has been placed.", + "doc_zh": "当某任务已被放置时返回 `true`。", "associated": true }, { @@ -7471,6 +8532,7 @@ "name": "placement_of", "signature": "pub fn placement_of(&self, task: TaskId) -> Option<&Placement> {", "doc": "Returns the placement of a task.", + "doc_zh": "返回某任务的放置。", "associated": true }, { @@ -7478,6 +8540,7 @@ "name": "place", "signature": "pub fn place( &mut self, task: &apsis_task::Task, start: f64, ) -> Result<&Placement, ScheduleError> {", "doc": "Places a task at a time.\n\nThe placement is checked against the task's window. The resources of\nthe task are recorded with the placement through the task itself, so\nthis method does not need them; the capacity check is performed by\n[`Schedule::validate_capacity`], which needs the resources.\n\nReturns an error when the task is already placed, when the start time\nis not finite, or when the placement falls outside the task's window.", + "doc_zh": "把某任务放置在某时刻。\n\n放置会对照该任务的窗口校验。任务的资源通过任务本身随放置一同记录,\n故本方法无需它们;容量检查由需要资源的\n[`Schedule::validate_capacity`] 完成。\n\n该任务已被放置、起始时刻非有限,或放置落在任务窗口之外时返回错误。", "associated": true }, { @@ -7485,6 +8548,7 @@ "name": "unplace", "signature": "pub fn unplace(&mut self, task: TaskId) -> Result {", "doc": "Removes the placement of a task.\n\nReturns an error when the task is not placed.", + "doc_zh": "移除某任务的放置。\n\n该任务未被放置时返回错误。", "associated": true }, { @@ -7492,6 +8556,7 @@ "name": "placements_on", "signature": "pub fn placements_on( &self, resource: ResourceId, tasks: &[apsis_task::Task], ) -> Vec {", "doc": "Returns the placements that use a resource, in time order.", + "doc_zh": "返回使用某资源的放置,按时间排序。", "associated": true }, { @@ -7499,6 +8564,7 @@ "name": "has_overlapping_placements", "signature": "pub fn has_overlapping_placements(&self) -> bool {", "doc": "Returns `true` when two placements of different tasks overlap.\n\nThis checks the schedule against itself, ignoring the resources. Two\ntasks on disjoint resources may legitimately overlap, so a caller that\nwants to know whether a resource is overbooked should use\n[`Schedule::validate_capacity`] instead.", + "doc_zh": "当两个不同任务的放置重叠时返回 `true`。\n\n这是计划与自身的比较,忽略资源。位于不相交资源上的两个任务本就可\n以合法重叠,故欲知某资源是否超订的调用方应改用\n[`Schedule::validate_capacity`]。", "associated": true }, { @@ -7506,6 +8572,7 @@ "name": "scheduled_seconds", "signature": "pub fn scheduled_seconds(&self) -> f64 {", "doc": "Returns the total placed duration, in seconds.", + "doc_zh": "返回已放置的总时长,单位为秒。", "associated": true }, { @@ -7513,6 +8580,7 @@ "name": "earliest_start", "signature": "pub fn earliest_start(&self) -> Option {", "doc": "Returns the earliest start of any placement.\n\nReturns `None` when the schedule is empty.", + "doc_zh": "返回所有放置中最早的开始时刻。\n\n计划为空时返回 `None`。", "associated": true }, { @@ -7520,6 +8588,7 @@ "name": "latest_end", "signature": "pub fn latest_end(&self) -> Option {", "doc": "Returns the latest end of any placement.\n\nReturns `None` when the schedule is empty.", + "doc_zh": "返回所有放置中最晚的结束时刻。\n\n计划为空时返回 `None`。", "associated": true }, { @@ -7527,6 +8596,7 @@ "name": "validate_capacity", "signature": "pub fn validate_capacity( &self, tasks: &[apsis_task::Task], resources: &apsis_resource::ResourceSet, ) -> Result<(), ScheduleError> {", "doc": "Validates a schedule against a set of resources.\n\nThe check has two parts. First every placement must agree with the\nrequirements of its task, which catches a schedule built with a\ndifferent task list. Then, for each resource, the total demand placed\nat any instant must not exceed the capacity.\n\nThe second check walks the placement boundaries of each resource in\ntime order, which is exact because the total demand is constant\nbetween boundaries.\n\nReturns an error describing the first violation.", + "doc_zh": "对照一组资源校验该计划。\n\n检查分两部分。其一,每处放置须与其任务的需求一致,这可捕获用另\n一份任务列表建成的计划。其二,对每个资源,任意瞬时的总需求不得超\n过容量。\n\n第二部分按时间序遍历各资源的放置边界,由于相邻边界之间总需求恒定,\n该遍历是精确的。\n\n返回描述首个违规的错误。", "associated": true }, { @@ -7534,6 +8604,7 @@ "name": "evaluate", "signature": "pub fn evaluate( &self, tasks: &[apsis_task::Task], resources: &apsis_resource::ResourceSet, ) -> Evaluation {", "doc": "Evaluates the schedule against a task list and a resource set.\n\nThe evaluation reports the counts, the makespan, and the usage of each\nresource. The peak demand of a resource is found by the same event\nsweep that the capacity check uses, so the two agree.", + "doc_zh": "对照任务列表与资源集合评估该计划。\n\n评估给出各项计数、makespan 以及每个资源的使用情况。资源的峰值\n需求由与容量检查相同的事件扫掠求得,故二者一致。", "associated": true }, { @@ -7541,6 +8612,7 @@ "name": "in_time_order", "signature": "pub fn in_time_order(&self) -> Vec {", "doc": "Returns the schedule sorted by start time, leaving the original alone.\n\nA schedule is stored in the order it was built, which is the order a\nscheduler considered the tasks. A caller that wants to read the plan\nin time order uses this.", + "doc_zh": "返回按开始时刻排序的计划副本,原计划不变。\n\n计划按其构建顺序存储,即调度器考虑任务的顺序。欲按时间阅读该计\n划的调用方使用本方法。", "associated": true }, { @@ -7548,6 +8620,7 @@ "name": "resource_busy_until", "signature": "pub fn resource_busy_until( &self, resource: ResourceId, tasks: &[apsis_task::Task], time: f64, ) -> Option {", "doc": "Returns the time at which a resource becomes free if it is busy at a\ngiven instant.\n\nReturns `None` when the resource is idle there. This is what a\nsequential scheduler asks when it looks for the next gap.", + "doc_zh": "若某资源在给定瞬时忙碌,返回其变为空闲的时刻。\n\n该资源在彼处空闲时返回 `None`。顺序调度器寻找下一个空隙时正是\n询问这一点。", "associated": true } ] @@ -7555,13 +8628,15 @@ { "name": "apsis-sim-engine", "layer": "sim", - "summary": "Discrete-event simulation engine. The engine advances a clock by jumping from one event to the next. It holds the event queue, the current simulation time, and the statistics a caller needs to judge a run. What an event means is the caller's: it supplies a handler that receives the event and may schedule more events, and the engine only sequences the calls. The engine keeps the simulation deterministic. Events at the same instant are delivered in the order they were scheduled, which the event queue guarantees, so a run with the same inputs produces the same output. That is what makes a result reproducible and a cross-check against another implementation meaningful. The engine stops for one of four reasons, reported as a [`StopReason`]: the queue empties, the simulation reaches its time limit, the caller asks it to stop, or an event limit is reached. The last two exist so that a model that schedules events forever cannot run without bound. ``` use apsis_sim_engine::Engine; let mut engine = Engine::new(); engine.schedule(10.0, \"hello\"); let mut seen = Vec::new(); let outcome = engine.run(|engine, event| { seen.push(event.payload); // The handler schedules the next event, which the engine delivers // after this one. It schedules it only once, so the queue drains. if event.payload == \"hello\" { engine.schedule(20.0, \"world\"); } }); assert_eq!(seen, vec![\"hello\", \"world\"]); assert_eq!(outcome.executed, 2); assert_eq!(engine.now(), 20.0); ```", + "summary": "Discrete-event simulation engine.\n\nThe engine advances a clock by jumping from one event to the next. It\nholds the event queue, the current simulation time, and the statistics a\ncaller needs to judge a run. What an event means is the caller's: it\nsupplies a handler that receives the event and may schedule more events,\nand the engine only sequences the calls.\n\nThe engine keeps the simulation deterministic. Events at the same instant\nare delivered in the order they were scheduled, which the event queue\nguarantees, so a run with the same inputs produces the same output. That\nis what makes a result reproducible and a cross-check against another\nimplementation meaningful.\n\nThe engine stops for one of four reasons, reported as a\n[`StopReason`]:\nthe queue empties, the simulation reaches its time limit, the caller asks\nit to stop, or an event limit is reached. The last two exist so that a\nmodel that schedules events forever cannot run without bound.\n\n```\nuse apsis_sim_engine::Engine;\n\nlet mut engine = Engine::new();\nengine.schedule(10.0, \"hello\");\n\nlet mut seen = Vec::new();\nlet outcome = engine.run(|engine, event| {\n seen.push(event.payload);\n // The handler schedules the next event, which the engine delivers\n // after this one. It schedules it only once, so the queue drains.\n if event.payload == \"hello\" {\n engine.schedule(20.0, \"world\");\n }\n});\n\nassert_eq!(seen, vec![\"hello\", \"world\"]);\nassert_eq!(outcome.executed, 2);\nassert_eq!(engine.now(), 20.0);\n```", + "summary_zh": "离散事件仿真引擎。\n\n引擎通过从一个事件跳到下一个来推进时钟。它保存事件队列、当前仿真时刻,以及调用方\n判断一次运行所需的统计。一个事件意味着什么由调用方定义:它提供一个接收该事件并可\n再调度更多事件的处理器,而引擎只负责排序各次调用。\n\n引擎保持仿真确定。同一瞬时的事件按它们被调度的次序投递,这是事件队列所保证的,故\n相同输入的一次运行产生相同输出。这正是使结果可复现、与另一实现的交叉核对有意义的\n原因。\n\n引擎因四种原因之一停止,报告为一个 [`StopReason`]:队列排空、仿真达到其时限、\n调用方请求它停止,或达到事件上限。后两者存在,是为了让永远调度事件的模型无法无界\n运行。", "items": [ { "kind": "struct", "name": "Engine", "signature": "pub struct Engine {", "doc": "A discrete-event simulation engine.\n\nThe engine owns the event queue and the clock. A run repeatedly takes the\nearliest event and passes it to a handler, which may schedule more\nevents. The engine stops when the queue empties or when one of the limits\nis reached.", + "doc_zh": "离散事件仿真引擎。\n\n引擎拥有事件队列与时钟。一次运行反复取出最早的事件并交给处理器,\n处理器可再调度更多事件。队列排空或达到某个限制时引擎停止。", "associated": false }, { @@ -7569,6 +8644,7 @@ "name": "new", "signature": "pub const fn new() -> Self {", "doc": "Creates an engine with the clock at zero and no limits.\n\nAn engine with no limits runs until the queue empties, which is the\nright default for a model that terminates. A model that schedules\nwithout bound needs a limit, which is set with\n[`Engine::with_time_limit`] or [`Engine::with_event_limit`].", + "doc_zh": "创建时钟为零且无限制的引擎。\n\n无限制的引擎一直运行到队列排空,这是会终止的模型的恰当缺省。\n无界地调度事件的模型需要限制,可用 [`Engine::with_time_limit`]\n或 [`Engine::with_event_limit`] 设置。", "associated": true }, { @@ -7576,6 +8652,7 @@ "name": "with_time_limit", "signature": "pub const fn with_time_limit(mut self, limit: f64) -> Self {", "doc": "Sets the latest simulation time the engine will process.\n\nAn event scheduled beyond the limit is left in the queue rather than\nexecuted, so the caller can examine what remained. A limit that is\nnegative or not finite disables the limit.", + "doc_zh": "设置引擎将处理的最晚仿真时刻。\n\n调度到超出该限制的事件被留在队列中而不执行,使调用方能检查\n剩余了哪些。限制为负或非有限时该限制被禁用。", "associated": true }, { @@ -7583,6 +8660,7 @@ "name": "with_event_limit", "signature": "pub const fn with_event_limit(mut self, limit: u64) -> Self {", "doc": "Sets the greatest number of events the engine will execute.\n\nA limit of zero disables the rule, because a run that executes no\nevents is never useful.", + "doc_zh": "设置引擎将执行的事件数上限。\n\n限制为零时该规则被禁用,因为不执行任何事件的运行从无用处。", "associated": true }, { @@ -7590,6 +8668,7 @@ "name": "now", "signature": "pub const fn now(&self) -> f64 {", "doc": "Returns the current simulation time, in seconds.", + "doc_zh": "返回当前仿真时刻,单位为秒。", "associated": true }, { @@ -7597,6 +8676,7 @@ "name": "set_now", "signature": "pub fn set_now(&mut self, time: f64) {", "doc": "Sets the current simulation time.\n\nThis is used to start a run at an offset, for example at a scenario\nepoch rather than at zero. It does not reschedule pending events, so a\ncaller that moves the clock forward past a pending event will execute\nthat event late. The engine has no way to detect the mistake, so the\ncaller is responsible for keeping the clock consistent with the\nqueue.", + "doc_zh": "设置当前仿真时刻。\n\n用于从某个偏移开始运行,例如从场景历元而非从零开始。它不重排\n待处理事件,故把时钟前移越过某待处理事件的调用方会迟执行该\n事件。引擎无法检测这一错误,故保持时钟与队列一致是调用方的\n责任。", "associated": true }, { @@ -7604,6 +8684,7 @@ "name": "executed", "signature": "pub const fn executed(&self) -> u64 {", "doc": "Returns the number of events executed so far.", + "doc_zh": "返回迄今已执行的事件数。", "associated": true }, { @@ -7611,6 +8692,7 @@ "name": "pending", "signature": "pub fn pending(&self) -> usize {", "doc": "Returns the number of pending events.", + "doc_zh": "返回待处理事件的数量。", "associated": true }, { @@ -7618,6 +8700,7 @@ "name": "is_idle", "signature": "pub fn is_idle(&self) -> bool {", "doc": "Returns `true` when no events are pending.", + "doc_zh": "当没有待处理事件时返回 `true`。", "associated": true }, { @@ -7625,6 +8708,7 @@ "name": "next_time", "signature": "pub fn next_time(&self) -> Option {", "doc": "Returns the time of the next pending event, if there is one.", + "doc_zh": "返回下一待处理事件的时间(若存在)。", "associated": true }, { @@ -7632,6 +8716,7 @@ "name": "state", "signature": "pub fn state(&self) -> EngineState {", "doc": "Returns what the engine currently holds.", + "doc_zh": "返回引擎当前所持有的状态。", "associated": true }, { @@ -7639,6 +8724,7 @@ "name": "schedule", "signature": "pub fn schedule(&mut self, time: f64, payload: T) -> Option {", "doc": "Schedules an event at a time.\n\nReturns the identifier of the new event, or `None` when the time is\nnot usable.", + "doc_zh": "在某个时刻调度一个事件。\n\n返回新事件的标识符;该时刻不可用时返回 `None`。", "associated": true }, { @@ -7646,6 +8732,7 @@ "name": "schedule_after", "signature": "pub fn schedule_after(&mut self, delay: f64, payload: T) -> Option {", "doc": "Schedules an event at a delay from the current simulation time.\n\nReturns the identifier of the new event, or `None` when the delay is\nnot usable.", + "doc_zh": "在当前仿真时刻之后某个延时处调度一个事件。\n\n返回新事件的标识符;该延时不可用时返回 `None`。", "associated": true }, { @@ -7653,6 +8740,7 @@ "name": "request_stop", "signature": "pub fn request_stop(&mut self) {", "doc": "Asks the engine to stop after the current event.\n\nThe handler calls this when its own model says the run is over, for\nexample when a stopping condition that the queue cannot express is\nmet. The pending events are left in the queue.", + "doc_zh": "请求引擎在当前事件之后停止。\n\n当处理器自身的模型表明运行已结束时调用它,例如当某个队列无法\n表达的停止条件被满足时。待处理事件被留在队列中。", "associated": true }, { @@ -7660,6 +8748,7 @@ "name": "clear_stop", "signature": "pub fn clear_stop(&mut self) {", "doc": "Clears the stop request, so that a subsequent run continues.", + "doc_zh": "清除停止请求,使后续运行得以继续。", "associated": true }, { @@ -7667,6 +8756,7 @@ "name": "clear", "signature": "pub fn clear(&mut self) -> usize {", "doc": "Removes every pending event and returns how many were discarded.", + "doc_zh": "移除所有待处理事件并返回被丢弃的数量。", "associated": true }, { @@ -7674,6 +8764,7 @@ "name": "queue", "signature": "pub const fn queue(&self) -> &EventQueue {", "doc": "Returns the queue, for a caller that needs to inspect or manipulate it\ndirectly.", + "doc_zh": "返回事件队列,供需要直接查看或操作它的调用方使用。", "associated": true }, { @@ -7681,6 +8772,7 @@ "name": "queue_mut", "signature": "pub const fn queue_mut(&mut self) -> &mut EventQueue {", "doc": "Returns the queue for modification.", + "doc_zh": "返回事件队列以供修改。", "associated": true }, { @@ -7688,6 +8780,7 @@ "name": "run", "signature": "pub fn run(&mut self, mut handler: F) -> EngineOutcome where F: FnMut(&mut Engine, Event), {", "doc": "Runs the engine until it stops.\n\nEach iteration takes the earliest event, advances the clock to its\ntime, and passes it to the handler. The handler may schedule further\nevents, including at the same instant, which are delivered after the\nevents already scheduled for that instant.\n\nReturns the outcome of the run. The clock is left at the time of the\nlast event executed, not at a limit, so a caller that records the\nclock sees the last instant the model actually reached.", + "doc_zh": "运行引擎直到其停止。\n\n每次迭代取出最早的事件,把时钟推进到其时刻,并交给处理器。处理器\n可再调度更多事件,包括在同一瞬时的,它们在该瞬时已调度的事件\n之后投递。\n\n返回该运行的结果。时钟留在最后执行事件的时间,而非某个限制处,\n故记录时钟的调用方看到的是模型实际到达的最后瞬时。", "associated": true }, { @@ -7695,6 +8788,7 @@ "name": "StopReason", "signature": "pub enum StopReason {", "doc": "Why a run stopped.\n\nThe reason is reported rather than inferred from the event count, because\nthe same count can arise from an empty queue or from a limit, and a\ncaller that records the outcome of a run needs to distinguish them.", + "doc_zh": "一次运行停止的原因。\n\n该原因是被报告的,而不是由事件计数推断的,因为同样的计数可能来自\n空队列或某个限制,而记录运行结果的调用方需要区分二者。", "associated": false }, { @@ -7702,6 +8796,7 @@ "name": "is_natural", "signature": "pub const fn is_natural(self) -> bool {", "doc": "Returns `true` when the run ended because the model had no more work\nto do rather than because a limit intervened.", + "doc_zh": "当运行因模型已无工作而结束、而非因某个限制介入时返回 `true`。", "associated": true }, { @@ -7709,6 +8804,7 @@ "name": "name", "signature": "pub const fn name(self) -> &'static str {", "doc": "Returns a short name for the reason.", + "doc_zh": "返回该原因的简称。", "associated": true }, { @@ -7716,6 +8812,7 @@ "name": "EngineOutcome", "signature": "pub struct EngineOutcome {", "doc": "What a run produced.\n\nThe outcome records the counts and the clock, so a caller can record the\nresult of a run without reaching back into the engine.", + "doc_zh": "一次运行所产生的结果。\n\n结果记录各项计数与时钟,使调用方无需回访引擎即可记录一次运行的结果。", "associated": false }, { @@ -7723,6 +8820,7 @@ "name": "duration", "signature": "pub fn duration(&self) -> f64 {", "doc": "Returns the time the run covered, in seconds.", + "doc_zh": "返回该运行所覆盖的时长,单位为秒。", "associated": true }, { @@ -7730,6 +8828,7 @@ "name": "event_rate", "signature": "pub fn event_rate(&self) -> Option {", "doc": "Returns the mean number of events per second of simulation time.\n\nThis measures how busy the model is rather than how long the run took\non the wall clock. Returns `None` when the run covered no time.", + "doc_zh": "返回每仿真秒的平均事件数。\n\n它衡量模型的繁忙程度,而非该运行在挂钟上耗时多久。运行未覆盖\n任何时间时返回 `None`。", "associated": true }, { @@ -7737,6 +8836,7 @@ "name": "is_natural", "signature": "pub fn is_natural(&self) -> bool {", "doc": "Returns `true` when the run finished because the model had no more\nwork rather than because a limit stopped it.", + "doc_zh": "当运行因模型已无工作而结束、而非因某个限制而停止时返回 `true`。", "associated": true }, { @@ -7744,6 +8844,7 @@ "name": "EngineState", "signature": "pub struct EngineState {", "doc": "What the engine currently holds.", + "doc_zh": "引擎当前所持有的状态。", "associated": false }, { @@ -7751,6 +8852,7 @@ "name": "is_idle", "signature": "pub const fn is_idle(&self) -> bool {", "doc": "Returns `true` when no events are pending.", + "doc_zh": "当没有待处理事件时返回 `true`。", "associated": true } ] @@ -7758,13 +8860,15 @@ { "name": "apsis-scenario", "layer": "sim", - "summary": "Scenario definition and loading. A scenario is a simulation's initial conditions: the resources a plan may use, the tasks it must place, the precedence constraints between those tasks, and the limits the run should respect. It is the input a run is built from, and keeping it in one place means a run can be reproduced from a single description rather than reconstructed from scattered arguments. The crate offers two ways to build one. The builder methods are what Rust code uses, and they check each addition as it is made. The text format is what a stored scenario uses, and [`Scenario::parse`] reads it back. The two agree: a scenario written with [`Scenario::to_text`] parses to an equal scenario, so a scenario can be stored, edited, and reloaded without drift. The text format is line oriented and carries no third party dependency. A blank line and a line whose first non-space character is `#` are ignored. Every other line is a keyword followed by its arguments: ```text scenario leo downlink epoch 0 time_limit 3600 event_limit 1000000 resource 0 capacity 1 window 0 0 600 window 0 1200 1800 task 0 duration 60 task_window 0 100 1000 task_priority 0 5 task_mandatory 0 true require 0 0 1 precedence 0 1 ``` `epoch` is the time the simulation clock starts at, in seconds. `time_limit` and `event_limit` are the two run limits; either may be omitted, which leaves that limit unset. ``` use apsis_scenario::Scenario; let text = \"\\ scenario demo resource 0 capacity 1 window 0 0 600 task 0 duration 60 require 0 0 1 \"; let scenario = Scenario::parse(text).expect(\"the text is well formed\"); assert_eq!(scenario.name(), \"demo\"); assert_eq!(scenario.resources().len(), 1); assert_eq!(scenario.tasks().len(), 1); assert_eq!(scenario.validate(), Ok(())); ```", + "summary": "Scenario definition and loading.\n\nA scenario is a simulation's initial conditions: the resources a plan may\nuse, the tasks it must place, the precedence constraints between those\ntasks, and the limits the run should respect. It is the input a run is\nbuilt from, and keeping it in one place means a run can be reproduced from\na single description rather than reconstructed from scattered arguments.\n\nThe crate offers two ways to build one. The builder methods are what Rust\ncode uses, and they check each addition as it is made. The text format is\nwhat a stored scenario uses, and [`Scenario::parse`] reads it back. The\ntwo agree: a scenario written with [`Scenario::to_text`] parses to an equal\nscenario, so a scenario can be stored, edited, and reloaded without drift.\n\nThe text format is line oriented and carries no third party dependency. A\nblank line and a line whose first non-space character is `#` are ignored.\nEvery other line is a keyword followed by its arguments:\n\n```text\nscenario leo downlink\nepoch 0\ntime_limit 3600\nevent_limit 1000000\n\nresource 0 capacity 1\nwindow 0 0 600\nwindow 0 1200 1800\n\ntask 0 duration 60\ntask_window 0 100 1000\ntask_priority 0 5\ntask_mandatory 0 true\nrequire 0 0 1\n\nprecedence 0 1\n```\n\n`epoch` is the time the simulation clock starts at, in seconds.\n`time_limit` and `event_limit` are the two run limits; either may be\nomitted, which leaves that limit unset.\n\n```\nuse apsis_scenario::Scenario;\n\nlet text = \"\\\nscenario demo\nresource 0 capacity 1\nwindow 0 0 600\ntask 0 duration 60\nrequire 0 0 1\n\";\n\nlet scenario = Scenario::parse(text).expect(\"the text is well formed\");\nassert_eq!(scenario.name(), \"demo\");\nassert_eq!(scenario.resources().len(), 1);\nassert_eq!(scenario.tasks().len(), 1);\nassert_eq!(scenario.validate(), Ok(()));\n```", + "summary_zh": "场景的定义与加载。\n\n场景是一次仿真的初始条件:计划可用的资源、必须放置的任务、这些任务之间的先后\n约束,以及运行应遵守的限制。它是一次运行所据以构建的输入,把它保存在一处意味着\n一次运行可以从单一描述复现,而不必从散落的参数重建。\n\n本 crate 提供两种构建方式。构建器方法是 Rust 代码所用的,它们在每次添加时就检查。\n文本格式是存储的场景所用的,[`Scenario::parse`] 把它读回。二者一致:用\n[`Scenario::to_text`] 写出的场景解析回相等的场景,故场景可以被存储、编辑并重新\n加载而不漂移。\n\n文本格式是面向行的,且不携带第三方依赖。空行与第一个非空格字符为 `#` 的行被忽略。\n其余每一行是一个关键字后接其参数:\n\n```text\nscenario leo downlink\nepoch 0\ntime_limit 3600\nevent_limit 1000000\n\nresource 0 capacity 1\nwindow 0 0 600\nwindow 0 1200 1800\n\ntask 0 duration 60\ntask_window 0 100 1000\ntask_priority 0 5\ntask_mandatory 0 true\nrequire 0 0 1\n\nprecedence 0 1\n```\n\n`epoch` 是仿真时钟的起始时刻,单位为秒。`time_limit` 与 `event_limit` 是两个运行\n限制;任一个都可省略,那会使该限制不被设置。", "items": [ { "kind": "enum", "name": "ScenarioErrorKind", "signature": "pub enum ScenarioErrorKind {", "doc": "The category of a scenario error.", + "doc_zh": "场景错误的类别。", "associated": false }, { @@ -7772,6 +8876,7 @@ "name": "ScenarioError", "signature": "pub struct ScenarioError {", "doc": "A failure while building, loading, or validating a scenario.", + "doc_zh": "构建、加载或校验场景时发生的失败。", "associated": false }, { @@ -7779,6 +8884,7 @@ "name": "new", "signature": "pub fn new(kind: ScenarioErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -7786,6 +8892,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of the values that caused the failure.", + "doc_zh": "附加对该失败所涉数值的说明。", "associated": true }, { @@ -7793,6 +8900,7 @@ "name": "with_line", "signature": "pub fn with_line(mut self, line: usize) -> Self {", "doc": "Attaches the one based line number a load failure came from.", + "doc_zh": "附加加载失败所来自的、以 1 为基的行号。", "associated": true }, { @@ -7800,6 +8908,7 @@ "name": "kind", "signature": "pub fn kind(&self) -> ScenarioErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -7807,6 +8916,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -7814,6 +8924,7 @@ "name": "line", "signature": "pub fn line(&self) -> Option {", "doc": "Returns the line number this failure came from, if any.", + "doc_zh": "返回该失败所来自的行号(若有)。", "associated": true }, { @@ -7821,6 +8932,7 @@ "name": "Scenario", "signature": "pub struct Scenario {", "doc": "A simulation's initial conditions.\n\nA scenario holds the resources a plan may use, the tasks it must place,\nand the constraints between them, together with the limits a run should\nrespect. It is built either with the builder methods or by parsing text,\nand both routes apply the same checks, so a scenario that parses is one\nthat would have been accepted by the builders.", + "doc_zh": "一次仿真的初始条件。\n\n场景保存计划可用的资源、必须放置的任务、它们之间的约束,以及一次\n运行应遵守的限制。它既可用构建器方法构建,也可由文本解析,两条\n路径施加相同的检查,故能解析的场景即是构建器会接受的场景。", "associated": false }, { @@ -7828,6 +8940,7 @@ "name": "new", "signature": "pub fn new(name: impl Into) -> Self {", "doc": "Creates an empty scenario with an epoch at zero.", + "doc_zh": "创建历元为零的空场景。", "associated": true }, { @@ -7835,6 +8948,7 @@ "name": "name", "signature": "pub fn name(&self) -> &str {", "doc": "Returns the name of the scenario.", + "doc_zh": "返回该场景的名称。", "associated": true }, { @@ -7842,6 +8956,7 @@ "name": "epoch", "signature": "pub fn epoch(&self) -> f64 {", "doc": "Returns the time the simulation clock starts at, in seconds.", + "doc_zh": "返回仿真时钟的起始时刻,单位为秒。", "associated": true }, { @@ -7849,6 +8964,7 @@ "name": "time_limit", "signature": "pub fn time_limit(&self) -> Option {", "doc": "Returns the latest time the run will process, if one is set.", + "doc_zh": "返回该运行将处理的最晚时刻(若已设置)。", "associated": true }, { @@ -7856,6 +8972,7 @@ "name": "event_limit", "signature": "pub const fn event_limit(&self) -> Option {", "doc": "Returns the greatest number of events the run will execute, if one is\nset.", + "doc_zh": "返回该运行将执行的事件数上限(若已设置)。", "associated": true }, { @@ -7863,6 +8980,7 @@ "name": "resources", "signature": "pub fn resources(&self) -> &[Resource] {", "doc": "Returns the resources in the order they were added.", + "doc_zh": "按添加次序返回各资源。", "associated": true }, { @@ -7870,6 +8988,7 @@ "name": "resources_mut", "signature": "pub fn resources_mut(&mut self) -> &mut Vec {", "doc": "Returns the resources for modification.\n\nA caller that changes a resource is responsible for keeping the\nscenario consistent; [`Scenario::validate`] checks it afterwards.", + "doc_zh": "返回各资源以供修改。\n\n修改资源的调用方负责保持场景一致;[`Scenario::validate`] 随后\n检查它。", "associated": true }, { @@ -7877,6 +8996,7 @@ "name": "parse", "signature": "pub fn parse(text: &str) -> Result {", "doc": "Reads a scenario from its text form.\n\nThe format is described on the crate documentation. A scenario written\nwith [`Scenario::to_text`] parses back to an equal scenario.", + "doc_zh": "由其文本形式读取场景。\n\n该格式在 crate 文档中描述。用 [`Scenario::to_text`] 写出的场景\n解析回相等的场景。", "associated": true }, { @@ -7884,6 +9004,7 @@ "name": "tasks", "signature": "pub fn tasks(&self) -> &[Task] {", "doc": "Returns the tasks in the order they were added.", + "doc_zh": "按添加次序返回各任务。", "associated": true }, { @@ -7891,6 +9012,7 @@ "name": "precedence", "signature": "pub const fn precedence(&self) -> &PrecedenceGraph {", "doc": "Returns the precedence constraints.", + "doc_zh": "返回先后约束。", "associated": true }, { @@ -7898,6 +9020,7 @@ "name": "with_epoch", "signature": "pub fn with_epoch(mut self, epoch: f64) -> Self {", "doc": "Sets the time the simulation clock starts at.\n\nReturns the scenario unchanged in value but not in type; the caller\nuses the returned value. The epoch must be finite and non-negative.", + "doc_zh": "设置仿真时钟的起始时刻。\n\n返回的场景值不变而类型改变;调用方使用返回的值。历元必须有限\n且非负。", "associated": true }, { @@ -7905,6 +9028,7 @@ "name": "with_time_limit", "signature": "pub const fn with_time_limit(mut self, limit: f64) -> Self {", "doc": "Sets the latest time the run will process.\n\nA negative or non-finite limit clears the limit rather than storing\nit, matching the engine, which ignores a limit it cannot use.", + "doc_zh": "设置该运行将处理的最晚时刻。\n\n负或非有限的限制清除该限制而非存储它,与引擎一致,后者忽略\n它无法使用的限制。", "associated": true }, { @@ -7912,6 +9036,7 @@ "name": "with_event_limit", "signature": "pub const fn with_event_limit(mut self, limit: u64) -> Self {", "doc": "Sets the greatest number of events the run will execute.\n\nA limit of zero clears the limit rather than storing it, matching the\nengine, which treats zero as no limit.", + "doc_zh": "设置该运行将执行的事件数上限。\n\n限制为零清除该限制而非存储它,与引擎一致,后者把零视为无限制。", "associated": true }, { @@ -7919,6 +9044,7 @@ "name": "set_epoch", "signature": "pub fn set_epoch(&mut self, epoch: f64) -> Result<(), ScenarioError> {", "doc": "Sets the epoch, reporting a value the clock cannot use.", + "doc_zh": "设置历元,并报告时钟无法使用的值。", "associated": true }, { @@ -7926,6 +9052,7 @@ "name": "set_time_limit", "signature": "pub fn set_time_limit(&mut self, limit: f64) -> Result<(), ScenarioError> {", "doc": "Sets the time limit, reporting a value a run cannot use.", + "doc_zh": "设置时限,并报告运行无法使用的值。", "associated": true }, { @@ -7933,6 +9060,7 @@ "name": "add_resource", "signature": "pub fn add_resource(&mut self, resource: Resource) -> Result<(), ScenarioError> {", "doc": "Adds a resource, reporting one whose identifier is already used.\n\nThe resource itself is also checked, so a scenario cannot hold a\nresource that a scheduler would refuse.", + "doc_zh": "添加一个资源,并报告标识符已被使用的那种情况。\n\n资源自身也受检查,故场景无法保存调度器会拒绝的资源。", "associated": true }, { @@ -7940,6 +9068,7 @@ "name": "add_task", "signature": "pub fn add_task(&mut self, task: Task) -> Result<(), ScenarioError> {", "doc": "Adds a task, reporting one whose identifier is already used.\n\nThe task's requirements must name resources the scenario holds, and\nthe task itself must be usable.", + "doc_zh": "添加一个任务,并报告标识符已被使用的那种情况。\n\n任务的需求必须指名场景所持有的资源,且任务自身必须可用。", "associated": true }, { @@ -7947,6 +9076,7 @@ "name": "add_precedence", "signature": "pub fn add_precedence(&mut self, before: TaskId, after: TaskId) -> Result<(), ScenarioError> {", "doc": "Adds a precedence constraint, reporting a cycle or a missing task.", + "doc_zh": "添加一条先后约束,并报告成环或缺失任务的情况。", "associated": true }, { @@ -7954,6 +9084,7 @@ "name": "resource_set", "signature": "pub fn resource_set(&self) -> Result {", "doc": "Returns the resources as a set, for a scheduler.", + "doc_zh": "返回资源集合,供调度器使用。", "associated": true }, { @@ -7961,6 +9092,7 @@ "name": "validate", "signature": "pub fn validate(&self) -> Result<(), ScenarioError> {", "doc": "Checks the whole scenario at once.\n\nA scenario built with the builder methods is already checked as it is\nbuilt; this is for one that was assembled field by field or read from\ntext, and it reports the first problem found.", + "doc_zh": "一次性检查整个场景。\n\n用构建器方法构建的场景在构建时已被检查;本方法用于逐字段拼装或\n从文本读取的场景,它报告所发现的第一个问题。", "associated": true }, { @@ -7968,6 +9100,7 @@ "name": "summary", "signature": "pub fn summary(&self) -> ScenarioSummary {", "doc": "Returns a summary of what the scenario holds.", + "doc_zh": "返回场景所持有内容的摘要。", "associated": true }, { @@ -7975,6 +9108,7 @@ "name": "to_text", "signature": "pub fn to_text(&self) -> String {", "doc": "Renders the scenario in the text form [`Scenario::parse`] reads.\n\nThe output parses to an equal scenario, which is what lets a scenario\nbe stored and reloaded without drift.", + "doc_zh": "以 [`Scenario::parse`] 所读取的文本形式呈现场景。\n\n输出解析回相等的场景,这正是场景可被存储并重新加载而不漂移的\n原因。", "associated": true }, { @@ -7982,6 +9116,7 @@ "name": "ScenarioSummary", "signature": "pub struct ScenarioSummary {", "doc": "What a scenario holds, as counts and totals.", + "doc_zh": "场景所持有的内容,以计数与总计表示。", "associated": false } ] @@ -7989,13 +9124,15 @@ { "name": "apsis-event", "layer": "sim", - "summary": "Simulation event types and event queue. A discrete-event simulation advances by jumping from one event to the next rather than by stepping through every instant. This crate provides the queue that makes that possible: a priority queue ordered by time, with a well defined order among events that fall at the same instant. The tie-break matters more than it first appears. Two events scheduled for the same instant must be delivered in a repeatable order, or the simulation produces a different result on each run and a cross-check against another implementation is impossible. Each event therefore carries a sequence number assigned when it is scheduled, and events at equal times are ordered by that number, so the order is the order in which they were scheduled. That is the convention used by every discrete-event simulator. Times are in seconds from the start of the simulation, so the queue holds no knowledge of calendars or time scales. ``` use apsis_event::EventQueue; let mut queue = EventQueue::new(); queue.schedule(10.0, \"second\"); queue.schedule(5.0, \"first\"); // The earliest event comes out first. let event = queue.pop().expect(\"the queue is not empty\"); assert_eq!(event.time, 5.0); assert_eq!(event.payload, \"first\"); ```", + "summary": "Simulation event types and event queue.\n\nA discrete-event simulation advances by jumping from one event to the\nnext rather than by stepping through every instant. This crate provides\nthe queue that makes that possible: a priority queue ordered by time,\nwith a well defined order among events that fall at the same instant.\n\nThe tie-break matters more than it first appears. Two events scheduled\nfor the same instant must be delivered in a repeatable order, or the\nsimulation produces a different result on each run and a cross-check\nagainst another implementation is impossible. Each event therefore\ncarries a sequence number assigned when it is scheduled, and events at\nequal times are ordered by that number, so the order is the order in\nwhich they were scheduled. That is the convention used by every\ndiscrete-event simulator.\n\nTimes are in seconds from the start of the simulation, so the queue holds\nno knowledge of calendars or time scales.\n\n```\nuse apsis_event::EventQueue;\n\nlet mut queue = EventQueue::new();\nqueue.schedule(10.0, \"second\");\nqueue.schedule(5.0, \"first\");\n\n// The earliest event comes out first.\nlet event = queue.pop().expect(\"the queue is not empty\");\nassert_eq!(event.time, 5.0);\nassert_eq!(event.payload, \"first\");\n```", + "summary_zh": "仿真事件类型与事件队列。\n\n离散事件仿真通过从一个事件跳到下一个来推进,而不是逐步走过每个瞬时。本 crate\n提供使之成为可能的队列:一个按时间排序的优先队列,并在落在同一瞬时的各事件之间\n有明确定义的次序。\n\n该并列打破规则比它初看更重要。为同一瞬时调度的两个事件必须以可重复的次序投递,\n否则仿真每次运行都会产生不同结果,与另一实现的交叉核对也不可能。故每个事件携带\n一个在调度时赋予的序号,时间相等的事件按该序号排序,故该次序即它们被调度的次序。\n这是每个离散事件仿真器所用的约定。\n\n时间以距仿真起点的秒数计,故队列对日历与时标一无所知。", "items": [ { "kind": "struct", "name": "EventId", "signature": "pub struct EventId {", "doc": "An identifier for a scheduled event.\n\nThe identifier combines the time at which the event is due with the order\nin which it was scheduled. Two events that fall at the same instant are\ntherefore distinguishable, which is what makes the delivery order\nrepeatable.", + "doc_zh": "已调度事件的标识符。\n\n该标识符把事件到期的时刻与其被调度的次序结合起来。因此落在同一\n瞬时的两个事件可区分,这正是投递次序可重复的原因。", "associated": false }, { @@ -8003,6 +9140,7 @@ "name": "new", "signature": "pub fn new(time: f64, sequence: u64) -> Option {", "doc": "Creates an identifier from a time and a sequence number.\n\nThe time must be finite and non-negative. A caller that has a time\noutside that range cannot use this type, which is deliberate: a\nsimulation clock that goes backwards or becomes infinite has no\nmeaningful order.", + "doc_zh": "由时刻与序号创建标识符。\n\n该时刻必须有限且非负。时刻超出该范围的调用方无法使用本类型,\n这是有意为之:倒退或趋于无限的仿真时钟没有有意义的次序。", "associated": true }, { @@ -8010,6 +9148,7 @@ "name": "time", "signature": "pub fn time(self) -> f64 {", "doc": "Returns the scheduled time, in seconds.", + "doc_zh": "返回调度时刻,单位为秒。", "associated": true }, { @@ -8017,6 +9156,7 @@ "name": "sequence", "signature": "pub const fn sequence(self) -> u64 {", "doc": "Returns the order in which the event was scheduled.", + "doc_zh": "返回该事件被调度的次序。", "associated": true }, { @@ -8024,6 +9164,7 @@ "name": "Event", "signature": "pub struct Event {", "doc": "One scheduled event.\n\nThe payload is the caller's: it names what should happen. The queue does\nnot interpret it, so the same queue serves any model.", + "doc_zh": "一个已调度的事件。\n\n载荷属于调用方:它指明应发生什么。队列不解释它,故同一队列可服务\n任何模型。", "associated": false }, { @@ -8031,6 +9172,7 @@ "name": "is_due", "signature": "pub fn is_due(&self, now: f64) -> bool {", "doc": "Returns `true` when the event is due at or before a time.", + "doc_zh": "当该事件在某时刻或之前到期时返回 `true`。", "associated": true }, { @@ -8038,6 +9180,7 @@ "name": "with_payload", "signature": "pub fn with_payload(self, payload: U) -> Event {", "doc": "Returns a copy of this event with a different payload.", + "doc_zh": "返回本事件的一个具有不同载荷的副本。", "associated": true }, { @@ -8045,6 +9188,7 @@ "name": "reschedule", "signature": "pub fn reschedule(&self, time: f64, sequence: u64) -> Option> where T: Clone, {", "doc": "Returns a copy of this event scheduled at a different time.\n\nReturns `None` when the new time is not usable, which leaves the\noriginal event available for the caller to keep using.", + "doc_zh": "返回本事件的一个调度在不同时刻的副本。\n\n新时刻不可用时返回 `None`,此时原事件仍可供调用方继续使用。", "associated": true }, { @@ -8052,6 +9196,7 @@ "name": "QueueState", "signature": "pub struct QueueState {", "doc": "What the queue currently holds.\n\nThe state is exposed so that a simulation loop can decide whether to\nadvance the clock or to stop without popping an event first.", + "doc_zh": "队列当前所持有的状态。\n\n该状态被公开,使仿真循环无需先弹出一个事件即可决定是推进时钟还是\n停止。", "associated": false }, { @@ -8059,6 +9204,7 @@ "name": "is_idle", "signature": "pub const fn is_idle(&self) -> bool {", "doc": "Returns `true` when no events are pending.", + "doc_zh": "当没有待处理事件时返回 `true`。", "associated": true }, { @@ -8066,6 +9212,7 @@ "name": "EventQueue", "signature": "pub struct EventQueue {", "doc": "A priority queue of events ordered by time and then by schedule order.\n\nThe queue assigns the sequence numbers, so a caller cannot accidentally\ncreate two events with the same identifier. Sequence numbers start at\nzero and increase by one with every event scheduled, including events\nscheduled from inside the handling of another event.", + "doc_zh": "按时间、再按调度次序排序的事件优先队列。\n\n序号由队列分配,故调用方无法意外创建两个具有相同标识符的事件。\n序号从零开始,每个被调度的事件(包括在另一事件处理过程中调度的\n事件)都使之加一。", "associated": false }, { @@ -8073,6 +9220,7 @@ "name": "new", "signature": "pub const fn new() -> Self {", "doc": "Creates an empty queue.", + "doc_zh": "创建一个空队列。", "associated": true }, { @@ -8080,6 +9228,7 @@ "name": "len", "signature": "pub fn len(&self) -> usize {", "doc": "Returns the number of pending events.", + "doc_zh": "返回待处理事件的数量。", "associated": true }, { @@ -8087,6 +9236,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when no events are pending.", + "doc_zh": "当没有待处理事件时返回 `true`。", "associated": true }, { @@ -8094,6 +9244,7 @@ "name": "delivered", "signature": "pub const fn delivered(&self) -> u64 {", "doc": "Returns the number of events delivered so far.", + "doc_zh": "返回迄今已投递的事件数。", "associated": true }, { @@ -8101,6 +9252,7 @@ "name": "state", "signature": "pub fn state(&self) -> QueueState {", "doc": "Returns what the queue currently holds.", + "doc_zh": "返回队列当前所持有的状态。", "associated": true }, { @@ -8108,6 +9260,7 @@ "name": "next_time", "signature": "pub fn next_time(&self) -> Option {", "doc": "Returns the time of the earliest pending event.\n\nReturns `None` when the queue is empty.", + "doc_zh": "返回最早待处理事件的时间。\n\n队列为空时返回 `None`。", "associated": true }, { @@ -8115,6 +9268,7 @@ "name": "schedule", "signature": "pub fn schedule(&mut self, time: f64, payload: T) -> Option {", "doc": "Schedules an event at a time.\n\nThe sequence number is assigned by the queue, so the delivery order\namong events at the same instant is the order they were scheduled.\n\nReturns the identifier of the new event, or `None` when the time is\nnot usable: a simulation clock runs forward from zero, so a negative\nor non-finite time is refused rather than stored.", + "doc_zh": "在某个时刻调度一个事件。\n\n序号由队列分配,故同一瞬时各事件之间的投递次序即它们被调度的\n次序。\n\n返回新事件的标识符;时刻不可用时返回 `None`:仿真时钟从零向前\n运行,故负或非有限的时刻被拒绝而非存储。", "associated": true }, { @@ -8122,6 +9276,7 @@ "name": "schedule_after", "signature": "pub fn schedule_after(&mut self, now: f64, delay: f64, payload: T) -> Option {", "doc": "Schedules an event at a time relative to a base instant.\n\nThis is the form a handler uses when it schedules a follow-up: the\ndelay is relative to the current simulation time.\n\nReturns `None` when the resulting time is not usable.", + "doc_zh": "在相对于某基准瞬时的时刻调度一个事件。\n\n这是处理器调度后续事件时所用的形式:延时相对于当前仿真时刻。\n\n所得时刻不可用时返回 `None`。", "associated": true }, { @@ -8129,6 +9284,7 @@ "name": "pop", "signature": "pub fn pop(&mut self) -> Option> {", "doc": "Removes and returns the earliest event.\n\nReturns `None` when the queue is empty. The payload is taken from the\nqueue, so the slot is left empty; scheduling a later event reuses the\nslot.", + "doc_zh": "取出并返回最早的事件。\n\n队列为空时返回 `None`。载荷被取离队列,故该槽位被留空;调度\n后续事件会复用该槽位。", "associated": true }, { @@ -8136,6 +9292,7 @@ "name": "peek", "signature": "pub fn peek(&self) -> Option<&T> {", "doc": "Returns a reference to the payload of the earliest event without\nremoving it.", + "doc_zh": "返回最早事件载荷的引用而不移除它。", "associated": true }, { @@ -8143,6 +9300,7 @@ "name": "clear", "signature": "pub fn clear(&mut self) -> usize {", "doc": "Removes every pending event and returns how many were discarded.\n\nThe delivered count is left alone, because it records history rather\nthan queue contents.", + "doc_zh": "移除所有待处理事件并返回被丢弃的数量。\n\n已投递计数保持不变,因为它记录的是历史而非队列内容。", "associated": true }, { @@ -8150,6 +9308,7 @@ "name": "drain", "signature": "pub fn drain(&mut self) -> Vec> {", "doc": "Returns every pending event in delivery order, leaving the queue\nempty.\n\nThe order is the order the events would have been delivered in, which\nis what a caller wants when it records the remaining schedule at the\nend of a run.", + "doc_zh": "按投递次序返回所有待处理事件,并使队列为空。\n\n该次序即各事件本会被投递的次序,这正是调用方在运行结束时记录\n剩余调度所需的。", "associated": true } ] @@ -8157,13 +9316,15 @@ { "name": "apsis-output", "layer": "sim", - "summary": "Simulation output recording. A simulation produces more output than a caller can act on at once: event times, the values a model computes as it runs, and the warnings it raises. This crate collects those into one trace, so that a run can be examined after it finishes rather than only as it happens. A record has a time, a kind, a label, and a value. The time is the simulation instant the record was made at, which is what makes a trace orderable and joinable against the event log. The kind separates the three things a caller usually wants to tell apart: an observation the model made, a state change it decided on, and a warning it wants to surface. The label names what the value is about, and the value is a single number, which covers the great majority of what a simulation records without imposing a schema the caller has to design. The trace keeps the records in the order they were made, not in time order. That distinction matters: a handler may record a value for an event it is handling after recording one for a follow-up it just scheduled, and the order the model reasoned in is worth keeping. A caller that wants time order asks for it with [`Trace::in_time_order`]. ``` use apsis_output::{Recorder, RecordKind}; let mut recorder = Recorder::new(); recorder.observe(10.0, \"altitude\", 550.0); recorder.note(10.0, \"eclipse\", 1.0); assert_eq!(recorder.len(), 2); assert!(recorder.trace().is_stable()); assert_eq!(recorder.trace().value_of(\"altitude\"), Some(550.0)); let _ = RecordKind::Warning; ```", + "summary": "Simulation output recording.\n\nA simulation produces more output than a caller can act on at once: event\ntimes, the values a model computes as it runs, and the warnings it raises.\nThis crate collects those into one trace, so that a run can be examined\nafter it finishes rather than only as it happens.\n\nA record has a time, a kind, a label, and a value. The time is the\nsimulation instant the record was made at, which is what makes a trace\norderable and joinable against the event log. The kind separates the three\nthings a caller usually wants to tell apart: an observation the model made,\na state change it decided on, and a warning it wants to surface. The label\nnames what the value is about, and the value is a single number, which\ncovers the great majority of what a simulation records without imposing a\nschema the caller has to design.\n\nThe trace keeps the records in the order they were made, not in time order.\nThat distinction matters: a handler may record a value for an event it is\nhandling after recording one for a follow-up it just scheduled, and the\norder the model reasoned in is worth keeping. A caller that wants time\norder asks for it with [`Trace::in_time_order`].\n\n```\nuse apsis_output::{Recorder, RecordKind};\n\nlet mut recorder = Recorder::new();\nrecorder.observe(10.0, \"altitude\", 550.0);\nrecorder.note(10.0, \"eclipse\", 1.0);\n\nassert_eq!(recorder.len(), 2);\nassert!(recorder.trace().is_stable());\nassert_eq!(recorder.trace().value_of(\"altitude\"), Some(550.0));\nlet _ = RecordKind::Warning;\n```", + "summary_zh": "仿真输出记录。\n\n一次仿真产生的输出多到调用方无法一次全部处理:事件时刻、模型运行时计算的值,以及\n它提出的警告。本 crate 把这些收集进一条轨迹,使一次运行可以在结束后被检视,而不\n只在发生时。\n\n一条记录有一个时刻、一个类别、一个标签与一个值。时刻是作出该记录时的仿真瞬时,\n这正是使轨迹可排序、可与事件日志连接的原因。类别区分了调用方通常想分辨的三件事:\n模型作出的观测、它决定的变更,以及它想揭示的警告。标签指明该值所关于的内容,而\n值是一个单数,这覆盖了一次仿真所记录的绝大多数内容,而不强加调用方必须设计的\n模式。\n\n轨迹保持记录在它们被作出的次序,而非时间次序。该区别要紧:处理器可能先为它刚\n调度的后续事件记录了一个值,再为它正在处理的事件记录一个值,而模型推理所用的\n次序值得保留。想要时间次序的调用方用 [`Trace::in_time_order`] 索取。", "items": [ { "kind": "enum", "name": "RecordKind", "signature": "pub enum RecordKind {", "doc": "What a record is about.\n\nThe three kinds separate the things a caller usually wants to tell apart\nwhen it reads a trace back: a value the model measured, a decision it\nmade, and a problem it wants to surface.", + "doc_zh": "一条记录所关于的内容。\n\n三种类别区分了调用方回读轨迹时通常想分辨的事物:模型测得的值、模型\n作出的决策,以及它想揭示的问题。", "associated": false }, { @@ -8171,6 +9332,7 @@ "name": "name", "signature": "pub const fn name(self) -> &'static str {", "doc": "Returns a short name for the kind.", + "doc_zh": "返回该类别的简称。", "associated": true }, { @@ -8178,6 +9340,7 @@ "name": "is_warning", "signature": "pub const fn is_warning(self) -> bool {", "doc": "Returns `true` when the kind is a warning.", + "doc_zh": "当该类别为警告时返回 `true`。", "associated": true }, { @@ -8185,6 +9348,7 @@ "name": "Record", "signature": "pub struct Record {", "doc": "One recorded value.\n\nA record is small and copied by value, so a trace holds a flat list rather\nthan a tree of references.", + "doc_zh": "一个被记录的值。\n\n记录很小且按值复制,故轨迹保存的是扁平列表而非引用树。", "associated": false }, { @@ -8192,6 +9356,7 @@ "name": "new", "signature": "pub fn new(time: f64, kind: RecordKind, label: impl Into, value: f64) -> Self {", "doc": "Creates a record.", + "doc_zh": "创建一条记录。", "associated": true }, { @@ -8199,6 +9364,7 @@ "name": "observation", "signature": "pub fn observation(time: f64, label: impl Into, value: f64) -> Self {", "doc": "Creates an observation record.", + "doc_zh": "创建一条观测记录。", "associated": true }, { @@ -8206,6 +9372,7 @@ "name": "state", "signature": "pub fn state(time: f64, label: impl Into, value: f64) -> Self {", "doc": "Creates a state record.", + "doc_zh": "创建一条状态记录。", "associated": true }, { @@ -8213,6 +9380,7 @@ "name": "warning", "signature": "pub fn warning(time: f64, label: impl Into, value: f64) -> Self {", "doc": "Creates a warning record.", + "doc_zh": "创建一条警告记录。", "associated": true }, { @@ -8220,6 +9388,7 @@ "name": "is_valid", "signature": "pub fn is_valid(&self) -> bool {", "doc": "Returns `true` when the record is usable.\n\nA record whose time or value is not finite has no place in a trace,\nbecause it cannot be ordered or summarized.", + "doc_zh": "当该记录可用时返回 `true`。\n\n时间或值非有限的记录在轨迹中没有位置,因为它无法被排序或汇总。", "associated": true }, { @@ -8227,6 +9396,7 @@ "name": "Recorder", "signature": "pub struct Recorder {", "doc": "Collects records as a simulation runs.\n\nThe recorder is the write side of a trace: a handler calls one of its\nmethods for each value it wants to keep, and asks for the trace when the\nrun finishes. The two are separate so that a handler can hold a recorder\nwhile the trace is read elsewhere.\n\n```\nuse apsis_output::Recorder;\n\nlet mut recorder = Recorder::new();\nrecorder.observe(0.0, \"range\", 1_000.0);\nrecorder.observe(10.0, \"range\", 950.0);\n\nlet summary = recorder.trace().summarize(\"range\").expect(\"the series exists\");\nassert_eq!(summary.count, 2);\nassert_eq!(summary.change, -50.0);\n```", + "doc_zh": "在仿真运行过程中收集记录。\n\n记录器是轨迹的写入侧:处理器为其想保留的每个值调用它的某个方法,\n并在运行结束时索取轨迹。二者分开,使处理器可以持有记录器而轨迹\n在别处被读取。\n\n```\nuse apsis_output::Recorder;\n\nlet mut recorder = Recorder::new();\nrecorder.observe(0.0, \"range\", 1_000.0);\nrecorder.observe(10.0, \"range\", 950.0);\n\nlet summary = recorder.trace().summarize(\"range\").expect(\"the series exists\");\nassert_eq!(summary.count, 2);\nassert_eq!(summary.change, -50.0);\n```", "associated": false }, { @@ -8234,6 +9404,7 @@ "name": "new", "signature": "pub const fn new() -> Self {", "doc": "Creates an empty recorder.", + "doc_zh": "创建一个空记录器。", "associated": true }, { @@ -8241,6 +9412,7 @@ "name": "record", "signature": "pub fn record( &mut self, time: f64, kind: RecordKind, label: impl Into, value: f64, ) -> bool {", "doc": "Records a value of a given kind.\n\nReturns `false` when the record is not usable, which a caller can\nignore when the value is known to be finite.", + "doc_zh": "记录给定类别的一个值。\n\n该记录不可用时返回 `false`;当值已知为有限时调用方可忽略。", "associated": true }, { @@ -8248,6 +9420,7 @@ "name": "observe", "signature": "pub fn observe(&mut self, time: f64, label: impl Into, value: f64) -> bool {", "doc": "Records an observation.", + "doc_zh": "记录一次观测。", "associated": true }, { @@ -8255,6 +9428,7 @@ "name": "note", "signature": "pub fn note(&mut self, time: f64, label: impl Into, value: f64) -> bool {", "doc": "Records a state change.", + "doc_zh": "记录一次状态变更。", "associated": true }, { @@ -8262,6 +9436,7 @@ "name": "warn", "signature": "pub fn warn(&mut self, time: f64, label: impl Into, value: f64) -> bool {", "doc": "Records a warning.", + "doc_zh": "记录一次警告。", "associated": true }, { @@ -8269,6 +9444,7 @@ "name": "len", "signature": "pub fn len(&self) -> usize {", "doc": "Returns the number of records made.", + "doc_zh": "返回已作出的记录数。", "associated": true }, { @@ -8276,6 +9452,7 @@ "name": "is_empty", "signature": "pub fn is_empty(&self) -> bool {", "doc": "Returns `true` when no records have been made.", + "doc_zh": "当尚无记录时返回 `true`。", "associated": true }, { @@ -8283,6 +9460,7 @@ "name": "refused", "signature": "pub const fn refused(&self) -> u64 {", "doc": "Returns the number of records refused because they were not usable.\n\nA caller that records values from a model checks this to see whether\nany value was dropped, rather than comparing counts by hand.", + "doc_zh": "返回因不可用而被拒绝的记录数。\n\n从模型记录值的调用方检查它即可看出是否有值被丢弃,而无需手工\n比较计数。", "associated": true }, { @@ -8290,6 +9468,7 @@ "name": "trace", "signature": "pub const fn trace(&self) -> &Trace {", "doc": "Returns the trace recorded so far.", + "doc_zh": "返回迄今所记录的轨迹。", "associated": true }, { @@ -8297,6 +9476,7 @@ "name": "take_trace", "signature": "pub fn take_trace(&mut self) -> Trace {", "doc": "Returns the trace, leaving an empty one behind.\n\nThis is what a caller does between runs: the finished trace goes to\nwhoever reads it, and the recorder starts fresh. The refused count is\nnot reset, because it records the whole life of the recorder.", + "doc_zh": "返回轨迹并留下一个空轨迹。\n\n这是调用方在两次运行之间所做的:已完成的轨迹交给读取它的一方,\n记录器重新开始。被拒绝的计数不重置,因为它记录记录器的整个\n生命期。", "associated": true }, { @@ -8304,6 +9484,7 @@ "name": "clear", "signature": "pub fn clear(&mut self) -> usize {", "doc": "Removes every record without changing the refused count.", + "doc_zh": "移除所有记录而不改变被拒绝的计数。", "associated": true }, { @@ -8311,6 +9492,7 @@ "name": "Trace", "signature": "pub struct Trace {", "doc": "A list of records in the order they were made.\n\nThe trace preserves insertion order rather than sorting by time, because\nthe order a model recorded in is part of what it decided. A caller that\nneeds time order asks for [`Trace::in_time_order`], which leaves the\noriginal alone.", + "doc_zh": "按作出次序排列的记录列表。\n\n轨迹保留插入次序而不按时间排序,因为模型记录的次序是其决策的一部分。\n需要时间次序的调用方索取 [`Trace::in_time_order`],它不改变原轨迹。", "associated": false }, { @@ -8318,6 +9500,7 @@ "name": "from_records", "signature": "pub fn from_records(records: Vec) -> Self {", "doc": "Creates a trace from a list of records.", + "doc_zh": "由记录列表创建轨迹。", "associated": true }, { @@ -8325,6 +9508,7 @@ "name": "records", "signature": "pub fn records(&self) -> &[Record] {", "doc": "Returns the records in the order they were made.", + "doc_zh": "按作出次序返回各记录。", "associated": true }, { @@ -8332,6 +9516,7 @@ "name": "push", "signature": "pub fn push(&mut self, record: Record) -> bool {", "doc": "Adds a record, returning `false` when it is not usable.\n\nA record whose time or value is not finite is refused rather than\nstored, so a trace never holds a value that cannot be summarized.", + "doc_zh": "添加一条记录,不可用时返回 `false`。\n\n时间或值非有限的记录被拒绝而非存储,故轨迹绝不保存无法汇总的\n值。", "associated": true }, { @@ -8339,6 +9524,7 @@ "name": "is_stable", "signature": "pub fn is_stable(&self) -> bool {", "doc": "Returns `true` when no record has a time or value that is not finite.", + "doc_zh": "当没有任何记录的时间或值非有限时返回 `true`。", "associated": true }, { @@ -8346,6 +9532,7 @@ "name": "in_time_order", "signature": "pub fn in_time_order(&self) -> Vec {", "doc": "Returns the records in time order, earliest first.\n\nRecords at the same instant keep their insertion order, so the sort is\nstable and the result does not depend on the sort algorithm.", + "doc_zh": "按时间次序返回各记录,最早在前。\n\n同一瞬时的记录保持其插入次序,故该排序是稳定的,结果不依赖于\n排序算法。", "associated": true }, { @@ -8353,6 +9540,7 @@ "name": "of_kind", "signature": "pub fn of_kind(&self, kind: RecordKind) -> Vec<&Record> {", "doc": "Returns the records of one kind, in order.", + "doc_zh": "按次序返回某一类别的记录。", "associated": true }, { @@ -8360,6 +9548,7 @@ "name": "with_label", "signature": "pub fn with_label(&self, label: &str) -> Vec<&Record> {", "doc": "Returns the records with a label, in order.", + "doc_zh": "按次序返回具有某标签的记录。", "associated": true }, { @@ -8367,6 +9556,7 @@ "name": "value_of", "signature": "pub fn value_of(&self, label: &str) -> Option {", "doc": "Returns the value of the last record with a label.\n\nThis is what a caller asks for after a run: the final value a series\nreached. Returns `None` when no record has the label.", + "doc_zh": "返回具有某标签的最后一条记录的值。\n\n这是调用方在运行结束后所索取的:某序列达到的最终值。没有记录\n具有该标签时返回 `None`。", "associated": true }, { @@ -8374,6 +9564,7 @@ "name": "last_time_of", "signature": "pub fn last_time_of(&self, label: &str) -> Option {", "doc": "Returns the time of the last record with a label.", + "doc_zh": "返回具有某标签的最后一条记录的时间。", "associated": true }, { @@ -8381,6 +9572,7 @@ "name": "labels", "signature": "pub fn labels(&self) -> Vec<&str> {", "doc": "Returns the labels present, in sorted order.\n\nThe order is sorted rather than first seen, so that two traces with\nthe same labels list them the same way.", + "doc_zh": "按排序次序返回出现的标签。\n\n次序是按排序而非按首次出现,使具有相同标签的两个轨迹以相同\n方式列出它们。", "associated": true }, { @@ -8388,6 +9580,7 @@ "name": "summarize", "signature": "pub fn summarize(&self, label: &str) -> Option {", "doc": "Returns a summary of the series recorded under a label.\n\nReturns `None` when no record has the label.", + "doc_zh": "返回在某标签下所记录序列的汇总。\n\n没有记录具有该标签时返回 `None`。", "associated": true }, { @@ -8395,6 +9588,7 @@ "name": "warning_count", "signature": "pub fn warning_count(&self) -> usize {", "doc": "Returns the number of warnings in the trace.", + "doc_zh": "返回轨迹中警告的数量。", "associated": true }, { @@ -8402,6 +9596,7 @@ "name": "summaries", "signature": "pub fn summaries(&self) -> Vec {", "doc": "Returns a summary of every label, in sorted label order.", + "doc_zh": "按标签排序次序返回每个标签的汇总。", "associated": true }, { @@ -8409,6 +9604,7 @@ "name": "longest_series", "signature": "pub fn longest_series(&self) -> usize {", "doc": "Returns the greatest number of records that share a label.\n\nThis is the length of the longest series, which a caller uses to size\na table of the trace.", + "doc_zh": "返回共享同一标签的记录数的最大值。\n\n它是最长序列的长度,调用方据此确定轨迹表格的大小。", "associated": true }, { @@ -8416,6 +9612,7 @@ "name": "SeriesSummary", "signature": "pub struct SeriesSummary {", "doc": "A summary of one series of records.", + "doc_zh": "某一条记录序列的汇总。", "associated": false }, { @@ -8423,6 +9620,7 @@ "name": "range", "signature": "pub fn range(&self) -> f64 {", "doc": "Returns the spread between the greatest and smallest values.", + "doc_zh": "返回最大值与最小值之间的跨度。", "associated": true }, { @@ -8430,6 +9628,7 @@ "name": "is_constant", "signature": "pub fn is_constant(&self) -> bool {", "doc": "Returns `true` when the series never changed.", + "doc_zh": "当该序列从未变化时返回 `true`。", "associated": true } ] @@ -8437,13 +9636,15 @@ { "name": "apsis-sim", "layer": "sim", - "summary": "Simulation facade that integrates the simulation layer. This crate is the single entry point to the simulation layer. It ties the four pieces together: a [`Scenario`] describes the initial conditions, the ops layer plans it, the engine runs the resulting timeline, and a [`Recorder`] collects what the run produced. The sequence a caller follows is short: 1. [`Simulation::prepare`] checks a scenario and plans it, producing a schedule. 2. [`Simulation::start`] turns that schedule into a timeline of start and end events and hands back a [`Session`]. 3. [`Session::run`] drives the engine, calling a handler at each event, and returns a [`RunReport`] holding the engine outcome and the trace. The plan is a greedy heuristic, described in `apsis-scheduler`, so a run may leave tasks unplaced. The facade reports that rather than hiding it: [`Simulation::plan`] exposes the full planning result, and [`Simulation::has_full_plan`] answers the common question directly. The timeline is the schedule, not a physical model. A start event fires when a task begins and an end event when it ends, which is what a caller needs to record state over time; the handler supplies whatever physics or bookkeeping the run requires. ``` use apsis_sim::{Activity, Scenario, Simulation}; let text = \"\\ scenario demo resource 0 capacity 1 window 0 0 600 task 0 duration 60 require 0 0 1 \"; let scenario = Scenario::parse(text).expect(\"the text is well formed\"); let simulation = Simulation::prepare(scenario).expect(\"the scenario is usable\"); let mut session = simulation.start(); let mut starts = 0_u32; let report = session.run(|_engine, _recorder, event| { if matches!(event.payload, Activity::Start { .. }) { starts += 1; } }); assert!(simulation.has_full_plan()); assert_eq!(starts, 1); assert_eq!(report.outcome.executed, 2); ```", + "summary": "Simulation facade that integrates the simulation layer.\n\nThis crate is the single entry point to the simulation layer. It ties the\nfour pieces together: a [`Scenario`] describes the initial conditions, the\nops layer plans it, the engine runs the resulting timeline, and a\n[`Recorder`] collects what the run produced.\n\nThe sequence a caller follows is short:\n\n1. [`Simulation::prepare`] checks a scenario and plans it, producing a\n schedule.\n2. [`Simulation::start`] turns that schedule into a timeline of start and\n end events and hands back a [`Session`].\n3. [`Session::run`] drives the engine, calling a handler at each event,\n and returns a [`RunReport`] holding the engine outcome and the trace.\n\nThe plan is a greedy heuristic, described in `apsis-scheduler`, so a run\nmay leave tasks unplaced. The facade reports that rather than hiding it:\n[`Simulation::plan`] exposes the full planning result, and\n[`Simulation::has_full_plan`] answers the common question directly.\n\nThe timeline is the schedule, not a physical model. A start event fires\nwhen a task begins and an end event when it ends, which is what a caller\nneeds to record state over time; the handler supplies whatever physics or\nbookkeeping the run requires.\n\n```\nuse apsis_sim::{Activity, Scenario, Simulation};\n\nlet text = \"\\\nscenario demo\nresource 0 capacity 1\nwindow 0 0 600\ntask 0 duration 60\nrequire 0 0 1\n\";\nlet scenario = Scenario::parse(text).expect(\"the text is well formed\");\nlet simulation = Simulation::prepare(scenario).expect(\"the scenario is usable\");\n\nlet mut session = simulation.start();\nlet mut starts = 0_u32;\nlet report = session.run(|_engine, _recorder, event| {\n if matches!(event.payload, Activity::Start { .. }) {\n starts += 1;\n }\n});\n\nassert!(simulation.has_full_plan());\nassert_eq!(starts, 1);\nassert_eq!(report.outcome.executed, 2);\n```", + "summary_zh": "整合仿真层的仿真门面。\n\n本 crate 是仿真层的单一入口。它把四部分联系起来:[`Scenario`] 描述初始条件,\nops 层为其作计划,引擎运行所得时间线,[`Recorder`] 收集该运行所产生的。\n\n调用方所遵循的序列很短:\n\n1. [`Simulation::prepare`] 检查场景并为其作计划,产生一个调度。\n2. [`Simulation::start`] 把该调度转为由开始与结束事件构成的时间线,并交回一个\n [`Session`]。\n3. [`Session::run`] 驱动引擎,在每个事件处调用一个处理器,并返回一个\n [`RunReport`],其中含有引擎结果与轨迹。\n\n该计划是贪心启发式,在 `apsis-scheduler` 中有述,故一次运行可能留下未放置的任务。\n门面报告这一点而非隐藏它:[`Simulation::plan`] 公开完整规划结果,而\n[`Simulation::has_full_plan`] 直接回答那个常见的问题。\n\n时间线是调度而非物理模型。任务开始时触发一个开始事件,结束时触发一个结束事件,\n这正是调用方随时间记录状态所需的;处理器提供该运行所需的任何物理或簿记。", "items": [ { "kind": "enum", "name": "Activity", "signature": "pub enum Activity {", "doc": "One thing that happens in the timeline.\n\nThe timeline is derived from a schedule: a placed task contributes a start\nand an end, and the handler is called at each. The two are separate events\nrather than one interval so that the engine orders them with everything\nelse on the clock, which is what lets two tasks overlap correctly.", + "doc_zh": "时间线上发生的一件事。\n\n时间线由一个调度导出:一处被放置的任务贡献一个开始与一个结束,\n处理器在每个处被调用。二者是分开的事件而非一个区间,使引擎把它们\n与时钟上其余一切一起排序,这正是让两个任务正确重叠的原因。", "associated": false }, { @@ -8451,6 +9652,7 @@ "name": "start", "signature": "pub const fn start(task: TaskId) -> Self {", "doc": "Creates a start activity.", + "doc_zh": "创建一个开始活动。", "associated": true }, { @@ -8458,6 +9660,7 @@ "name": "end", "signature": "pub const fn end(task: TaskId) -> Self {", "doc": "Creates an end activity.", + "doc_zh": "创建一个结束活动。", "associated": true }, { @@ -8465,6 +9668,7 @@ "name": "task", "signature": "pub const fn task(self) -> TaskId {", "doc": "Returns the task the activity is about.", + "doc_zh": "返回该活动所关于的任务。", "associated": true }, { @@ -8472,6 +9676,7 @@ "name": "is_start", "signature": "pub const fn is_start(self) -> bool {", "doc": "Returns `true` when the activity is a start.", + "doc_zh": "当该活动为开始时返回 `true`。", "associated": true }, { @@ -8479,6 +9684,7 @@ "name": "is_end", "signature": "pub const fn is_end(self) -> bool {", "doc": "Returns `true` when the activity is an end.", + "doc_zh": "当该活动为结束时返回 `true`。", "associated": true }, { @@ -8486,6 +9692,7 @@ "name": "SimulationErrorKind", "signature": "pub enum SimulationErrorKind {", "doc": "The category of a simulation error.", + "doc_zh": "仿真错误的类别。", "associated": false }, { @@ -8493,6 +9700,7 @@ "name": "SimulationError", "signature": "pub struct SimulationError {", "doc": "A failure while preparing or starting a simulation.", + "doc_zh": "准备或启动仿真时发生的失败。", "associated": false }, { @@ -8500,6 +9708,7 @@ "name": "new", "signature": "pub fn new(kind: SimulationErrorKind) -> Self {", "doc": "Creates an error with the given category.", + "doc_zh": "用给定类别创建一个错误。", "associated": true }, { @@ -8507,6 +9716,7 @@ "name": "with_detail", "signature": "pub fn with_detail(mut self, detail: impl Into) -> Self {", "doc": "Attaches a description of what caused the failure.", + "doc_zh": "附加对导致该失败之事的说明。", "associated": true }, { @@ -8514,6 +9724,7 @@ "name": "kind", "signature": "pub const fn kind(&self) -> SimulationErrorKind {", "doc": "Returns the category of this failure.", + "doc_zh": "返回该失败的类别。", "associated": true }, { @@ -8521,6 +9732,7 @@ "name": "detail", "signature": "pub fn detail(&self) -> Option<&str> {", "doc": "Returns the detail text attached to this error, if any.", + "doc_zh": "返回附加在该错误上的说明文本,若有。", "associated": true }, { @@ -8528,6 +9740,7 @@ "name": "RunReport", "signature": "pub struct RunReport {", "doc": "What a run produced.\n\nThe report pairs the engine's own outcome with the output trace, so a\ncaller has everything a run produced without reaching back into the\nsession.", + "doc_zh": "一次运行所产生的结果。\n\n该报告把引擎自身的结果与输出轨迹配对,使调用方无需回访会话即可得到\n一次运行所产生的全部内容。", "associated": false }, { @@ -8535,6 +9748,7 @@ "name": "executed", "signature": "pub const fn executed(&self) -> u64 {", "doc": "Returns the number of activities the run executed.", + "doc_zh": "返回该运行所执行的活动数。", "associated": true }, { @@ -8542,6 +9756,7 @@ "name": "is_complete", "signature": "pub fn is_complete(&self) -> bool {", "doc": "Returns `true` when the run finished because the timeline was\nexhausted rather than because a limit stopped it.", + "doc_zh": "当运行因时间线耗尽而结束、而非因某个限制而停止时返回 `true`。", "associated": true }, { @@ -8549,6 +9764,7 @@ "name": "warning_count", "signature": "pub fn warning_count(&self) -> usize {", "doc": "Returns the number of warnings recorded.", + "doc_zh": "返回所记录的警告数。", "associated": true }, { @@ -8556,6 +9772,7 @@ "name": "Session", "signature": "pub struct Session {", "doc": "One run of a prepared simulation.\n\nA session owns the engine and the recorder for a single run, so it can be\ndriven from one place without the caller holding either. It is created by\n[`Simulation::start`].", + "doc_zh": "一次已准备仿真的运行。\n\n会话为单次运行拥有引擎与记录器,故它可在一处被驱动而无需调用方持有\n二者中的任何一个。它由 [`Simulation::start`] 创建。", "associated": false }, { @@ -8563,6 +9780,7 @@ "name": "engine", "signature": "pub const fn engine(&self) -> &Engine {", "doc": "Returns the engine, for a caller that needs its state between calls.", + "doc_zh": "返回引擎,供需要在两次调用之间获取其状态的调用方使用。", "associated": true }, { @@ -8570,6 +9788,7 @@ "name": "engine_mut", "signature": "pub const fn engine_mut(&mut self) -> &mut Engine {", "doc": "Returns the engine for modification.", + "doc_zh": "返回引擎以供修改。", "associated": true }, { @@ -8577,6 +9796,7 @@ "name": "recorder", "signature": "pub const fn recorder(&self) -> &Recorder {", "doc": "Returns the recorder, for a caller that reads the trace as it grows.", + "doc_zh": "返回记录器,供在轨迹增长时读取它的调用方使用。", "associated": true }, { @@ -8584,6 +9804,7 @@ "name": "recorder_mut", "signature": "pub const fn recorder_mut(&mut self) -> &mut Recorder {", "doc": "Returns the recorder for modification.", + "doc_zh": "返回记录器以供修改。", "associated": true }, { @@ -8591,6 +9812,7 @@ "name": "now", "signature": "pub const fn now(&self) -> f64 {", "doc": "Returns the current simulation time, in seconds.", + "doc_zh": "返回当前仿真时刻,单位为秒。", "associated": true }, { @@ -8598,6 +9820,7 @@ "name": "run", "signature": "pub fn run(&mut self, mut handler: F) -> RunReport where F: FnMut(&mut Engine, &mut Recorder, apsis_event::Event), {", "doc": "Runs the engine, calling the handler at each activity.\n\nThe handler receives the engine, the recorder, and the activity. It\nnormally records something for the activity and may schedule further\nevents; the engine delivers them in time order alongside the timeline.\n\nReturns a report holding the engine outcome and the trace made during\nthis call. The recorder is left empty for the next call.", + "doc_zh": "运行引擎,在每个活动处调用处理器。\n\n处理器接收引擎、记录器与活动。它通常为该活动记录些什么,并可\n再调度更多事件;引擎把它们与时间线一起按时间次序投递。\n\n返回一个报告,其中含有引擎结果与本次调用期间所作出的轨迹。记录器\n被留空以备下次调用。", "associated": true }, { @@ -8605,6 +9828,7 @@ "name": "note", "signature": "pub fn note(&mut self, time: f64, label: impl Into, value: f64) -> bool {", "doc": "Records an activity as a state change without running anything.\n\nThis is for a caller that drives the engine itself and only wants the\nrecorder.", + "doc_zh": "把某个活动记录为状态变更而不运行任何东西。\n\n这适用于自行驱动引擎而只想要记录器的调用方。", "associated": true }, { @@ -8612,6 +9836,7 @@ "name": "record", "signature": "pub fn record( &mut self, time: f64, kind: RecordKind, label: impl Into, value: f64, ) -> bool {", "doc": "Records a value of a given kind without running anything.", + "doc_zh": "记录给定类别的一个值而不运行任何东西。", "associated": true }, { @@ -8619,6 +9844,7 @@ "name": "Simulation", "signature": "pub struct Simulation {", "doc": "A scenario prepared for a run.\n\nA simulation holds the scenario, the plan that was made from it, and the\ntimeline that plan implies. Preparing is separate from running so that a\ncaller can inspect the plan, and reject it, before a run commits to it.", + "doc_zh": "为一次运行准备好的场景。\n\n仿真保存场景、由其作出的计划,以及该计划所蕴含的时间线。准备与运行\n分开,使调用方可以在运行承诺之前检查该计划并拒绝它。", "associated": false }, { @@ -8626,6 +9852,7 @@ "name": "prepare", "signature": "pub fn prepare(scenario: Scenario) -> Result {", "doc": "Checks a scenario and plans it.\n\nThe scenario is validated, the tasks are planned against the\nresources, and the resulting schedule is turned into a timeline of\nstart and end events. The plan is greedy, so a scenario may be\nprepared successfully and still leave tasks unplaced; that is reported\nby [`Simulation::has_full_plan`] rather than being an error.", + "doc_zh": "检查场景并为其作计划。\n\n场景被校验,任务针对资源被规划,所得调度被转为由开始与结束事件\n构成的时间线。该计划是贪心的,故场景可能被成功准备却仍留下未\n放置的任务;这由 [`Simulation::has_full_plan`] 报告,而不是当作\n错误。", "associated": true }, { @@ -8633,6 +9860,7 @@ "name": "from_plan", "signature": "pub fn from_plan( scenario: Scenario, result: SchedulingResult, ) -> Result {", "doc": "Builds a simulation from an already planned result.\n\nThis is for a caller that has planned with its own settings and wants\nthe rest of the facade: the timeline and the run loop. The result must\nbelong to the scenario, which is checked.", + "doc_zh": "由已规划好的结果构建仿真。\n\n这适用于已用自己的设置完成规划、只想要外观其余部分的调用方:时间\n线与运行循环。该结果必须属于该场景,这一点会被检查。", "associated": true }, { @@ -8640,6 +9868,7 @@ "name": "scenario", "signature": "pub const fn scenario(&self) -> &Scenario {", "doc": "Returns the scenario.", + "doc_zh": "返回场景。", "associated": true }, { @@ -8647,6 +9876,7 @@ "name": "resources", "signature": "pub const fn resources(&self) -> &ResourceSet {", "doc": "Returns the resources in the scenario.", + "doc_zh": "返回场景中的资源。", "associated": true }, { @@ -8654,6 +9884,7 @@ "name": "plan", "signature": "pub const fn plan(&self) -> &SchedulingResult {", "doc": "Returns the planning result.", + "doc_zh": "返回规划结果。", "associated": true }, { @@ -8661,6 +9892,7 @@ "name": "schedule", "signature": "pub const fn schedule(&self) -> &Schedule {", "doc": "Returns the schedule.", + "doc_zh": "返回调度。", "associated": true }, { @@ -8668,6 +9900,7 @@ "name": "tasks", "signature": "pub fn tasks(&self) -> &[Task] {", "doc": "Returns the tasks in the scenario.", + "doc_zh": "返回场景中的任务。", "associated": true }, { @@ -8675,6 +9908,7 @@ "name": "timeline", "signature": "pub fn timeline(&self) -> &[(f64, Activity)] {", "doc": "Returns the timeline as time and activity pairs, in time order.\n\nEach placed task contributes a start and an end. Ties are ordered so\nthat an end precedes a start at the same instant, which lets two back\nto back tasks share a boundary without the reader seeing them overlap.", + "doc_zh": "按时间次序返回时间与活动对的列表。\n\n每处被放置的任务贡献一个开始与一个结束。并列情形按结束先于开始\n排序,使两个前后相接的任务可共享一个边界而读者不会看到它们\n重叠。", "associated": true }, { @@ -8682,6 +9916,7 @@ "name": "activity_count", "signature": "pub fn activity_count(&self) -> usize {", "doc": "Returns the number of start and end events in the timeline.", + "doc_zh": "返回时间线中开始与结束事件的数量。", "associated": true }, { @@ -8689,6 +9924,7 @@ "name": "has_full_plan", "signature": "pub fn has_full_plan(&self) -> bool {", "doc": "Returns `true` when every offered task was placed.", + "doc_zh": "当所有提交任务均被放置时返回 `true`。", "associated": true }, { @@ -8696,6 +9932,7 @@ "name": "honours_mandatory", "signature": "pub fn honours_mandatory(&self) -> bool {", "doc": "Returns `true` when every mandatory task was placed.", + "doc_zh": "当每个必需任务都被放置时返回 `true`。", "associated": true }, { @@ -8703,6 +9940,7 @@ "name": "honours_precedence", "signature": "pub fn honours_precedence(&self) -> bool {", "doc": "Returns `true` when every precedence constraint is satisfied.", + "doc_zh": "当每条先后约束都被满足时返回 `true`。", "associated": true }, { @@ -8710,6 +9948,7 @@ "name": "deferred", "signature": "pub fn deferred(&self) -> Vec {", "doc": "Returns the tasks that were not placed for want of time.", + "doc_zh": "返回因时间不足而未被放置的任务。", "associated": true }, { @@ -8717,6 +9956,7 @@ "name": "infeasible", "signature": "pub fn infeasible(&self) -> Vec {", "doc": "Returns the tasks that cannot be placed at all.", + "doc_zh": "返回完全无法放置的任务。", "associated": true }, { @@ -8724,6 +9964,7 @@ "name": "evaluate", "signature": "pub fn evaluate(&self) -> Evaluation {", "doc": "Evaluates the schedule against the scenario.", + "doc_zh": "针对场景评估该调度。", "associated": true }, { @@ -8731,6 +9972,7 @@ "name": "start", "signature": "pub fn start(&self) -> Session {", "doc": "Starts a run.\n\nThe returned session owns its own engine and recorder, so several\nsessions may run from one prepared simulation without interfering. The\nclock starts at the scenario epoch and the run limits come from the\nscenario.", + "doc_zh": "开始一次运行。\n\n返回的会话拥有自己的引擎与记录器,故多个会话可从同一个已准备\n仿真运行而互不干扰。时钟从场景历元开始,运行限制来自场景。", "associated": true } ] diff --git a/content/api.zh.json b/content/api.zh.json deleted file mode 100644 index 62e9550..0000000 --- a/content/api.zh.json +++ /dev/null @@ -1,539 +0,0 @@ -{ - "crates": { - "apsis-core": "ApsisRS 工作区的共享基础类型:通用错误类型、结果别名、物理常数与单位标记 trait。\n\n本 crate 位于依赖图的底层。它不依赖任何其他 ApsisRS crate,因此其他所有 crate 都可以使用它。\n\n这里承载三件事:\n\n- [`Error`] 与 [`Result`] 是携带粗略 [`ErrorKind`] 的通用错误。它们的存在使那些失败没有有趣结构的函数无需自造类型即可报告错误,也使工作区有一个可回退的形状。它们刻意**不是**各层的错误类型:有特定领域失败的层定义自己的 `Error` 与 `ErrorKind`,其变体指明调用方需要匹配的具体条件。\n- [`constant`] 存放以 SI 单位表示的物理与天文常数。\n- [`Quantity`] 标记携带物理单位的 newtype,使泛型代码可以接受任意物理量并仍能恢复其单位类型。", - "apsis-units": "物理量的强类型 newtype。\n\n每个物理量都是 `f64` 之上的 newtype,在类型系统中携带其单位,因此长度不能传给期望时长的位置。同一量纲的单位通过 [`Quantity::to_unit`] 换算,各单位也提供常见情况的直接换算方法。\n\n一个 newtype 同时实现 [`Unit`] 与 [`Quantity`],并以自身类型作为关联单位。例如 [`Meters`] 是 [`Length`](dimension::Length) 单位,也是以自身为单位的物理量。\n\n此处覆盖的量纲为长度、时间、频率、角度与热力学温度。温度是本集合中唯一的仿射标度,因此摄氏度由 [`Kelvin`] 上的显式方法处理,而不走线性的 [`Unit`] 换算因子。", - "apsis-math": "ApsisRS 工作区的无依赖线性代数。\n\n本 crate 提供三种类型:\n\n- [`Vec3`],`f64` 上的三分量列矢量\n- [`Quat`],用于旋转的单位四元数\n- [`Mat3`],按列主序存储的三阶矩阵\n\n这些类型是普通数据,除各方法文档所述外没有内部不变量,因此字段是公开的,值可以由结构体字面量构造。对某些输入在数学上无定义的运算——例如对零矢量归一化——返回 [`Option`] 而不是 panic 或产生非有限结果。\n\n所有旋转都按右手定则绕给定轴进行,所有角度都以弧度表示。输入角度不做归约;需要规范区间的方法会在文档中说明该要求。", - "apsis-macros": "支撑 ApsisRS 工作区的宏。", - "apsis-time": "时间尺度、历元与日历转换。\n\n本 crate 提供工作区其余部分所使用的时间模型。[`Epoch`] 是以国际原子时尺度上的约化儒略日存储的时刻,[`TimeScale`] 则为一个数所表示的尺度命名。\n\n存储在单一均匀尺度上使运算精确:两个历元之差就是二者之间经过的 SI 时间,与它们创建时所用的尺度无关。UTC 不是均匀的,因为为保持其接近地球自转而插入闰秒,因此 UTC 换算经由 `apsis-leap` 中的表。\n\n来源:\n\n- 儒略日与约化儒略日约定:IAU,见《天文年历解释性补充》。\n- 地球时相对 TAI 的偏差:IAU 2000 决议 B1.9。\n- 闰秒:IERS Bulletin C,由 `apsis-leap` 内嵌。\n- 日历算法:Howard Hinnant,*chrono-Compatible Low-Level Date Algorithms*,2015。", - "apsis-leap": "用于 UTC 时间尺度的内嵌闰秒表。\n\nUTC 通过插入闰秒保持接近 UT1,即由地球自转定义的尺度。每次插入都是 UTC 时钟中的阶跃间断,因此从 UTC 求“已经过多少时间”需要这张表。\n\n本 crate 以表的形式提供偏移 `TAI - UTC`,覆盖自 1972-01-01 起的时期,即现行方案以 10 秒偏移开始的时刻。表中最新一次插入之后的偏移保持不变,直到宣布新的闰秒,因此其后的日期是外推而非事实。\n\n来源:IERS Bulletin C,以及随公告发布的偏移历史。", - "apsis-coord": "坐标系及坐标系之间的变换。\n\n本 crate 定义在类型中携带参考系的位置,因此表示在某一系中的位置不能传给期望另一系的接口。两个参考系是地心惯性系(ECI)与地心地固系(ECEF)。\n\n提供三种位置表示:\n\n- [`Eci`],惯性系中的直角坐标位置,单位为米\n- [`Ecef`],地固系中的直角坐标位置,单位为米\n- [`Geodetic`],WGS 84 椭球面上的纬度、经度与高\n\n[`Eci`] 与 [`Ecef`] 之间的变换是由地球自转角定义的旋转,由 `apsis-frames` 计算。[`Ecef`] 与 [`Geodetic`] 之间的变换使用 `apsis-geodesy` 中的 WGS 84 椭球。\n\n来源:\n\n- 地球自转角:*IERS Conventions (2010)*,公式 8.32,由 `apsis-frames` 实现。\n- 大地坐标转换与 WGS 84 椭球:NIMA TR8350.2,第三版,2000,由 `apsis-geodesy` 实现。", - "apsis-geodesy": "参考椭球与大地坐标计算。\n\n大地坐标以相对某椭球的纬度、经度与高表示。此处的纬度始终是大地纬度,即椭球法线与赤道面之间的夹角,而不是地心纬度。二者在中纬度最大相差约 0.19 度,因此对指向与测距而言这一区别很重要。\n\n[`WGS84`] 是 GPS 以及几乎所有卫星工作所使用的椭球。其定义参数是长半轴与扁率倒数;其余所有量都由这两个导出,因此导出值与标准中公布的数值在定义参数的精度内一致。\n\n来源:美国国家影像与测绘局,*Department of Defense World Geodetic System 1984*,技术报告 TR8350.2,第三版,2000。", - "apsis-frames": "参考系定义以及地心天球参考系、当日平赤道平春分点与地固系之间的旋转。\n\n本 crate 实现 IERS 约定中定义参考系之间变换的部分:地球自转角、岁差、章动、框架偏差、极移,以及由它们组合而成的完整链条。\n\n这些量在 `rf` 层及以上的精密工作中需要;对 `orbit` 层的传播而言通常只需要地球自转角,即由 UT1 的一个线性表达式给出的角度。\n\n来源:*IERS Conventions (2010)*;IAU 2000A 章动模型;IAU 2006 岁差;MHB2000 行星项。", - "apsis-tle": "两行元素集的解析。\n\n两行元素集(TLE)以北美防空司令部使用、由 Space-Track 分发的形式描述卫星轨道。卫星名称位于可选的第一行,其后两行携带轨道数据。\n\n格式是定宽的。每个字段占据固定的列范围,每行的校验和是其数字之和对十取模,其中负号计一。本 crate 将 TLE 解析为结构化字段,并校验每行校验和以及两行的元素集编号是否一致。它不解释字段含义;平均元素及其单位是 `apsis-sgp4` 的关注点。\n\n列位置从 1 开始编号,与格式文档一致。格式中有两个约定值得明确指出,因为它们容易被误读:\n\n- 指数字段写作带符号的一位数字后接量值。例如 ` 12345-3` 表示 `0.12345e-3`,而不是 `12345e-3`。\n- 隐含小数点写作不带小数点的形式。例如偏心率字段 `0006703` 表示 `0.0006703`。\n\n来源:字段布局遵循 CelesTrak 发布的格式说明 *FAQs: Two-Line Element Set Format*,其中记录了原始 NORAD 规范定义的列位置。校验和规则即该文档所述并被 Space-Track 使用。", - "apsis-sgp4": "SGP4 与 SDP4 解析轨道传播器。\n\n传播器接收两行元素集的平均元素,返回卫星在指定时刻的位置与速度。它正是元素集所拟合的模型,因此也是使用这些元素的正确方式;把同样的元素交给通用数值积分器会偏离已发布的位置。\n\n存在两个分支,传播器按轨道周期选择。周期低于 225 分钟的卫星用 SGP4 传播,该分支模拟地球扁率的长期效应与一个阻力项。周期大于等于该值的卫星用 SDP4 传播,该分支加入在高轨占主导的共振与日月摄动。\n\n输入与输出:\n\n- 元素是 [`apsis_tle::TwoLineElement`] 中的平均元素。元素集中的角度以度表示、平均运动以圈每天表示;本 crate 内部转换为弧度与弧度每分钟。\n- 输出位于真赤道平春分点(TEME)坐标系,位置单位为千米、速度单位为千米每秒。TEME 是模型定义的坐标系;将其转换为地固系需要地球自转与春分点方程,不属于本 crate 的范围。\n- 时间自元素集历元量起,单位为分钟。\n\n来源:算法遵循 Vallado 等,*Revisiting Spacetrack Report #3*,AIAA 2006-6753,2006,以及随该论文分发的修订实现。", - "apsis-kepler": "开普勒轨道元素与二体问题。\n\n本 crate 定义六元素形式的椭圆轨道以及到直角状态的转换,并提供三种近点角之间的转换。\n\n这里给出的解是**二体**解:它只假设中心天体是质点。真实卫星还受到地球扁率、大气阻力与日月引力的作用,因此由瞬时状态算出的元素是**密切**元素,沿轨道变化。它们不是两行元素集中的平均元素,二者之间的转换与传播任一者都是不同的运算。", - "apsis-ephemeris": "星历采样与插值。\n\n传播器给出的是单个时刻的卫星状态,但要回答“卫星何时可见”这类问题,需要一条轨迹上的大量时刻。本 crate 把轨迹采样成表,并通过插值回答问题。\n\n插值对每个分量使用三次 Hermite 多项式,同时用到两端的位置与速度,因此它是 `C^1` 的:位置与速度在采样边界处连续。只由位置构建的表在每个采样点速度不连续,对任何需要求导的用途都是错误的。\n\n表在插值误差允许的范围内回答查询,误差随采样间距的四次方增长。区间之外的查询返回空值,而不是外推。", - "apsis-maneuver": "脉冲与有限推力轨道机动模型。\n\n机动以速度增量改变轨道。本 crate 提供两种模型:脉冲机动,在零时间内施加速度增量;有限推力机动,在一段时长内施加恒定加速度,这正是有实际推力限制的发动机的模型。\n\n由于有限推力在推力期间受重力作用,它达到的速度增量小于加速度与时长之积;这一差值即重力损失,由结果报告。\n\n除点火本身之外,本 crate 还提供霍曼转移,即在两个共面圆轨道之间最省燃料的两次脉冲转移。", - "apsis-orbit": "整合轨道层的轨道状态门面。\n\n本 crate 是轨道层的单一入口。它重导出元素解析、解析传播器与二体模型,并加入一个把它们串联起来的类型。\n\n[`Orbit`] 持有一个已准备好传播的卫星,以及它初始化时所用的引力模型,并提供该层所支持的两种轨道描述之间的转换:\n\n- 由 SGP4 传播的平均元素,这是两行元素集所针对的模型,也是复现已编目卫星已发布位置的唯一方式。\n- 瞬时开普勒元素,描述某一时刻的密切二体轨道,便于检视与规划机动。\n\n两种描述不可互换。用二体模型传播平均元素,或用 SGP4 传播密切元素,都会得到不同且错误的结果;此处的转换明确说明各自的方向。" - }, - "items": { - "SPEED_OF_LIGHT": "真空中的光速,单位为米每秒。\n\n按 SI 中米的定义是精确值。", - "GRAVITATIONAL_CONSTANT": "牛顿引力常数,单位为立方米每千克每二次方秒。\n\nCODATA 2018 值,其标准不确定度为 `1.5e-11 m^3 kg^-1 s^-2`。", - "BOLTZMANN": "玻尔兹曼常数,单位为焦耳每开尔文。\n\n按 SI 中开尔文的定义是精确值。", - "PLANCK": "普朗克常数,单位为焦耳秒。\n\n按 SI 中千克的定义是精确值。", - "ELEMENTARY_CHARGE": "基本电荷,单位为库仑。\n\n按 SI 中安培的定义是精确值。", - "ASTRONOMICAL_UNIT": "天文单位,单位为米。\n\n按 IAU 2012 决议 B2 是精确值。", - "STANDARD_GRAVITY": "标准重力加速度,单位为米每二次方秒。\n\n按标准大气的定义是精确值。", - "MINUTE": "一分钟,单位为秒。", - "HOUR": "一小时,单位为秒。", - "DAY": "24 小时的一天,单位为秒。\n\n这是 SI 日。它不是平太阳日(略长),也不是儒略日计算中使用的 86 400 秒的日,后者与本值相同。", - "JULIAN_YEAR": "365.25 天的儒略年,单位为秒。\n\n按儒略年的定义是精确值。", - "JULIAN_CENTURY": "36 525 天的儒略世纪,单位为秒。\n\n按儒略年的定义是精确值;以每儒略世纪给出的岁差与长期变化率表达式使用该单位。", - "JULIAN_DATE_UNIX_EPOCH": "Unix 历元的儒略日,即 1970-01-01T00:00:00 UTC。\n\n在儒略日系统中为 `2 440 587.5`。", - "JULIAN_DATE_J2000": "J2000.0 历元的儒略日,即 2000-01-01T12:00:00 TT。\n\n按 J2000.0 历元的定义是精确值。", - "SECONDS_J2000_TO_UNIX": "J2000.0 历元与 Unix 历元之间的秒数。\n\n由 [`JULIAN_DATE_J2000`] 与 [`JULIAN_DATE_UNIX_EPOCH`] 导出。", - "EARTH_MU": "地球引力参数,单位为立方千米每二次方秒。\n\nWGS 84 值,`GM = 398 600.4418 km^3/s^2`,其不确定度主要由牛顿引力常数的不确定度决定。轨道传播使用千米,因此该值以千米给出而非 SI 单位。", - "EARTH_EQUATORIAL_RADIUS": "地球赤道半径,单位为千米。\n\nWGS 84 长半轴,`a = 6 378.137 km`。", - "EARTH_FLATTENING": "地球扁率,无量纲。\n\nWGS 84 值,`f = 1 / 298.257 223 563`。", - "EARTH_ROTATION_RATE": "地球自转角速率,单位为弧度每秒。\n\nWGS 84 值。这是地球的标称平均角速度,`omega = 7.292 115e-5 rad/s`。", - "EARTH_J2": "地球二阶带谐系数,无量纲。\n\nWGS 84 值,`J2 = 1.082 626 683e-3`。", - "STANDARD_ATMOSPHERE": "标准大气海平面气压,单位为帕斯卡。\n\n按标准大气的定义是精确值。", - "ZERO_CELSIUS_IN_KELVIN": "摄氏零度在热力学温标上的值,单位为开尔文。\n\n按摄氏温标的定义是精确值。", - "ErrorKind": "一个 [`Error`] 的类别。\n\n变体按最常产生它们的层分组,但任何层都可以返回任何变体。调用方应匹配与自身恢复逻辑相关的变体,其余视为不透明。", - "Error": "工作区通用的错误类型。\n\n`Error` 携带可机器匹配的 [`ErrorKind`] 与人类可读的消息。可选的来源记录导致本错误的更低层错误。", - "Error::new": "创建一个只有类别、没有消息的错误。", - "Error::with_message": "创建一个带类别与消息的错误。\n\n消息应描述这一次具体失败的实际原因。它是给阅读日志的人看的,不用于匹配。", - "Error::with_source": "创建一个带类别与来源错误的错误。", - "Error::with_context": "为本错误附上消息,替换已有的消息。", - "Error::caused_by": "附上来源错误,替换已有的来源。", - "Error::kind": "返回本错误的类别。", - "Error::message": "返回附在本错误上的消息,若有。", - "Result": "错误类型为 [`ElementError`] 的 [`Result`](core::result::Result)。", - "Unit": "单一物理量纲的单位。\n\n实现者是零大小标记类型。为同一量纲实现 `Unit` 的两个单位通过 [`Quantity::to_unit`](crate::Quantity::to_unit) 换算。", - "Quantity": "与单位配对的数值。\n\n实现者是 `f64` 与 [`Unit`] 之上的 newtype。该 trait 使泛型代码无需假定所收到的 newtype 即可访问存储的数值及其单位。", - "Mat3": "按列主序存储的三阶矩阵。\n\n字段名遵循 `m<行><列>` 约定,因此 `m01` 是第一行第二列的元。字段按列主序列出,[`Mat3::from_columns`] 与 [`Mat3::column`] 也使用同一顺序。", - "Mat3::ZERO": "零矩阵。", - "Mat3::IDENTITY": "单位矩阵。", - "Mat3::from_rows": "由九个元素按行优先顺序创建一个矩阵。\n\n参数按行分组:先是 `row0`,然后是 `row1`,最后是 `row2`。", - "Mat3::from_columns": "由三个列向量创建一个矩阵。", - "Mat3::from_diagonal": "由对角元素的向量创建一个对角矩阵。", - "Mat3::from_quat": "由单位四元数创建一个旋转矩阵。\n\n假定该四元数长度为 1。非单位四元数会产生一个带缩放的矩阵,……", - "Mat3::from_axis_angle": "创建绕单位轴 `axis` 旋转 `angle` 弧度的旋转矩阵。\n\n当该轴长度为零或非有限时返回 `None`。", - "Mat3::from_rotation_x": "创建绕第一轴旋转 `angle` 弧度的旋转矩阵。", - "Mat3::from_rotation_y": "创建绕第二轴旋转 `angle` 弧度的旋转矩阵。", - "Mat3::from_rotation_z": "创建绕第三轴旋转 `angle` 弧度的旋转矩阵。", - "Mat3::get": "返回第 `row` 行第 `column` 列的元素。\n\n当任一索引落在 `0..=2` 之外时返回 `None`。", - "Mat3::column": "返回第 `index` 列。\n\n当 `index` 落在 `0..=2` 之外时返回 `None`。", - "Mat3::row": "返回第 `index` 行。\n\n当 `index` 落在 `0..=2` 之外时返回 `None`。", - "Mat3::transpose": "返回转置。", - "Mat3::trace": "返回对角元素之和。", - "Mat3::determinant": "返回行列式。", - "Mat3::is_rotation": "当该矩阵在 `tolerance` 范围内是一个旋转时返回 `true`。\n\n当矩阵正交且行列式为一时,它是一个旋转。该检查……", - "Mat3::is_finite": "当九个元素都有限时返回 `true`。", - "Quat": "四元数 `w + xi + yj + zk`。\n\n旋转矢量的方法假定四元数长度为 1。乘法与加法不作此假定,因为任意四元数运算在该假定之外仍有定义。用 [`Quat::normalize`] 施加单位长度,用 [`Quat::is_normalized`] 检查它。\n\n绕单位轴 `u` 旋转角度 `theta` 的旋转为 `w = cos(theta / 2)`,`(x, y, z) = u * sin(theta / 2)`。复合约定与矩阵乘法相同:`(a * b)` 作用于矢量时先施加 `b`,再施加 `a`。", - "Quat::IDENTITY": "单位旋转。", - "Quat::new": "由四个分量创建一个四元数。", - "Quat::from_mat3": "由旋转矩阵 `m` 创建一个四元数。\n\n该算法挑选对角线上最大的元素作除数,从而使平方根保持良态……", - "Quat::vector": "返回向量部分 `(x, y, z)`。", - "Quat::norm_squared": "返回这个四元数长度的平方。", - "Quat::norm": "返回这个四元数的长度。", - "Quat::normalize": "返回缩放到单位长度的这个四元数。\n\n当长度为零或非有限时返回 `None`。", - "Quat::conjugate": "返回共轭 `w - xi - yj - zk`。\n\n对单位四元数它就是逆旋转。", - "Quat::inverse": "返回逆旋转。\n\n当长度为零或非有限时返回 `None`。", - "Quat::dot": "返回 `self` 与 `rhs` 的点积,把两者都当作四维向量。", - "Quat::rotate": "用这个旋转旋转 `v`。\n\n假定该四元数长度为 1。旋转按 `v + 2 * cross(vector, cross(vector, v) + w * v)` 计算,……", - "Quat::angle": "返回以弧度计的旋转角,落在 `[0, pi]`。\n\n假定该四元数长度为 1。", - "Quat::axis": "以单位向量返回旋转轴。\n\n对单位旋转返回 `None`,因为那里没有定义旋转轴。假定该四元数长度为 1,……", - "Quat::lerp": "返回从 `self` 到 `rhs` 的线性插值。\n\n这是弦插值,因此结果不是单位四元数,角速度也……", - "Quat::slerp": "返回从 `self` 到 `rhs` 的球面线性插值。\n\n取两段弧中较短的一段,因此结果沿着旋转所……", - "Quat::is_normalized": "当长度在 `1e-9` 的容差内为一时返回 `true`。", - "Quat::quarter_turn_x": "返回绕第一轴四分之一周的旋转。", - "Quat::half_turn_z": "返回绕第三轴半周的旋转。", - "Vec3": "三分量列矢量。\n\n分量名为 `x`、`y`、`z`。此处不赋予它们含义;参考系由使用该矢量的 crate 定义。", - "Vec3::ZERO": "零向量。", - "Vec3::X": "沿第一轴的单位向量。", - "Vec3::Y": "沿第二轴的单位向量。", - "Vec3::Z": "沿第三轴的单位向量。", - "Vec3::new": "由三个分量创建一个向量。", - "Vec3::splat": "创建一个三个分量都等于 `value` 的向量。", - "Vec3::x": "返回第一分量。", - "Vec3::y": "返回第二分量。", - "Vec3::z": "返回第三分量。", - "Vec3::get": "返回由 `index` 选出的分量,其中 `0`、`1`、`2` 分别对应 `x`、`y`、`z`。\n\n当索引落在 `0..=2` 之外时返回 `None`……", - "Vec3::cross": "返回叉积 `self x rhs`。\n\n结果同时垂直于两个输入并遵循右手定则,因此 `Vec3::X.cross(Vec3::Y)` 等于……", - "Vec3::distance": "返回 `self` 与 `rhs` 之间的欧氏距离。", - "Vec3::distance_squared": "返回 `self` 与 `rhs` 之间欧氏距离的平方。", - "Vec3::angle_between": "以弧度返回 `self` 与 `rhs` 之间的夹角。\n\n结果落在 `[0, pi]`。当任一向量长度为零或非有限时返回 `None`……", - "Vec3::abs": "返回一个向量,其每个分量都被替换为其绝对值。", - "Vec3::max_component": "返回最大的分量。", - "Vec3::min_component": "返回最小的分量。", - "Radians": "以弧度表示的角度。", - "Degrees": "以度表示的角度。", - "Radians::new": "由弧度值创建角度。", - "Radians::value": "以弧度返回本角度。", - "Radians::to_degrees": "把本角度转换为度。", - "Radians::wrap": "返回归约到半开区间 `[-pi, pi)` 的本角度。\n\n归约使用 [`f64::rem_euclid`](f64::rem_euclid),因此结果对负输入与大输入都有定义。", - "Degrees::to_radians": "把本角度转换为弧度。", - "Length": "长度。", - "Time": "时长。", - "Frequency": "频率,时间的倒数。", - "Angle": "平面角。", - "Temperature": "热力学温度。", - "Hertz": "以赫兹表示的频率。", - "Hertz::period": "返回本频率对应的周期,单位为秒。\n\n频率为零时返回 `f64::INFINITY`,这是无限周期的正确极限。", - "Meters": "以米表示的长度。", - "Kilometers": "以千米表示的长度。", - "Meters::to_kilometers": "把本长度转换为千米。", - "Kilometers::to_meters": "把本长度转换为米。", - "Kelvin": "以开尔文表示的热力学温度。\n\n低于绝对零度的值不被拒绝,因为中间运算可能合理地经过它们;需要物理温度语义的调用方应自行检查该值。", - "Kelvin::from_celsius": "由摄氏度值创建温度。\n\n使用摄氏温标定义的精确偏移。", - "Kelvin::to_celsius": "把本温度转换为摄氏度。", - "Seconds": "以秒表示的时长。", - "SECONDS_PER_DAY": "一天中的秒数,不计闰秒。", - "Calendar": "日历日期与一天中的时刻。\n\n字段是外推格里高利历字段。`second` 是区间 `[0, 60)` 内的浮点数,因此包含插入闰秒的分钟可以表示为 60 秒。", - "Calendar::new": "由各字段创建日历日期。", - "Calendar::from_ymd": "创建给定日期起始时刻的日历日期。", - "Calendar::seconds_of_day": "返回自当天开始起经过的秒数。", - "Calendar::is_valid": "当各字段在外推格里高利历中构成存在的日期时返回 `true`。", - "Calendar::is_leap_year": "当 `year` 在外推格里高利历中是闰年时返回 `true`。", - "Calendar::days_in_month": "返回给定年份给定月份的天数。\n\n`month` 超出 `1..=12` 时返回零。", - "Calendar::mjd_from_ymd": "把日历日期转换为约化儒略日,忽略一天中的时刻。\n\n结果是给定日期起始时刻的 MJD,因此它表示的是一整天的开始。", - "Calendar::ymd_from_mjd": "把约化儒略日转换为日历日期。\n\n小数部分被忽略,因此结果是包含该时刻的日历日。", - "Calendar::seconds_of_day_from_mjd": "只保留 MJD 的小数部分,作为自午夜起的秒数。\n\n对任意有限输入,结果位于 `[0, 86400)`。", - "Epoch": "TAI 时间尺度上的时刻,以约化儒略日存储。\n\n字段是私有的,以保证所存储的尺度。使用 [`Epoch::from_mjd`] 或 [`Epoch::from_calendar`] 构造,再用 [`Epoch::mjd`] 或 [`Epoch::calendar`] 按所需尺度读回。", - "Epoch::J2000": "时刻 2000-01-01T12:00:00 TT,即 J2000.0 历元的定义。\n\n该值在 TT 尺度上是 `2451545.0`,换算回 TAI。", - "Epoch::from_tai_mjd": "由 TAI 尺度上的约化儒略日创建历元。", - "Epoch::from_mjd": "由给定尺度上的约化儒略日创建历元。\n\n当该时刻无法用所请求的尺度表示时返回 `None`。这发生在 1972 年之前的 UTC,因为用于从 UTC 换算的闰秒表不适用于该区间。", - "Epoch::from_calendar": "由日历日期创建历元,按给定尺度解释。\n\n当字段不构成有效日期,或该时刻无法用所请求的尺度表示时返回 `None`。", - "Epoch::tai_mjd": "以 TAI 尺度上的约化儒略日返回该时刻。", - "Epoch::mjd": "以给定尺度上的约化儒略日返回该时刻。\n\n当该时刻早于 1972 年且请求 UTC 尺度时返回 `None`,因为闰秒表不覆盖该区间。", - "Epoch::calendar": "以给定尺度上的日历日期返回该时刻。\n\n当该时刻无法进行该尺度换算时返回 `None`;见 [`Epoch::mjd`]。", - "Epoch::seconds_until": "返回从 `self` 到 `other` 的带符号秒数。\n\n当 `other` 更晚时结果为正。由于两个历元都以均匀的 TAI 尺度存储,该差值是精确的,与创建二者时所用的尺度无关。", - "Epoch::add_seconds": "返回平移 `seconds` 后的历元。\n\n平移以均匀 TAI 尺度上的 SI 秒计量,因此它使时刻恰好前进那么多经过时间。这是唯一提供的平移:以 UTC 时钟计量的平移在区间跨越插入闰秒时会不同,而 UTC 时钟在该时刻没有单一明确的数值。需要在 UTC 尺度上进行日历运算的调用方应转换为 [`Calendar`]、调整、再转换回来。", - "Epoch::is_finite": "当存储的约化儒略日是有限值时返回 `true`。", - "JULIAN_DATE_OFFSET": "儒略日尺度起点与约化儒略日尺度起点之间的天数。", - "mjd_from_julian_date": "把儒略日转换为约化儒略日。", - "julian_date_from_mjd": "把约化儒略日转换为儒略日。", - "TT_MINUS_TAI": "地球时减国际原子时,单位为秒。\n\n按地球时的定义是精确值。", - "TAI_MINUS_GPS": "国际原子时减 GPS 时,单位为秒。\n\n按 GPS 时的定义是精确值。", - "TimeScale": "时间尺度。\n\n尺度决定一个数值时刻如何被解释。它不改变时刻本身;在尺度之间转换 [`Epoch`](crate::Epoch) 只改变用于显示它的数值。", - "TimeScale::is_uniform": "当该尺度以均匀速率运行时返回 `true`。\n\nUTC 是此处唯一不均匀的尺度,因为闰秒使其部分分钟为 61 秒。", - "TimeScale::name": "返回该尺度的简称,例如 `TAI` 或 `UTC`。", - "LeapSecond": "一次闰秒公告。\n\n该记录记载新的 `TAI - UTC` 值生效的日期。它不记录所插入秒的大小,因为迄今为止每次公告插入的都恰好是 1 秒,且新偏移比前一个偏移大 1。", - "LeapSecond::effective_mjd": "返回生效时刻的约化儒略日。\n\n闰秒在 00:00:00 UTC 生效,因此生效时刻是给定日历日的开始。", - "LEAP_SECONDS": "闰秒表,按生效日期排序。\n\n来源:IERS Bulletin C。第一项是 1972-01-01,即现行方案以继承自早期调整的 10 秒初始偏移开始之时。", - "TABLE_COVERS_FROM_MJD": "表中第一项的约化儒略日,即 1972-01-01。\n\n早于该时刻的查询会被拒绝,因为当时 UTC 尚未使用整数秒偏移。", - "TAI_MINUS_UTC_AFTER_LAST_ENTRY": "表中最后一项及其之后的 `TAI - UTC` 值。\n\n这是在下一次闰秒公布之前保持有效的偏移。", - "mjd_from_ymd": "把外推格里高利历日期转换为约化儒略日。\n\n结果是给定日期 00:00:00 的 MJD。算法为 Fliegel-Van Flandern 整数转换,对格里高利历有定义的每个日期都是精确的,无需查找表。\n\n计算在 `i64` 中进行并在结束时转换一次,因此对闰秒可能出现的日期范围是精确的。", - "MJD_AT_UNIX_EPOCH": "Unix 历元的约化儒略日,即 1970-01-01T00:00:00。\n\nMJD 本身不携带时间尺度,因此无论该时刻以 UTC、TAI 还是任何其他尺度读取,该常数都是同一个数。", - "tai_minus_utc_at_mjd": "返回给定 UTC 约化儒略日处的 `TAI - UTC` 值,单位为秒。\n\n该偏移是阶跃函数:它在每次闰秒的生效日期 00:00:00 UTC 变化,其间保持恒定。恰好在生效日期上的查询返回新值。\n\n当该时刻早于 1972-01-01 时返回 `None`,此前 UTC 使用非整数偏移,本表不适用。", - "tai_mjd_from_utc_mjd": "把 UTC 约化儒略日转换为对应的 TAI 约化儒略日。\n\n该转换加上该时刻生效的偏移。由于偏移是在 UTC 一侧求值的阶跃函数,该转换对每个 UTC 时刻都有定义;逆转换则不然,因为闰秒使某些 TAI 时刻不对应任何 UTC 时刻。\n\n当该时刻早于 1972-01-01 时返回 `None`。", - "utc_mjd_from_tai_mjd": "把 TAI 约化儒略日转换为对应的 UTC 约化儒略日。\n\n依次尝试表中的每个候选偏移。当所得 UTC 时刻处生效的偏移与产生它的偏移相同时,该候选被接受。对给定输入至多只有一个偏移能满足该条件,因此搜索是明确的。\n\n两种情况返回 `None`。第一种是早于表起点的时刻,此时 UTC 使用本表未描述的分数偏移。第二种是严格落在某次插入闰秒之内的时刻。这样的时刻没有 UTC 表示,因为 UTC 时钟把该区间标记为 `23:59:60`,而该值在连续的约化儒略日尺度上不存在。", - "calendar_mjd": "把 00:00:00 的外推格里高利历日期转换为约化儒略日。\n\n这是用于放置闰秒表项之日历转换的便捷重导出。提供它是为了让调用方无需自行复现该转换,也因为 `validation/` 中的标定脚本需要在比较两侧使用同一函数。", - "Eci": "地心惯性系中的直角坐标位置。\n\n该系与 J2000.0 的平赤道平春分点对齐,并被视为惯性系,因此不随地球旋转。坐标单位为米。", - "Ecef": "地心地固系中的直角坐标位置。\n\n该系随地球旋转,因此固连于地面的点在其中有恒定坐标。坐标单位为米。", - "Eci::new": "由以米表示的矢量创建惯性系位置。", - "Eci::vector": "以米返回该位置。", - "Eci::to_ecef": "在给定地球自转角下,把本惯性系位置转换到地固系。", - "Ecef::to_eci": "在给定地球自转角下,把本地固系位置转换到惯性系。", - "Ecef::to_geodetic": "把本地固系位置转换为给定椭球面上的纬度、经度与高。\n\n当位置位于极轴上时返回 `None`,因为此时经度无定义。", - "Geodetic::new": "由纬度、经度和高度创建一个大地位置。\n\n纬度与经度以弧度为单位,高度以米为单位。", - "Geodetic::latitude": "以弧度返回大地纬度。", - "Geodetic::longitude": "以弧度返回经度,以东为正。", - "Geodetic::height": "以米返回该椭球之上的高度。", - "Geodetic::ellipsoidal": "返回底层的椭球位置。", - "Geodetic::to_ecef": "在 WGS 84 椭球上把这个位置转换为地固系。\n\n这是常见情形。换用其他椭球时请用 [`Geodetic::to_ecef_on`]。", - "Geodetic::to_ecef_on": "在给定椭球上把这个位置转换为地固系。", - "Geodetic": "大地坐标位置:椭球面上的纬度、经度与高。\n\n纬度与经度单位为弧度,高单位为米。纬度是大地纬度,即椭球法线与赤道面之间的夹角,位于 `[-pi/2, pi/2]`。经度自本初子午线向东量取。高是沿椭球法线的距离,椭球面之外为正。", - "Ellipsoid": "双轴参考椭球:绕短半轴的旋转椭圆。\n\n形状由两个数确定。本 crate 存储长半轴与扁率倒数,即标准中 WGS 84 椭球的两个定义参数。其余所有量都是导出的,因此不存在参数之间互相矛盾的风险。\n\n长度单位为米,角度单位为弧度。", - "Ellipsoid::new": "由半长轴和倒数扁率创建一个椭球。\n\n长度以米为单位。倒数扁率必须为正且有限;取无穷大则描述一个球。", - "Ellipsoid::semi_major_axis": "以米返回半长轴。", - "Ellipsoid::inverse_flattening": "返回倒数扁率。", - "Ellipsoid::flattening": "返回扁率。\n\n对球体为零。", - "Ellipsoid::semi_minor_axis": "以米返回半短轴。", - "Ellipsoid::eccentricity_squared": "返回第一偏心率平方,一个落在 `[0, 1)` 的无量纲数。\n\n对扁率 `f` 它等于 `2f - f^2`。", - "Ellipsoid::eccentricity": "返回第一偏心率,一个落在 `[0, 1)` 的无量纲数。", - "Ellipsoid::second_eccentricity_squared": "返回第二偏心率平方,一个无量纲数。\n\n它等于 `e^2 / (1 - e^2)`,出现在把大地位置转换为直角坐标的闭式解中。", - "Ellipsoid::polar_radius_of_curvature": "以米返回极曲率半径。\n\n这是子午圈内在极点处的曲率半径,等于 `a / sqrt(1 - e^2)`。", - "Ellipsoid::prime_vertical_radius": "以米返回给定大地纬度处卯酉圈内的曲率半径。\n\n这是沿椭球法线量得的、从地表到极轴的距离;它从赤道处的半长轴增长到极点处的 [`Ellipsoid::polar_radius_of_curvature`]。\n\n纬度以弧度为单位。", - "Ellipsoid::meridian_radius": "以米返回给定大地纬度处子午圈内的曲率半径。\n\n纬度以弧度为单位。", - "Ellipsoid::geocentric_latitude": "以弧度返回与给定大地纬度对应的地心纬度。\n\n地心纬度自赤道面量到矢径,而大地纬度量到椭球法线。离开赤道与两极时,地心纬度是两者中较小的那个。", - "WGS84": "WGS 84 参考椭球。\n\n两个定义参数是长半轴 `a = 6 378 137 m` 与扁率倒数 `1 / f = 298.257 223 563`。\n\n来源:NIMA TR8350.2,第三版,2000。", - "WGS84_GRAVITATIONAL_PARAMETER": "WGS 84 的地球引力参数,单位为立方米每二次方秒。\n\n它放在这里而不是 `apsis-core`,是为了让使用该椭球的调用方能配上相应的引力场。该值为 `3.986 004 418e14 m^3/s^2`。", - "Geodetic::to_cartesian": "把这个大地位置转换为给定椭球上的地固直角坐标。\n\n该坐标系的第三轴沿穿过北极的椭球短轴,第一轴穿过赤道与本初子午线的交点,第二轴凑成右手系。结果以米为单位。\n\n闭式解为\n\n```text\nN = a / sqrt(1 - e^2 sin^2(lat))\nx = (N + h) cos(lat) cos(lon)\ny = (N + h) cos(lat) sin(lon)\nz = (N (1 - e^2) + h) sin(lat)\n```\n\n其中 `N` 是卯酉圈曲率半径。这个形式处处精确,包括在极点处——那里余弦因子为零。", - "Geodetic::is_valid": "当每个字段都有限、且纬度与经度都在各自范围内时返回 `true`。", - "cartesian_from_geodetic": "把大地坐标位置转换为地固系直角坐标。\n\n坐标系定义与闭式见 [`Geodetic::to_cartesian`]。", - "geodetic_from_cartesian": "把地固系直角坐标转换为大地坐标位置。\n\n纬度与高由 Bowring 方法求得:闭式求解子午线方程,再用若干固定点迭代修正结果。对从椭球面以下到地球静止轨道半径的位置,迭代收敛到远优于一微米。\n\n当位置非有限,或位于极轴上以致经度无定义时返回 `None`。", - "ERA_J2000_TURNS": "J2000.0 历元处的地球自转角,单位为圈。\n\n按该角的定义,历元值为零:国际天文学联合会把地球自转角定义为自 J2000.0 起 UT1 天数的线性函数,因此历元处的常数项属于该定义的另一部分。", - "ERA_RATE_TURNS_PER_DAY": "地球自转角的变化率,单位为圈每 UT1 日。\n\n该值为 `1.002 737 811 911 354 6`,即每天 `360.985 612 288 088` 度。它超过每天一圈,因为地球在一个恒星日内转一圈,而恒星日短于太阳日。", - "ut1_days_from_j2000": "自 J2000.0 历元到给定 UT1 尺度儒略日的天数。\n\nJ2000.0 历元是儒略日 2451545.0,即 2000-01-01 的正午。", - "EarthRotationAngle": "给定时刻的地球自转角。\n\n该角是自天体中间原点绕天极量到地球中间原点的旋转。在实践中它就是惯性系与地固系之间的夹角。\n\n该角不归约到一圈之内,因此即使区间超过一天,两个角之差仍有意义。", - "EarthRotationAngle::from_turns": "由以周为单位的值创建一个角度。", - "EarthRotationAngle::from_radians": "由以弧度为单位的值创建一个角度。", - "EarthRotationAngle::from_ut1_julian_date": "返回 UT1 尺度上某个儒略日对应的角度。", - "EarthRotationAngle::from_ut1_days": "返回自 J2000.0 历元以来若干 UT1 日数对应的角度。\n\n该表达式是线性的:`theta = 0.779 057 273 2640 + 1.002 737 909 350 795 * d`,其中 `d` 是以日计的间隔。常数项 `0.779 057` 周就是历元本身的角度,约为 280.46 度。\n\n来源:IERS Conventions (2010),公式 8.32。", - "EarthRotationAngle::turns": "以周返回该角度。\n\n该值未被化归到 `[0, 1)`。", - "EarthRotationAngle::turns_positive": "以周返回该角度,化归到 `[0, 1)`。", - "EarthRotationAngle::radians": "以弧度返回该角度。\n\n该值未被化归到 `[0, 2 pi)`。", - "EarthRotationAngle::radians_positive": "以弧度返回该角度,化归到 `[0, 2 pi)`。", - "rotation_eci_to_ecef": "返回把惯性系矢量映射到地固系的旋转。\n\n该旋转是绕第三轴按地球自转角顺时针转动。把它作用于惯性系矢量得到地固系矢量,因此固定在惯性空间的矢量会随着地球在其下方转动而获得一个负向旋转。", - "rotation_ecef_to_eci": "返回把地固系矢量映射到惯性系的旋转。\n\n这是 [`rotation_eci_to_ecef`] 的转置,对旋转矩阵而言也就是它的逆。", - "mean_anomaly_moon": "以弧度返回月球的平近点角。\n\n这是 IERS 2003 表达式中的 Delaunay 参数 `l`。", - "mean_anomaly_sun": "以弧度返回太阳的平近点角。\n\n这是 Delaunay 参数 `l'`。参考实现将其内联求值而非通过通用函数,二者在常数项的最后几位不同。", - "mean_anomaly_sun_iers2003": "返回 IERS 2003 表达式给出的太阳平近点角。\n\n它与 [`mean_anomaly_sun`] 在常数项与线性项的最后几位不同。章动级数使用相应的形式。", - "mean_elongation_moon_sun_iers2003": "返回 IERS 2003 表达式给出的月球相对太阳的平距角。\n\n它与 [`mean_elongation_moon_sun`] 在常数项的最后几位不同。", - "mean_latitude_moon": "以弧度返回月球的平纬度辐角。\n\n这是 Delaunay 参数 `F`,即月球的平黄经减去其升交点的平黄经。", - "mean_elongation_moon_sun": "以弧度返回月球相对太阳的平距角。\n\n这是 Delaunay 参数 `D`。参考实现将其内联求值。", - "mean_longitude_node": "以弧度返回月球升交点的平黄经。\n\n这是 Delaunay 参数 `Om`。", - "mean_longitude_mercury": "以弧度返回水星的平黄经。", - "mean_longitude_venus": "以弧度返回金星的平黄经。", - "mean_longitude_earth": "以弧度返回地球的平黄经。", - "mean_longitude_mars": "以弧度返回火星的平黄经。", - "mean_longitude_jupiter": "以弧度返回木星的平黄经。", - "mean_longitude_saturn": "以弧度返回土星的平黄经。", - "mean_longitude_uranus": "以弧度返回天王星的平黄经。", - "mean_longitude_neptune": "以弧度返回海王星的平黄经。\n\n参考实现将其内联求值,取自 MHB2000 模型而非其他行星所用的 IERS 2003 表达式。", - "general_precession": "以弧度返回黄经中的总累积岁差。", - "DelaunayArguments": "五个 Delaunay 参数,按日月级数所用的顺序排列。", - "DelaunayArguments::at": "返回儒略世纪时间对应的五个参数。", - "DelaunayArguments::as_array": "按级数顺序以数组返回各参数。", - "PlanetaryArguments": "行星级数的十三个参数,按级数顺序排列。\n\n顺序为 `l`、`F`、`D`、`Om`、水星至天王星、海王星,以及总累积岁差。", - "PlanetaryArguments::as_array": "按级数顺序以数组返回各参数。", - "SeriesTerm": "日月章动级数的一项。\n\n五个乘数按 `l`、`l'`、`F`、`D`、`Om` 的顺序作用于 Delaunay 参数。六个系数按黄经与交角两部分给出。", - "ComplementaryTerm": "春分点方程补充项的一项。\n\n八个乘数作用于参数 `l`、`l'`、`F`、`D`、`Om`、金星的平黄经、地球的平黄经与总累积岁差。", - "PlanetaryTerm": "行星章动级数的一项。\n\n十三个乘数按 `l`、`F`、`D`、`Om`、水星至天王星、海王星与总累积岁差的顺序作用于行星参数。", - "Nutation": "以弧度表示的黄经章动与交角章动。", - "Nutation::new": "由两个角度创建一个章动。", - "Nutation::longitude_arcseconds": "以角秒返回经度方向的章动。", - "Nutation::obliquity_arcseconds": "以角秒返回黄经交角方向的章动。", - "Nutation::plus": "返回两个章动之和。", - "nutation_2000a": "返回给定时刻的黄经章动与交角章动。\n\n时间是以地球时尺度表示的、自 J2000.0 起的儒略世纪数。这是 IAU 2000A 模型,包含完整的日月与行星级数。", - "nutation_2000a_with_2006_adjustment": "返回施加 IAU 2006 岁差调整后的章动。\n\nIAU 2000A 模型建立在 IAU 1976 岁差之上,而 IAU 2006 岁差改变了岁差量的表示,因此需要一个小调整以保持两个模型相互一致。", - "FRAME_BIAS_RIGHT_ASCENSION": "赤经方向的框架偏差,单位为弧度。\n\n国际天球参考系原点与 J2000.0 动力学春分点之间的偏移。该值为负 0.0146 角秒。", - "FRAME_BIAS_DECLINATION": "赤纬方向的框架偏差,单位为弧度。\n\n国际天球参考系极轴与 J2000.0 平极之间的偏移。该值为负 0.0068192 角秒。", - "FRAME_BIAS_LONGITUDE": "黄经方向的框架偏差,单位为弧度。\n\n沿赤道量取的两个原点之间的偏移。该值为负 0.041775 角秒。", - "PrecessionAngles": "描述自 J2000.0 到某一日期的岁差的四个角。\n\n它们按参考实现所用的顺序施加:先绕第三轴旋转 `gamma_bar`,再绕第一轴旋转 `phi`,再绕第三轴旋转 `psi`,最后绕第一轴旋转 `epsilon`。", - "mean_obliquity": "以弧度返回黄赤交角的平均值。\n\n该多项式是 IAU 2006 表达式,以自 J2000.0 起在地球时尺度上的儒略世纪数求值。", - "precession_angles": "返回给定时刻的四个岁差角。\n\n时间是以地球时尺度表示的、自 J2000.0 起的儒略世纪数。\n\n来源:P03 岁差,按 `eraPfw06` 的实现。", - "frame_bias_matrix": "返回框架偏差旋转矩阵。\n\n该偏差是自国际天球参考系到 J2000.0 动力学平赤道平春分点的一个常数旋转。", - "bias_precession_matrix": "返回到当日平赤道平春分点的旋转。\n\n这是偏差-岁差矩阵。在其上施加章动即可到达当日真赤道真春分点。", - "frame_to_mean_of_date": "在给定已算好的岁差角时,返回到当日平赤道平春分点的旋转。\n\n来源:按 `eraFw2m` 的实现,它施加 P03 岁差。", - "rotation_z": "返回绕第三轴旋转 `angle` 弧度的旋转。\n\n符号约定与本模块中其他轴旋转一致。", - "greenwich_mean_sidereal_time_1982": "以弧度返回 1982 年表达式给出的格林尼治平恒星时。\n\n该表达式是自 J2000.0 起儒略世纪数加上当日小数部分的星历多项式。保留它是为了与采用该形式的已发布历表进行比较。", - "teme_to_terrestrial_matrix": "返回自真赤道平春分点系到地固系的旋转。\n\n参数是 UT1 儒略日,因为该旋转是地球的自转,而地球自转由 UT1 定义。", - "bias_precession_nutation_matrix": "返回给定时刻的偏差-岁差-章动矩阵。\n\n该矩阵把矢量自天球参考系带到当日真赤道真春分点。它由偏差、岁差与章动三部分构成。", - "equation_of_the_equinoxes_complementary_terms": "返回春分点方程的补充项。\n\n春分点方程除了黄经章动在真赤道上的投影之外,还有一组补充项,这部分由该函数给出。", - "equation_of_the_equinoxes": "返回给定时刻的春分点方程。\n\n春分点方程是沿真赤道量取的真春分点与平春分点之差。它就把平恒星时转换为视恒星时的那个量。", - "PolarMotion": "地球的极移,以两个角与相应旋转表示。\n\n极移是自转轴在地球内部的运动。国际地球自转与参考系统服务每日测量并发布它。它小于 0.5 角秒,约合地面 15 米,对大多数用途可忽略;本工作区保留它,以便需要时能够施加。", - "PolarMotion::NONE": "零极移,它是在拿不到公布值时所用的近似。\n\n略去极移带来的误差小于十分之一角秒,远低于章动,但高于现代变换的精度。", - "PolarMotion::new": "由三个角度创建一个极移。", - "PolarMotion::is_valid": "当这些角度可用时返回 `true`。", - "PolarMotion::matrix": "返回该极移的旋转矩阵。\n\n该旋转先绕第三轴按定位量转,再绕第二轴按 `x` 位移的相反数转,最后绕第一轴按 `y` 位移的相反数转。所用的符号与参考实现一致,它们把地球参考系对齐到天极。\n\n来源:按 `eraPom00` 的实现方式,它把每次旋转依次复合到累加器的左侧。", - "intermediate_to_terrestrial_matrix": "返回给定时刻的中间系到地固系矩阵。\n\n中间系是当日真赤道真春分点,地固系是该时刻的地固系。两者之差是地球自转与极移。", - "greenwich_apparent_sidereal_time": "返回给定 UT1 与 TT 儒略日的格林尼治视恒星时。\n\n它是平恒星时加上春分点方程,因此是真春分点的时角。", - "greenwich_mean_sidereal_time": "返回给定 UT1 与 TT 儒略日的格林尼治平恒星时。\n\n该关系是地球自转角加上赤经中的累积岁差,后者即平春分点相对天体中间原点的旋转量。", - "earth_rotation_angle": "返回给定 UT1 儒略日的地球自转角。\n\n这是国际天文学联合会的线性表达式,在该角已知的精度内是精确的。", - "celestial_to_terrestrial_matrix": "返回给定时刻的天球到地固矩阵。\n\n它组合了整条链条:框架偏差、岁差、章动、地球自转与极移。", - "line_checksum": "返回一行前 68 列的校验和。\n\n输入可以包含末尾的校验位,它会被忽略,因为只有前 68 列参与求和。短于 68 个字符的行只贡献它拥有的列。\n\n负号贡献一,十进制数字贡献其数值,其他字符贡献零。", - "verify_line_checksum": "校验一行的最后一个字符是其校验和。\n\n该行必须至少 69 个字符长,因为校验位占据第 69 列。期望值与同一行的 [`line_checksum`] 比较。\n\n`line_number` 是所返回错误中使用的一基行号,使解析多行输入的调用方能报告是哪一行失败。", - "Classification": "元素集的密级。", - "Classification::as_char": "返回元素集中使用的字符。", - "TwoLineElement": "已解析的两行元素集。\n\n各字段是元素集中原样书写的平均元素与标识数据。单位即格式所用的单位,在各字段的文档中说明。将它们转换为状态矢量是 `apsis-sgp4` 的关注点。", - "expand_two_digit_year": "按 `57` 至 `99` 属于二十世纪、`00` 至 `56` 属于二十一世纪的约定展开两位年。\n\n分界点位于卫星编目的起点之前,因此没有任何编目条目被放在与其真实日期相差一个世纪的位置。", - "parse": "从一行、两行或三行解析两行元素集。\n\n行结束符可以是 `\\n`、`\\r\\n` 或二者的混合;空行与全为空白字符的行被忽略。不以 `1 ` 开头的首行被视为卫星名称,这正是可选三行形式的写法。\n\n每行都会检查第 1 列的预期行号、行长度以及校验和。两行的元素集编号必须一致。\n\n返回描述首个失败及发生行号的错误。", - "ElementErrorKind": "解析失败的类别。", - "ElementError": "解析两行元素集时的失败。\n\n错误记录是哪一行失败,并在适用时记录当时正在读取的列,使调用方能指向有问题的文本。行号从 1 开始,列范围是格式定义中从 1 开始的闭区间。", - "ElementError::new": "由给定的类别、行号与列创建错误。\n\n行号从 1 开始。列范围是当时正在读取的从 1 开始的闭区间,若该失败与某个字段相关。", - "ElementError::with_detail": "附上有问题文本的描述。", - "ElementError::kind": "返回本失败的类别。", - "ElementError::line": "返回失败所在行的一基行号。", - "ElementError::columns": "返回失败的从 1 开始的闭列范围,若有。", - "ElementError::detail": "返回附在本错误上的详情文本,若有。", - "PropagationErrorKind": "传播失败的类别。", - "PropagationError": "初始化或传播卫星时的失败。", - "PropagationError::new": "以给定类别创建一个错误。", - "PropagationError::with_detail": "附上对导致失败的值的描述。", - "PropagationError::kind": "返回这次失败的类别。", - "PropagationError::detail": "返回附在这个错误上的细节文本,若有。", - "Elements": "以传播器所用单位表示的卫星元素。\n\n角度单位为弧度,平均运动单位为弧度每分钟。辅助函数把 [`TwoLineElement`] 转换为这种形式。", - "Elements::from_tle": "把已解析的元素集转换为传播器所用的单位。\n\n历元取为元素集中原样书写的 UTC 约化儒略日。元素集历元是一个 UTC 日历时刻,但传播器把它当作均匀的时间原点,这正是理论拟合时所用的约定。先把它转换为均匀尺度会因闰秒偏移而使历元移动约 33 秒,而该移动会传入深空分支的日月级数。", - "Elements::unkozai_mean_motion": "施加 Kozai 到 Brouwer 平均运动的改正。\n\n元素集中的平均运动是 Kozai 平均运动,理论在使用前必须把它转换为 Brouwer 平均运动。这里以求值所用的形式写作不动点迭代,因为 `a` 通过开普勒第三定律依赖于 `n0`。对元素集中出现的每一条轨道,迭代都从改正前的平均运动出发在若干步内收敛。\n\n来源:Vallado 等,*Revisiting Spacetrack Report #3*,AIAA 2006-6753,2006。", - "Elements::validate": "在初始化之前校验平均元素。\n\n当轨道无法传播时返回错误,例如偏心率不描述闭合轨道,或平均运动不为正。", - "Elements::is_deep_space": "当元素描述深空轨道时返回 `true`。\n\n判断依据是轨道周期,而周期由平均运动给出。", - "GravityModel": "引力模型:传播器使用的地球常数。", - "GravityModel::WGS72": "WGS 72 引力模型。\n\n这是 SGP4 理论所拟合的模型,也是与标准元素集匹配的模型。", - "GravityModel::WGS84": "WGS 84 引力模型。\n\n仅在元素集是针对 WGS 84 拟合时使用。", - "GravityModel::reciprocal_sqrt_mu": "返回引力参数平方根的倒数,单位为秒每千米的分之三次方。", - "GravityModel::sqrt_mu": "返回引力参数的平方根,单位为千米的分之三次方每秒。", - "GravityModel::xke": "返回 `xke`,即开普勒常数,单位为 `er^1.5 / min`。\n\n传播器以分钟和地球半径为单位工作,因此引力参数按\n\n```text\nxke = 60 / sqrt(radius^3 / mu)\n```\n\n换算,它是一个地球半径半径的圆轨道每分钟的角速率,单位为弧度每分钟。", - "GravityModel::tumin": "返回 `xke` 的倒数,单位为分钟。\n\n这是模型的时间单位:参考圆轨道的周期除以二 pi。", - "GravityModel::j3_over_j2": "返回 `j3 / j2`,它在理论中以单一比值出现。", - "GravityModel::x2o3": "返回 `2 * xke^2`,它是平均运动修正中出现的一项。", - "Satellite": "已准备好传播的卫星。\n\n用 [`Satellite::from_element`] 构造,然后对每个关注的时刻调用 [`Satellite::propagate_minutes`] 或 [`Satellite::propagate_at`]。初始化工作——包括平均运动改正与每个只依赖元素的系数——在构造时完成一次。", - "Satellite::from_element": "由解析出的元素集初始化一颗卫星。\n\n当平均根数不描述模型能够传播的轨道时返回 `None`,例如偏心率大于或等于一。需要知道具体原因的调用者请用 [`Satellite::try_from_element`]。", - "Satellite::try_from_element": "由解析出的元素集初始化一颗卫星。\n\n当这些根数被拒绝时,返回一个说明是哪个根数无法传播的错误。", - "Satellite::try_from_elements": "由已经处于模型单位下的平均根数初始化一颗卫星。", - "Satellite::epoch_mjd": "以 TAI 尺度返回根数的历元所对应的简化儒略日。", - "Satellite::is_deep_space": "当深空分支适用时返回 `true`。", - "Satellite::minutes_since_epoch": "返回从元素集历元到给定时刻的分钟数。\n\n该时刻以 TAI 尺度的简化儒略日给出,与 [`Satellite::epoch_mjd`] 是同一尺度。", - "Satellite::propagate_at": "返回卫星在给定时刻的状态。\n\n该时刻是 TAI 尺度的简化儒略日。", - "Satellite::propagate_minutes": "返回卫星在自元素集历元起若干分钟后的状态。\n\n负值表示向前传播。结果位于真赤道平赤道坐标系,位置以千米为单位,速度以千米每秒为单位。\n\n当传播后的半径低于一个地球半径时返回 [`PropagationErrorKind::Decayed`](crate::PropagationErrorKind::Decayed),模型把这视为卫星已经再入。", - "Satellite::xke": "返回模型的开普勒常数,单位为每地球半径之分之三次方每分钟弧度。", - "State": "卫星在真赤道平春分点坐标系中的位置与速度。\n\n位置单位为千米,速度单位为千米每秒。该系是模型定义的坐标系:第一轴指向平春分点,第三轴沿真赤道极轴,第二轴构成右手系。", - "State::altitude_radius": "以千米返回距地心的距离。", - "State::speed": "以千米每秒返回相对地心的速率。", - "wrap_to_pi": "把角度归约到 `[-pi, pi)`。", - "true_from_eccentric": "返回给定偏近点角与偏心率对应的真近点角。\n\n该关系来自辅助圆的几何:\n\n```text\ntan(nu / 2) = sqrt((1 + e) / (1 - e)) tan(E / 2)\n```\n\n采用半角形式而不是直接的余弦关系,是为了在四个象限中都正确而无需象限判断。", - "eccentric_from_true": "返回给定真近点角与偏心率对应的偏近点角。\n\n这是 [`true_from_eccentric`] 的逆。", - "eccentric_from_mean": "返回给定平近点角与偏心率对应的偏近点角。\n\n用牛顿法求解开普勒方程\n\n```text\nM = E - e sin E\n```\n\n初值取平近点角加上一个与偏心率成正比的项,使迭代在偏心率趋近 1 时仍稳定。迭代总会终止,结果是至多固定步数后达到的值。", - "mean_from_eccentric": "返回给定偏近点角与偏心率对应的平近点角。\n\n这是直接求值的开普勒方程,`M = E - e sin E`。", - "mean_from_true": "返回给定真近点角与偏心率对应的平近点角。\n\n先把真近点角转换为偏近点角,平近点角再由开普勒方程给出。经由偏近点角可避免为扫过的面积另写闭式,使该关系只由一个表达式负责。", - "true_from_mean": "返回给定平近点角与偏心率对应的真近点角。", - "KeplerianElements": "轨道的六个开普勒元素。\n\n形状与大小由半长轴与偏心率给出,朝向由倾角、升交点赤经与近地点幅角给出,沿轨道的位置由真近点角给出。\n\n角度单位为弧度,半长轴单位为千米。轨道是椭圆,因此偏心率必须小于一。", - "KeplerianElements::State": "卫星在元素所参考惯性系中的位置与速度。\n\n位置单位为千米,速度单位为千米每秒。该系的第一轴指向参考方向,第三轴沿轨道法线,第二轴构成右手系。", - "KeplerianElements::is_valid": "当元素描述椭圆轨道时返回 `true`。", - "KeplerianElements::semi_latus_rectum": "以千米返回半通径。\n\n它等于 `a (1 - e^2)`,即垂直于长轴量得的从焦点到轨道的距离。", - "KeplerianElements::semi_minor_axis": "以千米返回半短轴。", - "KeplerianElements::perigee_radius": "以千米返回近地点半径。\n\n这是对焦点的最近距离 `a (1 - e)`。", - "KeplerianElements::apogee_radius": "以千米返回远地点半径。\n\n这是离焦点最远的点 `a (1 + e)`。", - "KeplerianElements::period": "以秒返回轨道周期。\n\n这是开普勒第三定律,`T = 2 pi sqrt(a^3 / mu)`。", - "KeplerianElements::eccentric_anomaly": "返回与真近点角对应的偏近点角。", - "KeplerianElements::mean_anomaly": "返回与真近点角对应的平近点角。", - "KeplerianElements::to_state": "把这些根数转换为位置与速度。\n\n当这些根数不是有效的椭圆轨道时返回 `None`。\n\n该转换把卫星放在近焦点坐标系中,其中第一轴指向近地点,第三轴沿轨道法线,\n\n```text\nr = p / (1 + e cos nu)\nx = r cos nu, y = r sin nu, z = 0\nvx = -sqrt(mu / p) sin nu\nvy = sqrt(mu / p) (e + cos nu)\n```\n\n然后依次按近地点幅角、倾角和升交点赤经旋转。", - "KeplerianElements::from_state": "返回给定位置与速度对应的根数。\n\n当状态量非有限、半径为零、或轨道不是椭圆时返回 `None`。该转换假定轨道是束缚的,因此抛物线或双曲线状态会被拒绝,而不是返回一个偏心率大于或等于一的结果。", - "KeplerianElements::with_true_anomaly": "返回这些根数的一份副本,其中真近点角不同。", - "KeplerianElements::with_mean_anomaly": "返回处于给定平近点角的这些根数副本。\n\n真近点角由平近点角经开普勒方程解出。", - "KeplerianElements::to_mean_anomaly": "返回平近点角,它是沿轨道的线性时间量度。\n\n这与 [`KeplerianElements::mean_anomaly`] 是同一个值,此处命名是为了与 [`KeplerianElements::with_mean_anomaly`] 对称。", - "KeplerianElements::perifocal_to_inertial": "返回从近焦点坐标系到惯性系的旋转。\n\n该旋转是三个基本旋转之积:先绕第三轴转近地点幅角,再绕第一轴转倾角,最后绕第三轴转升交点赤经。", - "GravityParameter": "中心天体的引力参数,单位为立方千米每二次方秒。\n\n提供两个值是因为工作区同时支持两种引力模型,正确的选择是与轨道元素来源相匹配的那个。对由两行元素集导出的元素,应使用 SGP4 理论所拟合的 WGS 72 值。", - "GravityParameter::WGS72": "WGS 72 引力参数,`398 600.8 km^3/s^2`。", - "GravityParameter::WGS84": "WGS 84 引力参数,`398 600.5 km^3/s^2`。", - "GravityParameter::value": "以立方千米每二次方秒返回该值。", - "GravityParameter::sqrt": "返回参数的平方根,单位为千米的二分之三次方每秒。", - "Orbit::from_element": "由已解析的元素集与引力模型准备轨道。\n\n引力模型必须与元素集拟合时所用的一致,对已编目卫星而言即 [`GravityModel::WGS72`]。当元素不可传播时返回 `None`。", - "Orbit::try_from_element": "由已解析的元素集与引力模型准备轨道。\n\n返回描述哪个元素不可传播的错误。", - "Orbit::from_elements": "由已是模型单位的平均元素准备轨道。", - "Orbit::satellite": "返回底层传播器。", - "Orbit::gravity_parameter": "返回与引力模型配对的引力参数。\n\n该值用于密切元素转换,使转换与传播保持一致。", - "Orbit::epoch_mjd": "以 UTC 尺度上的约化儒略日返回元素的历元。\n\n历元是元素集中书写的值,也是传播器据以计量时间的原点。", - "Orbit::is_deep_space": "当传播器使用深空分支时返回 `true`。", - "Orbit::propagate_minutes": "返回距元素历元若干分钟处的状态。\n\n位置单位为千米、速度单位为千米每秒,位于真赤道平春分点坐标系。", - "Orbit::propagate_at": "返回 UTC 尺度上给定约化儒略日处的状态。", - "Orbit::osculating_elements": "返回距元素历元若干分钟处的密切开普勒元素。\n\n密切元素描述与那一时刻的位置和速度相符的二体轨道。它们不是元素集中的平均元素,且沿轨道变化,因为 SGP4 包含二体模型没有的摄动。\n\n当状态无法传播,或瞬时轨道不是椭圆时返回错误;后者在传播把状态推到逃逸轨迹上时可能发生。", - "Orbit::radius_at": "返回状态矢量的模。\n\n这是只想得到距地心距离这一常见情形的便捷方法。", - "Orbit::position_at": "返回距元素历元若干分钟处的位置。", - "Orbit::sample_ephemeris": "在分钟区间内把轨道采样为星历。\n\n传播是轨迹中开销较大的部分,因此建表一次、查询多次值得占用这些内存。采样时间是从元素历元起的分钟数,与本类型其余部分一致;而返回的星历保留秒,与 `apsis-ephemeris` 一致。\n\n表由传播器给出的位置与速度构建,因此 Hermite 插值在端点具有精确斜率,采样之间的误差极小;除非来源没有速度,否则无需使用只给位置的路径。\n\n当网格不可用,或某个采样无法传播时返回错误;后者在元素已衰减或区间超出模型定义范围时发生。", - "Orbit::apply_burn": "施加有限推力机动并返回所得的密切元素。\n\n机动施加在给定时刻的密切状态上,因此它描述的是相对卫星实际所在轨道而非平均元素的机动。所得元素是机动后状态的密切元素,这正是后续传播所需要的。由于底层传播器是为一个元素集构建的,这是规划结果而不是新的传播器;想要传播新轨道的调用方应从返回的元素重新构造一个 `Orbit`。", - "EphemerisSamplingError": "把轨道采样为星历时的失败。", - "ManeuverApplicationError": "对轨道施加机动时的失败。", - "Degrees::new": "由弧度值创建角度。", - "Degrees::value": "以弧度返回本角度。", - "Degrees::to_degrees": "把本角度转换为度。", - "Degrees::wrap": "返回归约到半开区间 `[-pi, pi)` 的本角度。\n\n归约使用 [`f64::rem_euclid`](f64::rem_euclid),因此结果对负输入与大输入都有定义。", - "Kilometers::to_kilometers": "把本长度转换为千米。", - "Sample": "一个采样状态。\n\n采样是某一时刻的位置与速度,这是工作区中每个传播器产生的形式,也是插值所需的形式。", - "Sample::new": "创建采样。", - "Sample::is_valid": "当时间、位置与速度都是有限值时返回 `true`。", - "Sample::radius": "以千米返回距原点的距离。", - "Sample::speed": "以千米每秒返回速率。", - "hermite_position": "对单个分量求值三次 Hermite 多项式。\n\n`p0` 与 `p1` 是端点值,`v0` 与 `v1` 是端点对时间的导数,`dt` 是区间长度,`s` 是 `[0, 1]` 内的区间比例。\n\n四项是标准的三次 Hermite 基:第一、三项承载端点值,第二、四项承载斜率。导数项乘以 `dt`,因为斜率是每单位时间的量,而 `s` 是每单位区间的量。", - "hermite_velocity": "对单个分量求 Hermite 三次多项式的导数。\n\n这是与 [`hermite_position`] 相配的速度。它是该三次多项式的精确导数,因此重建的状态内部自洽:对位置求导的调用方会得到这个速度。", - "hermite_state": "同时求值 Hermite 位置与速度。", - "Ephemeris": "通过插值回答查询的采样状态表。\n\n表至少包含两个采样,且时间严格递增。在采样时刻的查询精确返回该采样;两个采样之间的查询返回通过四个端点值的 Hermite 三次多项式。超出覆盖区间的查询被拒绝而不外推,因为插值在那里没有信息,而外推的轨道可能迅速发散。", - "Ephemeris::from_samples": "由按时间递增的采样构建表。\n\n当采样少于两个、某个采样非有限,或时间不严格递增时返回错误。", - "Ephemeris::samples": "按时间顺序返回采样。", - "Ephemeris::len": "返回采样个数。", - "Ephemeris::is_empty": "当表为空时返回 `true`。\n\n通过 [`Ephemeris::from_samples`] 构建的表永远不会为空,因此对已存在的表该值恒为 `false`;提供它是为了让接受切片的泛型代码能使用常见的方法对。", - "Ephemeris::start_time": "以秒返回第一个采样时间。", - "Ephemeris::end_time": "以秒返回最后一个采样时间。", - "Ephemeris::duration": "以秒返回表覆盖的时间长度。", - "Ephemeris::covers": "当给定时间落在覆盖区间内时返回 `true`。\n\n区间是闭区间,因此两个端点都被覆盖。", - "Ephemeris::uniform_step": "当采样均匀分布时返回标称步长。\n\n当间距变化时返回 `None`,调用方在按算术下标访问采样前会检查这一点。", - "Ephemeris::is_uniform": "当采样均匀分布时返回 `true`。", - "Ephemeris::state_at": "返回给定时间的状态,在采样之间插值。\n\n时间恰为采样时间时返回该采样。当时间超出覆盖区间时返回 `None`。", - "Ephemeris::clamped_state_at": "返回给定时间的状态;当时间超出覆盖区间时返回最近的端点状态。\n\n这适用于必须为任意时间给出数值、并宁愿保持最后已知状态而不愿失败的调用方。", - "Ephemeris::nearest_sample": "返回距给定时间最近的采样,不作插值。", - "Ephemeris::times": "按顺序返回每个采样的时间。", - "Ephemeris::all_samples": "当每个状态都满足给定谓词时返回 `true`。\n\n这是为想一次性检查整表某个性质(例如每个半径都在地表之上)的调用方提供的便捷方法。", - "EphemerisErrorKind": "星历错误的类别。", - "EphemerisError": "构建或查询星历时的失败。", - "EphemerisError::new": "由给定类别创建错误。", - "EphemerisError::with_detail": "附上导致失败的数值的描述。", - "EphemerisError::kind": "返回本失败的类别。", - "EphemerisError::detail": "返回附在本错误上的详情文本,若有。", - "Sampler": "用于求值状态函数的均匀时间网格。\n\n采样器描述何时采样以及多密。网格由起始时间、结束时间与步长定义,采样个数由它们推出:采样放在起点以及直到终点的每个步长处,终点无论是否落在网格上都被包含。包含终点很重要,因为要求覆盖到某一时间的调用方期望表能够到达那里。", - "Sampler::new": "创建带步长的区间采样器。", - "Sampler::sample_count": "返回网格产生的采样个数。\n\n这是区间内能容纳的完整步数,加上起点一个,以及当终点不在网格上时终点一个。当采样器不可用时返回 `None`。", - "Sampler::times": "按顺序返回网格的各时间。\n\n当采样器不可用时返回 `None`。当终点不在网格上时最后一个时间是终点,否则是最后一个网格点。", - "Sampler::sample": "通过在网格上求值状态函数填充星历。\n\n该函数把以秒计的时间映射为位置与速度。在本工作区中它通常是 `apsis_sgp4::Satellite::propagate_at` 上的闭包,但任何函数都可以,因此采样器可以用解析轨道测试。\n\n当采样器不可用,或函数产生非有限状态时返回错误。", - "Sampler::sample_positions": "由只返回位置的函数填充星历。\n\n速度由相邻位置的中心差分重建。这适用于只有位置的来源,例如表格文件;在有速度可用时应使用它,因此请优先使用 [`Sampler::sample`]。", - "FiniteBurn": "在一段时长内施加恒定加速度的机动。\n\n有限推力机动是发动机开机时间长到不可忽略时的模型:电推进发动机的推力段可能占轨道的很大一部分,或者航天器在推力期间移动明显的任何点火。\n\n加速度在调用方选定的惯性系中保持恒定。这是本模型所作的简化:真实发动机沿随航天器转动以保持某一方向的体轴点火,对其建模需要姿态历史,不属于本 crate 的范围。", - "FiniteBurn::new": "由加速度与时长创建机动。\n\n当某个值非有限、时长不为正,或加速度为零时返回错误。", - "FiniteBurn::along": "创建沿某方向的给定加速度大小的机动。\n\n方向会被归一化,因此无论方向矢量多长,加速度大小都是固定的。", - "FiniteBurn::acceleration": "以千米每二次方秒返回加速度矢量。", - "FiniteBurn::acceleration_magnitude": "以千米每二次方秒返回加速度的大小。", - "FiniteBurn::duration": "以秒返回时长。", - "FiniteBurn::nominal_delta_v": "以千米每秒返回该加速度在自由空间会给出的速度增量。\n\n这是加速度与时长之积,即点火期间无重力作用时会提供速度增量。在重力作用下,短点火实际达到的增量接近该值,长点火则偏离,而这一差异正是有限推力模型存在的意义。", - "FiniteBurn::equivalent_impulse": "返回与标称增量等效的脉冲。\n\n这是有限推力机动在时长趋于零时所逼近的脉冲机动,也是 [`FiniteBurn::apply`] 在恒定重力场极限下返回的结果。", - "FiniteBurn::apply": "在二体引力下对状态施加机动。\n\n在时长内把位置与速度向前积分,在重力之外加上恒定加速度。恒定加速度加质点引力没有闭式解,因此积分采用定步长四阶 Runge-Kutta,对一次点火所占的短弧是精确的。\n\n所达到的速度增量是与同长度的滑行比较,而不是与初速度比较。这一区别很重要:即使没有推力,重力在点火期间也会改变速度,因此末速度与初速度之差包含速度矢量的自然转动。与滑行比较可分离出推力的贡献,也就是点火所买到的东西;这也使结果在时长趋于零时收敛到标称增量,而与初速度之差不会如此。\n\n返回点火结束时的状态与所达到的速度增量。", - "FiniteBurn::apply_to_elements": "对一组开普勒元素施加机动。\n\n元素先转换为状态,施加机动,再转换回来。当元素无效、积分失败,或所得状态不再是椭圆轨道(在机动大到足以逃逸时发生)时返回错误。", - "BurnOutcome": "有限推力机动产生的结果。\n\n结果保留末状态、机动所达到的增量,以及加速度在自由空间会给出的增量,使调用方能看出重力在点火期间消耗或增加了多少。", - "BurnOutcome::achieved_magnitude": "返回所达到速度增量的大小。", - "BurnOutcome::gravity_loss": "以千米每秒返回所达到增量与标称增量之差。\n\n负值意味着重力在点火期间把速度转得与推力相反,因此该点火买到的比其自由空间速度增量少。", - "BurnOutcome::elements": "返回点火结束时的元素。\n\n当末状态不是椭圆轨道(在机动大到足以逃逸时发生)时返回 `None`。", - "ManeuverErrorKind": "机动错误的类别。", - "ManeuverError": "构建或施加机动时的失败。", - "ManeuverError::new": "由给定类别创建错误。", - "ManeuverError::with_detail": "附上导致失败的数值的描述。", - "ManeuverError::kind": "返回本失败的类别。", - "ManeuverError::detail": "返回附在本错误上的详情文本,若有。", - "HohmannTransfer": "两个共面圆轨道之间的两次脉冲转移。\n\n该转移即经典的霍曼机动:先点火进入一个椭圆,其近地点与较低轨道相切、远地点与较高轨道相切;滑行该椭圆周期的一半;再第二次点火进入目标圆轨道。在脉冲模型中,这是两个共面圆轨道之间最省燃料的两次脉冲转移,因此也是衡量其他一切转移的基准。\n\n两次点火沿运动方向带符号:正值抬升轨道,负值降低轨道,因此同一类型同时覆盖爬升与下降。总量是二者大小之和,即推进剂预算所用的量。", - "HohmannTransfer::total_delta_v": "以千米每秒返回总速度增量。\n\n这是两次点火大小之和,即该转移无论方向如何所付出的代价。", - "HohmannTransfer::is_raising": "当转移抬升轨道时返回 `true`。", - "HohmannTransfer::first_burn_magnitude": "返回第一次点火的速度增量,不带符号。", - "HohmannTransfer::second_burn_magnitude": "返回第二次点火的速度增量,不带符号。", - "HohmannTransfer::mean_radius": "以千米返回转移所对称的平均半径。\n\n这是椭圆的半长轴,即两个轨道半径的算术平均。", - "hohmann_transfer": "返回两个圆轨道之间的两次脉冲转移。\n\n半径单位为千米,必须有限且为正。二者可以以任意顺序给出:第二个半径较大是爬升,较小是下降。\n\n当某个半径不可用,或二者相等时返回错误,因为轨道与其自身之间的转移不是机动,几乎总是计划中的错误。\n\n两次点火由活力公式给出。在第一个半径处,圆轨道速度为 `sqrt(mu / r1)`,转移椭圆上的速度为 `sqrt(mu (2 / r1 - 1 / a))`,其中 `a` 是椭圆的半长轴,因此第一次点火即二者之差。第二次点火是目标处圆轨道速度与该处椭圆上速度之差,取相反的顺序,因为椭圆到达时慢于它必须并入的圆轨道。滑行时间为椭圆周期的一半。", - "Impulse": "速度的瞬时变化。\n\n脉冲是在零时间内施加的速度增量,因此它改变状态的速度而不改变位置。这是相对轨道周期很短的点的模型,也是速度增量预算的写法。\n\n增量以与将被施加的状态同一坐标系中的矢量存储,因此调用方负责在该系中表达它。其大小即速度增量,也就是推进剂预算所计量的量。", - "Impulse::new": "以千米每秒由速度增量矢量创建脉冲。\n\n当某个分量非有限,或矢量为零时返回错误,因为零增量不是机动,几乎总是计划中的错误。", - "Impulse::along": "创建沿某方向的给定大小的脉冲。\n\n方向会被归一化,因此无论方向矢量多长,大小就是速度增量。当大小不为正且有限,或方向为零或非有限时返回错误。", - "Impulse::delta_v": "以千米每秒返回速度增量矢量。", - "Impulse::magnitude": "以千米每秒返回增量的大小。\n\n这就是该点火所付出的速度增量。", - "Impulse::scaled": "返回按给定因子缩放后的增量副本。\n\n因子为零会产生零脉冲,而 [`Impulse::new`] 会拒绝它,因此这里对它返回错误,而不是返回一个无法构造的值。", - "Impulse::reversed": "返回反向的增量副本,即撤销该点火。", - "Impulse::apply_to_velocity": "对速度施加脉冲,返回新的速度。", - "Orbit": "以平均元素持有的、已准备好传播的卫星轨道。\n\n本类型把已准备好的 SGP4 卫星与其初始化时所用的引力模型配对,因为密切元素转换需要同一个引力参数才能与传播保持一致。", - "Geodetic::vector": "以米返回该位置。", - "Geodetic::to_eci": "在给定地球自转角下,把本地固系位置转换到惯性系。", - "Geodetic::to_geodetic": "把本地固系位置转换为给定椭球面上的纬度、经度与高。\n\n当位置位于极轴上时返回 `None`,因为此时经度无定义。", - "State::is_valid": "当元素描述椭圆轨道时返回 `true`。", - "State::semi_latus_rectum": "以千米返回半通径。\n\n这是 `a (1 - e^2)`,即从焦点到轨道的距离,沿垂直长轴方向量取。", - "State::semi_minor_axis": "以千米返回短半轴。", - "State::perigee_radius": "以千米返回近地点半径。\n\n这是距焦点的最近距离,`a (1 - e)`。", - "State::apogee_radius": "以千米返回远地点半径。\n\n这是距焦点的最远点,`a (1 + e)`。", - "State::period": "以秒返回轨道周期。\n\n这是开普勒第三定律,`T = 2 pi sqrt(a^3 / mu)`。", - "State::eccentric_anomaly": "返回与真近点角对应的偏近点角。", - "State::mean_anomaly": "返回与真近点角对应的平近点角。", - "State::to_state": "把本元素转换为位置与速度。\n\n当元素不构成有效椭圆轨道时返回 `None`。\n\n转换先把卫星置于近焦点坐标系,其第一轴指向近地点、第三轴沿轨道法线,\n\n```text\nr = p / (1 + e cos nu)\nx = r cos nu, y = r sin nu, z = 0\nvx = -sqrt(mu / p) sin nu\nvy = sqrt(mu / p) (e + cos nu)\n```\n\n再按近地点幅角、倾角与升交点赤经依次旋转。", - "State::from_state": "返回给定位置与速度对应的元素。\n\n当状态非有限、半径为零,或轨道不是椭圆时返回 `None`。该转换假定轨道是束缚的,因此抛物线与双曲线状态会被拒绝,而不是以大于等于一的偏心率返回。", - "State::with_true_anomaly": "返回具有不同真近点角的本元素副本。", - "State::with_mean_anomaly": "返回处于给定平近点角的本元素副本。\n\n真近点角由平近点角经开普勒方程恢复。", - "State::to_mean_anomaly": "返回平近点角,即沿轨道的时间线性度量。\n\n该值与 [`KeplerianElements::mean_anomaly`] 相同,命名是为了与 [`KeplerianElements::with_mean_anomaly`] 对称。", - "State::perifocal_to_inertial": "返回自近焦点系到惯性系的旋转。\n\n该旋转是三次初等旋转之积:先绕第三轴旋转近地点幅角,再绕第一轴旋转倾角,最后绕第三轴旋转升交点赤经。", - "apsis-core::new": "以给定的类别创建一个不带消息的错误。", - "apsis-core::with_message": "以给定的类别和消息创建一个错误。\n\n消息应当描述这一次失败的具体原因。它是给读日志的人看的,不是用来做匹配的。", - "apsis-core::with_source": "以给定的类别和一个来源错误创建一个错误。", - "apsis-core::with_context": "为这个错误附上一条消息,替换已有的消息。", - "apsis-core::caused_by": "附上一个来源错误,替换已有的来源。", - "apsis-core::kind": "返回这个错误的类别。", - "apsis-core::message": "返回附在这个错误上的消息,若有。", - "apsis-units::new": "以弧度值创建一个角度。", - "apsis-units::value": "以弧度返回这个角度。", - "apsis-units::to_degrees": "把这个角度转换为度。", - "apsis-units::wrap": "返回折叠到半开区间 `[-pi, pi)` 的这个角度。\n\n折叠使用 [`f64::rem_euclid`](f64::rem_euclid),因此对负值与很大的值结果都有定义。", - "apsis-units::to_radians": "把这个角度转换为弧度。", - "apsis-units::period": "返回与这个频率对应的周期,单位为秒。\n\n频率为零时返回 `f64::INFINITY`,这正是周期无穷大的正确极限。", - "apsis-units::to_kilometers": "把这个长度转换为千米。", - "apsis-units::to_meters": "把这个长度转换为米。", - "apsis-units::from_celsius": "以摄氏温度值创建一个温度。\n\n使用摄氏温标定义的精确偏移量。", - "apsis-units::to_celsius": "把这个温度转换为摄氏度。", - "apsis-leap::effective_mjd": "返回生效时刻的简化儒略日。\n\n闰秒在 UTC 的 00:00:00 生效,因此生效时刻就是给定日历日的起点。", - "apsis-tle::as_char": "返回元素集中所用的字符。", - "apsis-tle::new": "以给定的类别、行号和列区间创建一个错误。\n\n行号从 1 开始。列区间是从 1 开始、含两端的区间,指明读取失败时所在的字段。", - "apsis-tle::with_detail": "附上对出错文本的描述。", - "apsis-tle::kind": "返回这次失败的类别。", - "apsis-tle::line": "返回失败所在行的一基行号。", - "apsis-tle::columns": "返回失败的一基含两端列区间,若有。", - "apsis-tle::detail": "返回附在这个错误上的细节文本,若有。", - "apsis-tle::Result": "一个 [`Result`](core::result::Result),其错误类型为 [`ElementError`]。", - "apsis-time::new": "由各字段创建一个日历日期。", - "apsis-time::from_ymd": "在给定日期的起点创建一个日历日期。", - "apsis-time::seconds_of_day": "返回自当日零点起经过的秒数。", - "apsis-time::is_valid": "当各字段构成一个在前推格里高利历中存在的日期时返回 `true`。", - "apsis-time::is_leap_year": "当 `year` 在前推格里高利历中是闰年时返回 `true`。", - "apsis-time::days_in_month": "返回给定年份中给定月份的天数。\n\n`month` 落在 `1..=12` 之外时返回零。", - "apsis-time::ymd_from_mjd": "把简化儒略日转换为日历日期。\n\n小数部分被忽略,因此结果是包含该时刻的那一日历日。", - "apsis-time::seconds_of_day_from_mjd": "只保留 MJD 的小数部分,作为自午夜起的秒数。\n\n对任何有限输入,结果都落在 `[0, 86400)` 内。", - "apsis-time::J2000": "时刻 2000-01-01T12:00:00 TT,它定义了 J2000.0 历元。\n\n该值在 TT 尺度上是 `2451545.0`,再回退到 TAI。", - "apsis-time::from_tai_mjd": "由 TAI 尺度上的简化儒略日创建一个历元。", - "apsis-time::from_mjd": "由给定尺度上的简化儒略日创建一个历元。\n\n当该时刻无法在所请求的尺度上表示时返回 `None`。1972 年之前的 UTC 即属此情形,用于从 UTC 转换的闰秒表不适用于那段范围。", - "apsis-time::from_calendar": "由日历日期创建一个历元,并按给定尺度解释它。\n\n当各字段不构成有效日期,或该时刻无法在所请求的尺度上表示时返回 `None`。", - "apsis-time::tai_mjd": "以 TAI 尺度返回该时刻的简化儒略日。", - "apsis-time::mjd": "以给定尺度返回该时刻的简化儒略日。\n\n当该时刻早于 1972 年而请求的是 UTC 尺度时返回 `None`,因为闰秒表不覆盖那段范围。", - "apsis-time::calendar": "以给定尺度返回该时刻的日历日期。\n\n当该时刻的尺度转换不可用时返回 `None`;参见 [`Epoch::mjd`]。", - "apsis-time::seconds_until": "返回从 `self` 到 `other` 的带符号秒数。\n\n`other` 更晚时结果为正。由于两个历元都以均匀的 TAI 尺度存储,这个差是精确的,且与创建任一历元所用的尺度无关。", - "apsis-time::add_seconds": "返回平移了 `seconds` 的历元。\n\n平移量以均匀 TAI 尺度上的 SI 秒计,因此该时刻恰好前进这么多经过时间。这里只提供这一种平移:以 UTC 时钟计量的平移在区间跨越插入的闰秒时会不同,而 UTC 时钟在这种时刻上并没有单一良定义的数值。需要对 UTC 尺度做日历运算的调用者应当转换到 [`Calendar`]、调整后再转换回来。", - "apsis-time::is_finite": "当存储的简化儒略日为有限值时返回 `true`。", - "apsis-time::is_uniform": "当该尺度以均匀速率运行时返回 `true`。\n\n这里的 UTC 是唯一不均匀的尺度,因为闰秒会使它的一些分钟长达 61 秒。", - "apsis-time::name": "返回该尺度的短名,例如 `TAI` 或 `UTC`。", - "Inertial::new": "由以米为单位的向量创建一个惯性系位置。", - "Inertial::vector": "以米返回该位置。", - "Inertial::to_ecef": "在给定的地球自转角处,把这个惯性系位置转换为地固系。", - "Inertial::to_eci": "在给定的地球自转角处,把这个地固系位置转换为惯性系。", - "Inertial::to_geodetic": "把这个地固系位置转换为给定椭球之上的纬度、经度与高度。\n\n当该位置位于极轴上时返回 `None`,因为那里没有定义经度。", - "apsis-geodesy::new": "由纬度、经度和高度创建一个大地位置。", - "apsis-kepler::is_valid": "当这些根数描述一条椭圆轨道时返回 `true`。", - "apsis-kepler::period": "以秒返回轨道周期。\n\n这是开普勒第三定律 `T = 2 pi sqrt(a^3 / mu)`。", - "apsis-kepler::eccentric_anomaly": "返回与真近点角对应的偏近点角。", - "apsis-kepler::mean_anomaly": "返回与真近点角对应的平近点角。", - "apsis-kepler::WGS72": "WGS 72 引力参数 `398 600.8 km^3/s^2`。", - "apsis-kepler::value": "以立方千米每二次方秒返回该值。", - "apsis-kepler::sqrt": "返回该参数的平方根,单位为千米的三分之二次方每秒。", - "Series::at": "返回某个以儒略世纪计的时刻对应的五个参数。", - "apsis-sgp4::propagate_minutes_at": "按顺序把一颗卫星传播到自其历元起的一组分钟处。\n\n这是顺序形式,供对照使用,也用于较小的列表——那种情况下启动线程的代价超过工作本身。", - "apsis-sgp4::propagate_minutes_at_parallel": "跨多个线程把一颗卫星传播到自其历元起的一组分钟处。\n\n结果与 [`propagate_minutes_at`] 逐元素、且以相同顺序一致。模型报告卫星衰减的那一分钟给出 `None`,与顺序形式相同;线程不会在第一个这样的分钟处停下,因为调用者通常需要整条轨迹,会自行判断衰减意味着什么。\n\n传播是确定性的,只取决于这些分钟,因此结果与工作如何切分无关。\n\n收益取决于列表中工作量的大小,因为启动线程有固定开销,短列表无法收回这份开销。以一分钟步长算,一天是一千四百个状态,收益很小;一个月是四万三千个,能快好几倍。有许多短轨道的调用者更适合下面按卫星的形式,它一次接收整个列表。", - "apsis-sgp4::propagate_at": "按顺序把多颗卫星传播到同一时刻。\n\n按卫星的工作是彼此独立的,因此一个星座在某一时刻的状态是天然的批处理。卫星只被读取,因此它们以引用形式在各线程间共享。", - "apsis-sgp4::propagate_at_parallel": "跨多个线程把多颗卫星传播到同一时刻。\n\n结果与 [`propagate_at`] 逐元素、且以相同顺序一致。\n\n这是覆盖性计算想要的形式:它在一次调用中持有整个星座,于是线程为所有卫星只启动一次,而不是每颗卫星一次,即便只有几百颗的星座也能获益。", - "apsis-sgp4::from_tle": "把解析出的元素集转换为传播器所用的单位。\n\n历元取元素集中原样写下的 UTC 简化儒略日。元素集历元是一个 UTC 日历时刻,但传播器把它当作均匀的时间原点,这是该理论拟合时所依据的约定。先把它换算到均匀尺度会把历元平移一个闰秒偏移量,约为 33 秒,而这一平移会传播进深空分支的太阳与月球级数中。", - "apsis-sgp4::unkozai_mean_motion": "施加从 Kozai 平均运动到 Brouwer 平均运动的修正。\n\n元素集中的平均运动是 Kozai 平均运动,理论必须先把它换算成 Brouwer 平均运动。其关系为\n\n```text\nn0 = n / (1 + 1.5 k2 (3 cos^2 i - 1) / (a^2 sqrt(1 - e^2)) / a^2)\n```\n\n此处写成不动点迭代的形式,因为 `a` 通过开普勒第三定律依赖于 `n0`。对元素集中出现的每一条轨道,该迭代从未修正的平均运动出发,几步之内即收敛。\n\n施加从 Kozai 平均运动到 Brouwer 平均运动的修正。\n\n元素集中的平均运动是 Kozai 平均运动,理论必须先把它换算成 Brouwer 平均运动。以 `no` 表示元素集中的值、`xke` 表示开普勒常数,参考实现的求值为\n\n```text\nak = (xke / no)^(2/3)\nd1 = 0.75 j2 (3 cos^2 i - 1) / (sqrt(1 - e^2) (1 - e^2))\ndel = d1 / ak^2\nadel = ak (1 - del^2 - del (1/3 + 134 del^2 / 81))\ndel = d1 / adel^2\nn0 = no / (1 + del)\n```\n\n第二次求值 `del` 用的是 `adel` 而非 `ak`,这正是该修正与修正后平均运动所隐含的半长轴保持一致的原因。\n\n来源:Vallado 等,*Revisiting Spacetrack Report #3*,AIAA 2006-6753,2006。", - "apsis-sgp4::validate": "在初始化之前校验平均根数。\n\n当轨道无法传播时返回错误,例如偏心率不构成闭合轨道,或平均运动不为正。", - "GravityModel::is_deep_space": "当这些根数描述一条深空轨道时返回 `true`。\n\n判据是轨道周期,它由平均运动推出。", - "apsis-sgp4::WGS72": "WGS 72 重力模型。\n\n这是 SGP4 理论拟合所采用的模型,也是与标准元素集相匹配的那个。", - "Satellite::altitude_radius": "返回到地心的距离,单位为千米。", - "Satellite::speed": "返回相对地心的速率,单位为千米每秒。", - "apsis-math::thread_count": "一项工作应当使用的线程数。\n\n这是标准库报告的机器并行度,至少为一。它是……", - "apsis-math::threads_for": "对 `len` 个元素做循环时应当运行的线程数。\n\n比线程数还短的循环会让大多数线程无事可做,而……", - "apsis-math::for_each_index": "对一个范围的每个索引运行一次闭包,分散到多个线程。\n\n该范围被切成每个线程一段连续区间,每个线程……", - "apsis-math::map_index": "收集一个范围中每个索引对应的值,分散到多个线程。\n\n这就是常见情形下的 [`for_each_index`],此时结果……" - } -} diff --git a/docs/design.md b/docs/design.md index a1362bc..3087ab6 100644 --- a/docs/design.md +++ b/docs/design.md @@ -172,12 +172,21 @@ description would drift silently. The script reports the crates it read in `generated_from`, which the service returns in `/api/api`, so the provenance of what is displayed is checkable. -The translation is a separate file, `content/api.zh.json`, keyed by crate -name and by item name. It is separate from the catalog because the catalog -is generated and the translation is authored: regenerating the catalog must -not discard the translation. An associated item is keyed by the type that -declares it, as `Type::name`, because several types have an associated -`new`; the bare name is the fallback. +The translation is not a separate file. `apsis-rs` writes each doc comment in +English and then in Chinese, in the one comment beside the code, and the +extractor splits the two halves as it reads. Both languages therefore come +from the same place as the code they describe, and neither can fall behind +it: a crate that has not been translated shows up as an item with no Chinese +rather than as an item quietly reading as English, and a test asserts that no +such item is left in the committed catalog. + +An earlier revision kept the Chinese in `content/api.zh.json`, keyed by item +name. That is the arrangement this replaced. It was wrong for a reason worth +recording: the key was a name, and a name is not unique. `new` is defined by +fifty-nine crates and they do not all mean the same thing by it, so a +translation filed under the bare name could be shown against the wrong +documentation. Splitting the comment removes the question, because the +translation is attached to the item rather than looked up by a name. A doc comment refers to another item with a Rust path, written as `[Epoch](crate::Epoch)` or as a shortcut `[`Epoch::mjd`]`. The renderer turns diff --git a/docs/design.zh.md b/docs/design.zh.md index 6415ea1..3ce655a 100644 --- a/docs/design.zh.md +++ b/docs/design.zh.md @@ -126,9 +126,15 @@ props 的纯函数,并使图在测试或截图中可复现。 脚本在 `generated_from` 中报告它读过的 crate,服务在 `/api/api` 中返回该字段, 因此所显示内容的来源是可核查的。 -译文是另一个文件 `content/api.zh.json`,以 crate 名与条目名为键。它与目录分开, -是因为目录是生成的而译文是撰写的:重新生成目录不应丢弃译文。关联条目以声明 -它的类型为键,写作 `Type::name`,因为有若干类型都有关联的 `new`;裸名作为回退。 +译文不是一个单独的文件。`apsis-rs` 把每段文档注释先写成英文、再写成中文,都在 +代码旁的那一段注释里,提取器读取时把两半拆开。因此两种语言与它们所描述的代码 +出自同一处,谁也不会落后:某个 crate 若尚未翻译,会表现为条目没有中文,而不是 +悄悄以英文呈现给中文读者;并且有测试断言已提交的目录中不存在这样的条目。 + +此前的做法把中文放在 `content/api.zh.json`,以条目名为键。这个做法已被替换, +而它错在哪里值得记下:键是一个名字,而名字并不唯一。`new` 由五十九个 crate +定义,它们对它的含义并不相同,因此以裸名归档的译文可能被显示在错误的文档旁。 +拆开注释消除了这个问题,因为译文附着在条目上,而不是靠一个名字查找。 文档注释用 Rust 路径引用另一个条目,写作 `[Epoch](crate::Epoch)` 或简写 `` [`Epoch::mjd`] ``。渲染器把这类引用变成指向该条目接口页的链接。名称的解析 diff --git a/scripts/__pycache__/extract_api.cpython-314.pyc b/scripts/__pycache__/extract_api.cpython-314.pyc new file mode 100644 index 0000000..9407a5f Binary files /dev/null and b/scripts/__pycache__/extract_api.cpython-314.pyc differ diff --git a/scripts/extract_api.py b/scripts/extract_api.py index 6f313da..67554e8 100755 --- a/scripts/extract_api.py +++ b/scripts/extract_api.py @@ -3,10 +3,19 @@ The service presents the public interface of the `apsis-rs` workspace as it is written in the source, so the catalog is generated from the source rather -than maintained by hand. This script reads the crate directories of the -layers that `apsis-book` enables, collects the crate level documentation and -every public item with its doc comment and signature, and writes the result -to `content/api.json`. The file is embedded in the binary by `build.rs`. +than maintained by hand. This script reads the crate directories, collects +the crate level documentation and every public item with its doc comment and +signature, and writes the result to `content/api.json`. The file is embedded +in the binary by `build.rs`. + +Each doc comment in `apsis-rs` is written in English and then in Chinese, in +the one comment beside the code. The two halves are split here, on the first +line that contains a Chinese character, and both are written to the catalog. +The service shows one of them, so no separate translation file is needed and +a translation cannot fall behind the code it describes. This replaced an +earlier arrangement that kept the Chinese in `content/api.zh.json` keyed by +item name, which was unsound because a name such as `new` is defined by many +crates and does not mean the same thing in each. Run it from the `apsis-book` directory when the `apsis-rs` sources change: @@ -107,6 +116,62 @@ ITEM = re.compile( DOC = re.compile(r"^\s*/// ?(.*)$") MODULE_DOC = re.compile(r"^\s*//! ?(.*)$") +# A doc comment carries the English text first and the Chinese text after it, +# in one comment. The boundary is the first line that contains a Chinese +# character. The two halves are returned separately so that the service can +# show one of them rather than both at once. +CJK = re.compile(r"[\u4e00-\u9fff]") +# A line that is only a code fence or an example belongs to whichever half it +# is in; it is not a boundary on its own. +def split_bilingual_lines(lines): + """Split doc comment lines into their English and Chinese parts.""" + boundary = None + for position, line in enumerate(lines): + if CJK.search(line): + boundary = position + break + if boundary is None: + return "\n".join(lines).strip(), None + english = "\n".join(lines[:boundary]).strip() + chinese = "\n".join(lines[boundary:]).strip() + # A comment that is Chinese throughout has no English half; the Chinese + # is then the only text there is. + return english, chinese or None + + +def split_bilingual(comment): + """Split one doc comment, given as text, into its two languages.""" + return split_bilingual_lines(comment.split("\n")) + + +def module_doc_lines(lines): + """Return the leading //! block as its lines, keeping their structure.""" + collected = [] + for line in lines: + match = MODULE_DOC.match(line) + if match: + collected.append(match.group(1)) + elif collected: + break + elif line.strip() and not line.startswith("//!"): + break + return collected + + +def module_doc_pair(lines): + """Return the leading //! block in English and in Chinese. + + The module documentation is the whole block, written in English and then + in Chinese. Both halves are returned whole, with their paragraphs and + lists intact: the crate page shows all of it, and the one line a card + needs is derived from it later. + + The split is made on the lines rather than on the joined text, because a + paragraph boundary and a language boundary are both line boundaries, and + joining first would lose both. + """ + return split_bilingual_lines(module_doc_lines(lines)) + def module_doc(lines): """Return the first paragraph of the leading //! block.""" @@ -177,7 +242,8 @@ def collect_file(path): match = ITEM.match(line) if match: - doc = doc_comment(buffer) + doc_all = doc_comment(buffer) + doc, doc_zh = split_bilingual(doc_all) signature_lines = [line.strip()] # A declaration continues until its parentheses balance and the # line ends with `{`, `;`, or a where clause that is closed. @@ -201,6 +267,7 @@ def collect_file(path): "name": match.group("name"), "signature": signature, "doc": doc, + "doc_zh": doc_zh, "associated": bool(match.group("indent")), } ) @@ -213,17 +280,18 @@ def collect_file(path): buffer = buffer[-200:] index += 1 - return module_doc(lines), items + return module_doc_pair(lines), items def collect_crate(workspace, name, directory): """Return the catalog entry of one crate.""" root = os.path.join(workspace, directory, "src") summary = "" + summary_zh = None items = [] lib = os.path.join(root, "lib.rs") if os.path.exists(lib): - summary, _ = collect_file(lib) + (summary, summary_zh), _ = collect_file(lib) for current, _, files in os.walk(root): for file_name in sorted(files): if not file_name.endswith(".rs"): @@ -242,7 +310,13 @@ def collect_crate(workspace, name, directory): continue seen.add(key) unique.append(item) - return {"name": name, "layer": None, "summary": summary, "items": unique} + return { + "name": name, + "layer": None, + "summary": summary, + "summary_zh": summary_zh, + "items": unique, + } def main(): diff --git a/src/docs/api.rs b/src/docs/api.rs index 734928d..fce3a71 100644 --- a/src/docs/api.rs +++ b/src/docs/api.rs @@ -27,9 +27,9 @@ pub struct ApiItem { pub doc: String, /// The documentation comment in Chinese, when a translation exists. /// - /// The translation lives in `content/api.zh.json`, keyed by the item - /// name. Interface documentation is long, and a reader of the Chinese - /// interface should not have to read English to understand a function. + /// The author writes the doc comment in English and then in Chinese, and + /// the extractor splits the two halves, so both languages come from the + /// one comment beside the code. pub doc_zh: Option, /// `true` when the item is an associated item rather than a module /// level one. @@ -122,18 +122,14 @@ impl ApiCatalog { /// A malformed member is skipped rather than failing the whole document, so /// that a partially generated catalog still serves the items it contains. #[allow(dead_code)] -pub fn parse(text: &str) -> ApiCatalog { - parse_with(text, None) -} - -/// Parses the catalog and applies a Chinese translation when one is given. +/// Parses the catalogue. /// -/// `translation` is the parsed content of `content/api.zh.json`, which maps -/// a crate name to its summary and an item name to its documentation. The -/// two are kept in separate files because the catalog is generated from the -/// source while the translation is authored, so regenerating the catalog -/// must not discard the translation. -pub fn parse_with(text: &str, translation: Option<&Value>) -> ApiCatalog { +/// The catalogue carries both languages, because `apsis-rs` writes its doc +/// comments in English and then in Chinese. `scripts/extract_api.sh` splits +/// the two halves while it reads the sources, so no separate translation +/// file is needed and a translation cannot fall behind the source it +/// describes. +pub fn parse(text: &str) -> ApiCatalog { let Ok(value) = json::parse(text) else { return ApiCatalog { crates: Vec::new(), @@ -141,8 +137,6 @@ pub fn parse_with(text: &str, translation: Option<&Value>) -> ApiCatalog { item_names: std::collections::HashSet::new(), }; }; - let crate_texts = translation.and_then(|value| value.get("crates")); - let item_texts = translation.and_then(|value| value.get("items")); let generated_from = value .get("generated_from") .map(Value::as_array) @@ -155,7 +149,7 @@ pub fn parse_with(text: &str, translation: Option<&Value>) -> ApiCatalog { .map(Value::as_array) .unwrap_or_default() .iter() - .filter_map(|krate| parse_crate(krate, crate_texts, item_texts)) + .filter_map(parse_crate) .collect(); let item_names = crates .iter() @@ -168,48 +162,15 @@ pub fn parse_with(text: &str, translation: Option<&Value>) -> ApiCatalog { } } -/// Returns a translated member of a table, when the key is present. -fn translated(table: Option<&Value>, key: &str) -> Option { - table - .and_then(|table| table.get(key)) +/// Returns a string member of an object, when it is present and not empty. +fn string_member(value: &Value, key: &str) -> Option { + value + .get(key) .and_then(Value::as_str) .filter(|text| !text.is_empty()) .map(str::to_string) } -/// Returns the Chinese documentation of an item. -/// -/// An associated item is looked up as `Type::name` first, then as `name`. -/// Several types have an associated `new`, so the qualified key is the one -/// that disambiguates them; the bare key is kept as a fallback for a -/// translation that is the same for every owner. -/// Returns the Chinese text of an item. -/// -/// Three keys are tried, most specific first. A name such as `new` is used -/// by many crates and several of them mean different things by it, so a -/// translation filed under the name alone would be shown against the wrong -/// documentation. The crate qualified key names one item exactly. The owner -/// qualified key names a method of one type, which separates two items of -/// the same crate that share a name, such as the `new` of two types. The -/// bare key is the last resort and is only correct for a name that one crate -/// uses for one thing. -fn item_translation( - item_texts: Option<&Value>, - krate: &str, - owner: Option<&str>, - name: &str, -) -> Option { - if let Some(text) = translated(item_texts, &format!("{krate}::{name}")) { - return Some(text); - } - if let Some(owner) = owner { - if let Some(text) = translated(item_texts, &format!("{owner}::{name}")) { - return Some(text); - } - } - translated(item_texts, name) -} - /// Returns the name of the type an item is associated with. /// /// The catalog lists an item in source order, so the type an associated @@ -227,11 +188,7 @@ fn owner_of(items: &[Value], index: usize) -> Option { } /// Parses one crate. -fn parse_crate( - value: &Value, - crate_texts: Option<&Value>, - item_texts: Option<&Value>, -) -> Option { +fn parse_crate(value: &Value) -> Option { let name = value.get("name")?.as_str()?.to_string(); // A crate that belongs to no layer, which is the facade that re-exports // every layer, is left empty. Defaulting it to a layer would file it @@ -250,16 +207,11 @@ fn parse_crate( let items = raw_items .iter() .enumerate() - .filter_map(|(index, item)| { - parse_item( - item, - item_texts, - &name, - owner_of(raw_items, index).as_deref(), - ) - }) + .filter_map(|(index, item)| parse_item(item, &name, owner_of(raw_items, index).as_deref())) .collect(); - let summary_zh = translated(crate_texts, &name); + // The module documentation is written in English and then in Chinese; + // the extractor separated the two. + let summary_zh = string_member(value, "summary_zh"); Some(ApiCrate { name, layer, @@ -270,12 +222,7 @@ fn parse_crate( } /// Parses one item. -pub(crate) fn parse_item( - value: &Value, - item_texts: Option<&Value>, - krate: &str, - owner: Option<&str>, -) -> Option { +pub(crate) fn parse_item(value: &Value, _krate: &str, _owner: Option<&str>) -> Option { let name = value.get("name")?.as_str()?.to_string(); let kind = value.get("kind")?.as_str()?.to_string(); let signature = value @@ -292,7 +239,7 @@ pub(crate) fn parse_item( .get("associated") .and_then(Value::as_bool) .unwrap_or(false); - let doc_zh = item_translation(item_texts, krate, owner, &name); + let doc_zh = string_member(value, "doc_zh"); Some(ApiItem { kind, name, diff --git a/src/docs/mod.rs b/src/docs/mod.rs index 03ff2af..353797b 100644 --- a/src/docs/mod.rs +++ b/src/docs/mod.rs @@ -110,6 +110,5 @@ pub fn load_api_catalog() -> ApiCatalog { item_names: std::collections::HashSet::new(), }; }; - let translation = content::get("api.zh.json").and_then(|text| crate::json::parse(text).ok()); - api::parse_with(text, translation.as_ref()) + api::parse(text) } diff --git a/src/json.rs b/src/json.rs index 89ffd3c..7058511 100644 --- a/src/json.rs +++ b/src/json.rs @@ -224,6 +224,22 @@ impl std::error::Error for JsonError {} /// /// Whitespace is allowed around every token. Trailing content after the /// first complete value is rejected. +/// Returns the length in bytes of the UTF-8 sequence starting with `byte`. +/// +/// The leading byte states the length; the continuation bytes are checked +/// when the sequence itself is validated. +fn utf8_length(byte: u8) -> usize { + match byte { + 0x00..=0x7f => 1, + 0xc0..=0xdf => 2, + 0xe0..=0xef => 3, + 0xf0..=0xf7 => 4, + // A continuation or an invalid leading byte; one byte is taken so + // that the validation reports it as invalid rather than looping. + _ => 1, + } +} + pub fn parse(input: &str) -> Result { let mut parser = Parser { bytes: input.as_bytes(), @@ -466,15 +482,22 @@ impl Parser<'_> { )); } _ => { - // Decode one UTF-8 scalar from the remaining bytes. - let rest = std::str::from_utf8(&self.bytes[self.position..]) + // Decode one UTF-8 scalar from the bytes at this position. + // + // Only the sequence itself is validated. Validating the + // whole remaining buffer instead would be correct and + // quadratic, because it repeats for every character in + // the document: a catalogue of seven hundred kilobytes + // spent half a minute in this one line. + let length = utf8_length(byte); + let end = self.position + length; + if end > self.bytes.len() { + return Err(JsonError::new(self.position, "truncated utf-8 in string")); + } + let sequence = std::str::from_utf8(&self.bytes[self.position..end]) .map_err(|_| JsonError::new(self.position, "invalid utf-8 in string"))?; - let character = rest - .chars() - .next() - .ok_or_else(|| JsonError::new(self.position, "unterminated string"))?; - output.push(character); - self.position += character.len_utf8(); + output.push_str(sequence); + self.position = end; } } } diff --git a/src/testkit.rs b/src/testkit.rs index a111aab..5086eb8 100644 --- a/src/testkit.rs +++ b/src/testkit.rs @@ -40,14 +40,9 @@ pub fn crate_summary_brief(summary: &str) -> String { crate::api::crate_summary_brief(summary) } -/// Parses one interface item, looking its translation up by crate. -pub fn parse_item( - value: &Value, - item_texts: Option<&Value>, - krate: &str, - owner: Option<&str>, -) -> Option { - crate::docs::api::parse_item(value, item_texts, krate, owner) +/// Parses one interface item of the catalogue. +pub fn parse_item(value: &Value, krate: &str, owner: Option<&str>) -> Option { + crate::docs::api::parse_item(value, krate, owner) } /// Splits text into the tokens the search index is built from. diff --git a/tests/docs_api.rs b/tests/docs_api.rs index 698182a..ff44ab0 100644 --- a/tests/docs_api.rs +++ b/tests/docs_api.rs @@ -43,38 +43,57 @@ fn malformed_input_yields_an_empty_catalog() { } #[test] -fn a_translation_filed_under_the_crate_beats_the_bare_name() { - // `new` means something different in each crate, so a translation - // filed under the name alone would be shown against the wrong - // documentation. +fn an_item_carries_both_languages() { + // `apsis-rs` writes the doc comment in English and then in Chinese, and + // the extractor separates the two into the catalogue. let item = r#"{"kind":"fn","name":"new","doc":"English.", - "signature":"pub fn new()"}"#; - let texts = r#"{ - "new":"裸译。", - "apsis-core::new":"属于 apsis-core 的译文。"}"#; + "doc_zh":"中文。","signature":"pub fn new()"}"#; let parsed = parse_item( &apsis_book::json::parse(item).expect("an item"), - Some(&apsis_book::json::parse(texts).expect("the translations")), "apsis-core", None, ) .expect("an item"); - assert_eq!(parsed.doc_zh.as_deref(), Some("属于 apsis-core 的译文。")); + assert_eq!(parsed.doc.as_str(), "English."); + assert_eq!(parsed.doc_zh.as_deref(), Some("中文。")); } #[test] -fn a_translation_filed_under_the_owner_separates_two_items_of_one_crate() { - // Two types of one crate each have a `new`; the owner tells them - // apart. +fn an_item_without_a_translation_has_none() { + // A comment that is English only leaves the Chinese empty, and the + // reader is then shown the English rather than nothing. let item = r#"{"kind":"fn","name":"new","doc":"English.", "signature":"pub fn new()"}"#; - let texts = r#"{"Inertial::new":"惯性系的新建。"}"#; let parsed = parse_item( &apsis_book::json::parse(item).expect("an item"), - Some(&apsis_book::json::parse(texts).expect("the translations")), - "apsis-coord", - Some("Inertial"), + "apsis-core", + None, ) .expect("an item"); - assert_eq!(parsed.doc_zh.as_deref(), Some("惯性系的新建。")); + assert_eq!(parsed.doc_zh, None); +} + +#[test] +fn the_committed_catalogue_carries_both_languages_for_every_item() { + // The translation comes from the source doc comments rather than from a + // file maintained beside them, so a crate that was never translated shows + // up here rather than silently reading as English to a Chinese reader. + let catalog = apsis_book::docs::load_api_catalog(); + let mut missing = Vec::new(); + for krate in &catalog.crates { + if krate.summary_zh.is_none() { + missing.push(format!("{} (crate summary)", krate.name)); + } + for item in &krate.items { + if item.doc_zh.is_none() { + missing.push(format!("{}::{}", krate.name, item.name)); + } + } + } + assert!( + missing.is_empty(), + "{} entries have no Chinese: {:?}", + missing.len(), + &missing[..missing.len().min(8)] + ); }