View Javadoc
1   package nom.tam.fits.header;
2   
3   import java.util.logging.Logger;
4   
5   /*
6    * #%L
7    * nom.tam FITS library
8    * %%
9    * Copyright (C) 1996 - 2024 nom-tam-fits
10   * %%
11   * This is free and unencumbered software released into the public domain.
12   *
13   * Anyone is free to copy, modify, publish, use, compile, sell, or
14   * distribute this software, either in source code form or as a compiled
15   * binary, for any purpose, commercial or non-commercial, and by any
16   * means.
17   *
18   * In jurisdictions that recognize copyright laws, the author or authors
19   * of this software dedicate any and all copyright interest in the
20   * software to the public domain. We make this dedication for the benefit
21   * of the public at large and to the detriment of our heirs and
22   * successors. We intend this dedication to be an overt act of
23   * relinquishment in perpetuity of all present and future rights to this
24   * software under copyright law.
25   *
26   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
29   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
30   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
31   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
32   * OTHER DEALINGS IN THE SOFTWARE.
33   * #L%
34   */
35  
36  import nom.tam.fits.FitsException;
37  import nom.tam.fits.FitsFactory;
38  import nom.tam.fits.Header;
39  import nom.tam.fits.HeaderCard;
40  import nom.tam.util.type.ElementType;
41  
42  /**
43   * Standard BITPIX values and associated functions. Since the FITS BITPIX keyword has only a handful of legal values, an
44   * <code>enum</code> provides ideal type-safe representation. It also allows to interface the value for the type of data
45   * it represents in a natural way.
46   *
47   * @author Attila Kovacs
48   *
49   * @since  1.16
50   */
51  public enum Bitpix {
52      /** For FITS data stored as bytes */
53      BYTE(Byte.TYPE, ElementType.BYTE, "bytes"),
54  
55      /** For FITS data stored as 16-bit integers */
56      SHORT(Short.TYPE, ElementType.SHORT, "16-bit integers"),
57  
58      /** For FITS data stored as 32-bit integers */
59      INTEGER(Integer.TYPE, ElementType.INT, "32-bit integers"),
60  
61      /** For FITS data stored as 64-bit integers */
62      LONG(Long.TYPE, ElementType.LONG, "64-bit integers"),
63  
64      /** For FITS data stored as 32-bit single-precision floating point values */
65      FLOAT(Float.TYPE, ElementType.FLOAT, "32-bit floating point"),
66  
67      /** For FITS data stored as 64-bit double-precision floating point values */
68      DOUBLE(Double.TYPE, ElementType.DOUBLE, "64-bit floating point");
69  
70      private static final Logger LOG = Logger.getLogger("nom.tam.fits.HeaderCardParser");
71  
72      private static final int BITS_TO_BYTES_SHIFT = 3;
73  
74      /** BITPIX value for <code>byte</code> type data */
75      public static final int VALUE_FOR_BYTE = 8;
76  
77      /** BITPIX value for <code>short</code> type data */
78      public static final int VALUE_FOR_SHORT = 16;
79  
80      /** BITPIX value for <code>int</code> type data */
81      public static final int VALUE_FOR_INT = 32;
82  
83      /** BITPIX value for <code>long</code> type data */
84      public static final int VALUE_FOR_LONG = 64;
85  
86      /** BITPIX value for <code>float</code> type data */
87      public static final int VALUE_FOR_FLOAT = -32;
88  
89      /** BITPIX value for <code>double</code> type data */
90      public static final int VALUE_FOR_DOUBLE = -64;
91  
92      /** the number subclass represented this BITPIX instance */
93      private Class<? extends Number> numberType;
94  
95      /** the library's element type */
96      private ElementType<?> elementType;
97  
98      /** a concise description of the data type represented */
99      private String description;
100 
101     /**
102      * Constructor for a standard BITPIX instance.
103      *
104      * @param numberType  the Number subclass
105      * @param elementType the class of data element
106      * @param desc        a concise description of the data type
107      */
108     Bitpix(Class<? extends Number> numberType, ElementType<?> elementType, String desc) {
109         this.numberType = numberType;
110         this.elementType = elementType;
111         description = desc;
112     }
113 
114     /**
115      * Returns the FITS element type corresponding to this bitpix value
116      * 
117      * @return the FITS element type that corresponds to this bitpix value.
118      */
119     public final ElementType<?> getElementType() {
120         return elementType;
121     }
122 
123     /**
124      * Returns the sublass of {@link Number} corresponding for this BITPIX value.
125      *
126      * @return the number class for this BITPIX instance.
127      *
128      * @see    #getPrimitiveType()
129      * @see    Bitpix#forNumberType(Class)
130      */
131     public final Class<? extends Number> getNumberType() {
132         return numberType;
133     }
134 
135     /**
136      * Returns the primitive built-in Java number type corresponding for this BITPIX value.
137      *
138      * @return the primitive class for this BITPIX instance, such as <code>int.class</code>, or
139      *             <code>double.class</code>.
140      *
141      * @see    #getNumberType()
142      * @see    Bitpix#forPrimitiveType(Class)
143      */
144     public final Class<?> getPrimitiveType() {
145         return elementType.primitiveClass();
146     }
147 
148     /**
149      * Returns the FITS standard BITPIX header value for this instance.
150      *
151      * @return the standard FITS BITPIX value, such as 8, 16, 32, 64, -32, or -64.
152      *
153      * @see    Bitpix#forValue(int)
154      * @see    #getHeaderCard()
155      */
156     public final int getHeaderValue() {
157         return elementType.bitPix();
158     }
159 
160     /**
161      * Returns the Java letter ID for this BITPIX instance, such as the letter ID used in the Java array representation
162      * of that class. For example, an <code>int[]</code> array has class <code>I[</code>, so the letter ID is
163      * <code>I</code>.
164      *
165      * @return The Java letter ID for arrays corresponding to this BITPIX instance.
166      *
167      * @see    Bitpix#forArrayID(char)
168      */
169     public final char getArrayID() {
170         return elementType.type();
171     }
172 
173     /**
174      * Returns a concise description of the data type represented by this BITPIX instance.
175      *
176      * @return a brief description of the corresponding data type.
177      */
178     public final String getDescription() {
179         return description;
180     }
181 
182     /**
183      * Returns the size of a data element, in bytes, for this BITPIX instance
184      *
185      * @return the size of a data element in bytes.
186      */
187     public final int byteSize() {
188         return Math.abs(getHeaderValue()) >>> BITS_TO_BYTES_SHIFT;
189     }
190 
191     /**
192      * Returns the standard FITS header card for this BITPIX instance.
193      *
194      * @return the standard FITS header card with the BITPIX keyword and the corresponding value for this instance.
195      *
196      * @see    #getHeaderValue()
197      */
198     public final HeaderCard getHeaderCard() {
199         return HeaderCard.create(Standard.BITPIX, getHeaderValue());
200     }
201 
202     /**
203      * Returns the standard BITPIX object for a primitive type.
204      *
205      * @param  dataType      the primitive class, such as <code>int.class</code>.
206      *
207      * @return               the standard BITPIX associated to the number type
208      *
209      * @throws FitsException if the class is not a primitive class, or if its not one that has a corresponding BITPIX
210      *                           value (e.g. <code>
211      *                          boolean.class</code>).
212      *
213      * @see                  Bitpix#forNumberType(Class)
214      * @see                  #getPrimitiveType()
215      */
216     public static Bitpix forPrimitiveType(Class<?> dataType) throws FitsException {
217         if (dataType == byte.class) {
218             return BYTE;
219         }
220         if (dataType == short.class) {
221             return SHORT;
222         }
223         if (dataType == int.class) {
224             return INTEGER;
225         }
226         if (dataType == long.class) {
227             return LONG;
228         }
229         if (dataType == float.class) {
230             return FLOAT;
231         }
232         if (dataType == double.class) {
233             return DOUBLE;
234         }
235         if (Object.class.isAssignableFrom(dataType)) {
236             throw new FitsException("No BITPIX for type: " + dataType + " (expected primitive type)");
237         }
238 
239         throw new FitsException("No BITPIX for primitive type: " + dataType);
240     }
241 
242     /**
243      * Returns the standard BITPIX object for a number type.
244      *
245      * @param  dataType      the class of number, such as {@link Integer#TYPE}.
246      *
247      * @return               the standard BITPIX associated to the number type
248      *
249      * @throws FitsException if there is no standard BITPIX value corresponding to the number type (e.g.
250      *                           {@link java.math.BigDecimal}).
251      *
252      * @see                  Bitpix#forPrimitiveType(Class)
253      * @see                  #getNumberType()
254      */
255     public static Bitpix forNumberType(Class<? extends Number> dataType) throws FitsException {
256         if (Byte.class.isAssignableFrom(dataType)) {
257             return BYTE;
258         }
259         if (Short.class.isAssignableFrom(dataType)) {
260             return SHORT;
261         }
262         if (Integer.class.isAssignableFrom(dataType)) {
263             return INTEGER;
264         }
265         if (Long.class.isAssignableFrom(dataType)) {
266             return LONG;
267         }
268         if (Float.class.isAssignableFrom(dataType)) {
269             return FLOAT;
270         }
271         if (Double.class.isAssignableFrom(dataType)) {
272             return DOUBLE;
273         }
274         throw new FitsException("No BITPIX for Number type " + dataType);
275     }
276 
277     /**
278      * Returns the standard BITPIX object based on the value assigned to the BITPIX keyword in the header
279      *
280      * @param  h             the FITS header
281      *
282      * @return               the standard BITPIX enum that matches the header description, or is inferred from an
283      *                           invalid header description (provided {@link FitsFactory#setAllowHeaderRepairs(boolean)}
284      *                           is enabled).
285      *
286      * @throws FitsException if the header does not contain a BITPIX value or it is invalid and cannot or will not be
287      *                           repaired.
288      *
289      * @see                  Bitpix#fromHeader(Header, boolean)
290      * @see                  Bitpix#forValue(int)
291      * @see                  FitsFactory#setAllowHeaderRepairs(boolean)
292      */
293     public static Bitpix fromHeader(Header h) throws FitsException {
294         return forValue(h.getIntValue(Standard.BITPIX, 0));
295     }
296 
297     /**
298      * Returns the standard BITPIX object based on the value assigned to the BITPIX keyword in the header
299      *
300      * @param  h             the FITS header
301      * @param  allowRepair   if we can try repair non-standard (invalid) BITPIX values.
302      *
303      * @return               the standard BITPIX enum that matches the header description, or is inferred from an
304      *                           invalid header description.
305      *
306      * @throws FitsException if the header does not contain a BITPIX value or it is invalid and cannot or will not be
307      *                           repaired.
308      *
309      * @see                  Bitpix#fromHeader(Header)
310      * @see                  Bitpix#forValue(int, boolean)
311      */
312     public static Bitpix fromHeader(Header h, boolean allowRepair) throws FitsException {
313         return forValue(h.getIntValue(Standard.BITPIX, 0), allowRepair);
314     }
315 
316     /**
317      * Returns the standard BITPIX enum value for a given integer value, such as 8, 16, 32, 64, -32, or -64. If the
318      * value is not one of the standard values, then depending on whether header repairs are enabled either an exception
319      * is thrown, or else the value the value is 'repaired' and a loh entry is made to the logger of {@link Header}.
320      *
321      * @param  ival          The integer value of BITPIX in the FITS header.
322      *
323      * @return               The standard value as a Java object.
324      *
325      * @throws FitsException if the value was invalid or irreparable.
326      *
327      * @see                  Bitpix#forValue(int, boolean)
328      * @see                  FitsFactory#setAllowHeaderRepairs(boolean)
329      * @see                  #getHeaderValue()
330      */
331     public static Bitpix forValue(int ival) throws FitsException {
332         try {
333             return forValue(ival, FitsFactory.isAllowHeaderRepairs());
334         } catch (FitsException e) {
335             throw new FitsException(e.getMessage() + "\n\n" + " --> Try FitsFactory.setAllowHeaderRepairs(true).\n");
336         }
337     }
338 
339     /**
340      * Returns the standard BITPIX enum value for a given integer value, such as 8, 16, 32, 64, -32, or -64. If the
341      * value is not one of the standard values, then depending on whether repairs are enabled either an exception is
342      * thrown, or else the value the value is 'repaired' and a loh entry is made to the logger of {@link Header}.
343      *
344      * @param  ival          The integer value of BITPIX in the FITS header.
345      * @param  allowRepair   Whether we can fix up invalid values to make them valid.
346      *
347      * @return               The standard value as a Java object.
348      *
349      * @throws FitsException if the value was invalid or irreparable.
350      *
351      * @see                  Bitpix#forValue(int)
352      * @see                  #getHeaderValue()
353      */
354     public static Bitpix forValue(int ival, boolean allowRepair) throws FitsException {
355 
356         if (ival == 0) {
357             throw new FitsException("Invalid BITPIX value:" + ival);
358         }
359 
360         // Normally BITPIX must be one one of the supported values. Unfortunately, some
361         // commercial cameras fill illegal values, such as 20.
362         // We can 'repair' them by rounding up to the next valid value, so 20 repairs to 32, and
363         // maxing at +/- 64, so for example -80 repairs to -64.
364         if (allowRepair) {
365             int fixed = 0;
366 
367             if (ival < 0) {
368                 fixed = ival < VALUE_FOR_FLOAT ? VALUE_FOR_DOUBLE : VALUE_FOR_FLOAT;
369             } else if (ival < VALUE_FOR_BYTE) {
370                 fixed = VALUE_FOR_BYTE;
371             } else if (ival > VALUE_FOR_LONG) {
372                 fixed = VALUE_FOR_LONG;
373             } else if (ival > Integer.highestOneBit(ival)) {
374                 fixed = (Integer.highestOneBit(ival) << 1);
375             }
376 
377             if (fixed != 0) {
378                 LOG.warning("Repaired invalid BITPIX value:" + ival + " --> " + fixed);
379                 ival = fixed;
380             }
381         }
382 
383         switch (ival) {
384         case VALUE_FOR_BYTE:
385             return BYTE;
386         case VALUE_FOR_SHORT:
387             return SHORT;
388         case VALUE_FOR_INT:
389             return INTEGER;
390         case VALUE_FOR_LONG:
391             return LONG;
392         case VALUE_FOR_FLOAT:
393             return FLOAT;
394         case VALUE_FOR_DOUBLE:
395             return DOUBLE;
396         default:
397             throw new FitsException("Invalid BITPIX value:" + ival);
398         }
399     }
400 
401     /**
402      * Returns the standard BITPIX object for the given Java array ID. The array ID is the same letter code as Java uses
403      * for identifying ptrimitive array types. For example a Java array of <code>long[][]</code> has a class name of
404      * <code>J[[</code>, so so the array ID for <code>long</code> arrays is <code>J</code>.
405      *
406      * @param  id            The Java letter ID for arrays of the underlying primitive type. E.g. <code>J</code> for
407      *                           <code>long</code>.
408      *
409      * @return               The standard BITPIX enum corresponding to the data type.
410      *
411      * @throws FitsException if the data type is unknown or does not have a BITPIX ewquivalent.
412      */
413     public static Bitpix forArrayID(char id) throws FitsException {
414         switch (id) {
415         case 'B':
416             return BYTE;
417         case 'S':
418             return SHORT;
419         case 'I':
420             return INTEGER;
421         case 'J':
422             return LONG;
423         case 'F':
424             return FLOAT;
425         case 'D':
426             return DOUBLE;
427         default:
428             throw new FitsException("Invalid BITPIX data ID: '" + id + "'");
429         }
430     }
431 }