Source code for astrohack.antenna.base_panel

import toolviper.utils.logger as logger

from matplotlib import pyplot as plt
from matplotlib.colors import Normalize

from astrohack.antenna.panel_fitting import PANEL_MODEL_DICT, PanelPoint, PanelModel
from astrohack.utils.constants import *
from astrohack.utils.conversion import convert_unit


[docs] class BasePanel:
[docs] markers = ["X", "o", "*", "P", "D"]
[docs] colors = ["g", "g", "r", "r", "b"]
[docs] linewidth = 0.5
[docs] linecolor = "black"
def __init__( self, model, screws, plot_screw_pos, plot_screw_size, label, center=None, zeta=None, ref_points=None, ): """ Initializes the base panel with the appropriated fitting methods and providing basic functionality Fitting method models are: AIPS fitting models: mean: The panel is corrected by the mean of its samples rigid: The panel samples are fitted to a rigid surface Corotated Paraboloids (the two bending axes are parallel and perpendicular to the radius of the antenna crossing the middle of the panel): corotated_scipy: Paraboloid is fitted using scipy.optimize, robust but slow corotated_lst_sq: Paraboloid is fitted using the linear algebra least squares method, fast but unreliable corotated_robust: Tries corotated_lst_sq, if it diverges falls back to corotated_scipy Experimental fitting models: xy_paraboloid: fitted using scipy.optimize, bending axes are parallel to the x and y axes rotated_paraboloid: fitted using scipy.optimize, bending axes can be rotated by an arbitrary angle full_paraboloid_lst_sq: Full 9 parameter paraboloid fitted using least_squares method, heavily overfits Args: model: What model of surface fitting method to be used label: Panel label screws: position of the screws center: Panel center zeta: panel center angle """
[docs] self.model_name = model
[docs] self.solved = False
[docs] self.fall_back_fit = False
[docs] self.label = label
[docs] self.screws = screws
[docs] self.plot_screw_pos = plot_screw_pos
[docs] self.plot_screw_size = plot_screw_size
[docs] self.samples = []
[docs] self.margins = []
[docs] self.corr = None
if center is None: self.center = PanelPoint(0, 0) else: self.center = center if zeta is None: self.zeta = 0 else: self.zeta = zeta if ref_points is None: self.ref_points = [0, 0, 0] else: self.ref_points = ref_points try: model_dict = PANEL_MODEL_DICT[self.model_name] except KeyError: msg = f"Unknown model {self.model_name}" logger.error(msg) raise ValueError(msg)
[docs] self.model = PanelModel(model_dict, self.zeta, self.ref_points, self.center)
[docs] def add_sample(self, sample): """ Add a point to the panel's list of points to be fitted Args: sample: tuple/list containing point description [xcoor,ycoor,xidx,yidx,value] """ if self.model_name in PANEL_MODEL_DICT.keys(): self.samples.append(PanelPoint(*sample)) else: self.samples.append(sample)
[docs] def add_margin(self, sample): """ Add a point to the panel's list of points to be corrected, but not fitted Args: sample: tuple/list containing point description [xcoor,ycoor,xidx,yidx,value] """ if self.model_name in PANEL_MODEL_DICT.keys(): self.margins.append(PanelPoint(*sample)) else: self.margins.append(sample)
[docs] def solve(self): if len(self.samples) < self.model.npar: self._fallback_solve() status = False else: try: self.model.solve(self.samples) status = True except np.linalg.LinAlgError: self._fallback_solve() status = False self.solved = True return status
def _fallback_solve(self): """ Changes the method association to mean surface fitting, and fits the panel with it """ self.model = PanelModel( PANEL_MODEL_DICT["mean"], self.zeta, self.ref_points, self.center ) self.model.solve(self.samples) self.fall_back_fit = True
[docs] def get_corrections(self): if not self.solved: msg = "Cannot correct a panel that is not solved" logger.error(msg) raise RuntimeError(msg) self.corr = self.model.correct(self.samples, self.margins) return self.corr
[docs] def export_screws(self, unit="mm"): """ Export screw adjustments to a numpy array in unit Args: unit: Unit for the screw adjustments Returns: Numpy array with screw adjustments """ fac = convert_unit("m", unit, "length") nscrew = len(self.screws) screw_corr = np.zeros(nscrew) for iscrew, screw in enumerate(self.screws): screw_corr[iscrew] = fac * self.model.correct_point(screw) return screw_corr
[docs] def plot_label(self, ax, rotate=True): """ Plots panel label to ax Args: ax: matplotlib axes instance rotate: Rotate label for better display """ if rotate: angle = (-self.zeta % pi - pi / 2) * convert_unit( "rad", "deg", "trigonometric" ) else: angle = 0 ax.text( self.center.xc, self.center.yc, self.label, fontsize=fontsize, ha="center", va="center", rotation=angle, )
[docs] def plot_screws(self, ax): """ Plots panel screws to ax Args: ax: matplotlib axes instance """ for iscrew, screw in enumerate(self.screws): ax.scatter( screw.xc, screw.yc, marker=self.markers[iscrew], lw=self.linewidth, s=markersize, color=self.colors[iscrew], )
[docs] def plot_corrections(self, ax, cmap, corrections, threshold, vmin, vmax): """ Plot screw corrections onto an axis Args: ax: axis for plot cmap: Colormap of the corrections to be applied to each screw corrections: the screw corrections threshold: Threshold below which data is considered negligable vmin: bottom of the colormap vmax: top of the colormap """ norm = Normalize(vmin=vmin, vmax=vmax) for iscrew in range(self.plot_screw_pos.shape[0]): screw = self.plot_screw_pos[iscrew] if np.abs(corrections[iscrew]) < threshold: corr = 0 else: corr = corrections[iscrew] circle = plt.Circle( (screw.xc, screw.yc), self.plot_screw_size, color=cmap(norm(corr)), fill=True, ) ax.add_artist(circle)