IDL/Programming

Yale Bright Star Catalogue 데이터를 읽고 처리하기 [1]

이상우_IDL 2025. 3. 25. 15:44
728x90

Yale Bright Star Catalogue(예일 밝은 별 목록, YBSC)는 사람이 육안으로 관측할 수 있는 별들을 목록화하여 배포되고 있는 카탈로그 자료입니다. 여기서 "사람이 육안으로 관측할 수 있는"의 기준은 공식 웹페이지의 설명에 의하면 별의 겉보기 등급(Apparent Magnitude or Visual Magnitude)이 대략 7.0 이하인 경우들인 것으로 언급되고 있습니다. 참고로 밝기 등급은 그 값이 작을수록 밝고 클수록 어둡습니다. 이 목록에 등재된 별들의 갯수는 총 9110개이며 해당 목록 자료는 앞서 언급한 공식 웹페이지에서 배포되고 있습니다.

 

그래서 이 YBSC 목록 자료를 받아서 IDL에서 처리하고 관련된 표출까지도 진행해보는 과정을 수 차례에 걸쳐서 소개해보고자 합니다. 오늘은 먼저 공식 웹페이지에서 해당 자료 파일을 수신하고 IDL에서 이 파일에 수록된 데이터를 읽어들이는 과정을 살펴보겠습니다. 이 자료 파일은 공식 웹페이지에서 바이너리 파일(Binary File)로 배포되고 있으며, 웹페이지상에서 다음과 같은 링크 버튼을 누르면 받을 수 있습니다.

 

즉 위에 표시된 링크 버튼을 누르면 'BSC5'라는 이름(확장자는 없습니다)의 바이너리 파일을 내려받게 됩니다. 관련 설명에 의하면 이 파일의 크기는 291548 바이트(Byte)라고 명시되어 있으며, 실제로 이 파일 내에는 예일 밝은 별 별 목록 데이터가 291548 바이트만큼 수록되어 있습니다. 일단 이 파일은 여기에도 첨부해놓겠습니다.

 

BSC5
0.28MB

 

이제 이 파일을 IDL에서 읽어야 할텐데요. 바이너리 파일이기 때문에 그 특성에 맞는 방법을 동원해야 합니다. IDL에서 바이너리 파일을 읽는 방법에 관해서는 예전에 관련 게시물들(링크1, 링크2, 링크3)을 올린 바 있으며, 오늘도 기본적으로는 이 게시물들에서 소개된 방법들을 참조하여 작업을 진행할 것입니다. 사실 과학기술 분야에서 이와 같이 데이터가 바이너리 파일의 형태로 제공되는 경우가 드물지 않습니다. 따라서 오늘 소개될 내용은 바이너리 파일의 형태로 배포되는 데이터를 IDL에서 읽고 처리하는 방법에 관한 또 하나의 좋은 예제가 될 것입니다.

 

먼저 이 파일에 대하여 READ_BINARY 함수를 사용하여 파일 내 데이터의 전체 바이트수를 확인해봅시다.

 

file = 'BSC5'
result = READ_BINARY(file)
HELP, result

 

RESULT          BYTE      = Array[291548]

 

출력된 내용을 보면 291548개의 바이트형 값들로 구성된 배열 result가 획득되며, 공식 웹페이지에서 기술된 내용과 동일하게 291548 바이트의 데이터가 수록되어있는 파일임을 알 수 있습니다. 물론 이제부터는 바이너리 파일 내에 수록된 데이터를 순차적으로 정확하게 추출하는 작업을 진행해야 하는데요. 이 과정을 위해서는 바이너리 파일 내에 데이터가 수록된 방식에 대한 정확한 정보가 필요합니다. 물론 이러한 정보는 공식 웹페이지에서 상세히 기술되어 있는데, 헤더 부분에 관한 내용실제 데이터에 관한 내용으로 나눠서 소개가 되고 있습니다. 따라서 지금부터 이 파일을 읽는 과정도 이러한 정보를 바탕으로 할 것입니다.

 

