조회 수 4558 댓글 12
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
음양력 변환 모듈에 대한 마지막 글입니다. 이번에는 양력을 음력으로 변환하는 방법에 대해 설명하겠습니다. 정확히는 율리우스적일로된 날짜를 음력으로 바꾸는 방법입니다. 

음력을 계산하는 함수는 단 하나입니다. 

LuniSolarCal: 율리우스 적일을 음력으로 바꾸는 함수


이 함수는 율리우스적일 형태의 날짜를 입력받아 음력날짜로 바꾸어 출력합니다. 음력으로 바꾸는 방법은 기본적으로 청나라에서 만들어진 시헌력법에서 정한 방법을 따릅니다만(천문연구원도 시헌력에 따라 음력을 산출함), 여기에서 쓴 방법과 세부 조건은 조금 다릅니다. 이 조건에 명시되어 있지는 않지만 한 해가 시작되는 시점인 세수는 인월(寅月, 동짓달로부부터 세 번째 오는 달)로 하며, 천정동지(天正冬至)는 음력으로 바로 전 해의 동지, 즉 그 해의 1월(세수) 바로 전의 동지를 뜻합니다.

우선 시헌력에서는 다음 원칙에 따라 음력을 계산합니다. 

1) 동지가 든 달을 11월로 한다. 
2) 중기가 포함되지 않은 달(무중월)을 윤달로 하되(무중치윤법), 1년에 윤달이 2회 이상 있으면 첫번째 달만 윤달로 한다. 
3) 춘분이 든 달은 2월, 하지가 든 달은 5월, 추분이 든 달은 8월로 한다. 

그런데, 1과 2의 조건이 3의 조건에 우선하며, 3번 조건은 무중월이 2번 이상 반복되어 나타나는 경우에, 지킬 수 없는 때가 있습니다. 따라서 여기에서는 1과 2의 조건만을 반영하여 음력을 계산하였습니다. 또한 음력의 몇 가지 특성을 반영하였는데요, 

1) 2년 연속으로 윤년이 오는 경우는 없다. 
2) 무중월은 2개월 이상 연속으로 이어지지 않는다.

이 두 가지 특성을 반영하면 자동화 과정이 훨씬 간단해지게 됩니다. 

음력 계산 순서는 우선 구하려는 해의 입기시각과 정삭시각을 계산하여 표로 만들고, 천정동지와 그 다음 동지 사이에 삭망월이 몇 개인지를 찾습니다. 11월(천정동지월)부터 11월(동지월)까지이므로 이 사이에 삭망월이 13개가 들어가면 윤달이 포함되어 있는 것이고, 12개가 들어가면 모두 평달임을 알 수 있습니다. 12개의 달이 모두 평달이면, 그 사이에 무중월이 있든 없든, 11월부터 다음달은 12월, 그 다음달은 1월, 이런 식으로 윤달 없이 월 번호를 매기면 됩니다. 

13개의 삭망월이 포함되는 경우라면, 윤달의 위치를 찾아주어야하는데, 정확한 판단을 위해서는 천정동지가 포함된 11월과 그 다음 이어지는 2개월의 윤달 여부를 파악해야합니다. 여기에서 쓰는 조건이 바로 앞에서 설명한 두 조건을 이용합니다. 윤달이 포함된 주기(천정동지~그해의 동지 사이의 기간)는 2번 이상 연속적으로 나타나지 않으므로, 이전 주기와 다음 주기에는 윤달이 없습니다. 따라서, 천정동지가 포함된 11월(천정동지월)이 있는  주기와 이번 동지의 다음 주기에는 모두 평달이 들어가게 됩니다.

이를 염두에 두고 따져보면, 천정동지월과 이어지는 2개월의 윤달 여부는 다음처럼 됩니다(천정동지월-다음월-다음월 순). 
1) 평-평-평 : 이 때는 이 3개월이 11월, 12월, 1월이 되며, 천정동지 다음의 1~10월 사이에 있는 첫번째 무중월이 윤달이 됩니다. 
2) 평-윤-평 : 이 때는 천정동지월 바로 다음달이 윤 11월이 되어 11월, 윤 11월, 12월이 됩니다. 그 1~10월은 모두 평달로 처리합니다.
3) 평-평-윤 : 이 때는 천정동지월을 11월, 다음 달이 12월, 그 다음달이 윤 12월이 되고, 1~10월은 모두 평월로 처리합니다. 그런데, 12월은 지구가 근일점 부근을 지나므로, 윤달이 끼는 경우가 매우 드뭅니다. 실제로 정기법을 쓰면 12월 윤은 나타나지 않습니다.

