Source code for sofia_redux.scan.custom.sofia.simulation.aircraft
# Licensed under a 3-clause BSD style license - see LICENSE.rst
from abc import ABC
from astropy import constants
from astropy import units
from astropy.units import imperial
import numpy as np
from sofia_redux.scan.configuration.dates import DateRange
from sofia_redux.scan.coordinate_systems.equatorial_coordinates import \
EquatorialCoordinates
from sofia_redux.scan.coordinate_systems.geodetic_coordinates import \
GeodeticCoordinates
from sofia_redux.scan.utilities.utils import safe_sidereal_time
imperial.enable()
ft = units.Unit('ft')
kft = ft * 1000
knot = units.Unit('knot')
second = units.Unit('second')
hourangle = units.Unit('hourangle')
degree = units.Unit('degree')
__all__ = ['AircraftSimulation']
[docs]
class AircraftSimulation(ABC):
def __init__(self):
"""
Initialize a simulated SOFIA aircraft.
The simulated SOFIA contains location (longitude, latitude, altitude)
coordinates, speeds, and times. This is used to model a flight path
and angle of the telescope towards the observed source.
"""
self.start_altitude = 41 * kft
self.end_altitude = 41 * kft
self.heading = None
self.airspeed = 500 * knot
self.ground_speed = None # Calculate via subtracting wind
self.start_utc = None
self.end_utc = None
self.flight_time = None
self.source = None
self.start_location = None
self.end_location = None
self.start_lst = None
self.end_lst = None
self.start_horizontal = None
self.end_horizontal = None
[docs]
def orient_to_source(self):
"""
Orient the aircraft wrt the source.
Alters the heading of the aircraft so the source is at 90 degrees
to the side.
Returns
-------
None
"""
self.heading = self.start_horizontal.az - (90 * degree)
self.heading = self.heading.to('degree')
[docs]
def calculate_end_position(self):
"""
Calculate the end position of the flight using Vicenty's formula.
Returns
-------
None
"""
d = (self.airspeed * self.flight_time).decompose() # distance
mean_height = (self.start_altitude + self.end_altitude) / 2
r = (constants.R_earth + mean_height).decompose() # radius
a = self.heading # bearing
dr = (d / r).decompose().value * units.Unit('radian')
lon_1 = self.start_location.longitude
lat_1 = self.start_location.latitude
lat_2 = np.arcsin(np.sin(lat_1) * np.cos(dr)
+ np.cos(lat_1) * np.sin(dr) * np.cos(a))
lon_2 = lon_1 + np.arctan2(np.sin(a) * np.sin(dr) * np.cos(lat_1),
np.cos(dr) - (np.sin(lat_1)
* np.sin(lat_2)))
self.end_location = GeodeticCoordinates([lon_2, lat_2], unit='degree')
self.end_lst = safe_sidereal_time(
self.end_utc, 'mean', longitude=self.end_location.longitude)
self.end_horizontal = self.source.to_horizontal(
self.end_location, self.end_lst)