Example #1
0
    def __call__(self, telescope: ITelescope) -> Future:
        """Move telescope.

        Args:
            telescope: Telescope to use.

        Returns:
            Future for the movement call.
        """

        if self._initialized:
            return Future(empty=True)
        self._initialized = True

        # calculate Alt/Az position of sun
        sun = self.observer.sun_altaz(Time.now())
        log.info('Sun is currently located at alt=%.2f°, az=%.2f°',
                 sun.alt.degree, sun.az.degree)

        # get sweet spot for flat-fielding
        altaz = SkyCoord(alt=80 * u.deg,
                         az=sun.az + 180 * u.degree,
                         obstime=Time.now(),
                         location=self.observer.location,
                         frame='altaz')
        log.info('Sweet spot for flat fielding is at alt=80°, az=%.2f°',
                 altaz.az.degree)

        # move telescope
        log.info('Moving telescope to Alt=80, Az=%.2f...', altaz.az.degree)
        return telescope.move_altaz(80, float(altaz.az.degree))
Example #2
0
    def fetch_tasks(self,
                    end_after: Time,
                    start_before: Time,
                    state: str = 'PENDING') -> Dict[str, Task]:
        """Fetch tasks from portal.

        Args:
            end_after: Task must end after this time.
            start_before: Task must start before this time.
            state: State of tasks.

        Returns:
            Dictionary with tasks.

        Raises:
            Timeout if request timed out.
            ValueError if something goes wrong.
        """

        # get url and params
        url = urljoin(self._url, '/api/observations/')
        params = {
            'site': self._site,
            'end_after': end_after.isot,
            'start_before': start_before.isot,
            'state': state
        }

        # do request
        r = requests.get(url,
                         params=params,
                         headers=self._header,
                         timeout=10,
                         proxies=self._proxies)

        # success?
        if r.status_code != 200:
            raise ValueError()

        # get schedule
        schedules = r.json()['results']

        # create tasks
        tasks = {}
        for sched in schedules:
            # parse start and end
            sched['start'] = Time(sched['start'])
            sched['end'] = Time(sched['end'])

            # create task
            task = self._create_task(LcoTask, sched, scripts=self.scripts)
            tasks[sched['request']['id']] = task

        # finished
        return tasks
Example #3
0
    def update_gui(self) -> None:
        # enable myself and set filter
        self.setEnabled(True)

        # get current weather
        cur = self._current_weather["sensors"]

        # update current
        if "sensors" in self._current_weather:
            # get current list of sensors
            current_sensors = list(sorted(cur.keys()))

            # did it change?
            if current_sensors != self._current_sensors:
                layout = self.frameCurrent.layout()

                # remove all widgets from frameCurrent
                for w in self._current_widgets.values():
                    w.setParent(None)

                # add time
                self._current_widgets["time"] = WidgetCurrentSensor("Time", "")
                layout.addWidget(self._current_widgets["time"])

                # loop sensor types
                for sensor in AVERAGE_SENSOR_FIELDS:
                    if sensor["field"] in current_sensors:
                        widget = WidgetCurrentSensor(sensor["label"],
                                                     sensor["unit"])
                        self._current_widgets[sensor["field"]] = widget
                        layout.addWidget(widget)

            # set time
            if "time" in self._current_weather and self._current_weather[
                    "time"] is not None:
                t = Time(self._current_weather["time"])
                self._current_widgets["time"].set_value(
                    t.strftime("%Y-%m-%d\n%H:%M:%S"))
            else:
                self._current_widgets["time"].set_value("")

            # set values
            for sensor in AVERAGE_SENSOR_FIELDS:
                f = sensor["field"]
                if f in current_sensors:
                    format = "%d" if f == "rain" else "%.2f"
                    s = "N/A" if cur[f][
                        "value"] is None else format % cur[f]["value"]
                    self._current_widgets[f].set_value(s)
                    self._current_widgets[f].set_good(cur[f]["good"])

            # store it
            self._current_sensors = current_sensors
Example #4
0
    async def _init_system(self,
                           telescope: ITelescope,
                           camera: Union[ICamera, IExposureTime],
                           filters: Optional[IFilters] = None) -> None:
        """Initialize whole system."""

        # which twilight are we in?
        if self.observer is None:
            raise ValueError("No observer given.")
        sun = self.observer.sun_altaz(Time.now())
        sun_10min = self.observer.sun_altaz(Time.now() +
                                            TimeDelta(10 * u.minute))
        self._twilight = (FlatFielder.Twilight.DUSK
                          if sun_10min.alt.degree < sun.alt.degree else
                          FlatFielder.Twilight.DAWN)
        log.info("We are currently in %s twilight.", self._twilight.value)

        # do initial check
        if not self._initial_check():
            return

        # set binning
        if isinstance(camera, IBinning):
            log.info("Setting binning to %dx%d...", self._cur_binning[0],
                     self._cur_binning[1])
            await camera.set_binning(*self._cur_binning)

        # get bias level
        self._bias_level = await self._get_bias(camera)

        # move telescope
        future_track = self._pointing(
            telescope) if self._pointing is not None else None

        # get filter from first step and set it
        if filters is not None and self._cur_filter is not None:
            log.info("Setting filter to %s...", self._cur_filter)
            future_filter = await filters.set_filter(self._cur_filter)
        else:
            future_filter = Future(empty=True)

        # wait for both
        await Future.wait_all([future_track, future_filter])
        log.info("Finished initializing system.")

        # change stats
        log.info("Waiting for flat-field time...")
        self._state = FlatFielder.State.WAITING
Example #5
0
    async def _wait(self) -> None:
        """Wait for flat-field time."""

        # get solar elevation and evaluate function
        sun_alt, self._exptime = self._eval_function(Time.now())
        log.info(
            "Calculated optimal exposure time of %.2fs in %dx%d at solar elevation of %.2f°.",
            self._exptime,
            self._cur_binning[0],
            self._cur_binning[1],
            sun_alt,
        )

        # then evaluate exposure time within a larger range
        state = self._eval_exptime(self._min_exptime * 0.5,
                                   self._max_exptime * 2.0)
        if state < 0:
            log.info("Sleeping a little...")
            await event_wait(self._abort, 10)
        elif state == 0:
            log.info("Starting to take test flat-fields...")
            self._state = FlatFielder.State.TESTING
        else:
            log.info("Missed flat-fielding time, finish task...")
            self._state = FlatFielder.State.FINISHED
Example #6
0
    def __call__(self):
        # get all reduced skyflat frames of the last 100 days
        now = Time.now()
        frames = self._archive.list_frames(start=now - TimeDelta(100 * u.day),
                                           end=now,
                                           site=self._site,
                                           instrument=self._instrument,
                                           image_type=ImageType.SKYFLAT,
                                           rlevel=1)

        # get priorities
        from_archive = {}
        for f in frames:
            # get number of days since flat was taken, which is our priority
            prio = (now - f.dateobs).sec / 86400.

            # get key in priorities
            key = (f.filter_name, f.binning)

            # need to update it?
            if key not in from_archive or prio < from_archive[key]:
                from_archive[key] = prio

        # create priorities
        priorities = {}
        for fn in self._filter_names:
            for b in self._binnings:
                priorities[fn, b] = from_archive[fn, b] if (
                    fn, b) in from_archive else 100.

        # finished
        return priorities
Example #7
0
    def _initial_check(self) -> bool:
        """Do a quick initial check.

        Returns:
            False, if flat-field time for this filter is over, True otherwise.
        """

        # get solar elevation and evaluate function
        sun_alt, self._exptime = self._eval_function(Time.now())
        log.info(
            "Calculated optimal exposure time of %.2fs in %dx%d at solar elevation of %.2f°.",
            self._exptime,
            self._cur_binning[0],
            self._cur_binning[1],
            sun_alt,
        )

        # then evaluate exposure time within a larger range
        state = self._eval_exptime(self._min_exptime * 0.5,
                                   self._max_exptime * 2.0)
        if state > 0:
            log.info("Missed flat-fielding time, finishing task...")
            self._state = FlatFielder.State.FINISHED
            return False
        else:
            log.info("Flat-field time is still coming, keep going...")
            return True