율리우스적일을 음력으로 바꾸는 LuniSolarCal 함수는 여기까지 설명한 논리에 따라 음력을 계산합니다. 처리과정은 소스코드에 주석문 형태로 달아놓겠습니다. 

이 함수에 쓰는 구조체에는 삭망월의 자료를 저장합니다. 

Type LunarDay
  StartDay As Double '삭망월의 시작일(=정삭일)
  MonLength As Integer '달의 길이(큰 달=30일, 작은 달=29일)
  Junggi As Boolean '무중월, 유중월 여부(무중월이면 거짓)
  MonName As Byte '월 이름(월 번호)
  LYear As Integer '삭망월이 포함된 연도
End Type

음력 계산에 사용하는 주 함수는 LuniSolarCal입니다. 율리우스적일(JD)을 입력으로 받아 음력 연도(LunarYear), 월(LunarMon), 일(LunarDay), 윤달 여부(IsLeap)를 출력합니다. 윤달 여부는 윤달이며 참, 아니면 거짓으로 출력합니다. 함수 작동에 대한 상세한 내용은 주석문을 참고하세요. 

    Private Sub LuniSolarCal(ByVal jd As Double, LunarYear As Integer, LunarMon As Byte, LunarDay As Byte, IsLeap As Boolean)
      Dim jd0 As Double, dYear As Double, yJD0 As Double, bf As Double, B As Integer, M As Integer
      Dim i As Integer, j As Integer, k As Integer, LD(25) As LunarDay, SD(25) As Double
      Dim PreWinter As Double, ThisWinter As Double, Count1 As Integer, idx1 As Integer, idx2 As Integer
      Dim LeapType As Byte, Leap13 As Boolean, fMON As Integer, A As Integer, LCount As Integer
     
      jd0 = GetJD0(jd) + 0.5 '입력일을 정오로 설정
      dYear = InvJDYear(jd0) '입력일의 연도 계산
    
    '입력일이 천정동지일 이전이면 계산 주기를 1년 앞으로, 입력일 연도이 동지 이후이면 1년 뒤로 바꿈.
      If jd0 < cJunggi(dYear, 270, -13) Then dYear = dYear - 1
      If jd0 > cJunggi(dYear, 270, 355) Then dYear = dYear + 1
    
    '입력일 연도의 1일의 율리우스 적일 계산
      yJD0 = JULIANDAY(dYear, 1, 1)
      Call Set24Julgi: CalcJulGi yJD0  '24절기 입기시각 계산
    
    '정삭시각을 계산하여 배열에 저장(해당연도 1월 1일을 기준으로 96일 이전부터 이후 427일 사이의 기간)
      j = 0: SD(0) = 0: k = 0
      Do
        bf = GetJD0(NewMoon(yJD0 - 96 + j * 28)) + 0.5
        If bf >= Junggi(0).RealDay Then
          If k > 0 Then
            If bf > SD(k - 1) Then SD(k) = bf: k = k + 1
          Else
            SD(0) = bf: k = 1
          End If
        End If
        j = j + 1
      Loop Until bf > yJD0 + 427
      j = k: k = j - 1
    
    '삭망월 변수를 초기화(삭망월 변수에 정삭일만 저장)
      For i = 0 To 25
        LD(i).StartDay = 0
        LD(i).StartDay = SD(i)
        LD(i).MonName = 100 '100은 아무 값도 지정되지 않았음을 나타냄
      Next i
      PreWinter = Junggi(1).RealDay '천정동지일
      ThisWinter = Junggi(13).RealDay '계산하는 연도의 동지일
    
    '입기시각과 정삭일을 비교해 삭망월 변수에 달 번호 매기기
      idx1 = 0: idx2 = 0
      For i = 0 To 24
        LD(i).Junggi = False
        For j = 0 To 15
          If LD(i + 1).StartDay > Junggi(j).RealDay And Junggi(j).RealDay >= LD(i).StartDay Then
            LD(i).Junggi = True '무중월인지 유중월인지 파악
            If LD(i).MonName = 100 Then
              LD(i).MonName = Junggi(j).MonNumber '중기에 따른 달 이름 넣기(초기값)
            ElseIf Junggi(j).MonNumber = 11 Or LD(i).MonName = 11 Then '동짓달은 무조건 11월로 입력
              LD(i).MonName = 11
            End If
          End If
        Next j
      Next i
    
    '천정동지가 포함된 달과 계산하려는 해의 동짓달 사이에 포함된 달의 수 찾기
      Count1 = 0
      For i = 0 To k - 1
        If PreWinter < LD(i).StartDay And LD(i).StartDay <= ThisWinter Then Count1 = Count1 + 1
        If PreWinter < LD(i + 1).StartDay And LD(i).StartDay <= PreWinter Then idx1 = i 'idx1에 천정동지월의 변수번호 저장
        If ThisWinter < LD(i + 1).StartDay And LD(i).StartDay <= ThisWinter Then idx2 = i 'idx2에 동지월의 변수번호 저장
      Next i

      '포함된 달의 개월수에 따라 치윤 시작(4개의 경우로 윤달의 형태 지정)
      If Count1 = 12 Then
        LeapType = 4 '조건 4. 천정동지월과 동지월 사이에 12개월이 있으면 이 사이의 달은 모두 평월.
      Else '13개월이면 윤달이 있음.
        If LD(idx1 + 1).Junggi = True And LD(idx1 + 2).Junggi = True Then LeapType = 1  '조건 1
        If LD(idx1 + 1).Junggi = False And LD(idx1 + 2).Junggi = True Then LeapType = 2  '조건 2
        If LD(idx1 + 1).Junggi = True And LD(idx1 + 2).Junggi = False Then LeapType = 3  '조건 3
    '조건 1: 천정동지월 다음 2달이 모두 유중월, 평 12월과 평 1월임.
    '조건 2: 천정동지월 다음 달은 무중월, 그 다음은 유중월, 윤 11월과 평 12월.
    '조건 3: 천정동지월 다음 달이 유중월, 그 다음은 무중월, 평 12월과 윤 12월.  

        '여기서부터 윤달의 위치 추정

        Leap13 = False
        For i = (idx1 + 3) To (idx2 - 1)  '(천정동지월+3개월)째부터 금년 동지월까지 검사
          If LD(i).Junggi = False Then Leap13 = True '이 기간에 무중월이 있는지를 탐색
        Next i
        If LeapType = 2 Or LeapType = 3 Then Leap13 = False '조건 2와 3일 때는 이 기간동안 모두 평월임.
      End If


      '천정동지월에서 (천정동지월+2개월)까지 치윤, 월 번호 매기기.
      LCount = 0
      Select Case LeapType
       Case 1, 4
         LD(idx1 + 1).MonName = 12: LD(idx1 + 1).LYear = dYear - 1: LD(idx1 + 1).Junggi = True
         LD(idx1 + 2).MonName = 1: LD(idx1 + 2).LYear = dYear: LD(idx1 + 2).Junggi = True
        
       Case 2
          LD(idx1 + 1).MonName = 11: LD(idx1 + 1).LYear = dYear - 1: LD(idx1 + 1).Junggi = False
          LD(idx1 + 2).MonName = 12: LD(idx1 + 2).LYear = dYear - 1
        
       Case 3
         LD(idx1 + 1).MonName = 12: LD(idx1 + 1).LYear = dYear - 1
         LD(idx1 + 2).MonName = 12: LD(idx1 + 2).LYear = dYear - 1: LD(idx1 + 2).Junggi = False
     
      End Select
      LD(idx1).MonName = 11: LD(idx1).LYear = dYear - 1
    
      '(천정동지월+3개월)부터 동지월까지 치윤, 월 번호 매기기
      fMON = 1: A = 0
      If LeapType = 4 Then A = 1 '조건 4는 조건 1과 같은 방법으로 처리
      For i = idx1 + 3 To idx2
        LD(i).LYear = dYear
       
        If LeapType = 1 Then '조건 1(또는 4)이면 첫번째 나오는 무중월을 윤달로 간주.
          If LD(i).Junggi = True Or LCount > 0 Then
            LD(i).Junggi = True
            A = A + 1
          Else
            LCount = 1
          End If
          LD(i).MonName = fMON + A
       
        Else '조건 2, 3이면 (천정동지월+3개월)부터 동지월까지는 모두 평월로 처리.
          LD(i).MonName = fMON + A
          LD(i).Junggi = True
          A = A + 1
        End If
      Next i
     
      For i = 0 To 24  '달 길이 계산
        If Abs(LD(i + 1).StartDay - LD(i).StartDay) < 31 Then
          LD(i).MonLength = LD(i + 1).StartDay - LD(i).StartDay
        End If
      Next i

      For i = 0 To k - 1 '입력일이 포함된 달을 찾아 음력 날짜 출력
        If jd0 >= LD(i).StartDay And jd0 < LD(i + 1).StartDay Then
          LunarYear = LD(i).LYear
          LunarMon = LD(i).MonName
          LunarDay = jd0 - LD(i).StartDay + 1
          IsLeap = Not LD(i).Junggi
          Exit For
        End If
      Next i
    End Sub


