Skip to content

Commit

Permalink
Autofocus & bisque dome changes from May Huntsman commissioning run (#…
Browse files Browse the repository at this point in the history
…535)

* Reverted focus peak fitting from spline to Lorentzian

* Assorted small fixes

* Change Lorentzian fitting to polynomial fitting around peak

* Add colorbar to pretty images

* Fix focus plots so fit appeats

* Codestyle fixes

* Removed Slack code & excessive TheSkyX logging

* Removed coarse focus on 1st focus code
  • Loading branch information
AnthonyHorton authored Jul 13, 2018
1 parent 3e76522 commit c952500
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 29 deletions.
3 changes: 2 additions & 1 deletion conf_files/peas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ environment:
weather:
station: mongo
aag_cloud:
serial_port: '/dev/ttyUSB1'
# serial_port: '/dev/ttyUSB1'
serial_port: '/dev/tty.USA19H2P1.1'
threshold_cloudy: -25
threshold_very_cloudy: -15.
threshold_windy: 50.
Expand Down
5 changes: 5 additions & 0 deletions pocs/camera/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ def process_exposure(self, info, observation_event, exposure_event=None):
self.logger.warning('Problem with extracting pretty image: {}'.format(e))

file_path = self._process_fits(file_path, info)
try:
info['exp_time'] = info['exp_time'].value
except Exception:
pass

if info['is_primary']:
self.logger.debug("Adding current observation to db: {}".format(image_id))
Expand All @@ -261,6 +265,7 @@ def process_exposure(self, info, observation_event, exposure_event=None):
fits_utils.fpack(file_path)

self.logger.debug("Adding image metadata to db: {}".format(image_id))

