Write the formulas in the notation Markdown has for them
A formula was written as a fenced code block, which is what a plain text file allows and is not what Markdown has for mathematics. A code block is code: the reader of the source sees a formula presented as code, and the renderer had been taught to guess which blocks held equations, which is a guess that is wrong whenever a block holds something else. Every formula now stands between dollar signs, which is the notation every Markdown mathematics renderer reads, and the guess is gone from the renderer. A hundred and four blocks became two hundred and four formulas in the two languages. Three things that were in a code block were not formulas and are now what they are. A paragraph of geometry was prose. A table of the exponent notation is a table, because its two columns are a written form and what it means. Two blocks are left as they were: the layer diagram, which is a diagram, and a sample element set, which has to keep its column alignment. The renderer learned what the notation needs and did not have: matrix environments, with an ampersand between the columns of a row and a double backslash between the rows; the floor and ceiling marks; the modulo operator; a mark above an argument, as in the time derivative of Omega and a barred coefficient; the text command, whose argument is words and was being set one letter at a time; and the spacing commands. The check on the formulas covered the concept documents and not the interface documentation, so every formula there was unexamined. It covers both now, and it treats a command name and an environment name as notation, which is what they are: neither is drawn as the letters of its own name.
This commit is contained in:
parent
73b5f3de63
commit
9caa542b52
|
|
@ -178,6 +178,12 @@ or later. The Rust build does not run it and does not depend on Python.
|
||||||
| `content/flows/*.json` | The algorithm flow graphs |
|
| `content/flows/*.json` | The algorithm flow graphs |
|
||||||
| `content/api.json` | The generated interface catalog, in both languages; do not edit by hand |
|
| `content/api.json` | The generated interface catalog, in both languages; do not edit by hand |
|
||||||
|
|
||||||
|
A formula is written between dollar signs, as `$$...$$` for one on a line of
|
||||||
|
its own and `$...$` for one within a sentence. That is the notation every
|
||||||
|
Markdown mathematics renderer reads, and it is what the documents use: a
|
||||||
|
formula is mathematics, and a fenced code block is not the place for it. The
|
||||||
|
service translates the notation to MathML, which the browser lays out itself.
|
||||||
|
|
||||||
A document header declares `title`, `summary`, `tags`, and `prerequisites`.
|
A document header declares `title`, `summary`, `tags`, and `prerequisites`.
|
||||||
The identifier and the layer are not declared: the identifier is the file
|
The identifier and the layer are not declared: the identifier is the file
|
||||||
name and the layer is the directory, so the two cannot disagree with the
|
name and the layer is the directory, so the two cannot disagree with the
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,10 @@ cargo build --release
|
||||||
| `content/flows/*.json` | 算法流程图 |
|
| `content/flows/*.json` | 算法流程图 |
|
||||||
| `content/api.json` | 生成的接口目录,含两种语言;不要手工编辑 |
|
| `content/api.json` | 生成的接口目录,含两种语言;不要手工编辑 |
|
||||||
|
|
||||||
|
公式写在美元符号之间:独占一行用 `$$...$$`,行内用 `$...$`。这是各类
|
||||||
|
Markdown 数学渲染器都识别的写法,也是本文档所采用的写法:公式是数学,
|
||||||
|
代码块不是放它的地方。服务把该记法翻译为 MathML,由浏览器自行排版。
|
||||||
|
|
||||||
文档头部声明 `title`、`summary`、`tags` 与 `prerequisites`。标识与层不由头部
|
文档头部声明 `title`、`summary`、`tags` 与 `prerequisites`。标识与层不由头部
|
||||||
声明:标识即文件名,层即目录,二者无法与目录结构相抵触。交叉引用写作
|
声明:标识即文件名,层即目录,二者无法与目录结构相抵触。交叉引用写作
|
||||||
`[[id]]` 或 `[[id|label]]`,渲染为链接,前端无须整页加载即可路由。
|
`[[id]]` 或 `[[id|label]]`,渲染为链接,前端无须整页加载即可路由。
|
||||||
|
|
|
||||||
|
|
@ -35,17 +35,17 @@ problem and cannot be estimated from the arithmetic; it has to be recognised.
|
||||||
A fixed point is reached by repeated substitution, `x` becoming `g(x)`. It
|
A fixed point is reached by repeated substitution, `x` becoming `g(x)`. It
|
||||||
converges when the derivative of `g` at the root is small in magnitude:
|
converges when the derivative of `g` at the root is small in magnitude:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
|g'(x)| < 1 for convergence
|
\lvert g'(x) \rvert < 1 \quad \text{(convergence)}, \qquad
|
||||||
|g'(x)| > 1 for divergence
|
\lvert g'(x) \rvert > 1 \quad \text{(divergence)}
|
||||||
```
|
$$
|
||||||
|
|
||||||
Kepler's equation, `M = E - e sin E`, has no closed form solution for `E`. It
|
Kepler's equation, `M = E - e sin E`, has no closed form solution for `E`. It
|
||||||
is solved by Newton's method,
|
is solved by Newton's method,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
E_{n+1} = E_n - (E_n - e sin E_n - M) / (1 - e cos E_n)
|
E_{n+1} = E_n - \frac{E_n - e \sin E_n - M}{1 - e \cos E_n}
|
||||||
```
|
$$
|
||||||
|
|
||||||
which converges quadratically near the root when `e` is not close to one. The
|
which converges quadratically near the root when `e` is not close to one. The
|
||||||
iteration is fast and its rate depends on eccentricity; for a near-parabolic
|
iteration is fast and its rate depends on eccentricity; for a near-parabolic
|
||||||
|
|
@ -60,9 +60,9 @@ whether divergence or cost dominates.
|
||||||
The sensitivity of an answer to a perturbation of its input is the
|
The sensitivity of an answer to a perturbation of its input is the
|
||||||
**condition number**. For a function `f`,
|
**condition number**. For a function `f`,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
kappa = | f'(x) | * | x| / | f(x) |
|
\kappa = \frac{\lvert f'(x) \rvert \, \lvert x \rvert}{\lvert f(x) \rvert}
|
||||||
```
|
$$
|
||||||
|
|
||||||
A large condition number means a small input error becomes a large output
|
A large condition number means a small input error becomes a large output
|
||||||
error, whatever the method. Near a singular point the condition number
|
error, whatever the method. Near a singular point the condition number
|
||||||
|
|
|
||||||
|
|
@ -24,16 +24,16 @@ summary: 当精确答案不存在时,一个算出来的数字会有什么行
|
||||||
|
|
||||||
不动点通过反复代入达到,`x` 变为 `g(x)`。当根附近导数绝对值小于一时收敛:
|
不动点通过反复代入达到,`x` 变为 `g(x)`。当根附近导数绝对值小于一时收敛:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
|g'(x)| < 1 收敛
|
\lvert g'(x) \rvert < 1 \quad \text{(收敛)}, \qquad
|
||||||
|g'(x)| > 1 发散
|
\lvert g'(x) \rvert > 1 \quad \text{(发散)}
|
||||||
```
|
$$
|
||||||
|
|
||||||
开普勒方程 `M = E - e sin E` 对 `E` 没有闭式解,用牛顿法求解:
|
开普勒方程 `M = E - e sin E` 对 `E` 没有闭式解,用牛顿法求解:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
E_{n+1} = E_n - (E_n - e sin E_n - M) / (1 - e cos E_n)
|
E_{n+1} = E_n - \frac{E_n - e \sin E_n - M}{1 - e \cos E_n}
|
||||||
```
|
$$
|
||||||
|
|
||||||
当 `e` 不接近一时在根附近二次收敛。该迭代很快,其收敛率取决于偏心率;对接近抛物线的轨道分母趋近于零,需要改用其他形式。
|
当 `e` 不接近一时在根附近二次收敛。该迭代很快,其收敛率取决于偏心率;对接近抛物线的轨道分母趋近于零,需要改用其他形式。
|
||||||
|
|
||||||
|
|
@ -43,9 +43,9 @@ E_{n+1} = E_n - (E_n - e sin E_n - M) / (1 - e cos E_n)
|
||||||
|
|
||||||
答案对输入扰动的敏感程度是**条件数**。对函数 `f`,
|
答案对输入扰动的敏感程度是**条件数**。对函数 `f`,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
kappa = | f'(x) | * | x| / | f(x) |
|
\kappa = \frac{\lvert f'(x) \rvert \, \lvert x \rvert}{\lvert f(x) \rvert}
|
||||||
```
|
$$
|
||||||
|
|
||||||
条件数大意味着小的输入误差变成大的输出误差,与方法无关。在奇点附近条件数发散:由半长轴与偏心率计算轨道半径是良态的,而由同样元素计算近地点时刻不是,因为分母中出现 `sqrt(1 - e^2)`,当 `e` 趋近一时趋于零。问题病态而非算术病态,多给几位有效数字无济于事。
|
条件数大意味着小的输入误差变成大的输出误差,与方法无关。在奇点附近条件数发散:由半长轴与偏心率计算轨道半径是良态的,而由同样元素计算近地点时刻不是,因为分母中出现 `sqrt(1 - e^2)`,当 `e` 趋近一时趋于零。问题病态而非算术病态,多给几位有效数字无济于事。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,9 @@ A unit is a conversion factor to a base unit. Length may be expressed in
|
||||||
metres, kilometres or feet; each is a number multiplied by a factor to give
|
metres, kilometres or feet; each is a number multiplied by a factor to give
|
||||||
metres. Dimensionally aware arithmetic keeps track of that factor, so
|
metres. Dimensionally aware arithmetic keeps track of that factor, so
|
||||||
|
|
||||||
```text
|
$$
|
||||||
1 km + 500 m = 1500 m and never 1 + 500 = 501
|
1\,\text{km} + 500\,\text{m} = 1500\,\text{m}, \quad \text{never } 1 + 500 = 501
|
||||||
```
|
$$
|
||||||
|
|
||||||
The rule is that the conversion factor travels with the value. A quantity of
|
The rule is that the conversion factor travels with the value. A quantity of
|
||||||
type length carries the factor to metres with it, so adding it to another
|
type length carries the factor to metres with it, so adding it to another
|
||||||
|
|
@ -58,9 +58,9 @@ Dimension reasoning earns its keep in scaling arguments, where an error shows
|
||||||
up as an impossible exponent. The gravitational perturbation at distance `r`
|
up as an impossible exponent. The gravitational perturbation at distance `r`
|
||||||
from a body of mass `M` must scale as
|
from a body of mass `M` must scale as
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a ~ G M / r^2
|
a \propto \frac{G M}{r^2}
|
||||||
```
|
$$
|
||||||
|
|
||||||
because `G` has units of `m^3 kg^-1 s^-2`; any other combination either does
|
because `G` has units of `m^3 kg^-1 s^-2`; any other combination either does
|
||||||
not have the dimensions of an acceleration or does have them only for a
|
not have the dimensions of an acceleration or does have them only for a
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@ summary: 物理量如何携带其单位,以及为什么单位是类型的一
|
||||||
|
|
||||||
单位是换算到基本单位的因子。长度可以用米、千米或英尺表示;每一个都是乘以某个因子得到米数。带量纲的运算会跟踪这个因子,因此
|
单位是换算到基本单位的因子。长度可以用米、千米或英尺表示;每一个都是乘以某个因子得到米数。带量纲的运算会跟踪这个因子,因此
|
||||||
|
|
||||||
```text
|
$$
|
||||||
1 千米 + 500 米 = 1500 米 而绝不会是 1 + 500 = 501
|
1\,\text{千米} + 500\,\text{米} = 1500\,\text{米}, \quad \text{而绝不会是 } 1 + 500 = 501
|
||||||
```
|
$$
|
||||||
|
|
||||||
规则是换算因子随数值一同流动。一个长度类型的量把换算到米的因子一起携带,因此与另一个长度相加时两个因子同时生效。
|
规则是换算因子随数值一同流动。一个长度类型的量把换算到米的因子一起携带,因此与另一个长度相加时两个因子同时生效。
|
||||||
|
|
||||||
|
|
@ -42,9 +42,9 @@ summary: 物理量如何携带其单位,以及为什么单位是类型的一
|
||||||
|
|
||||||
量纲推理在标度分析中格外有用,因为一旦出错就会表现为不可能的指数。距质量为 `M` 的天体 `r` 处的引力摄动必然标度为
|
量纲推理在标度分析中格外有用,因为一旦出错就会表现为不可能的指数。距质量为 `M` 的天体 `r` 处的引力摄动必然标度为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a ~ G M / r^2
|
a \propto \frac{G M}{r^2}
|
||||||
```
|
$$
|
||||||
|
|
||||||
因为 `G` 的单位是 `m^3 kg^-1 s^-2`;任何其他组合要么不具备加速度的量纲,要么只在特定情形成立,后者正是论证不完整的信号。
|
因为 `G` 的单位是 `m^3 kg^-1 s^-2`;任何其他组合要么不具备加速度的量纲,要么只在特定情形成立,后者正是论证不完整的信号。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,9 @@ across the line of sight.
|
||||||
A satellite in a circular orbit at semi-major axis `a` passes a latitude
|
A satellite in a circular orbit at semi-major axis `a` passes a latitude
|
||||||
`phi` with a maximum elevation `e_max` that follows from the geometry,
|
`phi` with a maximum elevation `e_max` that follows from the geometry,
|
||||||
|
|
||||||
```text
|
`e_max` follows from the geometry: it is the angle whose tangent is the
|
||||||
e_max such that the elevation is arctan of the perpendicular
|
perpendicular distance from the observer to the orbit plane divided by the
|
||||||
distance from the observer to the orbit plane, divided
|
distance along the line of sight.
|
||||||
by the distance along the line of sight
|
|
||||||
```
|
|
||||||
|
|
||||||
with two limits worth naming. At the pole a satellite in an inclined orbit
|
with two limits worth naming. At the pole a satellite in an inclined orbit
|
||||||
reaches only `90 - inclination` degrees of elevation at best, so a
|
reaches only `90 - inclination` degrees of elevation at best, so a
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,9 @@ poorly covered at any given place and time.
|
||||||
At altitude `h`, the Earth subtends a half angle from the satellite, and the
|
At altitude `h`, the Earth subtends a half angle from the satellite, and the
|
||||||
ground area visible above an elevation angle `e` is
|
ground area visible above an elevation angle `e` is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
A(e) = 2 pi R^2 ( 1 + cos(e) )
|
A(e) = 2 \pi R^2 \left( 1 + \cos e \right)
|
||||||
```
|
$$
|
||||||
|
|
||||||
For `e = 0`, the visible cap is a hemisphere. The fraction of the globe a
|
For `e = 0`, the visible cap is a hemisphere. The fraction of the globe a
|
||||||
satellite can see at once is therefore fixed by its altitude alone, and no
|
satellite can see at once is therefore fixed by its altitude alone, and no
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ summary: 星座为何是一个关于轨道面、相位与壳层的算术问题
|
||||||
|
|
||||||
在高度 `h` 处地球对卫星张成半角,仰角 `e` 以上的可见地面面积为
|
在高度 `h` 处地球对卫星张成半角,仰角 `e` 以上的可见地面面积为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
A(e) = 2 pi R^2 ( 1 + cos(e) )
|
A(e) = 2 \pi R^2 \left( 1 + \cos e \right)
|
||||||
```
|
$$
|
||||||
|
|
||||||
`e = 0` 时可见的是一个半球。因此单颗卫星同时可见的全球比例只由其高度决定,再多的卫星也不能改善它覆盖该半球的程度。
|
`e = 0` 时可见的是一个半球。因此单颗卫星同时可见的全球比例只由其高度决定,再多的卫星也不能改善它覆盖该半球的程度。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@ plane drifts because the oblateness and the solar and lunar gravity produce
|
||||||
a nodal rate the design did not ask for. Holding the plane costs a
|
a nodal rate the design did not ask for. Holding the plane costs a
|
||||||
station-keeping manoeuvre at some interval,
|
station-keeping manoeuvre at some interval,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_v_keeping ~ | dOmega/dt | * interval * a
|
\Delta v_{\text{keeping}} \sim \left\lvert \dot\Omega \right\rvert \Delta t \, a
|
||||||
```
|
$$
|
||||||
|
|
||||||
so the cost is linear in the drift rate and in the interval. Halving the
|
so the cost is linear in the drift rate and in the interval. Halving the
|
||||||
allowed drift doubles the rate, and halving the allowed drift interval
|
allowed drift doubles the rate, and halving the allowed drift interval
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ summary: 让航天器待在它该在的位置这一日常问题,以及决定
|
||||||
|
|
||||||
对处于非严格太阳同步轨道的低轨,轨道面会漂移,因为扁率与日月引力产生了设计未要求的面内节点速率。保持该轨道面需要以某间隔做位保机动:
|
对处于非严格太阳同步轨道的低轨,轨道面会漂移,因为扁率与日月引力产生了设计未要求的面内节点速率。保持该轨道面需要以某间隔做位保机动:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_v_keeping ~ | dOmega/dt | * 间隔 * a
|
\Delta v_{\text{保持}} \sim \left\lvert \dot\Omega \right\rvert \Delta t \, a
|
||||||
```
|
$$
|
||||||
|
|
||||||
因此代价对漂移率与间隔都是线性的。允许漂移减半使速率加倍,允许漂移的间隔减半使机动次数加倍:两者相乘。
|
因此代价对漂移率与间隔都是线性的。允许漂移减半使速率加倍,允许漂移的间隔减半使机动次数加倍:两者相乘。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,17 @@ against lifetime, and it is why most low Earth orbits end.
|
||||||
|
|
||||||
**Drag** is the aerodynamic force opposite the velocity. Its magnitude is
|
**Drag** is the aerodynamic force opposite the velocity. Its magnitude is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
F = -1/2 * rho * v^2 * Cd * A
|
F = -\tfrac{1}{2} \rho v^2 C_d A
|
||||||
```
|
$$
|
||||||
|
|
||||||
with `rho` the atmospheric density, `v` the speed, `Cd` a drag coefficient of
|
with `rho` the atmospheric density, `v` the speed, `Cd` a drag coefficient of
|
||||||
order one, and `A` the projected area. What matters to the orbit is the
|
order one, and `A` the projected area. What matters to the orbit is the
|
||||||
combination
|
combination
|
||||||
|
|
||||||
```text
|
$$
|
||||||
B = (Cd * A) / m
|
B = \frac{C_d A}{m}
|
||||||
```
|
$$
|
||||||
|
|
||||||
the inverse of the **ballistic coefficient**, and `B` is what a drag model
|
the inverse of the **ballistic coefficient**, and `B` is what a drag model
|
||||||
actually needs.
|
actually needs.
|
||||||
|
|
@ -48,9 +48,9 @@ satellite can gain altitude while moving through it rather than losing it.
|
||||||
|
|
||||||
In an isothermal layer the density falls exponentially with a scale height
|
In an isothermal layer the density falls exponentially with a scale height
|
||||||
|
|
||||||
```text
|
$$
|
||||||
H = k * T / (m * g)
|
H = \frac{k T}{m g}
|
||||||
```
|
$$
|
||||||
|
|
||||||
with `T` the temperature, which itself depends on the solar activity. A warmer
|
with `T` the temperature, which itself depends on the solar activity. A warmer
|
||||||
thermosphere means a larger scale height, a higher density at every altitude,
|
thermosphere means a larger scale height, a higher density at every altitude,
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,15 @@ summary: 大气顶层为何是轨道模型最难的部分、密度如何估计
|
||||||
|
|
||||||
**阻力**是与速度相反的气动力。其大小为
|
**阻力**是与速度相反的气动力。其大小为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
F = -1/2 * rho * v^2 * Cd * A
|
F = -\tfrac{1}{2} \rho v^2 C_d A
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `rho` 为大气密度、`v` 为速度、`Cd` 为量级在一左右的阻力系数、`A` 为投影面积。对轨道真正重要的是其组合
|
其中 `rho` 为大气密度、`v` 为速度、`Cd` 为量级在一左右的阻力系数、`A` 为投影面积。对轨道真正重要的是其组合
|
||||||
|
|
||||||
```text
|
$$
|
||||||
B = (Cd * A) / m
|
B = \frac{C_d A}{m}
|
||||||
```
|
$$
|
||||||
|
|
||||||
即**弹道系数**的倒数,而 `B` 正是阻力模型实际需要的量。
|
即**弹道系数**的倒数,而 `B` 正是阻力模型实际需要的量。
|
||||||
|
|
||||||
|
|
@ -36,9 +36,9 @@ B = (Cd * A) / m
|
||||||
|
|
||||||
在等温层中密度按标高指数衰减:
|
在等温层中密度按标高指数衰减:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
H = k * T / (m * g)
|
H = \frac{k T}{m g}
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `T` 为温度,而温度本身又取决于太阳活动。更热的热层意味着更大的标高、每个高度上更高的密度,以及更快的衰减。
|
其中 `T` 为温度,而温度本身又取决于太阳活动。更热的热层意味着更大的标高、每个高度上更高的密度,以及更快的衰减。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,19 +25,21 @@ The interpolation is a cubic Hermite polynomial per component. It uses the
|
||||||
position at both endpoints and the velocity at both endpoints, so it matches
|
position at both endpoints and the velocity at both endpoints, so it matches
|
||||||
the value and the first derivative at the ends of every interval:
|
the value and the first derivative at the ends of every interval:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
p(s) = h00(s) p0 + h10(s) dt v0 + h01(s) p1 + h11(s) dt v1
|
p(s) = h_{00}(s) \, p_0 + h_{10}(s) \, \Delta t \, v_0 + h_{01}(s) \, p_1 + h_{11}(s) \, \Delta t \, v_1
|
||||||
```
|
$$
|
||||||
|
|
||||||
where `s` is the fraction along the interval in `[0, 1]`, `dt` is the
|
where `s` is the fraction along the interval in `[0, 1]`, `dt` is the
|
||||||
interval length, and
|
interval length, and
|
||||||
|
|
||||||
```text
|
$$
|
||||||
h00 = 2 s^3 - 3 s^2 + 1
|
\begin{aligned}
|
||||||
h10 = s^3 - 2 s^2 + s
|
h_{00} &= 2 s^3 - 3 s^2 + 1 \\
|
||||||
h01 = -2 s^3 + 3 s^2
|
h_{10} &= s^3 - 2 s^2 + s \\
|
||||||
h11 = s^3 - s^2
|
h_{01} &= -2 s^3 + 3 s^2 \\
|
||||||
```
|
h_{11} &= s^3 - s^2
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
are the Hermite basis polynomials. The velocity is the derivative of the
|
are the Hermite basis polynomials. The velocity is the derivative of the
|
||||||
same polynomial with respect to time.
|
same polynomial with respect to time.
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,20 @@ summary: 何时采样一次并多次查询轨迹,以及插值所付出的精
|
||||||
|
|
||||||
插值对每个分量使用三次 Hermite 多项式。它用到两个端点的位置与两个端点的速度,因此在每个区间两端同时匹配值与一阶导数:
|
插值对每个分量使用三次 Hermite 多项式。它用到两个端点的位置与两个端点的速度,因此在每个区间两端同时匹配值与一阶导数:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
p(s) = h00(s) p0 + h10(s) dt v0 + h01(s) p1 + h11(s) dt v1
|
p(s) = h_{00}(s) \, p_0 + h_{10}(s) \, \Delta t \, v_0 + h_{01}(s) \, p_1 + h_{11}(s) \, \Delta t \, v_1
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `s` 是区间内 `[0, 1]` 的比例,`dt` 是区间长度,而
|
其中 `s` 是区间内 `[0, 1]` 的比例,`dt` 是区间长度,而
|
||||||
|
|
||||||
```text
|
$$
|
||||||
h00 = 2 s^3 - 3 s^2 + 1
|
\begin{aligned}
|
||||||
h10 = s^3 - 2 s^2 + s
|
h_{00} &= 2 s^3 - 3 s^2 + 1 \\
|
||||||
h01 = -2 s^3 + 3 s^2
|
h_{10} &= s^3 - 2 s^2 + s \\
|
||||||
h11 = s^3 - s^2
|
h_{01} &= -2 s^3 + 3 s^2 \\
|
||||||
```
|
h_{11} &= s^3 - s^2
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
是 Hermite 基多项式。速度是同一多项式对时间的导数。
|
是 Hermite 基多项式。速度是同一多项式对时间的导数。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@ high-accuracy model does and where its difficulties begin.
|
||||||
The **geopotential** of a body is the potential energy per unit mass due to its
|
The **geopotential** of a body is the potential energy per unit mass due to its
|
||||||
gravitational field. Near the Earth it is conventionally written
|
gravitational field. Near the Earth it is conventionally written
|
||||||
|
|
||||||
```text
|
$$
|
||||||
U(r, theta, lambda) = (mu / r) [ 1
|
U(r, \theta, \lambda) = \frac{\mu}{r} \left[ 1
|
||||||
+ sum_n (R/r)^n * sum_m P_nm(sin theta)
|
+ \sum_{n} \left( \frac{R}{r} \right)^n \sum_{m} P_{nm}(\sin\theta)
|
||||||
* [ C_nm cos(m lambda) + S_nm sin(m lambda) ] ]
|
\left( C_{nm} \cos m\lambda + S_{nm} \sin m\lambda \right) \right]
|
||||||
```
|
$$
|
||||||
|
|
||||||
- `r` is the distance from the centre, `theta` the geocentric colatitude and
|
- `r` is the distance from the centre, `theta` the geocentric colatitude and
|
||||||
`lambda` the longitude.
|
`lambda` the longitude.
|
||||||
|
|
@ -67,9 +67,11 @@ strongest, not where it is weakest.
|
||||||
The coefficients published by a model are normalised so that the unnormalised
|
The coefficients published by a model are normalised so that the unnormalised
|
||||||
form does not grow with degree:
|
form does not grow with degree:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
[ C_nm S_nm ] = [ 1 / sqrt( (1-delta) (n-m)! (n+m)! ) ] * [ Cbar_nm Sbar_nm ]
|
\begin{bmatrix} C_{nm} \\ S_{nm} \end{bmatrix}
|
||||||
```
|
= \frac{1}{\sqrt{(1 - \delta)\,(n - m)!\,(n + m)!}}
|
||||||
|
\begin{bmatrix} \bar{C}_{nm} \\ \bar{S}_{nm} \end{bmatrix}
|
||||||
|
$$
|
||||||
|
|
||||||
A model is defined by its degree `N` and by the coefficients below it. Degree
|
A model is defined by its degree `N` and by the coefficients below it. Degree
|
||||||
180 with the first few hundred coefficients is typical.
|
180 with the first few hundred coefficients is typical.
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ summary: 扁率项遗漏了什么、地球引力场如何展开,以及这个
|
||||||
|
|
||||||
天体的**地球位势**是其引力场造成的单位质量势能。在地球附近它习惯写成
|
天体的**地球位势**是其引力场造成的单位质量势能。在地球附近它习惯写成
|
||||||
|
|
||||||
```text
|
$$
|
||||||
U(r, theta, lambda) = (mu / r) [ 1
|
U(r, \theta, \lambda) = \frac{\mu}{r} \left[ 1
|
||||||
+ sum_n (R/r)^n * sum_m P_nm(sin theta)
|
+ \sum_{n} \left( \frac{R}{r} \right)^n \sum_{m} P_{nm}(\sin\theta)
|
||||||
* [ C_nm cos(m lambda) + S_nm sin(m lambda) ] ]
|
\left( C_{nm} \cos m\lambda + S_{nm} \sin m\lambda \right) \right]
|
||||||
```
|
$$
|
||||||
|
|
||||||
- `r` 是到地心的距离,`theta` 是地心余纬,`lambda` 是经度。
|
- `r` 是到地心的距离,`theta` 是地心余纬,`lambda` 是经度。
|
||||||
- `P_nm` 是勒让德函数,是 `sin(theta)` 上的实多项式。
|
- `P_nm` 是勒让德函数,是 `sin(theta)` 上的实多项式。
|
||||||
|
|
@ -45,9 +45,11 @@ U(r, theta, lambda) = (mu / r) [ 1
|
||||||
|
|
||||||
模型公布的系数经过归一化,使未归一化形式不随阶次增长:
|
模型公布的系数经过归一化,使未归一化形式不随阶次增长:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
[ C_nm S_nm ] = [ 1 / sqrt( (1-delta) (n-m)! (n+m)! ) ] * [ Cbar_nm Sbar_nm ]
|
\begin{bmatrix} C_{nm} \\ S_{nm} \end{bmatrix}
|
||||||
```
|
= \frac{1}{\sqrt{(1 - \delta)\,(n - m)!\,(n + m)!}}
|
||||||
|
\begin{bmatrix} \bar{C}_{nm} \\ \bar{S}_{nm} \end{bmatrix}
|
||||||
|
$$
|
||||||
|
|
||||||
一个模型由其最高阶 `N` 以及此下的系数定义。180 阶配前几百个系数是常见配置。
|
一个模型由其最高阶 `N` 以及此下的系数定义。180 阶配前几百个系数是常见配置。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,37 +35,41 @@ farthest is apogee.
|
||||||
The radius as a function of the true anomaly follows from the conic
|
The radius as a function of the true anomaly follows from the conic
|
||||||
equation:
|
equation:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
r = p / (1 + e cos(nu)), p = a (1 - e^2)
|
r = \frac{p}{1 + e \cos\nu}, \qquad p = a (1 - e^2)
|
||||||
```
|
$$
|
||||||
|
|
||||||
where `p` is the semi-latus rectum. The velocity follows from the vis-viva
|
where `p` is the semi-latus rectum. The velocity follows from the vis-viva
|
||||||
equation:
|
equation:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
v^2 = mu (2 / r - 1 / a)
|
v^2 = \mu \left( \frac{2}{r} - \frac{1}{a} \right)
|
||||||
```
|
$$
|
||||||
|
|
||||||
where `mu` is the gravitational parameter of the Earth.
|
where `mu` is the gravitational parameter of the Earth.
|
||||||
|
|
||||||
Two derived quantities appear often:
|
Two derived quantities appear often:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
perigee radius r_p = a (1 - e)
|
\begin{aligned}
|
||||||
apogee radius r_a = a (1 + e)
|
r_p &= a (1 - e) && \text{(perigee radius)} \\
|
||||||
period T = 2 pi sqrt(a^3 / mu) (Kepler's third law)
|
r_a &= a (1 + e) && \text{(apogee radius)} \\
|
||||||
```
|
T &= 2 \pi \sqrt{\frac{a^3}{\mu}} && \text{(Kepler's third law)}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
The conversion to a Cartesian state places the satellite in the perifocal
|
The conversion to a Cartesian state places the satellite in the perifocal
|
||||||
frame, whose first axis points to perigee and whose third axis is along the
|
frame, whose first axis points to perigee and whose third axis is along the
|
||||||
orbit normal:
|
orbit normal:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
r = p / (1 + e cos nu)
|
\begin{aligned}
|
||||||
x = r cos(nu), y = r sin(nu), z = 0
|
r &= \frac{p}{1 + e \cos\nu} \\
|
||||||
vx = -sqrt(mu / p) sin(nu)
|
x &= r \cos\nu, \quad y = r \sin\nu, \quad z = 0 \\
|
||||||
vy = sqrt(mu / p) (e + cos(nu))
|
v_x &= -\sqrt{\frac{\mu}{p}} \sin\nu \\
|
||||||
```
|
v_y &= \sqrt{\frac{\mu}{p}} \left( e + \cos\nu \right)
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
and then rotates by the argument of perigee, the inclination, and the right
|
and then rotates by the argument of perigee, the inclination, and the right
|
||||||
ascension of the ascending node.
|
ascension of the ascending node.
|
||||||
|
|
@ -83,10 +87,9 @@ apogee and differ in between.
|
||||||
|
|
||||||
The relations are
|
The relations are
|
||||||
|
|
||||||
```text
|
$$
|
||||||
tan(nu / 2) = sqrt((1 + e) / (1 - e)) tan(E / 2)
|
\tan\frac{\nu}{2} = \sqrt{\frac{1 + e}{1 - e}} \tan\frac{E}{2}, \qquad M = E - e \sin E
|
||||||
M = E - e sin(E)
|
$$
|
||||||
```
|
|
||||||
|
|
||||||
`M = E - e sin(E)` is Kepler's equation. It is transcendental, so `E` is
|
`M = E - e sin(E)` is Kepler's equation. It is transcendental, so `E` is
|
||||||
recovered from `M` by iteration; the implementation uses Newton's method
|
recovered from `M` by iteration; the implementation uses Newton's method
|
||||||
|
|
|
||||||
|
|
@ -27,34 +27,38 @@ summary: 六个经典轨道元素、二体解与三种近点角。
|
||||||
|
|
||||||
半径随真近点角的变化由圆锥曲线方程给出:
|
半径随真近点角的变化由圆锥曲线方程给出:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
r = p / (1 + e cos(nu)), p = a (1 - e^2)
|
r = \frac{p}{1 + e \cos\nu}, \qquad p = a (1 - e^2)
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `p` 是半通径。速度由活力公式给出:
|
其中 `p` 是半通径。速度由活力公式给出:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
v^2 = mu (2 / r - 1 / a)
|
v^2 = \mu \left( \frac{2}{r} - \frac{1}{a} \right)
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `mu` 是地球的引力参数。
|
其中 `mu` 是地球的引力参数。
|
||||||
|
|
||||||
两个导出量经常出现:
|
两个导出量经常出现:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
近地点半径 r_p = a (1 - e)
|
\begin{aligned}
|
||||||
远地点半径 r_a = a (1 + e)
|
r_p &= a (1 - e) && \text{(近地点半径)} \\
|
||||||
周期 T = 2 pi sqrt(a^3 / mu) (开普勒第三定律)
|
r_a &= a (1 + e) && \text{(远地点半径)} \\
|
||||||
```
|
T &= 2 \pi \sqrt{\frac{a^3}{\mu}} && \text{(开普勒第三定律)}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
到直角状态的转换先把卫星置于近焦点坐标系,其第一轴指向近地点,第三轴沿轨道法线:
|
到直角状态的转换先把卫星置于近焦点坐标系,其第一轴指向近地点,第三轴沿轨道法线:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
r = p / (1 + e cos nu)
|
\begin{aligned}
|
||||||
x = r cos(nu), y = r sin(nu), z = 0
|
r &= \frac{p}{1 + e \cos\nu} \\
|
||||||
vx = -sqrt(mu / p) sin(nu)
|
x &= r \cos\nu, \quad y = r \sin\nu, \quad z = 0 \\
|
||||||
vy = sqrt(mu / p) (e + cos(nu))
|
v_x &= -\sqrt{\frac{\mu}{p}} \sin\nu \\
|
||||||
```
|
v_y &= \sqrt{\frac{\mu}{p}} \left( e + \cos\nu \right)
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
再按近地点幅角、倾角和升交点赤经依次旋转。
|
再按近地点幅角、倾角和升交点赤经依次旋转。
|
||||||
|
|
||||||
|
|
@ -70,10 +74,9 @@ vy = sqrt(mu / p) (e + cos(nu))
|
||||||
|
|
||||||
关系为
|
关系为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
tan(nu / 2) = sqrt((1 + e) / (1 - e)) tan(E / 2)
|
\tan\frac{\nu}{2} = \sqrt{\frac{1 + e}{1 - e}} \tan\frac{E}{2}, \qquad M = E - e \sin E
|
||||||
M = E - e sin(E)
|
$$
|
||||||
```
|
|
||||||
|
|
||||||
`M = E - e sin(E)` 即开普勒方程。它是超越方程,因此由 `M` 求 `E` 需迭代;实现采用带固定迭代次数与容差的牛顿法,对地球轨道通常具有的偏心率收敛很快。该方程没有闭式解,声称有闭式解的实现都是在近似。
|
`M = E - e sin(E)` 即开普勒方程。它是超越方程,因此由 `M` 求 `E` 需迭代;实现采用带固定迭代次数与容差的牛顿法,对地球轨道通常具有的偏心率收敛很快。该方程没有闭式解,声称有闭式解的实现都是在近似。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@ gravity loss is a substantial fraction of the ideal cost.
|
||||||
The velocity a vehicle can produce depends on the exhaust velocity of its
|
The velocity a vehicle can produce depends on the exhaust velocity of its
|
||||||
propellant and on the mass ratio,
|
propellant and on the mass ratio,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_v = v_e * ln (m_0 / m_f)
|
\Delta v = v_e \ln\frac{m_0}{m_f}
|
||||||
```
|
$$
|
||||||
|
|
||||||
with `v_e` the exhaust velocity. Since a logarithmic function grows slowly,
|
with `v_e` the exhaust velocity. Since a logarithmic function grows slowly,
|
||||||
doubling the propellant gives far less than double the velocity. This is why
|
doubling the propellant gives far less than double the velocity. This is why
|
||||||
|
|
@ -52,16 +52,18 @@ circularises.
|
||||||
With `r_1`, `r_2` the two radii and `a_t = (r_1 + r_2) / 2` the transfer
|
With `r_1`, `r_2` the two radii and `a_t = (r_1 + r_2) / 2` the transfer
|
||||||
semi-major axis, the two burns follow from the vis-viva equation,
|
semi-major axis, the two burns follow from the vis-viva equation,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_v_1 = sqrt( mu (2/r_1 - 1/a_t) ) - sqrt( mu / r_1 )
|
\begin{aligned}
|
||||||
delta_v_2 = sqrt( mu / r_2 ) - sqrt( mu (2/r_2 - 1/a_t) )
|
\Delta v_1 &= \sqrt{\mu \left( \frac{2}{r_1} - \frac{1}{a_t} \right)} - \sqrt{\frac{\mu}{r_1}} \\
|
||||||
```
|
\Delta v_2 &= \sqrt{\frac{\mu}{r_2}} - \sqrt{\mu \left( \frac{2}{r_2} - \frac{1}{a_t} \right)}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
and the coast takes half the period of the transfer ellipse,
|
and the coast takes half the period of the transfer ellipse,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
t_coast = pi sqrt( a_t^3 / mu )
|
t_{\text{coast}} = \pi \sqrt{\frac{a_t^3}{\mu}}
|
||||||
```
|
$$
|
||||||
|
|
||||||
A raise from 300 km to geostationary costs about 4.1 km/s, which is a
|
A raise from 300 km to geostationary costs about 4.1 km/s, which is a
|
||||||
large fraction of what a launch vehicle can provide, and most of it is the
|
large fraction of what a launch vehicle can provide, and most of it is the
|
||||||
|
|
@ -76,9 +78,9 @@ radius change and a given inclination change is a two-burn sequence in which
|
||||||
the plane change is made at the smaller radius, where the velocity is lowest
|
the plane change is made at the smaller radius, where the velocity is lowest
|
||||||
and therefore the same angular rotation is cheapest in delta-v.
|
and therefore the same angular rotation is cheapest in delta-v.
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_v_plane ~ V * 2 sin( inclination change / 2 )
|
\Delta v_{\text{plane}} \sim 2 V \sin\frac{\Delta i}{2}
|
||||||
```
|
$$
|
||||||
|
|
||||||
with `V` the circular speed at the radius where the rotation is performed.
|
with `V` the circular speed at the radius where the rotation is performed.
|
||||||
|
|
||||||
|
|
@ -88,9 +90,9 @@ Over a finite burn the satellite is still moving against gravity and the
|
||||||
thrust does not act along the instantaneous velocity. The achieved velocity
|
thrust does not act along the instantaneous velocity. The achieved velocity
|
||||||
change is less than the ideal `a * t`, and the shortfall is the gravity loss,
|
change is less than the ideal `a * t`, and the shortfall is the gravity loss,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
gravity loss = | delta_v_ideal | - | delta_v_achieved |
|
L_{\text{gravity}} = \left\lvert \Delta v_{\text{ideal}} \right\rvert - \left\lvert \Delta v_{\text{achieved}} \right\rvert
|
||||||
```
|
$$
|
||||||
|
|
||||||
which grows with burn duration. A chemical burn is short enough to ignore it.
|
which grows with burn duration. A chemical burn is short enough to ignore it.
|
||||||
An electric thruster is not.
|
An electric thruster is not.
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ summary: 如何改变轨道、代价是多少推进剂,以及为什么两个
|
||||||
|
|
||||||
飞行器能产生的速度取决于推进剂的喷气速度和质量比:
|
飞行器能产生的速度取决于推进剂的喷气速度和质量比:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_v = v_e * ln (m_0 / m_f)
|
\Delta v = v_e \ln\frac{m_0}{m_f}
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `v_e` 为喷气速度。由于对数函数增长缓慢,推进剂加倍远不能使速度加倍。这就是任务的速度增量是设计首要约束的原因,也是分级存在的原因。
|
其中 `v_e` 为喷气速度。由于对数函数增长缓慢,推进剂加倍远不能使速度加倍。这就是任务的速度增量是设计首要约束的原因,也是分级存在的原因。
|
||||||
|
|
||||||
|
|
@ -34,16 +34,18 @@ delta_v = v_e * ln (m_0 / m_f)
|
||||||
|
|
||||||
设 `r_1`、`r_2` 为两个半径、`a_t = (r_1 + r_2) / 2` 为转移半长轴,两次点火由活力公式给出:
|
设 `r_1`、`r_2` 为两个半径、`a_t = (r_1 + r_2) / 2` 为转移半长轴,两次点火由活力公式给出:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_v_1 = sqrt( mu (2/r_1 - 1/a_t) ) - sqrt( mu / r_1 )
|
\begin{aligned}
|
||||||
delta_v_2 = sqrt( mu / r_2 ) - sqrt( mu (2/r_2 - 1/a_t) )
|
\Delta v_1 &= \sqrt{\mu \left( \frac{2}{r_1} - \frac{1}{a_t} \right)} - \sqrt{\frac{\mu}{r_1}} \\
|
||||||
```
|
\Delta v_2 &= \sqrt{\frac{\mu}{r_2}} - \sqrt{\mu \left( \frac{2}{r_2} - \frac{1}{a_t} \right)}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
滑行时间取转移椭圆周期的一半:
|
滑行时间取转移椭圆周期的一半:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
t_coast = pi sqrt( a_t^3 / mu )
|
t_{\text{coast}} = \pi \sqrt{\frac{a_t^3}{\mu}}
|
||||||
```
|
$$
|
||||||
|
|
||||||
从 300 km 转移到地球静止轨道约需 4.1 km/s,它占运载器可提供能力的很大一部分,且其中大部分来自第一次点火。
|
从 300 km 转移到地球静止轨道约需 4.1 km/s,它占运载器可提供能力的很大一部分,且其中大部分来自第一次点火。
|
||||||
|
|
||||||
|
|
@ -51,9 +53,9 @@ t_coast = pi sqrt( a_t^3 / mu )
|
||||||
|
|
||||||
同时改变倾角的转移比共面转移更贵。在同一次点火中既抬升远地点又转动轨道面的联合机动,代价高于单独做其中任一项。给定半径改变与倾角改变时,最优解是一个两次点火的序列,其中变轨面在较小半径处完成,因为该处速度最低,同样的角度转动在速度增量上最便宜。
|
同时改变倾角的转移比共面转移更贵。在同一次点火中既抬升远地点又转动轨道面的联合机动,代价高于单独做其中任一项。给定半径改变与倾角改变时,最优解是一个两次点火的序列,其中变轨面在较小半径处完成,因为该处速度最低,同样的角度转动在速度增量上最便宜。
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_v_plane ~ V * 2 sin( 倾角改变量 / 2 )
|
\Delta v_{\text{变轨面}} \sim 2 V \sin\frac{\Delta i}{2}
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `V` 是完成转动处半径上的圆轨道速度。
|
其中 `V` 是完成转动处半径上的圆轨道速度。
|
||||||
|
|
||||||
|
|
@ -61,9 +63,9 @@ delta_v_plane ~ V * 2 sin( 倾角改变量 / 2 )
|
||||||
|
|
||||||
在有限推力过程中,卫星仍在抵抗重力运动,且推力不沿瞬时速度方向。实际达到的速度增量小于理想的 `a * t`,差值即重力损失:
|
在有限推力过程中,卫星仍在抵抗重力运动,且推力不沿瞬时速度方向。实际达到的速度增量小于理想的 `a * t`,差值即重力损失:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
重力损失 = | 理想 delta_v | - | 实际 delta_v |
|
L_{\text{重力}} = \left\lvert \Delta v_{\text{理想}} \right\rvert - \left\lvert \Delta v_{\text{实际}} \right\rvert
|
||||||
```
|
$$
|
||||||
|
|
||||||
它随点火时长增长。化学推力点火足够短,可以忽略;电推进不行。
|
它随点火时长增长。化学推力点火足够短,可以忽略;电推进不行。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,26 +36,30 @@ the dominant long period effect.
|
||||||
|
|
||||||
The central gravity is
|
The central gravity is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a_central = -mu r / |r|^3
|
\mathbf{a}_{\text{central}} = -\frac{\mu \mathbf{r}}{\lvert \mathbf{r} \rvert^3}
|
||||||
```
|
$$
|
||||||
|
|
||||||
The `J2` term, which models the equatorial bulge, is
|
The `J2` term, which models the equatorial bulge, is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a_J2 = -1.5 J2 mu R^2 / r^5 *
|
\mathbf{a}_{J_2} = -\frac{3}{2} \frac{J_2 \mu R^2}{r^5}
|
||||||
[ x (1 - 5 z^2/r^2), y (1 - 5 z^2/r^2), z (3 - 5 z^2/r^2) ]
|
\begin{bmatrix} x \left( 1 - 5 z^2/r^2 \right) \\[2pt]
|
||||||
```
|
y \left( 1 - 5 z^2/r^2 \right) \\[2pt]
|
||||||
|
z \left( 3 - 5 z^2/r^2 \right) \end{bmatrix}
|
||||||
|
$$
|
||||||
|
|
||||||
with `R` the equatorial radius and `z` measured along the rotation axis. It
|
with `R` the equatorial radius and `z` measured along the rotation axis. It
|
||||||
has no component that changes the energy, so the size of the orbit is
|
has no component that changes the energy, so the size of the orbit is
|
||||||
unaffected to first order; what it changes is the orientation. Its secular
|
unaffected to first order; what it changes is the orientation. Its secular
|
||||||
effects are
|
effects are
|
||||||
|
|
||||||
```text
|
$$
|
||||||
dOmega/dt = -1.5 n J2 (R/a)^2 cos(i) / (1 - e^2)^2
|
\begin{aligned}
|
||||||
domega/dt = 0.75 n J2 (R/a)^2 (5 cos^2(i) - 1) / (1 - e^2)^2
|
\dot\Omega &= -\frac{3}{2} n J_2 \left( \frac{R}{a} \right)^2 \frac{\cos i}{\left( 1 - e^2 \right)^2} \\
|
||||||
```
|
\dot\omega &= \frac{3}{4} n J_2 \left( \frac{R}{a} \right)^2 \frac{5 \cos^2 i - 1}{\left( 1 - e^2 \right)^2}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
The first makes a low inclination orbit precess slowly westward and a polar
|
The first makes a low inclination orbit precess slowly westward and a polar
|
||||||
orbit precess in right ascension. Both are large enough to matter within a
|
orbit precess in right ascension. Both are large enough to matter within a
|
||||||
|
|
|
||||||
|
|
@ -28,23 +28,27 @@ summary: 使轨道偏离椭圆的各种力,以及各模型包含其中哪些
|
||||||
|
|
||||||
中心引力为
|
中心引力为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a_central = -mu r / |r|^3
|
\mathbf{a}_{\text{中心引力}} = -\frac{\mu \mathbf{r}}{\lvert \mathbf{r} \rvert^3}
|
||||||
```
|
$$
|
||||||
|
|
||||||
描述赤道隆起的一阶带谐项 `J2` 为
|
描述赤道隆起的一阶带谐项 `J2` 为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a_J2 = -1.5 J2 mu R^2 / r^5 *
|
\mathbf{a}_{J_2} = -\frac{3}{2} \frac{J_2 \mu R^2}{r^5}
|
||||||
[ x (1 - 5 z^2/r^2), y (1 - 5 z^2/r^2), z (3 - 5 z^2/r^2) ]
|
\begin{bmatrix} x \left( 1 - 5 z^2/r^2 \right) \\[2pt]
|
||||||
```
|
y \left( 1 - 5 z^2/r^2 \right) \\[2pt]
|
||||||
|
z \left( 3 - 5 z^2/r^2 \right) \end{bmatrix}
|
||||||
|
$$
|
||||||
|
|
||||||
其中 `R` 是赤道半径,`z` 沿自转轴量取。它没有改变能量的分量,因此一阶上不影响轨道大小,改变的是轨道朝向。其长期效应为
|
其中 `R` 是赤道半径,`z` 沿自转轴量取。它没有改变能量的分量,因此一阶上不影响轨道大小,改变的是轨道朝向。其长期效应为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
dOmega/dt = -1.5 n J2 (R/a)^2 cos(i) / (1 - e^2)^2
|
\begin{aligned}
|
||||||
domega/dt = 0.75 n J2 (R/a)^2 (5 cos^2(i) - 1) / (1 - e^2)^2
|
\dot\Omega &= -\frac{3}{2} n J_2 \left( \frac{R}{a} \right)^2 \frac{\cos i}{\left( 1 - e^2 \right)^2} \\
|
||||||
```
|
\dot\omega &= \frac{3}{4} n J_2 \left( \frac{R}{a} \right)^2 \frac{5 \cos^2 i - 1}{\left( 1 - e^2 \right)^2}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
前者使小倾角轨道缓慢向西进动,使极轨轨道在赤经上进动。两者都大到在一天之内即可产生影响。
|
前者使小倾角轨道缓慢向西进动,使极轨轨道在赤经上进动。两者都大到在一天之内即可产生影响。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,14 +38,16 @@ The propagation begins by recovering the size and shape of the orbit from
|
||||||
the mean motion, which requires undoing the correction that was applied when
|
the mean motion, which requires undoing the correction that was applied when
|
||||||
the elements were fitted:
|
the elements were fitted:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a1 = (xke / n0)^(2/3)
|
\begin{aligned}
|
||||||
delta1 = 1.5 k2 (3 cos^2(i0) - 1) / (a1^2 (1 - e0^2)^(3/2))
|
a_1 &= \left( \frac{x_{ke}}{n_0} \right)^{2/3} \\
|
||||||
a0 = a1 (1 - delta1/3 - delta1^2 - 134/81 delta1^3)
|
\delta_1 &= \frac{3}{2} k_2 \frac{3 \cos^2 i_0 - 1}{a_1^2 \left( 1 - e_0^2 \right)^{3/2}} \\
|
||||||
delta0 = 1.5 k2 (3 cos^2(i0) - 1) / (a0^2 (1 - e0^2)^(3/2))
|
a_0 &= a_1 \left( 1 - \frac{\delta_1}{3} - \delta_1^2 - \frac{134}{81} \delta_1^3 \right) \\
|
||||||
n0' = n0 / (1 + delta0) (the un-Kozai mean motion)
|
\delta_0 &= \frac{3}{2} k_2 \frac{3 \cos^2 i_0 - 1}{a_0^2 \left( 1 - e_0^2 \right)^{3/2}} \\
|
||||||
a0'' = a0 / (1 - delta0)
|
n_0' &= \frac{n_0}{1 + \delta_0} && \text{(the un-Kozai mean motion)} \\
|
||||||
```
|
a_0'' &= \frac{a_0}{1 - \delta_0}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
The drag term `BSTAR` produces a secular decay of the semi-major axis and
|
The drag term `BSTAR` produces a secular decay of the semi-major axis and
|
||||||
the eccentricity, and the oblateness produces secular rates in the argument
|
the eccentricity, and the oblateness produces secular rates in the argument
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,16 @@ SGP4 意为简化一般摄动模型第 4 版。该模型是闭式解析传播:
|
||||||
|
|
||||||
传播首先从平均运动恢复轨道的大小与形状,这需要撤销拟合元素时所施加的改正:
|
传播首先从平均运动恢复轨道的大小与形状,这需要撤销拟合元素时所施加的改正:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a1 = (xke / n0)^(2/3)
|
\begin{aligned}
|
||||||
delta1 = 1.5 k2 (3 cos^2(i0) - 1) / (a1^2 (1 - e0^2)^(3/2))
|
a_1 &= \left( \frac{x_{ke}}{n_0} \right)^{2/3} \\
|
||||||
a0 = a1 (1 - delta1/3 - delta1^2 - 134/81 delta1^3)
|
\delta_1 &= \frac{3}{2} k_2 \frac{3 \cos^2 i_0 - 1}{a_1^2 \left( 1 - e_0^2 \right)^{3/2}} \\
|
||||||
delta0 = 1.5 k2 (3 cos^2(i0) - 1) / (a0^2 (1 - e0^2)^(3/2))
|
a_0 &= a_1 \left( 1 - \frac{\delta_1}{3} - \delta_1^2 - \frac{134}{81} \delta_1^3 \right) \\
|
||||||
n0' = n0 / (1 + delta0) (去 Kozai 平均运动)
|
\delta_0 &= \frac{3}{2} k_2 \frac{3 \cos^2 i_0 - 1}{a_0^2 \left( 1 - e_0^2 \right)^{3/2}} \\
|
||||||
a0'' = a0 / (1 - delta0)
|
n_0' &= \frac{n_0}{1 + \delta_0} && \text{(去 Kozai 平均运动)} \\
|
||||||
```
|
a_0'' &= \frac{a_0}{1 - \delta_0}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
阻力项 `BSTAR` 使半长轴与偏心率产生长期衰减,扁率使近地点幅角、赤经与平近点角产生长期变化率。随后传播由改正后的平近点角求出位置。
|
阻力项 `BSTAR` 使半长轴与偏心率产生长期衰减,扁率使近地点幅角、赤经与平近点角产生长期变化率。随后传播由改正后的平近点角求出位置。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@ order as `J_2` on a large satellite at low altitude.
|
||||||
|
|
||||||
The flux falls as the inverse square of the distance,
|
The flux falls as the inverse square of the distance,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
P(r) = P_1au * (1 au / r)^2
|
P(r) = P_{1\,\text{au}} \left( \frac{1\,\text{au}}{r} \right)^2
|
||||||
```
|
$$
|
||||||
|
|
||||||
with `P_1au = 4.56e-6 N/m^2` for total solar irradiance at 1 au.
|
with `P_1au = 4.56e-6 N/m^2` for total solar irradiance at 1 au.
|
||||||
|
|
||||||
|
|
@ -40,9 +40,9 @@ For a satellite with projected area `A`, mass `m`, and a coefficient
|
||||||
`C_R` that is 1 for absorption, 2 for perfect reflection and between for a
|
`C_R` that is 1 for absorption, 2 for perfect reflection and between for a
|
||||||
typical surface, the acceleration is
|
typical surface, the acceleration is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a_srp = C_R * P_1au * A / m * (1 au / r)^2
|
a_{\text{srp}} = C_R \frac{P_{1\,\text{au}} A}{m} \left( \frac{1\,\text{au}}{r} \right)^2
|
||||||
```
|
$$
|
||||||
|
|
||||||
The `A/m` ratio is the whole story. It ranges from about `0.002 m^2/kg` for a
|
The `A/m` ratio is the whole story. It ranges from about `0.002 m^2/kg` for a
|
||||||
large communications satellite to about `0.05` for a small cube of a
|
large communications satellite to about `0.05` for a small cube of a
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ summary: 受阳光照射的表面从光子动量获得的加速度、它为何
|
||||||
|
|
||||||
通量按距离平方反比衰减:
|
通量按距离平方反比衰减:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
P(r) = P_1au * (1 au / r)^2
|
P(r) = P_{1\,\text{au}} \left( \frac{1\,\text{au}}{r} \right)^2
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 1 au 处总太阳辐照度对应 `P_1au = 4.56e-6 N/m^2`。
|
其中 1 au 处总太阳辐照度对应 `P_1au = 4.56e-6 N/m^2`。
|
||||||
|
|
||||||
|
|
@ -30,9 +30,9 @@ P(r) = P_1au * (1 au / r)^2
|
||||||
|
|
||||||
对投影面积 `A`、质量 `m`、系数为 `C_R` 的卫星(完全吸收为 1,完全反射为 2,典型表面介于两者之间),加速度为
|
对投影面积 `A`、质量 `m`、系数为 `C_R` 的卫星(完全吸收为 1,完全反射为 2,典型表面介于两者之间),加速度为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a_srp = C_R * P_1au * A / m * (1 au / r)^2
|
a_{\text{srp}} = C_R \frac{P_{1\,\text{au}} A}{m} \left( \frac{1\,\text{au}}{r} \right)^2
|
||||||
```
|
$$
|
||||||
|
|
||||||
`A/m` 决定一切。它从大型通信卫星的约 `0.002 m^2/kg` 到立方卫星的约 `0.05`,相差 25 倍,完全归因于结构。
|
`A/m` 决定一切。它从大型通信卫星的约 `0.002 m^2/kg` 到立方卫星的约 `0.05`,相差 25 倍,完全归因于结构。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,10 @@ written as a sign, a mantissa without a decimal point, a sign for the
|
||||||
exponent, and a single exponent digit. The mantissa is read with a decimal
|
exponent, and a single exponent digit. The mantissa is read with a decimal
|
||||||
point in front of its first digit. So
|
point in front of its first digit. So
|
||||||
|
|
||||||
```text
|
| Written in the element set | Means |
|
||||||
12345-3 means 0.12345e-3
|
|----------------------------|-------|
|
||||||
-11606-4 means -0.11606e-4
|
| ` 12345-3` | `0.12345e-3` |
|
||||||
```
|
| `-11606-4` | `-0.11606e-4` |
|
||||||
|
|
||||||
**The eccentricity has an assumed decimal point.** The field `0006703` means
|
**The eccentricity has an assumed decimal point.** The field `0006703` means
|
||||||
`0.0006703`, not `6703`.
|
`0.0006703`, not `6703`.
|
||||||
|
|
@ -80,9 +80,9 @@ absent term.
|
||||||
The checksum of a line is the sum of the decimal digits on the line, plus
|
The checksum of a line is the sum of the decimal digits on the line, plus
|
||||||
one for each minus sign, reduced modulo ten:
|
one for each minus sign, reduced modulo ten:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
checksum = (sum of digits + number of minus signs) mod 10
|
\text{checksum} = \left( \text{sum of digits} + \text{number of minus signs} \right) \bmod 10
|
||||||
```
|
$$
|
||||||
|
|
||||||
Only the first 68 columns contribute; the checksum occupies column 69 and is
|
Only the first 68 columns contribute; the checksum occupies column 69 and is
|
||||||
not part of its own sum. For the example line 1 the sum is 167 and the
|
not part of its own sum. For the example line 1 the sum is 167 and the
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,10 @@ ISS (ZARYA)
|
||||||
|
|
||||||
**指数字段带有隐含小数点。** 指数字段写作:尾数符号、不含小数点的尾数、指数符号、一位指数数字。尾数的读取方式是在其第一位数字前放置小数点。因此
|
**指数字段带有隐含小数点。** 指数字段写作:尾数符号、不含小数点的尾数、指数符号、一位指数数字。尾数的读取方式是在其第一位数字前放置小数点。因此
|
||||||
|
|
||||||
```text
|
| 元素集中的写法 | 含义 |
|
||||||
12345-3 表示 0.12345e-3
|
|----------------|------|
|
||||||
-11606-4 表示 -0.11606e-4
|
| ` 12345-3` | `0.12345e-3` |
|
||||||
```
|
| `-11606-4` | `-0.11606e-4` |
|
||||||
|
|
||||||
**偏心率带有隐含小数点。** 字段 `0006703` 表示 `0.0006703`,不是 `6703`。
|
**偏心率带有隐含小数点。** 字段 `0006703` 表示 `0.0006703`,不是 `6703`。
|
||||||
|
|
||||||
|
|
@ -68,9 +68,9 @@ ISS (ZARYA)
|
||||||
|
|
||||||
一行的校验和是该行十进制数字之和,加上每个负号计一,再对十取模:
|
一行的校验和是该行十进制数字之和,加上每个负号计一,再对十取模:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
checksum = (数字之和 + 负号个数) mod 10
|
\text{校验和} = \left( \text{数字之和} + \text{负号个数} \right) \bmod 10
|
||||||
```
|
$$
|
||||||
|
|
||||||
只有前 68 列参与;校验位占据第 69 列,不计入自身的和。示例第 1 行的和为 167,校验位为 7。
|
只有前 68 列参与;校验位占据第 69 列,不计入自身的和。示例第 1 行的和为 167,校验位为 7。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ document gives both.
|
||||||
- **Modified Julian Date (MJD)** counts days from midnight on 1858-11-17.
|
- **Modified Julian Date (MJD)** counts days from midnight on 1858-11-17.
|
||||||
- The two differ by a fixed offset:
|
- The two differ by a fixed offset:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
JD = MJD + 2400000.5
|
\text{JD} = \text{MJD} + 2400000.5
|
||||||
```
|
$$
|
||||||
|
|
||||||
The half day is the difference between the noon start of the Julian Date and
|
The half day is the difference between the noon start of the Julian Date and
|
||||||
the midnight start of the Modified Julian Date. The Modified Julian Date is
|
the midnight start of the Modified Julian Date. The Modified Julian Date is
|
||||||
|
|
@ -37,9 +37,9 @@ representation.
|
||||||
A year is a leap year when it is divisible by four, except that a year
|
A year is a leap year when it is divisible by four, except that a year
|
||||||
divisible by 100 is not a leap year unless it is also divisible by 400:
|
divisible by 100 is not a leap year unless it is also divisible by 400:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
leap(y) = (y mod 4 == 0) and (y mod 100 != 0 or y mod 400 == 0)
|
\text{leap}(y) = (y \bmod 4 = 0) \land (y \bmod 100 \ne 0 \lor y \bmod 400 = 0)
|
||||||
```
|
$$
|
||||||
|
|
||||||
So 2000 is a leap year and 1900 is not.
|
So 2000 is a leap year and 1900 is not.
|
||||||
|
|
||||||
|
|
@ -50,13 +50,16 @@ arithmetic so that it is exact for every representable date. The algorithm
|
||||||
shifts the year so that it begins in March, which places the leap day at the
|
shifts the year so that it begins in March, which places the leap day at the
|
||||||
end of the shifted year and removes the special case from the day count.
|
end of the shifted year and removes the special case from the day count.
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a = floor((14 - month) / 12)
|
\begin{aligned}
|
||||||
y = year + 4800 - a
|
a &= \left\lfloor \frac{14 - \text{month}}{12} \right\rfloor \\
|
||||||
m = month + 12 a - 3
|
y &= \text{year} + 4800 - a \\
|
||||||
JDN = day + floor((153 m + 2) / 5) + 365 y
|
m &= \text{month} + 12a - 3 \\
|
||||||
+ floor(y / 4) - floor(y / 100) + floor(y / 400) - 32045
|
\text{JDN} &= \text{day} + \left\lfloor \frac{153m + 2}{5} \right\rfloor + 365y
|
||||||
```
|
+ \left\lfloor \frac{y}{4} \right\rfloor - \left\lfloor \frac{y}{100} \right\rfloor
|
||||||
|
+ \left\lfloor \frac{y}{400} \right\rfloor - 32045
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
The inverse uses the same structure with a shift of 32044. Both directions
|
The inverse uses the same structure with a shift of 32044. Both directions
|
||||||
are implemented once and tested against each other over a wide range of
|
are implemented once and tested against each other over a wide range of
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ summary: 日编号系统、外推格里高利历,以及转换算法。
|
||||||
- **约化儒略日(MJD)** 自 1858-11-17 午夜起算。
|
- **约化儒略日(MJD)** 自 1858-11-17 午夜起算。
|
||||||
- 二者相差一个固定偏移:
|
- 二者相差一个固定偏移:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
JD = MJD + 2400000.5
|
\text{JD} = \text{MJD} + 2400000.5
|
||||||
```
|
$$
|
||||||
|
|
||||||
半天的差异来自儒略日以正午起始而约化儒略日以午夜起始。此处使用约化儒略日,因为它使日编号保持在一个日的小数部分便于处理的范围内。
|
半天的差异来自儒略日以正午起始而约化儒略日以午夜起始。此处使用约化儒略日,因为它使日编号保持在一个日的小数部分便于处理的范围内。
|
||||||
|
|
||||||
|
|
@ -26,9 +26,9 @@ JD = MJD + 2400000.5
|
||||||
|
|
||||||
闰年规则为:能被 4 整除的年份是闰年,但能被 100 整除的年份不是,除非它也能被 400 整除:
|
闰年规则为:能被 4 整除的年份是闰年,但能被 100 整除的年份不是,除非它也能被 400 整除:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
leap(y) = (y mod 4 == 0) and (y mod 100 != 0 or y mod 400 == 0)
|
\text{闰年}(y) = (y \bmod 4 = 0) \land (y \bmod 100 \ne 0 \lor y \bmod 400 = 0)
|
||||||
```
|
$$
|
||||||
|
|
||||||
因此 2000 年是闰年,1900 年不是。
|
因此 2000 年是闰年,1900 年不是。
|
||||||
|
|
||||||
|
|
@ -36,13 +36,16 @@ leap(y) = (y mod 4 == 0) and (y mod 100 != 0 or y mod 400 == 0)
|
||||||
|
|
||||||
日历日期与日编号之间的双向转换使用整数运算,因此对每个可表示的日期都是精确的。算法把年份起点移到三月,使闰日落在调整后年份的末尾,从而从日计数中去掉特殊情况。
|
日历日期与日编号之间的双向转换使用整数运算,因此对每个可表示的日期都是精确的。算法把年份起点移到三月,使闰日落在调整后年份的末尾,从而从日计数中去掉特殊情况。
|
||||||
|
|
||||||
```text
|
$$
|
||||||
a = floor((14 - month) / 12)
|
\begin{aligned}
|
||||||
y = year + 4800 - a
|
a &= \left\lfloor \frac{14 - \text{月}}{12} \right\rfloor \\
|
||||||
m = month + 12 a - 3
|
y &= \text{年} + 4800 - a \\
|
||||||
JDN = day + floor((153 m + 2) / 5) + 365 y
|
m &= \text{月} + 12a - 3 \\
|
||||||
+ floor(y / 4) - floor(y / 100) + floor(y / 400) - 32045
|
\text{JDN} &= \text{日} + \left\lfloor \frac{153m + 2}{5} \right\rfloor + 365y
|
||||||
```
|
+ \left\lfloor \frac{y}{4} \right\rfloor - \left\lfloor \frac{y}{100} \right\rfloor
|
||||||
|
+ \left\lfloor \frac{y}{400} \right\rfloor - 32045
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
逆变换采用相同结构,偏移量为 32044。两个方向各实现一次,并在很宽的日期范围内相互校验。
|
逆变换采用相同结构,偏移量为 32044。两个方向各实现一次,并在很宽的日期范围内相互校验。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ It is a function of UT1, the time scale defined by the rotation of the Earth.
|
||||||
The angle is computed from the interval in days of UT1 since the J2000.0
|
The angle is computed from the interval in days of UT1 since the J2000.0
|
||||||
epoch by a linear expression:
|
epoch by a linear expression:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
theta = 0.779 057 273 2640 + 1.002 737 909 350 795 * d
|
\theta = 0.779\,057\,273\,2640 + 1.002\,737\,909\,350\,795 \, d
|
||||||
```
|
$$
|
||||||
|
|
||||||
where `d` is the number of days of UT1 since 2000-01-01 12:00 UT1, and the
|
where `d` is the number of days of UT1 since 2000-01-01 12:00 UT1, and the
|
||||||
result is in turns. The constant term is the angle at the epoch itself,
|
result is in turns. The constant term is the angle at the epoch itself,
|
||||||
|
|
@ -42,10 +42,10 @@ when the interval is longer than a day. A caller that needs an angle in
|
||||||
|
|
||||||
The rotation between the frames is a clockwise turn about the third axis:
|
The rotation between the frames is a clockwise turn about the third axis:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
R3(theta) : inertial -> Earth-fixed
|
R_3(\theta): \text{inertial} \to \text{Earth-fixed}, \qquad
|
||||||
R3(theta)^T : Earth-fixed -> inertial
|
R_3(\theta)^{\mathsf{T}}: \text{Earth-fixed} \to \text{inertial}
|
||||||
```
|
$$
|
||||||
|
|
||||||
## Unit convention
|
## Unit convention
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ summary: 惯性系与地固系之间的夹角,以及给出该值的线性表
|
||||||
|
|
||||||
该角由自 J2000.0 历元起 UT1 的天数经线性表达式给出:
|
该角由自 J2000.0 历元起 UT1 的天数经线性表达式给出:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
theta = 0.779 057 273 2640 + 1.002 737 909 350 795 * d
|
\theta = 0.779\,057\,273\,2640 + 1.002\,737\,909\,350\,795 \, d
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `d` 是自 2000-01-01 12:00 UT1 起 UT1 的天数,结果单位为圈。常数项是历元时刻的角,约 280.46 度。速率略大于每天一圈,因为地球在一个恒星日内转一圈,而恒星日短于太阳日。
|
其中 `d` 是自 2000-01-01 12:00 UT1 起 UT1 的天数,结果单位为圈。常数项是历元时刻的角,约 280.46 度。速率略大于每天一圈,因为地球在一个恒星日内转一圈,而恒星日短于太阳日。
|
||||||
|
|
||||||
|
|
@ -28,10 +28,10 @@ theta = 0.779 057 273 2640 + 1.002 737 909 350 795 * d
|
||||||
|
|
||||||
参考系之间的旋转是绕第三轴的顺时针转动:
|
参考系之间的旋转是绕第三轴的顺时针转动:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
R3(theta) : 惯性系 -> 地固系
|
R_3(\theta): \text{惯性系} \to \text{地固系}, \qquad
|
||||||
R3(theta)^T : 地固系 -> 惯性系
|
R_3(\theta)^{\mathsf{T}}: \text{地固系} \to \text{惯性系}
|
||||||
```
|
$$
|
||||||
|
|
||||||
## 单位约定
|
## 单位约定
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,18 +43,20 @@ degrees at mid latitudes, which is about 21 kilometers on the ground.
|
||||||
|
|
||||||
The conversion from geodetic to Cartesian coordinates is closed form. With
|
The conversion from geodetic to Cartesian coordinates is closed form. With
|
||||||
|
|
||||||
```text
|
$$
|
||||||
e^2 = 2 f - f^2
|
e^2 = 2f - f^2, \qquad
|
||||||
N = a / sqrt(1 - e^2 sin^2 phi) (prime vertical radius of curvature)
|
N = \frac{a}{\sqrt{1 - e^2 \sin^2 \phi}}
|
||||||
```
|
$$
|
||||||
|
|
||||||
the Cartesian coordinates in the Earth-fixed frame are
|
the Cartesian coordinates in the Earth-fixed frame are
|
||||||
|
|
||||||
```text
|
$$
|
||||||
x = (N + h) cos(phi) cos(lambda)
|
\begin{aligned}
|
||||||
y = (N + h) cos(phi) sin(lambda)
|
x &= (N + h) \cos\phi \cos\lambda \\
|
||||||
z = (N (1 - e^2) + h) sin(phi)
|
y &= (N + h) \cos\phi \sin\lambda \\
|
||||||
```
|
z &= \left( N (1 - e^2) + h \right) \sin\phi
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
`N` is the distance from the point to the rotation axis measured in the
|
`N` is the distance from the point to the rotation axis measured in the
|
||||||
equatorial plane for a point at zero height. It grows from `a` at the
|
equatorial plane for a point at zero height. It grows from `a` at the
|
||||||
|
|
@ -69,9 +71,9 @@ axis, because no longitude is defined there.
|
||||||
|
|
||||||
The meridian radius of curvature, in the plane of a meridian, is
|
The meridian radius of curvature, in the plane of a meridian, is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
M = a (1 - e^2) / (1 - e^2 sin^2 phi)^(3/2)
|
M = \frac{a (1 - e^2)}{\left(1 - e^2 \sin^2 \phi\right)^{3/2}}
|
||||||
```
|
$$
|
||||||
|
|
||||||
It is the radius of curvature of the ellipsoid in the north-south direction
|
It is the radius of curvature of the ellipsoid in the north-south direction
|
||||||
at that latitude. It appears in geodetic computations that integrate along a
|
at that latitude. It appears in geodetic computations that integrate along a
|
||||||
|
|
|
||||||
|
|
@ -34,18 +34,20 @@ WGS 84 椭球是由两个数定义的旋转扁椭球:
|
||||||
|
|
||||||
从大地坐标到直角坐标是闭式的。令
|
从大地坐标到直角坐标是闭式的。令
|
||||||
|
|
||||||
```text
|
$$
|
||||||
e^2 = 2 f - f^2
|
e^2 = 2f - f^2, \qquad
|
||||||
N = a / sqrt(1 - e^2 sin^2 phi) (卯酉圈曲率半径)
|
N = \frac{a}{\sqrt{1 - e^2 \sin^2 \phi}}
|
||||||
```
|
$$
|
||||||
|
|
||||||
则地固系中的直角坐标为
|
则地固系中的直角坐标为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
x = (N + h) cos(phi) cos(lambda)
|
\begin{aligned}
|
||||||
y = (N + h) cos(phi) sin(lambda)
|
x &= (N + h) \cos\phi \cos\lambda \\
|
||||||
z = (N (1 - e^2) + h) sin(phi)
|
y &= (N + h) \cos\phi \sin\lambda \\
|
||||||
```
|
z &= \left( N (1 - e^2) + h \right) \sin\phi
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
`N` 是零高点在赤道面内到自转轴的距离,从赤道的 `a` 增至极点的 `a / sqrt(1 - e^2)`。
|
`N` 是零高点在赤道面内到自转轴的距离,从赤道的 `a` 增至极点的 `a / sqrt(1 - e^2)`。
|
||||||
|
|
||||||
|
|
@ -53,9 +55,9 @@ z = (N (1 - e^2) + h) sin(phi)
|
||||||
|
|
||||||
子午圈曲率半径(在子午面内)为
|
子午圈曲率半径(在子午面内)为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
M = a (1 - e^2) / (1 - e^2 sin^2 phi)^(3/2)
|
M = \frac{a (1 - e^2)}{\left(1 - e^2 \sin^2 \phi\right)^{3/2}}
|
||||||
```
|
$$
|
||||||
|
|
||||||
它是在该纬度处椭球沿南北方向的曲率半径,出现在沿子午线积分的计算中。
|
它是在该纬度处椭球沿南北方向的曲率半径,出现在沿子午线积分的计算中。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ A leap second is a one second adjustment to UTC announced by the
|
||||||
International Earth Rotation and Reference Systems Service in a Bulletin C
|
International Earth Rotation and Reference Systems Service in a Bulletin C
|
||||||
notice. The adjustment keeps
|
notice. The adjustment keeps
|
||||||
|
|
||||||
```text
|
$$
|
||||||
| UT1 - UTC | < 0.9 s
|
\lvert \text{UT1} - \text{UTC} \rvert < 0.9\,\text{s}
|
||||||
```
|
$$
|
||||||
|
|
||||||
UT1 is the scale defined by the rotation of the Earth. The Earth's rotation
|
UT1 is the scale defined by the rotation of the Earth. The Earth's rotation
|
||||||
rate is not constant, so the number of seconds that have elapsed since an
|
rate is not constant, so the number of seconds that have elapsed since an
|
||||||
|
|
@ -30,18 +30,20 @@ close the gap.
|
||||||
|
|
||||||
The offset between TAI and UTC is a step function of the date:
|
The offset between TAI and UTC is a step function of the date:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
DTAI(date) = 10 s + N(date)
|
\Delta\text{TAI}(t) = 10\,\text{s} + N(t)
|
||||||
```
|
$$
|
||||||
|
|
||||||
where `N(date)` is the number of leap seconds inserted between 1972-01-01
|
where `N(date)` is the number of leap seconds inserted between 1972-01-01
|
||||||
and the date. The kernel stores an instant as TAI, so the offset is applied
|
and the date. The kernel stores an instant as TAI, so the offset is applied
|
||||||
only when an instant is converted to or from UTC:
|
only when an instant is converted to or from UTC:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
tai_mjd = utc_mjd + DTAI(utc_mjd) / 86400
|
\begin{aligned}
|
||||||
utc_mjd = tai_mjd - DTAI(utc_mjd) / 86400
|
\text{tai} &= \text{utc} + \frac{\Delta\text{TAI}(\text{utc})}{86400} \\
|
||||||
```
|
\text{utc} &= \text{tai} - \frac{\Delta\text{TAI}(\text{utc})}{86400}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
The second relation needs the offset at the UTC instant, which is what is
|
The second relation needs the offset at the UTC instant, which is what is
|
||||||
being computed, so the table is searched for the interval that contains the
|
being computed, so the table is searched for the interval that contains the
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ UTC 通过插入或删除整秒来保持与地球自转接近,而地球自转
|
||||||
|
|
||||||
闰秒是国际地球自转与参考系统服务在 Bulletin C 公告中宣布的对 UTC 的一秒调整。该调整使
|
闰秒是国际地球自转与参考系统服务在 Bulletin C 公告中宣布的对 UTC 的一秒调整。该调整使
|
||||||
|
|
||||||
```text
|
$$
|
||||||
| UT1 - UTC | < 0.9 s
|
\lvert \text{UT1} - \text{UTC} \rvert < 0.9\,\text{s}
|
||||||
```
|
$$
|
||||||
|
|
||||||
UT1 是由地球自转定义的尺度。地球自转速率并非常数,因此自某历元起经过的秒数与地球转过的角度不会保持同步。闰秒用于弥合这一差距。
|
UT1 是由地球自转定义的尺度。地球自转速率并非常数,因此自某历元起经过的秒数与地球转过的角度不会保持同步。闰秒用于弥合这一差距。
|
||||||
|
|
||||||
|
|
@ -22,16 +22,18 @@ UT1 是由地球自转定义的尺度。地球自转速率并非常数,因此
|
||||||
|
|
||||||
TAI 与 UTC 之间的偏移是日期的阶跃函数:
|
TAI 与 UTC 之间的偏移是日期的阶跃函数:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
DTAI(date) = 10 s + N(date)
|
\Delta\text{TAI}(t) = 10\,\text{s} + N(t)
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `N(date)` 是 1972-01-01 至该日期之间插入的闰秒数。内核以 TAI 存储时刻,因此该偏移只在时刻与 UTC 之间转换时应用:
|
其中 `N(date)` 是 1972-01-01 至该日期之间插入的闰秒数。内核以 TAI 存储时刻,因此该偏移只在时刻与 UTC 之间转换时应用:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
tai_mjd = utc_mjd + DTAI(utc_mjd) / 86400
|
\begin{aligned}
|
||||||
utc_mjd = tai_mjd - DTAI(utc_mjd) / 86400
|
\text{tai} &= \text{utc} + \frac{\Delta\text{TAI}(\text{utc})}{86400} \\
|
||||||
```
|
\text{utc} &= \text{tai} - \frac{\Delta\text{TAI}(\text{utc})}{86400}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
第二个关系式需要在 UTC 时刻处的偏移,而该时刻正是待求量,因此查找表中包含近似时刻的区间,并校验结果的自洽性。实现通过定位适用的表项来完成。
|
第二个关系式需要在 UTC 时刻处的偏移,而该时刻正是待求量,因此查找表中包含近似时刻的区间,并校验结果的自洽性。实现通过定位适用的表项来完成。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,18 +33,15 @@ another is expected. A conversion is an explicit method call.
|
||||||
The transformation between the inertial and Earth-fixed frames is a
|
The transformation between the inertial and Earth-fixed frames is a
|
||||||
rotation about the third axis by the Earth Rotation Angle:
|
rotation about the third axis by the Earth Rotation Angle:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
r_ecef = R3(theta) r_eci
|
\mathbf{r}_{\text{ecef}} = R_3(\theta) \, \mathbf{r}_{\text{eci}}, \qquad \mathbf{r}_{\text{eci}} = R_3(\theta)^{\mathsf{T}} \mathbf{r}_{\text{ecef}}
|
||||||
r_eci = R3(theta)^T r_ecef
|
$$
|
||||||
```
|
|
||||||
|
|
||||||
where `theta` is the Earth Rotation Angle and `R3` is the rotation matrix
|
where `theta` is the Earth Rotation Angle and `R3` is the rotation matrix
|
||||||
|
|
||||||
```text
|
$$
|
||||||
R3(theta) = [ cos(theta) sin(theta) 0
|
R_3(\theta) = \begin{bmatrix} \cos\theta & \sin\theta & 0 \\ -\sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix}
|
||||||
-sin(theta) cos(theta) 0
|
$$
|
||||||
0 0 1 ]
|
|
||||||
```
|
|
||||||
|
|
||||||
The rotation is orthogonal, so it preserves lengths and angles. This is a
|
The rotation is orthogonal, so it preserves lengths and angles. This is a
|
||||||
useful check: a conversion that changes the length of a vector by more than
|
useful check: a conversion that changes the length of a vector by more than
|
||||||
|
|
|
||||||
|
|
@ -24,18 +24,15 @@ summary: 惯性系与地固系、在类型中携带参考系的位置表示,
|
||||||
|
|
||||||
惯性系与地固系之间的变换是绕第三轴旋转地球自转角:
|
惯性系与地固系之间的变换是绕第三轴旋转地球自转角:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
r_ecef = R3(theta) r_eci
|
\mathbf{r}_{\text{ecef}} = R_3(\theta) \, \mathbf{r}_{\text{eci}}, \qquad \mathbf{r}_{\text{eci}} = R_3(\theta)^{\mathsf{T}} \mathbf{r}_{\text{ecef}}
|
||||||
r_eci = R3(theta)^T r_ecef
|
$$
|
||||||
```
|
|
||||||
|
|
||||||
其中 `theta` 是地球自转角,`R3` 是旋转矩阵
|
其中 `theta` 是地球自转角,`R3` 是旋转矩阵
|
||||||
|
|
||||||
```text
|
$$
|
||||||
R3(theta) = [ cos(theta) sin(theta) 0
|
R_3(\theta) = \begin{bmatrix} \cos\theta & \sin\theta & 0 \\ -\sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix}
|
||||||
-sin(theta) cos(theta) 0
|
$$
|
||||||
0 0 1 ]
|
|
||||||
```
|
|
||||||
|
|
||||||
该旋转是正交的,因此保持长度与夹角不变。这是一个有用的校验:某次转换使矢量长度变化超过舍入误差就说明有误。
|
该旋转是正交的,因此保持长度与夹角不变。这是一个有用的校验:某次转换使矢量长度变化超过舍入误差就说明有误。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ back to whatever scale is asked for when it is displayed.
|
||||||
|
|
||||||
The reason is exactness. Two instants stored on TAI differ by
|
The reason is exactness. Two instants stored on TAI differ by
|
||||||
|
|
||||||
```text
|
$$
|
||||||
seconds = (tai_mjd_later - tai_mjd_earlier) * 86400
|
\Delta t = \left( \text{tai}_{\text{later}} - \text{tai}_{\text{earlier}} \right) \times 86400
|
||||||
```
|
$$
|
||||||
|
|
||||||
and that difference is the elapsed SI time between them, whatever scales
|
and that difference is the elapsed SI time between them, whatever scales
|
||||||
they were created in. If an instant were stored on UTC, the same subtraction
|
they were created in. If an instant were stored on UTC, the same subtraction
|
||||||
|
|
@ -45,11 +45,13 @@ not have a single well defined numeric value at that point.
|
||||||
|
|
||||||
The conversions between scales are offsets except for UTC:
|
The conversions between scales are offsets except for UTC:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
TT - TAI = 32.184 s (constant, IAU 2000)
|
\begin{aligned}
|
||||||
TAI - GPS = 19 s (constant since 1980)
|
\text{TT} - \text{TAI} &= 32.184\,\text{s} && \text{(constant, IAU 2000)} \\
|
||||||
TAI - UTC = 10 s + (number of inserted leap seconds since 1972)
|
\text{TAI} - \text{GPS} &= 19\,\text{s} && \text{(constant since 1980)} \\
|
||||||
```
|
\text{TAI} - \text{UTC} &= 10\,\text{s} + N && \text{(N leap seconds since 1972)}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
The first two are constants. The third is a step function of the date, and
|
The first two are constants. The third is a step function of the date, and
|
||||||
its value comes from the leap second table in `apsis-leap`, which is the
|
its value comes from the leap second table in `apsis-leap`, which is the
|
||||||
|
|
|
||||||
|
|
@ -25,19 +25,21 @@ UTC 是此处唯一不均匀的尺度。它通过插入或删除整秒保持在
|
||||||
|
|
||||||
原因在于精确性。两个以 TAI 存储的时刻之差为
|
原因在于精确性。两个以 TAI 存储的时刻之差为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
seconds = (tai_mjd_later - tai_mjd_earlier) * 86400
|
\Delta t = \left( \text{tai}_{\text{后}} - \text{tai}_{\text{前}} \right) \times 86400
|
||||||
```
|
$$
|
||||||
|
|
||||||
该差就是二者之间经过的 SI 时间,无论它们是以何种尺度创建的。若时刻以 UTC 存储,同样的减法在跨越闰秒插入时会出错,因为 UTC 时钟在该点没有单一明确的数值。
|
该差就是二者之间经过的 SI 时间,无论它们是以何种尺度创建的。若时刻以 UTC 存储,同样的减法在跨越闰秒插入时会出错,因为 UTC 时钟在该点没有单一明确的数值。
|
||||||
|
|
||||||
尺度之间的转换除 UTC 外都是常数偏移:
|
尺度之间的转换除 UTC 外都是常数偏移:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
TT - TAI = 32.184 s (常数,IAU 2000)
|
\begin{aligned}
|
||||||
TAI - GPS = 19 s (自 1980 年起为常数)
|
\text{TT} - \text{TAI} &= 32.184\,\text{s} && \text{(常数,IAU 2000)} \\
|
||||||
TAI - UTC = 10 s + (1972 年以来插入的闰秒数)
|
\text{TAI} - \text{GPS} &= 19\,\text{s} && \text{(自 1980 年起为常数)} \\
|
||||||
```
|
\text{TAI} - \text{UTC} &= 10\,\text{s} + N && \text{(1972 年以来插入的闰秒数)}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
前两项是常数。第三项是日期的阶跃函数,其数值来自 `apsis-leap` 中的闰秒表,即 IERS Bulletin C 发布的表。
|
前两项是常数。第三项是日期的阶跃函数,其数值来自 `apsis-leap` 中的闰秒表,即 IERS Bulletin C 发布的表。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ over a pass.
|
||||||
Above about 10 GHz water vapour becomes significant, and above about 30 GHz
|
Above about 10 GHz water vapour becomes significant, and above about 30 GHz
|
||||||
it dominates. The specific attenuation for a given rain rate is
|
it dominates. The specific attenuation for a given rain rate is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
gamma = k * R^alpha / (cos theta)
|
\gamma = \frac{k R^{\alpha}}{\cos\theta}
|
||||||
```
|
$$
|
||||||
|
|
||||||
where `R` is the rain rate, `theta` the zenith angle, and `k` and `alpha`
|
where `R` is the rain rate, `theta` the zenith angle, and `k` and `alpha`
|
||||||
frequency-dependent. The `1 / cos theta` is why a low-elevation pass is
|
frequency-dependent. The `1 / cos theta` is why a low-elevation pass is
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ summary: 卫星与接收机之间的空气对信号做了什么,以及它在
|
||||||
|
|
||||||
约 10 GHz 以上水汽开始显著,约 30 GHz 以上它占主导。给定雨强的比衰减为
|
约 10 GHz 以上水汽开始显著,约 30 GHz 以上它占主导。给定雨强的比衰减为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
gamma = k * R^alpha / (cos theta)
|
\gamma = \frac{k R^{\alpha}}{\cos\theta}
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `R` 为雨强、`theta` 为天顶角,`k` 与 `alpha` 依赖频率。`1 / cos theta` 就是低仰角过境严重的原因:在地平线上十度处,穿过该层的路径约长六倍。
|
其中 `R` 为雨强、`theta` 为天顶角,`k` 与 `alpha` 依赖频率。`1 / cos theta` 就是低仰角过境严重的原因:在地平线上十度处,穿过该层的路径约长六倍。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ velocity.
|
||||||
For a satellite moving at `v_r` radially, at range `R`, the received
|
For a satellite moving at `v_r` radially, at range `R`, the received
|
||||||
frequency shift is
|
frequency shift is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_f / f = -v_r / c
|
\frac{\Delta f}{f} = -\frac{v_r}{c}
|
||||||
```
|
$$
|
||||||
|
|
||||||
approaching raises the frequency. The non-relativistic form is adequate for
|
approaching raises the frequency. The non-relativistic form is adequate for
|
||||||
orbital speeds; the relativistic correction is about one part in `10^13` and
|
orbital speeds; the relativistic correction is about one part in `10^13` and
|
||||||
|
|
@ -36,9 +36,9 @@ matters only for a clock discipline experiment.
|
||||||
A signal that travels to the satellite and back is shifted twice, so the
|
A signal that travels to the satellite and back is shifted twice, so the
|
||||||
observed fractional shift is
|
observed fractional shift is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_f / f = -2 * v_r / c
|
\frac{\Delta f}{f} = -\frac{2 v_r}{c}
|
||||||
```
|
$$
|
||||||
|
|
||||||
with `v_r` the range rate. The factor of two is why the same satellite
|
with `v_r` the range rate. The factor of two is why the same satellite
|
||||||
appears to shift twice as much when tracked in two-way mode, and a range
|
appears to shift twice as much when tracked in two-way mode, and a range
|
||||||
|
|
@ -48,9 +48,9 @@ model that ignores it has a factor-of-two error that is easy to miss.
|
||||||
|
|
||||||
Integrating the two-way shift gives the range,
|
Integrating the two-way shift gives the range,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
R(t) = R0 - integral 2 * c * v_r / f0 dt
|
R(t) = R_0 - \int \frac{2 c \, v_r}{f_0} \, dt
|
||||||
```
|
$$
|
||||||
|
|
||||||
which is why a tracking system measures range as the integral of a
|
which is why a tracking system measures range as the integral of a
|
||||||
frequency rather than as a timing. The accumulator drifts with any error in
|
frequency rather than as a timing. The accumulator drifts with any error in
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ summary: 卫星运动如何改变其被接收到的频率,以及该频移如
|
||||||
|
|
||||||
对沿径向以 `v_r` 运动、距离为 `R` 的卫星,接收频率的偏移为
|
对沿径向以 `v_r` 运动、距离为 `R` 的卫星,接收频率的偏移为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_f / f = -v_r / c
|
\frac{\Delta f}{f} = -\frac{v_r}{c}
|
||||||
```
|
$$
|
||||||
|
|
||||||
接近使频率升高。非相对论形式对轨道速度已足够;相对论改正约为 `10^-13` 量级,只在时钟驯服实验中才有意义。
|
接近使频率升高。非相对论形式对轨道速度已足够;相对论改正约为 `10^-13` 量级,只在时钟驯服实验中才有意义。
|
||||||
|
|
||||||
|
|
@ -26,9 +26,9 @@ delta_f / f = -v_r / c
|
||||||
|
|
||||||
往返各一次的信号被偏移两次,因此观测到的相对频移为
|
往返各一次的信号被偏移两次,因此观测到的相对频移为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
delta_f / f = -2 * v_r / c
|
\frac{\Delta f}{f} = -\frac{2 v_r}{c}
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `v_r` 为测距率。因子二就是同一颗卫星在双向跟踪下看起来频移加倍的原因,而忽略它的测距模型带有很容易漏掉的二倍误差。
|
其中 `v_r` 为测距率。因子二就是同一颗卫星在双向跟踪下看起来频移加倍的原因,而忽略它的测距模型带有很容易漏掉的二倍误差。
|
||||||
|
|
||||||
|
|
@ -36,9 +36,9 @@ delta_f / f = -2 * v_r / c
|
||||||
|
|
||||||
对双向频移积分即得距离:
|
对双向频移积分即得距离:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
R(t) = R0 - integral 2 * c * v_r / f0 dt
|
R(t) = R_0 - \int \frac{2 c \, v_r}{f_0} \, dt
|
||||||
```
|
$$
|
||||||
|
|
||||||
这就是跟踪系统把距离测成频率的积分而非测时的原因。累积器随参考频率的任何误差漂移,因此参考时钟是整条链路上的限制环节。
|
这就是跟踪系统把距离测成频率的积分而非测时的原因。累积器随参考频率的任何误差漂移,因此参考时钟是整条链路上的限制环节。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@ The usable quantity is the carrier to noise density ratio, written as
|
||||||
`C/N0`, which is the carrier power in a one hertz bandwidth over the noise
|
`C/N0`, which is the carrier power in a one hertz bandwidth over the noise
|
||||||
power in the same bandwidth.
|
power in the same bandwidth.
|
||||||
|
|
||||||
```text
|
$$
|
||||||
C/N0 = P_r * G / (k * T_system * F)
|
\frac{C}{N_0} = \frac{P_r G}{k T_{\text{system}} F}
|
||||||
```
|
$$
|
||||||
|
|
||||||
A pointing loss, a rain fade or a jammer each reduces it, and the range error
|
A pointing loss, a rain fade or a jammer each reduces it, and the range error
|
||||||
scales with the square root of it. A jammer that costs six decibels of
|
scales with the square root of it. A jammer that costs six decibels of
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ summary: 噪声与相干源的区别——前者逐步劣化测量,后者用
|
||||||
|
|
||||||
可用量是载噪密度比,记作 `C/N0`,即一赫兹带宽内的载波功率除以同带宽噪声功率:
|
可用量是载噪密度比,记作 `C/N0`,即一赫兹带宽内的载波功率除以同带宽噪声功率:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
C/N0 = P_r * G / (k * T_system * F)
|
\frac{C}{N_0} = \frac{P_r G}{k T_{\text{system}} F}
|
||||||
```
|
$$
|
||||||
|
|
||||||
指向损耗、雨衰落或干扰都会降低它,而测距误差按它的平方根标度。代价六分贝 `C/N0` 的干扰造成的测距精度损失远大于六分贝。
|
指向损耗、雨衰落或干扰都会降低它,而测距误差按它的平方根标度。代价六分贝 `C/N0` 的干扰造成的测距精度损失远大于六分贝。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ the only thing standing between a design and an outage.
|
||||||
|
|
||||||
The received power at the far end is
|
The received power at the far end is
|
||||||
|
|
||||||
```text
|
$$
|
||||||
P_r = EIRP * G_t / L
|
P_r = \frac{\text{EIRP} \, G_t}{L}
|
||||||
```
|
$$
|
||||||
|
|
||||||
with `L` the total propagation and implementation loss. The link closes when
|
with `L` the total propagation and implementation loss. The link closes when
|
||||||
`P_r` exceeds the sensitivity of the receiver, and the **link margin** is the
|
`P_r` exceeds the sensitivity of the receiver, and the **link margin** is the
|
||||||
|
|
@ -25,11 +25,13 @@ difference in decibels.
|
||||||
|
|
||||||
Every term is a ratio, so the budget is written in decibels throughout:
|
Every term is a ratio, so the budget is written in decibels throughout:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
EIRP = P_t + G_t - L_feeder
|
\begin{aligned}
|
||||||
P_r = EIRP + G_r - L_space - L_polarisation - L_pointing
|
\text{EIRP} &= P_t + G_t - L_{\text{feeder}} \\
|
||||||
margin = P_r - sensitivity
|
P_r &= \text{EIRP} + G_r - L_{\text{space}} - L_{\text{pol}} - L_{\text{point}} \\
|
||||||
```
|
\text{margin} &= P_r - \text{sensitivity}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
The two that decide the result are free space loss, which is fixed by
|
The two that decide the result are free space loss, which is fixed by
|
||||||
geometry, and the losses that are not: rain, pointing, polarisation
|
geometry, and the losses that are not: rain, pointing, polarisation
|
||||||
|
|
@ -42,15 +44,15 @@ mismatch.
|
||||||
The loss grows with the fourth power of distance and with the square of
|
The loss grows with the fourth power of distance and with the square of
|
||||||
frequency:
|
frequency:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
L_fs = (4 pi d f / c)^2
|
L_{\text{fs}} = \left( \frac{4 \pi d f}{c} \right)^2
|
||||||
```
|
$$
|
||||||
|
|
||||||
in linear units, or in decibels,
|
in linear units, or in decibels,
|
||||||
|
|
||||||
```text
|
$$
|
||||||
L_fs(dB) = 92.45 + 20 log10 d_km + 20 log10 f_GHz
|
L_{\text{fs,dB}} = 92.45 + 20 \log_{10} d_{\text{km}} + 20 \log_{10} f_{\text{GHz}}
|
||||||
```
|
$$
|
||||||
|
|
||||||
The fourth power is why doubling the frequency costs six decibels and
|
The fourth power is why doubling the frequency costs six decibels and
|
||||||
doubling the distance costs six. A constellation at 550 km versus one at
|
doubling the distance costs six. A constellation at 550 km versus one at
|
||||||
|
|
@ -63,9 +65,9 @@ three decibels.
|
||||||
An antenna receives a polarisation close to its own. A mismatch of an angle
|
An antenna receives a polarisation close to its own. A mismatch of an angle
|
||||||
`psi` costs
|
`psi` costs
|
||||||
|
|
||||||
```text
|
$$
|
||||||
L_pol = -log10( cos^2 psi ) dB
|
L_{\text{pol}} = -10 \log_{10} \left( \cos^2 \psi \right) \ \text{dB}
|
||||||
```
|
$$
|
||||||
|
|
||||||
which is small below a few degrees and large beyond thirty. A tilted
|
which is small below a few degrees and large beyond thirty. A tilted
|
||||||
heliostat or a rotating spacecraft body can put a link over this threshold
|
heliostat or a rotating spacecraft body can put a link over this threshold
|
||||||
|
|
|
||||||
|
|
@ -12,19 +12,21 @@ summary: 信号是否强到可用的算术,以及把宽裕设计变成不可
|
||||||
|
|
||||||
远端的接收功率为
|
远端的接收功率为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
P_r = EIRP * G_t / L
|
P_r = \frac{\text{EIRP} \, G_t}{L}
|
||||||
```
|
$$
|
||||||
|
|
||||||
其中 `L` 为全部传播与实现损耗。当 `P_r` 超过接收机灵敏度时链路闭合,**链路裕量**是以分贝计的差值。
|
其中 `L` 为全部传播与实现损耗。当 `P_r` 超过接收机灵敏度时链路闭合,**链路裕量**是以分贝计的差值。
|
||||||
|
|
||||||
每一项都是比值,因此预算全程以分贝书写:
|
每一项都是比值,因此预算全程以分贝书写:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
EIRP = P_t + G_t - L_feeder
|
\begin{aligned}
|
||||||
P_r = EIRP + G_r - L_space - L_polarisation - L_pointing
|
\text{EIRP} &= P_t + G_t - L_{\text{馈线}} \\
|
||||||
裕量 = P_r - 灵敏度
|
P_r &= \text{EIRP} + G_r - L_{\text{空间}} - L_{\text{极化}} - L_{\text{指向}} \\
|
||||||
```
|
\text{裕量} &= P_r - \text{灵敏度}
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
决定结果的两项是自由空间损耗(由几何决定)与不是自由的那几项:雨、指向、极化失配。
|
决定结果的两项是自由空间损耗(由几何决定)与不是自由的那几项:雨、指向、极化失配。
|
||||||
|
|
||||||
|
|
@ -34,15 +36,15 @@ P_r = EIRP + G_r - L_space - L_polarisation - L_pointing
|
||||||
|
|
||||||
损耗随距离四次方与频率平方增长:
|
损耗随距离四次方与频率平方增长:
|
||||||
|
|
||||||
```text
|
$$
|
||||||
L_fs = (4 pi d f / c)^2
|
L_{\text{fs}} = \left( \frac{4 \pi d f}{c} \right)^2
|
||||||
```
|
$$
|
||||||
|
|
||||||
线性形式,或分贝形式
|
线性形式,或分贝形式
|
||||||
|
|
||||||
```text
|
$$
|
||||||
L_fs(dB) = 92.45 + 20 log10 d_km + 20 log10 f_GHz
|
L_{\text{fs,dB}} = 92.45 + 20 \log_{10} d_{\text{km}} + 20 \log_{10} f_{\text{GHz}}
|
||||||
```
|
$$
|
||||||
|
|
||||||
四次方就是频率翻倍要付出六分贝、距离翻倍也要六分贝的原因。550 km 的星座与 1100 km 的星座用同样设备少十二分贝裕量,超过发射功率翻倍所能补回的量,因为功率翻倍只有三分贝。
|
四次方就是频率翻倍要付出六分贝、距离翻倍也要六分贝的原因。550 km 的星座与 1100 km 的星座用同样设备少十二分贝裕量,超过发射功率翻倍所能补回的量,因为功率翻倍只有三分贝。
|
||||||
|
|
||||||
|
|
@ -50,9 +52,9 @@ L_fs(dB) = 92.45 + 20 log10 d_km + 20 log10 f_GHz
|
||||||
|
|
||||||
天线接收接近自身极化的信号。角度 `psi` 的失配代价为
|
天线接收接近自身极化的信号。角度 `psi` 的失配代价为
|
||||||
|
|
||||||
```text
|
$$
|
||||||
L_pol = -log10( cos^2 psi ) dB
|
L_{\text{pol}} = -10 \log_{10} \left( \cos^2 \psi \right) \ \text{dB}
|
||||||
```
|
$$
|
||||||
|
|
||||||
几度以内很小,三十度以外很大。倾斜的抛物面或转动的航天器本体可能在一次过境中把链路推过该门限,这是只出现在轨道某一段的常见中断原因。
|
几度以内很小,三十度以外很大。倾斜的抛物面或转动的航天器本体可能在一次过境中把链路推过该门限,这是只出现在轨道某一段的常见中断原因。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,33 @@ sentence that contains 坐标, which a whole-run token would not. A single
|
||||||
Chinese character is left to the substring fallback rather than becoming a
|
Chinese character is left to the substring fallback rather than becoming a
|
||||||
token, because a token of one character would match nearly every document.
|
token, because a token of one character would match nearly every document.
|
||||||
|
|
||||||
|
## Formulas
|
||||||
|
|
||||||
|
A formula is written between dollar signs, `$$...$$` for one on a line of its
|
||||||
|
own and `$...$` for one within a sentence, and the backend translates the
|
||||||
|
notation to MathML.
|
||||||
|
|
||||||
|
MathML was chosen over a script that lays out mathematics, because the browser
|
||||||
|
draws it itself: nothing has to run for a formula to appear, the output prints,
|
||||||
|
and the service keeps one runtime dependency.
|
||||||
|
|
||||||
|
The documents did not always write formulas this way. They were first written
|
||||||
|
as fenced code blocks, which is what a plain text file allows, and the
|
||||||
|
renderer was made to recognise a block that held equations. That was treating
|
||||||
|
the symptom: a code block is not the notation for mathematics, the reader of
|
||||||
|
the source sees a formula presented as code, and a renderer that guesses which
|
||||||
|
blocks are formulas is a renderer that guesses wrong. The blocks are now
|
||||||
|
written in the notation they should always have used, and the renderer reads
|
||||||
|
the one notation every Markdown mathematics tool reads.
|
||||||
|
|
||||||
|
Setting a formula is mostly an exercise in not losing a character. Text inside
|
||||||
|
a MathML row with no element around it is not drawn, so a token that is not
|
||||||
|
given an element disappears and leaves a formula that looks plausible and is
|
||||||
|
wrong. Every piece of text is therefore given the element its kind calls for,
|
||||||
|
and a test walks every formula in the documents and in the interface
|
||||||
|
catalog and compares the characters the page would show against the characters
|
||||||
|
the formula was written with.
|
||||||
|
|
||||||
## Graphs
|
## Graphs
|
||||||
|
|
||||||
One graph shape serves the knowledge graph, the crate structure, and the
|
One graph shape serves the knowledge graph, the crate structure, and the
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,25 @@ Markdown 渲染器转义每一个文本节点,只输出它自己生成的标
|
||||||
单位。查询「坐标」能匹配含有「坐标」的句子,而整串词元做不到。单个中文字符留给
|
单位。查询「坐标」能匹配含有「坐标」的句子,而整串词元做不到。单个中文字符留给
|
||||||
子串回退而不成为词元,因为一个字符的词元几乎会匹配每一篇文档。
|
子串回退而不成为词元,因为一个字符的词元几乎会匹配每一篇文档。
|
||||||
|
|
||||||
|
## 公式
|
||||||
|
|
||||||
|
公式写在美元符号之间:独占一行用 `$$...$$`,行内用 `$...$`;后端把该记法翻译为
|
||||||
|
MathML。
|
||||||
|
|
||||||
|
选择 MathML 而非用脚本排版数学,是因为浏览器自己就会绘制它:公式出现不需要运行
|
||||||
|
任何脚本,输出可打印,服务也保持只有一个运行时依赖。
|
||||||
|
|
||||||
|
文档并非一直都是这么写的。起初它们写成代码块——纯文本文件里能用的写法——而
|
||||||
|
渲染器被改成能认出「装着等式的代码块」。那是治标:代码块不是数学的记法,读源码的
|
||||||
|
人看到的是被当作代码呈现的公式,而靠猜哪些块是公式的渲染器一定会猜错。这些块
|
||||||
|
现在改用它们本就该用的记法书写,渲染器也只读那一种所有 Markdown 数学工具都读的
|
||||||
|
记法。
|
||||||
|
|
||||||
|
排版公式基本就是一件「别丢字符」的事。MathML 行内没有元素包裹的文本不会被绘制,
|
||||||
|
因此没有元素的 token 会消失,留下一个看起来合理却是错的公式。所以每一段文本都被
|
||||||
|
赋予它那类内容应有的元素;并且有一个测试遍历文档与接口目录中的每一个公式,比对
|
||||||
|
「页面会显示的字符」与「公式书写时的字符」。
|
||||||
|
|
||||||
## 图
|
## 图
|
||||||
|
|
||||||
一种图结构同时服务于知识图谱、crate 结构与算法流程图:带种类与分组的节点,以及
|
一种图结构同时服务于知识图谱、crate 结构与算法流程图:带种类与分组的节点,以及
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,17 @@ pub(crate) fn normalize(latex: &str) -> Vec<Token> {
|
||||||
end += 1;
|
end += 1;
|
||||||
}
|
}
|
||||||
if end == index + 1 {
|
if end == index + 1 {
|
||||||
// A backslash before a non letter is that character.
|
// A backslash before a mark is a command of its own when
|
||||||
if let Some(next) = characters.get(index + 1) {
|
// the mark is a spacing one, and that character itself
|
||||||
tokens.push(Token::Symbol(*next));
|
// otherwise.
|
||||||
|
match characters.get(index + 1) {
|
||||||
|
Some(',') => tokens.push(Token::Command("thinspace".to_string())),
|
||||||
|
Some(';') => tokens.push(Token::Command("thickspace".to_string())),
|
||||||
|
Some(':') => tokens.push(Token::Command("medspace".to_string())),
|
||||||
|
Some('!') => tokens.push(Token::Command("negspace".to_string())),
|
||||||
|
Some(' ') => tokens.push(Token::Command("space".to_string())),
|
||||||
|
Some(next) => tokens.push(Token::Symbol(*next)),
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
index += 2;
|
index += 2;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -112,6 +120,18 @@ enum Node {
|
||||||
Nested(Box<Node>, Box<Node>),
|
Nested(Box<Node>, Box<Node>),
|
||||||
/// A named function such as `sin`.
|
/// A named function such as `sin`.
|
||||||
Function(String),
|
Function(String),
|
||||||
|
/// A mark written above its argument, as in `\dot\Omega`.
|
||||||
|
Overscript(Box<Node>, String),
|
||||||
|
/// Words that stand as they are written, as in `\text{km}`.
|
||||||
|
Verbatim(String),
|
||||||
|
/// A matrix, with the delimiters its environment asks for.
|
||||||
|
Matrix {
|
||||||
|
/// The opening and closing delimiters, which may be empty.
|
||||||
|
open: &'static str,
|
||||||
|
close: &'static str,
|
||||||
|
/// The cells, one row at a time.
|
||||||
|
rows: Vec<Vec<Node>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Greek letters, by command.
|
/// The Greek letters, by command.
|
||||||
|
|
@ -281,6 +301,121 @@ fn parse_row(tokens: &[Token], cursor: &mut usize, stop: Option<&[char]>) -> Vec
|
||||||
row
|
row
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the delimiters a matrix environment is drawn with.
|
||||||
|
fn matrix_delimiters(name: &str) -> Option<(&'static str, &'static str)> {
|
||||||
|
Some(match name {
|
||||||
|
"matrix" | "aligned" | "align" | "gathered" | "array" => ("", ""),
|
||||||
|
"pmatrix" => ("(", ")"),
|
||||||
|
"bmatrix" => ("[", "]"),
|
||||||
|
"vmatrix" => ("|", "|"),
|
||||||
|
"Vmatrix" => ("‖", "‖"),
|
||||||
|
"cases" => ("{", ""),
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the name of an environment from the group that follows `\begin`.
|
||||||
|
fn parse_environment_name(tokens: &[Token], cursor: &mut usize) -> String {
|
||||||
|
let mut name = String::new();
|
||||||
|
if !matches!(tokens.get(*cursor), Some(Token::Group)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
*cursor += 1;
|
||||||
|
while let Some(token) = tokens.get(*cursor) {
|
||||||
|
if token == &Token::Group {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if let Token::Symbol(character) = token {
|
||||||
|
name.push(*character);
|
||||||
|
}
|
||||||
|
*cursor += 1;
|
||||||
|
}
|
||||||
|
if matches!(tokens.get(*cursor), Some(Token::Group)) {
|
||||||
|
*cursor += 1;
|
||||||
|
}
|
||||||
|
name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the rows of a matrix, up to the `\end` that closes it.
|
||||||
|
///
|
||||||
|
/// A cell ends at `&`, a row ends at `\\`, and the environment ends at
|
||||||
|
/// `\end`. The tokens of a cell are read by the same handlers as anywhere
|
||||||
|
/// else, so a fraction or a root inside a cell works as it does outside.
|
||||||
|
fn parse_matrix_rows(tokens: &[Token], cursor: &mut usize) -> Vec<Vec<Node>> {
|
||||||
|
// Two accumulators: the cell being read, and the cells of the row so
|
||||||
|
// far. One alone does not do, because `&` ends a cell and starts the
|
||||||
|
// next, and the nodes after it belong to the next cell rather than
|
||||||
|
// beside the one just closed.
|
||||||
|
let mut rows: Vec<Vec<Node>> = Vec::new();
|
||||||
|
let mut cells: Vec<Node> = Vec::new();
|
||||||
|
let mut cell: Vec<Node> = Vec::new();
|
||||||
|
|
||||||
|
let end_cell = |cells: &mut Vec<Node>, cell: &mut Vec<Node>| {
|
||||||
|
cells.push(Node::Row(std::mem::take(cell)));
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match tokens.get(*cursor) {
|
||||||
|
None => break,
|
||||||
|
Some(Token::Command(name)) if name == "end" => break,
|
||||||
|
Some(Token::Symbol('\\')) => {
|
||||||
|
*cursor += 1;
|
||||||
|
end_cell(&mut cells, &mut cell);
|
||||||
|
rows.push(std::mem::take(&mut cells));
|
||||||
|
}
|
||||||
|
Some(Token::Symbol('&')) => {
|
||||||
|
*cursor += 1;
|
||||||
|
end_cell(&mut cells, &mut cell);
|
||||||
|
}
|
||||||
|
Some(Token::Group) => {
|
||||||
|
*cursor += 1;
|
||||||
|
let inner = parse_row(tokens, cursor, Some(&['}']));
|
||||||
|
cell.push(Node::Row(inner));
|
||||||
|
}
|
||||||
|
Some(Token::Symbol(symbol)) => {
|
||||||
|
let symbol = *symbol;
|
||||||
|
*cursor += 1;
|
||||||
|
cell.extend(parse_symbol(symbol, tokens, cursor, &mut 0));
|
||||||
|
}
|
||||||
|
Some(Token::Command(name)) => {
|
||||||
|
let name = name.clone();
|
||||||
|
*cursor += 1;
|
||||||
|
cell.extend(parse_command(&name, tokens, cursor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The last row has no `\\` after it.
|
||||||
|
if !cell.is_empty() || !cells.is_empty() || rows.is_empty() {
|
||||||
|
end_cell(&mut cells, &mut cell);
|
||||||
|
rows.push(cells);
|
||||||
|
}
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the characters of an argument as the text they are written with.
|
||||||
|
///
|
||||||
|
/// Used where the argument is words rather than mathematics, so that the
|
||||||
|
/// letters are not read as variables one at a time.
|
||||||
|
fn parse_raw_text(tokens: &[Token], cursor: &mut usize) -> String {
|
||||||
|
let mut out = String::new();
|
||||||
|
if !matches!(tokens.get(*cursor), Some(Token::Group)) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
*cursor += 1;
|
||||||
|
while let Some(token) = tokens.get(*cursor) {
|
||||||
|
match token {
|
||||||
|
Token::Group => break,
|
||||||
|
Token::Symbol(character) => out.push(*character),
|
||||||
|
Token::Command(name) => out.push_str(name),
|
||||||
|
}
|
||||||
|
*cursor += 1;
|
||||||
|
}
|
||||||
|
if matches!(tokens.get(*cursor), Some(Token::Group)) {
|
||||||
|
*cursor += 1;
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads the argument of a command, which is either a group or one symbol.
|
/// Reads the argument of a command, which is either a group or one symbol.
|
||||||
fn parse_argument(tokens: &[Token], cursor: &mut usize) -> Node {
|
fn parse_argument(tokens: &[Token], cursor: &mut usize) -> Node {
|
||||||
match tokens.get(*cursor) {
|
match tokens.get(*cursor) {
|
||||||
|
|
@ -451,6 +586,78 @@ fn parse_command(name: &str, tokens: &[Token], cursor: &mut usize) -> Vec<Node>
|
||||||
cursor,
|
cursor,
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
|
// A matrix environment: \begin{name} cells \end{name}, with `&`
|
||||||
|
// between the cells of a row and `\\` between the rows.
|
||||||
|
"begin" => {
|
||||||
|
let name = parse_environment_name(tokens, cursor);
|
||||||
|
let Some((open, close)) = matrix_delimiters(&name) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
let rows = parse_matrix_rows(tokens, cursor);
|
||||||
|
// The environment is closed by `\end{name}`, which is two
|
||||||
|
// tokens: the command and the name. Both are read here, or the
|
||||||
|
// name is left to be set as mathematics of its own.
|
||||||
|
if matches!(tokens.get(*cursor), Some(Token::Command(name)) if name == "end") {
|
||||||
|
*cursor += 1;
|
||||||
|
let _ = parse_raw_text(tokens, cursor);
|
||||||
|
}
|
||||||
|
vec![Node::Matrix { open, close, rows }]
|
||||||
|
}
|
||||||
|
// The closing of an environment is consumed by the environment.
|
||||||
|
"end" => Vec::new(),
|
||||||
|
// A floor, a ceiling, and the modulo operator.
|
||||||
|
"lfloor" | "lceil" | "langle" => {
|
||||||
|
let symbol = match name {
|
||||||
|
"lfloor" => "⌊",
|
||||||
|
"lceil" => "⌈",
|
||||||
|
_ => "⟨",
|
||||||
|
};
|
||||||
|
vec![Node::Text(symbol.to_string())]
|
||||||
|
}
|
||||||
|
"rfloor" | "rceil" | "rangle" => {
|
||||||
|
let symbol = match name {
|
||||||
|
"rfloor" => "⌋",
|
||||||
|
"rceil" => "⌉",
|
||||||
|
_ => "⟩",
|
||||||
|
};
|
||||||
|
vec![Node::Text(symbol.to_string())]
|
||||||
|
}
|
||||||
|
"bmod" => vec![Node::Text(" mod ".to_string())],
|
||||||
|
"pmod" => {
|
||||||
|
let argument = parse_argument(tokens, cursor);
|
||||||
|
vec![Node::Row(vec![
|
||||||
|
Node::Text(" (mod ".to_string()),
|
||||||
|
argument,
|
||||||
|
Node::Text(")".to_string()),
|
||||||
|
])]
|
||||||
|
}
|
||||||
|
// An accent, written as a mark above its argument.
|
||||||
|
"dot" | "ddot" | "bar" | "overline" | "vec" | "hat" | "tilde" => {
|
||||||
|
let mark = match name {
|
||||||
|
"dot" => "˙",
|
||||||
|
"ddot" => "¨",
|
||||||
|
"vec" => "→",
|
||||||
|
"hat" => "^",
|
||||||
|
"tilde" => "~",
|
||||||
|
_ => "‾",
|
||||||
|
};
|
||||||
|
let argument = parse_argument(tokens, cursor);
|
||||||
|
let accented = Node::Overscript(Box::new(argument), mark.to_string());
|
||||||
|
vec![attach_scripts(accented, tokens, cursor)]
|
||||||
|
}
|
||||||
|
// Commands that only set the size or the style, and carry no glyph.
|
||||||
|
"thinspace" => vec![Node::Text("\u{2009}".to_string())],
|
||||||
|
"medspace" | "thickspace" => vec![Node::Text("\u{2005}".to_string())],
|
||||||
|
"negspace" => Vec::new(),
|
||||||
|
"space" => vec![Node::Text(" ".to_string())],
|
||||||
|
"quad" | "qquad" => vec![Node::Text("\u{2009}\u{2009}".to_string())],
|
||||||
|
"displaystyle" | "textstyle" | "limits" | "nolimits" | "left" | "right" | "bigl"
|
||||||
|
| "bigr" | "Bigl" | "Bigr" | "biggl" | "biggr" | "Biggl" | "Biggr" | "big" | "Big"
|
||||||
|
| "bigg" | "Bigg" => {
|
||||||
|
// A size or a pairing command is structural. The delimiter that
|
||||||
|
// follows is read as an ordinary symbol.
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
"sqrt" => {
|
"sqrt" => {
|
||||||
// The index, when there is one, is given in brackets before the
|
// The index, when there is one, is given in brackets before the
|
||||||
// radicand: `\sqrt[3]{x}`.
|
// radicand: `\sqrt[3]{x}`.
|
||||||
|
|
@ -466,21 +673,16 @@ fn parse_command(name: &str, tokens: &[Token], cursor: &mut usize) -> Vec<Node>
|
||||||
cursor,
|
cursor,
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
"left" | "right" | "big" | "Big" | "bigg" | "Bigg" => {
|
// A comma written as a command is punctuation, not a separator
|
||||||
// The size and pairing commands are structural; the delimiter
|
// between two formulas, so it keeps its own shape.
|
||||||
// that follows is read as an ordinary symbol.
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
"quad" => vec![Node::Text("\u{2009}\u{2009}".to_string())],
|
|
||||||
// The spacing commands are punctuation with no shape of their own.
|
|
||||||
"," => vec![Node::Text(", ".to_string())],
|
"," => vec![Node::Text(", ".to_string())],
|
||||||
";" => vec![Node::Text("; ".to_string())],
|
// `\text{...}` holds words as they are written. Read as variables
|
||||||
":" => vec![Node::Text(": ".to_string())],
|
// they would be set one letter at a time, so `km` became `k m`.
|
||||||
"text" | "mathrm" | "operatorname" | "mbox" => {
|
"text" | "mathrm" | "operatorname" | "mbox" => {
|
||||||
let inner = parse_argument(tokens, cursor);
|
vec![Node::Verbatim(parse_raw_text(tokens, cursor))]
|
||||||
vec![Node::Row(vec![inner])]
|
|
||||||
}
|
}
|
||||||
"mathbf" | "mathit" | "mathsf" | "mathtt" => {
|
// A style command keeps its argument set as mathematics.
|
||||||
|
"mathsf" | "mathtt" | "mathbf" | "mathit" | "mathcal" | "mathfrak" => {
|
||||||
let inner = parse_argument(tokens, cursor);
|
let inner = parse_argument(tokens, cursor);
|
||||||
vec![Node::Row(vec![inner])]
|
vec![Node::Row(vec![inner])]
|
||||||
}
|
}
|
||||||
|
|
@ -548,6 +750,30 @@ fn emit(node: &Node) -> String {
|
||||||
// serif face reads as a number rather than as a variable.
|
// serif face reads as a number rather than as a variable.
|
||||||
Node::Text(text) => classify(text),
|
Node::Text(text) => classify(text),
|
||||||
Node::Function(label) => format!("<mi>{}</mi>", escape(label)),
|
Node::Function(label) => format!("<mi>{}</mi>", escape(label)),
|
||||||
|
Node::Verbatim(text) => format!("<mtext>{}</mtext>", escape(text)),
|
||||||
|
Node::Overscript(base, mark) => format!(
|
||||||
|
"<mover><mrow>{}</mrow><mo>{}</mo></mover>",
|
||||||
|
emit(base),
|
||||||
|
escape(mark)
|
||||||
|
),
|
||||||
|
Node::Matrix { open, close, rows } => {
|
||||||
|
let body: String = rows
|
||||||
|
.iter()
|
||||||
|
.map(|row| {
|
||||||
|
let cells: String = row
|
||||||
|
.iter()
|
||||||
|
.map(|cell| format!("<mtd>{}</mtd>", emit(cell)))
|
||||||
|
.collect();
|
||||||
|
format!("<mtr>{cells}</mtr>")
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let table = format!("<mtable>{body}</mtable>");
|
||||||
|
if open.is_empty() && close.is_empty() {
|
||||||
|
table
|
||||||
|
} else {
|
||||||
|
format!("<mrow><mo>{open}</mo>{table}<mo>{close}</mo></mrow>")
|
||||||
|
}
|
||||||
|
}
|
||||||
Node::Row(children) => match children.len() {
|
Node::Row(children) => match children.len() {
|
||||||
// A row of one is the child itself; an extra pair of brackets
|
// A row of one is the child itself; an extra pair of brackets
|
||||||
// would be noise in the markup and would break the grouping the
|
// would be noise in the markup and would break the grouping the
|
||||||
|
|
|
||||||
|
|
@ -56,26 +56,58 @@ fn visible(html: &str) -> String {
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the text with the notation words removed.
|
/// Returns the content characters of a formula source.
|
||||||
///
|
///
|
||||||
/// A formula is written either as LaTeX, where a command is `\sqrt`, or as
|
/// What is left out is notation: a command name, the name of an environment,
|
||||||
/// plain notation, where the same root is the bare word `sqrt`. Both are
|
/// the `&` that separates the cells of a matrix and the `\\` that separates
|
||||||
/// notation and neither is drawn as those letters.
|
/// its rows, and the braces that group. None of them is drawn as those
|
||||||
fn strip_commands(source: &str) -> String {
|
/// characters, so none of them is expected on the page. What remains is the
|
||||||
let source = source.replace("sqrt", "");
|
/// letters and digits, which are content and must all appear.
|
||||||
let mut out = String::with_capacity(source.len());
|
fn content_characters(source: &str) -> String {
|
||||||
let mut characters = source.chars().peekable();
|
// The attribute is HTML escaped, so a cell separator arrives as `&`
|
||||||
|
// and would otherwise leave the letters of `amp` behind.
|
||||||
|
// `sqrt` is written bare in the plain notation and with a backslash in
|
||||||
|
// LaTeX; the bare form has to go as well, because it is drawn as a
|
||||||
|
// radical sign rather than as its letters.
|
||||||
|
let unescaped = source
|
||||||
|
.replace("sqrt", "")
|
||||||
|
.replace("&", "&")
|
||||||
|
.replace("<", "<")
|
||||||
|
.replace(">", ">")
|
||||||
|
.replace(""", "\"")
|
||||||
|
.replace("'", "'");
|
||||||
|
|
||||||
|
let mut out = String::with_capacity(unescaped.len());
|
||||||
|
let mut characters = unescaped.chars().peekable();
|
||||||
while let Some(character) = characters.next() {
|
while let Some(character) = characters.next() {
|
||||||
if character == '\\' {
|
match character {
|
||||||
// A command runs to the first character that is not a letter.
|
// A command runs to the first character that is not a letter.
|
||||||
while characters.peek().is_some_and(|c| c.is_alphabetic()) {
|
// The name is captured so that `begin` and `end` can be told
|
||||||
|
// apart from the rest, because those two carry the name of an
|
||||||
|
// environment in braces after them.
|
||||||
|
'\\' => {
|
||||||
|
let mut name = String::new();
|
||||||
|
while let Some(next) = characters.peek().copied() {
|
||||||
|
if !next.is_alphabetic() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
name.push(next);
|
||||||
characters.next();
|
characters.next();
|
||||||
}
|
}
|
||||||
continue;
|
if (name == "begin" || name == "end") && characters.peek() == Some(&'{') {
|
||||||
|
characters.next();
|
||||||
|
for next in characters.by_ref() {
|
||||||
|
if next == '}' {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
out.push(character);
|
|
||||||
}
|
}
|
||||||
out
|
}
|
||||||
|
}
|
||||||
|
'&' | '{' | '}' | '^' | '_' | '/' | '*' | '$' | '~' => {}
|
||||||
|
other => out.push(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.chars().filter(|c| c.is_alphanumeric()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the formula sources of one document, in order.
|
/// Returns the formula sources of one document, in order.
|
||||||
|
|
@ -94,23 +126,43 @@ fn formula_blocks(html: &str) -> Vec<String> {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn every_formula_shows_every_character_it_was_written_with() {
|
fn every_formula_shows_every_character_it_was_written_with() {
|
||||||
let documents = apsis_book::docs::load_documents();
|
|
||||||
let mut checked = 0;
|
let mut checked = 0;
|
||||||
let mut failures = Vec::new();
|
let mut failures = Vec::new();
|
||||||
|
|
||||||
for document in &documents {
|
// The concept documents, in both languages.
|
||||||
|
let mut sources: Vec<(String, String)> = Vec::new();
|
||||||
|
for document in &apsis_book::docs::load_documents() {
|
||||||
for language in [Language::English, Language::Chinese] {
|
for language in [Language::English, Language::Chinese] {
|
||||||
let html = &document.variant(language).html;
|
sources.push((document.id.clone(), document.variant(language).html.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// And the interface documentation, which carries formulas of its own in
|
||||||
|
// the doc comments of the crate it describes. Leaving it out would have
|
||||||
|
// hidden every formula there.
|
||||||
|
for krate in apsis_book::docs::load_api_catalog().crates {
|
||||||
|
for item in &krate.items {
|
||||||
|
// The doc comment is Markdown, and the service renders it with
|
||||||
|
// the same renderer as a document.
|
||||||
|
for text in [Some(&item.doc), item.doc_zh.as_ref()]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
{
|
||||||
|
sources.push((
|
||||||
|
format!("{}::{}", krate.name, item.name),
|
||||||
|
apsis_book::docs::markdown::render(text),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (id, html) in &sources {
|
||||||
|
let html = html.as_str();
|
||||||
|
{
|
||||||
for source in formula_blocks(html) {
|
for source in formula_blocks(html) {
|
||||||
checked += 1;
|
checked += 1;
|
||||||
// The source as written, without the spaces that only set it
|
// The source as written, without the spaces that only set it
|
||||||
// out on the page.
|
// out on the page.
|
||||||
// A command name is notation: what it draws is a sign or a
|
let written = content_characters(&source);
|
||||||
// Greek letter, not the letters of its name.
|
|
||||||
let written: String = strip_commands(&source)
|
|
||||||
.chars()
|
|
||||||
.filter(|c| c.is_alphanumeric())
|
|
||||||
.collect();
|
|
||||||
// The part of the page that is the formula itself.
|
// The part of the page that is the formula itself.
|
||||||
let start = html.find(&format!("data-tex=\"{source}\"")).unwrap_or(0);
|
let start = html.find(&format!("data-tex=\"{source}\"")).unwrap_or(0);
|
||||||
let end = html[start..]
|
let end = html[start..]
|
||||||
|
|
@ -124,8 +176,7 @@ fn every_formula_shows_every_character_it_was_written_with() {
|
||||||
let missing: String = written.chars().filter(|c| !shown.contains(*c)).collect();
|
let missing: String = written.chars().filter(|c| !shown.contains(*c)).collect();
|
||||||
if !missing.is_empty() {
|
if !missing.is_empty() {
|
||||||
failures.push(format!(
|
failures.push(format!(
|
||||||
"{}: 公式少了字符 \"{}\":写入 {},显示 {}",
|
"{id}: 公式少了字符 \"{missing}\":写入 {written},显示 {shown}"
|
||||||
document.id, missing, written, shown
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue