算法系列之十八:用天文方法計算二十四節氣(下)

【接上篇】

 

        經過上述計算轉換得到座標值是理論值,或者說是天體的幾何位置,但是FK5系統是一個目視系統,也就是說體現的是人眼睛觀察效果(光學位置),這就需要根據地球的物理環境、大氣環境等信息做進一步的修正,使其和人類從地球上觀察星體的觀測結果一致。

        首先需要進行章動修正。章動是指地球沿自轉軸的指向繞黃道極緩慢旋轉過程中,由於地球上物質分佈不均勻性和月球及其它行星的攝動力造成的輕微抖動。英國天文學家詹姆斯·布拉德利(1693—1762)最早發現了章動,章動可以沿着黃道分解爲水平分量和垂直分量,黃道上的水平分量記爲Δψ,稱爲黃經章動,它影響了天球上所有天體的經度。黃道上的垂直分量記爲Δε,稱爲交角章動,它影響了黃赤交角。目前編制天文年曆所依據的章動理論是伍拉德在1953年建立的,它是以剛體地球模型爲基礎的。1977年,國際天文聯合會的一個專家小組建議採用非剛體地球模型――莫洛堅斯基II模型代替剛體地球模型計算章動,1979年的國際天文學聯合會第十七屆大會正式通過了這一建議,並決定於1984年正式實施。

        地球章動主要是月球運動引起的,也具有一定的週期性,可以描述爲一些週期項的和,主要項的週期是6798.4日(18.6年),但其它項是一些短週期項(小於10天)。本文采用的計算方法取自國際天文聯合會的IAU1980章動理論,週期項係數數據來源於《天文算法》一書第21章的表21-A,該表忽略了IAU1980章動理論中係數小於0.0003"的週期項,因此只有63項。每個週期項包括計算黃經章動(Δψ)的正弦係數(相位內項係數)、計算交角章動的(Δε)餘弦係數(相位外項係數)以及計算輻角的5個基本角距(MM'DFΩ)的線性組合係數。5個基本角距的計算公式是:

 

平距角(日月對地心的角距離)
D = 297.85036 + 455267.111480 * T - 0.0019142 * T2 + T3 / 189474        (3.10式)
太陽(地球)平近點角:
M = 357.52772 + 35999.050340 * T - 0.0001603 * T2 - T3 / 300000         (3.11式)
月球平近點角
M'= 134.96298 + 477198.867398 * T + 0.0086972 * T2 + T3 / 56250        (3.12式)

月球緯度參數:
F = 93.27191 + 483202.017538 * T - 0.0036825 * T2 + T3 / 327270          (3.13式)
黃道與月球平軌道升交點黃經:
Ω= 125.04452 - 1934.136261 * T + 0.0020708 * T2 + T3 / 450000            (3.14式)

 

以上各式中的T是儒略世紀數,計算出來的5個基本角距的單位都是度,在計算正弦或餘弦時要轉換爲弧度單位。計算每一個週期項的黃經章動過程是這樣的,首先將3.10-3.14式計算出來的值與對應的5個基本角距係數組合,計算出輻角。以本文使用的章動週期項係數表中的第七項爲例,5個基本角距對應的係數分別是1、0、-2、2和2,輻角θ的值就是:-2D + M + 2F + 2Ω。計算出輻角後就可以計算週期項的值:

 

S = (S1+ S2 * T) * sin(θ)                          (3.15式)

 

