태양의 일출/일몰 시간 구하기

2010/08/28 15:02


제가 천문에 관련된 프로그래밍을 조금 할줄 알다보니 태양의 위치 및 일출/일몰에 대한 문의가 자주 들어옵니다. 개인적인 소스는 있지만 높은 정확도를 위한 것이라 일반인들이 이해하기에는 수준이 높습니다.

출처 : http://very-bored.com/index.php?option=com_content&task=view&id=135&Itemid=29



대부분 문의유형을 살펴보면 태양전지판을 효율적으로 사용하기 위해 태양의 방향에 따라 입사각이 90도가 이뤄지도록 구동하는 장치를 개발을 해야한다던가 수족관의 전등을 일출/일몰 시간에 맞게 자동으로 On/Off시켜줘야 한다는 뭐 아주 간단하면서 정확도가 그리 중요하지 않은 수준의 구현을 요구하는 것들이 대부분 입니다.

일전에 관련되어 문의를 받았지만 요즘에는 개인적으로 너무 바쁘고 여유가 없어서 설명을 드리고 싶지만 드릴 수 있는 시간조차 없었습니다. 몇주가 답변을 못드리다가 잠깐 짬을 내어 답변을 드렸지요.

웹상에서 찾아보면 일몰/일출 시간 소스는 얼마든지 있습니다. 그 방법도 엄청 많이 나와있지요. 단지 어찌 찾아야하는 것인지 있어도 어찌 활용해야하는지 모르시는 분들이 대부분이라 생각합니다. 그래서 여기에 이 부분에 대해 설명한 사이트들을 소개하고 마지막으로 웹에 공개된 C++소스에 주석처리한 것을 공유하겠습니다.

태양위치 계산은 정확도에 따라서 계산방법은 천차만별입니다. 그러므로 완벽한 정답은 없습니다. 상황에 맞게 쓰시면 됩니다.
 
아래내용은 태양의 일출/일몰 계산 방법을 간략하게 소개하고 있습니다.
http://www.stargazing.net/kepler/sunrise.html
 
PHP 언어로 만들어진 일출/일몰 소스입니다.
http://www.phpschool.com/gnuboard4/bbs/board.php?bo_table=tipntech&wr_id=39302&sca=%C7%D4%BC%F6&page=29
 
C또는 C++로 만들어진 일출/일몰 소스입니다.
http://www.sci.fi/~benefon/stuff.html
아래 붙여놓은 소스는 여기에서 구현한 C++소스를 주석처리 해놓은 것 입니다. 참고하시고 main()함수부터 천천히 보세요. 이것으로 근사적 일출/일몰 시간을 계산할 수 있습니다.

구현하신다음 맞는지 확인하기 위해 아래 링크를 참고하세요.
http://www2009.kasi.re.kr/knowledge/solun_riset.aspx

// C++ program calculating the sunrise and sunset for
// the current date and a fixed location(latitude,longitude)
// Jarmo Lammi 1999 - 2000
// Last update January 6th, 2000

#include <iostream.h>
#include <math.h>
#include <time.h>

extern double pi;
double tpi = 2 * pi; 
double degs = 180.0/pi;
double rads = pi/180.0;

double L,g,daylen;
double SunDia = 0.53;     // Sunradius degrees(지구에서 본 태양의 반지름각. 단위:도)

double AirRefr = 34.0/60.0; // athmospheric refraction degrees (지구대기 굴절각)//

//   Get the days to J2000
//   h is UT in decimal hours
//   FNday only works between 1901 to 2099 - see Meeus chapter 7
// 이 함수는 J2000 1월 1일 12시 DT를 기준으로 날수(Day Number)를 구합니다. (Meeus의 책 챕터 7을 참고하세요)
// J2000에 대해서는 제 블로그 http://blog.jidolstar.com/487 를 참고하세요.
// 입력값은 년,월,일,시간(실수값)입니다. 
double FNday (int y, int m, int d, float h) {
int luku = - 7 * (y + (m + 9)/12)/4 + 275*m/9 + d;
// type casting necessary on PC DOS and TClite to avoid overflow
luku+= (long int)y*367;
return (double)luku - 730531.5 + h/24.0;
};

