/** @file  psAstrometry.h
*
*  @brief This file defines the basic types for astronomical coordinate
*  transformation
*
*  @ingroup AstroImage
*
*  @author GLG, MHPCC
*
*  @version $Revision: 1.33 $ $Name: not supported by cvs2svn $
*  @date $Date: 2004-12-30 20:18:36 $
*
*  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
*/

#ifndef PS_ASTROMETRY_H
#define PS_ASTROMETRY_H

#include "psType.h"
#include "psImage.h"
#include "psArray.h"
#include "psList.h"
#include "psFunctions.h"
#include "psMetadata.h"
#include "psCoord.h"
#include "psPhotometry.h"

struct psCell;
struct psChip;
struct psFPA;
struct psExposure;

/// @addtogroup AstroImage
/// @{

/** Wallace's Grommit
 *
 *  SLALib requires several elements to perform the transformations between
 *  the tangent plane and the sky.  Pre-computing these quantities for each
 *  exposure means that subsequent transformations are faster.  For historical
 *  reasons, this structure is known colloquially as "Wallace's Grommit".
 *
 */
typedef struct
{
    const double latitude;             ///< geodetic latitude (radians)
    const double sinLat;               ///< sine of geodetic latitude
    const double cosLat;               ///< cosine of geodetic latitude
    const double abberationMag;        ///< magnitude of diurnal aberration vector
    const double height;               ///< height (HM)
    const double temperature;          ///< ambient temperature (TDK)
    const double pressure;             ///< pressure (PMB)
    const double humidity;             ///< relative humidity (RH)
    const double wavelength;           ///< wavelength (WL)
    const double lapseRate;            ///< lapse rate (TLR)
    const double refractA;             ///< refraction constant A (radians)
    const double refractB;             ///< refraction constant B (radians)
    const double longitudeOffset;      ///< longitude + ... (radians)
    const double siderealTime;         ///< local apparent sidereal time (radians)
}
psGrommit;

/** Fixed Pattern Corrections
 *
 *  The fixed pattern is a correction to the general astrometric solution
 *  formed by summing the residuals from many observations. The intent is to
 *  correct for higher-order distortions in the camera system on a coarse
 *  grid (larger than individual pixels, but smaller than a single cell).
 *  Hence, in addition to the offsets, we need to specify the size and scale
 *  of the grid in x and y as well as the origin of the grid.
 */
typedef struct
{
    psS32 nX;                            ///< Number of elements in x direction
    psS32 nY;                            ///< Number of elements in y direction
    double x0;                         ///< X Position of 0,0 corner on focal plane
    double y0;                         ///< Y Position of 0,0 corner on focal plane
    double xScale;                     ///< Scale of the grid in x direction
    double yScale;                     ///< Scale of the grid in x direction
    /// XXX: I added the following memvers to facilitate the psFreeing of the x,y data structures.
    psS32 p_ps_xRows;                    ///< Number of rows in the x member
    psS32 p_ps_xCols;                    ///< Number of cols in the x member
    psS32 p_ps_yRows;                    ///< Number of rows in the y member
    psS32 p_ps_yCols;                    ///< Number of cols in the y member
    double **x;                        ///< The grid of offsets in x
    double **y;                        ///< The grid of offsets in y
}
psFixedPattern;

/** Readout data structure.
 *
 *  A readout is the result of a single read of a cell (or a portion thereof).
 *  It contains a pointer to the pixel data, and additional pointers to the
 *  objects found in the readout, and the readout metadata.  It also contains
 *  the offset from the lower-left corner of the chip, in the case that the
 *  CCD was windowed.
 *
 */
typedef struct
{
    const psS32 col0;                  ///< Offset from the left of chip.
    const psS32 row0;                  ///< Offset from the bottom of chip.

    const int colParity;               ///< Column Readout Direction
    const int rowParity;               ///< Row Readout Direction

    const psU32 colBins;               ///< Amount of binning in x-dimension
    const psU32 rowBins;               ///< Amount of binning in y-dimension

    psImage* image;                    ///< Imaging area of readout
    psImage* mask;                     ///< Mask area for readout
    psList* objects;                   ///< Objects derived from Readout
    psMetadata* metadata;              ///< Readout-level metadata
}
psReadout;

/** Cell data structure
 *
 *  A cell consists of one or more readouts.  It also contains a pointer to the
 *  cell's metadata, and its parent chip.  On the astrometry side, it also
 *  contains coordinate transforms from the cell to chip, from the cell to
 *  focal-plane, as well as a "quick and dirty" tranform from the cell to
 *  sky coordinates.
 *
 */
typedef struct
{
    const psS32 col0;                  ///< Offset from the left of chip
    const psS32 row0;                  ///< Offset from the bottom of chip

    psArray* readouts;                 ///< readouts from the cell

    psMetadata* metadata;              ///< cell-level metadata

    psPlaneTransform* toChip;          ///< transformations from cell to chip coordinates
    psPlaneTransform* fromChip;        ///< transformations from cell to chip coordinates
    psPlaneTransform* toFPA;           ///< transformations from cell to FPA coordinates
    psPlaneTransform* toTP;            ///< transformations from cell to FPA coordinates
    psPlaneTransform* toSky;           ///< transformations from cell to tangent plane coordinates

    struct psChip* parent;             ///< chip in which contains this cell
}
psCell;

/** Chip data structure
 *
 *  A chip consists of one or more cells (according to the number of amplifiers
 *  on the CCD). It contains a pointer to the chip's metadata, and a pointer
 *  to the parent focal plane.  For astrometry, it contains a coordinate
 *  transform from the chip to the focal plane, and vis-versa.
 *
 */
typedef struct psChip
{
    const psS32 col0;                  ///< Offset from the left of FPA
    const psS32 row0;                  ///< Offset from the bottom of FPA

    psArray* cells;                    ///< cells in the chip

    psMetadata* metadata;              ///< chip-level metadata

    psPlaneTransform* toFPA;           ///< transformation from chip to FPA coordinates
    psPlaneTransform* fromFPA;         ///< transformation from FPA to chip coordinates

    struct psFPA* parent;              ///< FPA which contains this chip
}
psChip;

/** A Focal-Plane
 *
 *  A focal plane consists of one or more chips (according to the number of
 *  contiguous silicon).  It contains pointers to the focal-plane's metadata
 *  and the exposure information.  For astrometry, it contains a transformation
 *  from the focal plane to the tangent plane and the fixed pattern residuals.
 *  Since colors are involved in the transformation, it is necessary to specify
 *  the color the transformation is defined.  We also include some values to
 *  characterize the quality of the transformation: the root square deviation
 *  for the x and y transformation fits, and the chi-squared for the
 *  transformation fit.
 *
 */
typedef struct psFPA
{
    psArray* chips;                    ///< chips in the focal plane array
    psMetadata* metadata;              ///< focal-plane's metadata

    psPlaneDistort* fromTangentPlane;  ///< transformation from tangent plane to focal plane
    psPlaneDistort* toTangentPlane;    ///< transformation from focal plane to tangent plane
    psFixedPattern* pattern;           ///< fixed pattern residual offsets

    const struct psExposure* exposure; ///< information about this exposure
    psGrommit *grommit;                ///< Wallace's grommit

    psPhotSystem* colorPlus;           ///< Color reference
    psPhotSystem* colorMinus;          ///< Color reference
    psProjection *projection;          ///< projection

    float rmsX;                        ///< RMS for x transformation fits
    float rmsY;                        ///< RMS for y transformation fits
    float chi2;                        ///< chi^2 of astrometric solution
}
psFPA;

/** Observatory Information
 *
 *  A container for the observatory data that doesn't change per exposure.
 *
 */
typedef struct
{
    const char* name;                  ///< Name of observatory
    const double latitude;             ///< Latitude of observatory, east positive (degrees?)
    const double longitude;            ///< Longitude of observatory (degrees?)
    const double height;               ///< Height of observatory in meters
    const double tlr;                  ///< Tropospheric Lapse Rate
}
psObservatory;

/** Exposure Information
 *
 *  Several quantities from the telescope in order to make a first guess at
 *  the astrometric solution.  From these quantities, further quantities can
 *  be derivedand stored for later use.
 *
 */
typedef struct psExposure
{
    const double ra;                   ///< Telescope boresight, right ascention
    const double dec;                  ///< Telescope boresight, declination
    const double hourAngle;            ///< Hour angle
    const double zenithDistance;       ///< Zenith distance
    const double azimuth;              ///< Azimuth
    const psTime* time;                ///< Time of observation
    const float rotAngle;              ///< Rotator position angle in degrees? XXX: see bug#209
    const float temperature;           ///< Air temperature in Kelvin
    const float pressure;              ///< Air pressure in mB
    const float humidity;              ///< Relative humidity, for refraction
    const float exposureTime;          ///< Exposure time
    const float wavelength;            ///< Wavelength in microns
    const psObservatory* observatory;  ///< Observatory data

    /* Derived quantities */
    const double lst;                  ///< Local Sidereal Time
    const float positionAngle;         ///< Position angle
    const float parallacticAngle;      ///< Parallactic angle
    const float airmass;               ///< Airmass, calculated from zenith distance
    const float parallacticFactor;     ///< Parallactic factor
    const char* cameraName;            ///< name of camera which provided exposure
    const char* telescopeName;         ///< name of telescope which provided exposure
}
psExposure;

