天文算法19

第二十一章 岁差

1. 名词解释

Precession

2. 赤道坐标岁差的近似计算

  当两个历元相差不远,并且如果星体没有太靠近天极,下面的公式可以用来计算在这两个历元之间的,相对于起始历元的截至历元的年度平均岁差:$$Δα = m + n\sin α\tan δ$$ 其中$m = 3s.07496 + 0s.00186T, n = 1s.33621 - 0s.00057T$ $$Δδ = n*cos(α)$$ 其中$n = 20”.0431 - 0”.0085T$
T是J2000.0起算的儒略世纪数

// ApproxAnnualPrecession returns approximate annual precision in right
// ascension and declination.
//
// The two epochs should be within a few hundred years.
// The declinations should not be too close to the poles.
// 近似计算截至历元相对于起始历元的年度平均岁差,俩历元不能相差太远,且天体不能靠近天极
func ApproxAnnualPrecession(eq *coord.Equatorial, epochFrom, epochTo float64) (Δα unit.HourAngle, Δδ unit.Angle) {
	m, nα, nδ := mn(epochFrom, epochTo)
	sα, cα := eq.RA.Sincos()
	// (21.1) p. 132
	Δα = m + nα.Mul(sα*eq.Dec.Tan())
	Δδ = nδ.Mul(cα)
	return
}

// mn as separate function for testing purposes
// 计算截至历元相对于起始历元的平均年度岁差要用的变量 m,n
func mn(epochFrom, epochTo float64) (m, nα unit.HourAngle, nδ unit.Angle) {
	T := (epochTo - epochFrom) * .01
	m = unit.HourAngleFromSec(3.07496 + 0.00186*T)
	nα = unit.HourAngleFromSec(1.33621 - 0.00057*T)
	nδ = unit.AngleFromSec(20.0431 - 0.0085*T)
	return
}

// ApproxPosition uses ApproxAnnualPrecession to compute a simple and quick
// precession while still considering proper motion.
//
// Both eqFrom and eqTo must be non-nil, although they may point to the same
// struct.  EqTo is returned for convenience.
// 两历元之间坐标的换算,考虑岁差和自行运动(mα,mδ)
func ApproxPosition(eqFrom, eqTo *coord.Equatorial, epochFrom, epochTo float64, mα unit.HourAngle, mδ unit.Angle) *coord.Equatorial {
	Δα, Δδ := ApproxAnnualPrecession(eqFrom, epochFrom, epochTo)
	dy := epochTo - epochFrom
	eqTo.RA = eqFrom.RA.Add((Δα + mα).Mul(dy))
	eqTo.Dec = eqFrom.Dec + (Δδ + mδ).Mul(dy)
	return eqTo
}

3. 赤道坐标岁差的精确计算

设T是J2000.0起算的儒略世纪数,t是某一起始历元到终止历元之间的时间差,单位也是儒略世纪数。 \begin{cases} ζ = ( 2306”.2181 + 1”.39656T - 0”.000139T^2)t + (0”.30188 - 0”.000344T)t^2 + 0”.017998t^3\\[2ex] z = ( 2306”.2181 + 1”.39656T - 0”.000139T^2)t + (1”.09468 + 0”.000066T)t^2 + 0”.018203t^3\\[2ex] θ = ( 2004”.3109 - 0”.85330T - 0”.000217T^2)t - (0”.42665 + 0”.000217T)t^2 - 0”.041833t^3 \end{cases} 当T=0时,即起始历元正好就是J2000.0, \begin{cases} ζ = 2306”.2181t + 0”.30188t^2 + 0”.017998t^3\\[2ex] z = 2306”.2181t + 1”.09468t^2 + 0”.018203t^3\\[2ex] θ = 2004”.3109t - 0”.42665t^2 - 0”.041833t^3 \end{cases} 再计算 \begin{cases} A &= \cos δ_0\sin(α_0 + ζ)\\[2ex] B &= \cos θ\cos δ_0\cos(α_0 +ζ) - \sin θ\sin δ_0\\[2ex] C &= \sin θ\cos δ_0\cos(α_0 +ζ) + \cos θ\sin δ_0 \end{cases} 则$$\tan(α-z) = A/B,\sin δ = C$$ 如果星体接近天极,使用$\cos δ = \sqrt {A^2+B^2}$代替$\sin δ = C$
$α,δ$即为经过岁差转换后的赤道坐标

// Precessor represents precession from one epoch to another.
//
// Construct with NewPrecessor, then call method Precess.
// After construction, Precess may be called multiple times to precess
// different coordinates with the same initial and final epochs.
// 计算赤道坐标精确岁差要用到的变量
type Precessor struct {
	ζ      unit.RA
	z      unit.Angle
	sθ, cθ float64
}

