Description
Astronomical Observations (FITS, ASDF, WCS, etc)
Description
Work with astronomical observations from modern telescopes
README.md
Telescope
Haskell library to work with scientific data files commonly used in astronomy
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]
Getting Raw Telescope Data
- NSO DKIST Solar Telescope
- MAST - Hubble and JWST
Attributions

FITS code was developed in collaboration with @krakrjak.