如何计算日食的时间和类型(附R源代码)?

​ 日食的原理在小学课本中就有介绍,也就是月亮的影子遮挡住了太阳。日食每次必定发生在农历初一,因为农历初一是朔,此时从地心看起来月亮距离太阳最近。为什么要强调地心呢? 因为在地球表面的不同位置,有视差的存在。天文学家在计算天象的时候,很多时候会先计算地心坐标。具体到某一个地点,再归算到地平坐标。

​ 日食的原理说来简单,但是计算起来并不容易。在我国古代,对日食的时间和类型的计算,是检验历法是否准确的重要标准。很多部历法由于不能准确的预测日月食,而不得不被新的历法所替代。如《新唐书.历志一》记载:“唐终始二百九十馀年,而历八改。初曰戊寅元历,曰麟德甲子元历,曰开元大衍历。”《新唐书.历志三上》又记载:”开元九年,麟德历署日蚀不效,诏僧一行作新历,推大衍数立术以应之…”。被誉为唐代历法首屈一指的《大衍历》,最后也难免被更为精确的历法所代替。

​ 这里采用的算法,是基于比利时天文学家Jean Meeus提出的现代天文算法。详情请参见许剑伟老师翻译的《天文算法》第52章。

​ 全部R程序改变自剑伟老师的《寿星万年历》javascript源代码。

### 快速日食搜索,jd为朔时间

### 改编自许剑伟老师的《寿星万年历》javascript源代码

### 输入儒略日2000,计算最近的朔的时间,并判断该次朔是否会发生日食。

DateSolarEclipse <-