/** Allocator for psFixedPattern struct
 *
 *  Allocates a new psFixedPattern struct with the attributes coorsponding
 *  to the parameters set to the said input values.
 *
 *  @return psFixedPattern*     New psFixedPattern struct.
 */
psFixedPattern* psFixedPatternAlloc(
    double x0,           ///< X Position of 0,0 corner on focal plane
    double y0,           ///< Y Position of 0,0 corner on focal plane
    double xScale,       ///< Scale of the grid in x direction
    double yScale,       ///< Scale of the grid in x direction
    const psImage *x,    ///< The grid of offsets in x
    const psImage *y     ///< The grid of offsets in y
);


/** Allocator for psExposure
 *
 *  We need several quantities from the telescope in order to make a first
 *  guess at the astrometric solution. From these quantities, further
 *  quantities can be derived and stored for later use.
 *
 *  @return     psExposure*    New psExposure struct
 */
psExposure* psExposureAlloc(
    double ra,                         ///< Telescope boresight, right ascention
    double dec,                        ///< Telescope boresight, declination
    double hourAngle,                  ///< Hour angle
    double zenithDistance,             ///< Zenith distance
    double azimuth,                    ///< Azimuth
    const psTime* time,                ///< time of observation
    float rotAngle,                    ///< Rotator position angle
    float temperature,                 ///< Temperature
    float pressure,                    ///< Pressure
    float humidity,                    ///< Relative humidity
    float exposureTime,                ///< Exposure time
    float wavelength,                  ///< wavelength
    const psObservatory* observatory   ///< Observatory data
);

/** Allocator for psObservatory
 *
 *  This function shall construct a new psObservatory with attributes
 *  cooresponding to the function parameters.
 *
 *  @return psObservatory*    new psObservatory struct
 */
psObservatory* psObservatoryAlloc(
    const char* name,                  ///< Name of observatory
    double latitude,                   ///< Latitude of observatory, east positive
    double longitude,                  ///< Longitude of observatory
    double height,                     ///< Height of observatory
    double tlr                         ///< Tropospheric Lapse Rate
);

/** Allocator for psFPA
 *
 *  This function shall make an empty psFPA, with the nChips allocated
 *  pointers to psChips being set to NULL; all other pointers in the structure
 *  shall be initialized to NULL, apart from the grommit, which shall be
 *  constructed on the basis of the exp parameter.
 *
 *  @return psFPA*    a newly allocated psFPA
 */
psFPA* psFPAAlloc(
    psS32 nChips,                        ///< number of chips in the FPA
    const psExposure* exp              ///< the exposure information
);

/** Allocates a psChip
 *
 *  This allocator shall make an empty psChip, with the nCells allocated
 *  pointers to psCells being set to NULL; all other pointers in the structure
 *  shall be initialized to NULL.
 *
 *  @return psChip*    newly allocated psChip
 */
psChip* psChipAlloc(
    psS32 nCells,                        ///< number of cells in Chip
    psFPA* parentFPA                   ///< parent FPA
);

/** Allocates a psCell
 *
 *  The constructor shall make an empty psCell, with the nReadouts allocated
 *  pointers to psReadouts being set to NULL; all other pointers in the
 *  structure shall be initialized to NULL.
 *
 *  @return psCell*    newly allocated psCell
 */
psCell* psCellAlloc(
    psS32 nReadouts,                     ///< number of readouts in cell
    psChip* parentChip                 ///< parent Chip
);

/** Allocates a psReadout
 *
 *  All pointers in the structure other than the image shall be initialized
 *  to NULL.
 *
 *  @return psReadout*    newly allocated psReadout with all internal pointers set to NULL
 */
psReadout* psReadoutAlloc();

/** Allocates a Wallace's Grommit structure.
 *
 *  The psGrommit is calculated from telescope information for the particular
 *  exposure.
 *
 *  @return psGrommit* New grommit structure.
 */
psGrommit* psGrommitAlloc(
    const psExposure* exp              ///< the cooresponding exposure structure.
);

/** Find cooresponding cell for given FPA coordinate
 *
 *  @return psCell*    the cell cooresponding to the coord in FPA
 */
psCell* psCellInFPA(
    const psPlane* coord,              ///< the coordinate in FPA plane
    const psFPA* FPA                   ///< the FPA to search for the cell
);