Example #8
0
    def _calc_dest_helioprojective_radial(self) -> None:
        # get sun
        sun = self.observer.sun_altaz(Time.now())
        sun_radec = sun.icrs

        # display
        self._show_dest_coords(sun_radec.ra, sun_radec.dec, sun.alt, sun.az)
Example #9
0
    def _calc_dest_heliographic_stonyhurst(self) -> None:
        # get sun
        sun = self.observer.sun_altaz(Time.now())
        sun_radec = sun.icrs

        # display
        self._show_dest_coords(sun_radec.ra, sun_radec.dec, sun.alt, sun.az)
Example #10
0
    def _calc_dest_equatorial(self, clear: bool = True) -> None:
        """Called, whenever RA/Dec input changes. Calculates destination."""

        # reset fields
        if clear:
            self.textSimbadName.clear()
            self.comboSolarSystemBody.setCurrentText("")
            self.textJplHorizonsName.clear()

        # parse RA/Dec
        try:
            ra_dec = SkyCoord(
                self.textMoveRA.text() + " " + self.textMoveDec.text(),
                frame=ICRS,
                unit=(u.hour, u.deg),
            )
        except ValueError:
            # on error, show it
            self._show_dest_coords()
            return

        # to alt/az
        alt_az = self.observer.altaz(Time.now(), ra_dec)

        # display
        self._show_dest_coords(ra_dec.ra, ra_dec.dec, alt_az.alt, alt_az.az)
Example #11
0
    def _select_solar_system(self, body: str) -> None:
        """Set RA/Dec for selected solar system body."""
        from astropy.coordinates import solar_system_ephemeris, get_body

        # nothing?
        if body == "":
            return

        # clear simbad and JPL
        self.textSimbadName.clear()
        self.textJplHorizonsName.clear()

        QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
        with solar_system_ephemeris.set("builtin"):
            # get coordinates
            body = get_body(body, Time.now(), self.observer.location)
        QtWidgets.QApplication.restoreOverrideCursor()

        # set them
        self.textMoveRA.setText(
            body.ra.to_string(unit=u.hour, sep=" ", precision=2))
        self.textMoveDec.setText(body.dec.to_string(sep=" ", precision=2))

        # update destination
        self._calc_dest_equatorial(clear=False)
Example #12
0
    def _move_altaz(self, alt: float, az: float, abort_event: threading.Event):
        """Actually moves to given coordinates. Must be implemented by derived classes.

        Args:
            alt: Alt in deg to move to.
            az: Az in deg to move to.
            abort_event: Event that gets triggered when movement should be aborted.

        Raises:
            Exception: On error.
        """

        # alt/az coordinates to ra/dec
        coords = SkyCoord(alt=alt * u.degree,
                          az=az * u.degree,
                          obstime=Time.now(),
                          location=self.location,
                          frame='altaz')
        icrs = coords.icrs

        # send event
        self.comm.send_event(TelescopeMovingEvent(alt=alt, az=az))

        # start slewing
        self.__move(icrs.ra.degree, icrs.dec.degree, abort_event)
Example #13
0
    def _update_thread(self):
        # time of last change in blocks
        last_change = None

        # run forever
        while not self.closing.is_set():
            # not running?
            if self._running is False:
                self.closing.wait(1)
                continue

            # got new time of last change?
            t = self._task_archive.last_changed()
            if last_change is None or last_change < t:
                # get schedulable blocks and sort them
                log.info(
                    'Found update in schedulable block, downloading them...')
                self._blocks = sorted(
                    self._task_archive.get_schedulable_blocks(),
                    key=lambda x: json.dumps(x.configuration, sort_keys=True))
                log.info('Downloaded %d schedulable block(s).',
                         len(self._blocks))

                # schedule update
                log.info('Triggering scheduler run...')
                self._need_update = True

                # remember now
                last_change = Time.now()

            # sleep a little
            self.closing.wait(5)
