Beispiel #1
0
    def test_transitioner(self):
        pachon = astroplan.Observer.at_site("Cerro Pachon")
        start_time = astropy.time.Time(59598.66945625747, format='mjd')
        readout_time = 2 * u.second
        exptime = 15 * u.second
        nexp = 2

        targets = [
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1339111328125 * u.deg,
                                                 dec=-17.532396316528303 *
                                                 u.deg),
                                  name="1857"),
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1758499145508 * u.deg,
                                                 dec=-12.2103328704834 *
                                                 u.deg),
                                  name="2100")
        ]

        constraints = [
            astroplan.AltitudeConstraint(30 * u.deg, 89 * u.deg),
            astroplan.AirmassConstraint(2.2),
            astroplan.AtNightConstraint.twilight_astronomical()
        ]
        obs_blocks = [
            astroplan.ObservingBlock.from_exposures(
                target,
                1,
                exptime,
                nexp,
                readout_time,
                configuration={'filter': band},
                constraints=constraints) for band in ('g', 'i')
            for target in targets
        ]

        transitioner = apsupp.LSSTTransitioner()
        for old_block, new_block in zip(obs_blocks[:-1], obs_blocks[1:]):
            block = transitioner(old_block, new_block, start_time, pachon)
            duration_seconds = (block.duration / u.second).value

            old_filter = old_block.configuration['filter']
            new_filter = new_block.configuration['filter']
            if old_filter == new_filter:
                min_expected_duration = 2
            else:
                min_expected_duration = 120

            self.assertGreaterEqual(duration_seconds, min_expected_duration)
            self.assertLess(duration_seconds, 600)
Beispiel #2
0
    def __init__(self, star, observatory, dataset="winter"):
        super().__init__()

        self.dataset = dataset

        self.star = star
        self.observatory = observatory
        # Define target parameterss
        coords = star.coordinates
        self.target = astroplan.FixedTarget(name=star.name, coord=coords)
        self.observer = astroplan.Observer(observatory)

        # Load data
        tapas_dir = join(dirname(__file__), "../../data/tapas/")
        tellw, self.airw, self.angw = load_tellurics(join(tapas_dir, "*winter*ipac.gz"))
        tells, self.airs, self.angs = load_tellurics(join(tapas_dir, "*summer*ipac.gz"))

        # Sort wavelength axis
        # We assume here that all files use the same wavelength axis
        wavew, waves = np.squeeze(tellw[0, :, 0]), np.squeeze(tells[0, :, 0])
        iiw, iis = np.argsort(wavew), np.argsort(waves)
        self.tellw, self.tells = tellw[:, iiw, :], tells[:, iis, :]

        # Create interpolator
        self.wavew, self.waves = (
            np.squeeze(self.tellw[0, :, 0]),
            np.squeeze(self.tells[0, :, 0]),
        )
        self.fluxw, self.fluxs = (
            np.squeeze(self.tellw[:, :, 1]),
            np.squeeze(self.tells[:, :, 1]),
        )
        self.tellwi = RegularGridInterpolator((self.airw, self.wavew), self.fluxw)
        self.tellsi = RegularGridInterpolator((self.airs, self.waves), self.fluxs)
Beispiel #3
0
def followup_targets(events, fields, area):
    """A generator of event/astroplan target pairs given events and fields

    All ra and dec values should be in decimal degrees.

    Args:
       - events :: a pandas.DataFrame with event coordinates
                   in ra and decl columns
       - fields :: a pandas.DataFrame with field coordinates
                   in fieldRA and fieldDec columns
       - area :: the search area in square degrees

    Returns:
       a succession of event/astroplan.FixedTarget pairs
    """

    event_fields = find_event_fields(events, fields, area)
    targets = OrderedDict()

    # there should be a faster (vectorized) way to do this.
    for idx, event in events.iterrows():
        targets = []
        for field_idx, field in event_fields.query(
                f'event_id=={event.event_id}').iterrows():
            field_coords = SkyCoord(ra=field.fieldRA * u.deg,
                                    dec=field.fieldDec * u.deg)
            field_name = "%d" % field.fieldID
            target = astroplan.FixedTarget(coord=field_coords, name=field_name)
            targets.append(target)

        yield event, targets
Beispiel #4
0
 def airmass(self):
     target = astroplan.FixedTarget(name=self.star.name,
                                    coord=self.star.coordinates)
     observer = astroplan.Observer(self.observatory_location)
     altaz = observer.altaz(self.datetime, target)
     airmass = altaz.secz.value
     return airmass
