def solve(self, solver={}, fitsPath='', raHint=None, decHint=None, scaleHint=None, radius=2, timeout=30, updateFits=False): """ Solve uses the astrometry.net solver capabilities. The intention is to use an offline solving capability, so we need a installed instance. As we go multi platform and we need to focus on MW function, we use the astrometry.net package which is distributed with KStars / EKOS. Many thanks to them providing such a nice package. As we go using astrometry.net we focus on the minimum feature set possible to omit many of the installation and wrapping work to be done. So we only support solving of FITS files, use no python environment for astrometry.net parts (as we could access these via MW directly) The base outside ideas of implementation come from astrometry.net itself and the astrometry implementation from cloudmakers.eu (another nice package for MAC Astro software) :param solver: which astrometry implementation to choose :param fitsPath: full path to fits file :param raHint: ra dest to look for solve in J2000 :param decHint: dec dest to look for solve in J2000 :param scaleHint: scale to look for solve in J2000 :param radius: search radius around target coordinates :param timeout: time after the subprocess will be killed. :param updateFits: if true update Fits image file with wcsHeader data :return: success """ self.process = None self.result = {'success': False} if not solver: return False if not os.path.isfile(fitsPath): self.result['message'] = 'image missing' return False tempPath = self.tempDir + '/temp.xy' configPath = self.tempDir + '/astrometry.cfg' solvedPath = self.tempDir + '/temp.solved' wcsPath = self.tempDir + '/temp.wcs' binPathImage2xy = solver['programPath'] + '/image2xy' binPathSolveField = solver['programPath'] + '/solve-field' if os.path.isfile(wcsPath): os.remove(wcsPath) cfgFile = self.tempDir + '/astrometry.cfg' with open(cfgFile, 'w+') as outFile: outFile.write('cpulimit 300\n') outFile.write(f'add_path {solver["indexPath"]}\n') outFile.write('autoindex\n') # using sextractor in astrometry.net and KStars """ with open('default.param', 'w+') as outFile: outFile.write('MAG_AUTO Kron-like elliptical aperture magnitude [mag]\n') outFile.write('X_IMAGE Object position along x [pixel]\n') outFile.write('Y_IMAGE Object position along y [pixel]\n') with open('default.conv', 'w+') as outFile: outFile.write('CONV NORM\n') outFile.write('1 2 1\n') outFile.write('2 4 2\n') outFile.write('1 2 1\nn') """ suc = self.runImage2xy(binPath=binPathImage2xy, tempPath=tempPath, fitsPath=fitsPath, timeout=timeout, ) if not suc: self.log.error(f'image2xy error in [{fitsPath}]') self.result['message'] = 'image2xy failed' return False raFITS, decFITS, scaleFITS, _, _ = self.readFitsData(fitsPath=fitsPath) # if parameters are passed, they have priority if raHint is None: raHint = raFITS if decHint is None: decHint = decFITS if scaleHint is None: scaleHint = scaleFITS searchRatio = 1.1 ra = transform.convertToHMS(raHint) dec = transform.convertToDMS(decHint) scaleLow = scaleHint / searchRatio scaleHigh = scaleHint * searchRatio options = ['--scale-low', f'{scaleLow}', '--scale-high', f'{scaleHigh}', '--ra', f'{ra}', '--dec', f'{dec}', '--radius', f'{radius:1.1f}', ] # split between ekos and cloudmakers as cloudmakers use an older version of # solve-field, which need the option '--no-fits2fits', whereas the actual # version used in KStars throws an error using this option. if 'Astrometry.app' in solver['programPath']: options.append('--no-fits2fits') suc = self.runSolveField(binPath=binPathSolveField, configPath=configPath, tempPath=tempPath, options=options, timeout=timeout, ) if not suc: self.log.error(f'solve-field error in [{fitsPath}]') self.result['message'] = 'solve-field error' return False if not os.path.isfile(solvedPath): self.log.info(f'solve files for [{fitsPath}] missing') self.result['message'] = 'solve failed' return False if not os.path.isfile(wcsPath): self.log.info(f'solve files for [{wcsPath}] missing') self.result['message'] = 'solve failed' return False with fits.open(wcsPath) as wcsHDU: wcsHeader = self.getWCSHeader(wcsHDU=wcsHDU) with fits.open(fitsPath, mode='update') as fitsHDU: solve, header = self.getSolutionFromWCS(fitsHeader=fitsHDU[0].header, wcsHeader=wcsHeader, updateFits=updateFits) fitsHDU[0].header = header self.result = { 'success': True, 'solvedPath': fitsPath, 'message': 'Solved', } self.result.update(solve) return True
def test_convertToDMS_2(): value = Angle(degrees=-90.0) value = transform.convertToDMS(value) assert value == '-90:00:00'
def test_convertToDMS_2(): parameter = 60 value = transform.convertToDMS(parameter) assert value == '+60:00:00'
def test_convertToDMS_3(): parameter = '60' value = transform.convertToDMS(parameter) assert value == ''
def test_convertToDMS_1(): parameter = Angle(degrees=60) value = transform.convertToDMS(parameter) assert value == '+60:00:00'