function (jd){

​ rad = 180***3600/**pi; #每弧度的角秒数

​ radd = 180**/**pi; #每弧度的度数

​ pi2 = pi*****2;

​ pi_2 = pi**/**2;

​ J2000 = 2451545;

​ re**=list()**;

​ W = floor**((jd+8)/29.5306)*pi***2; #合朔时的日月黄经差

​ #合朔时间计算,2000前+-4000年误差1小时以内,+-2000年小于10分钟

​ t = ( W + 1.08472 **)/**7771.37714500204; #平朔时间

​ re**$jd = re$jdNewMoon = t***36525;

​ t2**=t***t

​ t3**=t2***t

​ t4**=t3***t;

​ L = ( 93.2720993**+483202.0175273***t**-0.0034029***t2**-t3/3526000+t4/**863310000 )/180*pi;

​ re**$ac=**1

​ re**$type=**’N’;

if**(abs(sin(L))>0.4)** return**(re)**; #一般大于21度已不可能

​ t = t - ( -0.0000331*tt + 0.10976 cos( 0.785 + 8328.6914*t)** **)/**7771;

​ t2**=** t*****t;

​ L = **(-1.084719 +7771.377145013*t **-0.0000331*t2 +

(22640 ***** cos(0.785+ 8328.6914*t +0.000152*t2)

+4586 ***** cos(0.19 + 7214.063*t -0.000218*t2**)**

+2370 ***** cos(2.54 + 15542.754*t -0.000070*t2**)**

+ 769 ***** cos**(3.1 + 16657.383***t**)**

+ 666 ***** cos**(1.5 + 628.302***t**)**

+ 412 ***** cos**(4.8 + 16866.93***t**)**

+ 212 ***** cos**(4.1 -1114.63*t)**

+ 205 ***** cos**(0.2 + 6585.76***t**)**

+ 192 ***** cos**(4.9 + 23871.45***t**)**

+ 165 ***** cos**(2.6 + 14914.45***t**)**

+ 147 ***** cos**(5.5 -7700.39*t)**

+ 125 ***** cos**(0.5 + 7771.38***t**)**

+ 109 ***** cos**(3.9 + 8956.99***t**)**

+ 55 ***** cos**(5.6 -1324.18*t)**

+ 45 ***** cos**(0.9 + 25195.62***t**)**

+ 40 ***** cos**(3.8 -8538.24*t)**

+ 38 ***** cos**(4.3 + 22756.82***t**)**

+ 36 ***** cos**(5.5 + 24986.07***t**)**

-6893 ***** cos(4.669257+628.3076*t**)**

- 72 ***** cos**(4.6261 +1256.62*t)**

- 43 ***** cos**(2.67823 +628.31*t)***t

+ 21**)** / rad**)**;

​ t = t + ( W - L ) / ( 7771.38

- 914 ***** sin**(** 0.7848 + 8328.691425t + 0.0001523t2 )

- 179 ***** sin**(** 2.543 +15542.7543*t )

- 160 ***** sin**(** 0.1874 + 7214.0629*****t ) );

​ re**$jd = re$jdNewMoon = jd = t***36525; #朔时刻

​ #纬 52,15 (角秒)

​ t2**=t***t**/**10000;

​ t3**=t2***t**/**10000;

​ mB**=** ( 18461cos(0.0571+ 8433.46616****t -0.640*t2 -1*t3**)**

+ 1010***cos(**2.413 + 16762.1576 **t + 0.88 t2 + 25t3)*

+ 1000***cos(**5.440 **-**104.7747 **t + 2.16 t2 + 26t3)*

+ 624***cos(**0.915 + 7109.2881 **t + 0 t2 + 7t3)*

+ 199***cos(**1.82 + 15647.529 *****t **-**2.8 *t2 -19*t3)

+ 167***cos(**4.84 **-**1219.403 *****t **-**1.5 *t2 -18*t3)

+ 117***cos(**4.17 + 23976.220 *****t -1.3 t2 + 6t3)

+ 62***cos(**4.8 + 25090.849 **t + 2 t2 + 50t3)*

+ 33***cos(**3.3 + 15437.980 **t + 2 t2 + 32t3)*

+ 32***cos(**1.5 + 8223.917 **t + 4 t2 + 51t3)*

+ 30***cos(**1.0 + 6480.986 **t + 0 t2 + 7t3)*

+ 16***cos(**2.5 **-**9548.095 *****t **-**3 *t2 -43*t3)

+ 15***cos(**0.2 + 32304.912 **t + 0 t2 + 31t3)*

+ 12***cos(**4.0 + 7737.590 *t)

+ 9***cos(**1.9 + 15019.227 *t)

+ 8***cos(**5.4 + 8399.709 *t)

+ 8***cos(**4.2 + 23347.918 *t)

+ 7***cos(**4.9 **-**1847.705 *t)

+ 7***cos(**3.8 **-**16133.856 *t)

+ 7***cos(**2.7 + 14323.351 **t))*;

​ mB = mB / rad;

​ #月地距离 106, 23 (千米)

​ mR = **(**385001

+20905*cos**(5.4971+** 8328.691425t*+ 1.52 t2 + 25t3)**

+ 3699cos(4.900 + 7214.06287t **-**2.18 *t2 -19*t3)

+ 2956cos(0.972 + 15542.75429t -0.66 t2 + 6t3)

+ 570***cos(**1.57 + 16657.3828 **t + 3.0 t2 + 50t3)*

+ 246***cos(**5.69 **-**1114.6286 *****t **-**3.7 *t2 -44*t3)

+ 205***cos(**1.02 + 14914.4523 *****t -1 t2 + 6t3)

+ 171***cos(**3.33 + 23871.4457 **t + 1 t2 + 31t3)*

+ 152***cos(**4.94 + 6585.761 *****t **-**2 *t2 -19*t3)

+ 130***cos(**0.74 **-**7700.389 *****t **-**2 *t2 -25*t3)

+ 109***cos(**5.20 + 7771.377 *t)

+ 105***cos(**2.31 + 8956.993 **t + 1 t2 + 25t3)*

+ 80***cos(**5.38 **-**8538.241 **t + 2.8 t2 + 26t3)*

+ 49***cos(**6.24 + 628.302 *t)

+ 35***cos(**2.7 + 22756.817 *****t **-**3 *t2 -13*t3)

+ 31***cos(**4.1 + 16171.056 *****t -1 t2 + 6t3)

+ 24***cos(**1.7 + 7842.365 *****t **-**2 *t2 -19*t3)

+ 23***cos(**3.9 + 24986.074 **t + 5 t2 + 75t3)*

+ 22***cos(**0.4 + 14428.126 *****t **-**4 *t2 -38*t3)

+ 17***cos(**2.0 + 8399.679 **t))*;

​ mR = mR**/** 6378.1366;

​ t**=jd/**365250;

​ t2**=t***t;

​ t3**=t2***t;

​ #误0.0002AU

​ sR = **(**10001399 #日地距离

+167070*cos**(3.098464 + 6283.07585***t**)**

+ 1396***cos(**3.0552 + 12566.1517 *t)

+ 10302cos(1.10749 + 6283.07585t**)***t

+ 172***cos(**1.064 + 12566.152 ***t)***t

+ 436***cos(**5.785 + 6283.076 ***t)***t2

+ 14***cos(**4.27 + 6283.08 *t)*t3)

​ sR = sR ***** 1.49597870691**/6378.1366***10;

​ #经纬速度

​ t**=jd/**36525;

​ vL = **(**7771 #月日黄经差速度

-914*sin**(0.785 + 8328.6914***t**)**

-179*sin**(2.543 +15542.7543*t)**

-160*sin**(0.187 + 7214.0629***t**))**;

​ vB = (-755*sin**(0.057 + 8433.4662***t**)** #月亮黄纬速度

- 82sin*(**2.413 +16762.1576*t));

​ vR =(-27299*sin**(5.497 + 8328.691425***t**)**

- 4184sin(4.900 + 7214.06287t**)**

- 7204sin*(**0.972 +15542.75429*t));

​ vL = vL**/**36525

​ vB = vB**/**36525

​ vR = vR**/**36525; #每日速度

​ gm = mRsin(mB)*vL/sqrt(vBvB**+vL***vL**)**;

​ smR**=** sR**-**mR; #gm伽马值,smR日月距

​ mk = 0.2725076;

​ sk = 109.1222;

​ f1 = (sk+mk)/smR; r1 = mk+f1*mR; #tanf1半影锥角, r1半影半径

​ f2 = (sk-mk)/smR; r2 = mk-f2*mR; #tanf2本影锥角, r2本影半径

​ b = 0.9972;

​ Agm = abs**(gm)**;

​ Ar2 = abs**(r2)**;

​ fh2 = mR**-mk/**f2;

if**(Agm < 1){**

​ h = sqrt**(1-gm***gm**)**

}else{

​ h = 0

}

​ ## h = Agm<1 ? sqrt(1-gm*gm) : 0; #fh2本影顶点的z坐标

​ ## ls1,ls2,ls3,ls4;

if**(fh2<h)** {

​ re**$**type = ‘T’; #全食

}else{

​ re**$**type = ‘A’; #环食

}

​ ls1 = Agm**-(b+**r1 );

if**(abs(ls1)<0.016)** re**$ac=**0; #无食分界

​ ls2 = Agm**-(b+Ar2)**;

if**(abs(ls2)<0.016)** re**$ac=**0; #偏食分界

​ ls3 = Agm**-(**b );

if**(abs(ls3)<0.016)** re**$ac=**0; #无中心食分界

​ ls4 = Agm**-(b-Ar2)**;

if**(abs(ls4)<0.016)** re**$ac=**0; #有中心食分界(但本影未全部进入)

if (ls1>0) {

​ re**$**type = ‘N’; #无日食

} else {

if**(ls2>0)** {

​ re**$**type = ‘P’; #偏食

} else {

if**(ls3>0)** {

​ re**$type = paste(re$type, ‘0’, sep = “”)**; #无中心

} else {

if**(ls4>0)** {

​ re**$type = paste(re$type, ‘1’, sep = “”)**; #有中心(本影未全部进入)

} else { #本影全进入

if**(abs(fh2-h)<0.019)** re**$ac=**0;

if**(** abs**(fh2)<**h ){

​ dr = vR***h/vL/**mR;

​ H1 = mR**-dr-mk/**f2; #入点影锥z坐标

​ H2 = mR**+dr-mk/**f2; #出点影锥z坐标

if**(H1>0)** re**$type=**’H3’; #环全全

if**(H2>0)** re**$type=**’H2’; #全全环

if**(H1>0&H2>0)** re**$type=**’H’; #环全环

if**(abs(H1)<0.019)** re**$ac=**0;

if**(abs(H2)<0.019)** re**$ac=**0;

}

}

}

}

}

​ return (re);

}

#取整数部分

int2 <-

function (v) {

return (floor(v));

}

### 公历转儒略日

JD <-

function**(y,m,d){**

n**=0;G=**0;

if**(y***372**+m***31**+int2(d)>=588829)**

​ G = 1; #判断是否为格里高利历日1582372+1031+15

if**(m<=2)** {

​ m = m **+**12;

​ y = y - 1;

}

if**(G > 0)** {

​ n**=int2(y/100)**;

​ n**=2-n+int2(n/4)**; #加百年闰

}

res <- int2**(365.25*(y+4716))** + int2**(30.6001*(m+1))+d+**n - 1524.5;

class**(res)** <- “JD”

return**(res)**

}

### 将儒略日转换为儒略日2000

JD2000 <-

function**(jd){**

res <- jd - JD**(2000,1,1)**

return**(res)**

}

### 将日期转换为儒略日2000

date2JD2000 <-

function**(y,m,d){**

​ juliand <- JD**(y,m,d)**

​ res <- JD2000**(juliand)**

​ return**(res)**

}

### 打印JD类型对象的方法

print.JD <-

function**(JD){**

​ print**(sprintf(“%f”,JD))**

}

##儒略日转为公历

Julian2Date <-

function**(jd){**

r = list**()**;

D = int2**(jd+0.5)**;

F**=** jd**+0.5-**D #取得日数的整数部份A及小数部分F

if**(D >= 2299161)** {

​ c = int2**((D-1867216.25)/36524.25)**;

​ D = D + 1 + c - int2**(c/4)**;

}

D = D + 1524;

r**$Y = int2((D - 122.1)/365.25)**;#年数

D = D - int2**(365.25***r**$Y)**;

r**$M = int2(D/30.601)**; #月数

D = D - int2**(30.601***r**$M)**;

r**$**D = D; #日数

if**(r$M>13)** {

​ r**$M = r$**M - 13;

​ r**$Y = r$**Y - 4715;

} else {

​ r**$M = r$**M - 1;

​ r**$Y = r$**Y - 4716;

}

#日的小数转为时分秒

F = F ***** 24;

r**$h=int2(F)**;

F = F - r**$**h;

F = F ***** 60;

r**$m=int2(F)**;

F = F **-r$**m;

F = F ***** 60;

r**$s=**F;

return**(r)**;

}

#日期转为字符串

DD2str <-

function**(r){**

Y = r**$**Y

M = r**$**M

D = r**$**D;

h = r**$**h;

m = r**$**m;

s = int2**(r$s+0.5)**;

if**(s >= 60)** {

​ s = s - 60;

​ m = m + 1;

}

if**(m >= 60)** {

​ m = m - 60

​ h = h + 1;

}

h = paste**(“0”, h, sep = “”)**;

m = paste**(“0”, m, sep = “”)**;

s = paste**(“0”, s, sep = “”)**;

Y = substr**(Y, nchar(Y)-** 4, nchar**(Y)** );

M = substr**(M, nchar(M)-** 1, nchar**(M)** );

D = substr**(D, nchar(D)-** 1, nchar**(D)** );

h = substr**(h, nchar(h)-** 1, nchar**(h)** );

m = substr**(m, nchar(m)-** 1, nchar**(m)** );

s = substr**(s, nchar(s)-** 1, nchar**(s)** );

res <- paste**(Y,”-“,M,”-“,D,” “,h,”:”,m,”:”,s, sep = “”)**;

return**(res)**

}

JD2str <-

function**(jd){** #JD转为串

r = DD**(** jd );

res <- DD2str**(** r );

return**(res)**

}

### 判断距离输入日期最近的朔,有没有日食发生。

## “P”为偏食

## “T”为全食

## “A”为日环食

## “N”没有日食发生

find.solar.eclipse <-

function**(y,m,d){**

​ res <- list**()**

​ dd <- DateSolarEclipse**(date2JD2000(y,m,d))**

​ date.solar.eclipse <- DD2str**(Julian2Date(dd$jdNewMoon + JD(2000,1,1)** + 0.5**))**

​ type <- dd**$**type

​ res**$**NewMoonTime <- date.solar.eclipse

​ res**$**EclipseType <- type

​ return**(res)**

}

##距离1987年9月23日最近的朔,以及日食类型

find.solar.eclipse**(1987,9,23)**