Example #14
0
    async def _on_focus_found(self, event: Event, sender: str) -> bool:
        """Receive FocusFoundEvent.

        Args:
            event: The event itself
            sender: The name of the sender.
        """
        if not isinstance(event, FocusFoundEvent):
            raise ValueError("Not a focus event.")
        log.info("Received new focus of %.4f +- %.4f.", event.focus,
                 event.error)

        # collect values for model
        values = await self._get_values()

        # add focus and datetime
        values["focus"] = event.focus
        values["error"] = event.error
        values["datetime"] = Time.now().isot
        values["filter"] = event.filter_name

        # write log
        if self._publisher is not None:
            await self._publisher(**values)

        # finally, calculate new model
        log.info("Re-calculating model...")
        await self._calc_focus_model()

        # finished
        log.info("Done.")
        return True
Example #15
0
    async def _update_celestial_headers(self) -> None:
        """Calculate positions and distances to celestial objects like moon and sun."""
        # get now
        now = Time.now()
        alt: Optional[float]
        az: Optional[float]

        # no observer?
        if self.observer is None:
            return

        # get telescope alt/az
        try:
            alt, az = await self.get_altaz()
            tel_altaz = SkyCoord(alt=alt * u.deg, az=az * u.deg, frame="altaz")
        except:
            alt, az, tel_altaz = None, None, None

        # get current moon and sun information
        moon_altaz = self.observer.moon_altaz(now)
        moon_frac = self.observer.moon_illumination(now)
        sun_altaz = self.observer.sun_altaz(now)

        # calculate distance to telescope
        moon_dist = tel_altaz.separation(moon_altaz) if tel_altaz is not None else None
        sun_dist = tel_altaz.separation(sun_altaz) if tel_altaz is not None else None

        # store it
        self._celestial_headers = {
            "MOONALT": (float(moon_altaz.alt.degree), "Lunar altitude"),
            "MOONFRAC": (float(moon_frac), "Fraction of the moon illuminated"),
            "MOONDIST": (None if moon_dist is None else float(moon_dist.degree), "Lunar distance from target"),
            "SUNALT": (float(sun_altaz.alt.degree), "Solar altitude"),
            "SUNDIST": (None if sun_dist is None else float(sun_dist.degree), "Solar Distance from Target"),
        }
Example #16
0
    def _find_master(self, image_class: Type[CalibrationImage], instrument: str, binning: str,
                     filter_name: str = None) -> Optional[Image]:
        """Find master calibration frame for given parameters using a cache.

        Args:
            image_class: Image class.
            instrument: Instrument name.
            binning: Binning.
            filter_name: Name of filter.

        Returns:
            Image or None
        """

        # is in cache?
        if (image_class, instrument, binning, filter_name) in self._master_frames:
            return self._master_frames[image_class, instrument, binning, filter_name]

        # try to download one
        midnight = Time(self._night + ' 23:59:59')
        frame = image_class.find_master(self._archive, midnight, instrument, binning, filter_name)
        if frame is not None:
            # download it
            calib = self._archive.download_frames([frame])[0]

            # store and return it
            self._master_frames[image_class, instrument, binning, filter_name] = calib
            return calib
        else:
            # still nothing
            return None
Example #17
0
 def __init__(self, state='ATTEMPTED', reason=''):
     """Initializes a new Status with an ATTEMPTED."""
     self.start = Time.now()
     self.end = None
     self.state = state
     self.reason = reason
     self.time_completed = 0
