def add_earth_sphere(center=None): """Add a sphere the radius of the Earth to the current figure. :param [float] center: Coordinates to locate the center of the Earth in the plot :return: """ phi = np.linspace(0, 2 * np.pi, 100) theta = np.linspace(0, np.pi, 100) if center is None: center = vt.Vector([0, 0, 0]) x = constant.RADIUS_OF_EARTH * np.outer(np.cos(phi), np.sin(theta)) + center[0] y = constant.RADIUS_OF_EARTH * np.outer(np.sin(phi), np.sin(theta)) + center[1] z = constant.RADIUS_OF_EARTH * np.outer(np.ones(np.size(phi)), np.cos(theta)) + center[2] # earth_image_file_path = os.path.join(os.environ.get('SATSIMDIR'), 'basics', 'earth_map.png') # fn = mpl_cbook.get_sample_data(earth_image_file_path, asfileobj=False) # img = mpl_png.read_png(fn).transpose(1, 0, 2) # ax.plot_surface(x, y, z, color='r', shade=True)# rstride=1, cstride=1, facecolors=img) mlab.mesh(x, y, z, color=(0, 1, 0))
def __init__(self, time): """ :param datetime.datetime time: Initial time to start the Sun model :return: None """ self.time = time self.direction = vt.Vector([0, 0, 0]) self.distance = 0.0 self.update(time) # Update direction, distance to their correct values
def oe2rv(a, e, i, om, w, v, mu=constant.MU): """ :param float a: Semi-major axis :param float e: Eccentricity :param float i: Inclination :param float om: Longitude of ascending node :param float w: Argument of periapsis :param float v: True anomaly :param float mu: Earth gravitation parameter. Defaults to Earth in km^3/s^2 :rtype: (vt.Vector, vt.Vector) :return: position , velocity vectors in ECI """ e2 = e**2 p = a * (1.0 - e2) com = math.cos(om) som = math.sin(om) cin = math.cos(i) sin = math.sin(i) cwv = math.cos(w + v) swv = math.sin(w + v) snu = math.sin(v) # Comput the radius r = p / (1 + e * math.cos(v)) # Compute angular momentum h = math.sqrt(mu * p) # Compute position vector r_eci = r * vt.Vector( [com * cwv - som * swv * cin, som * cwv + com * swv * cin, sin * swv]) # Compute the velocity vector temp1 = r_eci * (h * e / (r * p)) * snu temp2 = (h / r) * vt.Vector([ -com * swv - som * cwv * cin, -som * swv + com * cwv * cin, +sin * cwv ]) v_eci = temp1 + temp2 return r_eci, v_eci
def sph_to_xyz(r, theta, phi): """ Converts from spherical to cartesian coordinates. :param float r: the radial component :param float theta: the polar angle component, measured from the +Z axis :param float phi: the azimuthal angle component, measured form the +X axis :rtype: vt.Vector :return: xyz vector """ return vt.Vector(r * math.cos(phi) * math.sin(theta), r * math.sin(phi) * math.sin(theta), r * math.cos(theta))
def update(self, time): """Calculates the direction and distance to the moon from the Earth :param datetime.datetime time: Update the moon model to the time given. :rtype: vt.Vector, float :return: Unit moon vector, distance from Earth to Moon """ delta_J2000 = self.time - constant.J2000_DATE n_days_J2000 = delta_J2000.days + delta_J2000.seconds / 86400 mean_lon_moon = 218.316 + 13.176396 * n_days_J2000 mean_lon_moon %= 360.0 mean_lon_moon *= constant.DEG_TO_RAD mean_anomaly_moon = 134.963 + 13.064993 * n_days_J2000 mean_anomaly_moon %= 360.0 mean_anomaly_moon *= constant.DEG_TO_RAD mean_dist_moon = 93.272 + 13.229350 * n_days_J2000 mean_dist_moon %= 360.0 mean_dist_moon *= constant.DEG_TO_RAD ecliptic_lon_moon = (mean_lon_moon / constant.DEG_TO_RAD + 6.289 * math.sin(mean_anomaly_moon)) ecliptic_lon_moon *= constant.DEG_TO_RAD ecliptic_lat_moon = 5.128 * math.sin(mean_dist_moon) ecliptic_lat_moon *= constant.DEG_TO_RAD self.distance = (385001.0 - 20905.0 * math.cos(mean_anomaly_moon)) obliquity_ecliptic = 23.439 - 0.0000004 * n_days_J2000 obliquity_ecliptic *= constant.DEG_TO_RAD x_ec = math.cos(ecliptic_lat_moon) * math.cos(ecliptic_lon_moon) y_ec = math.cos(ecliptic_lat_moon) * math.sin(ecliptic_lon_moon) z_ec = math.sin(ecliptic_lat_moon) rect_ec = vt.Vector([[x_ec], [y_ec], [z_ec]]) Q_eq_ec = vt.Matrix( [[1, 0, 0], [0, math.cos(obliquity_ecliptic), -math.sin(obliquity_ecliptic)], [0, math.sin(obliquity_ecliptic), math.cos(obliquity_ecliptic)]]) self.direction = Q_eq_ec * rect_ec self.time = time
def calculate_force(cls, r_ecef): """ :param dt.datetime t: Time at which the force takes place :param vt.Vector r_eci: Position in ECI :rtype: vt.Vector :return: Force vector in ECI """ r, theta, phi = conv.xyz_to_sph(r_ecef) f_r = cls._calculate_radial_force(r, phi, theta) f_t = cls._calculate_theta_force(r, phi, theta) f_p = cls._calculate_phi_force(r, phi, theta) f_ecef = conv.sph_to_xyz_dcm(theta, phi) * vt.Vector([f_r, f_t, f_p]) return f_ecef
def import_sat(cls, filename): single_items = ["name", "shape", "dry_mass", "fuel_mass", "drag_coeff", "rad_coeff"] vector_items = ["length", "cog", "cop", "drag_area", "solar_area", "rad_area"] matrix_items = ["moi"] params = {} try: tree = et.parse(filename) except IOError: print("Unable to open satellite file.") return except et.ParseError: print("Invalid XML file.") return root = tree.getroot() # Single items are defined in text for item in single_items: value = root.find(item).text try: params[item] = float(value) except ValueError: params[item] = value # Vector items are defined in attributes x, y, and z attributes = ['x', 'y', 'z'] for item in vector_items: node = root.find(item) value = [float(node.attrib.get(a, 0)) for a in attributes] params[item] = vector_types.Vector(value) # Matrix items are defined in attributes xx, xy, xz, yx, yy, yz, zx, zy, zz attributes = ['xx', 'xy', 'xz', 'yx', 'yy', 'yz', 'zx', 'zy', 'zz'] for item in matrix_items: node = root.find(item) value = [float(node.attrib.get(a, 0)) for a in attributes] params[item] = vector_types.Matrix(value) return SatModel(params)
def make_quat(ax, ang): """ :param vt.Vector axis: Axis of the rotation. :param float angle: Angle of rotation in radians. :rtype: Quaternion :return: quaternion representing the rotation """ # Get copy of axis axis = vt.Vector(ax.x, ax.y, ax.z) # Make unit vector axis /= axis.norm() sangle = math.sin(ang / 2.0) cangle = math.cos(ang / 2.0) axis *= sangle return Quaternion([cangle, axis.x, axis.y, axis.z])
def update(self, time): """Calculates the direction and distance to the sun from the Earth :param dt.datetime time: Update the sun model to the time given. :rtype: vt.Vector, float :return: Unit sun vector, distance from the Earth to the Sun """ delta_J2000 = self.time - constant.J2000_DATE n_days_J2000 = delta_J2000.days + delta_J2000.seconds / 86400 mean_lon_sun = 280.460 + 0.9856474 * n_days_J2000 mean_lon_sun %= 360.0 mean_lon_sun *= constant.DEG_TO_RAD mean_anomaly_sun = 357.528 + 0.9856003 * n_days_J2000 mean_anomaly_sun %= 360.0 mean_anomaly_sun *= constant.DEG_TO_RAD ecliptic_lon_sun = (mean_lon_sun / constant.DEG_TO_RAD + 1.915 * math.sin(mean_anomaly_sun) + 0.020 * math.sin(2.0 * mean_anomaly_sun)) ecliptic_lon_sun *= constant.DEG_TO_RAD dist_earth_to_sun = (1.00014 - 0.01671 * math.cos(mean_anomaly_sun) - 0.00014 * math.cos(2.0 * mean_anomaly_sun)) dist_earth_to_sun *= constant.AU_TO_KM obliquity_ecliptic = 23.439 - 0.0000004 * n_days_J2000 obliquity_ecliptic *= constant.DEG_TO_RAD x_J2000_sun = math.cos(ecliptic_lon_sun) y_J2000_sun = math.cos(obliquity_ecliptic) * math.sin(ecliptic_lon_sun) z_J2000_sun = math.sin(obliquity_ecliptic) * math.sin(ecliptic_lon_sun) self.direction = vt.Vector([x_J2000_sun, y_J2000_sun, z_J2000_sun]) self.distance = dist_earth_to_sun self.time = time
def __orbit_force_model(t, y, time_init): x, y, z, v_x, v_y, v_z = y x_dot, y_dot, z_dot = v_x, v_y, v_z v_dot = SatModel.force_model.force(vector_types.Vector(x, y, z), time_init + dt.timedelta(seconds=t)) return [x_dot, y_dot, z_dot, v_dot.x, v_dot.y, v_dot.z]
quat[1] = x/angle * sin(angle/2.0) quat[2] = y/angle * sin(angle/2.0) quat[3] = z/angle * sin(angle/2.0) self.q *= space_types.Quaternion(quat) def get_att_vec(self): return self.q.dcm() * self.att_init def __str__(self): return str(self.get_att_vec()) if __name__ == '__main__': time_init = dt.datetime.now() mass_prop = {'mass': 1500.0, 'cog': vector_types.Vector([0.5, 0.5, 1.5]), 'moi': vector_types.Matrix(1375, 0, 0, 0, 1375, 0, 0, 0, 275)} pos_init = vector_types.Vector([7141.9897400, 0.000, 0.000]) vel_init = vector_types.Vector([0.0, 270.96981048, 4306.941804035]) att_init = vector_types.Vector([0.0, 0.0, 1.0]) ang_vel_init = vector_types.Vector([0.0, 0.06, 0.0]) satmodel = SatModel(time_init, mass_prop, pos_init, vel_init, att_init, ang_vel_init) x_plt, y_plt, z_plt = satmodel.att.get_att_vec() x_plt = [x_plt] y_plt = [y_plt] z_plt = [z_plt] x_pos, y_pos, z_pos = pos_init.x, pos_init.y, pos_init.z x_pos = list(x_pos) y_pos = list(y_pos) z_pos = list(z_pos)
import datetime as dt import basics.conversions as conv import math import basics.plotting as plotting from mpl_toolkits.mplot3d import Axes3D import mayavi.mlab as mlab import basics.rkf6 as rkf6 import basics.vector_types as vt import scipy.integrate as spyi import numpy as np import time # Setup the satellite model time_init = dt.datetime.now() mass_prop = {'mass': 1500.0, 'cog': vt.Vector([0.5, 0.5, 1.5]), 'moi': vt.Matrix(1375, 0, 0, 0, 1375, 0, 0, 0, 275)} p_init = vt.Vector([7155.27304548, 0.0, 0.0]) v_init = vt.Vector([0.0, 0.46957258, 7.45043721]) att_init = vt.Vector([0.0, 0.0, 1.0]) ang_vel_init = vt.Vector([0.0, 0.06, 0.0]) satellite = SatModel.SatModel(time_init, mass_prop, p_init, v_init, att_init, ang_vel_init) # Make universe universe = Universe.Universe(satellite, time_init) # Make plotting variables x_sat = np.array([p_init.x]) y_sat = np.array([p_init.y]) z_sat = np.array([p_init.z])
def rv_to_oe(r_eci, v_eci, mu=constant.MU): """Convert ECI position/velocity state vectors to orbital elements :param vt.Vector r_eci: :param vt.Vector v_eci: :param float mu: :rtype: (float, float, float, float, float, float) :return: a, e, i, om, w, v """ # Calculate some initial constants r2 = r_eci.norm2() r = math.sqrt(r2) v2 = v_eci.norm2() v = math.sqrt(v2) rdotv = r_eci * v_eci # Calculate angular momentum h_eci = r_eci.cross(v_eci) h2 = h_eci.norm2() h = math.sqrt(h2) # Calculate node vector n_eci = vt.Vector([0, 0, 1]).cross(h_eci) n = n_eci.norm() n_unit = n_eci / n # Calculate eccentricity vector e_eci = ((v2 - mu / r) * r_eci - rdotv * v_eci) / mu e2 = e_eci.norm2() e = math.sqrt(e2) e_unit = e_eci / e # Calculate mechanical energy E = v2 / 2 - mu / r # Calculate semi-major axis and latus rectum if e != 1.0: a = -mu / (2 * E) p = a * (1 - e2) else: p = h2 / mu a = float('inf') # Calculate inclination i = math.acos(h_eci.z / h) # Calculate the longitude of ascending node om = math.acos(n_unit.x) # Calculate the argument of periapsis w = math.acos(n_unit * e_unit) if e_unit < 0.0: w = 360.0 - w # Calculate the true anomaly v = math.acos((e_unit * r_eci) / r) if rdotv < 0.0: v = 360.0 - v return a, e, i, om, w, v