//   the function below returns an angle in the range
//   0 to 2*pi
// 
// 들어온 값을 항상 0~2pi 값안으로 normalize시킵니다. 
double FNrange (double x) {
    double b = x / tpi;
    double a = tpi * (b - (long)(b));
    if (a < 0) a = tpi + a;
    return a;
};

// Calculating the hourangle
// 시간각을 계산합니다. 
double f0(double lat, double declin) {
double fo,dfo;
// Correction: different sign at S HS
dfo = rads*(0.5*SunDia + AirRefr); if (lat < 0.0) dfo = -dfo;
fo = tan(declin + dfo) * tan(lat*rads);
if (fo>0.99999) fo=1.0; // to avoid overflow //
fo = asin(fo) + pi/2.0;
return fo;
};

// Calculating the hourangle for twilight times
// 박명시간에 대한 시간각을 계산합니다. 
double f1(double lat, double declin) {
double fi,df1;
// Correction: different sign at S HS
df1 = rads * 6.0; if (lat < 0.0) df1 = -df1;
fi = tan(declin + df1) * tan(lat*rads);
if (fi>0.99999) fi=1.0; // to avoid overflow //
fi = asin(fi) + pi/2.0;
return fi;
};


//   Find the ecliptic longitude of the Sun
//  태양의 황경을 구합니다.
double FNsun (double d) {

//   mean longitude of the Sun (태양의 평균 황경)

L = FNrange(280.461 * rads + .9856474 * rads * d);

//   mean anomaly of the Sun (태양의 평균근점이각)

g = FNrange(357.528 * rads + .9856003 * rads * d);

//   Ecliptic longitude of the Sun (태양의 황경계산)

return FNrange(L + 1.915 * rads * sin(g) + .02 * rads * sin(2 * g));
};

// Display decimal hours in hours and minutes
// 이것은 실수(Real Number)로 표현된 시간을 시:분으로 표기 하기 위한 겁니다. 
void showhrmn(double dhr) {
int hr,mn;
hr=(int) dhr;
mn = (dhr - (double) hr)*60;
if (hr < 10) cout << '0';
cout << hr << ':';
if (mn < 10) cout << '0';
cout << mn;
};