Example #18
0
    async def __call__(self, telescope: ITelescope) -> None:
        """Move telescope.

        Args:
            telescope: Telescope to use.

        Returns:
            Future for the movement call.
        """

        if self._initialized:
            return
        self._initialized = True

        # calculate Alt/Az position of sun
        now = Time.now()
        sun = self.observer.sun_altaz(now)
        log.info("Sun is currently located at alt=%.2f°, az=%.2f°",
                 sun.alt.degree, sun.az.degree)

        # get sweet spot for flat-fielding
        altaz = SkyCoord(alt=80 * u.deg,
                         az=sun.az + 180 * u.degree,
                         obstime=now,
                         location=self.observer.location,
                         frame="altaz")
        log.info("Sweet spot for flat fielding is at alt=80°, az=%.2f°",
                 altaz.az.degree)

        # move telescope
        log.info("Moving telescope to Alt=80, Az=%.2f...", altaz.az.degree)
        return await telescope.move_altaz(80, float(altaz.az.degree))
Example #19
0
async def test_scheduler():
    # init observer and time
    observer = Observer.at_site('SAAO')
    now = Time('2019-11-21T17:10:00Z')

    # have some test functions
    functions = {
        'B': ' exp(-1.22034 * (h + 3.16086))',
        'V': ' exp(-1.27565 * (h + 3.48265))',
        'R': ' exp(-1.39148 * (h + 3.63401))',
    }

    # set constant priorities
    priorities = ConstSkyflatPriorities({('B', (1, 1)): 1, ('V', (1, 1)): 2, ('R', (1, 1)): 3})

    # create scheduler
    scheduler = Scheduler(functions, priorities, observer)
    await scheduler(now)

    # test order
    assert scheduler[0].filter_name == 'B'
    assert scheduler[1].filter_name == 'V'
    assert scheduler[2].filter_name == 'R'

    # test start/end times
    assert scheduler[0].start == 1160
    assert pytest.approx(scheduler[0].end, 0.01) == 1200.46
    assert scheduler[1].start == 1270
    assert pytest.approx(scheduler[1].end, 0.01) == 1310.59
    assert scheduler[2].start == 1330
    assert pytest.approx(scheduler[2].end, 0.01) == 1370.59
Example #20
0
    def _on_focus_found(self, event: FocusFoundEvent, sender: str):
        """Receive FocusFoundEvent.

        Args:
            event: The event itself
            sender: The name of the sender.
        """
        log.info('Received new focus of %.4f +- %.4f.', event.focus,
                 event.error)

        # collect values for model
        values = self._get_values()

        # add focus and datetime
        values['focus'] = event.focus
        values['error'] = event.error
        values['datetime'] = Time.now().isot
        values['filter'] = event.filter_name

        # write log
        if self._publisher is not None:
            self._publisher(**values)

        # finally, calculate new model
        log.info('Re-calculating model...')
        self._calc_focus_model()

        # finished
        log.info('Done.')
Example #21
0
    async def _on_task_finished(self, event: Event, sender: str) -> bool:
        """Reset current task, when it has finished.

        Args:
            event: The task finished event.
            sender: Who sent it.
        """
        if not isinstance(event, TaskFinishedEvent):
            return False

        # reset current task
        self._current_task_id = None

        # trigger?
        if self._trigger_on_task_finished:
            # get ETA in minutes
            log.info(
                "Received task finished event, triggering new scheduler run..."
            )

            # set it
            self._need_update = True
            self._schedule_start = Time.now()

        return True
Example #22
0
    async def _on_task_started(self, event: Event, sender: str) -> bool:
        """Re-schedule when task has started and we can predict its end.

        Args:
            event: The task started event.
            sender: Who sent it.
        """
        if not isinstance(event, TaskStartedEvent):
            return False

        # store it
        self._current_task_id = event.id
        self._last_task_id = event.id

        # trigger?
        if self._trigger_on_task_started:
            # get ETA in minutes
            eta = (event.eta - Time.now()).sec / 60
            log.info(
                "Received task started event with ETA of %.0f minutes, triggering new scheduler run...",
                eta)

            # set it
            self._need_update = True
            self._schedule_start = event.eta

        return True
