def compute_lonlat(self, width, utcs=None, clock_drift_adjust=True): """Compute lat/lon coordinates. Args: width: Number of coordinates per scanlines utcs: Scanline timestamps clock_drift_adjust: If True, adjust clock drift. """ tle1, tle2 = self.get_tle_lines() scan_points = np.arange(3.5, 2048, 5) if utcs is None: utcs = self.get_times() # adjusting clock for drift tic = datetime.datetime.now() if clock_drift_adjust: from pygac.clock_offsets_converter import get_offsets try: offset_times, clock_error = get_offsets(self.spacecraft_name) except KeyError: LOG.info("No clock drift info available for %s", self.spacecraft_name) else: offset_times = np.array(offset_times, dtype='datetime64[ms]') offsets = np.interp(utcs.astype(np.uint64), offset_times.astype(np.uint64), clock_error) utcs -= (offsets * 1000).astype('timedelta64[ms]') t = utcs[0].astype(datetime.datetime) if "constant_yaw_attitude_error" in self.head.dtype.fields: rpy = np.deg2rad([ self.head["constant_roll_attitude_error"] / 1e3, self.head["constant_pitch_attitude_error"] / 1e3, self.head["constant_yaw_attitude_error"] / 1e3 ]) else: rpy = [0, 0, 0] LOG.info("Using rpy: %s", str(rpy)) from pyorbital.geoloc_instrument_definitions import avhrr_gac from pyorbital.geoloc import compute_pixels, get_lonlatalt sgeom = avhrr_gac(utcs.astype(datetime.datetime), scan_points, 55.385) s_times = sgeom.times(t) pixels_pos = compute_pixels((tle1, tle2), sgeom, s_times, rpy) pos_time = get_lonlatalt(pixels_pos, s_times) toc = datetime.datetime.now() LOG.warning("Computation of geolocation: %s", str(toc - tic)) lons, lats = pos_time[:2] return lons.reshape(-1, width), lats.reshape(-1, width)
def _adjust_clock_drift(self): """Adjust the geolocation to compensate for the clock error.""" tic = datetime.datetime.now() self.get_times() try: error_utcs, clock_error = get_offsets(self.spacecraft_name) except KeyError: LOG.info("No clock drift info available for %s", self.spacecraft_name) return error_utcs = np.array(error_utcs, dtype='datetime64[ms]') # interpolate to get the clock offsets at the scan line utcs # the clock_error is given in seconds, so offsets are in seconds, too. offsets = np.interp(self.utcs.astype(np.uint64), error_utcs.astype(np.uint64), clock_error) LOG.info("Adjusting for clock drift of %s to %s seconds.", str(min(offsets)), str(max(offsets))) # For the interpolation of geolocations, we need to prepend/append # lines. The conversion from offset to line numbers is given by the # scan rate = 1/scan frequency. scan_rate = datetime.timedelta(milliseconds=1/self.scan_freq) offset_lines = offsets / scan_rate.total_seconds() # To avoid trouble with missing lines, we will construct an # array that covers the whole interpolation range. scan_lines = self.scans["scan_line_number"] shifted_lines = scan_lines - offset_lines shifted_lines_floor = np.floor(shifted_lines).astype(int) # compute the line range, note that the max requires a "+1" # to cover the interpolation range because of the floor. min_line = min(scan_lines.min(), shifted_lines_floor.min()) max_line = max(scan_lines.max(), shifted_lines_floor.max()+1) num_lines = max_line - min_line + 1 missed_lines = np.setdiff1d(np.arange(min_line, max_line+1), scan_lines) missed_utcs = ((missed_lines - scan_lines[0])*np.timedelta64(scan_rate, "ms") + self.utcs[0]) # calculate the missing geo locations missed_lons, missed_lats = self._compute_missing_lonlat(missed_utcs) # create arrays of lons and lats for interpolation. The locations # correspond to not yet corrected utcs, i.e. the time difference from # one line to the other should be equal to the scan rate. pixels_per_line = self.lats.shape[1] complete_lons = np.full((num_lines, pixels_per_line), np.nan, dtype=np.float) complete_lats = np.full((num_lines, pixels_per_line), np.nan, dtype=np.float) complete_lons[scan_lines - min_line] = self.lons complete_lats[scan_lines - min_line] = self.lats complete_lons[missed_lines - min_line] = missed_lons complete_lats[missed_lines - min_line] = missed_lats # perform the slerp interpolation to the corrected utc times slerp_t = shifted_lines - shifted_lines_floor # in [0, 1) slerp_res = slerp(complete_lons[shifted_lines_floor - min_line, :], complete_lats[shifted_lines_floor - min_line, :], complete_lons[shifted_lines_floor - min_line + 1, :], complete_lats[shifted_lines_floor - min_line + 1, :], slerp_t[:, np.newaxis, np.newaxis]) # set corrected values self.lons = slerp_res[:, :, 0] self.lats = slerp_res[:, :, 1] self.utcs -= (offsets * 1000).astype('timedelta64[ms]') toc = datetime.datetime.now() LOG.debug("clock drift adjustment took %s", str(toc - tic))
def adjust_clock_drift(self): """Adjust the geolocation to compensate for the clock error. TODO: bad things might happen when scanlines are skipped. """ tic = datetime.datetime.now() self.get_times() from pygac.clock_offsets_converter import get_offsets try: offset_times, clock_error = get_offsets(self.spacecraft_name) except KeyError: LOG.info("No clock drift info available for %s", self.spacecraft_name) else: offset_times = np.array(offset_times, dtype='datetime64[ms]') offsets = np.interp(self.utcs.astype(np.uint64), offset_times.astype(np.uint64), clock_error) LOG.info("Adjusting for clock drift of %s to %s", str(min(offsets)), str(max(offsets))) self.times = (self.utcs + offsets.astype('timedelta64[s]')).astype( datetime.datetime) offsets *= -2 int_offsets = np.floor(offsets).astype(np.int) # filling out missing geolocations with computation from pyorbital. line_indices = (self.scans["scan_line_number"] + int_offsets) missed = sorted((set(line_indices) | set(line_indices + 1)) - set(self.scans["scan_line_number"])) min_idx = min(line_indices) max_idx = max(max(line_indices), max(self.scans["scan_line_number"] - min_idx)) + 1 idx_len = max_idx - min_idx + 2 complete_lons = np.zeros((idx_len, 409), dtype=np.float) * np.nan complete_lats = np.zeros((idx_len, 409), dtype=np.float) * np.nan complete_lons[self.scans["scan_line_number"] - min_idx] = self.lons complete_lats[self.scans["scan_line_number"] - min_idx] = self.lats missed_utcs = ((np.array(missed) - 1) * np.timedelta64(500, "ms") + self.utcs[0]) mlons, mlats = self.compute_lonlat(missed_utcs, True) complete_lons[missed - min_idx] = mlons complete_lats[missed - min_idx] = mlats from pygac.slerp import slerp off = offsets - np.floor(offsets) res = slerp(complete_lons[line_indices - min_idx, :], complete_lats[line_indices - min_idx, :], complete_lons[line_indices - min_idx + 1, :], complete_lats[line_indices - min_idx + 1, :], off[:, np.newaxis, np.newaxis]) self.lons = res[:, :, 0] self.lats = res[:, :, 1] self.utcs += offsets.astype('timedelta64[s]') toc = datetime.datetime.now() LOG.debug("clock drift adjustment took %s", str(toc - tic))
def compute_lonlat(self, width, utcs=None, clock_drift_adjust=True): """Compute lat/lon coordinates. Args: width: Number of coordinates per scanlines utcs: Scanline timestamps clock_drift_adjust: If True, adjust clock drift. """ if utcs is None: utcs = self.get_times() # adjusting clock for drift tic = datetime.datetime.now() if clock_drift_adjust: from pygac.clock_offsets_converter import get_offsets try: offset_times, clock_error = get_offsets(self.spacecraft_name) except KeyError: LOG.info("No clock drift info available for %s", self.spacecraft_name) else: offset_times = np.array(offset_times, dtype='datetime64[ms]') offsets = np.interp(utcs.astype(np.uint64), offset_times.astype(np.uint64), clock_error) utcs = utcs - (offsets * 1000).astype('timedelta64[ms]') t = utcs[0].astype(datetime.datetime) if "constant_yaw_attitude_error" in self.head.dtype.fields: rpy = np.deg2rad([ self.head["constant_roll_attitude_error"] / 1e3, self.head["constant_pitch_attitude_error"] / 1e3, self.head["constant_yaw_attitude_error"] / 1e3 ]) else: try: # This needs to be checked thoroughly first # rpy_spacecraft = rpy_coeffs[self.spacecraft_name] # rpy = [rpy_spacecraft['roll'], # rpy_spacecraft['pitch'], # rpy_spacecraft['yaw']] # LOG.debug("Using static attitude correction") raise KeyError except KeyError: LOG.debug("Not applying attitude correction") rpy = [0, 0, 0] LOG.info("Using rpy: %s", str(rpy)) from pyorbital.geoloc_instrument_definitions import avhrr_gac from pyorbital.geoloc import compute_pixels, get_lonlatalt # TODO: Are we sure all satellites have this scan width in degrees ? sgeom = avhrr_gac(utcs.astype(datetime.datetime), self.scan_points, 55.385) s_times = sgeom.times(t) tle1, tle2 = self.get_tle_lines() pixels_pos = compute_pixels((tle1, tle2), sgeom, s_times, rpy) pos_time = get_lonlatalt(pixels_pos, s_times) toc = datetime.datetime.now() LOG.warning("Computation of geolocation: %s", str(toc - tic)) lons, lats = pos_time[:2] return lons.reshape(-1, width), lats.reshape(-1, width)