Beispiel #5
0
    def _set_fixed_targets(self, row):
        """
        Add a column of SkyCoords to pandas dataframe
        :return: 
        """

        return astroplan.FixedTarget(name=row['objname'], coord=row['SkyCoords'])
Beispiel #6
0
def _parse_single_target(target):
    try:
        crd = SkyCoord(target)
    except ValueError:  # The coordinate string is ambiguous; make assumptions
        if target[0].isalpha():
            crd = SkyCoord.from_name(target)
        elif ":" in target:
            crd = SkyCoord(target, unit="hour,deg")
        else:
            crd = SkyCoord(target, unit="deg")
    return astroplan.FixedTarget(crd)
Beispiel #7
0
    def __init__(self, star, observatory, degree=2, skip_resample=False):
        self.star = star
        self.observatory = observatory

        # Define target parameterss
        coords = star.coordinates
        self.target = astroplan.FixedTarget(name=star.name, coord=coords)
        self.observer = astroplan.Observer(observatory)

        self.degree = degree
        self.skip_resample = skip_resample
Beispiel #8
0
def rise_set(source, observatory, elevation_limit=None, when=None, lst=False):
    """Rise and set times for a source"""
    if isinstance(source, astroplan.FixedTarget):
        S = source
    elif isinstance(source, astropy.coordinates.SkyCoord):
        S = astroplan.FixedTarget(source)
    else:
        S = astroplan.FixedTarget.from_name(source)
    if isinstance(observatory, astroplan.Observer):
        L = observatory
    elif isinstance(observatory, astropy.coordinates.EarthLocation):
        L = astroplan.Observer(observatory)
    else:
        try:
            L = astroplan.Observer(observatory_location(observatory))
            if elevation_limit is None:
                elevation_limit = known_elevation_limits.get(
                    observatory_aliases[observatory.lower()], None)
        except ValueError:
            try:
                L = astroplan.Observer.from_site(observatory)
            except ValueError:
                raise ValueError(
                    "Observatory {!r} not found".format(observatory))
    if elevation_limit is None:
        elevation_limit = 0 * u.deg

    if when is None:
        T = astropy.time.Time.now()
    elif isinstance(when, astropy.time.Time):
        T = when
    else:
        T = astropy.time.Time(when)

    if L.target_is_up(T, S, horizon=elevation_limit):
        rise = L.target_rise_time(T,
                                  S,
                                  which="previous",
                                  horizon=elevation_limit)
    else:
        rise = L.target_rise_time(T, S, which="next", horizon=elevation_limit)
    set = L.target_set_time(rise, S, which="next", horizon=elevation_limit)
    rise.location = L.location
    set.location = L.location
    rise.format = "iso"
    set.format = "iso"
    if lst:
        return _rise_set(
            rise=format_sidereal_time(rise.sidereal_time("apparent")),
            set=format_sidereal_time(set.sidereal_time("apparent")),
        )
    else:
        return _rise_set(rise=rise, set=set)
Beispiel #9
0
def get_segments_tile(config_struct, observatory, radec, segmentlist):

    observer = astroplan.Observer(location=observatory)

    fxdbdy = astroplan.FixedTarget(coord=radec)

    date_start = Time(segmentlist[0][0], format='mjd', scale='utc')
    date_end = Time(segmentlist[-1][1], format='mjd', scale='utc')

    tilesegmentlist = segments.segmentlist()
    while date_start.mjd < date_end.mjd:
        date_rise = observer.target_rise_time(date_start, fxdbdy)
        date_set = observer.target_set_time(date_start, fxdbdy)

        print(date_rise.mjd, date_set.mjd)
        if (date_rise.mjd < 0) and (date_set.mjd < 0): break

        print(date_rise.mjd, date_set.mjd)

        if date_rise > date_set:
            date_rise = observer.target_rise_time(
                date_start - TimeDelta(24 * u.hour), fxdbdy)
        print(date_rise.mjd, date_set.mjd)

        segment = segments.segment(date_rise.mjd, date_set.mjd)
        tilesegmentlist = tilesegmentlist + segments.segmentlist([segment])
        tilesegmentlist.coalesce()

        date_start = date_set + TimeDelta(24 * u.hour)

    #moonsegmentlist = get_skybrightness(\
    #    config_struct,segmentlist,observer,fxdbdy,radec)

    moonsegmentlist = get_moon_segments(\
        config_struct,segmentlist,observer,fxdbdy,radec)

    tilesegmentlistdic = segments.segmentlistdict()
    tilesegmentlistdic["observations"] = segmentlist
    tilesegmentlistdic["tile"] = tilesegmentlist
    tilesegmentlistdic["moon"] = moonsegmentlist
    tilesegmentlist = tilesegmentlistdic.intersection(
        ["observations", "tile", "moon"])
    tilesegmentlist.coalesce()

    return tilesegmentlist
