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 }