A Ruby FITS I/O library based on C++ sfitsio.
RubyFits is an easy-to-use Ruby library that can be used to manipulate files in the FITS format, the standard file format for astronomy and astrophysics.
The library uses the C++ sfitsio library by Chisato Yamauchi via the SWIG interface generator.
To install this library, do the following:
-
RubyFits needs sllib and sfitsio to be installed.
These libraries can be installed via Homebrew, and
execute the following if you are missing these nice
libraries.
cd /usr/local/Library/Formula wget --no-check-certificate https://galaxy.astro.isas.jaxa.jp/%7Eyuasa/documents/20130513/sllib.rb wget --no-check-certificate https://galaxy.astro.isas.jaxa.jp/%7Eyuasa/documents/20130513/sfitsio.rb brew install sfitsio
-
Clone (download) from github.
git clone https://github.com/yuasatakayuki/RubyFits.git
-
Build with cmake.
cd RubyFits/swig mkdir build cd build cmake .. make install
Compiled library will be installed to $HOME/lib/ruby. If you do not like this, modify install prefix with -DCMAKE_INSTALL_PREFIX like:cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local
-
In the ruby scritps, you can import RubyFits by doing like:
require "RubyFits" include Fits
-
Define RUBYLIB to direct "$HOME/lib/ruby" in your .zshrc for example.
export RUBYLIB=$HOME/lib/ruby
- Takayuki Yuasa
- 2014- RIKEN The Institute of Physical and Chemical Research
- 2011-2014 Japan Aerospace Exploration Agency (JAXA)
- takayuki.yuasa _atmark_ riken.jp
- RubyFits API Reference generated by rdoc.
- Documentation contribution is very much welcome.
In the example/ folder, there are some scripts showing usages of RubyFits. "samplteTable.fits" can be used as a sample FITS file.
- readTable.rb well describes how to read TableHDU in a FITS file.
See openSave.rb.
require "RubyFits" include Fits f=FitsFile.new("sampleTable.fits") puts f puts f[0].headerKeyValueComment(1)
You can open a file after constructing an empty instance first.
f=FitsFile.new f.open("sampleTable.fits")
Save.
#save to file f.saveAs("openSave.fits")
You can check the saved FITS file with fv for example (HEASOFT should be installed):
fv openSave.fits
FitsFile::saveAs() has some aliases:
- writeToFile
- saveToFile
- writeToFile
- write
- save
- writeTo
- saveTo
See openSave.rb.
A FitsFile instance contains one or more HDU (header data unit, or sometimes called 'extension'). They can be accessed via one of the following accessors:
f=FitsFile.new("sampleTable.fits") f.hdu(1) # by index f[1] # by index f.hdu("SPECTRUM") # by name
An HDU contains Header and Data (table or image). Key-value records in a header can be accesses as follows:
#dump the 1-st and 3-rd header key-value records puts f[1].headerKeyValueComment(1) puts f[1].headerKeyValueComment(3) puts f[1].headerKeyValueComment("TELESCOP") #dump some header key-value records puts f[1].header("TELESCOP") puts f[1].header("RA_PNT").to_f #retrieve header key-value record f[1].headers.each { |headerRecord| puts headerRecord.keyword }
FitsFile::header(indexOrKeyword) returns header record object (an instance of FitsHeaderRecord class). Values of header record objects can be obtained via usual cast methods such as to_s(), to_f(), to_i(), and so on.
Header key-value record can be modified by assigning new value to the header record object. If no entry is found with a provided keyword, a new entry will be created with keyword and value (and comment if specified).
#modify header records f[1].header("TELESCOP") << "ASTRO-H" f[1].setHeader("INSTRUME","SXS") f[1].addHeader("NEWKEY",3.14159, "COMMENT")Type of record can be automatically changed depending on the assigned value.
#modify header record with comment f[1].header("TELESCOP") << [3.141592,"PI"]
FitsTableHDU consists of one or more FitsTableColumn objects which represent each column of the table. Data in a column can be accessed like ones contained in an array.
See readTable.rb in the example/ folder.
puts "TableHDU access example1:" puts "============================================" tableHDU=f.getHDU("SPECTRUM") puts "TableHDU #{tableHDU.getHDUName()} has #{tableHDU.nColumns()} columns and #{tableHDU.nRows} rows." tableHDU.columns.each {|column| puts column } puts "============================================"
This will result (with sampleTable.fits):
> ruby readTable.rb ... omitted ... TableHDU access example1: ============================================ TableHDU SPECTRUM has 2 columns and 256 rows. Column named "CHANNEL" = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ... (256 elements in total) ] Column named "COUNTS" = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... (256 elements in total) ] ============================================
Another example is to use array accessor of FitsTableColumn. Index or range can be specified, e.g.
puts "TableHDU access example3:" puts "============================================" tableHDU=f.getHDU("SPECTRUM") puts tableHDU["COUNTS"][100] puts tableHDU["COUNTS"][100...120].join(",") puts "============================================"The above means "retrieve data of 101th-120th entries from the 'COUNTS' column contained in the 'SPECTRUM' HDU".
This will result:
============================================ 115 115,109,115,84,87,95,79,111,76,71,93,102,93,81,88,77,73,86,71,78 ============================================
Length of TableHDU (i.e. number of entries or number of rows) can be retrieved via FitsTableColumn.length() or its aliases:
- nEntries
- nRows
- getNEntries
- getNRows
See createNewTable.fits.
require "RubyFits" include Fits #create an empty column column=FitsTableColumn.new #set column name and format column.initializeWithNameAndFormat("COUNTS","1J") #set number of entries column.resize(128) #fill rows with (row number * 2). for i in 0...column.nRows column[i]=i*2 end #another example of filling rows column[1..3]=[10,11,12] #append the created column to a TableHDU tableHDU=FitsTableHDU.new tableHDU.resize(128) tableHDU.appendColumn(column) puts tableHDU #append the create TableHDU to a FitsFile f=FitsFile.new f.append tableHDU #Write to a file f.saveAs("createNewTable.fits")
This will create a TableHDU with a column named "COUNTS" with a type of "1J" (i.e. 1 integer per row). Following the assignment, rows[1], row[2], and row[3] will have 10, 11, and 12, respectively.
FitsImageHDU handles access to ImageHDUs.
In the following example (copied from readImage.rb in the example/ folder), pHDU is the PrimarHDU os the "sampleTable.fits" file and its type is Image HDU (Primary HDU should be always ImageHDU). Although this sample FITS file does not contain meaningful values in the Primary HDU image, we can retrieve some pieces of information such as width, height, and pixel values.
require "RubyFits" include Fits f=FitsFile.new("sampleTable.fits") #Primary HDU pHDU=f.hdu(0) puts pHDU puts "Image type = #{pHDU.getTypeAsString()}" puts "Image size = #{pHDU.getXSize} x #{pHDU.getYSize}" puts "Pixel value:" puts " (1,2) = #{pHDU.getPixelValue(1,2)}" puts " (3,3) = #{pHDU.getPixelValue(3,3)}" puts " (15,2) = #{pHDU.getPixelValue(15,2)}" puts " (100,100) = #{pHDU.getPixelValue(100,100)} # <= this should be NaN because the pixel is outside the define size." croppedImage=pHDU.section(1,2)
The example code copied from newImage.rb shows how to create a new ImageHDU.
Note: In the current version of RubyFits, users have to call FitsImageHDU::imageHDU.constructImage(x,y,z,type) to actually construct an image with buffer allocated not only new() which creates an empty instance. This restriction is due to some limitation of SWIG related to the %rename attribute, and might be resolved in the future releases.
Type: type parameter should be one of the following values.
- Fits::DOUBLE_T (64-bit double precision floating value)
- Fits::FLOAT_T (32-bit single precision floating value)
- Fits::LONG_T (32-bit signed integer)
- Fits::LONGLONG_T (64-bit signed integer)
- Fits::BYTE_T (8-bit signed integer)
- Fits::SHORT_T (16-bit signed integer)
require "RubyFits" include Fits #Create an image HDU whose size is 100x100, and has type of DOUBLE_T puts Fits::DOUBLE_T imageHDU=FitsImageHDU.new imageHDU.constructImage(100,100,0,Fits::DOUBLE_T) puts imageHDU.getHDUName imageHDU.setHDUName("SampleImage") puts imageHDU puts "Image type = #{imageHDU.getTypeAsString()}" puts "Image size = #{imageHDU.getXSize} x #{imageHDU.getYSize}" puts "Setting a value to (50,50)" imageHDU.setDouble(3.14159,50,50) puts " (50,50) = #{imageHDU.getValue(50,50)}" f=FitsFile.new f.appendHDU(imageHDU) f.saveAs("newImage.fits") puts "Created image was saved to newImage.fits"
Since August 2014, RubyFits provides easy-to-use interface for filling and reading variable-length-array (VLA) columns. VLA columns can be created by specifying, as the column data type, e.g. "PI" (int16_t VLA), "PJ" (int32_t VLA), "PE" (float VLA), "PD" (double VLA), and so on.
See examples/createNewVLATable.rb or examples/createNewTableFromTemplate.rb for detailed usage of VLA access methods.
Once a VLA column is created in a Binary Table HDU, the column can be filled using the same ways as those for fixed-length columns, e.g.
# fill VLA column hdu["VLA_INT_COLUMN"][0]=[1,2,3] hdu["VLA_DOUBLE_COLUMN"][0]=[1.0,2.0,3.0] hdu["VLA_INT_COLUMN"][1]=[4,5,6,7,8] hdu["VLA_DOUBLE_COLUMN"][1]=[4.0,5.0,6.0,7.0,8.0]
Data type will be automatically determined based on the column type, and a Ruby-style Array will be converted to a C-style array (or C++ std::vector actually) internally, and written to the heap area of a Table HDU. When writing to a PE (VLA floating number column) or PD (VLA double-precision floating number column), create an array of Float by specifying floating-point numbers explicitly.
The following will result a conversion error.
# will result an error hdu["VLA_DOUBLE_COLUMN"][0]=[1,2,3] #Array of Fixnum cannot be written to a VLA double column
One important note is that, when filling VLA columns using RubyFits, users should fill VLA columns in a sequential way, and the row index should only incremented (avoiding changing row index back and forth or randomly). All the VLA columns in an HDU shares the single heap area, and each row of one VLA column holds a data length and a pointer to the heap area where actual data for the row are stored, and therefore, out-of-order write access to rows of the VLA columns may cause corruption of the data in the heap.
The size of the heap area and the heap byte position pointer are internally managed so that users can sequentially call the assign method (column[rowIndex]=array) to fill the VLA columns.
Data contained in the VLA columns can be easily read via the same methods as those used for fixed-length columns. Data will be returned as Ruby Array, and therefore, it is also very straightforward for users to retrieve the length of the data (i.e. via Array.length).
puts hdu["VLA_INT_COLUMN"][0] # => displays e.g. [1,2,3]
sfitsio accepts cfitsio-style template file to generated a new FitsFile instance, and so does RubyFits. FitsFile.constructFromTemplateString(str) returns a new FitsFile instance by constructing HDUs and columns based on a template specified as "str".
The following is the same code as contained in examples/createNewTableFromTemplate.rb, and shows how to construct a Binary Table HDU using a template string.
For template syntax, see CFITSIO Users Guide "Template Files" section.
require "RubyFits" include Fits templateString=<<EOS XTENSION=BINTABLE EXTNAME=TEST TFORM#=I TTYPE#=NUMBER TFORM#=PD TTYPE#=DOUBLE_ARRAY COMMENT This is a sample FITS file generated by createNewTableFromTemplate.rb. COMMENT The first column is of 16-bit signed integer type, and the second one COMMENT is of variable-length double-precision floating-point number. EOS fitsFile=FitsFile.constructFromTemplateString(templateString) hdu=fitsFile[1] array=[] nRows=100 hdu.resize(nRows) for i in 0...nRows hdu["NUMBER"][i]=i array << Math.sqrt(i) hdu["DOUBLE_ARRAY"][i]=array end fitsFile.saveToFile("tableCreatedFromTemplate.fits")
Comments, suggestions, and questions are welcome. Please send them to yuasa aaa astro.isas.jaxa.jp.
If you are interested in SpaceWire, see my another project "The open-source SpaceWire project" and "SpaceWire RMAP Library.