Beispiel #10
0
    def calcTargetLimits(self, X=1.5):
        """
        Calculate the limites of each target candidates
        
        Input:
        X -  airmass cutoff limit

        Computes:
        S - Start time target is above X (units: JD) [Ntargs]
        F - Set time of target below X   (units: JD) [Ntargs]
        alt - array of alts for target   [Ntimesteps, Ntargs]
        """
        Ntargs = len(self.dat.ids)
        # Set up arrays
        alt = np.zeros((len(self.time_window), Ntargs))  # altitudes of targets
        S = np.zeros(Ntargs)  # Start time above X
        F = np.zeros(Ntargs)  # Time sets below X
        for star in range(0, Ntargs):
            tempcoords = SkyCoord(self.dat.RA[star],
                                  self.dat.DEC[star],
                                  frame='icrs',
                                  unit=(u.hourangle, u.deg))
            temptarg = astroplan.FixedTarget(name=self.dat.ids[star],
                                             coord=tempcoords)
            self.whipple = astroplan.Observer.at_site('whipple')
            alt[:,
                star] = self.whipple.altaz(Time(self.time_window, format='jd'),
                                           temptarg).alt
            max_alt = np.arccos(1.0 / X) * 180.0 / np.pi
            star_up = np.where(alt[:, star] > 40.0)[0]
            if len(star_up) != 0:
                S[star] = self.time_window[star_up][0]  #star starts being up
                F[star] = self.time_window[star_up][
                    -1]  #star sets below 30 deg
            else:
                S[star] = 9.e9
                F[star] = -9.e9
        self.S = S
        self.F = F
        self.alt = alt
Beispiel #11
0
 def target(self):
     """Representation of the RA and Dec of this Obj as an
     astroplan.FixedTarget."""
     coord = ap_coord.SkyCoord(self.ra, self.dec, unit='deg')
     return astroplan.FixedTarget(name=self.id, coord=coord)
def single_planet(data,
                  date_start,
                  date_end,
                  constraints,
                  observer,
                  verbose=0):
    """
    Performs the observability and SNR estimation for a single planet

    Parameters
    ----------
    data : dict
        data for this planet
    date_start : Time
        start date
    date_end : Time
        end date
    constraints : list
        list of observability constraints
    observer : astroplan.Observer
        telescope location
    verbose : int, optional
        how much information to print, by default 0

    Returns
    -------
    result : dict
        contains everything about this planet, one entry per observable transit
    """
    set_verbose_level(verbose)

    primary_eclipse_time = Time(data["pl_tranmid"], format="jd")
    orbital_period = data["pl_orbper"] * u.day
    eclipse_duration = data["pl_trandur"] * u.hour
    name = data["pl_name"]
    # Update the progress bar
    # Define the target coordinates
    coord = SkyCoord(ra=data["ra"], dec=data["dec"], unit=u.deg)
    target = ap.FixedTarget(coord, name=name)
    # Define the star-planet system
    system = ap.EclipsingSystem(
        primary_eclipse_time=primary_eclipse_time,
        orbital_period=orbital_period,
        duration=eclipse_duration,
        name=name,
    )
    # Find all eclipses of this system
    n_max_eclipses = (date_end - date_start) / orbital_period
    n_max_eclipses = max(n_max_eclipses, 1)
    eclipses = system.next_primary_ingress_egress_time(
        date_start, n_eclipses=n_max_eclipses)
    # If the last eclipse is past the final date, just cut it off
    if eclipses[-1][0] > date_end:
        eclipses = eclipses[:-1]
    if len(eclipses) == 0:
        logger.warning(f"No observable transits found for planet {name}")
        return None

    # Check if they are observable
    is_observable = ap.is_event_observable(constraints,
                                           observer,
                                           target,
                                           times_ingress_egress=eclipses)[0]
    observable_eclipses = eclipses[is_observable]
    n_eclipses = len(observable_eclipses)
    if n_eclipses == 0:
        logger.warning(f"No observable transits found for planet {name}")
        return None

    # Calculate the SNR for those that are left
    magnitude = data["sy_kmag"] * u.mag
    exptime = eclipse_duration
    # exptime = np.diff(observable_eclipses.jd, axis=1).ravel() * u.day
    obstime = Time(np.mean(observable_eclipses.jd, axis=1), format="jd")
    airmass = calculate_airmass(obstime, observer, target)
    snr = estimate_snr(magnitude, exptime, airmass)

    # Save results for later
    result = {
        "name": [name] * n_eclipses,
        "snr": snr.to_value(1),
        "exptime": [eclipse_duration.to_value(u.second)] * n_eclipses,
        "time": obstime.mjd,
        "time_begin": observable_eclipses[:, 0].datetime,
        "time_end": observable_eclipses[:, 1].datetime,
        "stellar_effective_temperature": [data["st_teff"]] * n_eclipses,
    }
    return result
