万年历计算 之 节气(1)
一、基本知识
二十四节气起源于黄河流域。远在春秋时代,就定出仲春、仲夏、仲秋和仲冬等四个节气。以后不断地改进与完善,到秦汉年间,二十四节气已完全确立。公元前104年,由邓平等制定的《太初历》,正式把二十四节气订于历法,明确了二十四节气的天文位置。
太阳从黄经零度起,沿黄经每运行15度所经历的时日称为“一个节气”。每年运行360度,共经历24个节气,每月2个。其中,每月第一个节气为“节气”,即:立春、惊蛰、清明、立夏、芒种、小暑、立秋、白露、寒露、立冬、大雪和小寒等12个节气;每月的第二个节气为“中气”,即:雨水、春分、谷雨、小满、夏至、大暑、处暑、秋分、霜降、小雪、冬至和大寒等12个节气。“节气” 和“中气”交替出现,各历时15天,现在人们已经把“节气”和“中气”统称为“节气”。
二十四节气反映了太阳的周年视运动,所以节气在现行的公历中日期基本固定,上半年在6日、21日,下半年在8日、23日,前后不差1~2天。
二、基本概念
儒略日:Julian Day number 或 Julian Day 或 JD。是指从公元前 4712 年开始连续计算日数得出的天数及不满一日的尾数。传统上儒略日的计数是从格林尼治平午,即世界时间 12 点开始的。
格里历:即公历或格列高利历,是现行国际通行的历法,属于阳历的一种,通称阳历,其前身是奥古斯都历,而奥古斯都历的前身是儒略历。其历年为一个回归年(365.2425日),划分为12个历月。是教皇格里高利13世(也译格雷果里)在公元1582年改革儒略历制定的历法,儒略历的回归年为365.25,与实际的回归365.2422相差甚多,当时儒略历和地球实际位置的误差已达14天,格里历将误差纠正,确定所有整数世纪年除了可被400整除的外一律不设闰年,同时规定1582年10月4日之后的那天为1582年10月15日,但原有星期不变。新颁布的历法理论上可以达到两万年内误差不超过一天,但由于地球自转的变化,实际到公元4909年误差就可达一天。与儒略历相比,公历是新制定的历法,所以有时候,包括中华民国《中国国家标准》CNS 7648《数据元及交换格式–信息交换–日期及时间的表示法》,又称新历。
黄经:地球绕太阳运转一周约365天5小时多,运转94,000万公里。地球的公转在地球上人看来就表现为太阳周年视运动,其运行线路(即地球公转轨道在天球上的反映)称为黄道。黄经就是黄道上的度量坐标(经度),按天文学惯例,以春分点为起点自西向东度量,分360度。我国古人把太阳黄经的360度划分成24等份,每份15度,为一个节气。两个节气间相隔日数为15天左右,全年即有二十四节气。二十四节气的太阳黄经度分别为:立春315度,雨水330度,惊蛰345度,春分360度,清明15度,谷雨30度,立夏45度,小满60度,芒种75度,夏至90度,小暑105度,大暑120度,立秋135度,处暑150度,白露165度,秋分180度,寒露195度,霜降210度,立冬225度,小雪240度,大雪255度,冬至270度,小寒285度,大寒300度。
黄纬:黄道上的纬度。
三、基本算法:
3.1:儒略日的计算(算法描述请参考相关书籍,为了节省空间和时间,这里请罗列出 C++ 实现代码)
// ------------------------------------------------------------------------
//
// 儒略日转换为格历日
//
// dbGD[IN]:儒略日
// year[OUT]:年
// month[OUT]:月
// day[OUT]:日
// hour[OUT]:时
// minute[OUT]:分
// second[OUT]:秒
// ------------------------------------------------------------------------
void w_JDToGD(const double &dbJD, int &year, int &month, int &day, int &hour, int &minute, int &second)
{
double dJDM = dbJD + 0.5;
unsigned long ulZ = static_cast<unsigned long>(floor(dJDM));
double dF = dJDM - floor(dJDM);
unsigned long ulA, ulB, ulC, ulD;
int nE, nQ;
if (dbJD < 2299161)
ulA = ulZ;
else
{
nQ = static_cast<int>((ulZ - 1867216.25) / 36524.25);
ulA = ulZ + 1 + nQ - static_cast<int>(nQ >> 2);
}
ulB = ulA + 1524 ;
ulC = static_cast<int>(( ulB - 122.1) / 365.25);
ulD = static_cast<int>(365.25 * ulC);
nE = static_cast<int>((ulB - ulD) / 30.6001);
// 计算日
day = ulB - ulD - int ( 30.6001 * nE ) ;
// 计算时
hour = static_cast<int>(floor(dF * 24.0));
// 计算分
minute = static_cast<int>(((dF * 24.0) - floor(dF * 24.0)) * 60.0);
// 计算秒
second = static_cast<int>((((dF * 24.0) * 60.0) - floor(( dF * 24.0) * 60.0)) * 60.0);
// 计算月
if(nE < 14)
month = nE - 1;
if(nE == 14 || nE == 15)
month = nE - 13;
// 计算年
if(month > 2)
year = ulC - 4716;
if(month == 1 || month == 2)
year = ulC - 4715;
}
// ------------------------------------------------------------------------
//
// 格历日转换为儒略日
//
// year[IN]:年
// month[IN]:月
// day[IN]:日
// hour[IN]:时
// minute[IN]:分
// second[IN]:秒
//
// 返回对应的儒略日
// ------------------------------------------------------------------------
double w_GDToJD(const int &year, const int &month, const int &day, const int &hour, const int &minute, const int &second)
{
int nY = year, nM = month;
double dD = static_cast<double>(day) + hour / 24.0 + (minute / 60.0) / 24.0 + ((second / 60.0) / 60.0) / 24.0;
if(month == 1 || month == 2)
{
nY = year - 1;
nM = month + 12;
}
int nA = nY / 100;
int nB = 2 - nA + (nA >> 2);
return static_cast<double>(static_cast<int>(365.25 * (nY + 4716)) + static_cast<int>(30.6001 * (nM + 1)) + dD + nB - 1524.5);
}
3.2、角度调整
// ------------------------------------------------------------------------
//
// 调整角度到 0-360 之间
//
// dbDegrees[IN]:角度
//
// 返回调整后的角度
// ------------------------------------------------------------------------
double w_MapTo0To360Range(const double &dbDegrees)
{
double dbValue = dbDegrees;
// map it to the range 0 - 360
while(dbValue < 0.0)
dbValue += 360.0;
while(dbValue > 360.0)
dbValue -= 360.0;
return dbValue;
}
四、步骤简述
4.1、按照 5.2 和 5.3 节介绍方法计算出某一时刻太阳黄经和黄纬。
4.2、按照 5.4 、5.5、5.6 节介绍的三次校正方法对黄经进行校正,即可求得某时刻太阳黄经度数。
4.3、综合步骤演示在第 六 节。
4.4、第 七 节演示如何使用二分法求指定节气的时间。
4.5、第 八 节介绍了 本地时间(LST)和格林尼治时间(UTC)之间互换的函数。
4.6、第 九 节罗列出了本代码计算出的 2008年24节气时间(北京时间)
五、太阳黄经、黄纬、向量半径以及校正量的计算
5.1、为了代码可读性,定义类型变量。
//
// 圆周率
const double PI = 3.1415926535897932384626433832795;
//
// 一度代表的弧度
const double dbUnitRadian = PI / 180.0;
//
// 计算太阳黄经赤纬所需类型变量
typedef struct
{
double dA;
double dB;
double dC;
} VSOP87COEFFICIENT, *PVSOP87COEFFICIENT
// 二次修正黄经赤纬所需的天体章动系数类型变量
typedef struct
{
int nD;
int nM;
int nMprime;
int nF;
int nOmega;
int nSincoeff1;
double dSincoeff2;
int nCoscoeff1;
double dCoscoeff2;
} NUTATIONCOEFFICIENT, *PNUTATIONCOEFFICIENT;
//
// 节气枚举
enum SOLARTERMS
{
ST_VERNAL_EQUINOX = 0, // 春分
ST_CLEAR_AND_BRIGHT = 15, // 清明
ST_GRAIN_RAIN = 30, // 谷雨
ST_SUMMER_BEGINS = 45, // 立夏
ST_GRAIN_BUDS = 60, // 小满
ST_GRAIN_IN_EAR = 75, // 芒种
ST_SUMMER_SOLSTICE = 90, // 夏至
ST_SLIGHT_HEAT = 105, // 小暑
ST_GREAT_HEAT = 120, // 大暑
ST_AUTUMN_BEGINS = 135, // 立秋
ST_STOPPING_THE_HEAT = 150, // 处暑
ST_WHITE_DEWS = 165, // 白露
ST_AUTUMN_EQUINOX = 180, // 秋分
ST_COLD_DEWS = 195, // 寒露
ST_HOAR_FROST_FALLS = 210, // 霜降
ST_WINTER_BEGINS = 225, // 立冬
ST_LIGHT_SNOW = 240, // 小雪
ST_HEAVY_SNOW = 255, // 大雪
ST_WINTER_SOLSTICE = 270, // 冬至
ST_SLIGHT_COLD = 285, // 小寒
ST_GREAT_COLD = 300, // 大寒
ST_SPRING_BEGINS = 315, // 立春
ST_THE_RAINS = 330, // 雨水
ST_INSECTS_AWAKEN = 345 // 惊蛰
};
下一篇 万年历计算 之 节气(2)