def readFitsData(self, fitsPath): """ readFitsData reads the fits file with the image and tries to get some key fields out of the header for preparing the solver. :param fitsPath: fits file with image data :return: raHint, decHint, scaleHint """ with fits.open(fitsPath) as fitsHDU: fitsHeader = fitsHDU[0].header # todo: there might be the necessity to read more alternative header info # todo: the actual definition is OK for EKOS scaleHint = float(fitsHeader.get('SCALE', 0)) ra = fitsHeader.get('RA', 0) dec = fitsHeader.get('DEC', 0) raHint = transform.convertToAngle(ra, isHours=True) decHint = transform.convertToAngle(dec, isHours=False) self.log.info( f'RA: {raHint} ({ra}), DEC: {decHint} ({dec}), Scale: {scaleHint}') return raHint, decHint, scaleHint, ra, dec
def writeHeaderToGUI(self, header=None): """ writeHeaderToGUI tries to read relevant values from FITS header and possible replace values and write them to the imageW gui :param header: header of fits file :return: hasCelestial, hasDistortion """ name = header.get('OBJECT', '').upper() self.ui.object.setText(f'{name}') ra = header.get('OBJCTRA', 0) ra = transform.convertToAngle(ra, isHours=True) dec = header.get('OBJCTDEC', 0) dec = transform.convertToAngle(dec, isHours=False) self.ui.ra.setText(f'{transform.convertToHMS(ra)}') self.ui.dec.setText(f'{transform.convertToDMS(dec)}') scale = header.get('SCALE', 0) rotation = header.get('ANGLE', 0) self.ui.scale.setText(f'{scale:5.3f}') self.ui.rotation.setText(f'{rotation:6.3f}') ccdTemp = header.get('CCD-TEMP', 0) self.ui.ccdTemp.setText(f'{ccdTemp:4.1f}') expTime1 = header.get('EXPOSURE', 0) expTime2 = header.get('EXPTIME', 0) expTime = max(expTime1, expTime2) self.ui.expTime.setText(f'{expTime:5.1f}') filterCCD = header.get('FILTER', 0) self.ui.filter.setText(f'{filterCCD}') binX = header.get('XBINNING', 0) binY = header.get('YBINNING', 0) self.ui.binX.setText(f'{binX:1.0f}') self.ui.binY.setText(f'{binY:1.0f}') sqm = max(header.get('SQM', 0), header.get('SKY-QLTY', 0), header.get('MPSAS', 0), ) self.ui.sqm.setText(f'{sqm:5.2f}') flipped = bool(header.get('FLIPPED', False)) self.ui.isFlipped.setEnabled(flipped) # check if distortion is in header if 'CTYPE1' in header: wcsObject = wcs.WCS(header, relax=True) hasCelestial = wcsObject.has_celestial hasDistortion = wcsObject.has_distortion else: wcsObject = None hasCelestial = False hasDistortion = False self.ui.hasDistortion.setEnabled(hasDistortion) self.ui.hasWCS.setEnabled(hasCelestial) return hasDistortion, wcsObject
def test_convertToAngle_4(): value = transform.convertToAngle('+12:00:00.0') assert value.degrees == 12
def test_convertToAngle_4(): value = transform.convertToAngle('12:00:00.0', isHours=True) assert value.hours == 12
def test_convertToAngle_3(): value = transform.convertToAngle(180, isHours=True) assert value.hours == 12
def test_convertToAngle_2(): value = transform.convertToAngle(180) assert value.degrees == 180
def test_convertToAngle_2(): parameter = 180 value = transform.convertToAngle(parameter, isHours=True) assert value.hours == 12
def test_convertToAngle_1(): parameter = 180 value = transform.convertToAngle(parameter) assert value.degrees == 180
def getSolutionFromWCS(self, fitsHeader=None, wcsHeader=None, updateFits=False): """ getSolutionFromWCS reads the wcs fits file and uses the data in the header containing the wcs data and returns the basic data needed. in addition it embeds it to the given fits file with image. it removes all entries starting with some keywords given in selection. we starting with HISTORY :param fitsHeader: :param wcsHeader: :param updateFits: :return: ra in hours, dec in degrees, angle in degrees, scale in arcsec/pixel error in arcsec and flag if image is flipped """ self.log.info(f'wcs header: [{wcsHeader}]') raJ2000 = transform.convertToAngle(wcsHeader.get('CRVAL1'), isHours=True) decJ2000 = transform.convertToAngle(wcsHeader.get('CRVAL2'), isHours=False) angle, scale, flipped = self.calcAngleScaleFromWCS(wcsHeader=wcsHeader) raMount = transform.convertToAngle(fitsHeader.get('RA'), isHours=True) decMount = transform.convertToAngle(fitsHeader.get('DEC'), isHours=False) # todo: it would be nice, if adding, subtracting of angels are part of skyfield deltaRA = abs(raJ2000._degrees - raMount._degrees) * 3600 deltaDEC = abs(decJ2000.degrees - decMount.degrees) * 3600 error = np.sqrt(np.square(deltaRA) + np.square(deltaDEC)) solve = { 'raJ2000S': raJ2000, 'decJ2000S': decJ2000, 'errorRA_S': deltaRA, 'errorDEC_S': deltaDEC, 'angleS': angle, 'scaleS': scale, 'errorRMS_S': error, 'flippedS': flipped } if not updateFits: return solve, fitsHeader fitsHeader.append(('SCALE', solve['scaleS'], 'MountWizzard4')) fitsHeader.append(('PIXSCALE', solve['scaleS'], 'MountWizzard4')) fitsHeader.append(('ANGLE', solve['angleS'], 'MountWizzard4')) fitsHeader.append(('FLIPPED', solve['flippedS'], 'MountWizzard4')) fitsHeader.extend(wcsHeader, unique=True, update=True) # remove polynomial coefficients keys if '-SIP' is not selected in CTYPE1 and CTYPE2 # this might occur, if you solve a fits file a second time with another solver if 'CTYPE1' not in fitsHeader or 'CTYPE2' not in fitsHeader: return solve, fitsHeader if '-SIP' in fitsHeader['CTYPE1'] and '-SIP' in fitsHeader['CTYPE2']: return solve, fitsHeader for key in list(fitsHeader.keys()): if key.startswith('A_'): del fitsHeader[key] elif key.startswith('B_'): del fitsHeader[key] elif key.startswith('AP_'): del fitsHeader[key] elif key.startswith('BP_'): del fitsHeader[key] return solve, fitsHeader