const d = math.Pi / 180
const s = d / 3600

// Package variables allow these slices to be reused.  (As composite
// literals inside of NewPrecessor they would be reallocated on every
// function call.)
var (
	// coefficients from (21.2) p. 134
	ζT = []float64{2306.2181 * s, 1.39656 * s, -0.000139 * s}
	zT = []float64{2306.2181 * s, 1.39656 * s, -0.000139 * s}
	θT = []float64{2004.3109 * s, -0.8533 * s, -0.000217 * s}

	// coefficients from (21.3) p. 134
	ζt = []float64{2306.2181 * s, 0.30188 * s, 0.017998 * s}
	zt = []float64{2306.2181 * s, 1.09468 * s, 0.018203 * s}
	θt = []float64{2004.3109 * s, -0.42665 * s, -0.041833 * s}
)

// NewPrecessor constructs a Precessor object and initializes it to precess
// coordinates from epochFrom to epochTo.
// 构造赤道坐标岁差计算要素
func NewPrecessor(epochFrom, epochTo float64) *Precessor {
	// (21.2) p. 134
	ζCoeff := ζt
	zCoeff := zt
	θCoeff := θt
	if epochFrom != 2000 {
		T := (epochFrom - 2000) * .01
		ζCoeff = []float64{
			base.Horner(T, ζT...),
			0.30188*s - 0.000344*s*T,
			0.017998 * s}
		zCoeff = []float64{
			base.Horner(T, zT...),
			1.09468*s + 0.000066*s*T,
			0.018203 * s}
		θCoeff = []float64{
			base.Horner(T, θT...),
			-0.42665*s - 0.000217*s*T,
			-0.041833 * s}
	}
	t := (epochTo - epochFrom) * .01
	p := &Precessor{
		ζ: unit.RA(base.Horner(t, ζCoeff...) * t),
		z: unit.Angle(base.Horner(t, zCoeff...) * t),
	}
	θ := base.Horner(t, θCoeff...) * t
	p.sθ, p.cθ = math.Sincos(θ)
	return p
}

// Precess precesses coordinates eqFrom, leaving result in eqTo.
//
// The same struct may be used for eqFrom and eqTo.
// EqTo is returned for convenience.
// 赤道坐标的岁差转换计算
func (p *Precessor) Precess(eqFrom, eqTo *coord.Equatorial) *coord.Equatorial {
	// (21.4) p. 134
	sδ, cδ := eqFrom.Dec.Sincos()
	sαζ, cαζ := (eqFrom.RA + p.ζ).Sincos()
	A := cδ * sαζ
	B := p.cθ*cδ*cαζ - p.sθ*sδ
	C := p.sθ*cδ*cαζ + p.cθ*sδ
	eqTo.RA = unit.RAFromRad(math.Atan2(A, B) + p.z.Rad())
	if math.Abs(C) < base.CosSmallAngle {
		eqTo.Dec = unit.Angle(math.Asin(C))
	} else {
		eqTo.Dec = unit.Angle(math.Acos(math.Hypot(A, B))) // near pole
		if C < 0 {
			eqTo.Dec = -eqTo.Dec
		}
	}
	return eqTo
}

// Position precesses equatorial coordinates from one epoch to another,
// including proper motions.
//
// If proper motions are not to be considered or are not applicable, pass 0, 0
// for mα, mδ
//
// Both eqFrom and eqTo must be non-nil, although they may point to the same
// struct.  EqTo is returned for convenience.
// 考虑自行运动的赤道坐标的转换
func Position(eqFrom, eqTo *coord.Equatorial, epochFrom, epochTo float64, mα unit.HourAngle, mδ unit.Angle) *coord.Equatorial {
	p := NewPrecessor(epochFrom, epochTo)
	t := epochTo - epochFrom
	eqTo.RA = unit.RAFromRad(eqFrom.RA.Rad() + mα.Rad()*t)
	eqTo.Dec = eqFrom.Dec + mδ*unit.Angle(t)
	return p.Precess(eqTo, eqTo)
}

4. 黄道坐标岁差的精确计算

