def get_SMO_pos():
    global lat_i, long_i, RA_i, DEC_i, times, tname_i, option, places
    global coords_s, coords_m, coords_o, dist_sun, dist_moon, loc
    """read in from entry boxes"""

    ##handle if there is a look up to be done
    site = option.get()
    if (site is 'Anywhere Else'):
        lat = float(lat_i.get())
        long = float(long_i.get())

    else:
        if (places.get(site) is None):
            Label(master,
                  bg='red',
                  text='SITE NAME {} IS NOT VALID'.format(site.upper())).grid(
                      row=5, column=3)

            return None
        else:
            lat, long = places[site]

    #loc = coordinates.EarthLocation(lat=lat*u.deg, lon=long*u.deg)
    loc = Observer(longitude=long * u.degree,
                   latitude=lat * u.degree,
                   name=site)

    tname = str(tname_i.get())
    if (tname is ''):
        RA = float(RA_i.get())
        DEC = float(DEC_i.get())
        """target's RA and DEC"""
        coords_o = coordinates.SkyCoord(ra=RA * u.hour,
                                        dec=DEC * u.degree,
                                        frame='gcrs')

    else:
        coords_o = coordinates.SkyCoord.from_name(tname, frame='gcrs')
    """Sun's RA and DEC throughout the given time"""
    coords_s = loc.sun_altaz(times)
    """Moon's RA and DEC at the location, throughout the given time"""
    coords_m = loc.moon_altaz(times)
    """get separation angles in radians for the Sun and Moon"""
    """this returns a value in the range [-180, 180]"""
    dist_sun = (coords_s.separation(coords_o).degree)
    dist_moon = (coords_m.separation(coords_o).degree)
    """change button after press"""
    WHERE_button.config(fg='black', bg='#FFE100')
Пример #2
0
def timeline(request):
    # get location
    location = EarthLocation(
        lon=settings.OBSERVER_LOCATION['longitude'] * u.deg,
        lat=settings.OBSERVER_LOCATION['latitude'] * u.deg,
        height=settings.OBSERVER_LOCATION['elevation'] * u.m)

    # get observer and now
    now = Time.now()
    observer = Observer(location=location)

    # night or day?
    is_day = observer.sun_altaz(now).alt.degree > 0.

    # get sunset to start with
    sunset = observer.sun_set_time(now, which='next' if is_day else 'previous')

    # list of events
    events = []
    events.append(sunset.isot)

    # twilight at sunset
    sunset_twilight = observer.sun_set_time(sunset,
                                            which='next',
                                            horizon=-12. * u.deg)
    events.append(sunset_twilight.isot)

    # twilight at sunrise
    sunrise_twilight = observer.sun_rise_time(sunset_twilight,
                                              which='next',
                                              horizon=-12. * u.deg)
    events.append(sunrise_twilight.isot)

    # sunrise
    sunrise = observer.sun_rise_time(sunset_twilight, which='next')
    events.append(sunrise.isot)

    # return all
    return JsonResponse({'time': now.isot, 'events': events})
Пример #3
0
def good_weather(request):
    # get changes in status from last 24 hours
    changes = [{
        'time': g.time,
        'good': g.good
    } for g in GoodWeather.objects.filter(time__gt=datetime.utcnow() -
                                          timedelta(days=1)).all()]

    # if None, return last one
    if len(changes) == 0:
        last = GoodWeather.objects.last()
        if last is not None:
            changes = [{'time': last.time, 'good': last.good}]

    # get location
    location = EarthLocation(
        lon=settings.OBSERVER_LOCATION['longitude'] * u.deg,
        lat=settings.OBSERVER_LOCATION['latitude'] * u.deg,
        height=settings.OBSERVER_LOCATION['elevation'] * u.m)

    # get observer and now
    now = Time.now()
    observer = Observer(location=location)

    # get solar elevation for last 12 hours
    times = [now - TimeDelta((1. - x) * u.day) for x in np.linspace(0, 1, 100)]
    sun = [s.alt.degree for s in observer.sun_altaz(times)]

    # return all
    return JsonResponse({
        'changes': changes,
        'sun': {
            'time': [t.isot for t in times],
            'alt': sun
        }
    })
