조회 수 5242 댓글 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. LEO 80 굴절망원경 시험 촬영(2018년 3월 보름달)

    얼마 전 휴대용으로 가볍게 쓰기 위해 LEO 80 굴절 망원경을 구했습니다. 구경 80mm, 초점거리 500mm의 아크로메틱 굴절망원경으로 가격에 비해서는 제법 성능이 괜찮은 편으로 알려져 있습니다. 망원경이 가벼운 편이라 사진기용 삼각대를 쓰면 휴대성이 굉장...
    Date2018.03.02 Category관측기 By창환 Views1701
    Read More
  2. BW20 6.5x32과 옵티크론 사바나 6x30 비교

    (앞이 사바나, 뒤가 BW20입니다.) BW20 6.5x32는 중국에서 만든 저배율 쌍안경입니다. 비교적 저가에 괜찮은 성능을 보여주는 제품으로 알려진 쌍안경입니다. 현재 국내에는 판매하는 곳이 없고 외국의 쇼핑몰에서 직접 구해야 합니다(2017년 11월 기준 중국 ...
    Date2017.11.18 Category천문장비 By창환 Views1510
    Read More
  3. 옵티크론 사바나(Savanna) 6x30 WP 어포컬 사진 모음

    옵티크론 사바나(Savanna) 6x30 쌍안경을 이용해서 찍은 어포컬 사진 모음입니다. 카메라는 대부분 아이폰 6s입니다. 아래쪽에 대형 센서가 달린 카메라(올림푸스 E-M10)을 사용해 찍은 예제도 있습니다. 사진 아래에 사용한 카메라를 명시해 두었습니다. 쌍안...
    Date2017.11.06 Category천문장비 By창환 Views335
    Read More
  4. 옵티크론 사바나(Savanna) 6x30 WP 사용기

    옵티크론 사바나(Savanna) 6x30 WP <사양> - 구경 : 30mm - 배율 : 6배 - 시야 : 8.0도(겉보기 48도) - 아이릴리프 : 21mm - 프리즘 소재, 방식 : Bak4, 포로 프리즘 - 코팅 : 전체 광학면 멀티 코팅 - 최소 초점 거리 : 3m - 크기 : 116x160mm, 485g - 특징 :...
    Date2017.10.24 Category천문장비 By창환 Views2934
    Read More
  5. 쌍안경용 태양필터 만들기

    여가가 생긴 김에 쌍안경(솔로몬 EL 10x56)에 쓸 태양필터를 만들었습니다. 제대로 된 천체망원경만큼은 아니지만 휴대하기 편하고 부분일식이나 커다란 흑점을 관측하는 용도로는 쓸만합니다. 필터를 만드는 방법은 간단합니다. 적당한 재료를 써서 쌍안경 대...
    Date2017.10.06 Category천문 일반 By창환 Views972
    Read More
  6. No Image

    쌍안경, 단안경 유효구경 실측 자료

    지금까지 사용했던 쌍안경, 단안경 종류의 실측 유효 구경입니다. 정밀 측기가 없이 간이 측정을 한 까닭에 1mm 정도 차이가 날 수 있습니다. < 쌍안경 > BW22 8x32 : 31mm 마이스코프 15x70 : 63mm 솔로몬 EL 10x56 : 55mm S-MS 20x80 : 76mm 이프랑티스 8x21...
    Date2017.09.04 Category천문장비 By창환 Views794
    Read More
  7. Meade Lightbridge Mini 82 사용기

    Meade Lightbridge Mini 82 < 사양 > - 구경 : 82mm(구면경) - 초점거리 : 300mm(F3.7) - 부경 지름 : 27mm - 부경 차폐율 : 33%(지름 기준), 11%(면적 기준) - 방식 : 뉴턴식 반사 - 접안부 : 31.7mm(1.25") 랙피니언식 - 접안렌즈 : H 26mm, H 9mm, 2배 바로...
    Date2017.08.27 Category천문장비 By창환 Views2312
    Read More
  8. 망원렌즈를 망원경으로 쓰기

    사진용 망원렌즈를 망원경으로 바꿔봤습니다. 사용한 렌즈는 토키나(Tokina) 400mm F5.6 RMC 렌즈입니다.상당히 오래 전에 나온 수동 초점 초망원렌즈입니다. 초망원렌즈로는 크기도 작고 삼각대 마운트도 튼튼해서 좋습니다. 대물렌즈 구경은 65mm인데, 초점...
    Date2017.08.18 Category천문장비 By창환 Views1013
    Read More
  9. BW22 8x32 쌍안경 이야기 (1) 내부

    BW22 8x32 쌍안경 <사양> - 구경 : 32mm - 배율 : 8배 - 시야 : 8.1도(겉보기 65도) - 아이릴리프 : 15mm - 프리즘 소재, 방식 : Bak4, 포로 프리즘 - 코팅 : 전체 광학면 멀티 코팅 - 크기 : 105x165x48mm, 520g - 특징 : 생활방수 지원(질소충전) - 부속품 :...
    Date2017.08.07 Category천문장비 By창환 Views1859
    Read More
  10. 나시카 PV 8x21 단안경 사용기

    나시카 PV 8x21 단안경 <사양> - 구경 : 21mm - 배율 : 8배 - 시야 : 7.5도 (131m/1000m) - 사출동공 지름 : 2.6mm - 최소초점거리 : 6m - 프리즘 방식 : 루프프리즘 - 크기 : 9.6cm x 3.6cm, 75g - 재질 : 플라스틱 - 부속품 : 손목끈, 보관용 소형 주머니, ...
    Date2017.08.06 Category천문장비 By창환 Views1155
    Read More
Board Pagination Prev 1 2 3 4 5 6 7 8 9 10 ... 20 Next
/ 20
Powered by XE