Coverage for src/susi/io/fits_header.py: 69%

48 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2025-06-13 14:15 +0000

1#!/usr/bin/env python3 

2# -*- coding: utf-8 -*- 

3""" 

4module provides classes to deal with the FITS header 

5 

6@author: hoelken 

7""" 

8import re 

9from ..base import Logging 

10from ..base.header_keys import * 

11 

12logger = Logging.get_logger() 

13 

14 

15class Card: 

16 """ 

17 ## Card 

18 Class holds FITS header field information (name, value, comment) and provides methods to 

19 write them to a FITS file header. 

20 """ 

21 

22 @staticmethod 

23 def from_orig(orig, card_name): 

24 """ 

25 Creates a Card instance from an original FITS header. 

26 

27 ### Params 

28 - orig: [FitsHeader] the original header 

29 - card_name: [String] the name of the card to copy from orig 

30 

31 ### Returns 

32 The created Card instance 

33 """ 

34 return Card(card_name, value=orig[card_name], comment=orig.comments[card_name]) 

35 

36 @staticmethod 

37 def copy_all(orig) -> list: 

38 cards = [] 

39 for key in orig.keys(): 

40 if key in ['SIMPLE', 'BITPIX', 'NAXIS', 'NAXIS1', 'NAXIS2', 'NAXIS3', 'NAXIS4', 'EXTEND', 'COMMENT']: 

41 continue 

42 cards.append(Card(key, value=orig[key], comment=re.sub(r'[^\x00-\x7F]+', '?', str(orig.comments[key])))) 

43 return cards 

44 

45 def __init__(self, name, value=None, comment=None): 

46 #: Header Card name 

47 self.name = name 

48 #: Header Card value 

49 self.value = value 

50 #: Explanatory comment 

51 self.comment = comment 

52 

53 def to_card(self): 

54 """ 

55 Converts the content of the card to a tuple suitable for use with `hdu.header.append()` 

56 

57 ### Returns 

58 A 2 or 3-tuple (depending on if a comment is set) 

59 """ 

60 prefix = '' 

61 if len(self.name) > 8 or ' ' in self.name or '_' in self.name: 

62 prefix = 'hierarch ' 

63 if len(str(self.value)) > 75 - len(prefix + self.name): 

64 self.value = self.value[0 : 75 - len(prefix + self.name)] 

65 logger.warning('Key "%s" too long. Truncated to "%s"', self.name, self.value) 

66 if self.comment: 

67 return prefix + self.name, self.value, self.comment 

68 return prefix + self.name, self.value 

69 

70 

71class HeaderCards: 

72 """ 

73 ## HeaderCards 

74 Container for a set of FITS header `Card`s. 

75 Can be used to generate FITS headers. 

76 """ 

77 

78 def __init__(self): 

79 # Generated header cards 

80 self.cards = [ 

81 Card('Project Name', value='Sunrise3'), 

82 Card('Camera Name', value='SUSI '), 

83 ] 

84 

85 def append(self, name: str, value: object, comment: str = None): 

86 """ 

87 Append data to header 

88 ### Params 

89 - name: The name of the new card 

90 - value: the value to set 

91 - comment: an optional comment for the card 

92 :return: Nothing 

93 """ 

94 self.cards.append(Card(name, value=value, comment=comment)) 

95 

96 def copy_card(self, orig, card_name): 

97 """ 

98 Appends the value and comment of the original card 

99 ### Params 

100 - orig: [FitsHeader] the original header 

101 - card_name: [String] the name of the card to copy from orig 

102 """ 

103 self.cards.append(Card.from_orig(orig, card_name)) 

104 

105 

106class SUSIStandardHeader: 

107 """ 

108 creates/updates the final SUSI standard header of a single image file 

109 with dimensions [spectral, slit, stokes] for [NAXIS1, NAXIS2, NAXIS3] 

110 

111 # Translate header keys to desired standard names and units 

112 # Add WCS keywords to the header 

113 # Add other HK metadata to the header 

114 

115 @author: iglesias, castellanos, sanchez 

116 """ 

117 def __init__(self, header=None): 

118 self.header = header 

119 

120 def update(self): 

121 """ 

122 Updates the header with the standard header information 

123 ### Params 

124 - header: [FitsHeader] the header to update 

125 """ 

126 if self.header is None: 

127 logger.error('No header to update') 

128 return 

129 

130 # SLIT dimension 

131 # removes XSCALE and adds CDELT2 

132 self.header['CDELT2'] = self.header['XSCALE'] 

133 del self.header['XSCALE'] 

134 # removes XCEN and adds CRPIX2 

135 # TODO must workout rotation to translate XCEN, YCEN to CRPIX2,CRPIX3 

136 return