Description
Astronomical Observations (FITS, ASDF, WCS, etc)
Description
Work with astronomical observations from modern telescopes
README.md
Telescope
Haskell library to read and write Astronomical images from telescopes
FITS Example
import Telescope.Fits
import Data.ByteString qualified as BS
main :: IO ()
main = do
inp <- BS.readFile "hubble.fits"
fits <- decode inp
FITS files start with a primary Header Data Unit
print fits.primaryHDU
DataHDU
Header: 490 records
data: 0 bytes
dimensions:
format: Int8
axes: []
HDUs may contain both data and a header
print fits.primaryHDU.header
SIMPLE = T / conforms to FITS standard
BITPIX = 8 / array data type
NAXIS = 0 / number of array dimensions
EXTEND = T
DATE = '2009-11-10' / date this file was written (yyyy-mm-dd)
FILETYPE= 'SCI' / type of data found in data file
TELESCOP= 'HST' / telescope used to acquire data
INSTRUME= 'WFPC2' / identifier for instrument used to acquire data
EQUINOX = 2000.0 / equinox of celestial coord. system
...
The primary HDU is followed by multiple extensions in a similar format
mapM_ print fits.extensions
Image: DataHDU
Header: 103 records
data: 12960000 bytes
dimensions:
format: Float
axes: [1800,1800]
Image: DataHDU
Header: 124 records
data: 12960000 bytes
dimensions:
format: Float
axes: [1800,1800]
Image: DataHDU
Header: 123 records
data: 12960000 bytes
dimensions:
format: Int32
axes: [1800,1800]
We can use Array
from Data.Massiv to parse, manipulate, and display data
Image img : _ <- pure fits.extensions
arr <- decodeDataArray @Ix2 @Float img.dataArray
writeImage "hubble1.png" (heatmap arr)
Parsing Headers
Parse header keywords into Haskell records
import Telescope.Fits
import Effectful
data HubbleInfo = HubbleInfo
{ telescop :: Text
, instrume :: Text
, equinox :: Float
} deriving (Generic, FromHeader, ToHeader)
main = do
...
let h = fits.primaryHDU.header
print $ lookupKeyword "INSTRUME" h
case runPureEff $ runParser $ parseHeader h of
Left e -> fail $ show e
Right info -> do
print info.telescop
print info.equinox
ASDF Example
ASDF files contain a YAML tree and binary data afterwards. They are readable and the tree is editable in a text editor.
example :: IO ()
example = do
inp <- BS.readFile "../samples/dkist.asdf"
dset :: Dataset <- decodeM inp
print dset.info
data DKISTInfo = DKISTInfo
{ instrumentName :: Text
, frameCount :: Int
}
deriving (Show, Generic, FromAsdf)
data Dataset = Dataset
{ info :: DKISTInfo
}
instance FromAsdf Dataset where
-- parse .dataset.meta.inventory into DKISTInfo
parseValue val =
case val of
Object o -> do
dset <- o .: "dataset"
meta <- dset .: "meta"
info <- meta .: "inventory"
pure $ Dataset info
k -> expected "Object" k
DKISTInfo {instrumentName = "VISP", frameCount = 1960}
Data can be parsed directly from the YAML tree or from binary-encoded data blocks (an NDArray)
data Example = Example
{ foo :: Int32
, powers :: Maybe Powers
, sequence :: [Int64]
, random :: Array D Ix1 Double
}
deriving (Generic, FromAsdf)
example :: IO ()
example = do
inp <- BS.readFile "../samples/example.asdf"
ex :: Example <- decodeM inp
print ex.name
print ex.items
print $ take 30 ex.sequence
print $ take 10 $ M.toList ex.random
"Monty"
["one","two","three","four","five"]
[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29]
[0.7842310832387069,0.2279459557291822,0.9534462812074139,0.5100515929833191,0.6597920763222204,0.8778040169160855,0.8079416746447109,0.5373925949744411,0.5169365152585088,0.436101639340324]
Collaborators
- The FITS code was developed in collaboration with @krakrjak.