All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nom.tam.fits.header.Stokes Maven / Gradle / Ivy

package nom.tam.fits.header;

/*-
 * #%L
 * nom.tam.fits
 * %%
 * Copyright (C) 1996 - 2024 nom-tam-fits
 * %%
 * This is free and unencumbered software released into the public domain.
 * 
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 * 
 * In jurisdictions that recognize copyright laws, the author or authors
 * of this software dedicate any and all copyright interest in the
 * software to the public domain. We make this dedication for the benefit
 * of the public at large and to the detriment of our heirs and
 * successors. We intend this dedication to be an overt act of
 * relinquishment in perpetuity of all present and future rights to this
 * software under copyright law.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 * #L%
 */

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Map;
import java.util.StringTokenizer;

import nom.tam.fits.FitsException;
import nom.tam.fits.Header;

/**
 * 

* A mapping of image coordinate values for a coordinate axis with {@link WCS#CTYPEna} = 'STOKES' (or * equivalent), specifying polarization (or cross-polarization) data products along the image direction. The FITS * standard (4.0) defines a mapping of pixel coordinate values along an image imension to Stokes parameters, and this * enum provides an implementation of that for this library. *

*

* An dataset may typically contain 4 or 8 Stokes parameters (or fewer), which depending on the type of measurement can * be (I, Q, U, [V]), or (RR, LL, RL, LR) and/or (XX, YY, XY, YX). As such, the corresponding {@link WCS#CRPIXna} is * typically 0 and {@link WCS#CDELTna} is +/- 1, and depending on the type of measurement {@link WCS#CRVALna} is 1, or * -1, or -5. You can use the {@link Parameters} subclass to help populate or interpret Stokes parameters in headers. *

* * @author Attila Kovacs * * @since 1.20 * * @see WCS * @see #parameters() */ public enum Stokes { /** Stokes I: total (polarized + unpolarized) power */ I(1), /** Stokes Q: linear polarization Q component */ Q(2), /** Stokes U: linear polarization U component */ U(3), /** Stokes V: circularly polarization */ V(4), /** circular cross-polarization between two right-handed wave components */ RR(-1), /** circular cross-polarization between two left-handed wave components */ LL(-2), /** circular cross-polarization between a right-handled (input 1) and a left-handed (input 2) wave component */ RL(-3), /** circular cross-polarization between a left-handled (input 1) and a right-handed (input 2) wave component */ LR(-4), /** linear cross-polarization between two 'horizontal' wave components (in local orientation) */ XX(-5), /** linear cross-polarization between two 'vertical' wave components (in local orientation) */ YY(-6), /** * linear cross-polarization between a 'horizontal' (input 1) and a 'vertical' (input 2) wave component (in local * orientation) */ XY(-7), /** * linear cross-polarization between a 'vertical' (input 1) and a 'horizontal' (input 2) wave component (in local * orientation) */ YX(-8); /** The value to use for CTYPE type keywords to indicate Stokes parameter data */ public static final String CTYPE = "STOKES"; private int index; private static Stokes[] ordered = {YX, XY, YY, XX, LR, RL, LL, RR, null, I, Q, U, V}; private static final int STANDARD_PARAMETER_COUNT = 4; private static final int FULL_PARAMETER_COUNT = 8; Stokes(int value) { this.index = value; } /** * Returns the WCS coordinate value corresponding to this Stokes parameter for an image coordinate with * {@link WCS#CTYPEna} = 'STOKES'. * * @return the WCS coordinate value corresponding to this Stokes parameter. * * @see #forCoordinateValue(int) * @see WCS#CTYPEna * @see WCS#CRVALna */ public final int getCoordinateValue() { return index; } /** * Returns the Stokes parameter for the given pixel coordinate value for an image coordinate with * {@link WCS#CTYPEna} = 'STOKES'. * * @param value The image coordinate value * * @return The Stokes parameter, which corresponds to that coordinate value. * * @throws IndexOutOfBoundsException if the coordinate value is outt of the range of acceptable Stokes coordinate * values. * * @see #getCoordinateValue() */ public static Stokes forCoordinateValue(int value) throws IndexOutOfBoundsException { return ordered[value - YX.getCoordinateValue()]; } /** * Helper class for setting or interpreting a set of measured Stokes parameters stored along an array dimension. Two * instances of Stokes parameters are considered equal if they measure the same polarization terms, in the same * order. * * @author Attila Kovacs * * @since 1.20 */ public static final class Parameters { private int flags; private int offset; private int step; private int count; private Parameters(int flags) { this.flags = flags; boolean reversed = (flags & REVERSED_ORDER) != 0; step = reversed ? -1 : 1; count = STANDARD_PARAMETER_COUNT; if ((flags & FULL_CROSS_POLARIZATION) == 0) { offset = reversed ? Stokes.V.getCoordinateValue() : Stokes.I.getCoordinateValue(); } else { step = -step; if ((flags & CIRCULAR_CROSS_POLARIZATION) == 0) { offset = reversed ? Stokes.YX.getCoordinateValue() : Stokes.XX.getCoordinateValue(); } else if ((flags & LINEAR_CROSS_POLARIZATION) == 0) { offset = reversed ? Stokes.LR.getCoordinateValue() : Stokes.RR.getCoordinateValue(); } else { offset = reversed ? Stokes.YX.getCoordinateValue() : Stokes.RR.getCoordinateValue(); count = FULL_PARAMETER_COUNT; } } } private Parameters(int offset, int step, int n) { this.offset = offset; this.step = step; this.count = n; if (offset < 0) { int end = offset + (n - 1) * step; if (Math.min(offset, end) <= XX.index) { flags |= LINEAR_CROSS_POLARIZATION; } if (Math.max(offset, end) > XX.index) { flags |= CIRCULAR_CROSS_POLARIZATION; } step = -step; } if (step < 0) { flags |= REVERSED_ORDER; } } @Override public int hashCode() { return flags ^ Integer.hashCode(offset) ^ Integer.hashCode(step); } @Override public boolean equals(Object o) { if (!(o instanceof Parameters)) { return false; } Parameters p = (Parameters) o; if (p.flags != flags) { return false; } if (p.offset != offset) { return false; } if (p.step != step) { return false; } return true; } boolean isReversedOrder() { return (flags & REVERSED_ORDER) != 0; } /** * Checks if the parameters are for measuring cross-polarization between two inputs. * * @return true if it is for cross polarization, otherwise false. */ public boolean isCrossPolarization() { return (flags & FULL_CROSS_POLARIZATION) != 0; } /** * Checks if the parameters include linear polarization terms. * * @return true if linear polarization is measured, otherwise false. */ public boolean hasLinearPolarization() { return (flags & FULL_CROSS_POLARIZATION) != CIRCULAR_CROSS_POLARIZATION; } /** * Checks if the parameters include circular polarization term(s). * * @return true if cirular cross polarization is measured, otherwise false. */ public boolean hasCircularPolarization() { return (flags & FULL_CROSS_POLARIZATION) != LINEAR_CROSS_POLARIZATION; } /** * Returns the Stokes parameter for a given Java array index for a dimension that corresponds to the Stokes * parameters described by this instance. * * @param idx the zero-based Java array index, typically [0:3] for single-ended * polarization or circular or linear-only cross-polarization, or else * [0:7] for full cross-polarization. * * @return The specific Stokes parameter corresponding to the specified array index. * * @throws IndexOutOfBoundsException if the index is outside of the expected range. * * @see #getAvailableParameters() * * @since 1.19.1 * * @author Attila Kovacs */ public Stokes getParameter(int idx) throws IndexOutOfBoundsException { if (idx < 0 || idx >= count) { throw new IndexOutOfBoundsException(); } return Stokes.forCoordinateValue(offset + step * idx); } /** * Returns the ordered list of parameters, which can be used to translate array indexes to Stokes values, * supported by this parameter set. * * @return the ordered list of available Stokes parameters in this measurement set. * * @see #getParameter(int) */ public ArrayList getAvailableParameters() { ArrayList list = new ArrayList<>(count); for (int i = 0; i < count; i++) { list.add(getParameter(i)); } return list; } /** * Returns the Java array index corresponding to a given Stokes parameters for this set of parameters. * * @param s the Stokes parameter of interest * * @return the zero-based Java array index corresponding to the given Stokes parameter. * * @see #getParameter(int) * * @since 1.20 * * @author Attila Kovacs */ public int getArrayIndex(Stokes s) { return (s.getCoordinateValue() - offset) / step; } /** * Adds WCS description for the coordinate axis containing Stokes parameters. The header must already contain a * NAXIS keyword specifying the dimensionality of the data, or else a FitsException will be thrown. * * @param header the FITS header to populate (it must already have an NAXIS keyword * present). * @param coordinateIndex The 0-based Java coordinate index for the array dimension that corresponds * to the stokes parameter. * * @throws IndexOutOfBoundsException if the coordinate index is negative or out of bounds for the array * dimensions * @throws FitsException if the header does not contain an NAXIS keyword, or if the header is not * accessible * * @see #fillTableHeader(Header, int, int) * @see Stokes#fromImageHeader(Header) * * @since 1.20 * * @author Attila Kovacs */ public void fillImageHeader(Header header, int coordinateIndex) throws FitsException { int n = header.getIntValue(Standard.NAXIS); if (n == 0) { throw new FitsException("Missing NAXIS in header"); } if (coordinateIndex < 0 || coordinateIndex >= n) { throw new IndexOutOfBoundsException( "Invalid Java coordinate index " + coordinateIndex + " (for " + n + " dimensions)"); } int i = n - coordinateIndex; header.addValue(WCS.CTYPEna.n(i), Stokes.CTYPE); header.addValue(WCS.CRPIXna.n(i), 1); header.addValue(WCS.CRVALna.n(i), offset); header.addValue(WCS.CDELTna.n(i), step); } /** * Adds WCS description for the coordinate axis containing Stokes parameters to a table column containign * images. * * @param header the binary table header to populate (it should already contain a TDIMn * keyword for the specified column, or else 1D data is assumed). * @param column the zero-based Java column index containing the 'image' array. * @param coordinateIndex the zero-based Java coordinate index for the array dimension that * corresponds to the stokes parameter. * * @throws IndexOutOfBoundsException if the coordinate index is negative or out of bounds for the array * dimensions, or if the column index is invalid. * @throws FitsException if the header does not specify the dimensionality of the array elements, or * if the header is not accessible * * @see #fillImageHeader(Header, int) * @see Stokes#fromTableHeader(Header, int) * * @since 1.20 * * @author Attila Kovacs */ public void fillTableHeader(Header header, int column, int coordinateIndex) throws IndexOutOfBoundsException, FitsException { if (column < 0) { throw new IndexOutOfBoundsException("Invalid Java column index " + column); } String dims = header.getStringValue(Standard.TDIMn.n(++column)); if (dims == null) { throw new FitsException("Missing TDIM" + column + " in header"); } StringTokenizer tokens = new StringTokenizer(dims, "(, )"); int n = tokens.countTokens(); if (coordinateIndex < 0 || coordinateIndex >= n) { throw new IndexOutOfBoundsException( "Invalid Java coordinate index " + coordinateIndex + " (for " + n + " dimensions)"); } int i = n - coordinateIndex; header.addValue(WCS.nCTYPn.n(i, column), Stokes.CTYPE); header.addValue(WCS.nCRPXn.n(i, column), 1); header.addValue(WCS.nCRVLn.n(i, column), offset); header.addValue(WCS.nCDLTn.n(i, column), step); } } /** * Returns a new set of standard single-input Stokes parameters (I, Q, U, V). * * @return the standard set of I, Q, U, V Stokes parameters. * * @see #parameters(int) */ public static Parameters parameters() { return parameters(0); } /** * Returns the set of Stokes parameters for the given bitwise flags, which may specify linear or cicular cross * polarization, or both, and/or if the parameters are stored in reversed index order in the FITS. The flags can be * bitwise OR'd, e.g. {@link #LINEAR_CROSS_POLARIZATION} | {@link #CIRCULAR_CROSS_POLARIZATION} will select Stokes * parameters for measuring circular cross polarization, stored in reversed index order that is: (LR, RL, LL, RR). * * @param flags the bitwise flags specifying the type of Stokes parameters. * * @return the set of Stokes parameters for the given bitwise flags. * * @see #parameters() * @see #LINEAR_CROSS_POLARIZATION * @see #CIRCULAR_CROSS_POLARIZATION * @see #FULL_CROSS_POLARIZATION */ public static Parameters parameters(int flags) { return new Parameters(flags); } /** * Bitwise flag for Stokes parameters stored in reversed index order. */ static final int REVERSED_ORDER = 1; /** * Bitwise flag for dual-input linear cross polarization Stokes parameters (XX, YY, XY, YX) */ public static final int LINEAR_CROSS_POLARIZATION = 2; /** * Bitwise flag for dual-input circular cross polarization Stokes parameters (RR, LL, RL, LR) */ public static final int CIRCULAR_CROSS_POLARIZATION = 4; /** * Bitwise flag for dual-input full (linear + circular) cross polarization Stokes parameters (RR, LL, RL, LR, XX, * YY, XY, YX). By definition tme as ({@link #CIRCULAR_CROSS_POLARIZATION} | {@link #LINEAR_CROSS_POLARIZATION}). * * @see #CIRCULAR_CROSS_POLARIZATION * @see #LINEAR_CROSS_POLARIZATION */ public static final int FULL_CROSS_POLARIZATION = LINEAR_CROSS_POLARIZATION | CIRCULAR_CROSS_POLARIZATION; private static Parameters forCoords(double start, double delt, int count) throws FitsException { int offset = (int) start; if (start != offset) { throw new FitsException("Invalid (non-integer) Stokes coordinate start: " + start); } int step = (int) delt; if (delt != step) { throw new FitsException("Invalid (non-integer) Stokes coordinate step: " + delt); } int end = offset + step * (count - 1); if (Math.min(offset, end) <= 0 && Math.max(offset, end) >= 0) { throw new FitsException("Invalid Stokes coordinate range: " + offset + ":" + end); } return new Parameters(offset, step, count); } /** * Returns a mapping of a Java array dimension to a set of Stokes parameters, based on the WCS coordinate * description in the image header. The header must already contain a NAXIS keyword specifying the dimensionality of * the data, or else a FitsException will be thrown. * * @param header the FITS header to populate (it must already have an NAXIS keyword present). * * @return A mapping from a zero-based Java array dimension which corresponds to the Stokes dimension * of the data, to the set of stokes Parameters defined in that dimension; or * null if the header does not contain a fully valid description of a Stokes * coordinate axis. * * @throws FitsException if the header does not contain an NAXIS keyword, necessary for translating Java array * indices to FITS array indices, or if the CRVALn, CRPIXna or CDELTna values for the * 'STOKES' dimension are inconsistent with a Stokes coordinate definition. * * @see #fromTableHeader(Header, int) * @see Parameters#fillImageHeader(Header, int) * * @since 1.20 * * @author Attila Kovacs */ @SuppressWarnings({"unchecked", "rawtypes"}) public static Map.Entry fromImageHeader(Header header) throws FitsException { int n = header.getIntValue(Standard.NAXIS); if (n <= 0) { throw new FitsException("Missing, invalid, or insufficient NAXIS in header"); } for (int i = 1; i <= n; i++) { if (Stokes.CTYPE.equalsIgnoreCase(header.getStringValue(WCS.CTYPEna.n(i)))) { if (header.getDoubleValue(WCS.CRPIXna.n(i), 1.0) != 1.0) { throw new FitsException("Invalid Stokes " + WCS.CRPIXna.n(i).key() + " value: " + header.getDoubleValue(WCS.CRPIXna.n(i)) + ", expected 1"); } Parameters p = forCoords(header.getDoubleValue(WCS.CRVALna.n(i), 0.0), header.getDoubleValue(WCS.CDELTna.n(i), 1.0), header.getIntValue(Standard.NAXISn.n(i), 1)); return new AbstractMap.SimpleImmutableEntry(n - i, p); } } return null; } /** * Returns a mapping of a Java array dimension to a set of Stokes parameters, based on the WCS coordinate * description in the image header. * * @param header the FITS header to populate. * @param column the zero-based Java column index containing the 'image' array. * * @return A mapping from a zero-based Java array dimension which corresponds to the * Stokes dimension of the data, to the set of stokes Parameters defined in * that dimension; or null if the header does not contain a fully * valid description of a Stokes coordinate axis. * * @throws IndexOutOfBoundsException if the column index is invalid. * @throws FitsException if the header does not contain an TDIMn keyword for the column, necessary for * translating Java array indices to FITS array indices, or if the iCRVLn, * iCRPXn or iCDLTn values for the 'STOKES' dimension are inconsistent with a * Stokes coordinate definition. * * @see #fromImageHeader(Header) * @see Parameters#fillTableHeader(Header, int, int) * * @since 1.20 * * @author Attila Kovacs */ @SuppressWarnings({"unchecked", "rawtypes"}) public static Map.Entry fromTableHeader(Header header, int column) throws IndexOutOfBoundsException, FitsException { if (column < 0) { throw new IndexOutOfBoundsException("Invalid Java column index " + column); } String dims = header.getStringValue(Standard.TDIMn.n(++column)); if (dims == null) { throw new FitsException("Missing TDIM" + column + " in header"); } StringTokenizer tokens = new StringTokenizer(dims, "(, )"); int n = tokens.countTokens(); for (int i = 1; i <= n; i++) { String d = tokens.nextToken(); if (Stokes.CTYPE.equalsIgnoreCase(header.getStringValue(WCS.nCTYPn.n(i, column)))) { if (header.getDoubleValue(WCS.nCRPXn.n(i, column), 1.0) != 1.0) { throw new FitsException("Invalid Stokes " + WCS.nCRPXn.n(i, column).key() + " value: " + header.getDoubleValue(WCS.nCRPXn.n(i, column)) + ", expected 1"); } Parameters p = forCoords(header.getDoubleValue(WCS.nCRVLn.n(i, column), 0.0), header.getDoubleValue(WCS.nCDLTn.n(i, column), 1.0), Integer.parseInt(d)); return new AbstractMap.SimpleImmutableEntry(n - i, p); } } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy