Example #1
0
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
Example #2
0
 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}!'
         ))
Example #3
0
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
Example #4
0
 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}!"
         )
     )
Example #5
0
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
Example #6
0
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)
Example #7
0
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
Example #8
0
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
Example #9
0
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()
Example #10
0
 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
Example #11
0
    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()
Example #12
0
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
Example #13
0
    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__()
Example #14
0
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
Example #16
0
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
Example #17
0
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))
Example #19
0
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
Example #20
0
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))
Example #21
0
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
Example #22
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
Example #23
0
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!')
Example #24
0
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)
Example #25
0
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)

Example #26
0
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):
Example #27
0
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])
Example #28
0
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()
Example #29
0
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
Example #30
0
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)
Example #31
0
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