Example #23
0
    def night_obs(
        self,
        time: Optional[Union[datetime.datetime,
                             Time]] = None) -> datetime.date:
        """Returns the date of the night for the given night, i.e. the date of the start of the night.

        Args:
            time: Time to return night for. If none is given, current time is used.

        Returns:
            Night of observation.
        """

        # None given?
        if time is None:
            time = Time.now()

        # convert to Time
        if isinstance(time, Time):
            time = time.datetime

        # get local datetime
        if not isinstance(time, datetime.datetime):
            raise ValueError("Invalid time")
        utc_dt = pytz.utc.localize(time)
        loc_dt = utc_dt.astimezone(self._timezone)

        # get night
        if loc_dt.hour < 15:
            loc_dt += datetime.timedelta(days=-1)
        return loc_dt.date()
Example #24
0
    def process_new_image_event(self, event: NewImageEvent, sender: str, *args,
                                **kwargs):
        """Puts a new images in the DB with the given ID.

        Args:
            event:  New image event
            sender: Who sent the event?

        Returns:
            Success
        """

        # filter by source
        if self._sources is not None and sender not in self._sources:
            return

        # put into queue
        log.info('Received new image event from %s.', sender)

        # download image
        try:

            log.info('Downloading file %s...', event.filename)
            image = self.vfs.read_image(event.filename)
        except FileNotFoundError:
            log.error('Could not download image.')
            return

        # get catalog
        cat = image.catalog
        if cat is None:
            # no catalog found in file
            return

        # filter by ellipticity
        cat = cat[cat['ellipticity'] < self._max_ellipticity]

        # get WCS and pixel size
        wcs = WCS(image.header)
        pix_size = abs(proj_plane_pixel_scales(wcs)[0] * 3600.)

        # calculate seeing
        seeing = np.mean(cat['fwhm']) * pix_size

        # correct for airmass?
        if self._correct_for_airmass:
            # Seeing S as function of seeing S0 at zenith and airmass a:
            # S = S0 * a^0.6
            # see https://www.astro.auth.gr/~seeing-gr/seeing_gr_files/theory/node17.html
            # need airmass
            if 'AIRMASS' in image.header:
                seeing /= image.header['AIRMASS']**0.6
            else:
                # could not correct
                return

        # log it
        if self._publisher is not None:
            self._publisher(time=Time.now().isot, seeing=seeing)
Example #25
0
    def from_dict(cls, d: Dict[str, Any]) -> Event:
        # get eta
        eta: Optional[Time] = None
        if "eta" in d and isinstance(d["eta"], str):
            eta = Time(d["eta"])

        # return object
        return GoodWeatherEvent(eta=eta)
Example #26
0
 def __init__(self, info: Dict[str, str]):
     FrameInfo.__init__(self)
     self.info = info
     self.id = self.info["id"]
     self.filename = self.info["basename"]
     self.dateobs = Time(self.info["DATE_OBS"])
     self.filter_name = self.info["FILTER"]
     self.binning = int(self.info["binning"][0])
     self.url = self.info["url"]
Example #27
0
def parse_cli(parser: argparse.ArgumentParser):
    from pyobs.utils.time import Time

    # parse args
    args = parser.parse_args()

    # set debug time now
    if args.debug_time is not None:
        # calculate difference between now and given time
        delta = Time(args.debug_time) - Time.now()
        Time.set_offset_to_now(delta)

    # get full path of config
    if args.config:
        args.config = os.path.abspath(args.config)

    # finished
    return vars(args)
Example #28
0
    async def _find_master(self, image: Image,
                           image_type: ImageType) -> Optional[Image]:
        """Find master calibration frame for given parameters using a cache.

        Args:
            image_type: image type.

        Returns:
            Image or None

        Raises:
            ValueError: if no calibration frame could be found.
        """

        # get mode
        try:
            instrument = image.header["INSTRUME"]
            binning = "{0}x{0}".format(image.header["XBINNING"])
            filter_name = cast(
                str,
                image.header["FILTER"]) if "FILTER" in image.header else None
            time = Time(image.header["DATE-OBS"])
            mode = image_type, instrument, binning, filter_name
        except KeyError:
            # could not fetch header items
            raise ValueError("Could not fetch items from image header.")

        # is in cache?
        for m, item in Calibration.calib_cache:
            if m == mode:
                return item

        # try to download one
        master = await Pipeline.find_master(
            self._archive,
            image_type,
            time,
            instrument,
            binning,
            None
            if image_type in [ImageType.BIAS, ImageType.DARK] else filter_name,
            max_days=30,
        )

        # nothing?
        if master is None:
            raise ValueError("No master frame found.")

        # store it in cache
        Calibration.calib_cache.append((mode, master))

        # too many entries?
        while len(Calibration.calib_cache) > self._max_cache_size:
            Calibration.calib_cache.pop(0)

        # return it
        return master