self.db.insert('observations', {
'data': info,
'date': current_time(datetime=True),
Expand Down
1 change: 1 addition & 0 deletions pocs/camera/sbigudrv.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import platform
import ctypes
from ctypes.util import find_library
from warnings import warn
import _ctypes
import os
import time
Expand Down
3 changes: 0 additions & 3 deletions pocs/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ def has_messaging(self, value):
def should_retry(self):
return self._obs_run_retries >= 0


##################################################################################################
# Methods
##################################################################################################
Expand Down Expand Up @@ -396,7 +395,6 @@ def has_free_space(self, required_space=0.25 * u.gigabyte):
free_space = get_free_space()
return free_space.value >= required_space.to(u.gigabyte).value


##################################################################################################
# Convenience Methods
##################################################################################################
Expand Down Expand Up @@ -442,7 +440,6 @@ def wait_until_safe(self):
while not self.is_safe(no_warning=True):
self.sleep(delay=self._safe_delay)


##################################################################################################
# Class Methods
##################################################################################################
Expand Down
41 changes: 39 additions & 2 deletions pocs/dome/bisque.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(self, *args, **kwargs):
"Bisque Mounts required a template directory")

self.template_dir = template_dir
self._is_parked = True

@property
def is_connected(self):
Expand Down Expand Up @@ -57,7 +58,17 @@ def read_slit_state(self):

@property
def status(self):
return self.read_slit_state()
self.write(self._get_command('dome/status.js'))
return self.read()

@property
def position(self):
self.write(self._get_command('dome/position.js'))
return self.read()

@property
def is_parked(self):
return self._is_parked

def connect(self):
if not self.is_connected:
Expand Down Expand Up @@ -105,6 +116,33 @@ def close(self):

return self.is_closed

def park(self):
if self.is_connected:
self.write(self._get_command('dome/park.js'))
response = self.read()

self._is_parked = response['success']

return self.is_parked

def unpark(self):
if self.is_connected:
self.write(self._get_command('dome/unpark.js'))
response = self.read()

self._is_parked = not response['success']

return not self.is_parked

def find_home(self):
if self.is_connected:
self.write(self._get_command('dome/home.js'))
response = self.read()

self._is_parked = response['success']

return self.is_parked

##################################################################################################
# Communication Methods
##################################################################################################
Expand Down Expand Up @@ -133,7 +171,6 @@ def read(self, timeout=5):

return response_obj


##################################################################################################
# Private Methods
##################################################################################################
Expand Down
55 changes: 36 additions & 19 deletions pocs/focuser/focuser.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import matplotlib.colors as colours
import matplotlib.pyplot as plt

from scipy.interpolate import UnivariateSpline
from astropy.modeling import models, fitting
from scipy.ndimage import binary_dilation

import numpy as np
Expand Down Expand Up @@ -463,22 +463,38 @@ def _autofocus(self,
best_focus = focus_positions[imax]

elif not coarse:
# Crude guess at a standard deviation for focus metric, 40% of the maximum value
weights = np.ones(len(focus_positions)) / (spline_smoothing * metric.max())

# Fit smoothing spline to focus metric data
fit = UnivariateSpline(focus_positions, metric, w=weights, k=4, ext='raise')

try:
stationary_points = fit.derivative().roots()
except ValueError as err:
self.logger.warning('Error finding extrema of spline fit: {}'.format(err))
best_focus = focus_positions[imax]
else:
extrema = fit(stationary_points)
if len(extrema) > 0:
best_focus = stationary_points[extrema.argmax()]
fitted = True
# Fit data around the maximum value to determine best focus position.
# Initialise models
shift = models.Shift(offset=-focus_positions[imax])
poly = models.Polynomial1D(degree=4, c0=1, c1=0, c2=-1e-2, c3=0, c4=-1e-4,
fixed={'c0': True, 'c1': True, 'c3': True})
scale = models.Scale(factor=metric[imax])
reparameterised_polynomial = shift | poly | scale

# Initialise fitter
fitter = fitting.LevMarLSQFitter()

# Select data range for fitting. Tries to use 2 points either side of max, if in range.
fitting_indices = (max(imax - 2, 0), min(imax + 2, n_positions - 1))

# Fit models to data
fit = fitter(reparameterised_polynomial,
focus_positions[fitting_indices[0]:fitting_indices[1] + 1],
metric[fitting_indices[0]:fitting_indices[1] + 1])

best_focus = -fit.offset_0
fitted = True

# Guard against fitting failures, force best focus to stay within sweep range
if best_focus < focus_positions[0]:
self.logger.warning("Fitting failure: best focus {} below sweep limit {}".format(best_focus,
focus_positions[0]))
best_focus = focus_positions[1]

if best_focus > focus_positions[-1]:
self.logger.warning("Fitting failure: best focus {} above sweep limit {}".format(best_focus,
focus_positions[-1]))
best_focus = focus_positions[-2]

else:
# Coarse focus, just use max value.
Expand All @@ -488,8 +504,9 @@ def _autofocus(self,
ax2 = fig.add_subplot(3, 1, 2)
ax2.plot(focus_positions, metric, 'bo', label='{}'.format(merit_function))
if fitted:
fs = np.arange(focus_positions[0], focus_positions[-1] + 1)
ax2.plot(fs, fit(fs), 'b-', label='Smoothing spline fit')
fs = np.arange(focus_positions[fitting_indices[0]],
focus_positions[fitting_indices[1]] + 1)
ax2.plot(fs, fit(fs), 'b-', label='Polynomial fit')

ax2.set_xlim(focus_positions[0] - focus_step / 2, focus_positions[-1] + focus_step / 2)
u_limit = 1.10 * metric.max()
Expand Down
2 changes: 1 addition & 1 deletion pocs/observatory.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ def get_standard_headers(self, observation=None):

return headers

def autofocus_cameras(self, camera_list=None, coarse=False):
def autofocus_cameras(self, camera_list=None, coarse=None):
"""
Perform autofocus on all cameras with focus capability, or a named subset
of these. Optionally will perform a coarse autofocus first, otherwise will
Expand Down
5 changes: 3 additions & 2 deletions pocs/utils/images/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def _make_pretty_from_fits(
title = '{} ({}s {}) {}'.format(title, exp_time, filter_type, date_time)
norm = ImageNormalize(interval=PercentileInterval(percent_value), stretch=LogStretch())

plt.figure(figsize=figsize, dpi=dpi)
fig = plt.figure(figsize=figsize, dpi=dpi)

if wcs.is_celestial:
ax = plt.subplot(projection=wcs)
Expand Down Expand Up @@ -143,7 +143,8 @@ def _make_pretty_from_fits(
ax.set_xlabel('X / pixels')
ax.set_ylabel('Y / pixels')

ax.imshow(data, norm=norm, cmap=palette, origin='lower')
im = ax.imshow(data, norm=norm, cmap=palette, origin='lower')
fig.colorbar(im)
plt.title(title)

new_filename = fname.replace('.fits', '.jpg')
Expand Down
9 changes: 8 additions & 1 deletion resources/bisque/dome/park.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,12 @@ sky6Dome.Connect();
if (sky6Dome.IsConnected == 0) {
Out = "Not connected"
} else {
Out = sky6Dome.Park();
sky6Dome.Park();
while (!sky6Dome.IsParkComplete) {
sky6Web.Sleep(1000);
}

Out = JSON.stringify({
"success": sky6Dome.IsParkComplete
});
};

0 comments on commit c952500

Please sign in to comment.