def skyfield_ephem(load_path=None, parallax_correction=False, utc=None): '''Returns skyfield objects used for doing solar planning and conversion. Optional keywords: load_path: Directory location where the bsp files are stored. Defaults to current working directory if None. parallax_corection: Download the latest NuSTAR TLE file and apply the parallax correction based on NuSTAR's position in its orbit. Defaults to "False". If "True" then uses the nustar_pysolar.io TLE methods to parse the closest TLE entry in the NuSTAR database. If you set parallax_correction=True then Returns: observer, sun, ts The first two are Skyfield objects. "observer" is either geocentric or the NuSTAR location based on the TLE. "ts" is the Skyfield time object. ''' # Initialize Skyfield ephemeris tools. from skyfield.api import EarthSatellite, Loader from astropy.time import Time import sunpy.sun if load_path is None: load_path = './' load=Loader(load_path) else: load=Loader(load_path) ts = load.timescale() planets = load('de436.bsp') earth = planets['Earth'] sun = planets['Sun'] if parallax_correction is False: observer = earth else: assert (not utc is None),"Must set UTC when using parallax correction!" import nustar_pysolar.io as io tlefile = io.download_tle(outdir=load_path) mindt, line1, line2 = io.get_epoch_tle(utc, tlefile) nustar = EarthSatellite(line1, line2) observer = earth + nustar ts = load.timescale() return observer, sun, ts
def handle(self, *args, **options): load = Loader(settings.EPHEM_DIR) load('de405.bsp') load.timescale() self.stdout.write( self.style.SUCCESS( f'Successfully downloaded ephemeris files to {settings.EPHEM_DIR}!' ))
def generatePlot(): print('Generating Plot') df = pandas.read_csv('../Data/tempCSV.csv') objectID = df.values[1][0][2:7] L1 = df.values[1][0] L2 = df.values[2][0] load = Loader( '~/Documents/fishing/SkyData') # avoids multiple copies of large files ts = load.timescale() data = load('de421.bsp') earth = data['earth'] ts = load.timescale() minutes = np.arange(60. * 24) # seven days time = ts.utc(today.year, today.month, today.day, 0, minutes) # start June 1, 2018 ISS = EarthSatellite(L1, L2) subpoint = ISS.at(time).subpoint() lon = subpoint.longitude.degrees lat = subpoint.latitude.degrees breaks = np.where(np.abs(lon[1:] - lon[:-1]) > 30) #don't plot wrap-around lon, lat = lon[:-1], lat[:-1] lon[breaks] = np.nan my_tweet = 'Ground Track of NORAD ID #' + str( objectID) + ' for the next 24 hours' fig1, ax1 = plt.subplots() earth = mpimg.imread('../Figures/earth.tif') earth = mpimg.imread(earthImg) ax1.imshow(earth) ax1.plot(10800 * (lon / 360 + .5), 5400 * (lat / 180 + 0.5)) plt.axis('off') imgFile = "../Figures/latestGroundTrack.png" plt.savefig(imgFile, dpi=300, bbox_inches='tight', pad_inches=0) fig2, ax2 = plt.subplots() ax2.plot(lon / 360 + .5, lat / 180 - .5) print(lat[0]) if (math.isnan(lat[0])): exitFlag = False else: exitFlag = True return exitFlag, my_tweet
def handle(self, *args, **options): load = Loader(settings.EPHEM_DIR) load("de405.bsp") load.timescale(builtin=True) self.stdout.write( self.style.SUCCESS( f"Successfully downloaded ephemeris files to {settings.EPHEM_DIR}!" ) )
def skyfield_ephem(load_path=None, parallax_correction=False, utc=None): '''Returns skyfield objects used for doing solar planning and conversion. Optional keywords: load_path: Directory location where the bsp files are stored. Defaults to current working directory if None. parallax_corection: Download the latest NuSTAR TLE file and apply the parallax correction based on NuSTAR's position in its orbit. Defaults to "False". If "True" then uses the nustar_pysolar.io TLE methods to parse the closest TLE entry in the NuSTAR database. If you set parallax_correction=True then Returns: observer, sun, ts The first two are Skyfield objects. "observer" is either geocentric or the NuSTAR location based on the TLE. "ts" is the Skyfield time object. ''' # Initialize Skyfield ephemeris tools. from skyfield.api import EarthSatellite, Loader from astropy.time import Time # Not actually used? So just comment out, and so no sunpy v1 issue # import sunpy.sun if load_path is None: load_path = './' load = Loader(load_path) else: load = Loader(load_path) ts = load.timescale() planets = load('de436.bsp') earth = planets['Earth'] sun = planets['Sun'] if parallax_correction is False: observer = earth else: assert ( not utc is None), "Must set UTC when using parallax correction!" import nustar_pysolar.io as io tlefile = io.download_tle(outdir=load_path) mindt, line1, line2 = io.get_epoch_tle(utc, tlefile) nustar = EarthSatellite(line1, line2) observer = earth + nustar ts = load.timescale() return observer, sun, ts
def _convert_radec_to_altaz(ra, dec, lon, lat, height, time): """Convert a single position. This is done for easy code sharing with other tools. Skyfield does support arrays of positions. """ load = Loader('.') # Skyfield uses FTP URLs, but FTP doesn't work on Github Actions so # we use alternative HTTP URLs. load.urls['finals2000A.all'] = 'https://datacenter.iers.org/data/9/' load.urls['.bsp'] = [ ('*.bsp', 'https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/') ] radec = Star(ra=Angle(degrees=ra), dec=Angle(degrees=dec)) earth = load(EPHEMERIS)['earth'] location = earth + wgs84.latlon(longitude_degrees=lon, latitude_degrees=lat, elevation_m=height * 1000.0) ts = load.timescale(builtin=False) with load.open('finals2000A.all') as f: finals_data = iers.parse_x_y_dut1_from_finals_all(f) iers.install_polar_motion_table(ts, finals_data) obstime = ts.from_astropy(Time(time, scale='utc')) alt, az, _ = location.at(obstime).observe(radec).apparent().altaz( pressure_mbar=0) return dict(az=az.degrees, alt=alt.degrees)
def loadAstronomyData(): if g.astroDataLoaded: return try: from skyfield.api import Loader load = Loader(getUserDataPath(), verbose=False) g.timescale = load.timescale(builtin=False) except OSError: print( 'Downloading the astronomy data failed. Some astronomical functions will not be available.', file=sys.stderr) g.astroDataLoaded = True g.astroDataAvailable = False return try: g.ephemeris = load('de421.bsp') except OSError: print( 'Downloading the astronomy data failed. Some astronomical functions will not be available.', file=sys.stderr) g.astroDataLoaded = True g.astroDataAvailable = False return g.astroDataLoaded = True g.astroDataAvailable = True return
def load_skyfield_data(): """Load data files used in Skyfield. This will download files from the internet if they haven't been downloaded before. Skyfield downloads files to the current directory by default, which is not ideal. Here we abuse astropy and use its cache directory to cache the data files per-user. If we start downloading files in other places in pwkit we should maybe make this system more generic. And the dep on astropy is not at all necessary. Skyfield will print out a progress bar as it downloads things. Returns ``(planets, ts)``, the standard Skyfield ephemeris and timescale data files. """ import os.path from astropy.config import paths from skyfield.api import Loader cache_dir = os.path.join(paths.get_cache_dir(), 'pwkit') loader = Loader(cache_dir) planets = loader('de421.bsp') ts = loader.timescale() return planets, ts
def main(): # Set up and hide tkinter root window root = tk.Tk() root.withdraw() file_path = filedialog.askopenfilename() if not path.exists(file_path): print("Error - no such file") quit() root.destroy() # Load earth position from ephemeris data sfLoader = Loader('resources') ephemeris = sfLoader('de421.bsp') earth = ephemeris['earth'] # Setup timescale for calculating ground track ts = sfLoader.timescale() now = datetime.utcnow() steps = np.arange(0, 180, 1) time = ts.utc(now.year, now.month, now.day, now.hour, steps) # Load satellite data from tle file satellites = sfLoader.tle_file(file_path) # Set up plot plt.figure() img = plt.imread('resources/map.png') plt.imshow(img, extent=[-180, 180, -90, 90]) for sat in satellites: _calculateGroundTrack(earth, sat, time) plt.show()
def __init__(self, intercept_df=None, sat_df=None): ''' Initialize ''' load = Loader('./data') data = load('de421.bsp') self.timescale = load.timescale() self.intercept_df = intercept_df self.sat_df = sat_df
def _load_sky_data(self, tmpdir): """ Load the primary input data for skyfield. This requires a download for the first one, or the inclusion of the data files. """ load = Loader(tmpdir) self._planets = load("de421.bsp") self._ts = load.timescale()
def init_sf(spad): global ts, eph, earth, moon, sun, venus, mars, jupiter, saturn, df load = Loader(spad) # spad = folder to store the downloaded files EOPdf = "finals2000A.all" # Earth Orientation Parameters data file dfIERS = spad + EOPdf if config.useIERS: if compareVersion(VERSION, "1.31") >= 0: if path.isfile(dfIERS): if load.days_old(EOPdf) > float(config.ageIERS): load.download(EOPdf) ts = load.timescale(builtin=False) # timescale object else: load.download(EOPdf) ts = load.timescale(builtin=False) # timescale object else: ts = load.timescale() # timescale object with built-in UT1-tables else: ts = load.timescale() # timescale object with built-in UT1-tables if config.ephndx in set([0, 1, 2, 3, 4]): eph = load(config.ephemeris[config.ephndx][0]) # load chosen ephemeris earth = eph['earth'] moon = eph['moon'] sun = eph['sun'] venus = eph['venus'] jupiter = eph['jupiter barycenter'] saturn = eph['saturn barycenter'] if config.ephndx >= 3: mars = eph['mars barycenter'] else: mars = eph['mars'] # load the Hipparcos catalog as a 118,218 row Pandas dataframe. with load.open(hipparcos.URL) as f: #hipparcos_epoch = ts.tt(1991.25) df = hipparcos.load_dataframe(f) return ts
def __init__(self): self._tokenInEnglish = { '_tok_1': 'SWX ADVISORY line', 'test': 'STATUS: TEST', 'exercise': 'STATUS: EXER', 'dtg': 'Date/Time Group', 'centre': 'Issuing SWX Centre', 'advnum': 'YYYY/nnnn', 'prevadvsry': 'Previous Advisory YYYY/nnnn', 'phenomenon': 'SWX Hazard(s)', 'init': '(OBS|FCST) SWX', 'timestamp': 'DD/HHmmZ Group', 'noos': 'NO SWX EXP', 'notavail': 'NOT AVBL', 'daylight': 'DAY(LIGHT)? SIDE', 'lat_band': '(H|M)(N|S)H', 'equator': 'EQ(N|S)', 'longitudes': '(E|W)nnn[nn]-(E|W)nnn[nn]', 'box': 'lat/long bounding box', 'fltlvls': 'ABV FLnnn|FLnnn-nnn', 'fcsthr': 'FCST SWX +nn HR', 'rmk': 'RMK:', 'nextdtg': 'Next advisory issuance date/time', 'noadvisory': 'NO FURTHER ADVISORIES' } self.header = re.compile(r'.*(?=SWX ADVISORY)', re.DOTALL) setattr(self, 'lat_band', self.add_region) setattr(self, 'point', self.add_region) setattr(self, 'equator', self.add_region) self._Logger = logging.getLogger(__name__) # # Preparing Skyfield try: load = Loader(os.path.join(os.path.dirname(__file__), '../data'), verbose=False) self._ts = load.timescale() # # Open NAIF/JPL/NASA SPICE Kernel planets = load('de421.bsp') self._Gaia = planets['earth'] self._Helios = planets['sun'] except Exception: self._Logger.exception( 'Unable to load/initialize Skyfield Module.') raise return super(Decoder, self).__init__()
def params_newton_angle(): """ Session-scoped fixture to "cache" the newton angle func parameters """ load = Loader(get_skyfield_data_path()) ts = load.timescale() planets = load('de421.bsp') earth = planets['earth'] sun = planets['sun'] jan_first = ts.utc(date(2021, 1, 1)) t0 = ts.tt_jd(jan_first.tt).tt return t0, ts, earth, sun
def calculate_equinoxes(year, timezone='UTC'): """ calculate equinox with time zone """ tz = pytz.timezone(timezone) load = Loader(get_skyfield_data_path()) ts = load.timescale() planets = load('de421.bsp') t0 = ts.utc(year, 1, 1) t1 = ts.utc(year, 12, 31) datetimes, _ = almanac.find_discrete(t0, t1, almanac.seasons(planets)) vernal_equinox = datetimes[0].astimezone(tz).date() autumn_equinox = datetimes[2].astimezone(tz).date() return vernal_equinox, autumn_equinox
def init_ephem(orbits, load_path=None, show=False, parallax_correction=False): '''Initialize Skyfield ephemeris for Jupiter BSP file Takes output of io.parse_occ as input. Requires Astropy and SkyField Optional: load_path (where the bsp and SkyField data files are found. parllax_correction (apply the parallax correction from NuSTAR's orbit). Downloads the latest TLE archive from the NuSTAR SOC. Returns: observer, jupiter, ts The first two are Skyfield objects. The third is the Skyfield time series object. ''' from skyfield.api import Loader, EarthSatellite from astropy.time import Time if load_path is None: load_path = './' load = Loader(load_path) else: load = Loader(load_path) planets = load('jup310.bsp') jupiter, earth = planets['jupiter'], planets['earth'] ts = load.timescale() if parallax_correction is False: observer = earth else: import nustar_planning.io as io start_date = orbits.loc[0, 'visible'] utc = Time(start_date) tlefile = io.download_tle(outdir=load_path) mindt, line1, line2 = io.get_epoch_tle(utc, tlefile) nustar = EarthSatellite(line1, line2) observer = earth + nustar return observer, jupiter, ts
class StationList: def __init__(self): self._loader = Loader(DATA_PATH) self.update() def update(self): self._tles = {} for es in self._loader.tle_file(URL_TLE, True): self._tles[es.name] = es self._ts = self._loader.timescale() self._stations = self._tles.keys() LOGGER.info("Update StationList, found "+str(len(self._stations))+" items") def list(self): return self._stations def getStation(self, station, from_position): return Station(self._tles[station],self._ts, from_position)
def get_jupiter_radec(orbits, outfile=None): if outfile is not None: f = open(outfile, 'w') from skyfield.api import Loader load = Loader('../data') ts = load.timescale() planets = load('jup310.bsp') jupiter, earth = planets['jupiter'], planets['earth'] for ind in range(len(orbits)): tstart = orbits.loc[ind, 'visible'] tend = orbits.loc[ind, 'occulted'] print() on_time = (tend - tstart).total_seconds() point_time = tstart + 0.5 * (tend - tstart) astro_time = Time(point_time) t = ts.from_astropy(astro_time) astrometric = earth.at(t).observe(jupiter) ra, dec, distance = astrometric.radec() radeg = ra.to(u.deg) decdeg = dec.to(u.deg) dt += on_time print(tstart.isoformat() + ' {} {}'.format(radeg.value, decdeg.value)) if outfile is not None: f.write(tstart.isoformat() + ' {} {}'.format(radeg.value, decdeg.value) + '\n') if outfile is not None: f.close() print('Total accumualted time {}'.format(dt))
def is_light(): """is_light returns True if the sun is up and False otherwise""" # load in data directory to avoid redownloading loader = Loader('~/skyfield_data') ts = loader.timescale() e = loader('de421.bsp') # set current location (melbourne does not appear in the default list) melbourne = api.Topos('37.951910 S', '145.152080 E') # get current time in UTC format now = datetime.datetime.utcnow() now = now.replace(tzinfo=utc) # set the interval for now and 24 hours from now t0 = ts.utc(now) t1 = ts.utc(now + timedelta(hours=24)) # find the times and types of event (sunrise/sunset) t, y = almanac.find_discrete(t0, t1, almanac.sunrise_sunset(e, melbourne)) #y[0] = True for sunrise (which means it is currently dark) light = not y[0] return light
def get_jupiter_radec(orbits, outfile=None,load_path=None, show=False, parallax_correction=False): '''Get the position of Jupiter at the specified time Takes output of parse_occ as input. Requires Astropy, and SkyField Optional: load_path (where the bsp and SkyField data files are found. outfile (where you want the output to go) show (report the output). Always True if you don't specify outfile parllax_correction (apply the parallax correction from NuSTAR's orbit). Requries nustar_pysolar for IO routines. Downloads the latest TLE archive from the NuSTAR SOC. Always reports the amount of time that you've accumulated. ''' if outfile is None and show is False: show=True from skyfield.api import Loader, EarthSatellite if load_path is None: load_path = './' load=Loader(load_path) else: load=Loader(load_path) planets = load('jup310.bsp') jupiter, earth = planets['jupiter'], planets['earth'] ts = load.timescale() if parallax_correction is False: observer = earth else: import nustar_pysolar.io as io start_date = orbits.loc[0, 'visible'] utc = Time(start_date) tlefile = io.download_tle(outdir=load_path) mindt, line1, line2 = io.get_epoch_tle(utc, tlefile) nustar = EarthSatellite(line1, line2) observer = earth + nustar dt = 0. if outfile is not None: f = open(outfile, 'w') f.write('Arrive Time RA Dec\n') if show is True: print('Aim Time RA Dec') for ind in range(len(orbits)): tstart = orbits.loc[ind, 'visible'] tend = orbits.loc[ind, 'occulted'] on_time = (tend - tstart).total_seconds() point_time = tstart + 0.5*(tend - tstart) astro_time = Time(point_time) t = ts.from_astropy(astro_time) astrometric = observer.at(t).observe(jupiter) ra, dec, distance = astrometric.radec() if show is True and parallax_correction is True: from astropy.coordinates import SkyCoord radeg = ra.to(u.deg) decdeg = dec.to(u.deg) skyfield_ephem = SkyCoord(radeg, decdeg) geocentric = earth.at(t).observe(jupiter) skyfield_ephem = SkyCoord(radeg, decdeg) ra2, dec2, distance2 = geocentric.radec() ra2deg = ra2.to(u.deg) dec2deg = dec2.to(u.deg) geo_ephem = SkyCoord(ra2deg, dec2deg) print("Parallax corection (arcsec) {}".format( skyfield_ephem.separation(geo_ephem).arcsec)) radeg = ra.to(u.deg) decdeg = dec.to(u.deg) dt += on_time if show is True: print(tstart.isoformat()+' {} {}'.format(radeg.value, decdeg.value)) if outfile is not None: f.write(tstart.isoformat()+' {} {}'.format(radeg.value, decdeg.value)+'\n') if outfile is not None: f.close() print('Total accumualted time {}'.format(dt))
def test_load_finals_builtins(tmpdir): load = Loader(get_skyfield_data_path()) with mock.patch('skyfield.iokit.download') as download_patched: load.timescale(builtin=True) assert download_patched.call_count == 0
class Data(object): """This class is the central holder for astronomical data; it acts as a wrapper around all of the datasets we get from JPL, the Minor Planets Center, etc., and exposes them via nice, Skyfield-friendly APIs. """ def __init__(self, dirname: str = "data", ephemerides: str = "de441") -> None: self.load = Loader(dirname) self._ephName = ephemerides if not self._ephName.endswith(".bsp"): self._ephName += ".bsp" # CelestialObjects representing the most common things we might want to use. @cached_property def sun(self) -> CelestialObject: return makeObject( ObjectType.STAR, "The Sun", self._ephemerides["sun"], symbol="\u2609", ) @cached_property def moon(self) -> CelestialObject: return makeObject(ObjectType.MOON, "The Moon", self._ephemerides["moon"], symbol="\u263D") @cached_property def mercury(self) -> CelestialObject: return makeObject(ObjectType.PLANET, "Mercury", self._ephemerides["mercury"], symbol="\u263f") @cached_property def venus(self) -> CelestialObject: return makeObject(ObjectType.PLANET, "Venus", self._ephemerides["venus"], symbol="\u2640") @cached_property def earth(self) -> CelestialObject: return makeObject(ObjectType.PLANET, "Earth", self._ephemerides["earth"], symbol="\u2641") @cached_property def mars(self) -> CelestialObject: return makeObject( ObjectType.PLANET, "Mars", self._ephemerides["mars barycenter"], symbol="\u2642", ) @cached_property def jupiter(self) -> CelestialObject: return makeObject( ObjectType.PLANET, "Jupiter", self._ephemerides["jupiter barycenter"], symbol="\u2643", ) @cached_property def saturn(self) -> CelestialObject: return makeObject( ObjectType.PLANET, "Saturn", self._ephemerides["saturn barycenter"], symbol="\u2644", ) @cached_property def uranus(self) -> CelestialObject: return makeObject( ObjectType.PLANET, "Uranus", self._ephemerides["uranus barycenter"], symbol="\u2645", ) @cached_property def neptune(self) -> CelestialObject: return makeObject( ObjectType.PLANET, "Neptune", self._ephemerides["neptune barycenter"], symbol="\u2646", ) # Our favorite dwarf planets @cached_property def pluto(self) -> CelestialObject: # Still in the JPL planet ephemerides! return makeObject( ObjectType.MINOR_PLANET, "Pluto", self._ephemerides["pluto barycenter"], symbol="\u2647", ) @cached_property def ceres(self) -> CelestialObject: return self.minorPlanet("(1) Ceres", symbol="\u26b3") @cached_property def chiron(self) -> CelestialObject: return self.minorPlanet("(2060) Chiron", symbol="\u26b7") @cached_property def eris(self) -> CelestialObject: return self.minorPlanet("(136199) Eris", symbol="\u2bf0") @cached_property def makemake(self) -> CelestialObject: # Not yet in Unicode! return self.minorPlanet("(136472) Makemake") @cached_property def haumea(self) -> CelestialObject: # Not yet in Unicode! return self.minorPlanet("(136108) Haumea") @cached_property def sedna(self) -> CelestialObject: return self.minorPlanet("(90377) Sedna", symbol="\u2bf2") # Orcus, Quaoar,Pallas, Juno, Vesta, Astraea, Hebe, Iris, Flora, Metis, Hygiea, Parthenope, # Victoria, Egeria, Irene, Eunomia, Psyche, Thetis, Melpomene, Fortuna, Proserpina, Bellona, # Amphitrite, Leukothea, Fides? # How about some stars? @cached_property def sirius(self) -> CelestialObject: return self.star("Sirius", "western") @cached_property def arcturus(self) -> CelestialObject: return self.star("Arcturus", "western") # Observation points # Helpers for getting positions def observer( self, lat: float, lon: float, elevationMeters: Optional[float] = None, ) -> VectorFunction: """Return a VectorFunction representing a terrestrial observer.""" return self.earth.position + wgs84.latlon( lat, lon, elevation_m=elevationMeters or 0) @cached_property def berkeley(self) -> VectorFunction: return self.observer(37.8645 * N, 122.3015 * W, 52.1) def asTime(self, when: datetime) -> Time: return self.timescale.from_datetime(when) def observe( self, observer: VectorFunction, target: CelestialObject, when: Optional[datetime] = None, ) -> Observation: """Give the astrometric (relative) position of target relative to observer at time. If the time is not given, assume "now." """ time = self.timescale.from_datetime( when) if when else self.timescale.now() position = observer.at(time).observe(target.position) return Observation( target=target, position=position, magnitude=target.magnitude(position), subpoint=self.subpoint(target, time), ) # More general accessors to load up other celestial bodies in our databases. def comet(self, designation: str) -> CelestialObject: """Look up a comet by its designation, e.g. 1P/Halley""" # NB: mpc.comet_orbit returns an orbit centered on the Sun, so we need to offset it! row = self._comets.loc[designation] return makeObject( type=ObjectType.COMET, name=designation[designation.find("/") + 1:], position=self.sun.position + mpc.comet_orbit(row, self.timescale, GM_SUN), dataFrame=row, ) def minorPlanet(self, designation: str, symbol: Optional[str] = None) -> CelestialObject: """Look up a minor planet by its designation, e.g. (2060) Chiron""" data = self._minorPlanets.loc[designation] shortName = designation[designation.find(") ") + 2:] return makeObject( type=ObjectType.MINOR_PLANET, name=shortName, position=self.sun.position + mpc.mpcorb_orbit(data, self.timescale, GM_SUN), dataFrame=data, symbol=symbol, ) def star(self, ref: Union[str, int], culture: Optional[str] = None) -> CelestialObject: """Fetch a star by its name or Hipparcos catalogue number. Args: ref: Either the string common name, or the int catalogue number, e.g. 87937 for Barnard's Star. culture: If given, use as a hint to understand the common name. """ number = ref if isinstance(ref, int) else self.starNumber(ref) data = self._stars.loc[number] names = self._starNames.allNames(number) primaryName = (ref if isinstance(ref, str) else names["western"] if "western" in names else names["hip"]) star = Star.from_dataframe(data) return makeObject( type=ObjectType.STAR, name=primaryName, position=star, dataFrame=data, names=names, ) def cultures(self) -> Tuple[str, ...]: return self._starNames.cultures() def starNumber(self, name: str, culture: Optional[str] = None) -> int: return self._starNames.find(name, culture=culture) @property def timescale(self) -> Timescale: return self.load.timescale() def subpoint(self, target: CelestialObject, time: Time) -> GeographicPosition: return wgs84.subpoint( self.earth.position.at(time).observe(target.position)) ######################################################################################### # Much more internal accessors @cached_property def _comets(self) -> DataFrame: with self.load.open(mpc.COMET_URL) as f: return ( mpc.load_comets_dataframe(f).sort_values("reference").groupby( "designation", as_index=False).last().set_index("designation", drop=False)) @cached_property def _ephemerides(self) -> SpiceKernel: return self.load(self._ephName) @cached_property def _minorPlanets(self) -> DataFrame: with self.load.open(self._minorPlanetsPath()) as f: logging.info("Loading minor planets dataset") mp = mpc.load_mpcorb_dataframe(f) # Drop items without orbits badOrbits = mp.semimajor_axis_au.isnull() mp = mp[~badOrbits].set_index("designation", drop=False) return mp @cached_property def _stars(self) -> DataFrame: logging.info("Loading Hipparcos data") with self.load.open(hipparcos.URL) as f: df = hipparcos.load_dataframe(f) # Filter out the ones with no reliable position df = df[df["ra_degrees"].notnull()] return df @cached_property def _starNames(self) -> StarNames: return StarNames(self.load) # Logic for downloading data _MPC_ORB = "minor_planets.dat" _MPC_URL = "https://www.minorplanetcenter.net/iau/MPCORB/MPCORB.DAT.gz" def _minorPlanetsPath(self, ensure: bool = True, refresh: bool = False) -> str: """Return a path to the file containing minor planets data. If ensure is True, ensures that the file exists. If refresh is True, force a re-download. """ filename = self.load.path_to(self._MPC_ORB) if not ensure or (not refresh and os.path.exists(filename)): return self._MPC_ORB # Do we need to refetch the compressed data? compressed = self._MPC_ORB + ".gz" if not os.path.exists(compressed): logging.info(f"Downloading minor planet data from {self._MPC_URL}") compressed = self.load.download(self._MPC_URL, filename=self._MPC_ORB + ".gz") # Regenerate the "clean" data. Why do they stick an unformatted header in this file? # I really don't know. logging.info("Cleaning and parsing minor planets data") with gzip.open(compressed, mode="rt", encoding="ascii") as input, open(filename, "w") as output: sawHeader = False count = 0 for line in input: if not line: continue if not sawHeader: sawHeader = line.startswith("-----") else: count += 1 output.write(line) logging.info(f"Loaded {count} minor planets") # Now nuke the compressed file. os.remove(compressed) return self._MPC_ORB
def visible_pass(start_time, end_time, site, timezone=0, cutoff=10, twilight='nautical', visible_duration=120): ''' Generate the visible passes of space targets in a time period. Usage: visible_pass(start_time,end_time,site,timezone=8) Inputs: start_time -> [str] start time, such as '2020-06-01 00:00:00' end_time -> [str] end time, such as '2020-07-01 00:00:00' site -> [list of str] geodetic coordinates of a station, such as ['21.03 N','157.80 W','1987.05'] Parameters: timezone -> [int, optional, default=0] time zone, such as -10; if 0, then UTC is used. cutoff -> [float, optional, default=10] Satellite Cutoff Altitude Angle twilight -> [str, or float, optional, default='nautical'] Dark sign or solar cutoff angle; if 'dark', then the solar cutoff angle is less than -18 degrees; if 'astronomical', then less than -12 degrees; if 'nautical', then less than -6 degrees; if 'civil', then less than -0.8333 degrees; alternatively, it also can be set to a specific number, for example, 4.0 degrees. visible_duration -> [int, optional, default=120] Duration[seconds] of a visible pass Outputs: VisiblePasses_bysat.csv -> csv-format files that record visible passes in sort of target VisiblePasses_bydate.csv -> csv-format files that record visible passes in sort of date xxxx.txt -> one-day prediction files for targets ''' home = str(Path.home()) direc_eph = home + '/src/skyfield-data/ephemeris/' direc_time = home + '/src/skyfield-data/time_data/' de430 = direc_eph + 'de430.bsp' load_eph = Loader(direc_eph) load_time = Loader(direc_time) print('\nDownloading JPL ephemeris DE430 and timescale files', end=' ... \n') # URL of JPL DE430 url = 'http://www.shareresearch.me/wp-content/uploads/2020/05/de430.bsp' ''' url = 'http://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de430.bsp' url = 'https://repos.cosmos.esa.int/socci/projects/SPICE_KERNELS/repos/juice/browse/kernels/spk/de430.bsp' ''' if not path.exists(de430): desc = 'Downloading {:s}'.format('de430.bsp') tqdm_request(url, direc_eph, 'de430.bsp', desc) print('Downloading timescale files', end=' ... \n') ts = load_time.timescale() planets = load_eph('de430.bsp') # Print information about the time system file and ephemeris file print(load_time.log) print(load_eph.log) print( '\nCalculating one-day predictions and multiple-day visible passes for targets', end=' ... \n') if twilight == 'dark': sun_alt_cutoff = -18 elif twilight == 'astronomical': sun_alt_cutoff = -12 elif twilight == 'nautical': sun_alt_cutoff = -6 elif twilight == 'civil': sun_alt_cutoff = -0.8333 elif type(twilight) is int or type(twilight) is float: sun_alt_cutoff = twilight else: raise Exception( "twilight must be one of 'dark','astronomical','nautical','civil', or a number." ) dir_TLE = 'TLE/' dir_prediction = 'prediction/' sun, earth = planets['sun'], planets['earth'] sats = load.tle_file(dir_TLE + 'satcat_3le.txt') lon, lat, ele = site observer = Topos(lon, lat, elevation_m=float(ele)) # local start time t_start = Time(start_time) - timezone * u.hour # local end time t_end = Time(end_time) - timezone * u.hour fileList_prediction = glob(dir_prediction + '*') if path.exists(dir_prediction): for file in fileList_prediction: remove(file) else: mkdir(dir_prediction) filename0 = 'VisiblePasses_bysat.csv' filename1 = 'VisiblePasses_bydate.csv' outfile0 = open(dir_prediction + filename0, 'w') header = [ 'Start Time[UTC+' + str(timezone) + ']', 'End Time[UTC+' + str(timezone) + ']', 'NORADID', 'During[seconds]' ] outfile0.write('{},{},{},{}\n'.format(header[0], header[1], header[2], header[3])) print('Generate one-day prediction for targets:') for sat in sats: visible_flag = False noradid = sat.model.satnum passes = next_pass(ts, t_start, t_end, sat, observer, cutoff) if not passes: continue else: outfile = open(dir_prediction + str(noradid) + '.txt', 'w') outfile.write( '# {:18s} {:8s} {:8s} {:8s} {:8s} {:10s} {:14s} {:7s} \n'. format('Date and Time(UTC)', 'Alt[deg]', 'Az[deg]', 'Ra[h]', 'Dec[deg]', 'Range[km]', 'Solar Alt[deg]', 'Visible')) for pass_start, pass_end in passes: t = t_list(ts, Time(pass_start), Time(pass_end), 1) sat_observer = (sat - observer).at(t) sat_alt, sat_az, sat_distance = sat_observer.altaz() sat_ra, sat_dec, sat_distance = sat_observer.radec() sun_observer = (earth + observer).at(t).observe(sun).apparent() sun_alt, sun_az, sun_distance = sun_observer.altaz() sun_beneath = sun_alt.degrees < sun_alt_cutoff #shadow = eclipsed(sat,sun,earth,t) sunlight = sat.at(t).is_sunlit(planets) # Under the premise of satellite transit, visibility requires at least two conditions to be met: dark and outside the earth shadow. #visible = sun_beneath & ~shadow visible = sun_beneath & sunlight if visible.any(): visible_flag = True t_visible = np.array(t.utc_iso())[visible] t_visible_0 = (Time(t_visible[0]) + timezone * u.hour).iso t_visible_1 = (Time(t_visible[-1]) + timezone * u.hour).iso during = round((Time(t_visible[-1]) - Time(t_visible[0])).sec) if during >= visible_duration: outfile0.write('{:s},{:s},{:d},{:d}\n'.format( t_visible_0, t_visible_1, noradid, int(during))) # Generate a one-day prediction file if Time(pass_end) < t_start + 1: for i in range(len(t)): outfile.write( '{:20s} {:>8.4f} {:>8.4f} {:>8.5f} {:>8.4f} {:>10.4f} {:>10.4f} {:>7d} \n' .format( t.utc_strftime('%Y-%m-%d %H:%M:%S')[i], sat_alt.degrees[i], sat_az.degrees[i], sat_ra.hours[i], sat_dec.degrees[i], sat_distance.km[i], sun_alt.degrees[i], visible[i])) outfile.write('\n') ''' # Generate a one-day prediction in visibility period if visible.any(): t_visible = np.array(t.utc_iso())[visible] for i in range(len(t_visible)): outfile.write('{:20s} {:>8.4f} {:>8.4f} {:>8.5f} {:>8.4f} {:>10.4f} \n'.format(t_visible[i],sat_alt.degrees[visible][i],sat_az.degrees[visible][i],sat_ra.hours[visible][i],sat_dec.degrees[visible][i],sat_distance.km[visible][i])) outfile.write('\n') ''' if not visible_flag: outfile0.write('{:s},{:s},{:d}\n\n'.format('', '', noradid)) else: outfile0.write('\n') outfile.close() print(noradid) outfile0.close() print('Generate multiple-day visible passes for all targets.') # Sort by the start time of the visible passes dates, temp = [], [] VisiblePasses = pd.read_csv(dir_prediction + filename0, dtype=object) for date in VisiblePasses[header[0]]: if str(date) != 'nan': dates.append(date[:10]) dates = np.sort(list(set(dates))) for date in dates: date_flag = VisiblePasses[header[0]].str.contains(date, na=False) temp.append(VisiblePasses[date_flag].sort_values( by=[header[0]]).append(pd.Series(dtype=object), ignore_index=True)) if not temp: print('No visible passes are found!') else: pd.concat(temp).to_csv(dir_prediction + 'VisiblePasses_bydate.csv', index=False, mode='w') print('Over!')
from skyfield.api import Loader, Topos from skyfield.constants import AU_M from skyfield.earthlib import reverse_terra from skyfield.timelib import Time from skyfield.trigonometry import position_angle_of from skyfield.units import Angle import sip import sys import subprocess load = Loader('~/.skyfield-data', verbose=False) planets = load('de421.bsp') earth, moon, sun = planets['earth'], planets['moon'], planets['sun'] ts = load.timescale(builtin=False) # synthesize a flat-shaded moon. # size: size of output image in pixels # moon_diameter: diameter of moon in pixels (should be less than size) # sun_angle: position angle to sun in degrees # phase: sun-moon-earth angle in degrees def synthesize_moon(size, moon_diameter_pixels, sun_angle, moon_phase): old_seterr = np.seterr(invalid='ignore') s = np.array((0, 0, 1)) r = R.from_euler('y', moon_phase, degrees=True) s = r.apply(s) r = R.from_euler('z', sun_angle, degrees=True) s = r.apply(s)
import json import pytest from skyfield.api import Loader from django.conf import settings from v0.accesses import Access load = Loader(settings.EPHEM_DIR) timescale = load.timescale() def test_access_id_roundtrip(): sat_id = 5 gs_id = 6 time = timescale.utc(2018, 5, 23, 1, 2, 3) accid = Access.encode_access_id(sat_id, gs_id, time) assert (sat_id, gs_id, time) == Access.decode_access_id(accid)
import xml.etree.ElementTree as ET from datetime import datetime, timedelta, timezone import dbf import json from skyfield import api from skyfield import almanac from skyfield.api import Loader import math ziptable = dbf.Table(modules.config['storage']['zipdbf']) ziptable.open() # Yeah, so this is slow and I need to figure out how to persist it. zipidx = ziptable.create_index(lambda rec: rec.zcta5ce10) loader = Loader(modules.config['storage']['skyfield']) ts = loader.timescale() ephem = loader('de421.bsp') def actions(): return { '': get, 'get': get, 'details': details, 'astronomical': astronomical, 'srss': astronomical, } def with_zipcode(fcn): def parse_with_zipcode(parser, fstdin, *args):
from skyfield.api import Topos, Loader load = Loader('./Skyfield-Data', expire=False, verbose=False) planets = load('de423.bsp') earth = planets['earth'] from astropy.constants import c c = c.to(u.au / u.year) GDEGENERATE = 0.2 SIGMA2_USE_ENERGY = 9 ts = load.timescale() self.theta_x, self.theya_y, self.dtheta_x, self.dtheta_y = equatorial_to_tangent() # Returns xyz position of ground-based observatory. # Need to implement obscode! Hard-coded for DECam def earth3d(self): t = ts.tt(jd=self.obsdate) earth += Topos('30.169 S', '70.804 W', elevation_m=2200) return earth.at(t).position.au * u.au def kbo2d_linear(self): x_earth, y_earth, z_earth = earth3d() t_emit = self.obsdate - z_earth / c X = self.a + self.adot * t_emit - self.g * x_earth - self.gdot * (self.adot * t_emit**2 - self.g * x_earth * t_emit) Y = self.b + self.bdot * t_emit - self.g * y_earth - self.gdot * (self.bdot * t_emit**2 - self.g * y_earth * t_emit) dX = np.array([np.ones(nobs), t_emit, np.zeros(nobs), np.zeros(nobs), -x_earth, self.g * x_earth * t_emit - self.adot * t_emit**2])
def test_positions_skyfield(tmpdir): """ Test positions against those generated by skyfield. """ load = Loader(tmpdir) t = Time('1980-03-25 00:00') location = None # skyfield ephemeris try: planets = load('de421.bsp') ts = load.timescale() except OSError as e: if os.environ.get('CI', False) and 'timed out' in str(e): pytest.xfail('Timed out in CI') else: raise mercury, jupiter, moon = planets['mercury'], planets[ 'jupiter barycenter'], planets['moon'] earth = planets['earth'] skyfield_t = ts.from_astropy(t) if location is not None: earth = earth + Topos(latitude_degrees=location.lat.to_value(u.deg), longitude_degrees=location.lon.to_value(u.deg), elevation_m=location.height.to_value(u.m)) skyfield_mercury = earth.at(skyfield_t).observe(mercury).apparent() skyfield_jupiter = earth.at(skyfield_t).observe(jupiter).apparent() skyfield_moon = earth.at(skyfield_t).observe(moon).apparent() if location is not None: frame = TETE(obstime=t, location=location) else: frame = TETE(obstime=t) ra, dec, dist = skyfield_mercury.radec(epoch='date') skyfield_mercury = SkyCoord(ra.to(u.deg), dec.to(u.deg), distance=dist.to(u.km), frame=frame) ra, dec, dist = skyfield_jupiter.radec(epoch='date') skyfield_jupiter = SkyCoord(ra.to(u.deg), dec.to(u.deg), distance=dist.to(u.km), frame=frame) ra, dec, dist = skyfield_moon.radec(epoch='date') skyfield_moon = SkyCoord(ra.to(u.deg), dec.to(u.deg), distance=dist.to(u.km), frame=frame) # planet positions w.r.t true equator and equinox moon_astropy = get_moon(t, location, ephemeris='de430').transform_to(frame) mercury_astropy = get_body('mercury', t, location, ephemeris='de430').transform_to(frame) jupiter_astropy = get_body('jupiter', t, location, ephemeris='de430').transform_to(frame) assert (moon_astropy.separation(skyfield_moon) < skyfield_angular_separation_tolerance) assert (moon_astropy.separation_3d(skyfield_moon) < skyfield_separation_tolerance) assert (jupiter_astropy.separation(skyfield_jupiter) < skyfield_angular_separation_tolerance) assert (jupiter_astropy.separation_3d(skyfield_jupiter) < skyfield_separation_tolerance) assert (mercury_astropy.separation(skyfield_mercury) < skyfield_angular_separation_tolerance) assert (mercury_astropy.separation_3d(skyfield_mercury) < skyfield_separation_tolerance) planets.close()
def pslv_analysis(): obj_id = 90000 # Using Lawrence orbit parameters h = 505. a = Re + h e = 1e-4 LTAN = 10. launch_dt = datetime(2018, 11, 19, 16, 30, 0) RAAN = sunsynch_RAAN(launch_dt, LTAN) i = sunsynch_inclination(a, e) w = 0. M = 180. kep_dict = {} kep_dict[obj_id] = {} kep_dict[obj_id]['a'] = a kep_dict[obj_id]['e'] = e kep_dict[obj_id]['i'] = i kep_dict[obj_id]['RAAN'] = RAAN kep_dict[obj_id]['w'] = w kep_dict[obj_id]['M'] = M kep_dict[obj_id]['UTC'] = launch_dt tle_dict, tle_df = kep2tle([obj_id], kep_dict) print(tle_dict) print(a, e, i, RAAN, w, M) UTC = datetime(2018, 11, 19, 16, 30, 0) load = Loader(os.path.join(metis_dir, 'skyfield_data')) ephemeris = load('de430t.bsp') ts = load.timescale() sensor_id_list = [ 'Stromlo Optical', 'RMIT ROO', 'UNSW Falcon', 'FLC Falcon', 'Mamalluca Falcon' ] # Times for visibility check ndays = 14 dt = 10 obj_id_list = [obj_id] UTC0 = ts.utc(UTC.replace(tzinfo=utc)).utc sec_array = list(range(0, 86400 * ndays, dt)) skyfield_times = ts.utc(UTC0[0], UTC0[1], UTC0[2], UTC0[3], UTC0[4], sec_array) vis_dict = compute_visible_passes(skyfield_times, obj_id_list, sensor_id_list, ephemeris, tle_dict) print(vis_dict) # Generate output file vis_file_min_el = 0. outdir = os.getcwd() vis_file = os.path.join(outdir, 'PSLV_visible_passes.csv') generate_visibility_file(vis_dict, vis_file, vis_file_min_el) return
class RemoteSensingControlWidget(QtWidgets.QWidget, ui.Ui_RemoteSensingDockWidget): """This class implements the remote sensing functionality as dockable widget. """ def __init__(self, parent=None, view=None): """ Arguments: parent -- Qt widget that is parent to this widget. view -- reference to mpl canvas class """ super(RemoteSensingControlWidget, self).__init__(parent) self.setupUi(self) self.view = view self.load = Loader(MSS_CONFIG_PATH, verbose=False) self.planets = self.load('de421.bsp') self.timescale = self.load.timescale(builtin=True) # don't download files, use shipped files button = self.btTangentsColour palette = QtGui.QPalette(button.palette()) colour = QtGui.QColor() colour.setRgbF(1, 0, 0, 1) palette.setColor(QtGui.QPalette.Button, colour) button.setPalette(palette) self.dsbTangentHeight.setValue(10.) self.dsbObsAngleAzimuth.setValue(90.) self.dsbObsAngleElevation.setValue(-1.0) # update plot on every value change self.cbDrawTangents.stateChanged.connect(self.update_settings) self.cbShowSolarAngle.stateChanged.connect(self.update_settings) self.btTangentsColour.clicked.connect(self.set_tangentpoint_colour) self.dsbTangentHeight.valueChanged.connect(self.update_settings) self.dsbObsAngleAzimuth.valueChanged.connect(self.update_settings) self.dsbObsAngleElevation.valueChanged.connect(self.update_settings) self.cbSolarBody.currentIndexChanged.connect(self.update_settings) self.cbSolarAngleType.currentIndexChanged.connect(self.update_settings) self.lbSolarCmap.setText( "Solar angle colours, dark to light: reds (0-15), violets (15-45), greens (45-180)" ) self.solar_cmap = ListedColormap([(1.00, 0.00, 0.00, 1.0), (1.00, 0.45, 0.00, 1.0), (1.00, 0.75, 0.00, 1.0), (0.47, 0.10, 1.00, 1.0), (0.72, 0.38, 1.00, 1.0), (1.00, 0.55, 1.00, 1.0), (0.00, 0.70, 0.00, 1.0), (0.33, 0.85, 0.33, 1.0), (0.65, 1.00, 0.65, 1.0)]) self.solar_norm = BoundaryNorm( [0, 5, 10, 15, 25, 35, 45, 90, 135, 180], self.solar_cmap.N) self.update_settings() @staticmethod def compute_view_angles(lon0, lat0, h0, lon1, lat1, h1, obs_azi, obs_ele): mlat = ((lat0 + lat1) / 2.) lon0 *= np.cos(np.deg2rad(mlat)) lon1 *= np.cos(np.deg2rad(mlat)) dlon = lon1 - lon0 dlat = lat1 - lat0 obs_azi_p = fix_angle(obs_azi + np.rad2deg(np.arctan2(dlon, dlat))) return obs_azi_p, obs_ele def compute_body_angle(self, body, jsec, lon, lat): t = self.timescale.utc(jsec_to_datetime(jsec).replace(tzinfo=utc)) loc = self.planets["earth"] + Topos(lat, lon) astrometric = loc.at(t).observe(self.planets[body]) alt, az, d = astrometric.apparent().altaz() return az.degrees, alt.degrees def update_settings(self): """ Updates settings in TopView and triggers a redraw. """ settings = { "reference": self, "draw_tangents": self.cbDrawTangents.isChecked(), } if self.cbShowSolarAngle.isChecked(): settings["show_solar_angle"] = self.cbSolarAngleType.currentText( ), self.cbSolarBody.currentText() else: settings["show_solar_angle"] = None self.view.set_remote_sensing_appearance(settings) def set_tangentpoint_colour(self): """Slot for the colour buttons: Opens a QColorDialog and sets the new button face colour. """ button = self.btTangentsColour palette = QtGui.QPalette(button.palette()) colour = palette.color(QtGui.QPalette.Button) colour = QtWidgets.QColorDialog.getColor(colour) if colour.isValid(): palette.setColor(QtGui.QPalette.Button, colour) button.setPalette(palette) self.update_settings() def compute_tangent_lines(self, bmap, wp_vertices, wp_heights): """ Computes Tangent points of limb sounders aboard the aircraft Args: bmap: Projection of TopView wp_vertices: waypoints of the flight path wp_heights: altitude of the waypoints of flight path Returns: LineCollection of dotted lines at tangent point locations """ x, y = list(zip(*wp_vertices)) wp_lons, wp_lats = bmap(x, y, inverse=True) fine_lines = [ bmap.gcpoints2(wp_lons[i], wp_lats[i], wp_lons[i + 1], wp_lats[i + 1], del_s=10., map_coords=False) for i in range(len(wp_lons) - 1) ] line_heights = [ np.linspace(wp_heights[i], wp_heights[i + 1], num=len(fine_lines[i][0])) for i in range(len(fine_lines)) ] # fine_lines = list of tuples with x-list and y-list for each segment tplines = [ self.tangent_point_coordinates( fine_lines[i][0], fine_lines[i][1], line_heights[i], cut_height=self.dsbTangentHeight.value()) for i in range(len(fine_lines)) ] dirlines = self.direction_coordinates(wp_lons, wp_lats) lines = tplines + dirlines for i, line in enumerate(lines): for j, (lon, lat) in enumerate(line): line[j] = bmap(lon, lat) lines[i] = line return LineCollection( lines, colors=QtGui.QPalette(self.btTangentsColour.palette()).color( QtGui.QPalette.Button).getRgbF(), zorder=2, animated=True, linewidth=3, linestyles=[':'] * len(tplines) + ['-'] * len(dirlines)) def compute_solar_lines(self, bmap, wp_vertices, wp_heights, wp_times, solartype): """ Computes coloured overlay over the flight path that indicates the danger of looking into the sun with a limb sounder aboard the aircraft. Args: bmap: Projection of TopView wp_vertices: waypoints of the flight path wp_heights: altitude of the waypoints of flight path Returns: LineCollection of coloured lines according to the angular distance between viewing direction and solar angle """ # calculate distances and times body, difftype = solartype times = [datetime_to_jsec(_wp_time) for _wp_time in wp_times] x, y = list(zip(*wp_vertices)) wp_lons, wp_lats = bmap(x, y, inverse=True) fine_lines = [ bmap.gcpoints2(wp_lons[i], wp_lats[i], wp_lons[i + 1], wp_lats[i + 1], map_coords=False) for i in range(len(wp_lons) - 1) ] line_heights = [ np.linspace(wp_heights[i], wp_heights[i + 1], num=len(fine_lines[i][0])) for i in range(len(fine_lines)) ] line_times = [ np.linspace(times[i], times[i + 1], num=len(fine_lines[i][0])) for i in range(len(fine_lines)) ] # fine_lines = list of tuples with x-list and y-list for each segment # lines = list of tuples with lon-list and lat-list for each segment heights = [] times = [] for i in range(len(fine_lines) - 1): heights.extend(line_heights[i][:-1]) times.extend(line_times[i][:-1]) heights.extend(line_heights[-1]) times.extend(line_times[-1]) solar_x = [] solar_y = [] for i in range(len(fine_lines) - 1): solar_x.extend(fine_lines[i][0][:-1]) solar_y.extend(fine_lines[i][1][:-1]) solar_x.extend(fine_lines[-1][0]) solar_y.extend(fine_lines[-1][1]) points = [] old_wp = None total_distance = 0 for i, (lon, lat) in enumerate(zip(solar_x, solar_y)): points.append([[lon, lat] ]) # append double-list for later concatenation if old_wp is not None: wp_dist = get_distance((old_wp[0], old_wp[1]), (lat, lon)) * 1000. total_distance += wp_dist old_wp = (lat, lon) vals = [] for i in range(len(points) - 1): p0, p1 = points[i][0], points[i + 1][0] sol_azi, sol_ele = self.compute_body_angle(body, times[i], p0[0], p0[1]) obs_azi, obs_ele = self.compute_view_angles( p0[0], p0[1], heights[i], p1[0], p1[1], heights[i + 1], self.dsbObsAngleAzimuth.value(), self.dsbObsAngleElevation.value()) if sol_azi < 0: sol_azi += 360 if obs_azi < 0: obs_azi += 360 rating = self.calc_view_rating(obs_azi, obs_ele, sol_azi, sol_ele, heights[i], difftype) vals.append(rating) # convert lon, lat to map points for i in range(len(points)): points[i][0][0], points[i][0][1] = bmap(points[i][0][0], points[i][0][1]) points = np.concatenate([points[:-1], points[1:]], axis=1) # plot solar_lines = LineCollection(points, cmap=self.solar_cmap, norm=self.solar_norm, zorder=2, linewidths=3, animated=True) solar_lines.set_array(np.array(vals)) return solar_lines def tangent_point_coordinates(self, lon_lin, lat_lin, flight_alt=14, cut_height=12): """ Computes coordinates of tangent points given coordinates of flight path. Args: lon_lin: longitudes of flight path lat_lin: latitudes of flight path flight_alt: altitude of aircraft (scalar or numpy array) cut_height: altitude of tangent points Returns: List of tuples of longitude/latitude coordinates """ lins = list(zip(lon_lin[0:-1], lon_lin[1:], lat_lin[0:-1], lat_lin[1:])) lins = [(x0 * np.cos(np.deg2rad(np.mean([y0, y1]))), x1 * np.cos(np.deg2rad(np.mean([y0, y1]))), y0, y1) for x0, x1, y0, y1 in lins] direction = [(x1 - x0, y1 - y0) for x0, x1, y0, y1 in lins] direction = [(_x / np.hypot(_x, _y), _y / np.hypot(_x, _y)) for _x, _y in direction] los = [ rotate_point(point, -self.dsbObsAngleAzimuth.value()) for point in direction ] los.append(los[-1]) if isinstance(flight_alt, (collections.abc.Sequence, np.ndarray)): dist = [(np.sqrt( max((EARTH_RADIUS + a)**2 - (EARTH_RADIUS + cut_height)**2, 0)) / 110.) for a in flight_alt[:-1]] dist.append(dist[-1]) else: dist = (np.sqrt((EARTH_RADIUS + flight_alt)**2 - (EARTH_RADIUS + cut_height)**2) / 110.) tp_dir = (np.array(los).T * dist).T tps = [(x0 + tp_x, y0 + tp_y, y0) for ((x0, x1, y0, y1), (tp_x, tp_y)) in zip(lins, tp_dir)] tps = [(x0 / np.cos(np.deg2rad(yp)), y0) for (x0, y0, yp) in tps] return tps def direction_coordinates(self, lon_lin, lat_lin): """ Computes coordinates of tangent points given coordinates of flight path. Args: lon_lin: longitudes of flight path lat_lin: latitudes of flight path flight_alt: altitude of aircraft (scalar or numpy array) cut_height: altitude of tangent points Returns: List of tuples of longitude/latitude coordinates """ lins = list(zip(lon_lin[0:-1], lon_lin[1:], lat_lin[0:-1], lat_lin[1:])) lins = [(x0 * np.cos(np.deg2rad(np.mean([y0, y1]))), x1 * np.cos(np.deg2rad(np.mean([y0, y1]))), y0, y1) for x0, x1, y0, y1 in lins] lens = [np.hypot(x1 - x0, y1 - y0) * 110. for x0, x1, y0, y1 in lins] lins = [_x for _x, _l in zip(lins, lens) if _l > 10] direction = [(0.5 * (x0 + x1), 0.5 * (y0 + y1), x1 - x0, y1 - y0) for x0, x1, y0, y1 in lins] direction = [(_u, _v, _x / np.hypot(_x, _y), _y / np.hypot(_x, _y)) for _u, _v, _x, _y in direction] los = [ rotate_point(point[2:], -self.dsbObsAngleAzimuth.value()) for point in direction ] dist = 1. tp_dir = (np.array(los).T * dist).T tps = [(x0, y0, x0 + tp_x, y0 + tp_y) for ((x0, y0, _, _), (tp_x, tp_y)) in zip(direction, tp_dir)] tps = [[(x0 / np.cos(np.deg2rad(y0)), y0), (x1 / np.cos(np.deg2rad(y0)), y1)] for (x0, y0, x1, y1) in tps] return tps @staticmethod def calc_view_rating(obs_azi, obs_ele, sol_azi, sol_ele, height, difftype): """ Calculates the angular distance between given directions under the condition that the sun is above the horizon. Args: obs_azi: observator azimuth angle obs_ele: observator elevation angle sol_azi: solar azimuth angle sol_ele: solar elevation angle height: altitude of observer Returns: angular distance or 180 degrees if sun is below horizon """ delta_azi = obs_azi - sol_azi delta_ele = obs_ele - sol_ele if "horizon" in difftype: thresh = -np.rad2deg( np.arccos(EARTH_RADIUS / (height + EARTH_RADIUS))) - 3 if sol_ele < thresh: delta_ele = 180 if "azimuth" == difftype: return np.abs(obs_azi - sol_azi) elif "elevation" == difftype: return np.abs(obs_ele - sol_ele) else: return np.hypot(delta_azi, delta_ele)
def spacex_ssoa_analysis(): # # Using Thomas excel file # obj_id = 90000 # UTC = datetime(2018, 6, 23, 2, 13, 21) # # ecef_dict = {} # ecef_dict[obj_id] = {} # # r_ITRF = np.array([-3651380.321, 1598487.431, -5610448.359]) * 0.001 # v_ITRF = np.array([5276.523548, -3242.081015, -4349.310553]) * 0.001 # # ecef_dict[obj_id]['r_ITRF'] = r_ITRF.reshape(3,1) # ecef_dict[obj_id]['v_ITRF'] = v_ITRF.reshape(3,1) # ecef_dict[obj_id]['UTC'] = UTC # Using Rasit TLE # obj_id_list = [43690, 43691, 43692, 43693, 43694, 43695, 43696, 43697] obj_id_list = [43758, 43763] # line1 = '1 99999U 18999B 18315.16116898 +.00000500 +00000-0 +32002-2 0 9993' # line2 = '2 99999 085.0168 090.4036 0012411 292.8392 108.1000 15.20833469601616' # # line1 = '1 99999U 18999B 18315.19693718 +.00000500 +00000-0 +60940-2 0 9999' # line2 = '2 99999 085.0165 102.9279 0012642 291.6624 115.0006 15.20806704601617' # line1 = '1 43690U 18088A 18315.20213355 .00000372 -11738-5 00000+0 0 9993' # line2 = '2 43690 85.0339 102.9499 0224293 222.7416 214.1638 15.71130100 06' # # UTC = tletime2datetime(line1) # tle_dict = {} # tle_dict[obj_id] = {} # tle_dict[obj_id]['line1_list'] = [line1] # tle_dict[obj_id]['line2_list'] = [line2] # tle_dict[obj_id]['UTC_list'] = [UTC] UTC = datetime(2018, 12, 3, 0, 0, 0) load = Loader(os.path.join(metis_dir, 'skyfield_data')) ephemeris = load('de430t.bsp') ts = load.timescale() sensor_id_list = [ 'RMIT ROO', 'UNSW Falcon', 'USAFA Falcon', 'Mamalluca Falcon' ] # Times for visibility check ndays = 5 dt = 10 UTC0 = ts.utc(UTC.replace(tzinfo=utc)).utc sec_array = list(range(0, 86400 * ndays, dt)) skyfield_times = ts.utc(UTC0[0], UTC0[1], UTC0[2], UTC0[3], UTC0[4], sec_array) vis_dict = compute_visible_passes(skyfield_times, obj_id_list, sensor_id_list, ephemeris) print(vis_dict) # Generate output file vis_file_min_el = 0. outdir = os.getcwd() vis_file = os.path.join(outdir, 'SSOA_visible_passes_2018_12_04.csv') generate_visibility_file(vis_dict, vis_file, vis_file_min_el) return