Пример #4
0
def createPicture(name, times):
    rcParams['font.size'] = 13
    rcParams['lines.linewidth'] = 3
    rcParams['lines.markersize'] = 0
    rcParams['grid.linestyle'] = '--'
    rcParams['axes.titlepad'] = 13
    rcParams['xtick.direction'] = 'in'
    rcParams['ytick.direction'] = 'in'
    rcParams['xtick.top'] = True
    rcParams['ytick.right'] = True
    rcParams['font.family'] = 'serif'
    rcParams['mathtext.fontset'] = 'dejavuserif'
    rc('legend', fontsize=13)
    rc('xtick.major', size=5, width=1.5)
    rc('ytick.major', size=5, width=1.5)
    rc('xtick.minor', size=3, width=1)
    rc('ytick.minor', size=3, width=1)

    star_name = name
    star_style = {'linestyle': '--', 'linewidth': 4, 'color': 'tomato'}
    star = FixedTarget.from_name(star_name)

    longitude_kgo = 42.6675 * u.deg
    latitude_kgo = 43.73611 * u.deg
    elevation_kgo = 2112 * u.m

    kgo = Observer(longitude=longitude_kgo,
                   latitude=latitude_kgo,
                   elevation=elevation_kgo,
                   name="KGO",
                   timezone="Europe/Moscow")

    # start_time = Time('2020-01-01 '+ times)
    # end_time = Time('2020-02-01 '+ times)
    # delta_t = end_time - start_time
    # observe_time = start_time + delta_t*np.linspace(0, 1, 32)
    observe_time = Time(times)

    # sunset_tonight = kgo.sun_set_time(observe_time, which="nearest")
    # sunrise_tonight = kgo.sun_rise_time(observe_time, which="nearest")

    # star_rise = list(map(lambda observe_time : kgo.target_rise_time(observe_time, star) + 5*u.minute, observe_time))
    # star_set = list(map(lambda observe_time: kgo.target_set_time(observe_time, star) + 5*u.minute, observe_time))

    # sunset_tonight = list(map(lambda observe_time:  kgo.sun_set_time(observe_time, which="nearest"), observe_time))
    # sunrise_tonight = list(map(lambda observe_time:  kgo.sun_rise_time(observe_time, which="nearest"), observe_time))

    visible_time = observe_time + np.linspace(-10, +10, 25) * u.hour
    #visible_time = start + (end - start)*np.linspace(0, 1, 25)
    stars_alts = kgo.altaz(visible_time, star).alt
    sun_alts = kgo.sun_altaz(visible_time).alt

    moon_coord = kgo.moon_altaz(visible_time)
    star_coord = kgo.altaz(visible_time, star)
    angle = moon_coord.separation(star_coord)
    moon_star = angle.deg

    #print(stars_alts)
    #t = Time(visible_time, format='iso', scale='utc')
    # start = Time(list(map(lambda x,y: np.max([x, y]), sunset_tonight, star_rise)))
    # end = Time(list(map(lambda x,y: np.min([x,y]), sunrise_tonight, star_set)))

    #visible_time = (end-start)

    #time_final = abs(visible_time.value*24)

    locator = mdates.MonthLocator()
    fmt = mdates.DateFormatter('%b')

    canvas = FigureCanvasAgg(plt.figure(1))
    plt.figure(figsize=(8, 7))
    plt.subplot(211)
    plt.plot_date(visible_time.plot_date,
                  stars_alts,
                  linestyle='-.',
                  color='mediumslateblue',
                  label=star_name)
    plt.plot_date(visible_time.plot_date,
                  sun_alts,
                  linestyle='-.',
                  color='gold',
                  label='Sun')
    plt.ylim(0, np.max([stars_alts, sun_alts]) + 5)
    plt.ylabel('Altitude, degrees')
    plt.legend(shadow=True, loc="best")
    plt.gcf().autofmt_xdate()
    plt.grid()

    plt.subplot(212)
    plt.plot_date(visible_time.plot_date,
                  moon_star,
                  linestyle='-',
                  color='slategrey',
                  label='star_name' + '\n' + times)
    plt.ylabel('Moon-Star angle, degrees')
    plt.gcf().autofmt_xdate()
    plt.grid()
    data = BytesIO()

    plt.savefig(data, format='png')
    return data.getvalue()
    return places

numDays = 365
oneDay = TimeDelta(1.0,format='jd')
firstDay = Time('2018-01-01 00:00:00',scale='utc')
allDays = [ firstDay + i*oneDay for i in range(numDays) ]