/** Find cooresponding chip for given FPA coordinate
 *
 *  @return psChip*    the chip cooresponding to coord
 */
psChip* psChipInFPA(
    const psPlane* coord,              ///< the coordinate in FPA plane
    const psFPA* FPA                   ///< the FPA to search for the cell
);

/** Find cooresponding cell for given Chip coordinate
 *
 *  @return psCell*    the cell cooresponding to coord
 */
psCell* psCellInChip(
    const psPlane* coord,              ///< the coordinate in Chip plane
    const psChip* chip                 ///< the chip to search for the cell
);

/** Translate a cell coordinate into a chip coordinate
 *
 *  @return psPlane*    the resulting chip coordinate
 */
psPlane* psCoordCellToChip(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the coordinate within Cell
    const psCell* cell                 ///< the Cell in interest
);

/** Translate a chip coordinate into a FPA coordinate
 *
 *  @return psPlane*    the resulting FPA coordinate
 */
psPlane* psCoordChipToFPA(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the coordinate within Chip
    const psChip* chip                 ///< the chip in interest
);

/** Translate a FPA coordinate into a Tangent Plane coordinate
 *
 *  @return psPlane*    the resulting Tangent Plane coordinate
 */
psPlane* psCoordFPAToTP(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the coordinate within FPA
    double color,                      ///< Color of source
    double magnitude,                  ///< Magnitude of source
    const psFPA* fpa                   ///< the FPA in interest
);

/** Translate a Tangent Plane coordinate into a Sky coordinate
 *
 *  @return psSphere*    the resulting Sky coordinate
 */
psSphere* psCoordTPToSky(
    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the coordinate within Tangent Plane
    const psGrommit* grommit           ///< the grommit of the tangent plane
);

/** Translate a cell coordinate into a FPA coordinate
 *
 *  @return psPlane*    the resulting FPA coordinate
 */
psPlane* psCoordCellToFPA(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the coordinate within cell
    const psCell* cell                 ///< the cell in interest
);

/** Translate a cell coordinate into a Sky coordinate
 *
 *  @return psSphere*    the resulting Sky coordinate
 */
psSphere* psCoordCellToSky(
    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the coordinate within cell
    double color,                      ///< Color of source
    double magnitude,                  ///< Magnitude of source
    const psCell* cell                 ///< the cell in interest
);

/** Translate a cell coordinate into a Sky coordinate using a 'quick and
 *  dirty' method
 *
 *  @return psSphere*    the resulting Sky coordinate
 */
psSphere* psCoordCellToSkyQuick(
    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the coordinate within cell
    const psCell* cell                 ///< the cell in interest
);

/** Translate a Sky coordinate into a Tangent Plane coordinate
 *
 *  @return psPlane*    the resulting Tangent Plane coordinate
 */
psPlane* psCoordSkyToTP(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psSphere* in,                ///< the sky coordinate
    const psGrommit* grommit           ///< the grommit
);

/** Translate a Tangent Plane coordinate into a FPA coordinate
 *
 *  @return psPlane*    the resulting FPA coordinate
 */
psPlane* psCoordTPToFPA(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the coordinate within tangent plane
    double color,                      ///< Color of source
    double magnitude,                  ///< Magnitude of source
    const psFPA* fpa                   ///< the FPA of interest
);

/** Translate a FPA coordinate into a chip coordinate
 *
 *  @return psPlane*    the resulting chip coordinate
 */
psPlane* psCoordFPAToChip(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the FPA coordinate
    const psChip* chip                 ///< the chip of interest
);

/** Translate a chip coordinate into a cell coordinate
 *
 *  @return psPlane*    the resulting cell coordinate
 */
psPlane* psCoordChipToCell(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psPlane* in,                 ///< the Chip coordinate
    const psCell* cell                 ///< the cell of interest
);

/** Translate a sky coordinate into a cell coordinate
 *
 *  @return psPlane*    the resulting cell coordinate
 */
psPlane* psCoordSkyToCell(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psSphere* in,                ///< the Sky coordinate
    double color,                      ///< Color of source
    double magnitude,                  ///< Magnitude of source
    const psCell* cell                 ///< the cell of interest
);

/** Translate a sky coordinate into a cell coordinate using a 'quick and
 *  dirty' method
 *
 *  @return psPlane*    the resulting cell coordinate
 */
psPlane* psCoordSkyToCellQuick(
    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
    const psSphere* in,                ///< the Sky coordinate
    const psCell* cell                 ///< the cell of interest
);

#endif