同上,我们先计算 \begin{cases} η = (47”.0029 - 0”.06603T + 0”.000598T^2)t + (-0”.03302 + 0”.000598T)t^2 +0”.000060t^3\\[2ex] П = 174°.876384 + 3289”.4789T + 0”.60622T^2 (869”.8089 + 0”.50491T)t + 0”.03536t^2\\[2ex] p = (5029”.0966 + 2”.22226T - 0”.000042T^2)t + (1”.11113 - 0”.000042T)t^2 -0”.000006t^3 \end{cases} 当$T=0$时, \begin{cases} η = 47”.0029t -0”.03302t^2 +0”.000060t^3\\[2ex] П = 174°.876384 -869”.8089t +0”.03536t^2\\[2ex] p = 5029”.0966t +1”.11113t^2 -0”.000006t^3 \end{cases} 再计算 \begin{cases} A′ &= \cos η\cos β_0\sin(П-λ_0) - \sin η\sin β_0\\[2ex] B′ &= \cos β_0\cos(П-λ_0)\\[2ex] C′ &= \cos η\sin β_0 + \sin η\cos β_0\sin(П-λ_0) \end{cases} 则$$\tan(p +П-λ) = A′/B′,\sin β = C′$$ 如果星体接近天极,使用$\cos β = \sqrt {A′^2+B′^2}$代替$\sin β = C′$
$λ,β$即为经过岁差转换后的黄道坐标

// EclipticPrecessor represents precession from one epoch to another.
//
// Construct with NewEclipticPrecessor, then call method Precess.
// After construction, Precess may be called multiple times to precess
// different coordinates with the same initial and final epochs.
// 计算黄道坐标精确岁差要用到的变量
type EclipticPrecessor struct {
	sη, cη float64
	π, p   unit.Angle
}

var (
	// coefficients from (21.5) p. 136, scaled to radians
	ηT = []float64{47.0029 * s, -0.06603 * s, 0.000598 * s}
	πT = []float64{174.876384 * d, 3289.4789 * s, 0.60622 * s}
	pT = []float64{5029.0966 * s, 2.22226 * s, -0.000042 * s}

	// coefficients from (21.6) p. 136, scaled to radians
	ηt = []float64{47.0029 * s, -0.03302 * s, 0.000060 * s}
	πt = []float64{174.876384 * d, -869.8089 * s, 0.03536 * s}
	pt = []float64{5029.0966 * s, 1.11113 * s, -0.000006 * s}
)

// NewEclipticPrecessor constructs an EclipticPrecessor object and initializes
// it to precess coordinates from epochFrom to epochTo.
// 构造黄道坐标岁差计算要素
func NewEclipticPrecessor(epochFrom, epochTo float64) *EclipticPrecessor {
	// (21.5) p. 136
	ηCoeff := ηt
	πCoeff := πt
	pCoeff := pt
	if epochFrom != 2000 {
		T := (epochFrom - 2000) * .01
		ηCoeff = []float64{
			base.Horner(T, ηT...),
			-0.03302*s + 0.000598*s*T,
			0.000060 * s}
		πCoeff = []float64{
			base.Horner(T, πT...),
			-869.8089*s - 0.50491*s*T,
			0.03536 * s}
		pCoeff = []float64{
			base.Horner(T, pT...),
			1.11113*s - 0.000042*s*T,
			-0.000006 * s}
	}
	t := (epochTo - epochFrom) * .01
	p := &EclipticPrecessor{
		π: unit.Angle(base.Horner(t, πCoeff...)),
		p: unit.Angle(base.Horner(t, pCoeff...) * t),
	}
	η := unit.Angle(base.Horner(t, ηCoeff...) * t)
	p.sη, p.cη = η.Sincos()
	return p
}

// EclipticPrecess precesses coordinates eclFrom, leaving result in eclTo.
//
// The same struct may be used for eclFrom and eclTo.
// EclTo is returned for convenience.
// 黄道坐标的岁差转换
func (p *EclipticPrecessor) Precess(eclFrom, eclTo *coord.Ecliptic) *coord.Ecliptic {
	// (21.7) p. 137
	sβ, cβ := eclFrom.Lat.Sincos()
	sd, cd := (p.π - eclFrom.Lon).Sincos()
	A := p.cη*cβ*sd - p.sη*sβ
	B := cβ * cd
	C := p.cη*sβ + p.sη*cβ*sd
	eclTo.Lon = p.p + p.π - unit.Angle(math.Atan2(A, B))
	if math.Abs(C) < base.CosSmallAngle {
		eclTo.Lat = unit.Angle(math.Asin(C))
	} else {
		eclTo.Lat = unit.Angle(math.Acos(math.Hypot(A, B))) // near pole
		if C < 0 {
			eclTo.Lat = -eclTo.Lat
		}
	}
	return eclTo
}