#places = get_places_fromlist(loc_names,loc_coord)
places = get_places(loc_names)

elevations = np.zeros((len(places.locations),len(allDays)))
for ind,(loc,coord) in enumerate(zip(places.locations,places.coordinates)):
    print(f"Computing sun elevations for {loc}")
    observer = Observer(location=coord)
    noons  = [ observer.noon(h,which=u'next') for h in allDays ]
    sunpos = [ observer.sun_altaz(h) for h in noons ]
    elevations[ind,:] = [ az.alt/u.deg for az in sunpos ]


df = pd.DataFrame([d.to_datetime() for d in allDays])
df.columns = [ 'Day' ]
dfelevs = pd.DataFrame(elevations.transpose(),columns=places.locations)
df = df.join(dfelevs).set_index('Day')

dfs = df.stack().reset_index()
dfs.columns = ['day','location','elevation']

p = ggplot(dfs,aes(x='day',y='elevation',color='location')) + geom_line()
p = p + ggtitle('Sun elevations by date and location')
p = p + xlab('Day of year') + ylab('Elevation (degrees)')
p.draw()
def main(args=None):
    p = parser()
    opts = p.parse_args(args)

    # Late imports
    import operator
    import sys

    from astroplan import Observer
    from astroplan.plots import plot_airmass
    from astropy.coordinates import EarthLocation, SkyCoord
    from astropy.table import Table
    from astropy.time import Time
    from astropy import units as u
    from matplotlib import dates
    from matplotlib.cm import ScalarMappable
    from matplotlib.colors import Normalize
    from matplotlib.patches import Patch
    from matplotlib import pyplot as plt
    from tqdm import tqdm
    import pytz

    from ..io import fits
    from .. import moc
    from .. import plot  # noqa
    from ..extern.quantile import percentile

    if opts.site is None:
        if opts.site_longitude is None or opts.site_latitude is None:
            p.error('must specify either --site or both '
                    '--site-longitude and --site-latitude')
        location = EarthLocation(lon=opts.site_longitude * u.deg,
                                 lat=opts.site_latitude * u.deg,
                                 height=(opts.site_height or 0) * u.m)
        if opts.site_timezone is not None:
            location.info.meta = {'timezone': opts.site_timezone}
        observer = Observer(location)
    else:
        if not ((opts.site_longitude is None) and
                (opts.site_latitude is None) and (opts.site_height is None) and
                (opts.site_timezone is None)):
            p.error('argument --site not allowed with arguments '
                    '--site-longitude, --site-latitude, '
                    '--site-height, or --site-timezone')
        observer = Observer.at_site(opts.site)

    m = fits.read_sky_map(opts.input.name, moc=True)

    # Make an empty airmass chart.
    t0 = Time(opts.time) if opts.time is not None else Time.now()
    t0 = observer.midnight(t0)
    ax = plot_airmass([], observer, t0, altitude_yaxis=True)

    # Remove the fake source and determine times that were used for the plot.
    del ax.lines[:]
    times = Time(np.linspace(*ax.get_xlim()), format='plot_date')

    theta, phi = moc.uniq2ang(m['UNIQ'])
    coords = SkyCoord(phi, 0.5 * np.pi - theta, unit='rad')
    prob = moc.uniq2pixarea(m['UNIQ']) * m['PROBDENSITY']

    levels = np.arange(90, 0, -10)
    nlevels = len(levels)
    percentiles = np.concatenate((50 - 0.5 * levels, 50 + 0.5 * levels))

    airmass = np.column_stack([
        percentile(condition_secz(coords.transform_to(observer.altaz(t)).secz),
                   percentiles,
                   weights=prob) for t in tqdm(times)
    ])

    cmap = ScalarMappable(Normalize(0, 100), plt.get_cmap())
    for level, lo, hi in zip(levels, airmass[:nlevels], airmass[nlevels:]):
        ax.fill_between(
            times.plot_date,
            clip_verylarge(lo),  # Clip infinities to large but finite values
            clip_verylarge(hi),  # because fill_between cannot handle inf
            color=cmap.to_rgba(level),
            zorder=2)

    ax.legend([Patch(facecolor=cmap.to_rgba(level)) for level in levels],
              ['{}%'.format(level) for level in levels])
    # ax.set_title('{} from {}'.format(m.meta['objid'], observer.name))

    # Adapted from astroplan
    start = times[0]
    twilights = [
        (times[0].datetime, 0.0),
        (observer.sun_set_time(Time(start), which='next').datetime, 0.0),
        (observer.twilight_evening_civil(Time(start),
                                         which='next').datetime, 0.1),
        (observer.twilight_evening_nautical(Time(start),
                                            which='next').datetime, 0.2),
        (observer.twilight_evening_astronomical(Time(start),
                                                which='next').datetime, 0.3),
        (observer.twilight_morning_astronomical(Time(start),
                                                which='next').datetime, 0.4),
        (observer.twilight_morning_nautical(Time(start),
                                            which='next').datetime, 0.3),
        (observer.twilight_morning_civil(Time(start),
                                         which='next').datetime, 0.2),
        (observer.sun_rise_time(Time(start), which='next').datetime, 0.1),
        (times[-1].datetime, 0.0),
    ]

    twilights.sort(key=operator.itemgetter(0))
    for i, twi in enumerate(twilights[1:], 1):
        if twi[1] != 0:
            ax.axvspan(twilights[i - 1][0],
                       twilights[i][0],
                       ymin=0,
                       ymax=1,
                       color='grey',
                       alpha=twi[1],
                       linewidth=0)
        if twi[1] != 0.4:
            ax.axvspan(twilights[i - 1][0],
                       twilights[i][0],
                       ymin=0,
                       ymax=1,
                       color='white',
                       alpha=0.8 - 2 * twi[1],
                       zorder=3,
                       linewidth=0)

    # Add local time axis
    timezone = (observer.location.info.meta or {}).get('timezone')
    if timezone:
        tzinfo = pytz.timezone(timezone)
        ax2 = ax.twiny()
        ax2.set_xlim(ax.get_xlim())
        ax2.set_xticks(ax.get_xticks())
        ax2.xaxis.set_major_formatter(dates.DateFormatter('%H:%M', tz=tzinfo))
        plt.setp(ax2.get_xticklabels(), rotation=-30, ha='right')
        ax2.set_xlabel("Time from {} [{}]".format(
            min(times).to_datetime(tzinfo).date(), timezone))

    if opts.verbose:
        # Write airmass table to stdout.
        times.format = 'isot'
        table = Table(masked=True)
        table['time'] = times
        table['sun_alt'] = np.ma.masked_greater_equal(
            observer.sun_altaz(times).alt, 0)
        table['sun_alt'].format = lambda x: '{}'.format(int(np.round(x)))
        for p, data in sorted(zip(percentiles, airmass)):
            table[str(p)] = np.ma.masked_invalid(data)
            table[str(p)].format = lambda x: '{:.01f}'.format(np.around(x, 1))
        table.write(sys.stdout, format='ascii.fixed_width')

    # Show or save output.
    opts.output()
