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 }