그래서 파일 내에 수록된 데이터의 구조에 대한 설명을 자세히 보면, 처음 28바이트는 헤더(Header) 정보이고 그 뒤부터는 실제 데이터 값들을 담은 32바이트의 레코드들이 순차적으로 이어지는 구조를 갖는다고 합니다. 즉 전체 291548 바이트 중에서 처음 28바이트는 헤더 부분이므로 그 뒤에 이어지는 나머지 부분은 291548-28=291520바이트가 되는데, 이 크기를 32바이트로 나누면 정확히 9110이 됩니다. 즉 헤더 부분 다음에는 9110개의 별들에 대한 데이터가 순차적으로 수록된 구조이며, 결국 헤더 및 모든 데이터들이 점유하는 바이트 크기는 28+32*9110=291548바이트가 됩니다. 따라서 9110개의 별들에 대한 데이터를 읽으려면, 일단 처음의 헤더 부분(28바이트)을 먼저 읽어서 넘기고 그 다음부터 32바이트씩 총 9110회에 걸쳐서 반복 작업에 의하여 읽어야 합니다. 물론 그 과정에서는 각 회차마다 필요한 값을 추출하여 배열에 담는 작업도 병행되어야 할 것입니다. 각 회차마다 읽게 될 32바이트 크기의 레코드 내에는 여러 종류의 데이터 값들이 수록되어 있습니다. 웹페이지에 소개된 설명에 의하면 이 32비트의 레코드 내에 수록된 데이터는 다음과 같습니다.

 

Real*4 XNO Catalog number of star
Real*8 SRA0 B1950 Right Ascension (radians)
Real*8 SDEC0 B1950 Declination (radians)
Character*2 IS Spectral type (2 characters)
Integer*2 MAG V Magnitude * 100
Real*4 XRPM R.A. proper motion (radians per year)
Real*4 XDPM Dec. proper motion (radians per year)

 

이와 같이 카탈로그상의 일련번호, RA, Dec, Spectral Type, V 등급 등 총 7종의 값들로 구성되어 있으며 각각의 값마다 점유하는 바이트수는 4, 8, 8, 2, 2, 4, 4입니다. 이 바이트수를 모두 합치면 32가 됩니다. 그리고 각 항목마다 자료형이 서로 다른데 Real*4는 4바이트 실수, Real*8은 8바이트 실수, Integer*2는 2바이트 정수에 해당됩니다. 따라서 이러한 자료형 및 바이트수의 특성을 고려하여 값을 읽어들여야 합니다. 여기서 4바이트 실수는 IDL에서는 일반 실수형에 대응되고, 8바이트 실수는 2배 정밀도 실수형, 2바이트 정수는 일반 정수형에 대응됩니다. 결국 매 회차마다 읽어들이는 32바이트 크기의 레코드로부터 개별 데이터 값을 해당 자료형에 맞춰서 추출하는 작업을 정의하는 것이 가장 중요합니다.

 

그러면 이러한 전략을 바탕으로 지금부터 바이너리 파일을 읽어봅시다. 이 작업에서는 READ_BINARY 함수를 주로 사용하고자 합니다. 가장 먼저 헤더(Header)부터 읽어야 하는데 이 과정은 다음과 같이 처리합니다.

 

result = READ_BINARY(file, DATA_DIMS=7, DATA_TYPE=3)
HELP, result
PRINT, result

 

실제로 헤더에 관하여 웹페이지에 소개된 설명에 의하면 헤더 자료는 4바이트 정수 7개(4x7=28바이트)로 구성됩니다. 따라서 위의 내용에서도 READ_BINARY 함수를 사용하면서 관련 키워드들을 이러한 특성에 맞게 설정하였습니다. 즉 IDL에서 4바이트 정수(Long Integer)에 해당되는 코드번호 3을 DATA_TYPE 키워드에 부여하고, 갯수에 해당되는 숫자 7을 DATA_DIMS 키워드에 부여하였습니다. 즉 위의 내용은 4바이트 정수 7개를 연달아 읽어서 배열로 가져오라는 의미입니다. 실행 후 출력된 내용을 보면 다음과 같습니다.

 

RESULT          LONG      = Array[7]
           0           1       -9110           1           1           1          32

 

이와 같이 28바이트 크기의 헤더 부분을 읽었는데 사실 이 값들은 후속 작업에서는 사용될 예정이 없지만 순서상 앞부분에 위치하므로 이와 같이 읽고 넘겨야 그 다음 과정으로 진행할 수 있니다. 이제부터는 실제 데이터에 해당되는 9110개의 레코드들을 순차적으로 하나씩 접근해야 합니다. 먼저 9110개 중 첫번째 레코드부터 접근해봅시다. 물론 이 과정에서는 앞서 살펴보았던 각 레코드 내 데이터 구조에 관한 내용을 근거로 하여 READ_BINARY 함수를 사용해야 합니다. 그 과정은 다음과 같습니다.

 