Beispiel #13
0
def get_skybrightness(config_struct, segmentlist, observer, fxdbdy, radec):

    moonsegmentlist = segments.segmentlist()
    if config_struct["filt"] == "c":
        passband = "g"
    else:
        passband = config_struct["filt"]

    # Moon phase data (from Coughlin, Stubbs, and Claver Table 2)
    moon_phases = [2, 10, 45, 90]
    moon_data = {
        'u': [2.7, 3.1, 4.2, 5.7],
        'g': [2.4, 2.8, 3.8, 5.2],
        'r': [2.1, 2.5, 3.4, 4.9],
        'i': [1.9, 2.3, 3.3, 4.7],
        'z': [1.9, 2.2, 3.2, 4.6],
        'y': [1.8, 2.2, 3.1, 4.5]
    }

    # Determine moon data for this phase
    moon_data_passband = moon_data[passband]

    # Fits to solar sky brightness (from Coughlin, Stubbs, and Claver Table 4)
    sun_data = {
        'u': [88.5, -0.5, -0.5, 0.4],
        'g': [386.5, -2.2, -2.4, 0.8],
        'r': [189.0, -1.4, -1.1, 0.8],
        'i': [164.8, -1.5, -0.7, 0.6],
        'z': [231.2, -2.8, -0.7, 1.4],
        'zs': [131.1, -1.4, -0.5, 0.2],
        'y': [92.0, -1.3, -0.2, 0.9]
    }

    sun_data_error = {
        'u': [6.2, 0.1, 0.1, 0.1],
        'g': [34.0, 0.2, 0.2, 0.5],
        'r': [32.7, 0.2, 0.2, 0.5],
        'i': [33.1, 0.2, 0.2, 0.5],
        'z': [62.3, 0.3, 0.4, 0.9],
        'zs': [45.6, 0.2, 0.3, 0.6],
        'y': [32.7, 0.2, 0.2, 0.5]
    }

    # Determine sun data for this phase
    sun_data_passband = sun_data[passband]

    dt = 6.0 / 24.0
    tt = np.arange(segmentlist[0][0], segmentlist[-1][1] + dt, dt)

    fxdbdy = astroplan.FixedTarget(coord=radec)

    ra2 = radec.ra.radian
    d2 = radec.dec.radian

    # Where is the moon?
    for ii in range(len(tt) - 1):

        date_start = Time(tt[ii], format='mjd', scale='utc')

        alt_target = observer.altaz(date_start, fxdbdy).alt.deg
        az_target = observer.altaz(date_start, fxdbdy).az.deg
        #print("Altitude / Azimuth of target: %.5f / %.5f"%(alt_target,az_target))

        alt_moon = observer.moon_altaz(date_start).alt.deg
        az_moon = observer.moon_altaz(date_start).az.deg

        #print("Altitude / Azimuth of moon: %.5f / %.5f"%(alt_moon,az_moon))

        if (alt_target < 30.0) or (alt_moon < 30.0):
            total_mag, total_mag_error, flux_mag, flux_mag_error = np.inf, np.inf, np.inf, np.inf
        else:
            # Coverting both target and moon ra and dec to radians
            ra1 = astropy.coordinates.get_moon(date_start).ra.radian
            d1 = astropy.coordinates.get_moon(date_start).dec.radian

            # Calculate angle between target and moon
            cosA = np.sin(d1) * np.sin(d2) + np.cos(d1) * np.cos(d2) * np.cos(
                ra1 - ra2)
            angle = np.arccos(cosA) * (360 / (2 * np.pi))
            #print("Angle between moon and target: %.5f"%(angle))

            moon_phase = np.mod(
                observer.moon_phase(date_start).value * (360 / (2 * np.pi)),
                90)

            delta_mag = np.interp(moon_phase, moon_phases, moon_data_passband)
            delta_mag_error = 0.1 * delta_mag

            flux = sun_data_passband[0] + sun_data_passband[1]*angle +\
                sun_data_passband[2]*alt_target + sun_data_passband[3]*alt_moon
            flux_zp = sun_data_passband[0] + sun_data_passband[1]*90.0 +\
                sun_data_passband[2]*90.0 + sun_data_passband[3]*45.0

            # check if flux < 0: too small to fit
            if flux < 0:
                flux = 1e-10

            flux = flux * (10**11)
            flux_zp = flux_zp * (10**11)
            flux_mag = -2.5 * (np.log10(flux) - np.log10(flux_zp))

            sun_data_passband_error = sun_data_error[passband]
            flux_error = np.sqrt(sun_data_passband_error[0]**2 + sun_data_passband_error[1]**2 * angle**2 +\
                sun_data_passband_error[2]**2 * alt_target**2 + sun_data_passband_error[3]**2 * alt_moon**2)
            flux_error = flux_error * (10**11)

            flux_mag_error = 1.08574 * flux_error / flux

            # Determine total magnitude contribution
            total_mag = delta_mag + flux_mag
            total_mag_error = np.sqrt(delta_mag_error**2 + flux_mag_error**2)
            #print(tt[ii], angle, alt_target, alt_moon, total_mag, total_mag_error)
        if total_mag > 0.0:
            segment = segments.segment(tt[ii], tt[ii + 1])
            moonsegmentlist = moonsegmentlist + segments.segmentlist([segment])
            moonsegmentlist.coalesce()
        #else:
        #    print(tt[ii], angle, alt_target, alt_moon, total_mag, total_mag_error)

    moonsegmentlistdic = segments.segmentlistdict()
    moonsegmentlistdic["observations"] = segmentlist
    moonsegmentlistdic["moon"] = moonsegmentlist
    moonsegmentlist = moonsegmentlistdic.intersection(["observations", "moon"])
    moonsegmentlist.coalesce()

    #print("Keeping %.2f %% of data"%(100.0*np.sum(np.diff(moonsegmentlist))/np.sum(np.diff(segmentlist))))

    return moonsegmentlist