Пример #7
0
"""used to make all further calculations substantially faster"""
times = astropy.time.Time([(t + (i * step) * u.minute)
                           for i in range((60 // step) * 24 * ndays)])
"""labels for the plot later"""
window = [i.datetime for i in times]
if ndays <= 60:
    window_x = [
        i.date().strftime('%m-%d') for i in window[::(60 // step) * 24]
    ]
else:
    window_x = window[::(60 // step) * 24]
window_y = [i for i in window[0:(60 // step) * 24 + (60 // step) + 1]]

loc = Observer(longitude=long * u.degree, latitude=lat * u.degree, name=site)
"""Sun's RA and DEC throughout the given time"""
coords_s = loc.sun_altaz(times)
"""get separation angles in radians for the Sun and Moon"""
"""whether or not the altitudes are low enough"""
alt_s = (sun_alt - coords_s.alt.degree)

fig = plt.figure(figsize=(18, 9))
ax = plt.subplot(111)
"""set the proper number of ticks"""
if ndays <= 60:
    ext = (0, (60 // step) * 24, 0, (60 // step) * 24)

    xticks = range(0, (60 // step) * 24, (60 // step) * 24 // ndays)
    plt.xticks(xticks)
    ax.set_xticklabels(window_x, rotation=90)
else:
    ext = (0, ndays, 0, (60 // step) * 24)
Пример #8
0
def sassy_cron_airmass(_log=None, _folder=''):

    # define observer, observatory, time, frame, sun and moon
    _observatory = EarthLocation(lat=MMT_LATITUDE*u.deg, lon=MMT_LONGITUDE * u.deg, height=MMT_ELEVATION * u.m)
    _observer = Observer(location=_observatory, name='MMT', timezone='US/Arizona')

    _now_isot = get_isot(0)
    _now = Time(_now_isot)
    _start_time = Time(_now_isot.replace('T', ' ')) + 1.0*u.day * np.linspace(0.0, 1.0, int(24.0*60.0/5.0))

    _frame = AltAz(obstime=_start_time, location=_observatory)

    _moon = _observer.moon_altaz(_start_time)
    _moon_time = _moon.obstime
    _moon_alt = _moon.alt
    _moon_az = _moon.az

    _sun = _observer.sun_altaz(_start_time)
    _sun_time = _sun.obstime
    _sun_alt = _sun.alt
    _sun_az = _sun.az

    # get record(s)
    _s = db_connect()
    _query = _s.query(SassyCron)
    for _q in _query.all():

        # get data
        _zoid, _zra, _zdec = _q.zoid, _q.zra, _q.zdec
        _base = f"{_folder}/{_q.dpng}"
        _replace = 'difference'
        if _base == '':
            _base = f"{_folder}/{_q.spng}"
            _replace = 'science'
        if _base == '':
            _base = f"{_folder}/{_q.tpng}"
            _replace = 'template'
        if _base == '':
            return
        _airmass = _base.replace(_replace, 'airmass')
        if _log:
            _log.info(f"_zoid={_zoid}, _zra={_zra}, _zdec={_zdec}, _base={_base}, _replace={_replace}, _airmass={_airmass}")

        # convert
        _ra = ra_to_hms(_zra)
        _dec = dec_to_dms(_zdec)
        _dec = f"{_dec}".replace("+", "")
        _title = f"{_zoid} Airmass @ MMT\nRA={_ra} ({_zra:.3f}), Dec={_dec} ({_zdec:.3f})"

        # get target
        _coords = SkyCoord(ra=_zra*u.deg, dec=_zdec*u.deg)
        _target = _coords.transform_to(_frame)
        _target_time = _target.obstime
        _target_alt = _target.alt
        _target_az = _target.az

        # plot data
        _time = str(_target_time[0]).split()[0]
        fig, ax = plt.subplots()
        _ax_scatter = ax.plot(_moon_time.datetime, _moon_alt.degree, 'g--', label='Moon')
        _ax_scatter = ax.plot(_sun_time.datetime, _sun_alt.degree, 'r--', label='Sun')
        _ax_scatter = ax.scatter(_target_time.datetime, _target_alt.degree,
                                 c=np.array(_target_az.degree), lw=0, s=8, cmap='viridis')
        ax.plot([_now.datetime, _now.datetime], [ASTRONOMICAL_DAWN, 90.0], 'orange')
        ax.plot([_target_time.datetime[0], _target_time.datetime[-1]], [0.0, 0.0], 'black')
        ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
        plt.gcf().autofmt_xdate()
        plt.colorbar(_ax_scatter, ax=ax).set_label(f'Azimuth ({OBS_DEGREE})')
        ax.set_ylim([ASTRONOMICAL_DAWN, 90.0])
        ax.set_xlim([_target_time.datetime[0], _target_time.datetime[-1]])
        ax.set_title(f'{_title}')
        ax.set_ylabel(f'Altitude ({OBS_DEGREE})')
        ax.set_xlabel(f'{_time} (UTC)')
        plt.legend(loc='upper left')
        plt.fill_between(_target_time.datetime, ASTRONOMICAL_DAWN*u.deg, 90.0*u.deg,
                         _sun_alt < 0.0*u.deg, color='0.80', zorder=0)
        plt.fill_between(_target_time.datetime, ASTRONOMICAL_DAWN*u.deg, 90.0*u.deg,
                         _sun_alt < CIVIL_DAWN*u.deg, color='0.60', zorder=0)
        plt.fill_between(_target_time.datetime, ASTRONOMICAL_DAWN*u.deg, 90.0*u.deg,
                         _sun_alt < NAUTICAL_DAWN*u.deg, color='0.40', zorder=0)
        plt.fill_between(_target_time.datetime, ASTRONOMICAL_DAWN*u.deg, 90.0*u.deg,
                         _sun_alt < ASTRONOMICAL_DAWN*u.deg, color='0.20', zorder=0)

        # save plot
        _buf = io.BytesIO()
        plt.savefig(_airmass)
        plt.savefig(_buf, format='png', dpi=100, bbox_inches='tight')
        plt.close()
        if _log:
            _log.info(f"exit ...  _airmass={_airmass}")
    db_disconnect(_s)
Пример #9
0
win_time = midnight + delta_md
night = win_time
# day = (midnight + 12*u.hour) + delta_md
day = (midnight + 12 * u.hour) + np.linspace(0, 3, 30) * u.hour  # GMS
op = pd.read_csv('op.csv', index_col='nama')
ra = op.ra
dec = op.dec

sunset = lokasi.sun_set_time(time)
sunrise = lokasi.sun_rise_time(time)
moonrise = lokasi.moon_rise_time(time)
moonset = lokasi.moon_set_time(time)
moon_ill = lokasi.moon_illumination(time)
moonaltaz = lokasi.moon_altaz(time)
moonaltaz.name = 'moon'
sunaltaz = lokasi.sun_altaz(time)
sunaltaz.name = 'sun'


def objek(nama, ra, dec):
    op = SkyCoord(ra, dec, frame='icrs', unit='deg')
    op_name = FixedTarget(coord=op, name=nama)
    # betelgeus = SkyCoord.from_name('betelgeuse')
    # altaz_frame = lokasi.altaz(time)  # ubah lokasi pengamat ke altaz frame
    # betelgeus_altaz = betelgeus.transform_to(altaz_frame)  # ubah target ke altaz
    altaz = lokasi.altaz(win_time, op)
    terbit = lokasi.target_rise_time(win_time, op)
    transit = lokasi.target_meridian_transit_time(win_time, op)
    terbenam = lokasi.target_set_time(win_time, op)
    return op_name, altaz, terbit, transit, terbenam
Пример #10
0
def plot_tel_airmass(_log=None,
                     _ra=math.nan,
                     _dec=math.nan,
                     _oid='',
                     _tel='mmt',
                     _img=''):

    # define observer, observatory, time, frame, sun and moon
    if _tel.lower().strip() == 'bok':
        _observatory = EarthLocation(lat=BOK_LATITUDE * u.deg,
                                     lon=BOK_LONGITUDE * u.deg,
                                     height=BOK_ELEVATION * u.m)
        _observer = Observer(location=_observatory,
                             name='BOK',
                             timezone='US/Arizona')
    elif _tel.lower().strip() == 'greenwich':
        _observatory = EarthLocation(lat=GREENWICH_LATITUDE * u.deg,
                                     lon=GREENWICH_LONGITUDE * u.deg,
                                     height=GREENWICH_ELEVATION * u.m)
        _observer = Observer(location=_observatory,
                             name='GREENWICH',
                             timezone='Greenwich')
    elif _tel.lower().strip() == 'kuiper':
        _observatory = EarthLocation(lat=KUIPER_LATITUDE * u.deg,
                                     lon=KUIPER_LONGITUDE * u.deg,
                                     height=KUIPER_ELEVATION * u.m)
        _observer = Observer(location=_observatory,
                             name='KUIPER',
                             timezone='US/Arizona')
    elif _tel.lower().strip() == 'mmt':
        _observatory = EarthLocation(lat=MMT_LATITUDE * u.deg,
                                     lon=MMT_LONGITUDE * u.deg,
                                     height=MMT_ELEVATION * u.m)
        _observer = Observer(location=_observatory,
                             name='MMT',
                             timezone='US/Arizona')
    elif _tel.lower().strip() == 'steward':
        _observatory = EarthLocation(lat=STEWARD_LATITUDE * u.deg,
                                     lon=STEWARD_LONGITUDE * u.deg,
                                     height=STEWARD_ELEVATION * u.m)
        _observer = Observer(location=_observatory,
                             name='STEWARD',
                             timezone='US/Arizona')
    elif _tel.lower().strip() == 'vatt':
        _observatory = EarthLocation(lat=VATT_LATITUDE * u.deg,
                                     lon=VATT_LONGITUDE * u.deg,
                                     height=VATT_ELEVATION * u.m)
        _observer = Observer(location=_observatory,
                             name='VATT',
                             timezone='US/Arizona')
    else:
        return

    if _log:
        _log.info(
            f"plot_tel_airmass(_ra={_ra:.3f}, _dec={_dec:.3f}, _oid={_oid}, _tel={_tel}, _img={_img}"
        )

    # get sun, moon etc
    _now_isot = get_isot(0)
    _now = Time(_now_isot)
    _start_time = Time(_now_isot.replace(
        'T',
        ' ')) + 1.0 * u.day * np.linspace(0.0, 1.0, int(24.0 * 60.0 / 5.0))

    _frame = AltAz(obstime=_start_time, location=_observatory)

    _moon = _observer.moon_altaz(_start_time)
    _moon_time = _moon.obstime
    _moon_alt = _moon.alt
    _moon_az = _moon.az

    _sun = _observer.sun_altaz(_start_time)
    _sun_time = _sun.obstime
    _sun_alt = _sun.alt
    _sun_az = _sun.az

    # convert
    _oid_s = str(_oid).replace("'", "")
    _ra_hms = ra_to_hms(_ra)
    _dec_dms = dec_to_dms(_dec)
    _dec_dms = f"{_dec_dms}".replace("+", "")
    _sup_title = f"{_oid} Airmass @ {_tel.upper()}"
    _sub_title = f"RA={_ra_hms} ({_ra:.3f}), Dec={_dec_dms} ({_dec:.3f})"

    # get target
    _coords = SkyCoord(ra=_ra * u.deg, dec=_dec * u.deg)
    _target = _coords.transform_to(_frame)
    _target_time = _target.obstime
    _target_alt = _target.alt
    _target_az = _target.az

    # plot data
    _time = str(_target_time[0]).split()[0]
    fig, ax = plt.subplots()
    fig.suptitle(_sup_title.replace("'", "").replace("{", "").replace("}", ""))
    _ax_scatter = ax.plot(_moon_time.datetime,
                          _moon_alt.degree,
                          'g--',
                          label='Moon')
    _ax_scatter = ax.plot(_sun_time.datetime,
                          _sun_alt.degree,
                          'r--',
                          label='Sun')
    _ax_scatter = ax.scatter(_target_time.datetime,
                             _target_alt.degree,
                             c=np.array(_target_az.degree),
                             lw=0,
                             s=8,
                             cmap='viridis')
    ax.plot([_now.datetime, _now.datetime], [ASTRONOMICAL_DAWN, 90.0],
            'orange')
    ax.plot([_target_time.datetime[0], _target_time.datetime[-1]], [0.0, 0.0],
            'black')
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
    plt.gcf().autofmt_xdate()
    plt.colorbar(_ax_scatter, ax=ax).set_label(f'Azimuth ({OBS_DEGREE})')
    ax.set_ylim([ASTRONOMICAL_DAWN, 90.0])
    ax.set_xlim([_target_time.datetime[0], _target_time.datetime[-1]])
    ax.set_title(_sub_title)
    ax.set_ylabel(f'Altitude ({OBS_DEGREE})')
    ax.set_xlabel(f'{_time} (UTC)')
    plt.legend(loc='upper left')
    plt.fill_between(_target_time.datetime,
                     ASTRONOMICAL_DAWN * u.deg,
                     90.0 * u.deg,
                     _sun_alt < 0.0 * u.deg,
                     color='0.80',
                     zorder=0)
    plt.fill_between(_target_time.datetime,
                     ASTRONOMICAL_DAWN * u.deg,
                     90.0 * u.deg,
                     _sun_alt < CIVIL_DAWN * u.deg,
                     color='0.60',
                     zorder=0)
    plt.fill_between(_target_time.datetime,
                     ASTRONOMICAL_DAWN * u.deg,
                     90.0 * u.deg,
                     _sun_alt < NAUTICAL_DAWN * u.deg,
                     color='0.40',
                     zorder=0)
    plt.fill_between(_target_time.datetime,
                     ASTRONOMICAL_DAWN * u.deg,
                     90.0 * u.deg,
                     _sun_alt < ASTRONOMICAL_DAWN * u.deg,
                     color='0.20',
                     zorder=0)

    # save plot
    _buf = io.BytesIO()
    plt.savefig(_img)
    plt.savefig(_buf, format='png', dpi=100, bbox_inches='tight')
    plt.close()

    # return
    _img = os.path.abspath(os.path.expanduser(_img))
    if os.path.exists(_img):
        if _log:
            _log.debug(f"created {_img}")
        return _img
    else:
        if _log:
            _log.debug(f"created None")
        return