tmp1 = READ_BINARY(file, DATA_TYPE=4, DATA_START=28, DATA_DIMS=0)
tmp2 = READ_BINARY(file, DATA_TYPE=5, DATA_START=32, DATA_DIMS=0)
tmp3 = READ_BINARY(file, DATA_TYPE=5, DATA_START=40, DATA_DIMS=0)
tmp4 = READ_BINARY(file, DATA_TYPE=1, DATA_START=48, DATA_DIMS=0)
tmp5 = READ_BINARY(file, DATA_TYPE=1, DATA_START=49, DATA_DIMS=0)
tmp6 = READ_BINARY(file, DATA_TYPE=2, DATA_START=50, DATA_DIMS=0)
tmp7 = READ_BINARY(file, DATA_TYPE=4, DATA_START=52, DATA_DIMS=0)
tmp8 = READ_BINARY(file, DATA_TYPE=4, DATA_START=56, DATA_DIMS=0)

HELP, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8

 

이 내용에서는 레코드 내 값들을 순서 및 해당 자료형에 맞춰서 하나씩 접근하고 있습니다. 먼저 주목해야 할 것은 레코드 내 각 항목별로 READ_BINARY 함수를 사용하여 읽는다는 것입니다. 이 때 DATA_TYPE 키워드에 부여된 숫자는 각 항목의 자료형에 대응되는 코드번호입니다. 자료형별 코드번호에 대해서는 IDL 도움말에서 READ_BINARY 함수에 관한 내용에 자세히 소개되어 있습니다. 여기서 각 항목마다 부여된 코드번호를 결정한 근거를 표로 정리해보면 다음과 같습니다.

 

그리고 DATA_START 키워드에 부여된 숫자는 각 항목을 읽을 바이트 위치를 나타냅니다. 앞서 이미 우리가 헤더 부분을 읽었는데, 이 헤더는 크기가 28바이트이고 파일의 맨 처음 부분에 위치하므로 바이트 위치상으로는 0~27의 범위를 점유하고 있다고 보면 됩니다. 그 다음에 이어지는 첫번째 레코드에서 첫번째 항목의 바이트 위치는 28이 됩니다. 그리고 이 항목의 크기가 4바이트이므로 그 다음 항목의 위치는 32가 됩니다. 이러한 방식으로 각 항목의 바이트 위치를 DATA_START 키워드에 부여하면 됩니다. 그리고 DATA_DIMS 키워드에 0을 부여한 것은 각 항목을 배열이 아닌 단일 값으로 읽으라는 의미입니다. 이와 같은 방식으로 읽은 각 항목의 값을 tmp1, tmp2, ....., tmp8 까지 총 8개의 변수들에 담게 됩니다.

 

그런데 위의 표를 보면 항목들의 갯수는 7개인데 변수들의 갯수는 8개라는 것이 좀 이상합니다. 그 이유는 4번째 항목인 Spectral Type에 해당되는 문자값이 2개의 철자들로 구성되는데, 이러한 과정에서는 2개의 철자들이 합쳐진 하나의 문자로서 바로 읽는 것이 아니라 각 철자에 대응되는 바이트형 숫자를 읽고 그 숫자에 대응되는 문자를 각각 얻어서 이들을 나중에 합쳐야 합니다. 즉 바이너리 파일로부터 문자 데이터를 읽는 경우에는 여러 철자들이 합쳐진 단어 또는 문장을 바로 읽는 것이 아니라 그냥 한 글자씩 읽어들인 후 나중에 합쳐야 한다는 것으로 이해하면 됩니다. 설명이 좀 복잡하긴 하지만, 일단 HELP 명령에 의하여 출력된 내용을 먼저 봅시다.

 

TMP1            FLOAT     =       1.00000
TMP2            DOUBLE    =      0.022536564
TMP3            DOUBLE    =       0.78939788
TMP4            BYTE      =   65
TMP5            BYTE      =   49
TMP6            INT       =      670
TMP7            FLOAT     =  -5.81776e-08
TMP8            FLOAT     =  -8.72665e-08

 