仍以第七項爲例,S的值就是(-517 + 1.2 * T* sin(θ)。對每一項的值S累加就可得到黃經章動,單位是0.0001"。交角章動的計算方法與黃經章動的計算類似,輻角θ的值是一樣的,只是計算章動使用的是餘弦係數:

C = (C1 + C2 * T) * cos(θ)                          (3.16式)

 

CalcEarthLongitudeNutation()函數就是計算黃經章動的實現代碼:

 double CalcEarthLongitudeNutation(double dt)

 {

     double T = dt * 10;

     double D,M,Mp,F,Omega;

 

     GetEarthNutationParameter(dt, &D, &M, &Mp, &F, &Omega);

 

     double resulte = 0.0 ;

     for(int i = 0; i < sizeof(nutation) / sizeof(nutation[0]); i++)

     {

         double sita = nutation[i].D * D + nutation[i].M * M + nutation[i].Mp * Mp + nutation[i].F * F + nutation[i].omega * Omega;

         

         resulte += (nutation[i].sine1 + nutation[i].sine2 * T ) * sin(sita);

     }

 

     /*先乘以章動表的係數 0.0001,然後換算成度的單位*/

     return resulte * 0.0001 / 3600.0;

 }

 GetEarthNutationParameter()輔助函數用於計算5個基本角距:

 void GetEarthNutationParameter(double dt, double *D, double *M, double *Mp, double *F, double *Omega)

 {

     double T = dt * 10; /*T是從J2000起算的儒略世紀數*/

     double T2 = T * T;

     double T3 = T2 * T;

 

     /*平距角(如月對地心的角距離)*/

     *D = 297.85036 + 445267.111480 * T - 0.0019142 * T2 + T3 / 189474.0;

 

     /*太陽(地球)平近點角*/

     *M = 357.52772 + 35999.050340 * T - 0.0001603 * T2 - T3 / 300000.0;

 

     /*月亮平近點角*/

     *Mp = 134.96298 + 477198.867398 * T + 0.0086972 * T2 + T3 / 56250.0;

 

     /*月亮緯度參數*/

     *F = 93.27191 + 483202.017538 * T - 0.0036825 * T2 + T3 / 327270.0;

 

     /*黃道與月亮平軌道升交點黃經*/

     *Omega = 125.04452 - 1934.136261 * T + 0.0020708 * T2 + T3 / 450000.0;

 }

同樣,計算交角章動的實現代碼是:

 double CalcEarthObliquityNutation(double dt)

 {

     double T = dt * 10; /*T是從J2000起算的儒略世紀數*/

     double D,M,Mp,F,Omega;

 

     GetEarthNutationParameter(dt, &D, &M, &Mp, &F, &Omega);

 

     double resulte = 0.0 ;

     for(int i = 0; i < sizeof(nutation) / sizeof(nutation[0]); i++)

     {

         double sita = nutation[i].D * D + nutation[i].M * M + nutation[i].Mp * Mp + nutation[i].F * F + nutation[i].omega * Omega;

 

         resulte += (nutation[i].cosine1 + nutation[i].cosine2 * T ) * cos(sita);

     }

 

     /*先乘以章動表的係數 0.001,然後換算成度的單位*/

     return resulte * 0.0001 / 3600.0;

 }

         除了章動修正,對於目測系統來說,還要進行光行差修正。光行差是指在同一瞬間,運動中的觀察者所觀測到的天體視方向與靜止的觀測者所觀測到天體的真方向之差。造成光行差的原因有兩個,一個是光的有限速度,另一個是觀察者的運動。在地球上的天文觀測者因和地球一起運動(自傳+公轉),他所看到的星光方向與假設地球不動時看到的方向不一樣。以太陽爲例,光線從太陽傳到地球需要約8分鐘的時間,在這8分鐘多的時間中,地球沿着公轉軌道移動了一段距離人們根據現在的觀察認定太陽在那個視位置,事實上那是8分鐘前太陽的位置。在精確的天文計算中,需要考慮這種光行差引起的視位置差異,在計算太陽的地心視黃經時,要對其進行光行差修正。地球上的觀測者可能會遇到幾種光行差,分別是因地球公轉引起的週年光行差,因地球自傳引起的週日光行差,還有因太陽系或銀河系運動形成的長期光行差等等,對於從地球上觀察太陽這種情況,只需要考慮週年光行差和週日光行差。因太陽公轉速度比較快,週年光行差最大可達到20.5角秒,在計算太陽視黃經時需要考慮修正。地球自傳速度比較慢,週日光行差最大約爲零點幾個角秒,因此計算太陽視黃經時忽略週日光行差。

        下面是一個粗略計算太陽地心黃經光行差修正量的公式,其中R是地球和太陽的距離:

 

AC = -20".4898 / R                                    (3.17式)

 

分子20.4898並不是一個常數,但是其只的變化非常緩慢,在0年是20".4893,在4000年是20".4904。前文提到過,太陽到地球的距離R可以用VSOP87D表的R0-R5週期項計算出來,R的單位是“天文單位(AU)”,和計算太陽地心黃經和地心黃緯類似,太陽到地球的距離可以這樣算出來:

 double CalcSunEarthRadius(double dt)

 {

     double R0 = CalcPeriodicTerm(Earth_R0, sizeof(Earth_R0) / sizeof(VSOP87_COEFFICIENT), dt);

     double R1 = CalcPeriodicTerm(Earth_R1, sizeof(Earth_R1) / sizeof(VSOP87_COEFFICIENT), dt);

     double R2 = CalcPeriodicTerm(Earth_R2, sizeof(Earth_R2) / sizeof(VSOP87_COEFFICIENT), dt);

     double R3 = CalcPeriodicTerm(Earth_R3, sizeof(Earth_R3) / sizeof(VSOP87_COEFFICIENT), dt);

     double R4 = CalcPeriodicTerm(Earth_R4, sizeof(Earth_R4) / sizeof(VSOP87_COEFFICIENT), dt);

 

     double R = (((((R4 * dt) + R3) * dt + R2) * dt + R1) * dt + R0) / 100000000.0;

 

     return R;

 }

也可以不使用VSOP,而用下面的公式直接計算日地距離R:

R = 1.000001018 (1 - e2) / (1 + e * cos(v))                 (3.18式)

 

其中e是地球軌道的離心率:

 

e = 0.016708617 - 0.000042037 * T - 0.0000001236 * T2      (3.19式)

 

v的計算公式是v = M + C,其中M是太陽平近地角:

 

M = 357.52910 + 35999.05030 * T - 0.0001559 * T2 - 0.00000048 * T3     (3.20式)

 

中心C的太陽方程:

 

C = (1.914600 - 0.004817 * T - 0.000014 * T2) * sin(M)

+ (0.019993 - 0.000101 * T) * sin(2M)

+ 0.000290 * sin(3M)                                              (3.21式)

 

以上各式中的T都是儒略世紀數,M和C的單位都是度,帶入3.18式計算時需要轉換成弧度單位,計算出R以後,就可以這樣計算光行差修正量:

 

AC = K / R (K是光行差常數,K = 20".49552)          (3.22式)

 

無論是使用3.17式還是使用3.22式,最終計算出來的太陽光行差修正單位都是角秒。

        由VSOP87理論計算出來的幾何位置黃經,經過座標轉換,章動修正和光行差修正後,就可以得到比較準確的太陽地心視黃經,GetSunEclipticLongitudeEC()函數就是整個過程的代碼:

 double GetSunEclipticLongitudeEC(double jde)

 {

     double dt = (jde - JD2000) / 365250.0; /*儒略千年數*/

 

     // 計算太陽的地心黃經

     double longitude = CalcSunEclipticLongitudeEC(dt);

 

     // 計算太陽的地心黃緯

     double latitude = CalcSunEclipticLatitudeEC(dt) * 3600.0;

 

     // 修正精度

     longitude += AdjustSunEclipticLongitudeEC(dt, longitude, latitude);

 

     // 修正天體章動

     longitude += CalcEarthLongitudeNutation(dt);

 

     // 修正光行差

     /*太陽地心黃經光行差修正項是: -20".4898/R*/

     longitude -= (20.4898 / CalcSunEarthRadius(dt)) / (20 * PI);

 

     return longitude;

 }

 

參數jde是力學時時間,單位是儒略日,返回太陽地心視黃經,單位是度。

        到現在爲止,我們已經知道如何使用VSOP82/87理論計算以儒略日爲單位的任意時刻的太陽地心視黃經,但是這和實際曆法計算需求還不一致,曆法計算需要根據太陽地心視黃經反求出此時的時間。VSOP82/87理論沒有提供反向計算的方法,但是可以採用根據時間正向計算太陽視黃經,配合誤差修正進行迭代計算的方法,使正向計算出來的結果向已知結果收斂,當達到一定的迭代次數或計算結果與已知結果誤差滿足精度要求時,停止迭代,此時的正向輸入時間就是所求的時間。地球公轉軌道是近似橢圓軌道,軌道方程不具備單調性,但是在某個節氣附件的一小段時間區間中,軌道方程具有單調性,這個是本文迭代算法的基礎。

        實際上,我們要做的事情就是求解方程的根,但是我們面臨的這個方程沒有求根公式。對此類問題,數學上通常採用的迭代求解方法有二分逼近法和牛頓迭代法,事實上二分逼近法可以用更好的策略,比如用黃金分割代替二分法進行逼近區間的選擇。接下來我們將分別介紹這兩種方法在計算二十四節氣中的應用,首先介紹黃金分割逼近法。

        已知太陽視黃經的值,反求對應的時間的過程是這樣的,首先根據節氣對應的視黃經角度值W,估算出節氣可能的時間區間[A, B],然後找到這個時間區間內黃金分割點對應的時間值C,C的計算採用3.23式:

 

C = ((B - A) * 0.618) + A                               (3.23式)

C值估算出太陽視黃經W’,如果W’ > W,則調整調迭代時間區間爲[A, C],如果W’ < W,則調整迭代時間區間爲[C, B],然後重複上述過程,直到W’ 與W的差值滿足精度要求爲止(區間上下限A和B的差值小於門限制也可以作爲迭代退出條件)。採用黃金分割法進行逼近求值的算法實現如下:

34 double CalculateSolarTerms(int year, int angle)

35 {

36     double lJD, rJD;

37     EstimateSTtimeScope(year, angle, lJD, rJD); /*估算迭代起始時間區間*/

38 

39     double solarTermsJD = 0.0;

40     double longitude = 0.0;

41 

42     do

43     {

44         solarTermsJD = ((rJD - lJD) * 0.618) + lJD;

45         longitude = GetSunEclipticLongitudeECDegree(solarTermsJD);

46         /*

47             對黃經0度迭代逼近時,由於角度360度圓周性,估算黃經值可能在(345,360]和[0,15)兩個區間,

48             如果值落入前一個區間,需要進行修正

49         */

50         longitude = ((angle == 0) && (longitude > 345.0)) ? longitude - 360.0 : longitude;

51 

52         (longitude > double(angle)) ? rJD = solarTermsJD : lJD = solarTermsJD;

53     }while((rJD - lJD) > 0.0000001);

54 

55     return solarTermsJD;

56 }

這裏要特別說明一下,由於角度的360度圓周性,當在太陽黃經0度附近逼近時,區間的上下界可能分別位於(345, 360]和[0, 15)兩個區間上,此時需要將(345, 360]區間修正爲(-15, 0],使得逼近區間邊界的選取能夠正常進行。EstimateSTtimeScope()函數估算節氣的時間區間,估算的依據是每個月的節氣時間比較固定,最多相差一兩天,考慮的幾千年後歲差的影響,這個估算範圍還可以再放寬一點,比如,對於月內的第一個節氣,可以將時間範圍估算爲4日到9日,對於月內的第二個節氣,可以將時間範圍估算爲16日到24日,保證迭代範圍內有解。EstimateSTtimeScope()函數算法簡單,這裏就不列出代碼了。

        二分逼近或黃金分割逼近算法實現簡單,很容易控制,但是也存在效率低,收斂速度慢的問題,現在我們介紹牛頓迭代法,牛頓迭代法是一種在實數域和複數域上近似求解方程的方法。假設我們要求解的方程是f(x) = 0,如果f(x)的導函數f’(x)是連續的,則在真實解x附近的區域內任意一點x0開始迭代,則牛頓迭代法必收斂,特別當f’(x)不等於0的時候,牛頓迭代法是平方收斂的,也就是說,每迭代一次,結果的有效數字將增加一倍。

        簡單的說,對於方程f(x) = 0,f(x)的導函數是f’(x),則牛頓迭代法的迭代公式是:

 

Xn+1 = xn – f(xn)/f’(xn)                              (3.24式)

 

現在問題就是如何確定方程f(x)。對於我們面臨的問題,可以理解爲已知angle,通過GetSunEclipticLongitudeEC(solarTermsJD)函數反向求解solarTermsJD的值,因此我們的方程可以理解爲:

 

f(x) = GetSunEclipticLongitudeEC(x) – angle = 0

 

確定了方程f(x),剩下的問題就是求導函數f’(x)。嚴格的求解,應該根據GetSunEclipticLongitudeEC()函數,以儒略千年數dt爲自變量,按照函數求導的規則求出導函數。因爲GetSunEclipticLongitudeEC()函數內部是調用其他函數,因此可以理解爲是一個多個函數組合的複合函數,類似f(x) = g(x) + h(x, k(x)) + p(x)這樣的形式,可以按照求導規則逐步對其求導得到導函數。但是我不打算這麼做,因爲我有更簡單的方法,那就是使用計算導數的近似公式。其實求導函數的目的就是爲了得到某一點的導數,如果有近似公式可以直接得到這一點的導數,就不用費勁求導函數了。

        如果函數f(x)是單調函數,或者是在某個區間上是單調函數,則在此函數的其單調區間上某一點的導數值可以用近似公式計算,這個近似公式是:

 

f’(x0) = (f(x0 + 0.000005) – f(x0 – 0.000005)) / 0.00001            (3.25式)

 

這是一個精度很高的近似公式,完全可以滿足民用曆法計算的精度要求。

        根據以上分析結果,使用牛頓迭代法求解節氣的算法就很容易實現了,以下就是牛頓迭代法求解節氣的代碼:

74 double CalculateSolarTermsNewton(int year, int angle)

75 {

76     double JD0, JD1,stDegree,stDegreep;

77 

78     JD1 = GetInitialEstimateSolarTerms(year, angle);

79     do

80     {

81         JD0 = JD1;

82         stDegree = GetSunEclipticLongitudeECDegree(JD0) - angle;

83         stDegreep = (GetSunEclipticLongitudeECDegree(JD0 + 0.000005)

84                       - GetSunEclipticLongitudeECDegree(JD0 - 0.000005)) / 0.00001;

85         JD1 = JD0 - stDegree / stDegreep;

86     }while((fabs(JD1 - JD0) > 0.0000001));

87 

88     return JD1;

89 }

經過驗證,牛頓迭代法具有非常好的收斂效果,一般只需3次迭代就可以得到滿足精度的結果。

        至此,我們就有了完整的計算節氣發生時間的方法,輸入年份和節氣對應的太陽黃經度數,即可求的節氣發生的精確時間。最後說明一下,以上算法中討論的時間都是力學時時間(TD),與國際協調時(UTC)以及各個時區的本地時間都有不同,以上計算出來的時間都需要調整成本地時間,比如中國的中原地區就是東八區標準時(UTC + 8)。關於力學時、國際協調時(世界時)的定義,請參考文末的小知識3:力學時、原子時和國際協調時。應用本文的算法計算出2012年各個節氣的時間如下(已經轉換爲東八區標準時),與紫金山天文臺發佈的《2012中國天文年曆》中發佈的時間在分鐘級別上完全吻合(此年曆只精確到分鐘):

 

2012-01-06, 06:43:54.28   小寒

2012-01-21, 00:09:49.08   大寒

2012-02-04, 18:22:22.53   立春

2012-02-19, 14:17:35.37   雨水

2012-03-05, 12:21:01.56   驚蟄

2012-03-20, 13:14:24.17   春分

2012-04-04, 17:05:34.65   清明

2012-04-20, 00:12:03.28   穀雨

2012-05-05, 10:19:39.54   立夏

2012-05-20, 23:15:30.28   小滿

2012-06-05, 14:25:52.96   芒種

2012-06-21, 07:08:46.98   夏至

2012-07-07, 00:40:42.66   小暑

2012-07-22, 18:00:50.72   大暑

2012-08-07, 10:30:31.88   立秋

2012-08-23, 01:06:48.41   處暑

2012-09-07, 13:28:59.41   白露

2012-09-22, 22:48:57.14   秋分

2012-10-08, 05:11:41.45   寒露

2012-10-23, 08:13:32.83   霜降

2012-11-07, 08:25:56.47   立冬

2012-11-22, 05:50:08.09   小雪

2012-12-07, 01:18:55.23   大雪

2012-12-21, 19:11:35.61   冬至

 

小知識3:力學時、原子時和國際協調時

        力學時全稱是“牛頓力學時”,也被稱作是“曆書時”。它描述天體運動的動力學方程中作爲時間自變量所體現的時間,或天體歷表中應用的時間,是由天體力學的定律確定的均勻時間。力學時的初始曆元取爲1900年初附近,太陽幾何平黃經爲279°41′48″.04的瞬間,秒長定義爲1900.0年迴歸年長度的1/31556925.9747。1958年國際天文學聯合會決議決定:自1960年開始用力學時代替世界時作爲基本的時間計量系統,規定天文年曆中太陽系天體的位置都按力學時推算。力學時與世界時之差由觀測太陽系天體(主要是月球)定出,因此力學時的測定精度較低,1967年起被原子時代替作爲基本時間計量系統。

        國際協調時又稱世界時,是以本初子午線的平子夜起算的平太陽時,又稱格林威治時間。世界各地地方時與世界時之差等於該地的地理經度。世界時1960年以前曾作爲基本時間計量系統被廣泛應用。由於地球自轉速度變化的影響,它不是一種均勻的時間系統。後來世界時先後被曆書時和原子時所取代。

        原子時是以物質的原子內部發射的電磁振盪頻率爲基準的時間計量系統。原子時的初始曆元規定爲1958年1月1日世界時0時,秒長定義爲銫-133原子基態的兩個超精細能級間在零磁場下躍遷輻射9192631770周所持續的時間。這是一種均勻的時間計量系統。1967年起,原子時已取代力學時作爲基本時間計量系統。

 

 

參考文章:

[1] Secular variations of the planetary orbitshttp://www.worldlingo.com/ma/enwiki/en/Secular_variations_of_the_planetary_orbits

[2] Jean.Meeus.Astronomical.Algorithms(天文算法)

[3] M.Chapront-Touze and J.Chapront.ELP 2000-85 - A semi-analytical lunar ephemeris adequate for historical times.Astronomy And Astrophysics,1998

[4] P.Bretagnon and G.Francou.Planetray theories in rectangular and spherical variables VSOP87 solutions. Astronomy And Astrophysics,1998

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章