telescope: Astronomical Observations (FITS, ASDF, WCS, etc)

[ astronomy, bsd3, library, science ] [ Propose Tags ] [ Report a vulnerability ]
Versions [RSS] 0.2.0, 0.3.0
Dependencies base (>=4.18 && <5), binary (>=0.8.9 && <0.9), byte-order (>=0.1.3 && <0.2), bytestring (>=0.11 && <0.13), casing (>=0.1.4 && <0.2), conduit (>=1.3 && <1.4), effectful (>=2.3 && <3), exceptions (>=0.10 && <0.11), libyaml (>=0.1.4 && <0.2), massiv (>=1.0 && <1.1), massiv-io (>=1.0 && <1.1), megaparsec (>=9.6 && <9.7), resourcet-effectful (>=1.0.1 && <1.1), scientific (>=0.3 && <0.4), text (>=2.0 && <3), time (>=1.12 && <2) [details]
License BSD-3-Clause
Copyright (c) 2024 Sean Hess
Author Sean Hess
Maintainer Sean Hess <shess@nso.edu>
Category Science, Astronomy
Home page https://github.com/dkistdc/telescope.hs
Uploaded by seanhess at 2025-05-29T17:59:22Z
Distributions
Downloads 34 total (4 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for telescope-0.3.0

[back to package description]

Telescope

Hackage

Haskell library to read and write Astronomical images from telescopes

  • FITS (Flexible Image Transport System) Files
  • ASDF (Advanced Scientific Data Format) Files

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)

Hubble Output

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