// 메인함수입니다. 
int main(void){
double y,m,day,h,latit,longit;

time_t sekunnit;
struct tm *p;

//  get the date and time from the user
// read system date and extract the year
// 사용자의 시스템에서부터 날짜와 시간을 얻습니다.
/** First get time **/
time(&sekunnit);

/** Next get localtime **/

 p=localtime(&sekunnit);

 y = p->tm_year; //년 
 // this is Y2K compliant method
 y+= 1900;
 m = p->tm_mon + 1; //월 

 day = p->tm_mday; //일

 h = 12; //낮 12시를 기준으로 계산합니다.

//관측장소의 경도(단위 degree),위도(단위 degree),타임존(단위시간) 값을 입력받습니다.
double tzone=2.0;
cout << "Input latitude, longitude and timezone and month\n";
cin >> latit;
cin >> longit;
cin >> tzone;

// testing
// m=6; day=10;

double d = FNday(y, m, day, h);

//   Use FNsun to find the ecliptic longitude of the
//   Sun
// 태양의 황경 
double lambda = FNsun(d);

//   Obliquity of the ecliptic (황도기울기 계산)

double obliq = 23.439 * rads - .0000004 * rads * d;

//   Find the RA and DEC of the Sun (태양의 적경,적위 계산)

double alpha = atan2(cos(obliq) * sin(lambda), cos(lambda)); //태양의 적경
double delta = asin(sin(obliq) * sin(lambda)); //태양의 적위 

// Find the Equation of Time
// in minutes
// Correction suggested by David Smith
// 균시차 계산 
double LL = L - alpha;
if (L < pi) LL += tpi;
double equation = 1440.0 * (1.0 - LL / tpi);
double ha = f0(latit,delta);
double hb = f1(latit,delta);
double twx = hb - ha;	// length of twilight in radians (라디안 단위 박명길이)
twx = 12.0*twx/pi;		// length of twilight in hours (시간단위 박명길이)
cout << "ha=" << ha << "  hb=" << hb << endl; 
// Conversion of angle to hours and minutes (하루길이를 시간단위로 계산) //
daylen = degs*ha/7.5;
     if (daylen<0.0001) {daylen = 0.0;}
// arctic winter     //

double riset = 12.0 - 12.0 * ha/pi + tzone - longit/15.0 + equation/60.0; //뜨는시간
double settm = 12.0 + 12.0 * ha/pi + tzone - longit/15.0 + equation/60.0; //지는시간 
double noont = riset + 12.0 * ha/pi; //정오 시간 

//정오시에 태양의 고도(maximum altitude)
double altmax = 90.0 + delta * degs - latit; 
// Correction for S HS suggested by David Smith
// to express altitude as degrees from the N horizon
if (latit < delta * degs) altmax = 180.0 - altmax;

double twam = riset - twx;	// morning twilight begin 시민박명(아침)
double twpm = settm + twx;	// evening twilight end 시민박명(저녁)

if (riset > 24.0) riset-= 24.0;
if (settm > 24.0) settm-= 24.0;

cout << "\n Sunrise and set\n";
cout << "===============\n";

cout.setf(ios::fixed);
cout.precision(0);
cout << "  year  : " << y << '\n'; //년
cout << "  month : " << m << '\n'; //월
cout << "  day   : " << day << "\n\n"; //일
cout << "Days until Y2K :  " << d << '\n'; //J2000을 기준으로 센 날수 
cout.precision(2);
cout << "Latitude :  " << latit << ", longitude:  " << longit << '\n'; //관측자의 경도와 위도 출력 
cout << "Timezone :  " << tzone << "\n\n"; //타임존 출력 (한국은 UT+9h)
cout << "Declination   : " << delta * degs << '\n'; //태양의 적위
cout << "Daylength     : "; showhrmn(daylen); cout << " hours \n"; //하루길이 

//시민박명(아침) 
cout << "Civil twilight: ";
showhrmn(twam); cout << '\n';
//일출시간 
cout << "Sunrise       : "; //
showhrmn(riset); cout << '\n';

 //정오때 태양 고도(Amendment by D. Smith)
cout << "Sun altitude at noontime "; 
showhrmn(noont); cout << " = " << altmax << " degrees" 
    << (latit>=0.0 ? " S" : " N") << endl;

//일몰시간 
cout << "Sunset        : ";
showhrmn(settm); cout << '\n';
//시민박명(저녁)
cout << "Civil twilight: ";
showhrmn(twpm); cout << '\n';

return 0;
}

어떤가요? 조금만 공을 들이면 완벽히 이해는 못해도 구현은 얼마든지 가능합니다.

글쓴이 : 지돌스타 (http://blog.jidolstar.com/705)

저작자 표시 비영리 동일 조건 변경 허락

천문프로그래밍 , , , , , , , , ,

  1. Blog Icon
    김승덕

    플래시 뿐만 아니라 이렇게 다양한 분야에까지! 지돌스타님의 블로그는 정말 없는게 없네요 ㅎㅎ 항상 이렇게 좋은 정보 공유해주심에 무한히 감사드립니다.

  2. 그저 약간 아는 지식정도입니다

  3. Blog Icon
    GRS

    좋은 정보감사합니다!

  4. Blog Icon
    감사합니다

    만머리가 터질것 같네요ㅠㅠ

  5. Blog Icon
    무소

    좋은 정보 감사합니다. 유용하게 쓰겠습니다.

  6. 좋은 일에 쓰시길 바래요 ^^