Files
astro/doc/manual/en/formula.md
T
b612 16c62a97d5 feat: 完善时标与天象几何计算并扩展输出接口
- 新增时标、ΔT 模型、质心时间与 UT1 支持
- 改进日月食、月掩、行星事件及路径边界计算
- 完善恒星三维自行与动态距离传播
- 扩展 SVG、GeoJSON、KML 输出与底层距离换算工具
- 整理中英文手册、示例资源及回归测试
2026-09-23 18:55:12 +08:00

342 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Formula Helpers
[中文](../formula.md) | [Back to README](../../../README.en.md)
`formula` holds the common formulas that are unrelated to a specific date or ephemeris; they suit popular-science estimates, novel settings, and teaching demonstrations.
It performs no time-scale conversion and reads no ephemeris tables: the inputs are only instantaneous or constant parameters such as temperature, wavelength, distance, aperture, and altitude.
Altitude and zenith-distance conventions follow [Observing-angle semantics](sun-moon.md#observing-angle-semantics); when coordinate-layer refraction is needed, use `coord`'s [airmass](coord.md#airmass); overall accuracy and applicability are in the manual [Scope And Accuracy](accuracy.md).
## Contents
- [Magnitude, synodic period and blackbody radiation](#magnitude-synodic-period-and-blackbody-radiation)
- [API Reference](#api-reference)
- [Blackbody and Radiation](#blackbody-and-radiation)
- [Synodic Period](#synodic-period)
- [Magnitude and Distance](#magnitude-and-distance)
- [Distance Units](#distance-units)
- [Telescope Metrics](#telescope-metrics)
- [Stellar Parameter Conversions](#stellar-parameter-conversions)
- [Airmass Models](#airmass-models)
- [Usage examples](#usage-examples)
- [Blackbody peak, total flux and stellar parameters](#blackbody-peak-total-flux-and-stellar-parameters)
- [Synodic period and magnitude/distance](#synodic-period-and-magnitudedistance)
- [Telescope limiting magnitude and resolution](#telescope-limiting-magnitude-and-resolution)
- [Comparing airmass models](#comparing-airmass-models)
- [Parameter and result conventions](#parameter-and-result-conventions)
- [Units](#units)
- [Time Scales](#time-scales)
- [Angle Quadrants and Degree/Radian Boundaries](#angle-quadrants-and-degreeradian-boundaries)
- [Zero Values and Invalid Input](#zero-values-and-invalid-input)
- [Accuracy and Applicability](#accuracy-and-applicability)
## Magnitude, synodic period and blackbody radiation
```go
package main
import (
"fmt"
"b612.me/astro/formula"
)
func main() {
// Empirical limiting magnitude for a 70 mm refractor at a site with naked-eye limit 6.
fmt.Printf("limiting=%.6f\n", formula.LimitingMagnitudeEmpirical(70, 6))
// Synodic period of Earth and Venus. Inputs and output are days.
fmt.Printf("synodic=%.6f\n", formula.SynodicPeriod(365.25636, 224.70069))
// Apparent magnitude of a Sun-like absolute-magnitude object at 100 pc.
fmt.Printf("apparent=%.6f\n", formula.ApparentMagnitudeFromAbsolute(4.83, 100))
// Treat the Sun as a 5772 K blackbody; compute peak wavelength and total radiant exitance.
fmt.Printf("peak=%.9em flux=%.6e\n",
formula.WienPeakWavelength(5772),
formula.StefanBoltzmannFlux(5772),
)
}
```
Output:
```text
limiting=11.000000
synodic=583.920635
apparent=9.830000
peak=5.020394932e-07m flux=6.293859e+07
```
## API Reference
The interfaces, units, and return values are listed below by what is computed.
The group snippets omit shared preamble: `fmt` is imported at the top of the file, and `formula` means `b612.me/astro/formula`.
### Blackbody and Radiation
| Name | Purpose | Units and conventions |
| --- | --- | --- |
| `PlanckRadianceByWavelength` | Planck spectral radiance by wavelength | wavelength meters, temperature K; returns W·sr⁻¹·m⁻³; non-positive temperature or wavelength returns NaN |
| `WienPeakWavelength` | Wien displacement peak wavelength | temperature K; returns meters; temperature `≤ 0` or non-finite returns NaN |
| `StefanBoltzmannFlux` | total radiant exitance per unit area | temperature K; returns W/m²; `0 K` is valid and returns `0` |
| `SolarEffectiveTemperature` | built-in solar effective temperature constant | no arguments; returns K, currently `5772` |
```go
// Treat the Sun as a 5772 K blackbody: peak wavelength, total exitance, and spectral radiance at 500 nm.
tSun := formula.SolarEffectiveTemperature()
fmt.Printf("peak=%.9e m flux=%.6e W/m^2\n",
formula.WienPeakWavelength(tSun), formula.StefanBoltzmannFlux(tSun))
fmt.Printf("radiance@500nm=%.6e W·sr^-1·m^-3\n",
formula.PlanckRadianceByWavelength(500e-9, tSun))
```
### Synodic Period
| Name | Purpose | Units and conventions |
| --- | --- | --- |
| `SynodicPeriod` | synodic period of two orbiting bodies | both inputs must share one unit and the output uses it; a period `≤ 0` or non-finite returns NaN, equal periods return `+Inf` |
```go
// Synodic periods of Earth with other planets; inputs and outputs are days.
earth := 365.25636
fmt.Printf("venus=%.6f mars=%.6f jupiter=%.6f\n",
formula.SynodicPeriod(earth, 224.70069),
formula.SynodicPeriod(earth, 686.980),
formula.SynodicPeriod(earth, 4332.589))
```
### Magnitude and Distance
| Name | Purpose | Units and conventions |
| --- | --- | --- |
| `DistanceModulus` | distance modulus | distance pc; returns `m − M`; it is `0` at `10 pc`, and a distance `≤ 0` returns NaN |
| `ApparentMagnitudeFromAbsolute` | absolute magnitude plus distance to apparent magnitude | magnitude mag, distance pc; equals `M + distance modulus` |
| `AbsoluteMagnitudeFromApparent` | apparent magnitude plus distance to absolute magnitude | magnitude mag, distance pc; equals `m − distance modulus` |
```go
// Sun-like absolute magnitude 4.83 placed at 10 pc / 100 pc / 1 kpc, and the inverse.
for _, d := range []float64{10, 100, 1000} {
m := formula.ApparentMagnitudeFromAbsolute(4.83, d)
fmt.Printf("d=%.0f pc m=%.6f M=%.6f mod=%.6f\n",
d, m, formula.AbsoluteMagnitudeFromApparent(m, d), formula.DistanceModulus(d))
}
```
### Distance Units
| Name | Purpose | Units and convention |
| --- | --- | --- |
| `Distance` | Converts parsecs, light-years or astronomical units to parsecs | A positive value plus a `DistanceUnit`; non-positive values, NaN and unknown units return NaN |
| Constant | Meaning |
| --- | --- |
| `DistanceParsec` | Parsecs, an identity conversion |
| `DistanceLightYear` | Light-years |
| `DistanceAU` | Astronomical units |
```go
// Sirius has a parallax of 0.375 arcseconds, i.e. 2.667 pc or 8.70 light-years.
pc := formula.Distance(1/0.375, formula.DistanceParsec)
fmt.Printf("%.3f pc = %.2f ly\n", pc, formula.Distance(pc, formula.DistanceParsec)/formula.Distance(1, formula.DistanceLightYear))
```
Convention: the astronomical unit is `149597870.7` km, the light-year is the IAU defined `9460730472580.8` km, and the parsec follows from the exact relation `648000/π` astronomical units, giving `1 pc = 3.261563777 ly`.
### Telescope Metrics
| Name | Purpose | Units and conventions |
| --- | --- | --- |
| `DawesLimitArcsec` | Dawes resolution limit | aperture mm; returns arcseconds from the empirical `116 / D` |
| `RayleighLimitArcsec` | Rayleigh resolution limit | aperture mm; returns arcseconds from the empirical `138.4 / D` |
| `LightGatheringPowerRatio` | light-gathering power ratio | two apertures in mm; returns `(D1 / D2)²`, dimensionless |
| `LimitingMagnitudeEmpirical` | empirical limiting magnitude | aperture mm, naked-eye limit mag; estimated as `naked-eye limit + 5·log10(D / 7)`, with 7 mm as the built-in dark-adapted pupil |
```go
// 70 mm refractor: resolution limits, light grasp relative to a 7 mm dark-adapted pupil, and empirical limit at naked-eye limit 6.
fmt.Printf("dawes=%.6f rayleigh=%.6f\n",
formula.DawesLimitArcsec(70), formula.RayleighLimitArcsec(70))
fmt.Printf("power=%.6f limiting=%.6f\n",
formula.LightGatheringPowerRatio(70, 7),
formula.LimitingMagnitudeEmpirical(70, 6))
```
### Stellar Parameter Conversions
| Name | Purpose | Units and conventions |
| --- | --- | --- |
| `LuminosityFromRadiusTemperature` | radius plus temperature to luminosity | radius meters, temperature K; returns W via `4πR²σT⁴` |
| `LuminositySolarFromRadiusTemperature` | same, solar units | radius R☉, temperature K; returns L☉ |
| `RadiusFromLuminosityTemperature` | luminosity plus temperature to radius | luminosity W, temperature K; returns meters |
| `RadiusSolarFromLuminosityTemperature` | same, solar units | luminosity L☉, temperature K; returns R☉ |
| `EffectiveTemperatureFromLuminosityRadius` | luminosity plus radius to effective temperature | luminosity W, radius meters; returns K |
| `EffectiveTemperatureFromLuminositySolarRadius` | same, solar units | luminosity L☉, radius R☉; returns K |
| `SolarEffectiveTemperature` | built-in solar effective temperature | no arguments; returns K |
```go
// A 2.5 R☉, 20 L☉ main-sequence star: solve for temperature, then recompute luminosity and radius.
t := formula.EffectiveTemperatureFromLuminositySolarRadius(20, 2.5)
fmt.Printf("Teff=%.6f K\n", t)
fmt.Printf("L=%.6f Lsun R=%.6f Rsun\n",
formula.LuminositySolarFromRadiusTemperature(2.5, t),
formula.RadiusSolarFromLuminosityTemperature(20, t))
// The MKS version of the same quantities; the solar radius is the built-in constant 6.957e8 m.
rM := 2.5 * 6.957e8
lW := formula.LuminosityFromRadiusTemperature(rM, t)
fmt.Printf("L=%.6e W R=%.6e m Teff=%.6f K\n",
lW, formula.RadiusFromLuminosityTemperature(lW, t),
formula.EffectiveTemperatureFromLuminosityRadius(lW, rM))
```
### Airmass Models
| Name | Purpose | Units and conventions |
| --- | --- | --- |
| `AirmassPlaneParallel` | plane-parallel model | true altitude in degrees; equivalent to `sec(z)`, returns `+Inf` at `0°` |
| `AirmassPlaneParallelByZenithDistance` | plane-parallel model by zenith distance | zenith distance in degrees; returns `+Inf` at `90°` |
| `AirmassKastenYoung` | Kasten-Young 1989 | apparent altitude in degrees; more robust than `sec(z)` at low altitude |
| `AirmassPickering` | Pickering 2002 | apparent altitude in degrees; intended for low-altitude correction |
All four limit the input to `[0,90]` and return NaN outside that range or for non-finite input; altitude is measured from the horizon at `0°` to the zenith at `+90°`, zenith distance is its complement, and the convention is described under [Observing-angle semantics](sun-moon.md#observing-angle-semantics).
```go
fmt.Println(formula.AirmassPlaneParallel(30))
fmt.Println(formula.AirmassKastenYoung(5))
fmt.Println(formula.AirmassPickering(5))
fmt.Println(formula.AirmassPlaneParallelByZenithDistance(60))
```
If coordinate-layer refraction correction is not needed, `formula` also provides the three airmass models directly, with more direct input semantics:
- `AirmassPlaneParallel`: true altitude input, equivalent to the geometric `sec(z)` approximation
- `AirmassPlaneParallelByZenithDistance`: zenith-distance input
- `AirmassKastenYoung` / `AirmassPickering`: apparent-altitude input, no automatic refraction correction
## Usage examples
### Blackbody peak, total flux and stellar parameters
```go
fmt.Println(formula.WienPeakWavelength(5772)) // peak wavelength (metres)
fmt.Println(formula.StefanBoltzmannFlux(5772)) // flux per unit area (W/m^2)
fmt.Println(formula.RadiusSolarFromLuminosityTemperature(1, 5772)) // radius from luminosity and temperature
fmt.Println(formula.EffectiveTemperatureFromLuminositySolarRadius(1, 1)) // temperature from luminosity and radius
```
```text
5.020394932432432e-07
6.293859246828887e+07
1.0000011882005775
5772.003429145848
```
The three conversions invert each other and return the input under one convention (the `1 -> 1.0000012` and `5772 -> 5772.0034` residuals come from both sides rounding to 5772 K); the `...Solar` variants use solar units and the plain ones use SI.
### Synodic period and magnitude/distance
```go
fmt.Println(formula.SynodicPeriod(365.25636, 224.70069)) // Earth and Venus (days)
fmt.Println(formula.DistanceModulus(10)) // distance modulus at 10 pc
fmt.Println(formula.ApparentMagnitudeFromAbsolute(4.83, 100)) // absolute magnitude 4.83 seen from 100 pc
fmt.Println(formula.AbsoluteMagnitudeFromApparent(4.83, 100)) // the inverse conversion
```
```text
583.9206352820089
0
9.83
-0.16999999999999993
```
`DistanceModulus(10)` is 0 because 10 pc is the defining distance of absolute magnitude; synodic periods take and return days, and the argument order does not matter.
### Telescope limiting magnitude and resolution
```go
fmt.Println(formula.DawesLimitArcsec(70), formula.RayleighLimitArcsec(70)) // both resolution limits at 70 mm
fmt.Println(formula.LightGatheringPowerRatio(200, 70)) // 200 mm against 70 mm
fmt.Println(formula.LimitingMagnitudeEmpirical(70, 6)) // limiting magnitude at 70 mm with a naked-eye limit of 6
```
```text
1.6571428571428573 1.9771428571428573
8.16326530612245
11
```
Dawes and Rayleigh differ by a coefficient (1.66" versus 1.98" at 70 mm), so state which one a report uses; the second argument of `LimitingMagnitudeEmpirical` is the naked-eye limit of the site and changes with it.
### Comparing airmass models
```go
for _, alt := range []float64{5, 30, 60, 90} {
fmt.Printf("alt=%.0f KY=%.6f Pickering=%.6f plane=%.6f\n", alt,
formula.AirmassKastenYoung(alt), formula.AirmassPickering(alt), formula.AirmassPlaneParallel(alt))
}
```
```text
alt=5 KY=10.305791 Pickering=10.333706 plane=11.473713
alt=30 KY=1.994293 Pickering=1.993154 plane=2.000000
alt=60 KY=1.153992 Pickering=1.154058 plane=1.154701
alt=90 KY=0.999712 Pickering=1.000000 plane=1.000000
```
The three models nearly coincide at moderate and high altitude and differ most at 5 degrees (Kasten-Young against the plane-parallel model is about 1.2 airmasses); the plane-parallel model diverges at 0 degrees, so use Kasten-Young or Pickering for careful low-altitude work.
Pressure- and temperature-corrected versions live in [Coordinate tools](coord.md#airmass) and this package keeps the raw formulas.
## Parameter and result conventions
### Units
- Blackbody family: wavelength meters, temperature kelvin; `PlanckRadianceByWavelength` returns spectral radiance `W·sr⁻¹·m⁻³`, `StefanBoltzmannFlux` returns `W/m²`, and `WienPeakWavelength` returns meters.
- Stellar family: the MKS variants use radius meters, luminosity watts, and temperature kelvin; the Solar variants use solar radius R☉, solar luminosity L☉, and temperature kelvin, and both inputs and outputs are dimensionless solar multiples.
- Magnitude family: distance parsecs, magnitude mag; `DistanceModulus` returns `m − M`, also in mag.
- Distance units: `Distance` performs unit conversion only; the input unit comes from `DistanceUnit` and the return value is always parsecs.
- Telescope family: aperture millimeters; `DawesLimitArcsec` and `RayleighLimitArcsec` return **arcseconds**, not degrees; `LightGatheringPowerRatio` and `LimitingMagnitudeEmpirical` return a dimensionless ratio and a magnitude respectively.
- Airmass family: altitude (or zenith distance) in degrees; the return value is the dimensionless relative airmass with 1 at the zenith.
- Synodic period: the unit is chosen by the caller, both inputs must match, and the output matches them; the package does not assume days.
- This package produces no apparent diameter or apparent radius. Apparent-radius fields in the eclipse and occultation manuals are in arcseconds; here only the two Dawes/Rayleigh limits use arcseconds while all other angles are degrees, so do not interchange them.
### Time Scales
- No entry point accepts an instant or performs any time-scale conversion: the formulas depend only on parameters such as temperature, wavelength, distance, aperture, and altitude, and are independent of UTC, UT1, and TT.
- The synodic period is a length of time, not the instant of the next conjunction.
Landing on a date requires the conjunction APIs in the planet packages plus civil time, whose convention is in the manual [Time Scale Conventions](timescale.md).
- The package has no ΔT, leap-second, or UT1 entry point; those only appear in instant-dependent chains such as `coord`, `eclipse`, and `moon`.
### Angle Quadrants and Degree/Radian Boundaries
- All angle parameters are degrees, are converted to radians internally, and come back as degrees or arcseconds; radians never leak to the caller.
- Altitude and zenith distance are both limited to `[0,90]`: the horizon is `0°`, the zenith is `+90°`, and zenith distance is the complement of altitude.
The package performs no quadrant folding, so a negative altitude (below the horizon) is simply invalid and is never folded to the zenith or replaced by an absolute value.
- Zenith distance `z` and altitude `h` satisfy `z = 90° − h`; `AirmassPlaneParallel` takes `h` and `AirmassPlaneParallelByZenithDistance` takes `z`, and both must agree for the same geometry.
- Dawes/Rayleigh return arcseconds; divide by 3600 to compare with the degree-based angles in the other manuals.
### Zero Values and Invalid Input
- The blackbody family deliberately treats invalid input differently: `WienPeakWavelength` and `PlanckRadianceByWavelength` return NaN for temperature `≤ 0` or non-finite, while `StefanBoltzmannFlux` only returns NaN for temperature `< 0` or non-finite, so `0 K` is valid and returns `0` (a 0 K body has zero flux, while its peak wavelength is undefined).
- Synodic period: either period `≤ 0` or non-finite returns NaN; equal periods make the frequency difference zero and return `+Inf`.
- Magnitude family: `distanceParsec ≤ 0` or non-finite makes `DistanceModulus` return NaN, and the two conversion functions then return NaN as well; `DistanceModulus(10)` is always `0`.
- Telescope family: an aperture (or the second aperture) `≤ 0` or non-finite returns NaN; a zero second aperture in `LightGatheringPowerRatio` is rejected as invalid rather than dividing by zero; `LimitingMagnitudeEmpirical` takes no pupil argument, and 7 mm is a built-in constant.
- Stellar family: every input must be `> 0` or the function returns NaN; `EffectiveTemperatureFromLuminositySolarRadius`, `LuminositySolarFromRadiusTemperature`, and `RadiusSolarFromLuminosityTemperature` convert solar units to SI before computing.
- Airmass family: an altitude or zenith distance outside `[0,90]` (including negative values) or non-finite returns NaN; `AirmassPlaneParallel(0)` and `AirmassPlaneParallelByZenithDistance(90)` return `+Inf`, while `AirmassKastenYoung(0)` and `AirmassPickering(0)` stay finite.
- The package has no `(value, error)` or `(value, ok)` returns: invalid input is always expressed as NaN, which the caller checks with `math.IsNaN`.
### Accuracy and Applicability
- The blackbody family is an ideal-blackbody model with no absorption lines, interstellar extinction, or atmospheric extinction. `WienPeakWavelength` uses the Wien displacement constant `b = 2.897771955e-3 m·K`; because the per-frequency and per-wavelength peak conventions differ, `b/T` is strictly the peak in the wavelength convention.
- Constant conventions: `h = 6.62607015e-34 J·s`, `c = 299792458 m/s`, `k = 1.380649e-23 J/K`, `σ = 5.670374419e-8 W·m⁻²·K⁻⁴`; solar parameters `L☉ = 3.828e26 W`, `R☉ = 6.957e8 m`, `Teff = 5772 K`, the last exposed through `SolarEffectiveTemperature`.
- The magnitude family assumes no extinction, no K-correction, and no cosmological term; the two conversion functions merely add or subtract `DistanceModulus`, so their accuracy is entirely that of the externally supplied absolute magnitude and distance.
- The telescope family contains visible-light empirical values: Dawes and Rayleigh use fixed coefficients (`116` and `138.4`, aperture in mm) and do not vary with wavelength; `LightGatheringPowerRatio` compares aperture squares only and ignores central obstruction, transmission, and secondary-mirror losses; `LimitingMagnitudeEmpirical` ignores sky background, magnification, transmission, and observer skill.
- The stellar family solves `L = 4πR²σT⁴` in both directions, assuming spherical symmetry with no limb-darkening correction, rotation, or magnetic effects; the Solar and MKS variants share the same solar constants, so any difference between them comes only from that constant convention.
- Airmass family: the plane-parallel model is purely geometric `sec(z)`, usable only at moderate and high altitude, and it diverges near the horizon; Kasten-Young (1989) and Pickering (2002) are empirical fits that agree at moderate altitude and differ most near the horizon. With an apparent altitude already in hand, call `AirmassKastenYoung` / `AirmassPickering` directly;
with only a true altitude plus refraction, use `coord`'s [airmass](coord.md#airmass).
- Every function in this package is a stateless pure function: it caches nothing and reads no global time-scale state, so it can be called in any order and, with caller-side synchronization, concurrently.