Example #29
0
    async def _flat_field(self, telescope: ITelescope,
                          camera: ICamera) -> None:
        """Take flat-fields."""

        # set window
        await self._set_window(camera, testing=False)

        # move telescope
        if self._pointing is not None:
            await self._pointing(telescope)

        # do exposures, do not broadcast while testing
        now = Time.now()
        log.info("Exposing flat field %d/%d for %.2fs...",
                 self._exposures_done + 1, self._exposures_total,
                 self._exptime)
        if isinstance(camera, IExposureTime):
            await camera.set_exposure_time(float(self._exptime))
        if isinstance(camera, IImageType):
            await camera.set_image_type(ImageType.SKYFLAT)
        filename = await camera.grab_image()

        # analyse image
        if await self._analyse_image(filename):
            # increase count and quite here, if finished
            self._exptime_done += self._exptime
            self._exposures_done += 1
            if self._exposures_done >= self._exposures_total:
                log.info("Finished all requested flat-fields..")
                self._state = FlatFielder.State.FINISHED
                return

            # call callback
            if self._callback is not None:
                if self.observer is None:
                    raise ValueError("No observer given.")
                sun = self.observer.sun_altaz(now)
                await self._callback(
                    datetime=now.isot,
                    solalt=sun.alt.degree,
                    exptime=self._exptime,
                    counts=self._target_count,
                    filter_name=self._cur_filter,
                    binning=self._cur_binning,
                )

        # then evaluate exposure time
        state = self._eval_exptime()
        if state < 0:
            log.info("Going back to testing...")
            self._state = FlatFielder.State.TESTING
        elif state == 0:
            pass
        else:
            log.info("Missed flat-fielding time, finish task...")
            self._state = FlatFielder.State.FINISHED
Example #30
0
    async def _update(self) -> None:
        now = Time.now()

        # get RA/Dec
        if isinstance(self.module, IPointingRaDec):
            ra, dec = await self.module.get_radec()
            self._ra_dec = SkyCoord(
                ra=ra * u.deg,
                dec=dec * u.deg,
                frame="icrs",
                location=self.observer.location,
                obstime=now,
            )
        else:
            self._ra_dec = None

        # get Alt/Az
        if isinstance(self.module, IPointingAltAz):
            alt, az = await self.module.get_altaz()
            self._alt_az = SkyCoord(
                alt=alt * u.deg,
                az=az * u.deg,
                frame="altaz",
                location=self.observer.location,
                obstime=now,
            )
        else:
            self._alt_az = None

        # get offsets
        if isinstance(self.module, IOffsetsAltAz) and self._alt_az is not None:
            # get offsets
            self._off_alt, self._off_az = await self.module.get_offsets_altaz()

            # convert to ra/dec
            self._off_ra, self._off_dec = self._offset_altaz_to_radec(
                self._off_alt, self._off_az)

        elif isinstance(self.module,
                        IOffsetsRaDec) and self._ra_dec is not None:
            # get offsets
            self._off_ra, self._off_dec = await self.module.get_offsets_radec()

            # convert to alt/az
            self._off_alt, self._off_az = self._offset_radec_to_altaz(
                self._off_ra, self._off_dec)
        else:
            self._off_ra, self._off_dec, self._off_alt, self._off_az = (
                None,
                None,
                None,
                None,
            )

        # signal GUI update
        self.signal_update_gui.emit()