万年历计算 之 节气(续)

上一篇 / 下一篇  2008-05-07 16:11:02

    5.5、第二次校正黄经(章动):(算法描述请参考相关书籍,为了节省空间和时间,这里请罗列出 C++ 实现代码)

         //
          // 二次修正黄经黄纬所需的天体章动系数
          const NUTATIONCOEFFICIENT Nutation_Gene[63] =
          {
              { 0, 0, 0, 0, 1, -171996,  -174.2,  92025,     8.9    },
              {-2, 0, 0, 2, 2,  -13187,    -1.6,   5736,    -3.1    },
              { 0, 0, 0, 2, 2,   -2274,    -0.2,    977,    -0.5    },
              { 0, 0, 0, 0, 2,    2062,     0.2,   -895,     0.5    },
              { 0, 1, 0, 0, 0,    1426,    -3.4,     54,    -0.1    },
              { 0, 0, 1, 0, 0,     712,     0.1,     -7,       0    },
              {-2, 1, 0, 2, 2,    -517,     1.2,    224,    -0.6    },
              { 0, 0, 0, 2, 1,    -386,    -0.4,    200,       0    },
              { 0, 0, 1, 2, 2,    -301,       0,    129,    -0.1    },
              {-2,-1, 0, 2, 2,     217,    -0.5,    -95,     0.3    },
              {-2, 0, 1, 0, 0,    -158,       0,      0,       0    },
              {-2, 0, 0, 2, 1,     129,     0.1,    -70,       0    },
              { 0, 0,-1, 2, 2,     123,       0,    -53,       0    },
              { 2, 0, 0, 0, 0,      63,       0,      0,       0    },
              { 0, 0, 1, 0, 1,      63,     0.1,    -33,       0    },
              { 2, 0,-1, 2, 2,     -59,       0,     26,       0    },
              { 0, 0,-1, 0, 1,     -58,    -0.1,     32,       0    },
              { 0, 0, 1, 2, 1,     -51,       0,     27,       0    },
              {-2, 0, 2, 0, 0,      48,       0,      0,       0    },
              { 0, 0,-2, 2, 1,      46,       0,    -24,       0    },
              { 2, 0, 0, 2, 2,     -38,       0,     16,       0    },
              { 0, 0, 2, 2, 2,     -31,       0,     13,       0    },
              { 0, 0, 2, 0, 0,      29,       0,      0,       0    },
              {-2, 0, 1, 2, 2,      29,       0,    -12,       0    },
              { 0, 0, 0, 2, 0,      26,       0,      0,       0    },
              {-2, 0, 0, 2, 0,     -22,       0,      0,       0    },
              { 0, 0,-1, 2, 1,      21,       0,    -10,       0    },
              { 0, 2, 0, 0, 0,      17,    -0.1,      0,       0    },
              { 2, 0,-1, 0, 1,      16,       0,     -8,       0    },
              {-2, 2, 0, 2, 2,     -16,     0.1,      7,       0    },
              { 0, 1, 0, 0, 1,     -15,       0,      9,       0    },
              {-2, 0, 1, 0, 1,     -13,       0,      7,       0    },
              { 0,-1, 0, 0, 1,     -12,       0,      6,       0    },
              { 0, 0, 2,-2, 0,      11,       0,      0,       0    },
              { 2, 0,-1, 2, 1,     -10,       0,      5,       0    },
              { 2, 0, 1, 2, 2,      -8,       0,      3,       0    },
              { 0, 1, 0, 2, 2,       7,       0,     -3,       0    },
              {-2, 1, 1, 0, 0,      -7,       0,      0,       0    },
              { 0,-1, 0, 2, 2,      -7,       0,      3,       0    },
              { 2, 0, 0, 2, 1,      -7,       0,      3,       0    },
              { 2, 0, 1, 0, 0,       6,       0,      0,       0    },
              {-2, 0, 2, 2, 2,       6,       0,     -3,       0    },
              {-2, 0, 1, 2, 1,       6,       0,     -3,       0    },
              { 2, 0,-2, 0, 1,      -6,       0,      3,       0    },
              { 2, 0, 0, 0, 1,      -6,       0,      3,       0    },
              { 0,-1, 1, 0, 0,       5,       0,      0,       0    },
              {-2,-1, 0, 2, 1,      -5,       0,      3,       0    },
              {-2, 0, 0, 0, 1,      -5,       0,      3,       0    },
              { 0, 0, 2, 2, 1,      -5,       0,      3,       0    },
              {-2, 0, 2, 0, 1,       4,       0,      0,       0    },
              {-2, 1, 0, 2, 1,       4,       0,      0,       0    },
              { 0, 0, 1,-2, 0,       4,       0,      0,       0    },
              {-1, 0, 1, 0, 0,      -4,       0,      0,       0    },
              {-2, 1, 0, 0, 0,      -4,       0,      0,       0    },
              { 1, 0, 0, 0, 0,      -4,       0,      0,       0    },
              { 0, 0, 1, 2, 0,       3,       0,      0,       0    },
              { 0, 0,-2, 2, 2,      -3,       0,      0,       0    },
              {-1,-1, 1, 0, 0,      -3,       0,      0,       0    },
              { 0, 1, 1, 0, 0,      -3,       0,      0,       0    },
              { 0,-1, 1, 2, 2,      -3,       0,      0,       0    },
              { 2,-1,-1, 2, 2,      -3,       0,      0,       0    },
              { 0, 0, 3, 2, 2,      -3,       0,      0,       0    },
              { 2,-1, 0, 2, 2,      -3,       0,      0,       0    }
          };

         // ------------------------------------------------------------------------
          //
          // 计算天体章动
          // 二次修正某时刻太阳在黄道上的纬度(单位:度)
          // 使用天体章动系数修正,消除扰动影响
          //
          // dbJD[IN]:儒略日(计算该时刻天体章动补偿量)
          //
          // 返回天体扰动干扰量
          // ------------------------------------------------------------------------
          double w_GetNutationJamScalar(const double &dbJD)
          {
               double dbT = (dbJD - 2451545.0) / 36525.0;
               double dbTsquared = dbT * dbT;
               double dbTcubed = dbTsquared * dbT;

               double dbD = 297.85036 + 445267.111480 * dbT - 0.0019142 * dbTsquared + dbTcubed / 189474.0;
               dbD = w_MapTo0To360Range(dbD);

               double dbM = 357.52772 + 35999.050340 * dbT - 0.0001603 * dbTsquared - dbTcubed / 300000.0;
               dbM = w_MapTo0To360Range(dbM);

               double dbMprime = 134.96298 + 477198.867398 * dbT + 0.0086972 * dbTsquared + dbTcubed / 56250.0;
               dbMprime = w_MapTo0To360Range(dbMprime);

               double dbF = 93.27191 + 483202.017538 * dbT - 0.0036825 * dbTsquared + dbTcubed / 327270.0;
               dbF = w_MapTo0To360Range(dbF);

               double dbOmega = 125.04452 - 1934.136261 * dbT + 0.0020708 * dbTsquared + dbTcubed / 450000.0;
               dbOmega = w_MapTo0To360Range(dbOmega);

               double dbResulte = 0.0 ;

               for(int i = 0; i < sizeof(Nutation_Gene) / sizeof(NUTATIONCOEFFICIENT); i++)
               {
               double dbRadargument = (Nutation_Gene[i].nD * dbD + Nutation_Gene[i].nM * dbM + Nutation_Gene[i].nMprime * dbMprime + Nutation_Gene[i].nF * dbF + Nutation_Gene[i].nOmega * dbOmega) * dbUnitRadian;

               dbResulte += (Nutation_Gene[i].nSincoeff1 + Nutation_Gene[i].dSincoeff2 * dbT ) * sin(dbRadargument) * 0.0001;
               }

               return dbResulte;
          }

    5.6、第三次校正黄经(太阳半径向量):(算法描述请参考相关书籍,为了节省空间和时间,这里请罗列出 C++ 实现代码)

        //
         // 计算太阳向量半径用参数
         const VSOP87COEFFICIENT Earth_SRV0[40] =
         {
             { 100013989   , 0         ,    0           },
             { 1670700     , 3.0984635 ,    6283.0758500},
             { 13956       , 3.05525   ,   12566.15170  },
             { 3084        , 5.1985    ,   77713.7715   },
             { 1628        , 1.1739    ,   5753.3849    },
             { 1576        , 2.8469    ,   7860.4194    },
             { 925         , 5.453     ,   11506.770    },
             { 542         , 4.564     ,   3930.210     },
             { 472         , 3.661     ,   5884.927     },
             { 346         , 0.964     ,   5507.553     },
             { 329         , 5.900     ,   5223.694     },
             { 307         , 0.299     ,   5573.143     },
             { 243         , 4.273     ,   11790.629    },
             { 212         , 5.847     ,   1577.344     },
             { 186         , 5.022     ,   10977.079    },
             { 175         , 3.012     ,   18849.228    },
             { 110         , 5.055     ,   5486.778     },
             { 98          , 0.89      ,   6069.78      },
             { 86          , 5.69      ,   15720.84     },
             { 86          , 1.27      ,   161000.69    },
             { 65          , 0.27      ,   17260.15     },
             { 63          , 0.92      ,   529.69       },
             { 57          , 2.01      ,   83996.85     },
             { 56          , 5.24      ,   71430.70     },
             { 49          , 3.25      ,   2544.31      },
             { 47          , 2.58      ,   775.52       },
             { 45          , 5.54      ,   9437.76      },
             { 43          , 6.01      ,   6275.96      },
             { 39          , 5.36      ,   4694.00      },
             { 38          , 2.39      ,   8827.39      },
             { 37          , 0.83      ,   19651.05     },
             { 37          , 4.90      ,   12139.55     },
             { 36          , 1.67      ,   12036.46     },
             { 35          , 1.84      ,   2942.46      },
             { 33          , 0.24      ,   7084.90      },
             { 32          , 0.18      ,   5088.63      },
             { 32          , 1.78      ,   398.15       },
             { 28          , 1.21      ,   6286.60      },
             { 28          , 1.90      ,   6279.55      },
             { 26          , 4.59      ,   10447.39     }
         };
         const VSOP87COEFFICIENT Earth_SRV1[10] =
         {
             { 103019 , 1.107490 , 6283.075850 },
             { 1721   , 1.0644   , 12566.1517  },
             { 702    , 3.142    , 0           },
             { 32     , 1.02     , 18849.23    },
             { 31     , 2.84     , 5507.55     },
             { 25     , 1.32     , 5223.69     },
             { 18     , 1.42     , 1577.34     },
             { 10     , 5.91     , 10977.08    },
             { 9      , 1.42     , 6275.96     },
             { 9      , 0.27     , 5486.78     }
         };
         const VSOP87COEFFICIENT Earth_SRV2[ 6] =
         {
             { 4359 , 5.7846 , 6283.0758 },
             { 124  , 5.579  , 12566.152 },
             { 12   , 3.14   , 0         },
             { 9    , 3.63   , 77713.77  },
             { 6    , 1.87   , 5573.14   },
             { 3    , 5.47   , 18849.23  }
         };
         const VSOP87COEFFICIENT Earth_SRV3[ 2] =
         {
             { 145 , 4.273 , 6283.076 },
             {   7 , 3.92 , 12566.15 }
         };
         const VSOP87COEFFICIENT Earth_SRV4[ 1] =
         {
             { 4 , 2.56 , 6283.08 }
         };

         // ------------------------------------------------------------------------
         //
         // 计算某时刻太阳半径向量
         // 三次修正某时刻太阳在黄道上的经度(单位:弧度)
         //
         // dbJD[IN]:儒略日(计算该时刻太阳半径向量)
         //
         // 返回太阳半径向量
         // ------------------------------------------------------------------------
         double w_GetSunRadiusVector(const double &dbJD)
         {
               // 计算τ
               double dbt = (dbJD - 2451545.0) / 365250.0;

               double dbR = 0.0, dbR0 = 0.0, dbR1 = 0.0, dbR2 = 0.0, dbR3 = 0.0, dbR4 = 0.0;
               // R0 40x3
               for(int i = 0; i < sizeof(Earth_SRV0) / sizeof(VSOP87COEFFICIENT); i++)
                  dbR0 += (Earth_SRV0[i].dA * cos((Earth_SRV0[i].dB + Earth_SRV0[i].dC * dbt)));

               // R1 10x3
               for(int i = 0; i < sizeof(Earth_SRV1) / sizeof(VSOP87COEFFICIENT); i++)
                  dbR1 += (Earth_SRV1[i].dA * cos((Earth_SRV1[i].dB + Earth_SRV1[i].dC * dbt)));

               // R2 6x3
               for(int i = 0; i < sizeof(Earth_SRV2) / sizeof(VSOP87COEFFICIENT); i++)
                  dbR2 += (Earth_SRV2[i].dA * cos((Earth_SRV2[i].dB + Earth_SRV2[i].dC * dbt)));

               // R3 2x3
               for(int i = 0; i < sizeof(Earth_SRV3) / sizeof(VSOP87COEFFICIENT); i++)
                  dbR3 += (Earth_SRV3[i].dA * cos((Earth_SRV3[i].dB + Earth_SRV3[i].dC * dbt)));

               // R4 1x3
               for(int i = 0; i < sizeof(Earth_SRV4) / sizeof(VSOP87COEFFICIENT); i++)
                  dbR4 += (Earth_SRV4[i].dA * cos((Earth_SRV4[i].dB + Earth_SRV4[i].dC * dbt)));

               // 计算 R = ( R0 + R1 * τ^1 + R2 * τ^2 + R3 * τ^3 + R4 * τ^4 ) / 10^8 ;(单位弧度)
               return ((dbR0 + (dbR1 * dbt) + (dbR2 * (dbt * dbt)) + (dbR3 * (dbt * dbt * dbt)) * (dbR4 * (dbt * dbt * dbt * dbt))) / 100000000.0);
         }

   六、计算指定时刻太阳黄道经度度数(算法描述请参考相关书籍,为了节省空间和时间,这里请罗列出 C++ 实现代码)

    利用以上介绍算法,如下:

      // ------------------------------------------------------------------------
       //
       // 计算某时刻太阳黄经黄纬
       //
       // dbJD[IN]:儒略日(计算该时刻太阳在黄道面上的经度和纬度)
       // dbLongitude[OUT]:黄经
       // dbLatitude[OUT]:黄纬
       // ------------------------------------------------------------------------
       void w_CalcEclipticLongLat(const double & dbJD, double &dbLongitude, double &dbLatitude)
       {
            // 计算太阳黄经
            dbLongitude = w_GetSunLongitude(dbJD);
            // 计算太阳黄纬
            dbLatitude = w_GetSunLatitude(dbJD);

            // 一次校正经度
            dbLongitude += w_CorrectionCalcSunLongitude(dbLongitude, dbLatitude, dbJD);
            // 二次校正天体章动
            dbLongitude += w_GetNutationJamScalar(dbJD) / 3600.0;
            // 三次校正太阳半径向量
            dbLongitude -= (20.4898 / w_GetSunRadiusVector(dbJD)) /3600.0;

            // 校正太阳黄纬
            dbLatitude += w_CorrectionCalcSunLatitude(dbLongitude, dbJD);
       }

   七、使用二分法计算指定节气的时间

       // ------------------------------------------------------------------------
       //
       // 计算节气
       //
       // year[IN]:公历年份(计算该年份,指定节气的时间)
       // ST_SolarTerms[IN]:节气指定的节气
       //
       // 返回指定节气的儒略日时间
       // ------------------------------------------------------------------------
       double w_CalcSolarTerms(const int &year, const SOLARTERMS &ST_SolarTerms)
       {
             // 节气月份
             int SolarTermsMonth = static_cast<int>(ceil(static_cast<double>((ST_SolarTerms + 90.0) / 30.0)));
             SolarTermsMonth = SolarTermsMonth > 12 ? SolarTermsMonth - 12 : SolarTermsMonth;
             // 节令的发生日期基本都在每月 4 - 9 号间
             int LowerLimitSolarTermsDay = ST_SolarTerms % 15 == 0 && ST_SolarTerms % 30 != 0 ? 4 : 16;
             // 节气的发生日期基本都在每月 16 - 24 号间
             int UpperLimitSolarTermsDay = ST_SolarTerms % 15 == 0 && ST_SolarTerms % 30 != 0 ? 9 : 24;

             // 采用二分法逼近计算
             double dbLowerLinit = w_GDToJD(year, SolarTermsMonth, LowerLimitSolarTermsDay, 0, 0, 0);
             double dbUpperLinit = w_GDToJD(year, SolarTermsMonth, UpperLimitSolarTermsDay, 23, 59, 59);

             // 二分法分界点日期
             double dbDichotomyDivisionDayJD = 0;
             // 太阳黄经角度
             double dbLongitude = 0;

             // 对比二分法精度是否达到要求
             for(; fabs(dbLongitude - static_cast<double>(ST_SolarTerms)) >= 0.00001;)
             {
                 dbDichotomyDivisionDayJD = ((dbUpperLinit - dbLowerLinit) / 2.0) + dbLowerLinit;

                 // 计算太阳黄经
    dbLongitude = w_GetSunLongitude(dbDichotomyDivisionDayJD);

                 // 一次校正经度
                 dbLongitude += w_CorrectionCalcSunLongitude(dbLongitude, w_GetSunLatitude(dbDichotomyDivisionDayJD), dbDichotomyDivisionDayJD);
                 // 二次校正天体章动
                 dbLongitude += w_GetNutationJamScalar(dbDichotomyDivisionDayJD) / 3600.0;
                 // 三次校正太阳半径向量
                 dbLongitude -= (20.4898 / w_GetSunRadiusVector(dbDichotomyDivisionDayJD)) /3600.0;

                 // 由于春分这天黄经为 0 度,比较特殊,因此专门判断(如不加以特殊对待则会导致计算范围覆盖整个 360 度角)
                 dbLongitude = ((ST_SolarTerms == ST_VERNAL_EQUINOX) && (dbLongitude > 345.0)) ? -dbLongitude : dbLongitude;

                 // 调整二分法上下限
                 (dbLongitude > static_cast<double>(ST_SolarTerms)) ? dbUpperLinit = dbDichotomyDivisionDayJD : dbLowerLinit = dbDichotomyDivisionDayJD;
             }

             return dbDichotomyDivisionDayJD;
       }

   八、本地时间(LST)和格林尼治时间(UTC)的互换(仅限于 VC 代码)

      // ------------------------------------------------------------------------
       //
       // 格林威治时间转本地时间(以格里历表示本地时间)
       //
       // ------------------------------------------------------------------------
       void w_UTCToLST(int &year, int &month, int &day, int &hour, int &minute, int &second)
       {
            _tzset () ;

            // 计算本地时间和标准时间的时差(单位:秒)
            int nDifference_hour = static_cast<int>(_timezone / 3600);
            int nDifference_minute = static_cast<int>((_timezone - nDifference_hour * 3600) / 60);
            int nDifference_second = static_cast<int>((_timezone - nDifference_hour * 3600) - nDifference_minute * 60);

            // 格林威治时间 + 时差 = 本地时间

            // 秒
            second = second - nDifference_second;
            if(second >= 60 || second < 0)
            {
                minute = second > 0 ? minute + 1 : minute - 1 ;
                second = abs(abs(second) - 60);
            }

            // 分
            minute = minute - nDifference_minute;
            if(minute >= 60 || minute < 0)
            {
                hour = minute > 0 ? hour + 1 : hour - 1;
                minute = abs(abs(minute) - 60);
            }

            // 时
            hour = hour - nDifference_hour;
            if(hour >= 24 || hour < 0)
            {
                day = (hour >= 24 || hour == 0) ? day + 1 : day - 1;
                hour = abs(abs(hour) - 24);
            }

            // 日
            int nDaysOfMonth = w_GetDaysOfMonth(year, month);
            if(day > nDaysOfMonth || day <= 0)
            {
                if(day > nDaysOfMonth)
                   month++;
                if(day < nDaysOfMonth || day <= 0)
                   month--;

                day = abs(abs(day) - nDaysOfMonth);
            }
            // 月
            if(month > 12 || month <= 0)
            {
                year = month > 0 ? year + 1 : year - 1;
                month = month > 0 ? abs(month - 12) : abs(12 + month);
            }
       }

       // ------------------------------------------------------------------------
       //
       // 本地时间转格林威治时间(以格里历表示)
       //
       // ------------------------------------------------------------------------
       void w_LSTToUTC(int &year, int &month, int &day, int &hour, int &minute, int &second)
       {
            _tzset () ;

            // 计算本地时间和标准时间的时差(单位:秒)
            int nDifference_hour = static_cast<int>(_timezone / 3600);
            int nDifference_minute = static_cast<int>((_timezone - nDifference_hour * 3600) / 60);
            int nDifference_second = static_cast<int>((_timezone - nDifference_hour * 3600) - nDifference_minute * 60);

            // 本地时间 - 时差 = 格林威治时间

            // 秒
            second = second + nDifference_second;
            if(second >= 60 || second < 0)
            {
                minute = second > 0 ? minute + 1 : minute - 1 ;
                second = abs(abs(second) - 60);
            }

            // 分
            minute = minute + nDifference_minute;
            if(minute >= 60 || minute < 0)
            {
                hour = minute > 0 ? hour + 1 : hour - 1;
                minute = abs(abs(minute) - 60);
             }

            // 时
            hour = hour + nDifference_hour;
            if(hour >= 24 || hour < 0)
            {
                day = (hour >= 24 || hour == 0) ? day + 1 : day - 1;
                hour = abs(abs(hour) - 24);
            }

            // 日
            int nDaysOfMonth = w_GetDaysOfMonth(year, month);
            if(day > nDaysOfMonth || day <= 0)
            {
                if(day > nDaysOfMonth)
                     month++;
                if(day < nDaysOfMonth || day <= 0)
                   month--;

                day = abs(abs(day) - nDaysOfMonth);
            }
            // 月
            if(month > 12 || month <= 0)
            {
                year = month > 0 ? year + 1 : year - 1;
                month = month > 0 ? abs(month - 12) : abs(12 + month);
            }
       }

 

   九、2008年全年 24 节气发生时间(北京时间)

       春分:03月20日 13:49:24

       清明:04月04日 17:46:58

       谷雨:04月20日 00:52:17

       立夏:05月05日 11:04:35

       小满:05月21日 00:02:03

       芒种:06月05日 15:12:53

       夏至:06月21日 08:00:30

       小暑:07月07日 01:27:59

       大暑:07月22日 18:55:58

       立秋:08月07日 11:17:19

       处暑:08月23日 02:03:22

       白露:09月07日 14:15:15

       秋分:09月22日 23:45:36

       寒露:10月08日 05:57:46

       霜降:10月23日 09:09:49

       立冬:11月07日 09:11:45

       小雪:11月22日 06:45:32

       大雪:12月07日 02:03:28

       冬至:12月21日 20:04:54

       小寒:01月06日 07:25:55

       大寒:01月21日 00:44:38

       立春:02月04日 19:01:30

       雨水:02月19日 14:50:40

       惊蛰:03月05日 12:59:54


     本文给出了一种通过计算得到某一年的节气发生日期,相比之下比通过查表的方法的优点不言而喻,经本人比对(与日梭万年历)在范围公元300-3000年间误差很小,如果对历法进一步熟悉,你会发现历法的计算非常困难,计算的误差也随时间跨度的增加增长的很快,即便是所有查阅到的最权威的书籍数据,也只能保证在前后200年间的准确。

 


    最后欢迎所有朋友提出你的意见。(全文完)


TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

日历

« 2008-11-20  
      1
2345678
9101112131415
16171819202122
23242526272829
30      

数据统计

  • 访问量: 1155
  • 日志数: 3
  • 建立时间: 2007-01-29
  • 更新时间: 2008-05-07

RSS订阅

Open Toolbar