음력을 산출하는 구체적인 과정은 이 글에서 설명한대로 입니다. 추후에 실제 예를 들어가며 더욱 자세히 설명토록 하겠습니다.


?
  • ?
    rub 2014.03.14 23:06


    안녕하세요


    단순히 C#으로 변환해보다가

    몇가지 알아낸게 있어서 글을 올립니다.


    1. LuniSolarCal에서 B As Integer, M As Integer는 전혀 사용이 되지 않습니다.

    2. 월, 일에 관한 변수들이 어쩔때는 byte, 어쩔때는 int로 사용이 되는것 같습니다.

    3. LuniSolarCal에서 Leap13 As Boolean는 값을 대입하긴 하지만 사용이 안되고 있습니다.


    Leap13은 중요한 역할을 하는거 같은데 안쓰여도 되는건지요.


  • ?
    창환 2014.03.15 10:58

    네, 확인해보니 그렇네요.

    1. 안 쓰는 변수는 지우면 됩니다. 프로그램을 만들면서 쓸데없는 변수가 남았네요.

    2. 전부 정수형으로 사용하시면 됩니다.

    3. 예전에는 사용하던 변수였으나, 프로그램을 수정하면서 쓰이지 않게 되었습니다. 해당 부분은 빼셔도 됩니다.

  • ?
    글쓴이 2015.07.27 07:38

    대단합니다.

    좋은 글 잘 보고 갑니다.

  • ?
    루나 2017.11.08 14:19

    감사히 사용중이오나 현재 오늘일을 기준으로 음력변환해보니 한달이 더 갑니다..

    어떻게 하죠?ㅠㅠ

  • profile
    창환 2017.11.08 23:10

    실험해보도록 하겠습니다. 

  • profile
    창환 2017.11.19 00:06

    제 환경에선 정상 실행됩니다만..

     

    ? Sol2Lun(2017,11,19)

    2017-10-2

    ? Lun2Sol(2017,10,2,False )

    2017-11-19

     
    개발 환경이 어떤지 알 수 있을는지요?
  • ?
    루나 2017.11.22 19:13

    개발환경은 

    윈도우즈10 홈이며

    VS2010 에서 VB를 이용해 개발중입니다.

     

    제가 댓글을 달았던 그 날짜를 기준으로 한달이 쳐져서 나옵니다 ㅜㅜ

  • ?
    루나 2017.11.22 20:25

    비밀번호 까먹어서 수정이 안데네요 ㅎㅎ

    한달이 처지는게 아니라 빨라요..

     

    2017,11,08 넣으면 

    2017,09,20 인가 나와야 하는데 10,20인가 로 나오네요..

  • profile
    창환 2017.11.26 10:03

    .Net 기반이라 VB6과 다르게 작동하나 봅니다. 

    확인해 보겠습니다. 

  • ?
    루나 2017.12.10 18:57

    아직 확인 안되시나봐요 ㅠㅠ

    그래도 감사합니다.^^ 도움주시려고 하시는것 자체로도 ..^^;;

  • ?
    신념 2020.11.29 20:10
    여기서 질문을 드려도 보실 수 있을지 모르겠네요. 태양의 위치를 계산하는 로직을 찾다가 선생님의 홈페이지를 발견했습니다.
    홈페이지에 추천해주신대로 Astronomical Algorithms 원서도 구해서 보고 있는데 쉽지가 않네요^^;
    올려주신 글 중에 음력-양력 상호변환 함수 모듈에 SUN이라는 함수로 태양 위치를 구하는 로직이 있고, 태양이 특정 황경을 통과할 때의 날짜 계산 엑셀의 매크로 코드를 보니 VSOP87_FK5 함수가 있는데 이것 역시 태양의 위치를 구하는 로직으로 이해했습니다.
    2개 로직이 동일(근사치)한 결과를 계산하는 로직으로 보면 되는지, 정확도는 어떤게 더 좋은지가 궁금합니다.
    엑셀자료는 2009년에 올리셔서 음력-양력 상호변환 함수 모듈보다 일찍 올리신 것 같은데, 음력-양력 상호변환 함수 모듈보다는 엑셀매크로 쪽이 좀더 간결하면서도 체계적인 것 같아 보여서, 선생님께서 최신으로 사용하시는 로직이 어떤 것인지 궁금하여 질문을 올리게 되었습니다.
    이 질문을 언제 보실지는 모르겠지만, 보신다면 짧게라도 의견 부탁드립니다.
  • profile
    창환 2020.12.01 21:43
    안녕하세요. 질문은 편하게 하셔도 됩니다.
    계산 코드는 엑셀 시트의 것이 훨씬 정밀도가 높습니다. 아무래도 장기간 계산을 지원하려다 보니 계산 정밀도가 높은 코드를 쓸 수밖에 없습니다. 모듈에 있는 것의 24절기 시각이 10분까지의 오차가 생길 수 있다면 엑셀 차트의 것은 훨씬 장기간에 걸쳐 1분 내외의 오차로 계산합니다. 대신 계산 자원을 훨씬 더 요구하고요. 즉 동작 속도가 약간 더 느립니다. 옛날 컴퓨터에서는 체감이 될 정도로 느렸는데, 요즘 나오는 컴퓨터 성능이면 사실 속도에 큰 차이가 안 나긴 합니다.
    그외의 차이는 엑셀 시트의 코드는 음양력 계산 말고도 이런저런 다른 기능을 지원하기 위해서 코드 자체가 더 덩치가 크고 복잡합니다. 다른 프로그램에는 속도보다는 장기간 계산 지원과 계산 정확도가 중요해서 엑셀 시트의 것을 씁니다.

  1. 망원경 시스템의 빛 손실에...

    * 반사율 부분 설명에 잘못된 부분이 있어 고쳤습니다(2013. 4. 23.). 망원경의 가장 기본적인 역할은 빛을 모으는 것입니다. 커다란 렌즈나 거울을 이용하여 빛을 많이 모아서 어두워서 눈으로 볼 수 없는 천체를 볼 수 있게 도와주는 것, 이것이 가장 기본적...
    Date2013.04.21 Category천문 일반 By창환 Views6384
    Read More
  2. No Image

    11월 방문할 ISON 혜성(C/2...

    최신의 관측자료를 바탕으로 쓴 아래 기사도 참고하세요. ISON 혜성(C/2012 S1), 좀 더 자세한 소식 - http://blueedu.dothome.co.kr/xe/16782 올해 11월 말에서 12월 초 세기의 대혜성이 될 가능성이 있는 ISON 혜성(C/2012 S1)이 다가오고 있습니다. 자세한 ...
    Date2013.04.02 Category천문 일반 By창환 Views3739
    Read More
  3. C/2011 L4(PanSTARRS) 혜성...

    지난 3월 중순, 혜성 관측자들을 들뜨게 했던 C/2011 L4(PanSTARRS) 혜성은 이제 태양에서 점차 멀어지면서 서서히 어두워지고 있습니다. 그러나 여전히 밝은 혜성(밝기에 비해 상당히 작긴 하지만..)의 면모를 유지하고 있는데요, 3월말까지는 2~3등급으로 밝...
    Date2013.03.24 Category천문 일반 By창환 Views4602
    Read More
  4. C/2011 L4(PanSTARRS) 혜성...

    ▲ C/2011 L4(PanSTARRS) 혜성 스케치(2013년 3월 16일 19시 10분) 3월 16일, 예상보다 날씨가 좋아서 다시 혜성을 관측하러 갔습니다. 장소는 15일과 같습니다. 높은 구름이 약간 있었지만, 15일에 비해 대기의 투명도가 좋았습니다. 혜성 관측 이래 가장 좋...
    Date2013.03.17 Category관측기 By창환 Views5385
    Read More
  5. C/2011 L4(PanSTARRS) 혜성...

    ▲ C/2011 L4(PanSTARRS) 혜성(2013년 3월 15일 19시 20분, 가시광+근적외) 3월 15일, 다시 혜성을 관측했습니다. 날씨는 11일보다 조금 더 좋았지만, 높은 구름이 약간 떠 있었고, 지평선 부근의 연무는 강도는 11일보다 약했지만, 여전히 있었습니다. 관측장...
    Date2013.03.16 Category관측기 By창환 Views5160
    Read More
  6. C/2011 L4(PanSTARRS) 혜성...

    ▲ C/2011 L4(PanSTARRS) 혜성(2013년 3월 11일 19시 5분) 2013년 3월 11일, C/2011 L4(PanSTARRS) 혜성 관측을 다녀왔습니다. 관측장소는 대구광역시 달성군에 있는 화원동산입니다. 낙동강이 공원 서쪽으로 흐르고 있어 서쪽이 트여있어 시야가 좋고, 접근성...
    Date2013.03.12 Category관측기 By창환 Views4018
    Read More
  7. C/2011 L4(PanSTARRS) 혜성...

    * 혜성이 예상보다 훨씬 밝아지고 있습니다. 근일점 통과 전 0등급을 돌파하여 지금은 -1등급 정도에 이른 것으로 보입니다. 아직 고도가 낮아 관측환경이 좋지 않지만, 이 추세라면 3월 중순 내내 1등급에서 2등급 사이의 밝기를 유지할 것으로 예상됩니다. ...
    Date2013.02.26 Category천문 일반 By창환 Views4868
    Read More
  8. 주요 시간체계 1

    현재 사용 중인 주요한 시간체계를 간단히 정리했습니다. 1. 세계시(UT, Universal Time) 경도 0도에서의 평균태양시로 과거에 쓰던 GMT(그리니치평균시)를 대체합니다. UT는 지구의 회전과 관련이 있는 시간체계입니다. 1) UT0 : 관측지에서 측정한 평균태양...
    Date2013.01.25 Category천문 계산 By창환 Views6808
    Read More
  9. No Image

    음력-양력 상호 변환 함수 ...

    음양력 변환 모듈에 대한 마지막 글입니다. 이번에는 양력을 음력으로 변환하는 방법에 대해 설명하겠습니다. 정확히는 율리우스적일로된 날짜를 음력으로 바꾸는 방법입니다. 음력을 계산하는 함수는 단 하나입니다. LuniSolarCal: 율리우스 적일을 음력으로 ...
    Date2013.01.18 Category천문 계산 By창환 Views4558
    Read More
  10. No Image

    음력-양력 상호 변환 함수 ...

    이번 글에서는 음력을 양력으로 변환하는 함수와 음양력 변환에 쓰는 보조함수에 대하여 설명하겠습니다. 양력을 음력으로 바꾸는 함수는 다소 긴 설명이 필요하므로 마지막 편에서 다루도록 하겠습니다. 이 글에서 다룰 함수는 다음 3개 입니다. Sol2Lun: 양...
    Date2013.01.17 Category천문 계산 By창환 Views10711
    Read More
Board Pagination Prev 1 2 3 4 5 6 7 8 9 10 ... 15 Next
/ 15
Powered by XE