Beispiel #14
0
    def test_schedule(self):
        pachon = astroplan.Observer.at_site("Cerro Pachon")
        start_time = astropy.time.Time(59598.66945625747, format='mjd')
        readout_time = 2 * u.second
        exptime = 15 * u.second
        nexp = 2
        slew_rate = 0.5 * (0.66 + 0.57) * u.deg / u.second
        filter_change_time = {'filter': {'default': 120 * u.second}}

        transitioner = astroplan.Transitioner(slew_rate, filter_change_time)

        targets = [
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1339111328125 * u.deg,
                                                 dec=-17.532396316528303 *
                                                 u.deg),
                                  name="1857"),
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1758499145508 * u.deg,
                                                 dec=-12.2103328704834 *
                                                 u.deg),
                                  name="2100")
        ]

        constraints = [
            astroplan.AltitudeConstraint(30 * u.deg, 89 * u.deg),
            astroplan.AirmassConstraint(2.2),
            astroplan.AtNightConstraint.twilight_astronomical()
        ]
        obsblocks = [
            astroplan.ObservingBlock.from_exposures(
                target,
                1,
                exptime,
                nexp,
                readout_time,
                configuration={'filter': band},
                constraints=constraints) for band in ('g', 'i')
            for target in targets
        ]

        scheduler = apsupp.DirectScheduler(constraints=constraints,
                                           observer=pachon,
                                           transitioner=transitioner,
                                           time_resolution=1 * u.hour)
        schedule = astroplan.scheduling.Schedule(start_time,
                                                 start_time + 1 * u.day)
        scheduler(obsblocks, schedule)

        self.assertIsInstance(schedule.slots[1].block.target,
                              astroplan.FixedTarget)
        self.assertEqual(schedule.slots[1].block.target.name, '1857')
        self.assertIsInstance(schedule.slots[2].block,
                              astroplan.TransitionBlock)
        self.assertIsInstance(schedule.slots[3].block.target,
                              astroplan.FixedTarget)
        self.assertEqual(schedule.slots[3].block.target.name, '2100')
        self.assertIsInstance(schedule.slots[4].block,
                              astroplan.TransitionBlock)
        self.assertIsInstance(schedule.slots[5].block.target,
                              astroplan.FixedTarget)
        self.assertEqual(schedule.slots[5].block.target.name, '1857')
        self.assertIsInstance(schedule.slots[6].block,
                              astroplan.TransitionBlock)
        self.assertIsInstance(schedule.slots[7].block.target,
                              astroplan.FixedTarget)
        self.assertEqual(schedule.slots[7].block.target.name, '2100')