먼저 tmp1은 카탈로그상의 일련번호입니다. 그리고 tmp2 및 tmp3은 각각 RA(Right Ascension), Dec(Declination)에 해당되는 각도의 라디안(Radian) 값입니다. 이 값들은 나중에 도(Degree) 단위로 변환해야 할 수도 있습니다. 그리고 tmp4와 tmp5를 보면 각각 65와 49라는 바이트형 값들인데, 이 둘을 각각 문자로 변환하여 합치면 Spectral Type에 해당되는 문자값을 얻게 됩니다. 그 과정은 잠시후에 보기로 합니다. 그리고 tmp6은 시등급(V Magnitude)에 해당되는 값인데 원래의 값에 100을 곱한 정수의 형태입니다. 따라서 원래의 시등급 값을 얻으려면 100으로 나눠줘야 합니다. 따라서 tmp6의 값이 670이라는 것은 실제 시등급의 값이 6.7이라는 의미가 됩니다. 그리고 tmp7, tmp8은 각각 RA 및 Dec 방향의 고유운동(Proper Motion) 수치에 해당되는 각도의 라디안 값입니다.

 

그러면 여기서 Spectral Type에 해당되는 문자값을 얻어봅시다. 이를 위해서는 앞서 얻은 tmp4, tmp5의 바이트형 값에 대응되는 문자를 얻어야 합니다. 이를 위해서는 다음과 같이 STRING 함수를 사용하면 됩니다.

 

HELP, STRING(tmp4), STRING(tmp5)

 

이와 같이 tmp4와 tmp5 각각에 대하여 STRING 함수를 적용하면 해당 문자를 얻게 됩니다. 출력된 내용을 보면 다음과 같습니다.

 

<Expression>    STRING    = 'A'
<Expression>    STRING    = '1'

 

따라서 이 두 문자를 합치면 Spectral Type이 'A1'임을 알 수 있습니다. 이와 같이 바이너리 파일에 수록된 문자 데이터는 각 철자에 대응되는 바이트형 숫자를 한번 더 해독해야 얻을 수 있다는 점을 유의해야 합니다.

 

어쨌든 이와 같은 방식으로 첫번째 레코드 32바이트의 내용을 해독하였습니다. 물론 이것은 총 9110개의 레코드들 중 첫번째일 뿐입니다. 두번째 레코드를 읽는 것도 기본적으로는 위와 같은 방식으로 작업하면 됩니다. 다만 여기서 주의할 것은 모든 레코드를 순차적으로 제대로 읽으려면 각 레코드의 바이트 위치를 순서에 맞게 계속 옮겨줘야 한다는 것입니다. 첫번째 레코드가 시작되는 바이트 위치가 28임을 앞서 언급한 바 있습니다. 그리고 각 레코드의 크기는 32바이트입니다. 그러면 두번째 레코드의 시작 위치는 28+32=60이 될 것임을 알 수 있습니다. 이런 식으로 따지면 세번째 레코드의 시작 위치는 28+32+32=92, 네번째 레코드는 28+32+32+32=124, 다섯번째 레코드는 28+32+32+32+32=156 등과 같이 될 것입니다. 따라서 9110개의 모든 레코드들에 대한 데이터를 읽으려면, 각각의 레코드를 읽는 작업을 총 9110회에 걸쳐서 위치를 옮겨가면서 반복적으로 수행해야 합니다. 그리고 궁극적으로는 이러한 과정을 통하여 레코드를 구성하는 항목들 각각에 대한 배열을 얻는 것이 중요한 목표가 될 것입니다.

 

여기서 BSC5 파일에 수록된 데이터의 전체적인 구조를 도식적으로 나타내본다면 대략 다음과 같습니다. 결국 이 파일의 데이터를 읽어들이는데 있어서는 이와 같은 구조를 염두에 두고 전체적인 작업을 설계하는 것이 필요합니다. 물론 이와 같이 바이너리 파일 내에 수록된 데이터의 구조에 관한 세부적인 정보는 데이터 파일을 배포하는 배포처에서 당연히 제공을 해주며 그래야만 합니다(만약 혹시라도 제공을 안해주면 유저 입장에서는 읽을 방법이 없습니다).

 

그러면 향후 이어서 진행할 작업에 관해서는 다음 회차에서 자세히 알아보기로 하겠습니다.

 

 

 

이 글이 도움이 되었다면 게시물에 대하여 공감 버튼(하트 모양) 클릭 및 블로그 구독도 해주시면 더 큰 힘이 됩니다. 감사합니다.

LIST