Source code for sorts.radar.scans.scan

#!/usr/bin/env python

'''Defines what a radar observation schema is in the form of a class.

'''

#Python standard import
from abc import ABC, abstractmethod


#Third party import
import numpy as np

#Local import
from ... import frames


[docs]class Scan(ABC): '''Encapsulates the observation schema of a radar system, i.e. its "scan". :param str coordinates: The coordinate system used, can be :code:`'azelr'`, :code:`'ned'` or :code:`'enu'`. If `azelr` is used, degrees are assumed. **Pointing function:** The pointing function must follow the following standard: * Take in time in seconds past reference epoch in seconds as first argument. * Take any number of keyword arguments. * It must return the pointing coordinates as an `(3,)`, `(3,n)` or `(3,n,m)` numpy ndarray where `n` is the length of the input time vector and `m` is the number of simultaneous pointing directions. * Units are in meters. * Should be vectorized according to time as the second axis. Example pointing function: .. code-block:: python import numpy as np #TODO **Coordinate systems:** :azelr: Azimuth, Elevation, Range in degrees east of north and above horizon. :ned: Cartesian coordinates in North, East, Down. :enu: Cartesian coordinates in East, North, Up. '''
[docs] def __init__(self, coordinates='enu'): self.coordinates = coordinates.lower()
[docs] def dwell(self, t): '''The current dwell time of the scan. ''' return None
[docs] def min_dwell(self): '''If there are dynamic dwell times, this is the minimum dwell time. Otherwise, returns same as :code:`dwell`. ''' return None
[docs] def cycle(self): '''The cycle time of the scan if applicable. ''' return None
[docs] def copy(self): '''Return a copy of the current instance. ''' raise NotImplementedError()
[docs] def check_dwell_tx(self, tx): '''Checks if the transmitting antenna pulse pattern and coherent integration schema is compatible with the observation schema. Raises an Exception if not. :param sorts.radar.TX tx: The antenna that should perform this scan. ''' time_slice = tx.n_ipp*tx.ipp dwell_time = self.min_dwell() if dwell_time is not None: if time_slice > dwell_time: raise Exception(f'TX time_slice of {time_slice:.2f} s incompatible with minimum dwell time of scan {dwell_time:.2f} s')
[docs] @abstractmethod def pointing(self, t): pass
[docs] def enu_pointing(self, t): '''Returns the instantaneous pointing in East, North, Up (ENU) local coordinates. :param float/numpy.ndarray t: Seconds past a reference epoch to retrieve the pointing at. ''' point = self.pointing(t) if self.coordinates == 'ned': point[2,...] = -point[2,...] elif self.coordinates == 'enu': pass elif self.coordinates == 'azelr': if len(point.shape) == 3: p_ = np.zeros(point.shape, dtype=point.dtype) for ind in range(point.shape[2]): p_[:,:,ind] = frames.sph_to_cart(point[:,:,ind], radians=False) point = p_ del p_ else: point = frames.sph_to_cart(point, radians=False) return point
def _transform_ecef(self, point, ant): if self.coordinates == 'ned': k0 = frames.ned_to_ecef(ant.lat, ant.lon, ant.alt, point, radians=False) elif self.coordinates == 'enu': k0 = frames.enu_to_ecef(ant.lat, ant.lon, ant.alt, point, radians=False) elif self.coordinates == 'azelr': k0 = frames.azel_to_ecef(ant.lat, ant.lon, ant.alt, point[0,...], point[1,...], radians=False) return k0
[docs] def ecef_pointing(self, t, ant): '''Returns the instantaneous WGS84 ECEF pointing direction and the radar geographical location in WGS84 ECEF coordinates. :param float t: Seconds past a reference epoch to retrieve the pointing at. ''' point = self.pointing(t) if len(point.shape) == 3: k0 = np.zeros(point.shape, dtype=point.dtype) for ind in range(point.shape[2]): k0[:,:,ind] = self._transform_ecef(point[:,:,ind], ant) else: k0 = self._transform_ecef(point, ant) return k0