Beispiel #15
0
    def test_transitioner(self):
        observatory = ObservatoryModel()
        observatory.configure_from_module()
        pachon = astroplan.Observer.at_site("Cerro Pachon")
        start_time = astropy.time.Time(59598.66945625747, format='mjd')
        readout_time = observatory.params.readouttime * u.second
        exptime = 15 * u.second
        nexp = 2

        targets = [
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1339111328125 * u.deg,
                                                 dec=-17.532396316528303 *
                                                 u.deg),
                                  name="1857"),
            astroplan.FixedTarget(coord=SkyCoord(ra=81.1758499145508 * u.deg,
                                                 dec=-12.2103328704834 *
                                                 u.deg),
                                  name="2100")
        ]

        constraints = [
            astroplan.AltitudeConstraint(30 * u.deg, 89 * u.deg),
            astroplan.AirmassConstraint(2.2),
            astroplan.AtNightConstraint.twilight_astronomical()
        ]
        obs_blocks = [
            astroplan.ObservingBlock.from_exposures(
                target,
                1,
                exptime,
                nexp,
                readout_time,
                configuration={'filter': band},
                constraints=constraints) for band in ('g', 'i')
            for target in targets
        ]

        self.assertGreater(observatory.params.readouttime, 1.5)
        self.assertGreater(observatory.params.filter_changetime, 15.0)

        slew_src = SlewTimeSource()
        transitioner = apsupp.OpsimTransitioner(slew_src)
        whitepaper_transitioner = apsupp.LSSTTransitioner()
        for old_block, new_block in zip(obs_blocks[:-1], obs_blocks[1:]):
            block = transitioner(old_block, new_block, start_time, pachon)
            wp_block = whitepaper_transitioner(old_block, new_block,
                                               start_time, pachon)
            duration_seconds = (block.duration / u.second).value
            wp_duration_seconds = (wp_block.duration / u.second).value

            self.assertAlmostEqual(duration_seconds,
                                   wp_duration_seconds,
                                   delta=5)

            old_filter = old_block.configuration['filter']
            new_filter = new_block.configuration['filter']
            if old_filter == new_filter:
                min_expected_duration = observatory.params.readouttime
            else:
                min_expected_duration = max(
                    observatory.params.readouttime,
                    observatory.params.filter_changetime)

            self.assertGreaterEqual(duration_seconds, min_expected_duration)
            self.assertLess(duration_seconds, 600)
