From 0d0abeaed23a9ae3c1da49700dfbe488b155c639 Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Thu, 1 Apr 2021 15:26:20 -0400 Subject: [PATCH 01/13] added support for FRIPON files --- RMS/Formats/FrameInterface.py | 113 ++++++++++++++++++++++++---------- 1 file changed, 82 insertions(+), 31 deletions(-) diff --git a/RMS/Formats/FrameInterface.py b/RMS/Formats/FrameInterface.py index 4249b4945..82ccb5d07 100644 --- a/RMS/Formats/FrameInterface.py +++ b/RMS/Formats/FrameInterface.py @@ -25,6 +25,7 @@ import cv2 import numpy as np +from astropy.io import fits from RMS.Astrometry.Conversions import unixTime2Date, datetime2UnixTime from RMS.Formats.FFfile import read as readFF @@ -1206,20 +1207,26 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa self.ff = None self.cache = {} + self.dt_frame_time = None + self.frame_dt_list = None + # This type of input probably won't have any calstars files self.require_calstars = False # Disctionary which holds the time of every frame, used for fast frame time lookup - self.uwo_png_dt_dict = {} + self.frame_dt_dict = {} + + self.fripon_mode = False + + img_types = ['.png', '.jpg', '.bmp', '.fit'] + # Add raw formats if rawpy is installed if 'rawpy' in sys.modules: - ### Find images in the given folder ### - img_types = ['.png', '.jpg', '.bmp', '.nef', '.cr2'] - else: - img_types = ['.png', '.jpg', '.bmp'] + img_types += ['.nef', '.cr2'] self.img_list = [] + ### Find images in the given folder ### for file_name in sorted(os.listdir(self.dir_path)): # Check if the file ends with support file extensions @@ -1242,6 +1249,16 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa ### ### + ### Filter the file list so only the images with most extension types are used ### + + extensions = [entry.split('.')[-1] for entry in self.img_list] + unique = np.unique(extensions, return_counts=True) + most_common_extension = unique[0][np.argmax(unique[1])].lower() + + self.img_list = [entry for entry in self.img_list if entry.lower().endswith(most_common_extension)] + + ### + # Compute the total number of used frames self.total_frames = len(self.img_list) @@ -1274,7 +1291,7 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa # Get the beginning time self.loadFrame(fr_no=0) - beginning_time = self.uwo_png_frame_time + beginning_time = self.dt_frame_time print('UWO PNG mode') @@ -1287,8 +1304,11 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa else: self.byteswap = False - self.uwo_png_frame_time = None - self.uwo_png_dt_list = None + + # Set the begin time if in the FRIPON mode + if self.fripon_mode: + beginning_time = self.dt_frame_time + # Check if the beginning time was given (it will be read from the PNG if the UWO format is given) if beginning_time is None: @@ -1359,15 +1379,18 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa if self.uwo_png_mode and not self.single_image_mode: # Convert datetimes to Unix times - unix_times = [datetime2UnixTime(dt) for dt in self.uwo_png_dt_list] + unix_times = [datetime2UnixTime(dt) for dt in self.frame_dt_list] fps = 1/((unix_times[-1] - unix_times[0])/self.current_fr_chunk_size) + # If FPS is not given, use one from the config file if fps is None: - self.fps = self.config.fps - print('Using FPS from config file: ', self.fps) + # Don't use the config file if FRIPON files are used + if not self.fripon_mode: + self.fps = self.config.fps + print('Using FPS from config file: ', self.fps) else: @@ -1452,13 +1475,13 @@ def loadChunk(self, first_frame=None, read_nframes=None): # Check if this chunk has been cached if cache_id in self.cache: - frame, self.uwo_png_dt_list, self.current_fr_chunk_size = self.cache[cache_id] + frame, self.frame_dt_list, self.current_fr_chunk_size = self.cache[cache_id] return frame # Init making the FF structure ff_struct_fake = FFMimickInterface(self.nrows, self.ncols, self.img_dtype) - self.uwo_png_dt_list = [] + self.frame_dt_list = [] # Load the chunk of frames for i in range(frames_to_read): @@ -1477,8 +1500,8 @@ def loadChunk(self, first_frame=None, read_nframes=None): ff_struct_fake.addFrame(frame.astype(np.uint16)) # Add the datetime of the frame to list of the UWO png is used - if self.uwo_png_mode: - self.uwo_png_dt_list.append(self.currentFrameTime(frame_no=img_indx, dt_obj=True)) + if self.uwo_png_mode or self.fripon_mode: + self.frame_dt_list.append(self.currentFrameTime(frame_no=img_indx, dt_obj=True)) self.current_fr_chunk_size = i @@ -1488,7 +1511,7 @@ def loadChunk(self, first_frame=None, read_nframes=None): # Store the FF struct to cache to avoid recomputing self.cache = {} - self.cache[cache_id] = [ff_struct_fake, self.uwo_png_dt_list, self.current_fr_chunk_size] + self.cache[cache_id] = [ff_struct_fake, self.frame_dt_list, self.current_fr_chunk_size] # Set the computed chunk as the current FF self.ff = ff_struct_fake @@ -1559,7 +1582,7 @@ def loadFrame(self, avepixel=None, fr_no=None): # In the single image mode (but not for UWO), the frame will not change, so load it from the cache # if available single_image_key = "single_image" - if self.single_image_mode and not self.uwo_png_mode: + if self.single_image_mode and (not self.uwo_png_mode) and (not self.fripon_mode): if single_image_key in self.cache: # Load the frame from cache @@ -1582,6 +1605,29 @@ def loadFrame(self, avepixel=None, fr_no=None): # Convert the image to grayscale frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) + # Load a FRIPON fit file + if current_img_file.lower().endswith('.fit'): + + # Load the data from a fits file + with open(os.path.join(self.dir_path, current_img_file), 'rb') as f: + + # Open the image data + fits_file = fits.open(f) + frame = fits_file[0].data + + # Load the header + head = fits_file[0].header + + # Load the frame time + self.dt_frame_time = datetime.datetime.strptime(head['DATE'], "%Y-%m-%dT%H:%M:%S.%f") + + # Load the FPS + self.fps = 1.0/head["EXPOSURE"] + + # Indicate that a FRIPON fit file is read + self.fripon_mode = True + + # Load a normal image else: @@ -1623,11 +1669,14 @@ def loadFrame(self, avepixel=None, fr_no=None): frame_dt = unixTime2Date(ts, tu, dt_obj=True) - self.uwo_png_frame_time = frame_dt + self.dt_frame_time = frame_dt + + + if self.uwo_png_mode or self.fripon_mode: # Save the frame time of the current frame - if fr_no not in self.uwo_png_dt_dict: - self.uwo_png_dt_dict[fr_no] = frame_dt + if fr_no not in self.frame_dt_dict: + self.frame_dt_dict[fr_no] = self.dt_frame_time # Bin the frame if self.detection and (self.config.detection_binning_factor > 1): @@ -1661,10 +1710,10 @@ def name(self, beginning=False): def currentTime(self, dt_obj=False): """ Return the mean time of the current image. """ - if self.uwo_png_mode: + if self.uwo_png_mode or self.fripon_mode: # Convert datetimes to Unix times - unix_times = [datetime2UnixTime(dt) for dt in self.uwo_png_dt_list] + unix_times = [datetime2UnixTime(dt) for dt in self.frame_dt_list] # Compute the mean of unix times unix_mean = np.mean(unix_times) @@ -1695,20 +1744,20 @@ def currentFrameTime(self, frame_no=None, dt_obj=False): if frame_no is None: frame_no = self.current_frame - # If the UWO png is used, return the time read from the PNG - if self.uwo_png_mode: + # If the UWO png or FRIPON fit is used, return the time read from the file + if self.uwo_png_mode or self.fripon_mode: # If the frame number is not given, return the time of the current frame if frame_no is None: - dt = self.uwo_png_frame_time + dt = self.dt_frame_time # Otherwise, load the frame time else: # If the frame number is not in the dictionary, load the frame and read the time from it - if frame_no not in self.uwo_png_dt_dict: + if frame_no not in self.frame_dt_dict: current_frame_backup = self.current_frame # Load the time from the given frame @@ -1718,7 +1767,7 @@ def currentFrameTime(self, frame_no=None, dt_obj=False): self.loadFrame(fr_no=current_frame_backup) # Read the frame time from the dictionary - dt = self.uwo_png_dt_dict[frame_no] + dt = self.frame_dt_dict[frame_no] else: @@ -1934,11 +1983,13 @@ def detectInputTypeFolder(input_dir, config, beginning_time=None, fps=None, skip use_fr_files: [bool] Include FR files together with FF files. False by default, only used in SkyFit. """ + + ### Find images in the given folder ### + img_types = ['.png', '.jpg', '.bmp', '.fit'] + if 'rawpy' in sys.modules: - ### Find images in the given folder ### - img_types = ['.png', '.jpg', '.bmp', '.nef', '.cr2'] - else: - img_types = ['.png', '.jpg', '.bmp'] + img_types += ['.nef', '.cr2'] + img_handle = None From 1c7801e4c0fd33ec3d5cf81193b0f392949ebaed Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Mon, 5 Apr 2021 12:31:56 -0400 Subject: [PATCH 02/13] fixed loading dfn images --- RMS/Formats/FrameInterface.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/RMS/Formats/FrameInterface.py b/RMS/Formats/FrameInterface.py index 82ccb5d07..89a9c283d 100644 --- a/RMS/Formats/FrameInterface.py +++ b/RMS/Formats/FrameInterface.py @@ -1818,8 +1818,15 @@ def __init__(self, file_path, config, beginning_time=None, fps=None): if self.beginning_datetime is None and \ any([self.image_file.lower().endswith(fextens) for fextens in img_types]): try: + + # Extract the DFN timestamp from the file name + image_filename_split = self.image_file.split("_") + date_str = image_filename_split[1] + time_str = image_filename_split[2] + datetime_str = date_str + "_" + time_str + beginning_datetime = datetime.datetime.strptime( - self.image_file[4:21], + datetime_str, "%Y-%m-%d_%H%M%S") self.beginning_datetime = beginning_datetime From b46ed144f5eb230314438e92f9ad751df2f30f85 Mon Sep 17 00:00:00 2001 From: Alfredo Dal'Ava Junior Date: Thu, 8 Apr 2021 01:16:56 -0300 Subject: [PATCH 03/13] SkyFit2: silence deprecation warnings on python 3.8 --- Utils/SkyFit2.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Utils/SkyFit2.py b/Utils/SkyFit2.py index f4a6c4dc9..7d2106482 100644 --- a/Utils/SkyFit2.py +++ b/Utils/SkyFit2.py @@ -1332,7 +1332,7 @@ def updateStars(self): filtered_indices, _ = self.filterCatalogStarsInsideFOV(self.catalog_stars) # Create a mask to filter out all stars outside the image and the FOV - filter_indices_mask = np.zeros(len(cat_stars_xy), dtype=np.bool) + filter_indices_mask = np.zeros(len(cat_stars_xy), dtype=bool) filter_indices_mask[filtered_indices] = True filtered_indices_all = filter_indices_mask & (cat_stars_xy[:, 0] > 0) \ & (cat_stars_xy[:, 0] < self.platepar.X_res) \ @@ -2170,12 +2170,12 @@ def onMouseMoved(self, event): if mp.x() > (range_[1] - range_[0])/2 + range_[0]: self.v_zoom_left = True if self.show_key_help != 2: - self.v_zoom.move(QtCore.QPoint(self.label1.boundingRect().width(), 0)) + self.v_zoom.move(QtCore.QPoint(int(self.label1.boundingRect().width()), 0)) else: self.v_zoom.move(QtCore.QPoint(0, 0)) else: self.v_zoom_left = False - self.v_zoom.move(QtCore.QPoint(self.img_frame.size().width() - self.show_zoom_window_size, 0)) + self.v_zoom.move(QtCore.QPoint(int(self.img_frame.size().width() - self.show_zoom_window_size), 0)) self.updateBottomLabel() From 5bad04662a5b201cb8ad191ef9757aa0f699a7b5 Mon Sep 17 00:00:00 2001 From: Alfredo Dal'Ava Junior Date: Thu, 8 Apr 2021 23:07:51 -0300 Subject: [PATCH 04/13] SkyFit2: silence deprecation warnings on python 3.8 --- Utils/SkyFit2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utils/SkyFit2.py b/Utils/SkyFit2.py index 7d2106482..5b4fd194a 100644 --- a/Utils/SkyFit2.py +++ b/Utils/SkyFit2.py @@ -1664,7 +1664,7 @@ def photometry(self, show_plot=False): photom_resid_txt = "{:.2f}".format(fit_diff) # Determine the size of the residual text, larger the residual, larger the text - photom_resid_size = 8 + np.abs(fit_diff)/(np.max(np.abs(fit_resids))/5.0) + photom_resid_size = int(8 + np.abs(fit_diff)/(np.max(np.abs(fit_resids))/5.0)) if self.stdev_text_filter*std <= abs(fit_diff): text1 = TextItem(photom_resid_txt, anchor=(0.5, -0.5)) From bccd4f4039bcf8d4461524570d19621f81bb5b06 Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Tue, 13 Apr 2021 22:17:16 -0400 Subject: [PATCH 05/13] added better fripon support --- RMS/Formats/FrameInterface.py | 37 +++++++++++++++++++++++++- RMS/Routines/CustomPyqtgraphClasses.py | 2 +- Utils/SkyFit2.py | 19 +++++++------ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/RMS/Formats/FrameInterface.py b/RMS/Formats/FrameInterface.py index 89a9c283d..52b8752b4 100644 --- a/RMS/Formats/FrameInterface.py +++ b/RMS/Formats/FrameInterface.py @@ -37,6 +37,7 @@ from RMS.Formats.Vid import VidStruct from RMS.Routines import Image + # Import cython functions import pyximport pyximport.install(setup_args={'include_dirs': [np.get_include()]}) @@ -1217,6 +1218,7 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa self.frame_dt_dict = {} self.fripon_mode = False + self.fripon_header = None img_types = ['.png', '.jpg', '.bmp', '.fit'] @@ -1309,6 +1311,30 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa if self.fripon_mode: beginning_time = self.dt_frame_time + # Set station parameters if in the FRIPON mode + self.config.stationID = self.fripon_header["TELESCOP"].strip() + self.config.latitude = self.fripon_header["SITELAT"] + self.config.longitude = self.fripon_header["SITELONG"] + self.config.elevation = self.fripon_header["SITEELEV"] + + self.config.width = self.fripon_header["NAXIS1"] + self.config.height = self.fripon_header["NAXIS2"] + + # Global shutter + self.config.deinterlace_order = -2 + + # Set the catalog to BSC5 + self.config.star_catalog_path = os.path.join(self.config.rms_root_dir, "Catalogs") + self.config.star_catalog_file = "BSC5" + + # Set approximate FOV + self.config.fov_h = 180 + self.config.fov_w = 200 + + # Set magnitude limit + self.config.catalog_mag_limit = 3.5 + + # Check if the beginning time was given (it will be read from the PNG if the UWO format is given) if beginning_time is None: @@ -1615,11 +1641,17 @@ def loadFrame(self, avepixel=None, fr_no=None): fits_file = fits.open(f) frame = fits_file[0].data + # Flip image vertically + frame = np.flipud(frame) + # Load the header head = fits_file[0].header + # Save the FRIPON header + self.fripon_header = head + # Load the frame time - self.dt_frame_time = datetime.datetime.strptime(head['DATE'], "%Y-%m-%dT%H:%M:%S.%f") + self.dt_frame_time = datetime.datetime.strptime(head["DATE-OBS"], "%Y-%m-%dT%H:%M:%S.%f") # Load the FPS self.fps = 1.0/head["EXPOSURE"] @@ -1800,6 +1832,9 @@ def __init__(self, file_path, config, beginning_time=None, fps=None): """ self.input_type = 'dfn' + # Set the frames to a global shutter, so no correction is applied + self.config.deinterlace_order = -2 + self.dir_path, self.image_file = os.path.split(file_path) self.config = config diff --git a/RMS/Routines/CustomPyqtgraphClasses.py b/RMS/Routines/CustomPyqtgraphClasses.py index ee5768e77..9b1c399da 100644 --- a/RMS/Routines/CustomPyqtgraphClasses.py +++ b/RMS/Routines/CustomPyqtgraphClasses.py @@ -1286,7 +1286,7 @@ def __init__(self, gui): hbox = QtWidgets.QHBoxLayout() self.elev = DoubleSpinBox() - self.elev.setMinimum(0) + self.elev.setMinimum(-1000) self.elev.setMaximum(1000000) self.elev.setDecimals(3) self.elev.setSingleStep(1) diff --git a/Utils/SkyFit2.py b/Utils/SkyFit2.py index 5b4fd194a..810ed79bf 100644 --- a/Utils/SkyFit2.py +++ b/Utils/SkyFit2.py @@ -321,6 +321,17 @@ def __init__(self, input_path, config, beginning_time=None, fps=None, gamma=None self.fit_only_pointing = False ################################################################################################### + + + # Detect data input type and init the image handle + self.detectInputType(load=True, beginning_time=beginning_time, use_fr_files=self.use_fr_files) + + # Update the FPS if it's forced + self.setFPS() + + + ################################################################################################### + # LOADING STARS # Load catalog stars @@ -346,14 +357,6 @@ def __init__(self, input_path, config, beginning_time=None, fps=None, gamma=None self.loadCalstars() - - # Detect data input type and init the image handle - self.detectInputType(load=True, beginning_time=beginning_time, use_fr_files=self.use_fr_files) - - # Update the FPS if it's forced - self.setFPS() - - ################################################################################################### # PLATEPAR From 9ec8d99e421abcdbf7dc9cebea8ab76d9bfd6947 Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Wed, 14 Apr 2021 12:31:24 -0400 Subject: [PATCH 06/13] fixed timestamps for fripon data --- RMS/Formats/FrameInterface.py | 1 + Utils/SkyFit2.py | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/RMS/Formats/FrameInterface.py b/RMS/Formats/FrameInterface.py index 52b8752b4..b3bd863ab 100644 --- a/RMS/Formats/FrameInterface.py +++ b/RMS/Formats/FrameInterface.py @@ -1319,6 +1319,7 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa self.config.width = self.fripon_header["NAXIS1"] self.config.height = self.fripon_header["NAXIS2"] + self.config.fps = self.fps # Global shutter self.config.deinterlace_order = -2 diff --git a/Utils/SkyFit2.py b/Utils/SkyFit2.py index 810ed79bf..e5e80288b 100644 --- a/Utils/SkyFit2.py +++ b/Utils/SkyFit2.py @@ -4598,7 +4598,7 @@ def showLightcurve(self): # If the platepar is available, compute the magnitudes, otherwise show the instrumental magnitude if self.platepar is not None: - time_data = [self.img.img_handle.currentFrameTime()]*len(intensities) + time_data = [self.img.img_handle.currentFrameTime(fr) for fr in frames] # Compute the magntiudes _, _, _, mag_data = xyToRaDecPP(time_data, x_centroids, y_centroids, intensities, self.platepar) @@ -4787,8 +4787,12 @@ def saveFTPdetectinfo(self): if pick['mode'] == 0: continue + # Normalize the frame number to the actual time + frame_dt = self.img_handle.currentFrameTime(frame_no=frame, dt_obj=True) + frame_no = (frame_dt - self.img_handle.beginning_datetime).total_seconds()*self.img_handle.fps + # Get the rolling shutter corrected (or not, depending on the config) frame number - frame_no = self.getRollingShutterCorrectedFrameNo(frame, pick) + frame_no = self.getRollingShutterCorrectedFrameNo(frame_no, pick) centroids.append([frame_no, pick['x_centroid'], pick['y_centroid'], pick['intensity_sum']]) @@ -4899,7 +4903,7 @@ def saveJSON(self): if self.meas_ground_points: pp_tmp.switchToGroundPicks() - time_data = [self.img_handle.currentFrameTime()] + time_data = [self.img_handle.currentFrameTime(frame_no=frame)] # Compute measured RA/Dec from image coordinates _, ra_data, dec_data, mag_data = xyToRaDecPP(time_data, [pick['x_centroid']], @@ -4912,8 +4916,12 @@ def saveJSON(self): else: ra = dec = mag = None + # Normalize the frame number to the actual time + frame_dt = self.img_handle.currentFrameTime(frame_no=frame, dt_obj=True) + frame_no = (frame_dt - self.img_handle.beginning_datetime).total_seconds()*self.img_handle.fps + # Get the rolling shutter corrected (or not, depending on the config) frame number - frame_no = self.getRollingShutterCorrectedFrameNo(frame, pick) + frame_no = self.getRollingShutterCorrectedFrameNo(frame_no, pick) # Compute the time relative to the reference JD t_rel = frame_no/self.img_handle.fps @@ -5034,7 +5042,7 @@ def saveECSV(self): if self.meas_ground_points: pp_tmp.switchToGroundPicks() - time_data = [self.img_handle.currentFrameTime()] + time_data = [self.img_handle.currentFrameTime(frame_no=frame)] # Compute measured RA/Dec from image coordinates jd_data, ra_data, dec_data, mag_data = xyToRaDecPP(time_data, [pick['x_centroid']], @@ -5048,8 +5056,12 @@ def saveECSV(self): # Compute alt/az (topocentric, i.e. without refraction) azim, alt = trueRaDec2ApparentAltAz(ra, dec, jd, pp_tmp.lat, pp_tmp.lon, refraction=False) + # Normalize the frame number to the actual time + frame_dt = self.img_handle.currentFrameTime(frame_no=frame, dt_obj=True) + frame_no = (frame_dt - dt_ref).total_seconds()*self.img_handle.fps + # Get the rolling shutter corrected (or not, depending on the config) frame number - frame_no = self.getRollingShutterCorrectedFrameNo(frame, pick) + frame_no = self.getRollingShutterCorrectedFrameNo(frame_no, pick) # Compute the time relative to the reference JD t_rel = frame_no/self.img_handle.fps From c7fff3d16fa6540445e0c3e7e3ad028f1f503bce Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Wed, 14 Apr 2021 15:05:28 -0400 Subject: [PATCH 07/13] changed message box to qt from tkinter --- RMS/Formats/FrameInterface.py | 34 +++++++++++--------------- RMS/Routines/CustomPyqtgraphClasses.py | 15 ++++++++++++ Utils/SkyFit2.py | 14 ----------- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/RMS/Formats/FrameInterface.py b/RMS/Formats/FrameInterface.py index b3bd863ab..6fc96fe00 100644 --- a/RMS/Formats/FrameInterface.py +++ b/RMS/Formats/FrameInterface.py @@ -9,13 +9,6 @@ import datetime -# tkinter import that works on both Python 2 and 3 -try: - from tkinter import messagebox -except: - import tkMessageBox as messagebox - - # Rawpy for DFN images try: import rawpy @@ -35,6 +28,7 @@ from RMS.Formats.FRbin import read as readFR, validFRName from RMS.Formats.Vid import readFrame as readVidFrame from RMS.Formats.Vid import VidStruct +from RMS.Routines.CustomPyqtgraphClasses import qmessagebox from RMS.Routines import Image @@ -202,7 +196,7 @@ def __init__(self, dir_path, config, single_ff=False, use_fr_files=False): # Check that there are any FF files in the folder if not self.ff_list: - messagebox.showinfo(title='File list warning', message='No FF files in the selected folder!') + qmessagebox(title='File list warning', message='No FF files in the selected folder!') sys.exit() @@ -676,8 +670,8 @@ def __init__(self, dir_path, config, beginning_time=None, detection=False): self.beginning_datetime = datetime.datetime.strptime(file_name_noext, "%Y%m%d_%H%M%S.%f") except: - messagebox.showerror('Input error', - 'The time of the beginning cannot be read from the file name! Either change the name of the file to be in the YYYYMMDD_hhmmss format, or specify the beginning time using command line options.') + qmessagebox(title="Input error", \ + message="The time of the beginning cannot be read from the file name! Either change the name of the file to be in the YYYYMMDD_hhmmss format, or specify the beginning time using command line options.") sys.exit() @@ -1245,8 +1239,8 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa break if len(self.img_list) == 0: - messagebox.showerror('Input error', - "Can't find any images in the given directory! Only PNG, JPG and BMP are supported!") + qmessagebox(title='Input error', + message="Can't find any images in the given directory! Only PNG, JPG and BMP are supported!") sys.exit() ### ### @@ -1346,8 +1340,8 @@ def __init__(self, dir_path, config, beginning_time=None, fps=None, detection=Fa "%Y%m%d_%H%M%S.%f") except: - messagebox.showerror('Input error', - 'The time of the beginning cannot be read from the file name! Either change the name of the file to be in the YYYYMMDD_hhmmss format, or specify the beginning time using command line options.') + qmessagebox(title='Input error', + message='The time of the beginning cannot be read from the file name! Either change the name of the file to be in the YYYYMMDD_hhmmss format, or specify the beginning time using command line options.') sys.exit() else: @@ -1867,8 +1861,8 @@ def __init__(self, file_path, config, beginning_time=None, fps=None): self.beginning_datetime = beginning_datetime except: - messagebox.showerror('Input error', - "Can't parse given DFN file name!") + qmessagebox(title='Input error', \ + message="Can't parse given DFN file name!") sys.exit() print('Using folder:', self.dir_path) @@ -2053,8 +2047,8 @@ def detectInputTypeFolder(input_dir, config, beginning_time=None, fps=None, skip if skip_ff_dir: - messagebox.showinfo('FF directory', - 'ManualReduction only works on individual FF files, and not directories with FF files!') + qmessagebox(title='FF directory', + message='ManualReduction only works on individual FF files, and not directories with FF files!') return None else: @@ -2118,8 +2112,8 @@ def detectInputTypeFile(input_file, config, beginning_time=None, fps=None, detec img_handle = InputTypeDFN(input_file, config, beginning_time=beginning_time, fps=fps) else: - messagebox.showerror(title='Input format error', - message='Couldn\'t find the file type given') + qmessagebox(title="Input format error", + message="Couldn\'t find the file type given") return None return img_handle diff --git a/RMS/Routines/CustomPyqtgraphClasses.py b/RMS/Routines/CustomPyqtgraphClasses.py index 9b1c399da..4ec080309 100644 --- a/RMS/Routines/CustomPyqtgraphClasses.py +++ b/RMS/Routines/CustomPyqtgraphClasses.py @@ -14,6 +14,21 @@ import sys + +def qmessagebox(message="", title="Error", message_type="warning"): + msg = QtGui.QMessageBox() + if message_type == "warning": + msg.setIcon(QtGui.QMessageBox.Warning) + elif message_type == "error": + msg.setIcon(QtGui.QMessageBox.Critical) + else: + msg.setIcon(QtGui.QMessageBox.Information) + msg.setText(message) + msg.setWindowTitle(title) + msg.setStandardButtons(QtGui.QMessageBox.Ok) + msg.exec_() + + class Plus(QtGui.QPainterPath): """ Used as a symbol for ScatterPlotItem diff --git a/Utils/SkyFit2.py b/Utils/SkyFit2.py index e5e80288b..beec49f40 100644 --- a/Utils/SkyFit2.py +++ b/Utils/SkyFit2.py @@ -40,20 +40,6 @@ from RMS.Astrometry.CyFunctions import subsetCatalog, equatorialCoordPrecession -def qmessagebox(message="", title="Error", message_type="warning"): - msg = QtGui.QMessageBox() - if message_type == "warning": - msg.setIcon(QtGui.QMessageBox.Warning) - elif message_type == "error": - msg.setIcon(QtGui.QMessageBox.Critical) - else: - msg.setIcon(QtGui.QMessageBox.Information) - msg.setText(message) - msg.setWindowTitle(title) - msg.setStandardButtons(QtGui.QMessageBox.Ok) - msg.exec_() - - class QFOVinputDialog(QtWidgets.QDialog): def __init__(self, *args, **kwargs): From 599c5dc16d877415b108ddf6540057f58153ed2a Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Thu, 22 Apr 2021 15:03:29 -0400 Subject: [PATCH 08/13] fixed fripon data input, fixed grid and star filtering outside FOV --- RMS/Astrometry/ApplyAstrometry.py | 103 +++++++++++++++++++++++++++--- RMS/Astrometry/CheckFit.py | 12 +--- RMS/Astrometry/FFTalign.py | 2 +- RMS/Astrometry/SkyFit.py | 6 +- RMS/Formats/FrameInterface.py | 8 ++- RMS/Formats/Platepar.py | 3 +- RMS/Math.py | 16 ++--- RMS/Routines/AddCelestialGrid.py | 25 ++++---- RMS/Routines/DebruijnSequence.py | 7 +- Utils/CalibrationReport.py | 5 +- Utils/SkyFit2.py | 5 +- 11 files changed, 136 insertions(+), 56 deletions(-) diff --git a/RMS/Astrometry/ApplyAstrometry.py b/RMS/Astrometry/ApplyAstrometry.py index 19ab5f8fc..d337320b5 100644 --- a/RMS/Astrometry/ApplyAstrometry.py +++ b/RMS/Astrometry/ApplyAstrometry.py @@ -41,7 +41,7 @@ from RMS.Formats.FTPdetectinfo import readFTPdetectinfo, writeFTPdetectinfo from RMS.Formats.FFfile import filenameToDatetime import RMS.Formats.Platepar -from RMS.Math import angularSeparation +from RMS.Math import angularSeparation, polarToCartesian, cartesianToPolar # Import Cython functions import pyximport @@ -294,6 +294,47 @@ def photometryFitRobust(px_intens_list, radius_list, catalog_mags, fixed_vignett +def checkFOVLimits(platepar): + """ Check if the FOV is larger than 180 deg in any direction. """ + + # Construct poinits on the middle of every side of the image + x_data = np.array([ 0, platepar.X_res, platepar.X_res/2, platepar.X_res/2, platepar.X_res/2.0]) + y_data = np.array([platepar.Y_res/2, platepar.Y_res/2, 0, platepar.Y_res, platepar.Y_res/2.0]) + time_data = np.array(len(x_data)*[jd2Date(platepar.JD)]) + level_data = np.ones(len(x_data)) + + # Compute RA/Dec of the points + _, ra_data, dec_data, _ = xyToRaDecPP(time_data, x_data, y_data, level_data, platepar, \ + extinction_correction=False) + + ra1, ra2, ra3, ra4, ra_mid = ra_data + dec1, dec2, dec3, dec4, dec_mid = dec_data + + + # Check if the mean coordinates are close the the centre of the FOV, or opposite + x1, y1, z1 = polarToCartesian(np.radians(ra1), np.radians(dec1)) + x2, y2, z2 = polarToCartesian(np.radians(ra2), np.radians(dec2)) + x3, y3, z3 = polarToCartesian(np.radians(ra3), np.radians(dec3)) + x4, y4, z4 = polarToCartesian(np.radians(ra4), np.radians(dec4)) + + x_avg = np.mean([x1, x2, x3, x4]) + y_avg = np.mean([y1, y2, y3, y4]) + z_avg = np.mean([z1, z2, z3, z4]) + + dec_avg, ra_avg = cartesianToPolar(x_avg, y_avg, z_avg) + + # Angular separation between middle coordiantes and the centre of the FOV + avg_separation = np.degrees(angularSeparation(ra_avg, dec_avg, np.radians(ra_mid), np.radians(dec_mid))) + + # If the average separation is larger than 90 deg, flip the FOV radius + if avg_separation > 45: + return True + + else: + return False + + + def computeFOVSize(platepar): """ Computes the size of the FOV in deg from the given platepar. @@ -305,16 +346,17 @@ def computeFOVSize(platepar): """ # Construct poinits on the middle of every side of the image - time_data = np.array(4*[jd2Date(platepar.JD)]) - x_data = np.array([0, platepar.X_res, platepar.X_res/2, platepar.X_res/2]) - y_data = np.array([platepar.Y_res/2, platepar.Y_res/2, 0, platepar.Y_res]) - level_data = np.ones(4) + x_data = np.array([ 0, platepar.X_res, platepar.X_res/2, platepar.X_res/2, platepar.X_res/2.0]) + y_data = np.array([platepar.Y_res/2, platepar.Y_res/2, 0, platepar.Y_res, platepar.Y_res/2.0]) + time_data = np.array(len(x_data)*[jd2Date(platepar.JD)]) + level_data = np.ones(len(x_data)) # Compute RA/Dec of the points - _, ra_data, dec_data, _ = xyToRaDecPP(time_data, x_data, y_data, level_data, platepar) + _, ra_data, dec_data, _ = xyToRaDecPP(time_data, x_data, y_data, level_data, platepar, \ + extinction_correction=False) - ra1, ra2, ra3, ra4 = ra_data - dec1, dec2, dec3, dec4 = dec_data + ra1, ra2, ra3, ra4, ra_mid = ra_data + dec1, dec2, dec3, dec4, dec_mid = dec_data # Compute horizontal FOV fov_h = np.degrees(angularSeparation(np.radians(ra1), np.radians(dec1), np.radians(ra2), \ @@ -325,10 +367,55 @@ def computeFOVSize(platepar): np.radians(dec4))) + # If the average separation is larger than 90 deg, flip the FOV radius + if checkFOVLimits(platepar): + fov_h = 360 - fov_h + fov_v = 360 - fov_v + + return fov_h, fov_v +def getFOVSelectionRadius(platepar): + """ Get a radius around the centre of the FOV which includes the FOV, but excludes stars outside the FOV. + Arguments: + platepar: [Platepar instance] + + Return: + fov_radius: [float] Radius in degrees. + """ + + + # Construct points in every corner + x_data = np.array([0, platepar.X_res, platepar.X_res, 0, platepar.X_res/2.0]) + y_data = np.array([0, platepar.Y_res, 0, platepar.Y_res, platepar.Y_res/2.0]) + time_data = np.array(len(x_data)*[jd2Date(platepar.JD)]) + level_data = np.ones(len(x_data)) + + # Compute RA/Dec of the points + _, ra_data, dec_data, _ = xyToRaDecPP(time_data, x_data, y_data, level_data, platepar, \ + extinction_correction=False) + + ra1, ra2, ra3, ra4, ra_mid = ra_data + dec1, dec2, dec3, dec4, dec_mid = dec_data + + # Compute diagonals + fov_diag1 = np.degrees(angularSeparation(np.radians(ra1), np.radians(dec1), np.radians(ra2), \ + np.radians(dec2))) + fov_diag2 = np.degrees(angularSeparation(np.radians(ra3), np.radians(dec3), np.radians(ra4), \ + np.radians(dec4))) + + # Compute FOV radius + fov_radius = np.mean([fov_diag1, fov_diag2])/2.0 + + # Check if the angle computed is on the short side if all-sky cameras are used, and correct the radius + if checkFOVLimits(platepar): + fov_radius = 360 - fov_radius + + return fov_radius + + def rotationWrtHorizon(platepar): """ Given the platepar, compute the rotation of the FOV with respect to the horizon. diff --git a/RMS/Astrometry/CheckFit.py b/RMS/Astrometry/CheckFit.py index f59832b31..afdc15d19 100644 --- a/RMS/Astrometry/CheckFit.py +++ b/RMS/Astrometry/CheckFit.py @@ -20,7 +20,7 @@ from RMS.Formats import CALSTARS from RMS.Formats import StarCatalog from RMS.Formats import FFfile -from RMS.Astrometry.ApplyAstrometry import raDecToXYPP, xyToRaDecPP, rotationWrtHorizon +from RMS.Astrometry.ApplyAstrometry import raDecToXYPP, xyToRaDecPP, rotationWrtHorizon, getFOVSelectionRadius from RMS.Astrometry.Conversions import date2JD, jd2Date, raDec2AltAz from RMS.Astrometry.FFTalign import alignPlatepar from RMS.Math import angularSeparation @@ -78,15 +78,7 @@ def matchStarsResiduals(config, platepar, catalog_stars, star_dict, match_radius # Estimate the FOV radius - fov_w = platepar.X_res/platepar.F_scale - fov_h = platepar.Y_res/platepar.F_scale - - fov_radius = np.sqrt((fov_w/2)**2 + (fov_h/2)**2) - - # print('fscale', platepar.F_scale) - # print('FOV w:', fov_w) - # print('FOV h:', fov_h) - # print('FOV radius:', fov_radius) + fov_radius = getFOVSelectionRadius(platepar) # Dictionary containing the matched stars, the keys are JDs of every image diff --git a/RMS/Astrometry/FFTalign.py b/RMS/Astrometry/FFTalign.py index 25a2a7ac2..5413a279c 100644 --- a/RMS/Astrometry/FFTalign.py +++ b/RMS/Astrometry/FFTalign.py @@ -238,7 +238,7 @@ def alignPlatepar(config, platepar, calstars_time, calstars_coords, scale_update # Calculate the FOV radius in degrees fov_y, fov_x = ApplyAstrometry.computeFOVSize(platepar) - fov_radius = np.sqrt(fov_x**2 + fov_y**2) + fov_radius = ApplyAstrometry.getFOVSelectionRadius(platepar) # Take only those stars which are inside the FOV filtered_indices, _ = subsetCatalog(catalog_stars, ra_centre, dec_centre, jd, platepar.lat, \ diff --git a/RMS/Astrometry/SkyFit.py b/RMS/Astrometry/SkyFit.py index c25129fb2..15ada64a3 100644 --- a/RMS/Astrometry/SkyFit.py +++ b/RMS/Astrometry/SkyFit.py @@ -52,7 +52,8 @@ from RMS.Astrometry.ApplyAstrometry import xyToRaDecPP, raDecToXYPP, \ rotationWrtHorizon, rotationWrtHorizonToPosAngle, computeFOVSize, photomLine, photometryFit, \ - rotationWrtStandard, rotationWrtStandardToPosAngle, correctVignetting, extinctionCorrectionTrueToApparent + rotationWrtStandard, rotationWrtStandardToPosAngle, correctVignetting, extinctionCorrectionTrueToApparent, + getFOVSelectionRadius from RMS.Astrometry.AstrometryNetNova import novaAstrometryNetSolve from RMS.Astrometry.Conversions import date2JD, JD2HourAngle import RMS.ConfigReader as cr @@ -2222,8 +2223,7 @@ def filterCatalogStarsInsideFOV(self, catalog_stars): ra_centre, dec_centre = self.computeCentreRADec() # Calculate the FOV radius in degrees - fov_y, fov_x = computeFOVSize(self.platepar) - fov_radius = np.sqrt(fov_x**2 + fov_y**2) + fov_radius = getFOVSelectionRadius(self.platepar) # Compute the current Julian date jd = date2JD(*self.img_handle.currentTime()) diff --git a/RMS/Formats/FrameInterface.py b/RMS/Formats/FrameInterface.py index 6fc96fe00..b5eb7d28b 100644 --- a/RMS/Formats/FrameInterface.py +++ b/RMS/Formats/FrameInterface.py @@ -1827,12 +1827,12 @@ def __init__(self, file_path, config, beginning_time=None, fps=None): """ self.input_type = 'dfn' - # Set the frames to a global shutter, so no correction is applied - self.config.deinterlace_order = -2 - self.dir_path, self.image_file = os.path.split(file_path) self.config = config + # Set the frames to a global shutter, so no correction is applied + self.config.deinterlace_order = -2 + # This type of input probably won't have any calstars files self.require_calstars = False @@ -1860,6 +1860,7 @@ def __init__(self, file_path, config, beginning_time=None, fps=None): "%Y-%m-%d_%H%M%S") self.beginning_datetime = beginning_datetime + except: qmessagebox(title='Input error', \ message="Can't parse given DFN file name!") @@ -1902,6 +1903,7 @@ def __init__(self, file_path, config, beginning_time=None, fps=None): self.fps = fps print('Using FPS:', self.fps) + def loadImage(self): # Load the NEF file diff --git a/RMS/Formats/Platepar.py b/RMS/Formats/Platepar.py index f475b0b93..f1356a4e0 100644 --- a/RMS/Formats/Platepar.py +++ b/RMS/Formats/Platepar.py @@ -39,7 +39,7 @@ from RMS.Astrometry.Conversions import date2JD, jd2Date, trueRaDec2ApparentAltAz import RMS.Astrometry.ApplyAstrometry -from RMS.Math import angularSeparation +from RMS.Math import angularSeparation, sphericalPointFromHeadingAndDistance # Import Cython functions import pyximport @@ -564,7 +564,6 @@ def _calcImageResidualsDistortion(params, platepar, jd, catalog_stars, img_stars dist_sum = np.sum((catalog_x - img_x)**2 + (catalog_y - img_y)**2) - return dist_sum diff --git a/RMS/Math.py b/RMS/Math.py index b589e52e0..610c1b035 100644 --- a/RMS/Math.py +++ b/RMS/Math.py @@ -108,8 +108,8 @@ def polarToCartesian(theta, phi): """ Converts 3D spherical coordinates to 3D cartesian coordinates. Arguments: - theta: [float] Inclination in radians. - phi: [float] Azimuth angle in radians. + theta: [float] Longitude in radians. + phi: [float] Latitude in radians. Return: (x, y, z): [tuple of floats] Coordinates of the point in 3D cartiesian coordinates. @@ -124,7 +124,7 @@ def polarToCartesian(theta, phi): def isAngleBetween(left, ang, right): - """ Checks if ang is between the angle on the left anf right. + """ Checks if ang is between the angle on the left and right. Arguments: left: [float] Left (counter-clockwise) angle (radians). @@ -174,12 +174,10 @@ def sphericalPointFromHeadingAndDistance(ra1, dec1, heading, distance): # Compute the new declination dec = np.arcsin(np.sin(dec1)*np.cos(distance) + np.cos(dec1)*np.sin(distance)*np.cos(heading)) - # Handle poles and compute right ascension - if np.cos(dec) == 0: - ra = ra1 - - else: - ra = (ra1 - np.arcsin(np.sin(heading)*np.sin(distance)/np.cos(dec)) + np.pi)% (2*np.pi) - np.pi + # Compute the new RA + dra = np.arctan2(np.sin(heading)*np.sin(distance)*np.cos(dec1), np.cos(distance) \ + - np.sin(dec1)*np.sin(dec)) + ra = (ra1 - dra + np.pi)%(2*np.pi) - np.pi return np.degrees(ra)%360, np.degrees(dec) diff --git a/RMS/Routines/AddCelestialGrid.py b/RMS/Routines/AddCelestialGrid.py index 090fbc55f..9cb870a42 100644 --- a/RMS/Routines/AddCelestialGrid.py +++ b/RMS/Routines/AddCelestialGrid.py @@ -4,7 +4,7 @@ import numpy as np -from RMS.Astrometry.ApplyAstrometry import xyToRaDecPP, raDecToXYPP, computeFOVSize +from RMS.Astrometry.ApplyAstrometry import xyToRaDecPP, raDecToXYPP, computeFOVSize, getFOVSelectionRadius from RMS.Astrometry.Conversions import jd2Date, apparentAltAz2TrueRADec, trueRaDec2ApparentAltAz from RMS.Math import angularSeparation @@ -31,12 +31,11 @@ def addEquatorialGrid(plt_handle, platepar, jd): azim_centre, alt_centre = trueRaDec2ApparentAltAz(RA_c, dec_c, jd, platepar.lat, platepar.lon) # Compute FOV size - fov_h, fov_v = computeFOVSize(platepar) - fov_radius = np.hypot(*computeFOVSize(platepar)) + fov_radius = getFOVSelectionRadius(platepar) # Determine gridline frequency (double the gridlines if the number is < 4eN) - grid_freq = 10**np.floor(np.log10(fov_radius)) - if 10**(np.log10(fov_radius) - np.floor(np.log10(fov_radius))) < 4: + grid_freq = 10**np.floor(np.log10(2*fov_radius)) + if 10**(np.log10(2*fov_radius) - np.floor(np.log10(2*fov_radius))) < 4: grid_freq /= 2 # Set a maximum grid frequency of 15 deg @@ -48,11 +47,11 @@ def addEquatorialGrid(plt_handle, platepar, jd): plot_dens = grid_freq/100 # Compute the range of declinations to consider - dec_min = platepar.dec_d - fov_radius/2 + dec_min = platepar.dec_d - fov_radius if dec_min < -90: dec_min = -90 - dec_max = platepar.dec_d + fov_radius/2 + dec_max = platepar.dec_d + fov_radius if dec_max > 90: dec_max = 90 @@ -183,12 +182,12 @@ def updateRaDecGrid(grid, platepar): # Compute FOV size - fov_radius = np.hypot(*computeFOVSize(platepar)) + fov_radius = getFOVSelectionRadius(platepar) # Determine gridline frequency (double the gridlines if the number is < 4eN) - grid_freq = 10**np.floor(np.log10(fov_radius)) - if 10**(np.log10(fov_radius) - np.floor(np.log10(fov_radius))) < 4: + grid_freq = 10**np.floor(np.log10(2*fov_radius)) + if 10**(np.log10(2*fov_radius) - np.floor(np.log10(2*fov_radius))) < 4: grid_freq /= 2 # Set a maximum grid frequency of 15 deg @@ -335,12 +334,12 @@ def updateAzAltGrid(grid, platepar): ### ### # Compute FOV size - fov_radius = np.hypot(*computeFOVSize(platepar)) + fov_radius = getFOVSelectionRadius(platepar) # Determine gridline frequency (double the gridlines if the number is < 4eN) - grid_freq = 10**np.floor(np.log10(fov_radius)) - if 10**(np.log10(fov_radius) - np.floor(np.log10(fov_radius))) < 4: + grid_freq = 10**np.floor(np.log10(2*fov_radius)) + if 10**(np.log10(2*fov_radius) - np.floor(np.log10(2*fov_radius))) < 4: grid_freq /= 2 # Set a maximum grid frequency of 15 deg diff --git a/RMS/Routines/DebruijnSequence.py b/RMS/Routines/DebruijnSequence.py index 4c0f4c390..1665268a2 100644 --- a/RMS/Routines/DebruijnSequence.py +++ b/RMS/Routines/DebruijnSequence.py @@ -103,9 +103,12 @@ def generateDeBruijnSequence(k, n): if __name__ == '__main__': - sequence = generateDeBruijnSequence(2, 4) + + + sequence = generateDeBruijnSequence(2, 9) print(''.join([str(x) for x in sequence])) - print(''.join([str(x) for x in sequence[::-1]])) + + #print(''.join([str(x) for x in sequence[::-1]])) # print(cyclicSubsequence([1, 2, None, 4, None, 5], # [5, 1, 2, 1, 4, 1], unknowns=True)) diff --git a/Utils/CalibrationReport.py b/Utils/CalibrationReport.py index bff195fdc..e595173c5 100644 --- a/Utils/CalibrationReport.py +++ b/Utils/CalibrationReport.py @@ -10,7 +10,8 @@ import matplotlib.pyplot as plt from RMS.Astrometry.ApplyAstrometry import computeFOVSize, xyToRaDecPP, raDecToXYPP, \ - photometryFitRobust, correctVignetting, photomLine, rotationWrtHorizon, extinctionCorrectionTrueToApparent + photometryFitRobust, correctVignetting, photomLine, rotationWrtHorizon, \ + extinctionCorrectionTrueToApparent, getFOVSelectionRadius from RMS.Astrometry.CheckFit import matchStarsResiduals from RMS.Astrometry.Conversions import date2JD, jd2Date, raDec2AltAz from RMS.Formats.CALSTARS import readCALSTARS @@ -352,7 +353,7 @@ def generateCalibrationReport(config, night_dir_path, match_radius=2.0, platepar RA_c = RA_c[0] dec_c = dec_c[0] - fov_radius = np.hypot(*computeFOVSize(platepar)) + fov_radius = getFOVSelectionRadius(platepar) # Get stars from the catalog around the defined center in a given radius _, extracted_catalog = subsetCatalog(catalog_stars, RA_c, dec_c, max_jd, platepar.lat, platepar.lon, \ diff --git a/Utils/SkyFit2.py b/Utils/SkyFit2.py index beec49f40..d801e1db7 100644 --- a/Utils/SkyFit2.py +++ b/Utils/SkyFit2.py @@ -17,7 +17,7 @@ from RMS.Astrometry.ApplyAstrometry import xyToRaDecPP, raDecToXYPP, \ rotationWrtHorizon, rotationWrtHorizonToPosAngle, computeFOVSize, photomLine, photometryFit, \ rotationWrtStandard, rotationWrtStandardToPosAngle, correctVignetting, \ - extinctionCorrectionTrueToApparent, applyAstrometryFTPdetectinfo + extinctionCorrectionTrueToApparent, applyAstrometryFTPdetectinfo, getFOVSelectionRadius from RMS.Astrometry.Conversions import date2JD, JD2HourAngle, trueRaDec2ApparentAltAz, raDec2AltAz, \ apparentAltAz2TrueRADec, J2000_JD, jd2Date, datetime2JD, JD2LST, geo2Cartesian, vector2RaDec, raDec2Vector from RMS.Astrometry.AstrometryNetNova import novaAstrometryNetSolve @@ -3296,8 +3296,7 @@ def filterCatalogStarsInsideFOV(self, catalog_stars, remove_under_horizon=True): ra_centre, dec_centre = self.computeCentreRADec() # Calculate the FOV radius in degrees - fov_y, fov_x = computeFOVSize(self.platepar) - fov_radius = np.sqrt(fov_x**2 + fov_y**2) + fov_radius = getFOVSelectionRadius(self.platepar) # Compute the current Julian date jd = date2JD(*self.img_handle.currentTime()) From 9c7518601a6a935da30e8f8716e7cc592542c328 Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Fri, 23 Apr 2021 09:47:08 -0400 Subject: [PATCH 09/13] fixed computing fov size and radius --- RMS/Astrometry/ApplyAstrometry.py | 84 +++++++------------------------ 1 file changed, 18 insertions(+), 66 deletions(-) diff --git a/RMS/Astrometry/ApplyAstrometry.py b/RMS/Astrometry/ApplyAstrometry.py index d337320b5..cef8e46fa 100644 --- a/RMS/Astrometry/ApplyAstrometry.py +++ b/RMS/Astrometry/ApplyAstrometry.py @@ -293,48 +293,6 @@ def photometryFitRobust(px_intens_list, radius_list, catalog_mags, fixed_vignett - -def checkFOVLimits(platepar): - """ Check if the FOV is larger than 180 deg in any direction. """ - - # Construct poinits on the middle of every side of the image - x_data = np.array([ 0, platepar.X_res, platepar.X_res/2, platepar.X_res/2, platepar.X_res/2.0]) - y_data = np.array([platepar.Y_res/2, platepar.Y_res/2, 0, platepar.Y_res, platepar.Y_res/2.0]) - time_data = np.array(len(x_data)*[jd2Date(platepar.JD)]) - level_data = np.ones(len(x_data)) - - # Compute RA/Dec of the points - _, ra_data, dec_data, _ = xyToRaDecPP(time_data, x_data, y_data, level_data, platepar, \ - extinction_correction=False) - - ra1, ra2, ra3, ra4, ra_mid = ra_data - dec1, dec2, dec3, dec4, dec_mid = dec_data - - - # Check if the mean coordinates are close the the centre of the FOV, or opposite - x1, y1, z1 = polarToCartesian(np.radians(ra1), np.radians(dec1)) - x2, y2, z2 = polarToCartesian(np.radians(ra2), np.radians(dec2)) - x3, y3, z3 = polarToCartesian(np.radians(ra3), np.radians(dec3)) - x4, y4, z4 = polarToCartesian(np.radians(ra4), np.radians(dec4)) - - x_avg = np.mean([x1, x2, x3, x4]) - y_avg = np.mean([y1, y2, y3, y4]) - z_avg = np.mean([z1, z2, z3, z4]) - - dec_avg, ra_avg = cartesianToPolar(x_avg, y_avg, z_avg) - - # Angular separation between middle coordiantes and the centre of the FOV - avg_separation = np.degrees(angularSeparation(ra_avg, dec_avg, np.radians(ra_mid), np.radians(dec_mid))) - - # If the average separation is larger than 90 deg, flip the FOV radius - if avg_separation > 45: - return True - - else: - return False - - - def computeFOVSize(platepar): """ Computes the size of the FOV in deg from the given platepar. @@ -359,19 +317,18 @@ def computeFOVSize(platepar): dec1, dec2, dec3, dec4, dec_mid = dec_data # Compute horizontal FOV - fov_h = np.degrees(angularSeparation(np.radians(ra1), np.radians(dec1), np.radians(ra2), \ - np.radians(dec2))) + fov_hl = np.degrees(angularSeparation(np.radians(ra1), np.radians(dec1), np.radians(ra_mid), \ + np.radians(dec_mid))) + fov_hr = np.degrees(angularSeparation(np.radians(ra2), np.radians(dec2), np.radians(ra_mid), \ + np.radians(dec_mid))) + fov_h = fov_hl + fov_hr # Compute vertical FOV - fov_v = np.degrees(angularSeparation(np.radians(ra3), np.radians(dec3), np.radians(ra4), \ - np.radians(dec4))) - - - # If the average separation is larger than 90 deg, flip the FOV radius - if checkFOVLimits(platepar): - fov_h = 360 - fov_h - fov_v = 360 - fov_v - + fov_vu = np.degrees(angularSeparation(np.radians(ra3), np.radians(dec3), np.radians(ra_mid), \ + np.radians(dec_mid))) + fov_vd = np.degrees(angularSeparation(np.radians(ra4), np.radians(dec4), np.radians(ra_mid), \ + np.radians(dec_mid))) + fov_v = fov_vu + fov_vd return fov_h, fov_v @@ -386,8 +343,7 @@ def getFOVSelectionRadius(platepar): fov_radius: [float] Radius in degrees. """ - - # Construct points in every corner + # Construct poinits on the middle of every side of the image x_data = np.array([0, platepar.X_res, platepar.X_res, 0, platepar.X_res/2.0]) y_data = np.array([0, platepar.Y_res, 0, platepar.Y_res, platepar.Y_res/2.0]) time_data = np.array(len(x_data)*[jd2Date(platepar.JD)]) @@ -400,18 +356,14 @@ def getFOVSelectionRadius(platepar): ra1, ra2, ra3, ra4, ra_mid = ra_data dec1, dec2, dec3, dec4, dec_mid = dec_data - # Compute diagonals - fov_diag1 = np.degrees(angularSeparation(np.radians(ra1), np.radians(dec1), np.radians(ra2), \ - np.radians(dec2))) - fov_diag2 = np.degrees(angularSeparation(np.radians(ra3), np.radians(dec3), np.radians(ra4), \ - np.radians(dec4))) - - # Compute FOV radius - fov_radius = np.mean([fov_diag1, fov_diag2])/2.0 + # Angular separation between the centre of the FOV and corners + ul_sep = np.degrees(angularSeparation(np.radians(ra1), np.radians(dec1), np.radians(ra_mid), np.radians(dec_mid))) + lr_sep = np.degrees(angularSeparation(np.radians(ra2), np.radians(dec2), np.radians(ra_mid), np.radians(dec_mid))) + ur_sep = np.degrees(angularSeparation(np.radians(ra3), np.radians(dec3), np.radians(ra_mid), np.radians(dec_mid))) + ll_sep = np.degrees(angularSeparation(np.radians(ra4), np.radians(dec4), np.radians(ra_mid), np.radians(dec_mid))) - # Check if the angle computed is on the short side if all-sky cameras are used, and correct the radius - if checkFOVLimits(platepar): - fov_radius = 360 - fov_radius + # Take the average radius + fov_radius = np.mean([ul_sep, lr_sep, ur_sep, ll_sep]) return fov_radius From b47273f669f228505243e83e582b576f0342ac9e Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Sat, 24 Apr 2021 17:11:35 -0400 Subject: [PATCH 10/13] fixed astrometry.net --- RMS/Astrometry/AstrometryNetNova.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RMS/Astrometry/AstrometryNetNova.py b/RMS/Astrometry/AstrometryNetNova.py index 0fe61d73f..1816e95bf 100644 --- a/RMS/Astrometry/AstrometryNetNova.py +++ b/RMS/Astrometry/AstrometryNetNova.py @@ -355,7 +355,7 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None # Save the avepixel as a memory file file_handle = BytesIO() - pil_img = Image.fromarray(img) + pil_img = Image.fromarray(img.T) # Save image to memory as JPG pil_img.save(file_handle, format='JPEG') @@ -376,7 +376,7 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None # Add keyword arguments kwargs = {} - kwargs['publicly_visible'] = 'n' + kwargs['publicly_visible'] = 'y' kwargs['crpix_center'] = True kwargs['tweak_order'] = 3 From e6be7c92ede362a9c4058a80c9393f0febac226a Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Sat, 24 Apr 2021 21:49:55 -0400 Subject: [PATCH 11/13] fixed astrometry.net code --- RMS/Astrometry/AstrometryNetNova.py | 7 ++++++- Utils/SkyFit2.py | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/RMS/Astrometry/AstrometryNetNova.py b/RMS/Astrometry/AstrometryNetNova.py index 1816e95bf..afc1be923 100644 --- a/RMS/Astrometry/AstrometryNetNova.py +++ b/RMS/Astrometry/AstrometryNetNova.py @@ -418,8 +418,9 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None tries = 0 while True: - # Limit the number of checking if the fiels is solved, so the script does not get stuck + # Limit the number of checking if the field is solved, so the script does not get stuck if tries > solution_tries: + print("Link to web page: http://nova.astrometry.net/user_images/{:d}".format(stat.get("user_images", "")[0])) return None stat = c.sub_status(sub_id, justdict=True) @@ -455,6 +456,7 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None if solution_tries > get_solution_tries: print('Waiting too long for the solution!') + print("Link to web page: http://nova.astrometry.net/user_images/{:d}".format(stat.get("user_images", "")[0])) return None # Get the job status @@ -470,6 +472,9 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None elif stat.get('status','') in ['failure']: print('Failed to find a solution!') + + print("Link to web page: http://nova.astrometry.net/user_images/{:d}".format(stat.get("user_images", "")[0])) + return None # Wait until the job is solved diff --git a/Utils/SkyFit2.py b/Utils/SkyFit2.py index d801e1db7..d1be0a426 100644 --- a/Utils/SkyFit2.py +++ b/Utils/SkyFit2.py @@ -3373,7 +3373,11 @@ def getInitialParamsAstrometryNet(self, upload_image=True): jd = date2JD(*self.img_handle.currentTime()) # Compute the position angle from the orientation - pos_angle_ref = rotationWrtStandardToPosAngle(self.platepar, orientation) + #pos_angle_ref = rotationWrtStandardToPosAngle(self.platepar, orientation) + + # Assume zero rotation wrt horizon + orientation = 0.0 + pos_angle_ref = rotationWrtHorizonToPosAngle(self.platepar, orientation) # Compute reference azimuth and altitude azim, alt = trueRaDec2ApparentAltAz(ra, dec, jd, self.platepar.lat, self.platepar.lon) @@ -3389,6 +3393,9 @@ def getInitialParamsAstrometryNet(self, upload_image=True): # Save the current rotation w.r.t horizon value self.platepar.rotation_from_horiz = rotationWrtHorizon(self.platepar) + # Reset the distortion parameters + self.platepar.resetDistortionParameters() + # Print estimated parameters print() print('Astrometry.net solution:') From fecf78eed124e7c609a7e237d1ac745ee2a0e1b8 Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Sat, 24 Apr 2021 22:12:28 -0400 Subject: [PATCH 12/13] added catches to always print our a web link for astrometry.net --- RMS/Astrometry/AstrometryNetNova.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/RMS/Astrometry/AstrometryNetNova.py b/RMS/Astrometry/AstrometryNetNova.py index afc1be923..07ca2e2d4 100644 --- a/RMS/Astrometry/AstrometryNetNova.py +++ b/RMS/Astrometry/AstrometryNetNova.py @@ -9,6 +9,7 @@ import os import sys +import copy import time import base64 import json @@ -338,6 +339,16 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None """ + def _printWebLink(stat, first_status=None): + + if first_status is not None: + if not len(stat.get("user_images", "")): + stat = first_status + + if len(stat.get("user_images", "")): + print("Link to web page: http://nova.astrometry.net/user_images/{:d}".format(stat.get("user_images", "")[0])) + + # Read the FF file, if given if ff_file_path is not None: @@ -420,7 +431,7 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None # Limit the number of checking if the field is solved, so the script does not get stuck if tries > solution_tries: - print("Link to web page: http://nova.astrometry.net/user_images/{:d}".format(stat.get("user_images", "")[0])) + _printWebLink(stat) return None stat = c.sub_status(sub_id, justdict=True) @@ -442,6 +453,9 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None tries += 1 + + first_status = copy.deepcopy(stat) + # Get results get_results_tries = 10 get_solution_tries = 30 @@ -452,11 +466,12 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None # Limit the number of tries of getting the results, so the script does not get stuck if results_tries > get_results_tries: print('Too many tries in getting the results!') + _printWebLink(stat, first_status=first_status) return None if solution_tries > get_solution_tries: print('Waiting too long for the solution!') - print("Link to web page: http://nova.astrometry.net/user_images/{:d}".format(stat.get("user_images", "")[0])) + _printWebLink(stat, first_status=first_status) return None # Get the job status @@ -473,7 +488,7 @@ def novaAstrometryNetSolve(ff_file_path=None, img=None, x_data=None, y_data=None elif stat.get('status','') in ['failure']: print('Failed to find a solution!') - print("Link to web page: http://nova.astrometry.net/user_images/{:d}".format(stat.get("user_images", "")[0])) + _printWebLink(stat, first_status=first_status) return None From ef319baf1417c43a69cb43a73b00c59d8d515fd7 Mon Sep 17 00:00:00 2001 From: Denis Vida Date: Mon, 26 Apr 2021 15:31:30 -0400 Subject: [PATCH 13/13] fixed orentation from astrometry.net --- Utils/SkyFit2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Utils/SkyFit2.py b/Utils/SkyFit2.py index d1be0a426..17d5911a1 100644 --- a/Utils/SkyFit2.py +++ b/Utils/SkyFit2.py @@ -3384,6 +3384,7 @@ def getInitialParamsAstrometryNet(self, upload_image=True): # Set parameters to platepar self.platepar.pos_angle_ref = pos_angle_ref + self.platepar.rotation_from_horiz = orientation self.platepar.F_scale = scale self.platepar.az_centre = azim self.platepar.alt_centre = alt