// ReduceElements reduces orbital elements of a solar system body from one
// equinox to another.
//
// This function is described in chapter 24, but is located in this
// package so it can be a method of EclipticPrecessor.
func (p *EclipticPrecessor) ReduceElements(eFrom, eTo *elementequinox.Elements) *elementequinox.Elements {
	ψ := p.π + p.p
	si, ci := eFrom.Inc.Sincos()
	snp, cnp := (eFrom.Node - p.π).Sincos()
	// (24.1) p. 159
	eTo.Inc = unit.Angle(math.Acos(ci*p.cη + si*p.sη*cnp))
	// (24.2) p. 159
	eTo.Node = ψ +
		unit.Angle(math.Atan2(si*snp, p.cη*si*cnp-p.sη*ci))
	// (24.3) p. 160
	eTo.Peri = eFrom.Peri +
		unit.Angle(math.Atan2(-p.sη*snp, si*p.cη-ci*p.sη*cnp))
	return eTo
}

// EclipticPosition precesses ecliptic coordinates from one epoch to another,
// including proper motions.
//
// While eclFrom is given as ecliptic coordinates, proper motions mα, mδ are
// still expected to be equatorial.  If proper motions are not to be considered
// or are not applicable, pass 0, 0.
//
// Both eclFrom and eclTo must be non-nil, although they may point to the same
// struct.  EclTo is returned for convenience.
// 考虑自行运动的黄道坐标的转换,
// 注意此处的mα,mδ是赤道坐标系中的数值,要先转换为黄道坐标mλ, mβ
func EclipticPosition(eclFrom, eclTo *coord.Ecliptic, epochFrom, epochTo float64, mα unit.HourAngle, mδ unit.Angle) *coord.Ecliptic {
	p := NewEclipticPrecessor(epochFrom, epochTo)
	*eclTo = *eclFrom
	if mα != 0 || mδ != 0 {
		mλ, mβ := eqProperMotionToEcl(mα, mδ, epochFrom, eclFrom)
		t := epochTo - epochFrom
		eclTo.Lon += mλ.Mul(t)
		eclTo.Lat += mβ.Mul(t)
	}
	return p.Precess(eclTo, eclTo)
}

// 将自行运动由赤道坐标转黄道坐标
func eqProperMotionToEcl(mα unit.HourAngle, mδ unit.Angle, epoch float64, pos *coord.Ecliptic) (mλ, mβ unit.Angle) {
	ε := nutation.MeanObliquity(base.JulianYearToJDE(epoch))
	sε, cε := ε.Sincos()
	α, δ := coord.EclToEq(pos.Lon, pos.Lat, sε, cε)
	sα, cα := α.Sincos()
	sδ, cδ := δ.Sincos()
	cβ := pos.Lat.Cos()
	mλ = (mδ.Mul(sε*cα) + unit.Angle(mα).Mul(cδ*(cε*cδ+sε*sδ*sα))).Div(cβ * cβ)
	mβ = (mδ.Mul(cε*cδ+sε*sδ*sα) - unit.Angle(mα).Mul(sε*cα*cδ)).Div(cβ)
	return
}

5. 天体自行运动导致的坐标的转换

// ProperMotion3D takes the 3D equatorial coordinates of an object
// at one epoch and computes its coordinates at a new epoch, considering
// proper motion and radial velocity.
//
// Radial distance (r) must be in parsecs, radial velocitiy (mr) in
// parsecs per year.
//
// Both eqFrom and eqTo must be non-nil, although they may point to the same
// struct.  EqTo is returned for convenience.
// 自行运动导致的赤道坐标变化的精确计算(不在当成常量乘以时间间隔)
func ProperMotion3D(eqFrom, eqTo *coord.Equatorial, epochFrom, epochTo, r, mr float64, mα unit.HourAngle, mδ unit.Angle) *coord.Equatorial {
	sα, cα := eqFrom.RA.Sincos()
	sδ, cδ := eqFrom.Dec.Sincos()
	x := r * cδ * cα
	y := r * cδ * sα
	z := r * sδ
	mrr := mr / r
	zmδ := z * mδ.Rad()
	mx := x*mrr - zmδ*cα - y*mα.Rad()
	my := y*mrr - zmδ*sα + x*mα.Rad()
	mz := z*mrr + r*mδ.Rad()*cδ
	t := epochTo - epochFrom
	xp := x + t*mx
	yp := y + t*my
	zp := z + t*mz
	eqTo.RA = unit.RAFromRad(math.Atan2(yp, xp))
	eqTo.Dec = unit.Angle(math.Atan2(zp, math.Hypot(xp, yp)))
	return eqTo
}

相关

下一页
上一页
comments powered by Disqus