Beispiel #16
0
    def __init__(
        self,
        name: str,
        ra: float = None,
        dec: float = None,
        arrivaltime: str = None,
        date: str = None,
        max_airmass=2.0,
        observationlength: float = 300,
        bands: list = ["g", "r"],
        alertsource: str = None,
        verbose: bool = True,
        **kwargs,
    ):

        self.name = name
        self.arrivaltime = arrivaltime
        self.alertsource = alertsource
        self.max_airmass = max_airmass
        self.observationlength = observationlength
        self.bands = bands
        self.ra_err = None
        self.dec_err = None
        self.warning = None
        self.observable = True
        self.rejection_reason = None
        self.datasource = None
        self.found_in_archive = False
        self.search_full_archive = False

        if ra is None and self.alertsource in icecube:
            if verbose:
                print("Parsing an IceCube alert")

            # Check if request is archival:
            archive, latest_archive_no = gcn_parser.get_gcn_circulars_archive()

            # Check if the alert is younger than latest archive entry
            archival_names = [entry[0] for entry in archive]
            archival_dates = [int(entry[2:-1]) for entry in archival_names]
            latest_archival = max(archival_dates)
            this_alert_date = int(self.name[2:-1])
            if this_alert_date > latest_archival:
                if verbose:
                    print(
                        "Alert too new, no GCN circular available yet. Using latest GCN notice"
                    )
            else:
                if verbose:
                    print("Alert info should be in GCN circular archive")
                self.search_full_archive = True

            if self.search_full_archive:
                self.search_match_in_archive(archive)

                # Well, if it's not in the latest archive, use the full
                # backwards search
                while self.found_in_archive is False:
                    archive, _ = gcn_parser.get_gcn_circulars_archive(latest_archive_no)
                    self.search_match_in_archive(archive)
                    latest_archive_no -= 1

            if self.found_in_archive:
                gcn_info = gcn_parser.parse_gcn_circular(self.gcn_nr)
                self.ra = gcn_info["ra"]
                self.ra_err = gcn_info["ra_err"]
                self.dec = gcn_info["dec"]
                self.dec_err = gcn_info["dec_err"]
                self.arrivaltime = gcn_info["time"]

            else:
                if verbose:
                    print("No archival GCN circular found. Using newest notice!")
                (
                    ra_notice,
                    dec_notice,
                    self.arrivaltime,
                    revision,
                ) = gcn_parser.parse_latest_gcn_notice()
                gcn_nr_latest = archive[0][1]
                gcn_info = gcn_parser.parse_gcn_circular(gcn_nr_latest)
                ra_circ = gcn_info["ra"]
                ra_err_circ = gcn_info["ra_err"]
                dec_circ = gcn_info["dec"]
                dec_err_circ = gcn_info["dec_err"]
                coords_notice = SkyCoord(
                    ra_notice * u.deg, dec_notice * u.deg, frame="icrs"
                )
                coords_circular = SkyCoord(
                    ra_circ * u.deg, dec_circ * u.deg, frame="icrs"
                )
                separation = coords_notice.separation(coords_circular).deg
                if separation < 1:
                    self.ra = ra_circ
                    self.dec = dec_circ
                    self.ra_err = ra_err_circ
                    self.dec_err = dec_err_circ
                    self.datasource = f"GCN Circular {gcn_nr_latest}\n"
                else:
                    self.ra = ra_notice
                    self.dec = dec_notice
                    self.datasource = f"GCN Notice (Rev. {revision})\n"

        elif ra is None and self.alertsource in ztf:
            if is_ztf_name(name):
                print(f"{name} is a ZTF name. Looking in Fritz database for ra/dec")
                from ztf_plan_obs.fritzconnector import FritzInfo

                fritz = FritzInfo([name])

                self.ra = fritz.queryresult["ra"]
                self.dec = fritz.queryresult["dec"]

                self.datasource = "Fritz\n"

                if np.isnan(self.ra):
                    raise ValueError("Object apparently not found on Fritz")

                print("\nFound ZTF object information on Fritz")
        elif ra is None:
            raise ValueError("Please enter ra and dec")

        else:
            self.ra = ra
            self.dec = dec

        self.coordinates = SkyCoord(self.ra * u.deg, self.dec * u.deg, frame="icrs")
        self.coordinates_galactic = self.coordinates.galactic
        self.target = ap.FixedTarget(name=self.name, coord=self.coordinates)
        self.site = Observer.at_site("Palomar", timezone="US/Pacific")
        #'Roque de los Muchachos'
        self.now = Time(datetime.utcnow())
        self.date = date

        if self.date is not None:
            self.start_obswindow = Time(self.date + " 00:00:00.000000")

        else:
            self.start_obswindow = Time(
                str(self.now.datetime.date()) + " 00:00:00.000000"
            )

        self.end_obswindow = Time(self.start_obswindow.mjd + 1, format="mjd").iso

        constraints = [
            ap.AltitudeConstraint(20 * u.deg, 90 * u.deg),
            ap.AirmassConstraint(max_airmass),
            ap.AtNightConstraint.twilight_astronomical(),
        ]

        # Obtain moon coordinates at Palomar for the full time window
        times = Time(self.start_obswindow + np.linspace(0, 24, 1000) * u.hour)
        moon_times = Time(self.start_obswindow + np.linspace(0, 24, 50) * u.hour)
        moon_coords = []

        for time in moon_times:
            moon_coord = astropy.coordinates.get_moon(
                time=time, location=self.site.location
            )
            moon_coords.append(moon_coord)
        self.moon = moon_coords

        airmass = self.site.altaz(times, self.target).secz
        airmass = np.ma.array(airmass, mask=airmass < 1)
        airmass = airmass.filled(fill_value=99)
        airmass = [x.value for x in airmass]

        self.twilight_evening = self.site.twilight_evening_astronomical(
            Time(self.start_obswindow), which="next"
        )
        self.twilight_morning = self.site.twilight_morning_astronomical(
            Time(self.start_obswindow), which="next"
        )

        indices_included = []
        airmasses_included = []
        times_included = []

        for index, t_mjd in enumerate(times.mjd):
            if (
                t_mjd > self.twilight_evening.mjd + 0.01
                and t_mjd < self.twilight_morning.mjd - 0.01
            ):
                if airmass[index] < 2.0:
                    indices_included.append(index)
                    airmasses_included.append(airmass[index])
                    times_included.append(times[index])

        if len(airmasses_included) == 0:
            self.observable = False
            self.rejection_reason = "airmass"

        if np.abs(self.coordinates_galactic.b.deg) < 10:
            self.observable = False
            self.rejection_reason = "proximity to gal. plane"

        self.g_band_recommended_time_start = None
        self.g_band_recommended_time_end = None
        self.r_band_recommended_time_start = None
        self.r_band_recommended_time_end = None

        if self.observable:
            min_airmass = np.min(airmasses_included)
            min_airmass_index = np.argmin(airmasses_included)
            min_airmass_time = times_included[min_airmass_index]

            distance_to_evening = min_airmass_time.mjd - self.twilight_evening.mjd
            distance_to_morning = self.twilight_morning.mjd - min_airmass_time.mjd

            if distance_to_morning < distance_to_evening:
                if "g" in self.bands:
                    self.g_band_recommended_time_start = round_time(
                        min_airmass_time - self.observationlength * u.s - 0.5 * u.hour
                    )
                    self.g_band_recommended_time_end = (
                        self.g_band_recommended_time_start
                        + self.observationlength * u.s
                    )
                if "r" in self.bands:
                    self.r_band_recommended_time_start = round_time(
                        min_airmass_time - self.observationlength * u.s
                    )
                    self.r_band_recommended_time_end = (
                        self.r_band_recommended_time_start
                        + self.observationlength * u.s
                    )

            else:
                if "g" in self.bands:
                    self.g_band_recommended_time_start = round_time(
                        min_airmass_time + self.observationlength * u.s + 0.5 * u.hour
                    )
                    self.g_band_recommended_time_end = (
                        self.g_band_recommended_time_start
                        + self.observationlength * u.s
                    )
                if "r" in self.bands:
                    self.r_band_recommended_time_start = round_time(
                        min_airmass_time + self.observationlength * u.s
                    )
                    self.r_band_recommended_time_end = (
                        self.r_band_recommended_time_start
                        + self.observationlength * u.s
                    )
        if self.alertsource in icecube:
            summarytext = f"Name = IceCube-{self.name[2:]}\n"
        else:
            summarytext = f"Name = {self.name}\n"

        if self.ra_err is not None:
            summarytext += f"RA = {self.coordinates.ra.deg} + {self.ra_err[0]} - {self.ra_err[1]*-1}\nDec = {self.coordinates.dec.deg} + {self.dec_err[0]} - {self.dec_err[1]*-1}\n"
        else:
            summarytext += f"RADEC = {self.coordinates.ra.deg:.8f} {self.coordinates.dec.deg:.8f}\n"

        if self.datasource is not None:
            summarytext += f"Data source: {self.datasource}"

        if self.observable:
            summarytext += (
                f"Minimal airmass ({min_airmass:.2f}) at {min_airmass_time}\n"
            )
        summarytext += f"Separation from galactic plane: {self.coordinates_galactic.b.deg:.2f} deg\n"

        if self.observable:
            summarytext += "Recommended observation times:\n"
            if "g" in self.bands:
                gbandtext = f"g-band: {short_time(self.g_band_recommended_time_start)} - {short_time(self.g_band_recommended_time_end)} [UTC]"
            if "r" in self.bands:
                rbandtext = f"r-band: {short_time(self.r_band_recommended_time_start)} - {short_time(self.r_band_recommended_time_end)} [UTC]"

            if (
                "g" in bands
                and "r" in bands
                and self.g_band_recommended_time_start
                < self.r_band_recommended_time_start
            ):
                bandtexts = [gbandtext + "\n", rbandtext]
            elif (
                "g" in bands
                and "r" in bands
                and self.g_band_recommended_time_start
                > self.r_band_recommended_time_start
            ):
                bandtexts = [rbandtext + "\n", gbandtext]
            elif "g" in bands and "r" not in bands:
                bandtexts = [gbandtext]
            else:
                bandtexts = [rbandtext]

            for item in bandtexts:
                summarytext += item

        if verbose:
            print(summarytext)

        if not os.path.exists(self.name):
            os.makedirs(self.name)

        self.summarytext = summarytext