百度空间 | 百度首页 
               
 
查看文章
 
万年历计算 之 节气(1)
2006年11月27日 星期一 12:34

万年历计算 之 节气(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)


类别:默认分类 | 添加到搜藏 | 浏览() | 评论 (1)
 
最近读者:
 
网友评论:
2
2008年05月06日 星期二 14:10 | 回复
correctionCalcSunLatitude 和correctionCalcSunLongitude函数有错误:dLongitude *= dUnitRadian ,你将 dLongitude 重新赋值。不过你没用到correctionCalcSunLatitude 这个函数。 另外不见calcSunLatitude(dJD)。 double correctionCalcSunLatitude ( double dLongitude , double dJD ) { double dT = ( dJD - 2451545.0 ) / 36525.0 ; double dLdash = dLongitude - 1.397 * dT - 0.00031 * dT * dT ; // 转换为弧度 dLdash *= dUnitRadian ; dLongitude *= dUnitRadian ; return ( 0.03916 * ( cos ( dLdash ) - sin ( dLdash ) ) ) / 3600.0 ; } double correctionCalcSunLongitude(double dLongitude, double dLatitude, double dJD) { double dT = (dJD - 2451545.0) / 36525.0 double dLdash = dLongitude - 1.397 * dT - 0.00031 * dT * dT; // 转换为弧度 dLdash *= dUnitRadian; dLongitude *= dUnitRadian; dLatitude *= dUnitRadian; return (- 0.09033 + 0.03916 * (cos(dLdash) + sin(dLdash)) * tan(dLatitude)) / 3600.0; }
 
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
验证码: 请点击后输入四位验证码,字母不区分大小写
      

     

©2009 Baidu