diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 6cb07a7..b8d6f54 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -44,11 +44,6 @@ jobs: python: '3.9' toxenv: py39-test-alldeps-cov - - name: OS X - Python 3.9 with all optional dependencies - os: macos-latest - python: '3.9' - toxenv: py39-test-alldeps - - name: Windows - Python 3.9 with all optional dependencies os: windows-latest python: '3.9' diff --git a/.gitignore b/.gitignore index 37e86e8..020fd87 100644 --- a/.gitignore +++ b/.gitignore @@ -135,9 +135,12 @@ dmypy.json # Directory to store test data test_data/ +lco_debug_data/ +manual_reduction/ # VS Code .vscode test.db - +debug.db +.DS_Store diff --git a/CHANGES.md b/CHANGES.md index ff81784..582c66d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +0.10.0 (2024-11-05) +------------------- +- Numerous fixes based on commissioning experience +- Added/refactored the necessary logic to re-extract + the data via the UI + 0.9.0 (2024-04-02) ------------------ - Fixes based on Joey's comments diff --git a/Dockerfile b/Dockerfile index 0611e2a..79dfe70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/lcogt/banzai:1.13.1 +FROM ghcr.io/lcogt/banzai:1.19.1 USER root diff --git a/banzai_floyds/arc_lines.py b/banzai_floyds/arc_lines.py index 05c795c..92448bc 100644 --- a/banzai_floyds/arc_lines.py +++ b/banzai_floyds/arc_lines.py @@ -20,12 +20,6 @@ 'line_source': 'Hg', 'line_notes': '' }, - { - 'wavelength': 4077.837, - 'line_strength': 0.031, - 'line_source': 'Hg', - 'line_notes': '' - }, { 'wavelength': 4358.335, 'line_strength': 1.577, @@ -50,12 +44,6 @@ 'line_source': 'ArI', 'line_notes': '' }, - { - 'wavelength': 7147.0416, - 'line_strength': 0.011, - 'line_source': 'ArI', - 'line_notes': '' - }, { 'wavelength': 7272.9359, 'line_strength': 0.079, @@ -116,12 +104,6 @@ 'line_source': 'ArI', 'line_notes': '' }, - { - 'wavelength': 10139.75, - 'line_strength': 0.019, - 'line_source': 'Hg', - 'line_notes': '' - }, ] unused_lines = [{ @@ -139,6 +121,11 @@ 'line_strength': 0.0064, 'line_source': 'Hg', 'line_notes': 'blend' +}, { + 'wavelength': 4077.837, + 'line_strength': 0.031, + 'line_source': 'Hg', + 'line_notes': 'weak' }, { 'wavelength': 4339.2232, 'line_strength': 'nan', @@ -174,6 +161,11 @@ 'line_strength': 'nan', 'line_source': 'Hg', 'line_notes': 'Weak' +}, { + 'wavelength': 7147.0416, + 'line_strength': 0.011, + 'line_source': 'ArI', + 'line_notes': 'Too close to previous line' }, { 'wavelength': 4680.1359, @@ -360,6 +352,11 @@ 'line_strength': 'nan', 'line_source': 'ArI', 'line_notes': 'No flux in FLOYDS' +}, { + 'wavelength': 10139.75, + 'line_strength': 0.019, + 'line_source': 'Hg', + 'line_notes': 'Too close to the chip edge at COJ' }, { 'wavelength': 11078.87, 'line_strength': 'nan', diff --git a/banzai_floyds/background.py b/banzai_floyds/background.py new file mode 100644 index 0000000..092e4bf --- /dev/null +++ b/banzai_floyds/background.py @@ -0,0 +1,115 @@ +import numpy as np +from astropy.table import Table, vstack +from scipy.interpolate import CloughTocher2DInterpolator +from banzai.stages import Stage +from numpy.polynomial.legendre import Legendre + + +def fit_background(data, background_order=3): + # I tried a wide variety of bsplines and two fits here without success. + # The scipy bplines either had significant issues with the number of points we are fitting in the whole 2d frame or + # could not capture the variation near sky line edges (the key reason to use 2d fits from Kelson 2003). + # I also tried using 2d polynomials, but to get the order high enough to capture the variation in the skyline edges, + # I was introducing significant ringing in the fits (to the point of oscillating between positive and + # negative values in the data). + # This is now doing something closer to what IRAF did, interpolating the background regions onto the wavelength + # bin centers, fitting a 1d polynomial, and interpolating back on the original wavelengths to subtract per pixel. + # In this way, it is only the background model that is interpolated and not the pixel values themselves. + data['data_bin_center'] = 0.0 + data['uncertainty_bin_center'] = 0.0 + for order in [1, 2]: + in_order = data['order'] == order + + data_interpolator = CloughTocher2DInterpolator(np.array([data['wavelength'][in_order], + data['y_profile'][in_order]]).T, + data['data'][in_order].ravel(), fill_value=0) + uncertainty_interpolator = CloughTocher2DInterpolator(np.array([data['wavelength'][in_order], + data['y_profile'][in_order]]).T, + data['uncertainty'][in_order].ravel(), fill_value=0) + + data['data_bin_center'][in_order] = data_interpolator(data['wavelength_bin'][in_order], + data['y_profile'][in_order]) + data['uncertainty_bin_center'][in_order] = uncertainty_interpolator(data['wavelength_bin'][in_order], + data['y_profile'][in_order]) + + # Assume no wavelength dependence for the wavelength_bin = 0 and first and last bin in the order + # which have funny edge effects + background_bin_center = [] + for data_to_fit in data.groups: + if data_to_fit['wavelength_bin'][0] == 0: + continue + # Catch the case where we are an edge and fall outside the qhull interpolation surface + if np.all(data_to_fit['data_bin_center'] == 0): + data_column = 'data' + uncertainty_column = 'uncertainty' + else: + data_column = 'data_bin_center' + uncertainty_column = 'uncertainty_bin_center' + in_background = data_to_fit['in_background'] + in_background = np.logical_and(in_background, data_to_fit[data_column] != 0) + polynomial = Legendre.fit(data_to_fit['y_profile'][in_background], data_to_fit[data_column][in_background], + background_order, + domain=[np.min(data_to_fit['y_profile']), np.max(data_to_fit['y_profile'])], + w=1/data_to_fit[uncertainty_column][in_background]**2) + + background_bin_center.append(polynomial(data_to_fit['y_profile'])) + + data['background_bin_center'] = 0.0 + data['background_bin_center'][data['wavelength_bin'] != 0] = np.hstack(background_bin_center) + + results = Table({'x': [], 'y': [], 'background': []}) + for order in [1, 2]: + in_order = np.logical_and(data['order'] == order, data['wavelength_bin'] != 0) + background_interpolator = CloughTocher2DInterpolator(np.array([data['wavelength_bin'][in_order], + data['y_profile'][in_order]]).T, + data['background_bin_center'][in_order], fill_value=0) + background = background_interpolator(data['wavelength'][in_order], data['y_profile'][in_order]) + # Deal with the funniness at the wavelength bin edges + upper_edge = data['wavelength'][in_order] > np.max(data['wavelength_bin'][in_order]) + background[upper_edge] = data[in_order]['background_bin_center'][upper_edge] + lower_edge = data['wavelength'][in_order] < np.min(data['wavelength_bin'][in_order]) + background[lower_edge] = data['background_bin_center'][in_order][lower_edge] + order_results = Table({'x': data['x'][in_order], 'y': data['y'][in_order], 'background': background}) + results = vstack([results, order_results]) + # Clean up our intermediate columns for now + data.remove_columns(['data_bin_center', 'uncertainty_bin_center', 'background_bin_center']) + return results + + +def set_background_region(image): + if 'in_background' in image.binned_data.colnames: + return + + image.binned_data['in_background'] = False + for order_id in [2, 1]: + in_order = image.binned_data['order'] == order_id + this_background = np.zeros(in_order.sum(), dtype=bool) + data = image.binned_data[in_order] + for background_region in image.background_windows[order_id - 1]: + in_background_reg = data['y_profile'] >= (background_region[0] * data['profile_sigma']) + in_background_reg = np.logical_and(data['y_profile'] <= (background_region[1] * data['profile_sigma']), + in_background_reg) + this_background = np.logical_or(this_background, in_background_reg) + image.binned_data['in_background'][in_order] = this_background + for order in [1, 2]: + for reg_num, region in enumerate(image.background_windows[order - 1]): + image.meta[f'BKWO{order}{reg_num}0'] = ( + region[0], f'Background region {reg_num} for order:{order} minimum in profile sigma' + ) + image.meta[f'BKWO{order}{reg_num}1'] = ( + region[1], f'Background region {reg_num} for order:{order} maximum in profile sigma' + ) + + +class BackgroundFitter(Stage): + DEFAULT_BACKGROUND_WINDOW = (7.5, 15) + + def do_stage(self, image): + if not image.background_windows: + background_window = [[-self.DEFAULT_BACKGROUND_WINDOW[1], -self.DEFAULT_BACKGROUND_WINDOW[0]], + [self.DEFAULT_BACKGROUND_WINDOW[0], self.DEFAULT_BACKGROUND_WINDOW[1]]] + image.background_windows = [background_window, background_window] + set_background_region(image) + background = fit_background(image.binned_data) + image.background = background + return image diff --git a/banzai_floyds/binning.py b/banzai_floyds/binning.py new file mode 100644 index 0000000..5251a9f --- /dev/null +++ b/banzai_floyds/binning.py @@ -0,0 +1,9 @@ +from banzai.stages import Stage +from banzai_floyds.utils.binning_utils import bin_data + + +class Binning(Stage): + def do_stage(self, image): + image.binned_data = bin_data(image.data, image.uncertainty, image.wavelengths, + image.orders, image.mask) + return image diff --git a/banzai_floyds/extract.py b/banzai_floyds/extract.py index b50f613..3fe25ad 100644 --- a/banzai_floyds/extract.py +++ b/banzai_floyds/extract.py @@ -1,183 +1,34 @@ from banzai.stages import Stage import numpy as np -from astropy.table import Table, vstack -from banzai_floyds.matched_filter import optimize_match_filter -from numpy.polynomial.legendre import Legendre -from banzai_floyds.utils.fitting_utils import gauss, fwhm_to_sigma -from astropy.modeling import fitting, models +from astropy.table import Table from banzai.logs import get_logger -from banzai_floyds.utils.profile_utils import profile_fits_to_data logger = get_logger() -def profile_gauss_fixed_width(params, x, sigma): - center, = params - return gauss(x, center, sigma) - - -def bins_to_bin_edges(bins): - bin_edges = bins['center'] - (bins['width'] / 2.0) - bin_edges = np.append(bin_edges, bins['center'][-1] + (bins['width'][-1] / 2.0)) - return bin_edges - - -def bin_data(data, uncertainty, wavelengths, orders, wavelength_bins): - binned_data = None - x2d, y2d = np.meshgrid(np.arange(data.shape[1]), np.arange(data.shape[0])) - for order_id, order_wavelengths in zip(orders.order_ids, wavelength_bins): - in_order = orders.data == order_id - # Throw away the data that is outside the first and last bins - min_wavelength = order_wavelengths[0]['center'] - (order_wavelengths[0]['width'] / 2.0) - max_wavelength = order_wavelengths[-1]['center'] + (order_wavelengths[-1]['width'] / 2.0) - - in_order = np.logical_and(in_order, wavelengths.data > min_wavelength) - in_order = np.logical_and(in_order, wavelengths.data < max_wavelength) - - y_order = y2d[in_order] - orders.center(x2d[in_order])[order_id - 1] - data_table = Table({'data': data[in_order], 'uncertainty': uncertainty[in_order], - 'wavelength': wavelengths.data[in_order], 'x': x2d[in_order], - 'y': y2d[in_order], 'y_order': y_order}) - bin_number = np.digitize(data_table['wavelength'], bins_to_bin_edges(order_wavelengths)) - data_table['wavelength_bin'] = order_wavelengths['center'][bin_number - 1] - data_table['wavelength_bin_width'] = order_wavelengths['width'][bin_number - 1] - data_table['order'] = order_id - if binned_data is None: - binned_data = data_table - else: - binned_data = vstack([binned_data, data_table]) - return binned_data.group_by(('order', 'wavelength_bin')) - - -def fit_profile_centers(data, polynomial_order=9, profile_width=4): - trace_points = Table({'wavelength': [], 'center': [], 'order': []}) - for data_to_fit in data.groups: - # Pass a match filter (with correct s/n scaling) with a gaussian with a default width - initial_guess = (data_to_fit['y_order'][np.argmax(data_to_fit['data'])],) - best_fit_center, = optimize_match_filter(initial_guess, data_to_fit['data'], data_to_fit['uncertainty'], - profile_gauss_fixed_width, data_to_fit['y_order'], - args=(fwhm_to_sigma(profile_width),)) - # If the peak pixel of the match filter is > 2 times the median (or something like that) keep the point - peak = np.argmin(np.abs(data_to_fit['y_order'] - best_fit_center)) - median_snr = np.median(np.abs(data_to_fit['data'] / data_to_fit['uncertainty'])) - peak_snr = data_to_fit['data'][peak] / data_to_fit['uncertainty'][peak] - if peak_snr > 2.0 * median_snr: - new_trace_table = Table({'wavelength': [data_to_fit['wavelength_bin'][0]], - 'center': [best_fit_center], - 'order': [data_to_fit['order'][0]]}) - trace_points = vstack([trace_points, new_trace_table]) - - # save the polynomial for the profile - trace_centers = [Legendre.fit(order_data['wavelength'], order_data['center'], deg=polynomial_order) - for order_data in trace_points.group_by('order').groups] - return trace_centers - - -def fit_profile_width(data, profile_fits, poly_order=3, default_width=4): - # In principle, this should be some big 2d fit where we fit the profile center, the profile width, - # and the background in one go - profile_width = {'wavelength': [], 'width': [], 'order': []} - fitter = fitting.LevMarLSQFitter() - for data_to_fit in data.groups: - wavelength_bin = data_to_fit['wavelength_bin'][0] - order_id = data_to_fit['order'][0] - profile_center = profile_fits[order_id - 1](wavelength_bin) - - # If the SNR of the peak of the profile is 2 > than the peak of the background, keep the profile width - peak = np.argmin(np.abs(profile_center - data_to_fit['y_order'])) - peak_snr = data_to_fit['data'][peak] / data_to_fit['uncertainty'][peak] - median_snr = np.median(np.abs(data_to_fit['data'] / data_to_fit['uncertainty'])) - # Short circuit if the trace is not significantly brighter than the background in this bin - if peak_snr < 2.0 * median_snr: - continue - - # Only fit the data close to the profile so that we can assume a low order background - peak_window = np.abs(data_to_fit['y_order'] - profile_center) <= 6.0 * fwhm_to_sigma(default_width) - - # Pass a match filter (with correct s/n scaling) with a gaussian with a default width - initial_amplitude = np.max(data_to_fit['data'][peak_window]) - model = models.Gaussian1D(amplitude=initial_amplitude, mean=profile_center, - stddev=fwhm_to_sigma(default_width)) - model += models.Legendre1D(degree=2, domain=(np.min(data_to_fit['y_order'][peak_window]), - np.max(data_to_fit['y_order'][peak_window])), - c0=np.median(data_to_fit['data'][peak_window])) - # Force the profile center to be on the chip...(add 0.3 to pad the edge) - model.mean_0.min = np.min(data_to_fit['y_order'][peak_window]) + 0.3 - model.mean_0.max = np.max(data_to_fit['y_order'][peak_window]) - 0.3 - - inv_variance = data_to_fit['uncertainty'][peak_window] ** -2.0 - best_fit_model = fitter(model, x=data_to_fit['y_order'][peak_window], - y=data_to_fit['data'][peak_window], weights=inv_variance, maxiter=400) - - best_fit_sigma = best_fit_model.stddev_0.value - - profile_width['wavelength'].append(wavelength_bin) - profile_width['width'].append(best_fit_sigma) - profile_width['order'].append(order_id) - profile_width = Table(profile_width) - # save the polynomial for the profile - profile_widths = [Legendre.fit(order_data['wavelength'], order_data['width'], deg=poly_order) - for order_data in profile_width.group_by('order').groups] - return profile_widths - - -def fit_background(data, profile_centers, profile_widths, x_poly_order=2, y_poly_order=4): - results = Table({'x': [], 'y': [], 'background': []}) - fitter = fitting.LinearLSQFitter() - for data_to_fit in data.groups: - wavelength_bin = data_to_fit['wavelength_bin'][0] - order_id = data_to_fit['order'][0] - profile_center = profile_centers[order_id - 1](wavelength_bin) - profile_width = profile_widths[order_id - 1](wavelength_bin) - peak = np.argmin(np.abs(profile_center - data_to_fit['y_order'])) - - # Pass a match filter (with correct s/n scaling) with a gaussian with a default width - initial_coeffs = np.zeros((x_poly_order + 1) + y_poly_order) - initial_coeffs[0] = np.median(data_to_fit['data']) / data_to_fit['data'][peak] - # TODO: Fit the background with a totally fixed profile, and no need to iterate - # since our filter is linear - background_window = np.abs(data_to_fit['y_order'] - profile_center) > 3.0 * fwhm_to_sigma(profile_width) - model = models.Legendre2D(x_degree=x_poly_order, y_degree=y_poly_order, - x_domain=(np.min(data_to_fit['wavelength'][background_window]), - np.max(data_to_fit['wavelength'][background_window])), - y_domain=(np.min(data_to_fit['y_order'][background_window]), - np.max(data_to_fit['y_order'][background_window]))) - inv_variance = data_to_fit['uncertainty'][background_window] ** -2.0 - best_fit_model = fitter(model, - x=data_to_fit['wavelength'][background_window], - y=data_to_fit['y_order'][background_window], - z=data_to_fit['data'][background_window], - weights=inv_variance) - background = best_fit_model(data_to_fit['wavelength'], data_to_fit['y_order']) - background_fit = Table({'x': data_to_fit['x'], - 'y': data_to_fit['y'], - 'background': background}) - results = vstack([background_fit, results]) - return results - - -def get_wavelength_bins(wavelengths): - """ - Set the wavelength bins to be at the pixel edges along the center of the orders. - """ - # TODO: in the long run we probably shouldn't bin at all and just do a full 2d sky fit - # (including all flux in the order, yikes) - # Throw out the edge bins of the order as the lines are tilt and our orders are vertical - pixels_to_cut = np.round(0.5 * np.sin(np.deg2rad(wavelengths.line_tilts)) * wavelengths.orders.order_heights) - pixels_to_cut = pixels_to_cut.astype(int) - bin_edges = wavelengths.bin_edges - cuts = [] - for cut in pixels_to_cut: - if cut == 0: - right_side_slice = slice(1, None) - else: - right_side_slice = slice(1+cut, -cut) - left_side_slice = slice(cut, -1-cut) - cuts.append((right_side_slice, left_side_slice)) - return [Table({'center': (edges[right_cut] + edges[left_cut]) / 2.0, - 'width': edges[right_cut] - edges[left_cut]}) - for edges, (right_cut, left_cut) in zip(bin_edges, cuts)] +def set_extraction_region(image): + if 'extraction_window' in image.binned_data.colnames: + return + + image.binned_data['extraction_window'] = False + for order_id in [2, 1]: + in_order = image.binned_data['order'] == order_id + data = image.binned_data[in_order] + extraction_region = image.extraction_windows[order_id - 1] + this_extract_window = data['y_profile'] >= extraction_region[0] * data['profile_sigma'] + this_extract_window = np.logical_and( + data['y_profile'] <= extraction_region[1] * data['profile_sigma'], this_extract_window + ) + image.binned_data['extraction_window'][in_order] = this_extract_window + for order in [1, 2]: + this_extract_window = image.extraction_windows[order - 1] + image.meta[f'XTRTW{order}0'] = ( + this_extract_window[0], f'Extraction window minimum in profile sigma for order {order}' + ) + image.meta[f'XTRTW{order}1'] = ( + this_extract_window[1], f'Extraction window maximum in profile sigma for order {order}' + ) def extract(binned_data): @@ -189,20 +40,37 @@ def extract(binned_data): results = {'fluxraw': [], 'fluxrawerr': [], 'wavelength': [], 'binwidth': [], 'order': [], 'background': []} for data_to_sum in binned_data.groups: wavelength_bin = data_to_sum['wavelength_bin'][0] + # Skip pixels that don't fall into a bin we are going to extract + if wavelength_bin == 0: + continue + if data_to_sum['extraction_window'].sum() == 0: + continue + # Cut any bins that don't include the profile center. If the weights are small (i.e. we only caught the edge + # of the profile), this blows up numerically. The threshold here is a little arbitrary. It needs to be small + # enough to not have numerical artifacts but large enough to not reject broad profiles. + if np.max(data_to_sum['weights'][data_to_sum['extraction_window']]) < 5e-3: + continue + wavelength_bin_width = data_to_sum['wavelength_bin_width'][0] order_id = data_to_sum['order'][0] # This should be equivalent to Horne 1986 optimal extraction flux = data_to_sum['data'] - data_to_sum['background'] - flux *= data_to_sum['weights'] + weights = data_to_sum['weights'] + # If we are using uniform weights, the sum of the weights needs to be normalized + # i.e. the psf needs to sum to 1. We assume non-uniform weights are already normalized. + if np.all(weights == 0): + weights /= data_to_sum['extraction_window'].sum() + flux *= weights flux *= data_to_sum['uncertainty'] ** -2 - flux = np.sum(flux) - flux_normalization = np.sum(data_to_sum['weights']**2 * data_to_sum['uncertainty']**-2) - background = data_to_sum['background'] * data_to_sum['weights'] + flux = np.sum(flux[data_to_sum['extraction_window']]) + flux_normalization = weights**2 * data_to_sum['uncertainty']**-2 + flux_normalization = np.sum(flux_normalization[data_to_sum['extraction_window']]) + background = data_to_sum['background'] * weights background *= data_to_sum['uncertainty'] ** -2 - background = np.sum(background) + background = np.sum(background[data_to_sum['extraction_window']]) results['fluxraw'].append(flux / flux_normalization) results['background'].append(background / flux_normalization) - uncertainty = np.sqrt(np.sum(data_to_sum['weights']) / flux_normalization) + uncertainty = np.sqrt(np.sum(weights[data_to_sum['extraction_window']]) / flux_normalization) results['fluxrawerr'].append(uncertainty) results['wavelength'].append(wavelength_bin) results['binwidth'].append(wavelength_bin_width) @@ -210,60 +78,17 @@ def extract(binned_data): return Table(results) -def combine_wavelegnth_bins(wavelength_bins): - """ - Combine wavelength bins, taking the small delta (higher resolution) bins - """ - # Find the overlapping bins - # Assume that the orders are basically contiguous and monotonically increasing - wavelength_regions = [(min(order_bins['center']), max(order_bins['center'])) for order_bins in wavelength_bins] - - # Assume the smaller of the bin widths are from the blue order - # We assume here we only have 2 orders and that one order does not fully encompass the other - min_wavelength = min(np.array(wavelength_regions).ravel()) - blue_order_index = 0 if min_wavelength in wavelength_regions[0]['center'] else 1 - red_order_index = 0 if blue_order_index else 1 - - overlap_end_index = np.min(np.argwhere(wavelength_bins[red_order_index]['center'] > - np.max(wavelength_regions[blue_order_index]['center']))) - # clean up the middle partial overlaps - middle_bin_upper = wavelength_bins[red_order_index]['center'][overlap_end_index + 1] - middle_bin_upper -= wavelength_bins[red_order_index]['width'][overlap_end_index] / 2.0 - middle_bin_lower = wavelength_bins[blue_order_index]['center'][-1] - middle_bin_lower += wavelength_bins[blue_order_index]['width'] / 2.0 - middle_bin_center = (middle_bin_upper + middle_bin_lower) / 2.0 - middle_bin_width = middle_bin_upper - middle_bin_lower - overlap_end_index += 1 - new_bins = {'center': np.hstack([wavelength_bins[blue_order_index]['center'], [middle_bin_center], - wavelength_bins[red_order_index][overlap_end_index:]['center']]), - 'width': np.hstack([wavelength_bins[blue_order_index]['center'], - [middle_bin_width], - wavelength_bins[red_order_index][overlap_end_index:]['center']])} - return Table(new_bins) - - -class ProfileFitter(Stage): - POLYNOMIAL_ORDER = 6 +class Extractor(Stage): + DEFAULT_EXTRACT_WINDOW = 2.5 def do_stage(self, image): - image.wavelength_bins = get_wavelength_bins(image.wavelengths) - image.binned_data = bin_data(image.data, image.uncertainty, image.wavelengths, - image.orders, image.wavelength_bins) - profile_centers = fit_profile_centers(image.binned_data, polynomial_order=self.POLYNOMIAL_ORDER) - profile_widths = fit_profile_width(image.binned_data, profile_centers) - image.profile_fits = profile_centers, profile_widths - image.profile = profile_fits_to_data(image.data.shape, profile_centers, profile_widths, - image.orders, image.wavelengths.data) - return image - + if not image.extraction_windows: + window = [-self.DEFAULT_EXTRACT_WINDOW, self.DEFAULT_EXTRACT_WINDOW] + image.extraction_windows = [window, window] + set_extraction_region(image) -class Extractor(Stage): - def do_stage(self, image): - profile_centers, profile_widths = image.profile_fits - background = fit_background(image.binned_data, profile_centers, profile_widths) - image.background = background image.extracted = extract(image.binned_data) - # TODO: Stitching together the orders is going to require flux calibration and probably - # a scaling due to aperture corrections - return image + +# TODO: Stitching together the orders is going to require flux calibration and probably +# a scaling due to aperture corrections diff --git a/banzai_floyds/flux.py b/banzai_floyds/flux.py index 4bb73e5..15aedf2 100644 --- a/banzai_floyds/flux.py +++ b/banzai_floyds/flux.py @@ -18,7 +18,6 @@ def do_stage(self, image): flux_standard = get_standard(image.ra, image.dec, self.runtime_context) if flux_standard is None or len(flux_standard) == 0: return image - flux_standard.sort('wavelength') sensitivity = np.zeros_like(image.extracted['wavelength'].data) sensitivity_order = np.zeros_like(sensitivity, dtype=np.uint8) diff --git a/banzai_floyds/frames.py b/banzai_floyds/frames.py index 6c7edef..b7daa21 100644 --- a/banzai_floyds/frames.py +++ b/banzai_floyds/frames.py @@ -1,4 +1,5 @@ -from banzai.lco import LCOObservationFrame, LCOFrameFactory, LCOCalibrationFrame +from banzai.lco import LCOObservationFrame, LCOCalibrationFrame, LCOFrameFactory +from banzai.frames import CalibrationFrame from banzai.data import DataProduct, HeaderOnly, ArrayData, DataTable from banzai_floyds.orders import orders_from_fits from banzai_floyds.utils.wavelength_utils import WavelengthSolution @@ -8,21 +9,23 @@ from banzai_floyds.utils.flux_utils import airmass_extinction from astropy.coordinates import SkyCoord from astropy import units -from banzai.frames import CalibrationFrame +from banzai_floyds.utils.profile_utils import load_profile_fits, profile_fits_to_data +from astropy.table import Table class FLOYDSObservationFrame(LCOObservationFrame): def __init__(self, hdu_list: list, file_path: str, frame_id: int = None, hdu_order: list = None): self.orders = None self._wavelengths = None - self.profile_fits = None + self._profile_fits = None self._background_fits = None - self.wavelength_bins = None - self.binned_data = None + self._binned_data = None self._extracted = None self.fringe = None self._sensitivity = None self._telluric = None + self.background_windows = None + self.extraction_windows = None LCOObservationFrame.__init__(self, hdu_list, file_path, frame_id=frame_id, hdu_order=hdu_order) # Override ra and dec to use the RA and Dec values because the CRVAL keywords don't really # have a lot of meaning when the slitmask is in place @@ -44,8 +47,11 @@ def __init__(self, hdu_list: list, file_path: str, frame_id: int = None, hdu_ord self.orders = orders_from_fits(self['ORDER_COEFFS'].data, self['ORDER_COEFFS'].meta, self.shape) if 'WAVELENGTHS' in self: self.wavelengths = WavelengthSolution.from_header(self['WAVELENGTHS'].meta, self.orders) - if 'PROFILE' in self: - self.profile = self['PROFILE'].data + if 'PROFILEFITS' in self: + self.profile = load_profile_fits(self['PROFILEFITS']) + if 'BINNED2D' in self: + binned_data = Table(self['BINNED2D'].data) + self.binned_data = binned_data.group_by(('order', 'wavelength_bin')) if 'EXTRACTED' in self: self.extracted = self['EXTRACTED'].data if 'FRINGE' in self: @@ -88,17 +94,68 @@ def get_output_data_products(self, runtime_context): else: return super().get_output_data_products(runtime_context) + def save_processing_metadata(self, context): + super().save_processing_metadata(context) + if 'REDUCER' not in self.meta: + self.meta['REDUCER'] = 'BANZAI' + @property def profile(self): return self['PROFILE'].data @profile.setter def profile(self, value): - self.add_or_update(ArrayData(value, name='PROFILE', meta=fits.Header({}))) + centers, sigmas, fitted_points = value + self._profile_fits = centers, sigmas + if fitted_points is None: + fitted_points = Table({'wavelength': [], 'center': [], 'order': []}) + header = fits.Header() + for order, center, sigma in zip([1, 2], centers, sigmas): + for i, coef in enumerate(sigma.coef): + header[f'O{order}SIG{i:02}'] = coef, f'P_{i:02} coefficient for width for order {order}' + for i, coef in enumerate(center.coef): + header[f'O{order}CTR{i:02}'] = coef, f'P_{i:02} coefficient for center for order {order}' + + header[f'O{order}CTRO'] = center.degree(), f'Polynomial Order for the center in order {order}' + header[f'O{order}SIGO'] = sigma.degree(), f'Polynomial Order for the width in order {order}' + + domain_str = '{0} domain value for {1} fit of the profile for order {2}' + header[f'O{order}SIGDM0'] = sigma.domain[0], domain_str.format('Min', 'sigma', order) + header[f'O{order}SIGDM1'] = sigma.domain[1], domain_str.format('Max', 'sigma', order) + header[f'O{order}CTRDM0'] = center.domain[0], domain_str.format('Min', 'center', order) + header[f'O{order}CTRDM1'] = center.domain[1], domain_str.format('Max', 'center', order) + self.add_or_update(DataTable(fitted_points, name='PROFILEFITS', meta=header)) + + profile_hdu = ArrayData(profile_fits_to_data(self.data.shape, centers, sigmas, + self.orders, self.wavelengths.data), + name='PROFILE', meta=fits.Header({})) + self.add_or_update(profile_hdu) if self.binned_data is not None: + profile_centers = np.zeros(len(self.binned_data)) + profile_sigma = np.zeros(len(self.binned_data)) + for order in [1, 2]: + in_order = self.binned_data['order'] == order + profile_centers[in_order] = centers[order - 1](self.binned_data['wavelength'][in_order]) + profile_sigma[in_order] = sigmas[order - 1](self.binned_data['wavelength'][in_order]) + + self.binned_data['y_profile'] = self.binned_data['y_order'] - profile_centers + self.binned_data['profile_sigma'] = profile_sigma x, y = self.binned_data['x'].astype(int), self.binned_data['y'].astype(int) self.binned_data['weights'] = self['PROFILE'].data[y, x] + @property + def profile_fits(self): + return self._profile_fits + + @property + def binned_data(self): + return self._binned_data + + @binned_data.setter + def binned_data(self, value): + self._binned_data = value + self.add_or_update(DataTable(value, name='BINNED2D', meta=fits.Header({}))) + @property def obstype(self): return self.primary_hdu.meta.get('OBSTYPE') diff --git a/banzai_floyds/fringe.py b/banzai_floyds/fringe.py index b9d26a7..2f057fd 100644 --- a/banzai_floyds/fringe.py +++ b/banzai_floyds/fringe.py @@ -5,10 +5,11 @@ from datetime import datetime from scipy.interpolate import CloughTocher2DInterpolator from banzai_floyds.matched_filter import optimize_match_filter - - +from banzai.logs import get_logger import numpy as np +logger = get_logger() + def fringe_weights(theta, x, spline): y_offset = theta @@ -97,13 +98,16 @@ class FringeCorrector(Stage): def do_stage(self, image): # Only divide the fringe out where the divisor is > 0.1 so we don't amplify # artifacts due to the edge of the slit + logger.info('Interpolating super fringe', image=image) fringe_spline = fit_smooth_fringe_spline(image.fringe, image.fringe > 0.1) + logger.info('Fitting fringe offset', image=image) fringe_offset = find_fringe_offset(image, fringe_spline) + logger.info('Correcting for fringing', image=image) x, y = np.meshgrid(np.arange(image.shape[1]), np.arange(image.shape[0])) in_order = image.orders.data == 1 fringe_correction = fringe_spline(np.array([x[in_order], y[in_order] - fringe_offset]).T) to_correct = in_order.copy() - to_correct[in_order] *= fringe_correction > 0.1 + to_correct[in_order] = fringe_correction > 0.1 image.data[to_correct] /= fringe_correction[fringe_correction > 0.1] image.uncertainty[to_correct] /= fringe_correction[fringe_correction > 0.1] image.meta['L1FRNGOF'] = (fringe_offset, 'Fringe offset (pixels)') diff --git a/banzai_floyds/matched_filter.py b/banzai_floyds/matched_filter.py index 0cc5adb..8b8d281 100644 --- a/banzai_floyds/matched_filter.py +++ b/banzai_floyds/matched_filter.py @@ -350,7 +350,7 @@ def optimize_match_filter(initial_guess, data, error, weights_function, x, weigh args=(data, error, weights_function, weights_jacobian_function, weights_hessian_function, x, *args), - method='Powell', bounds=bounds) + method='L-BFGS-B', bounds=bounds) elif weights_hessian_function is None: best_fit = optimize.minimize(lambda *params: sign * matched_filter_metric(*params), initial_guess, args=(data, error, weights_function, weights_jacobian_function, diff --git a/banzai_floyds/orders.py b/banzai_floyds/orders.py index e8fd954..31ea8d4 100644 --- a/banzai_floyds/orders.py +++ b/banzai_floyds/orders.py @@ -404,7 +404,9 @@ class OrderSolver(Stage): ORDER_HEIGHT = 93 CENTER_CUT_WIDTH = 31 POLYNOMIAL_ORDER = 3 - ORDER_REGIONS = [(0, 1700), (630, 1975)] + + ORDER_REGIONS = {'ogg': [(0, 1550), (500, 1835)], + 'coj': [(0, 1600), (615, 1920)]} def do_stage(self, image): if image.orders is None: @@ -422,13 +424,14 @@ def do_stage(self, image): self.ORDER_HEIGHT, order_center, image.data.shape[1] // 2) - good_region = np.logical_and(x >= self.ORDER_REGIONS[i][0], - x <= self.ORDER_REGIONS[i][1]) + order_region = self.ORDER_REGIONS[image.site] + good_region = np.logical_and(x >= order_region[i][0], + x <= order_region[i][1]) initial_model = Legendre.fit(deg=self.POLYNOMIAL_ORDER, x=x[good_region], y=order_locations[good_region], - domain=(self.ORDER_REGIONS[i][0], - self.ORDER_REGIONS[i][1])) + domain=(order_region[i][0], + order_region[i][1])) order_estimates.append((initial_model.coef, self.ORDER_HEIGHT, initial_model.domain)) else: # Load from previous solve diff --git a/banzai_floyds/profile.py b/banzai_floyds/profile.py new file mode 100644 index 0000000..08728c7 --- /dev/null +++ b/banzai_floyds/profile.py @@ -0,0 +1,134 @@ +import numpy as np +from astropy.modeling import fitting, models +from astropy.table import Table, vstack, join +from numpy.polynomial.legendre import Legendre +from banzai_floyds.matched_filter import matched_filter_metric, optimize_match_filter +from banzai_floyds.utils.fitting_utils import fwhm_to_sigma, gauss +from banzai.stages import Stage +from banzai.logs import get_logger + +logger = get_logger() + + +def fit_profile_sigma(data, profile_fits, domains, poly_order=2, default_fwhm=6.0): + # In principle, this should be some big 2d fit where we fit the profile center, the profile width, + # and the background in one go + profile_width_table = {'wavelength': [], 'sigma': [], 'order': []} + fitter = fitting.LMLSQFitter() + for data_to_fit in data.groups: + wavelength_bin = data_to_fit['wavelength_bin'][0] + + # Skip pixels that don't fall into a normal bin + if wavelength_bin == 0: + continue + order_id = data_to_fit['order'][0] + profile_center = profile_fits[order_id - 1](wavelength_bin) + + peak = np.argmin(np.abs(profile_center - data_to_fit['y_order'])) + peak_snr = (data_to_fit['data'][peak] - np.median(data_to_fit['data'])) / data_to_fit['uncertainty'][peak] + # Short circuit if the trace is not significantly brighter than the background in this bin + if peak_snr < 15.0: + continue + + # Only fit the data close to the profile so that we can assume a low order background + peak_window = np.abs(data_to_fit['y_order'] - profile_center) <= 10.0 * fwhm_to_sigma(default_fwhm) + + # Mask out any bad pixels + peak_window = np.logical_and(peak_window, data_to_fit['mask'] == 0) + # Pass a match filter (with correct s/n scaling) with a gaussian with a default width + initial_amplitude = np.max(data_to_fit['data'][peak_window]) + initial_amplitude -= np.median(data_to_fit['data'][peak_window]) + + model = models.Gaussian1D(amplitude=initial_amplitude, mean=profile_center, + stddev=fwhm_to_sigma(default_fwhm)) + model += models.Legendre1D(degree=2, domain=(np.min(data_to_fit['y_order'][peak_window]), + np.max(data_to_fit['y_order'][peak_window])), + c0=np.median(data_to_fit['data'][peak_window])) + # Force the profile center to be on the chip...(add 0.3 to pad the edge) + model.mean_0.min = np.min(data_to_fit['y_order'][peak_window]) + 0.3 + model.mean_0.max = np.max(data_to_fit['y_order'][peak_window]) - 0.3 + + inv_variance = data_to_fit['uncertainty'][peak_window] ** -2.0 + + best_fit_model = fitter(model, x=data_to_fit['y_order'][peak_window], + y=data_to_fit['data'][peak_window], weights=inv_variance, maxiter=400, + acc=1e-4) + best_fit_sigma = best_fit_model.stddev_0.value + + profile_width_table['wavelength'].append(wavelength_bin) + profile_width_table['sigma'].append(best_fit_sigma) + profile_width_table['order'].append(order_id) + profile_width_table = Table(profile_width_table) + # save the polynomial for the profile + profile_widths = [Legendre.fit(order_data['wavelength'], order_data['sigma'], deg=poly_order, domain=domain) + for order_data, domain in zip(profile_width_table.group_by('order').groups, domains)] + return profile_widths, profile_width_table + + +def profile_gauss_fixed_width(params, x, sigma): + center, = params + return gauss(x, center, sigma) + + +def fit_profile_centers(data, domains, polynomial_order=5, profile_fwhm=6): + trace_points = Table({'wavelength': [], 'center': [], 'order': []}) + for data_to_fit in data.groups: + # Skip pixels that don't fall into a normal bin + if data_to_fit['wavelength_bin'][0] == 0: + continue + # Pass a match filter (with correct s/n scaling) with a gaussian with a default width + # Find a rough estimate of the center by running across the whole slit (excluding 1-sigma on each side) + sigma = fwhm_to_sigma(profile_fwhm) + # Remove the edges of the slit because they do funny things numerically + upper_bound = np.max(data_to_fit['y_order']) - sigma + lower_bound = np.min(data_to_fit['y_order']) + sigma + non_edge = np.logical_and(data_to_fit['y_order'] > lower_bound, data_to_fit['y_order'] < upper_bound) + # Run a matched filter (don't fit yet) over all the centers + snrs = [matched_filter_metric([center,], data_to_fit['data'] - np.median(data_to_fit['data']), + data_to_fit['uncertainty'], profile_gauss_fixed_width, None, None, + data_to_fit['y_order'], sigma) + for center in data_to_fit['y_order'][non_edge]] + + # If the peak pixel of the match filter is < 2 times the median (ish) move on + if np.max(snrs) < 2.0 * np.median(snrs): + continue + + initial_guess = (data_to_fit['y_order'][non_edge][np.argmax(snrs)],) + + # Put s/n check first + # stay at least 1 sigma away from the edges using fitting bounds + + best_fit_center, = optimize_match_filter(initial_guess, data_to_fit['data'] - np.median(data_to_fit['data']), + data_to_fit['uncertainty'], + profile_gauss_fixed_width, data_to_fit['y_order'], + args=(fwhm_to_sigma(profile_fwhm),), + bounds=[(lower_bound, upper_bound,),]) + + new_trace_table = Table({'wavelength': [data_to_fit['wavelength_bin'][0]], + 'center': [best_fit_center], + 'order': [data_to_fit['order'][0]]}) + trace_points = vstack([trace_points, new_trace_table]) + + # save the polynomial for the profile + trace_centers = [Legendre.fit(order_data['wavelength'], order_data['center'], + deg=polynomial_order, domain=domain) + for order_data, domain in zip(trace_points.group_by('order').groups, domains)] + + return trace_centers, trace_points + + +class ProfileFitter(Stage): + POLYNOMIAL_ORDER = 5 + + def do_stage(self, image): + logger.info('Fitting profile centers', image=image) + profile_centers, center_fitted_points = fit_profile_centers(image.binned_data, + image.wavelengths.wavelength_domains, + polynomial_order=self.POLYNOMIAL_ORDER) + logger.info('Fitting profile widths', image=image) + profile_widths, width_fitted_points = fit_profile_sigma(image.binned_data, profile_centers, + image.wavelengths.wavelength_domains) + logger.info('Storing profile fits', image=image) + fitted_points = join(center_fitted_points, width_fitted_points, join_type='inner') + image.profile = profile_centers, profile_widths, fitted_points + return image diff --git a/banzai_floyds/settings.py b/banzai_floyds/settings.py index 49ef4e9..befbc34 100644 --- a/banzai_floyds/settings.py +++ b/banzai_floyds/settings.py @@ -5,15 +5,18 @@ 'banzai.trim.Trimmer', 'banzai.gain.GainNormalizer', 'banzai.uncertainty.PoissonInitializer', + 'banzai.cosmic.CosmicRayDetector', 'banzai_floyds.orders.OrderLoader', # Note that we currently don't apply the order tweak, only calculate it and save it in the header 'banzai_floyds.orders.OrderTweaker', 'banzai_floyds.wavelengths.WavelengthSolutionLoader', 'banzai_floyds.fringe.FringeLoader', 'banzai_floyds.fringe.FringeCorrector', - 'banzai_floyds.extract.ProfileFitter', + 'banzai_floyds.binning.Binner', + 'banzai_floyds.profile.ProfileFitter', + 'banzai_floyds.background.BackgroundFitter', 'banzai_floyds.extract.Extractor', - 'banzai_floyds.trim.Trimmer', + # 'banzai_floyds.trim.Trimmer', 'banzai_floyds.flux.StandardLoader', 'banzai_floyds.flux.FluxSensitivity', 'banzai_floyds.flux.FluxCalibrator', @@ -27,6 +30,7 @@ LAST_STAGE = { 'SPECTRUM': None, + 'STANDARD': None, 'LAMPFLAT': 'banzai_floyds.fringe.FringeLoader', 'ARC': 'banzai_floyds.orders.OrderTweaker', 'SKYFLAT': 'banzai.uncertainty.PoissonInitializer' @@ -36,7 +40,7 @@ CALIBRATION_MIN_FRAMES['LAMPFLAT'] = 2 # noqa: F405 CALIBRATION_FILENAME_FUNCTIONS['LAMPFLAT'] = ('banzai_floyds.utils.file_utils.config_to_filename',) # noqa: F405 -EXTRA_STAGES = {'SPECTRUM': None, 'LAMPFLAT': None, +EXTRA_STAGES = {'SPECTRUM': None, 'LAMPFLAT': None, 'STANDARD': None, 'ARC': ['banzai_floyds.wavelengths.CalibrateWavelengths'], 'SKYFLAT': ['banzai_floyds.orders.OrderSolver']} diff --git a/banzai_floyds/tests/data/orders_e2e_fits.dat b/banzai_floyds/tests/data/orders_e2e_fits.dat new file mode 100644 index 0000000..2194f87 --- /dev/null +++ b/banzai_floyds/tests/data/orders_e2e_fits.dat @@ -0,0 +1,77 @@ + { + "ogg": { + "1": { + "coef": [ + 138.1292771919839, + 86.0238134270852, + 45.754903419885224, + -9.956328659887918, + 3.2566132807719836, + -0.9768283235085474], + "domain": [ + 0.0, + 1734.0 + ], + "window": [ + -1.0, + 1.0 + ] + }, + "2": { + "coef": [ + 405.0926503816508, + 10.411074391773528, + 54.206687719045036, + -9.717214543538553, + 1.8543737581971458, + -0.5523398601956832 + ], + "domain": [ + 495.0, + 1896.0 + ], + "window": [ + -1.0, + 1.0 + ] + } + }, + "coj": { + "1": { + "coef": [ + 142.35513147231913, + 69.31029321714496, + 51.706072070738976, + -13.27948331236889, + 4.354609235930751, + -1.3553743246027983 + ], + "domain": [ + 0.0, + 1747.0 + ], + "window": [ + -1.0 + 1.0 + ] + }, + "2}: { + "coef": [ + 410.22292280602596, + 8.244579386132935, + 49.31971294091589, + -7.87168204098573, + 2.0107484907274142, + -0.8701721253811551 + ], + "domain": [ + 601.0, + 1946.0 + ], + "window": [ + -1.0, + 1.0 + ] + } + } + } diff --git a/banzai_floyds/tests/data/wavelength_e2e_fits.dat b/banzai_floyds/tests/data/wavelength_e2e_fits.dat new file mode 100644 index 0000000..0808626 --- /dev/null +++ b/banzai_floyds/tests/data/wavelength_e2e_fits.dat @@ -0,0 +1,11094 @@ +{ + "coj2m002-en12-20200813-0028-a00.fits.fz": { + "1": { + "coef": [ + [ + 7483.226090297141, + 3047.9927674688993, + 15.213723050598093, + -14.698284577602937, + 9.369501930600093, + -3.11077214951269 + ], + [ + 7481.449836047009, + 3056.2160452242924, + 2.2169356687667494, + 3.3254233990700164, + -0.5399214575740886, + 2.711295305423731 + ], + [ + 7484.142786399168, + 3048.0571311786834, + 9.782285737230833, + -7.274697751114739, + 5.935242533117173, + -2.8914737138687916 + ], + [ + 7484.614533932458, + 3047.905490596603, + 7.785907662794728, + -5.107547169236943, + 3.6935017855450205, + -2.093635602836215 + ], + [ + 7485.910988951718, + 3045.973553659719, + 9.508981635823732, + -6.243273551522905, + 3.989990369574219, + -2.4815548433559473 + ], + [ + 7485.988194934678, + 3048.2563326369263, + 6.605107100088659, + -1.3282780055287378, + 0.7240660311519189, + -0.17639751710077603 + ], + [ + 7486.595112476535, + 3047.36905895356, + 7.6475559093885686, + -3.250517029736791, + 1.528011989594392, + -0.6939697480323108 + ], + [ + 7487.234279287229, + 3045.966348256173, + 7.62779264856793, + -5.396973927829345, + 1.7154233813406028, + -2.0829466514281076 + ], + [ + 7487.212931392475, + 3048.865632394431, + 4.963218478270505, + -0.5641507424044743, + -0.6775278508158908, + 0.23463225483093916 + ], + [ + 7487.972529245949, + 3048.1916204820836, + 6.4083998534784925, + -1.6599491319875654, + 0.9667039963940779, + 0.4598899816366468 + ], + [ + 7487.948781424755, + 3050.596311704372, + 4.077067112960345, + 1.9247420807277553, + -0.6860520017753797, + 2.11476402715212 + ], + [ + 7488.928352235324, + 3048.4645782158095, + 6.4435215370032015, + -1.4885670858949211, + 1.0369170577204117, + 0.5852131333999713 + ], + [ + 7489.913276819783, + 3046.4205457709154, + 8.276651845099678, + -4.914012842178618, + 2.413686113348587, + -0.9863013605275627 + ], + [ + 7489.133508293314, + 3050.6046254022053, + 3.05738649044166, + 1.837486245930983, + -1.6645397909910988, + 2.465617859905493 + ], + [ + 7490.306166828827, + 3050.7480157404816, + 5.432330053398082, + 1.8650311984767067, + 0.3333756404857437, + 2.4500446856439897 + ], + [ + 7491.505017703096, + 3046.9623203213846, + 8.126058583550334, + -3.656632442936937, + 2.380270971606461, + -0.08903589458568249 + ], + [ + 7491.533168375761, + 3049.0319466928604, + 5.675236162681295, + 0.21212475501390293, + 0.29006324233753944, + 1.5451035387233074 + ], + [ + 7491.604363946222, + 3049.905611271191, + 3.786894863080464, + 1.633142373533814, + -1.1879786570295556, + 2.0661712247476127 + ], + [ + 7492.271228897501, + 3048.9826677262317, + 4.026347221867978, + 0.07807332290031525, + -1.0867048901872323, + 1.4945102827214203 + ], + [ + 7493.270614357792, + 3048.42055745129, + 6.005312915974951, + -0.660881463786795, + 0.5661026078089435, + 0.8149113335144047 + ], + [ + 7493.608889050168, + 3049.511754202916, + 5.349600389396076, + 0.8398601882597208, + 0.3449600241870513, + 1.4989222702278873 + ], + [ + 7494.259267759703, + 3047.5211960498627, + 6.058070945214462, + -2.291054930087589, + 1.0830213331738279, + -0.17930640449439783 + ], + [ + 7494.8250042229365, + 3047.512451258969, + 6.2295651560926695, + -2.482034281471364, + 0.9880914427323635, + -0.048704725384311545 + ], + [ + 7495.974607016204, + 3045.6420748143405, + 9.01123798484606, + -5.879100602103608, + 3.2924045830949344, + -1.66255508585332 + ], + [ + 7495.707132211234, + 3048.1711824675117, + 5.617745179244595, + -1.4865535991329855, + 0.008485737596485885, + 0.04135791815437931 + ], + [ + 7495.510657202456, + 3050.952047353882, + 2.9347751869897993, + 2.789304950078461, + -2.152760324077343, + 2.164826634935614 + ], + [ + 7496.598941297197, + 3049.4825128241764, + 5.3311087510329465, + 0.16588679082493032, + -0.04200228459855417, + 0.8335918736696087 + ], + [ + 7497.66882103904, + 3045.4993752018554, + 8.177959302635282, + -6.719381754873289, + 2.5597900360449106, + -2.5655534997840417 + ], + [ + 7497.879791676211, + 3047.77810119136, + 6.665674206168052, + -2.861104808416538, + 1.3618965398374039, + -0.40219537782066844 + ], + [ + 7498.419378916629, + 3046.439643745903, + 7.014717729690555, + -4.836646318047578, + 1.803184060347739, + -1.3463338549996973 + ], + [ + 7499.217304699701, + 3044.9283644163847, + 8.664891425770541, + -7.3186434442823325, + 2.803760255463258, + -2.523256303315279 + ], + [ + 7499.913446001352, + 3044.827429463936, + 9.384434754710332, + -6.893737550938864, + 3.494066317502489, + -2.446337303417677 + ], + [ + 7499.417772616237, + 3050.5870583453243, + 5.115877229284494, + 3.049720106506254, + -0.0815765650344478, + 2.479746465827693 + ], + [ + 7500.131601147511, + 3047.9354956007296, + 6.918463935693833, + -1.4233435505597827, + 1.383005095848245, + 0.2051536186204624 + ], + [ + 7500.357241811831, + 3050.306031688894, + 5.4874072663317, + 2.65969507744795, + -0.18150470237740654, + 2.2828151267485155 + ], + [ + 7501.917233013305, + 3046.1055814392794, + 9.96568847343654, + -4.593139471483843, + 3.6427667122947214, + -1.0859778437436114 + ], + [ + 7502.478744208361, + 3045.591643560644, + 10.117025692132465, + -5.58405630550646, + 4.095927455066024, + -1.6792972064687848 + ], + [ + 7502.629769995278, + 3047.643163809291, + 8.094800413116133, + -2.4319143852634078, + 2.486219096368078, + 0.16343043497523388 + ], + [ + 7504.5431145804005, + 3041.68165143182, + 14.552121961056068, + -12.366771344984448, + 7.796383487720348, + -4.623502867819954 + ], + [ + 7504.454773099054, + 3043.3373242627217, + 12.09661656414818, + -9.320566618876878, + 5.419424026960918, + -2.9626493051154807 + ], + [ + 7504.786672283221, + 3043.5682510569227, + 11.91472026185204, + -9.126260928764562, + 5.357740307952625, + -2.775927263806704 + ], + [ + 7506.795533638312, + 3040.3307060474303, + 18.17206991595634, + -14.739215217186548, + 10.582981487234205, + -5.534339622231835 + ], + [ + 7506.971030773997, + 3040.0730677695155, + 16.62578314625096, + -15.459643700916985, + 9.619263841591598, + -6.292572945211822 + ], + [ + 7506.671935490215, + 3045.1438596174494, + 13.074397059004106, + -6.832873087864359, + 6.425160287604319, + -1.6641485478393754 + ], + [ + 7506.141814695079, + 3048.2993858122613, + 7.998394610024042, + -1.5211769180180912, + 2.3464555977778194, + 0.27069830300675374 + ], + [ + 7507.204617964027, + 3046.3818749984353, + 10.504054340799238, + -4.887886683422857, + 4.057206777990238, + -0.5637729164487187 + ], + [ + 7507.273548541759, + 3046.6585755421806, + 8.174927703712541, + -4.2843933687456275, + 2.1806168034844204, + -0.5333648651956278 + ], + [ + 7507.17301625372, + 3049.334534941173, + 5.261631949888201, + 0.5165980393148945, + -0.5242961115666778, + 1.6496036098104836 + ], + [ + 7507.107065553221, + 3051.5333005008943, + 2.6134682238775113, + 3.674792005868021, + -2.7209279488828853, + 3.0716447479416558 + ], + [ + 7508.167582302427, + 3048.8125386323895, + 5.11663612244187, + -1.3130760095968261, + -0.6903492269549859, + 0.5040404005018796 + ], + [ + 7508.161238263007, + 3050.8965579213, + 2.4258750492301817, + 2.301997443262961, + -2.6966428988128843, + 2.5011947374458354 + ], + [ + 7510.274136377772, + 3044.9895845331143, + 9.33763574233025, + -7.108808116919236, + 3.6829625147047507, + -2.6218220893838766 + ], + [ + 7511.003202350243, + 3045.5204298000895, + 9.761712871966854, + -5.750702868871242, + 3.748165465253117, + -1.8099510704898742 + ], + [ + 7510.39829177169, + 3048.2579711047556, + 5.88362436881394, + -1.0617591853046178, + 0.2602486639666976, + 0.7887676760253302 + ], + [ + 7511.431236868287, + 3044.32288777588, + 8.831844926387504, + -7.272617812280599, + 2.418200156261501, + -2.7900950301340743 + ], + [ + 7511.449058594474, + 3046.472203773879, + 7.489414377906833, + -4.0428224437736295, + 1.4249096657767824, + -0.8404742783555942 + ], + [ + 7512.240205946269, + 3045.76095668916, + 8.29031589151616, + -5.211947814764534, + 2.1699060086280944, + -1.6920485266405731 + ], + [ + 7512.088713190839, + 3049.2782481355316, + 5.302824176096504, + 0.5505101335902666, + -0.1653957868675426, + 1.328883972139843 + ], + [ + 7511.99650920888, + 3051.9569580101415, + 2.987158388500994, + 4.892191751239896, + -2.4200187807671574, + 3.7054728021859367 + ], + [ + 7513.22528772542, + 3047.9778331334555, + 6.66952815043438, + -2.085240740845464, + 0.8612271982282906, + 0.2910853876804245 + ], + [ + 7514.253767867969, + 3046.024104645365, + 8.497925694158921, + -5.621580959920273, + 2.7943398377105457, + -1.1804434140362026 + ], + [ + 7514.434537993026, + 3047.8366620437832, + 6.535379585886547, + -2.102889445989734, + 1.1202349775080072, + 0.5963089644605628 + ], + [ + 7514.72627891192, + 3049.203927291274, + 5.645035627630096, + -0.27532467996331017, + 0.04620425304656627, + 0.9612801226700495 + ], + [ + 7513.697015580557, + 3056.1735721680584, + -1.2817947070823825, + 11.627968635770362, + -5.755500145875875, + 7.139346792597354 + ], + [ + 7516.415054006597, + 3047.3831856282363, + 8.870080030176162, + -3.3817073405363063, + 2.718834112698541, + -0.6328872450282942 + ], + [ + 7517.646468768082, + 3043.034170081407, + 11.782001507724413, + -10.150188857231779, + 5.288923615486777, + -4.3120810936217495 + ], + [ + 7517.269648269547, + 3046.6394861437207, + 7.540053170430244, + -3.434523525683559, + 1.5875066316505033, + -0.9942846894699832 + ], + [ + 7518.2512876178525, + 3045.847277273769, + 9.36677001209898, + -4.844849998951411, + 3.5393098201114457, + -1.3739123114649703 + ], + [ + 7518.456825775902, + 3046.9148344887326, + 7.821890377390348, + -2.1721823439739043, + 2.074715502765257, + 0.13455212480038695 + ], + [ + 7518.73618604264, + 3048.435429708302, + 7.221643232430497, + -0.2858968955101351, + 1.6756388066255126, + 1.0539225591062236 + ], + [ + 7519.101595515573, + 3047.6535812473307, + 6.7304570586700665, + -1.8421925746741847, + 1.410222058996792, + 0.39575461793997047 + ], + [ + 7519.402335459068, + 3047.9915777903593, + 5.915629658110184, + -0.9815089744336966, + 0.7004615611627042, + 0.43398662260830306 + ], + [ + 7520.425907473448, + 3046.4176097223617, + 8.274240261566593, + -3.874450847333881, + 2.6638724544223, + -0.7293998569857477 + ], + [ + 7520.747708303543, + 3047.094679028969, + 6.689824358711485, + -2.014232199447946, + 0.8506546853171796, + -0.1401414691177653 + ], + [ + 7521.293163397279, + 3047.269746755875, + 7.632027308191747, + -2.461447517996748, + 1.6221039872236362, + 0.004257377348742408 + ], + [ + 7522.287790289887, + 3045.0305203547296, + 10.101357739243879, + -6.359148403530443, + 3.8224252201343916, + -2.0019739945458466 + ], + [ + 7522.470898623616, + 3046.473391178286, + 8.37670696181497, + -4.1205890715696905, + 2.6505711547686914, + -1.5658090492495134 + ], + [ + 7522.398255129857, + 3049.528395333277, + 5.049523961257388, + 1.5118800697764387, + -0.23013411031855313, + 1.68212159141775 + ], + [ + 7524.221246140868, + 3042.6262566900145, + 11.437372677029643, + -9.99527995221195, + 5.238210031139077, + -4.266653215000204 + ], + [ + 7524.024951945034, + 3045.9020027772635, + 8.445105566652257, + -4.2850207608913315, + 2.3530689647263063, + -0.8747951365470409 + ], + [ + 7524.871260031312, + 3044.4049690017573, + 10.236222884858115, + -7.025750198843946, + 3.7994180345898276, + -2.703510068307075 + ], + [ + 7525.276320199794, + 3045.874968587358, + 9.166021808306555, + -4.278549623435878, + 2.9841635433277425, + -1.1720362802355473 + ], + [ + 7526.613802104063, + 3041.0536008014824, + 13.545501848001418, + -12.295282943946594, + 6.618070805636894, + -5.646186036928386 + ], + [ + 7527.308620503365, + 3041.8425497793933, + 14.368829065766526, + -11.3832117126765, + 7.341347086040132, + -4.4779805135947885 + ], + [ + 7528.561138336752, + 3035.429546514257, + 18.067904624665974, + -21.651346789177303, + 10.711485503374988, + -9.540680206731365 + ], + [ + 7528.276392192287, + 3041.919306410263, + 13.40272967478506, + -10.692528355316558, + 6.497404834927304, + -4.214585955765263 + ], + [ + 7528.788240341985, + 3039.8736800168735, + 15.058862348920224, + -14.880448260527851, + 8.26635816948335, + -5.819775218351129 + ], + [ + 7528.9621523126125, + 3042.010749574964, + 13.005530654079763, + -11.512463933161502, + 6.276065702562096, + -3.8036354954923604 + ], + [ + 7529.4465185278705, + 3042.54440826792, + 13.103038720903163, + -10.664965245725192, + 6.463858263755095, + -3.5259434043452975 + ], + [ + 7527.468035055211, + 3054.4228527365535, + 1.5728436269490096, + 9.445514591712707, + -3.2891722683859403, + 6.2730457808449325 + ], + [ + 7529.9327688582225, + 3045.476634275447, + 10.849893116596466, + -6.008277192556906, + 4.676977239353134, + -1.2703554883507928 + ], + [ + 7531.602111790175, + 3040.7397463910597, + 14.55378857555745, + -12.759119996091771, + 7.620579109892822, + -5.199785937495895 + ], + [ + 7532.289545654394, + 3039.6595171548183, + 13.906014300520978, + -12.552652187559701, + 6.8187932796662185, + -5.670329026351149 + ], + [ + 7531.37528034023, + 3046.746948696965, + 6.919076221653157, + 0.8246841940627432, + 0.5711878522293434, + 0.9924481050062911 + ], + [ + 7532.496633174121, + 3042.53786522396, + 9.72904762046192, + -5.47893859370107, + 3.0453777048494035, + -2.4193570846464203 + ] + ], + "domain": [ + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ] + ], + "window": [ + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ] + ] + }, + "2": { + "coef": [ + [ + 4498.191508763917, + 1161.1738726663098, + 4.497228526072078 + ], + [ + 4498.274691200699, + 1160.855831662314, + 4.569637564745374 + ], + [ + 4498.297683756848, + 1160.6406888134723, + 5.146229916133284 + ], + [ + 4498.461362307409, + 1159.8682436087213, + 6.239719363792358 + ], + [ + 4498.838094125356, + 1159.7496052649167, + 6.418141668299431 + ], + [ + 4499.074311233285, + 1159.5142703596944, + 6.724434736041604 + ], + [ + 4499.301611346766, + 1159.5520586119999, + 6.564653759405311 + ], + [ + 4499.539597746935, + 1159.6588953662058, + 6.510365202795439 + ], + [ + 4499.836502204756, + 1159.661216016733, + 6.5223386181388125 + ], + [ + 4500.144831246003, + 1159.7289744180953, + 6.549563542071501 + ], + [ + 4500.376607352042, + 1159.7060129474473, + 6.553499991764435 + ], + [ + 4500.614608359847, + 1159.7428188905471, + 6.436592995314451 + ], + [ + 4500.767109227782, + 1159.4533032946408, + 6.86712461587711 + ], + [ + 4501.134004104326, + 1159.562935108903, + 6.705223767285304 + ], + [ + 4501.296137558789, + 1159.327499614647, + 6.8117569672957945 + ], + [ + 4501.5547601029375, + 1159.5054706363785, + 6.618464592209478 + ], + [ + 4501.775355117672, + 1159.508956068677, + 6.824071343898055 + ], + [ + 4502.100252299853, + 1159.4626091675275, + 6.719620886888674 + ], + [ + 4502.404361794952, + 1159.6015742718105, + 6.6554835561596235 + ], + [ + 4502.617385137216, + 1159.5035708978317, + 6.754610969822795 + ], + [ + 4502.863896021259, + 1159.456039178168, + 6.658808315017913 + ], + [ + 4503.13345852628, + 1159.5926957054107, + 6.478510489050973 + ], + [ + 4503.328733130283, + 1159.6648430506739, + 6.46962424794225 + ], + [ + 4503.508810078185, + 1159.331243727938, + 6.820286242274206 + ], + [ + 4503.866562903549, + 1159.333309375101, + 6.771448639722991 + ], + [ + 4503.951498605922, + 1159.6377382105732, + 6.498949265514406 + ], + [ + 4504.167467065126, + 1159.6482104657202, + 6.569840872503624 + ], + [ + 4504.416691419672, + 1159.4404304791224, + 6.6631766039419285 + ], + [ + 4504.773133839337, + 1159.2582500300382, + 7.010984329372441 + ], + [ + 4504.995484097674, + 1159.169802014045, + 6.977626001721084 + ], + [ + 4505.237616747912, + 1159.31570544543, + 6.651795269104915 + ], + [ + 4505.5352470831085, + 1159.365259133961, + 6.8210929293703435 + ], + [ + 4505.7798052571625, + 1159.2639957176211, + 6.923105689999185 + ], + [ + 4506.170342704713, + 1159.1402030675542, + 7.192648730937024 + ], + [ + 4506.3320393447775, + 1159.2894781695973, + 6.900210824036815 + ], + [ + 4506.653382049193, + 1159.1331753104312, + 7.02518923417263 + ], + [ + 4506.775675038451, + 1159.5030938028665, + 6.609731468307895 + ], + [ + 4507.073624891993, + 1159.4725543101326, + 6.757181277121898 + ], + [ + 4507.273682404726, + 1159.5425509490117, + 6.928162348833648 + ], + [ + 4507.677959685207, + 1159.338948310305, + 6.953547776713443 + ], + [ + 4507.757266663282, + 1159.3978371016667, + 7.272088370341674 + ], + [ + 4508.100481189526, + 1159.427371477916, + 7.029444225581165 + ], + [ + 4508.346430947495, + 1159.4655582239043, + 6.896043342466906 + ], + [ + 4508.556983377711, + 1159.250566184331, + 6.978836988889463 + ], + [ + 4508.938456925277, + 1159.338969211268, + 6.93522606922926 + ], + [ + 4509.2292736729505, + 1159.433343235684, + 7.060807212255563 + ], + [ + 4509.47081248074, + 1159.3592682445078, + 7.010541520330526 + ], + [ + 4509.578227408464, + 1159.5168344535882, + 6.786435048157367 + ], + [ + 4509.828184986004, + 1159.2718602098187, + 6.936985051919787 + ], + [ + 4510.18883332527, + 1159.30999984133, + 6.743432050382825 + ], + [ + 4510.311124898688, + 1159.3025214181662, + 6.785451463225162 + ], + [ + 4510.563262754889, + 1159.1245046956772, + 7.050697813669487 + ], + [ + 4510.777508480566, + 1159.3238425317013, + 6.97154524710484 + ], + [ + 4511.067566344706, + 1159.15142252661, + 6.971625478322107 + ], + [ + 4511.298575459588, + 1159.1888406510343, + 6.983064346548899 + ], + [ + 4511.613566819161, + 1159.2226544509565, + 6.979353468984324 + ], + [ + 4511.769164633099, + 1159.3016213827027, + 6.956453750140025 + ], + [ + 4512.05992171116, + 1159.0076489807198, + 7.5717960287014705 + ], + [ + 4512.4377769433895, + 1159.1852103171136, + 7.051103640210929 + ], + [ + 4512.551192308578, + 1159.483018720599, + 6.626928145201262 + ], + [ + 4512.737557242725, + 1159.35722303502, + 6.859673294996025 + ], + [ + 4513.020205357353, + 1159.1394878739936, + 7.009184526854807 + ], + [ + 4513.355431275058, + 1159.412771796002, + 6.799940024597151 + ], + [ + 4513.518736957637, + 1159.0626337921196, + 7.143740549401281 + ], + [ + 4513.779096023656, + 1159.2309529119855, + 6.897632620509175 + ], + [ + 4513.9383233937215, + 1159.1751989400182, + 7.196777486181238 + ], + [ + 4514.389590626567, + 1159.2900376250784, + 7.067731073442237 + ], + [ + 4514.588819853776, + 1159.3126147667945, + 7.061375057951236 + ], + [ + 4514.878628876835, + 1159.3380900427712, + 7.184632993230706 + ], + [ + 4515.143225240618, + 1159.214207962248, + 7.0081433601060965 + ], + [ + 4515.47311770119, + 1159.3885177970021, + 6.748769271808325 + ], + [ + 4515.663096551119, + 1159.303449297887, + 7.16895962266274 + ], + [ + 4515.868405679659, + 1159.2074574354776, + 7.478805747272371 + ], + [ + 4516.090687575145, + 1159.1823479839672, + 7.4509437733345445 + ], + [ + 4516.373605123107, + 1159.1871099485993, + 7.359049506946094 + ], + [ + 4516.69974964144, + 1159.1813784596825, + 7.3057741820235895 + ], + [ + 4516.764290915209, + 1159.5153500957442, + 6.922708078397673 + ], + [ + 4517.064408471298, + 1159.3053229186721, + 7.228547529624697 + ], + [ + 4517.342649023631, + 1159.328685619748, + 7.276611567078326 + ], + [ + 4517.600574202544, + 1159.3548841984953, + 7.1224072781242995 + ], + [ + 4517.907262718296, + 1159.4939259451082, + 6.65966201888109 + ], + [ + 4518.188007365182, + 1159.320002161707, + 6.996989121179166 + ], + [ + 4518.384763681578, + 1159.5122723441837, + 6.578958497915455 + ], + [ + 4518.724336583268, + 1159.1403465506205, + 7.035019738000983 + ], + [ + 4518.92002288363, + 1159.4481447858007, + 6.761836581975735 + ], + [ + 4519.170515001363, + 1159.335157715903, + 6.975401440064845 + ], + [ + 4519.361609268274, + 1159.4616475435478, + 6.86098445373257 + ], + [ + 4519.660579150306, + 1159.2050397966939, + 7.029601460452997 + ], + [ + 4519.923473736496, + 1159.3037370665058, + 7.001531660071858 + ], + [ + 4519.939850366689, + 1159.5355904272637, + 7.211416611313024 + ], + [ + 4520.0577819946275, + 1159.6146469313696, + 7.126475728428721 + ], + [ + 4520.296724924965, + 1159.8494055343247, + 7.007967831343464 + ], + [ + 4520.829087757831, + 1159.3391077433764, + 7.328482124587609 + ], + [ + 4521.243200280299, + 1158.845770619084, + 7.667824146295837 + ], + [ + 4521.415756148813, + 1158.3803734836597, + 7.9404861654655114 + ] + ], + "domain": [ + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ] + ], + "window": [ + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ] + ] + } + }, + "coj2m002-en12-20200813-0015-a00.fits.fz": { + "1": { + "coef": [ + [ + 7475.997071988249, + 3054.5266842533088, + 7.033157094067572, + -5.230446223547718, + 1.2074767675017337, + 2.666628320147796 + ], + [ + 7476.053175670135, + 3055.23433580515, + 2.1178237683883308, + -0.07730454590718122, + -0.789257899200121, + 1.6639646490486832 + ], + [ + 7476.1587024686605, + 3056.875761420227, + -2.256260929926182, + 5.820912823907005, + -3.8869566031306992, + 2.5462805314533385 + ], + [ + 7477.824655318145, + 3053.792900042688, + 0.6994481134427102, + 3.7030187706522533, + -1.3155416716768904, + 1.4719286951055768 + ], + [ + 7479.116836591741, + 3051.527605865024, + 2.5248228619122455, + 2.1679961036138518, + -1.1134996981126348, + 1.5793135850662086 + ], + [ + 7480.417814370638, + 3049.46829282334, + 5.207742257142432, + -0.07126019419272205, + -0.0017266079208403116, + 0.4596151875780797 + ], + [ + 7480.646755452321, + 3050.0665153963046, + 3.5227891190191607, + 1.8288735251992214, + -1.670713723896785, + 1.2100743368644513 + ], + [ + 7481.71226967099, + 3047.804919442252, + 6.577622598342886, + -2.159049290648062, + 0.6326076393036635, + -0.07911087749997096 + ], + [ + 7482.107372607775, + 3048.8187947955844, + 5.432336687431347, + -0.24532189319236408, + -0.2712908660972238, + 0.7477883909802533 + ], + [ + 7482.550842070388, + 3049.0046219810683, + 5.047406254587254, + 0.054935572396561175, + -0.38517187170578066, + 1.1221258290339382 + ], + [ + 7483.604573930078, + 3047.2435915553965, + 8.304569964311117, + -3.1988063053022215, + 2.6644504471929333, + 0.04596665907479577 + ], + [ + 7484.878809078626, + 3044.463356216555, + 11.521177002081053, + -8.04244431798843, + 5.454555649791439, + -2.382036526138313 + ], + [ + 7484.802001018032, + 3046.6354796283986, + 8.601272603230582, + -4.321793862329962, + 2.5552899460042324, + -0.819230112180818 + ], + [ + 7483.967652083919, + 3051.114951282844, + 2.382289999737821, + 3.261871446910059, + -2.5929371648597463, + 2.8051114066224687 + ], + [ + 7485.7847658909595, + 3046.5935513248373, + 8.503292592850356, + -4.544942736175076, + 2.6326112147907663, + -0.6713915404159079 + ], + [ + 7486.882021524846, + 3044.881982125697, + 10.691915798878616, + -7.149415450164382, + 4.640055514377094, + -1.9660979589675682 + ], + [ + 7486.605683199088, + 3047.4529767475688, + 7.2990270422407235, + -2.4238421011983853, + 1.659280537208625, + 0.8240102514423365 + ], + [ + 7487.644301408231, + 3045.8045197306333, + 9.226610600497064, + -4.848108069525929, + 3.20313634065482, + -1.0009999799091498 + ], + [ + 7488.61511118959, + 3044.8524844261633, + 11.03783485313617, + -6.949452505783412, + 4.721591130327715, + -1.7792808777692553 + ], + [ + 7488.528781229079, + 3046.764921270489, + 8.121650317628433, + -3.5217681073859484, + 2.317611382470609, + -0.20248005586391918 + ], + [ + 7489.08871495699, + 3046.0634174242086, + 8.347787176274643, + -4.254107033842336, + 2.787182171187513, + -0.90986919062248 + ], + [ + 7489.472331353912, + 3046.5707566418846, + 7.263675539447483, + -3.415831539764134, + 1.9831667426178365, + -0.7383338192546355 + ], + [ + 7490.124390604412, + 3046.2934340638058, + 8.406114426471667, + -4.172020504776736, + 2.613706836689251, + -0.9530732748135358 + ], + [ + 7489.977529245715, + 3048.274396719321, + 5.89654542786061, + -0.8899318778304767, + 0.6437239731811983, + 0.8786475491340382 + ], + [ + 7490.610687915226, + 3047.7787668457336, + 5.871131553183164, + -1.8872018090538238, + 0.39625577193327, + 0.20647292476148718 + ], + [ + 7490.726878360077, + 3049.1974382871817, + 4.428802008146978, + 0.08247785597059959, + -0.9747886796173564, + 1.3497464080368105 + ], + [ + 7491.676147896285, + 3048.2109592938805, + 6.629922972726731, + -2.169899340582154, + 0.707556977231376, + 0.08738826465226819 + ], + [ + 7492.177941628964, + 3047.532477411016, + 6.616463805104378, + -3.2349539722309526, + 1.0389317272912006, + -0.40174599969170605 + ], + [ + 7492.480342728355, + 3048.383453887819, + 5.809347930278368, + -1.6832437450435747, + 0.30578445339614085, + 0.20480450434697986 + ], + [ + 7492.388551241639, + 3050.8484718122463, + 3.357085020472894, + 2.467591453347571, + -1.6533910142934958, + 2.5959910780444697 + ], + [ + 7492.626144790319, + 3051.5813893336403, + 1.7465220218839261, + 4.046442678029305, + -3.0387272836521344, + 2.876151047424116 + ], + [ + 7494.4240012791815, + 3046.5035211889863, + 8.206403800567909, + -4.845092913204364, + 2.230234478862771, + -1.1138062266722895 + ], + [ + 7494.426766701249, + 3048.5941251647314, + 6.052805304115574, + -0.5272601553055497, + 0.31836857821734305, + 1.0275543937773148 + ], + [ + 7495.060470448851, + 3047.176776188148, + 7.184626755131302, + -2.2425078501176907, + 1.3793359074855676, + 0.10637204567106145 + ], + [ + 7495.357089225952, + 3048.357639126455, + 6.090057536033528, + -0.45342755834796145, + 0.543207766081913, + 0.7421506651087678 + ], + [ + 7497.183620251822, + 3043.6846486443933, + 12.20534540643574, + -8.81364124391228, + 5.351108409769113, + -2.92462270409513 + ], + [ + 7497.132066510876, + 3046.0216124545773, + 9.447010323781862, + -4.722080768250091, + 2.9698210320343534, + -0.9538316766103625 + ], + [ + 7498.4341852722055, + 3043.144520132599, + 12.55027517709963, + -9.698709791254656, + 6.252607906833815, + -3.764920913828743 + ], + [ + 7498.9189594922145, + 3043.6931950945905, + 12.389273915680423, + -8.97989645136579, + 5.8902815402732, + -3.2035737657381955 + ], + [ + 7498.9719584039885, + 3044.5894988051964, + 11.220296138583583, + -7.27341897842259, + 5.082355402536874, + -2.0278906300525823 + ], + [ + 7499.829551199078, + 3043.5619367390727, + 12.752642895862268, + -9.00180116385165, + 6.02845886935758, + -2.505353656665382 + ], + [ + 7500.116124158527, + 3045.044152872536, + 11.357477828556359, + -6.4357239400589075, + 4.872191337145726, + -1.5374729500641717 + ], + [ + 7500.733593382496, + 3044.2809717685072, + 11.840204944944686, + -8.114323076554765, + 5.046031607173724, + -2.1892577342268527 + ], + [ + 7500.149249387498, + 3047.7733487250102, + 7.120497482638699, + -2.337186325520727, + 1.2781570653937127, + 0.5731492639099333 + ], + [ + 7501.104608576409, + 3046.220640608491, + 9.141311026323132, + -4.992227092326495, + 3.147943765144653, + -0.9165043742643938 + ], + [ + 7500.692878620373, + 3049.702407111847, + 4.924828271181373, + 0.9738377075196818, + -0.5957388208293217, + 2.5871645769091103 + ], + [ + 7501.523962736621, + 3048.737603708811, + 6.175752522953554, + -0.5074279047971405, + 0.4466368242829578, + 1.8534890914518884 + ], + [ + 7501.375181485209, + 3051.0976895650897, + 2.8112031889071467, + 3.4156282838645957, + -2.584410845590229, + 3.5082547860362316 + ], + [ + 7501.851455863779, + 3051.4208445422173, + 2.4379852217580478, + 3.716539986416157, + -2.871548338507792, + 3.4465340717161 + ], + [ + 7502.76324248233, + 3049.82780125551, + 4.231363145398507, + 0.8492740054309187, + -1.311080476974402, + 2.0684075777791153 + ], + [ + 7504.007076491666, + 3047.4774008936183, + 7.3942572885054725, + -3.3576407049302373, + 1.6820363566127188, + -0.05347852292214297 + ], + [ + 7504.035487009487, + 3049.0978809233225, + 5.098399113358292, + -0.31210410610649447, + 0.2699959792133852, + 1.3293800886677367 + ], + [ + 7505.138371786424, + 3047.4458666336777, + 7.149490750199833, + -2.642140365755321, + 1.7912840371829504, + -0.08168278348890759 + ], + [ + 7505.492943908333, + 3047.581042745342, + 6.453629686454949, + -2.0525451371759416, + 0.7824725931394688, + 0.2601047242160148 + ], + [ + 7505.722347757273, + 3048.2693671300935, + 5.621392461380748, + -0.49851109166561874, + -0.4700410069267218, + 0.43700679069063114 + ], + [ + 7505.711611791268, + 3048.7517015245844, + 4.599929589728527, + 0.21156118221028436, + -1.5014827874884111, + 0.8824160949605854 + ], + [ + 7506.582509731816, + 3048.0404125130594, + 6.182664287219549, + -1.150712650388818, + 0.030099341574919752, + 0.4867300641392468 + ], + [ + 7507.058810281736, + 3047.396004704359, + 6.5856977825165295, + -2.146886650663985, + 0.8239668801753173, + 0.021191154049023175 + ], + [ + 7507.629387691336, + 3047.347741837465, + 7.143227781156447, + -2.365666594195661, + 1.178094206866869, + -0.10463038126581525 + ], + [ + 7507.948675745836, + 3047.8396369082993, + 6.377510932549141, + -2.085724223016974, + 0.6312192835969781, + 0.08429286195936835 + ], + [ + 7508.649339034654, + 3047.819855832594, + 6.573529479072188, + -1.9659008438968055, + 0.9119671304988755, + 0.3794168013130695 + ], + [ + 7508.770863806544, + 3049.1489337698054, + 4.492432518306138, + 0.7272145482610897, + -0.8948683301555976, + 1.92101443726243 + ], + [ + 7510.094098437513, + 3046.372791305837, + 8.286006644638363, + -4.610164161573504, + 2.209306733832063, + -0.972103320343778 + ], + [ + 7510.397026632449, + 3047.0092681612846, + 7.508025397774807, + -3.5462458126902754, + 1.5830807778034994, + -0.34364668297783074 + ], + [ + 7511.278435681812, + 3046.2146835897133, + 9.159301977411891, + -4.949462696867274, + 3.0923155831044054, + -1.1222455642336049 + ], + [ + 7512.165400861701, + 3044.517862590901, + 10.376918486427726, + -7.416974532046755, + 4.047964568262174, + -2.8221545088306614 + ], + [ + 7512.181322184698, + 3046.503646492893, + 8.116151542717972, + -3.636466237853551, + 2.2085637729524836, + -0.8537595870971149 + ], + [ + 7513.227059710655, + 3045.0448790091546, + 10.417477327530227, + -6.007652560020762, + 4.230795622133287, + -2.174106874934858 + ], + [ + 7513.4040115100515, + 3046.2303121065597, + 8.716335393879666, + -3.492547760662743, + 2.502872237243888, + -0.6318435208377352 + ], + [ + 7513.870848020697, + 3046.422973357576, + 8.336185197759972, + -3.3422930665945816, + 2.283822678789815, + -0.23277856061216246 + ], + [ + 7513.86173066588, + 3047.707963686727, + 6.574150238116042, + -1.6069616205809416, + 1.0515429271796228, + 0.38032771431331736 + ], + [ + 7514.261166585874, + 3047.554450458783, + 6.382250382496518, + -1.6955954454657247, + 0.8549436055227232, + -0.13038164186432005 + ], + [ + 7514.36837155594, + 3049.624262814051, + 4.508687925139831, + 1.7566877214283796, + -0.6589444285713458, + 1.8389989693298703 + ], + [ + 7515.830840061194, + 3045.79191533788, + 8.927644458242671, + -4.558807387684588, + 2.9590691938694995, + -1.2540887841442832 + ], + [ + 7516.460710965519, + 3045.92140454498, + 9.533511162274436, + -4.520142922756202, + 3.0502973021716437, + -1.0023422522875993 + ], + [ + 7516.517338681092, + 3046.7516528179935, + 7.554539625521974, + -3.030127343850277, + 1.3553968926822177, + -0.5374782617157912 + ], + [ + 7517.099612329422, + 3046.859508009554, + 7.8425736451133075, + -3.2300028457400267, + 1.752459990279503, + -0.4565436597187479 + ], + [ + 7517.397609406928, + 3047.858006773109, + 6.332874292844171, + -1.3290388330196146, + 0.7023805063897078, + 0.37753535390120296 + ], + [ + 7518.627463572369, + 3045.192480521573, + 9.691165235562009, + -5.79684055016334, + 3.4957647893350527, + -1.9257655359591908 + ], + [ + 7518.700638138364, + 3046.664478011208, + 7.930814217421499, + -2.979714213860388, + 1.75460509524512, + -0.2006464059492639 + ], + [ + 7520.065933223463, + 3043.767606017513, + 12.21869044193231, + -8.221572067462699, + 5.275584231390237, + -2.794200457963233 + ], + [ + 7520.065206350221, + 3045.7972506001374, + 9.308385746093974, + -4.083438537324933, + 3.260492261651881, + -0.872138560839271 + ], + [ + 7519.754487914907, + 3048.323348270645, + 5.655843749286233, + 0.7501809184708412, + -0.16098271592861907, + 1.4481575887910096 + ], + [ + 7522.060899011089, + 3041.737575467241, + 14.518634204306768, + -11.092972731186611, + 7.533569193072299, + -4.672520563159304 + ], + [ + 7521.978365851759, + 3044.008229171197, + 11.369626881339443, + -6.985051228796397, + 4.755323752829993, + -2.145923275145726 + ], + [ + 7522.559233705733, + 3043.364236031124, + 11.812090475656271, + -8.139843175500614, + 5.0916418927760265, + -2.480658822386386 + ], + [ + 7523.127501456421, + 3042.7925955896153, + 12.538125127584596, + -9.463544628656226, + 5.7172521094051785, + -3.2183462082509893 + ], + [ + 7523.35662146426, + 3044.2290811534, + 11.509750048435968, + -7.381672310848836, + 4.837370381263304, + -1.759481457388449 + ], + [ + 7523.855898142819, + 3044.6728897552007, + 11.138817640549485, + -6.772647578733399, + 4.509438395700174, + -1.2804665112380724 + ], + [ + 7524.951207584597, + 3042.5004120049152, + 14.519182703639165, + -11.216089986765839, + 7.714019624698611, + -3.5269957359942596 + ], + [ + 7524.913533757775, + 3044.109476369167, + 12.029932988210685, + -8.25274327422792, + 5.759755506681266, + -2.1279582119641844 + ], + [ + 7525.903119218235, + 3042.717062557848, + 13.481325656415803, + -9.832334857009045, + 6.656409056782396, + -3.2344504028612793 + ], + [ + 7527.19432228696, + 3039.7439305587227, + 15.654790789135822, + -13.329310523724871, + 8.30691865100002, + -5.5758460726924275 + ], + [ + 7525.75534799397, + 3047.2764762464876, + 5.088474866700532, + 1.7652801060418633, + -0.8415538751784298, + 2.025854858031047 + ], + [ + 7526.382701254405, + 3046.08954484368, + 5.454027763389522, + 0.9037813043360227, + -0.7378573252930374, + 1.2775494129945597 + ] + ], + "domain": [ + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ], + [ + 0.0, + 1747.0 + ] + ], + "window": [ + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ] + ] + }, + "2": { + "coef": [ + [ + 4495.684268819373, + 1160.85645536392, + 4.669461899676833 + ], + [ + 4495.7145905246725, + 1160.899038155606, + 4.686187288196229 + ], + [ + 4495.857590664473, + 1160.4916048181185, + 5.0726345276486775 + ], + [ + 4496.007435915184, + 1160.3497406138988, + 5.2774639864962065 + ], + [ + 4496.284597643993, + 1159.9018158442543, + 5.9690840385090835 + ], + [ + 4496.511796877051, + 1159.6507151885085, + 6.371591852270039 + ], + [ + 4496.7013210586265, + 1159.6658778248354, + 6.426911261767424 + ], + [ + 4496.985850403768, + 1159.589139729585, + 6.538906493417679 + ], + [ + 4497.24614088871, + 1159.641651164001, + 6.4991043993062885 + ], + [ + 4497.500711564829, + 1159.642321988003, + 6.591360322214559 + ], + [ + 4497.73694618844, + 1159.6426086398394, + 6.563681232842859 + ], + [ + 4497.9960524952885, + 1159.5657617601644, + 6.729636693528231 + ], + [ + 4498.232356423074, + 1159.712440149833, + 6.6069715878801585 + ], + [ + 4498.481090259488, + 1159.605942867873, + 6.7543871005669445 + ], + [ + 4498.7009756925045, + 1159.6961333162274, + 6.608288929030031 + ], + [ + 4498.9861588698495, + 1159.6030862070857, + 6.6802526396131965 + ], + [ + 4499.208971850472, + 1159.558538970687, + 6.689227394124673 + ], + [ + 4499.446575401234, + 1159.6983886503158, + 6.632615442897459 + ], + [ + 4499.770637355447, + 1159.695172842858, + 6.608570083221195 + ], + [ + 4500.049834488148, + 1159.5655592885355, + 6.714372224008427 + ], + [ + 4500.272886282818, + 1159.5985168535587, + 6.709000610784842 + ], + [ + 4500.474206877329, + 1159.5783884089929, + 6.636781710294714 + ], + [ + 4500.727553554859, + 1159.6990026799276, + 6.483387130196654 + ], + [ + 4501.029171774029, + 1159.5208115217658, + 6.709212802341781 + ], + [ + 4501.221203810738, + 1159.5228685826362, + 6.769067884060206 + ], + [ + 4501.441007907356, + 1159.568157907286, + 6.681908875472778 + ], + [ + 4501.693321918522, + 1159.551220651166, + 6.606835504094737 + ], + [ + 4501.941737301514, + 1159.5750224995554, + 6.509064367466886 + ], + [ + 4502.139458890173, + 1159.5654761937571, + 6.596590215477621 + ], + [ + 4502.432527592562, + 1159.4570425136312, + 6.666315678863506 + ], + [ + 4502.663227370524, + 1159.464704236273, + 6.714318315114064 + ], + [ + 4502.943948705022, + 1159.4309112482347, + 6.747561016298924 + ], + [ + 4503.208550641462, + 1159.5297733839298, + 6.641892830185157 + ], + [ + 4503.469100470442, + 1159.4946868362397, + 6.711670526806663 + ], + [ + 4503.710181478606, + 1159.478270741012, + 6.683670377284117 + ], + [ + 4503.9692298893, + 1159.5254899591878, + 6.67955253746699 + ], + [ + 4504.208505145998, + 1159.411330859147, + 6.7535382186208786 + ], + [ + 4504.504830548232, + 1159.4573331018419, + 6.8324472832689445 + ], + [ + 4504.780872298886, + 1159.584451406811, + 6.697794307251958 + ], + [ + 4505.0430943549745, + 1159.3370486335214, + 6.889607961180611 + ], + [ + 4505.232890665285, + 1159.4897166985797, + 6.84231918043532 + ], + [ + 4505.464923717081, + 1159.5024678246125, + 6.967357996841936 + ], + [ + 4505.72437269378, + 1159.493429081841, + 6.817126372521673 + ], + [ + 4506.02824078468, + 1159.5439334055081, + 6.616558697996388 + ], + [ + 4506.292752454349, + 1159.3813019875256, + 6.872619282224619 + ], + [ + 4506.555908518462, + 1159.4403721672961, + 6.912559264361245 + ], + [ + 4506.7909843215575, + 1159.5077335609003, + 6.783651242356873 + ], + [ + 4507.013552758811, + 1159.5183526172973, + 6.828792223516528 + ], + [ + 4507.297259455092, + 1159.4917573357868, + 6.704467161158574 + ], + [ + 4507.551015999677, + 1159.4459888093284, + 6.798723393946558 + ], + [ + 4507.809564530822, + 1159.439143589771, + 6.863769276724065 + ], + [ + 4508.006767057143, + 1159.432541438739, + 6.830448946320226 + ], + [ + 4508.268420296922, + 1159.4198103534914, + 6.854412357105114 + ], + [ + 4508.5494218862095, + 1159.4433630147962, + 6.810645021851658 + ], + [ + 4508.7662709297265, + 1159.498104827222, + 6.880019271057525 + ], + [ + 4509.017790484317, + 1159.3972227471177, + 7.042225278623781 + ], + [ + 4509.266909514648, + 1159.465709773885, + 6.961276014510532 + ], + [ + 4509.5204925006365, + 1159.393179681542, + 7.030602937865991 + ], + [ + 4509.7360738768875, + 1159.4913478385758, + 6.929827404128742 + ], + [ + 4509.992348757754, + 1159.4646944005792, + 6.944745903749134 + ], + [ + 4510.273518339023, + 1159.400746964428, + 6.91336696158579 + ], + [ + 4510.465469569443, + 1159.4024792139655, + 6.9037553847128565 + ], + [ + 4510.741222687293, + 1159.3907611138234, + 6.967758379742379 + ], + [ + 4510.951082013242, + 1159.363625368784, + 6.855718465281154 + ], + [ + 4511.1891991208195, + 1159.3182711116742, + 6.901736978434482 + ], + [ + 4511.410374337382, + 1159.4026872852346, + 6.853397678942286 + ], + [ + 4511.695077096462, + 1159.4706202713294, + 6.8760433766150415 + ], + [ + 4511.994800036622, + 1159.456909586657, + 6.958730640031569 + ], + [ + 4512.240753801644, + 1159.4492192194632, + 6.804444470595295 + ], + [ + 4512.526198976122, + 1159.4029188427678, + 6.863073263660821 + ], + [ + 4512.783166997684, + 1159.4634287826816, + 6.820680741941331 + ], + [ + 4513.007284484801, + 1159.4882947807107, + 6.868434518777149 + ], + [ + 4513.254033355891, + 1159.4830681442622, + 6.852178437790176 + ], + [ + 4513.4578720497675, + 1159.429674209966, + 7.015611698176912 + ], + [ + 4513.718723882948, + 1159.341276214053, + 7.223052338991053 + ], + [ + 4513.990171273071, + 1159.477350982599, + 7.01881531578579 + ], + [ + 4514.200306691604, + 1159.3472758210507, + 7.139751005303014 + ], + [ + 4514.430731899714, + 1159.499717760073, + 6.89063630407899 + ], + [ + 4514.698534359562, + 1159.4544756472865, + 7.035163191056964 + ], + [ + 4514.924347501373, + 1159.4651532196767, + 6.986790609435544 + ], + [ + 4515.303478323683, + 1159.528116224021, + 6.963343199001277 + ], + [ + 4515.545494456207, + 1159.4548289456898, + 6.922977178596373 + ], + [ + 4515.784080386444, + 1159.5641864409336, + 6.72633103736038 + ], + [ + 4516.066825502903, + 1159.5238563650223, + 6.788532079213753 + ], + [ + 4516.289921571189, + 1159.545687636485, + 6.820108695231009 + ], + [ + 4516.548685018257, + 1159.508501846482, + 6.879189857506896 + ], + [ + 4516.78514307049, + 1159.5343126719306, + 6.781845845896972 + ], + [ + 4517.070022707005, + 1159.3578148669262, + 7.059885074488405 + ], + [ + 4517.252958448279, + 1159.4951352647688, + 6.905203345599695 + ], + [ + 4517.464063968906, + 1159.613636198604, + 6.876344819968288 + ], + [ + 4517.568011251623, + 1159.6769599224895, + 7.066895474486845 + ], + [ + 4517.6818333220435, + 1159.823820820075, + 7.11051253513778 + ], + [ + 4518.032631554935, + 1159.7302552839708, + 7.091288268803704 + ], + [ + 4518.510417009017, + 1159.221083463828, + 7.340908457333534 + ], + [ + 4518.842414097268, + 1158.9918780323096, + 7.418777863164971 + ] + ], + "domain": [ + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ], + [ + 601.0, + 1946.0 + ] + ], + "window": [ + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ] + ] + } + }, + "ogg2m001-en06-20200822-0028-a00.fits.fz": { + "1": { + "coef": [ + [ + 7751.997434470427, + 3023.1505666230464, + 14.033017495099978, + 2.2244051082485266, + 1.3476318869324828, + 2.652633678388438 + ], + [ + 7752.0658962914395, + 3019.834917672589, + 13.893934911678366, + -4.109420972256612, + 2.414376843517977, + -1.7045105403837508 + ], + [ + 7752.3028691306245, + 3021.170390493258, + 12.957913404032137, + -1.4846977657499234, + 1.41807886315186, + -0.18880427663424582 + ], + [ + 7752.879451060976, + 3022.768880989016, + 12.872498994435297, + 1.4453104953994422, + 1.0030805394990339, + 1.4722425404408528 + ], + [ + 7753.25409559117, + 3022.628423881509, + 12.60992553455272, + 1.3442346423197935, + 0.6411023734868793, + 1.2944096985218039 + ], + [ + 7753.582557578731, + 3023.1756004006993, + 11.481262767390914, + 3.2082668051198366, + -0.7415380400085051, + 2.658769954028839 + ], + [ + 7753.928255146176, + 3022.0241418695996, + 11.21082462736528, + 0.7221356772677625, + -0.7196964186631096, + 1.3460416485494904 + ], + [ + 7754.669023319312, + 3023.5498410892883, + 12.247583730835121, + 3.318779336676995, + -0.16319355182636838, + 2.6790165212917616 + ], + [ + 7754.953964708378, + 3022.7144197354005, + 11.566062422705972, + 1.9684032986950808, + -0.7918790471474945, + 1.9534282187866607 + ], + [ + 7755.499330656856, + 3021.8586564022994, + 12.252124324087111, + -0.11208888826141147, + 0.3083483854716676, + 0.8338881862833956 + ], + [ + 7755.982339058768, + 3022.157001406531, + 12.204666042842996, + 0.5809892838425518, + 0.414718854812074, + 1.1320680844944808 + ], + [ + 7756.734949529281, + 3023.421505692136, + 12.81397145342478, + 2.9092877382851725, + 0.565250749111499, + 2.3644731501489757 + ], + [ + 7756.966582561562, + 3022.3113943066746, + 12.105329580639848, + 1.174148930589599, + 0.20522014421754972, + 1.3171589372558634 + ], + [ + 7757.384471664412, + 3020.4513859272647, + 12.52405240710963, + -2.689513574562898, + 1.087678265150251, + -1.074953329395835 + ], + [ + 7758.01086048653, + 3022.4183574985664, + 12.365255738196375, + 1.1427610603968013, + 0.2966265852488658, + 1.4993543908787001 + ], + [ + 7758.338736455708, + 3021.9549442631865, + 12.030414434148197, + 0.1741866060555628, + 0.2901449704513755, + 0.987356077778631 + ], + [ + 7758.925771288604, + 3021.8382709685825, + 12.556792544274813, + -0.223130515136929, + 1.06375807945823, + 0.7000368753551061 + ], + [ + 7759.465344529735, + 3022.4323468958873, + 12.319030842366374, + 1.220190274282498, + 0.5796458332961921, + 1.527376352287836 + ], + [ + 7759.824488714974, + 3022.0313522644187, + 11.711405849463679, + 0.7971377542867562, + 0.10718871328567858, + 1.3877932039098204 + ], + [ + 7760.402584838622, + 3022.480095367782, + 12.106529335810732, + 1.2736708133381758, + 0.35099482089218503, + 1.6097354302081157 + ], + [ + 7760.87874870246, + 3022.256452330986, + 12.32479208750614, + 0.6242219785936255, + 0.452716684353521, + 1.115445982635155 + ], + [ + 7761.420622462481, + 3022.2011432015556, + 12.569700146154917, + 0.31947723946449386, + 0.6743143195641766, + 0.9373140049383542 + ], + [ + 7762.04209185933, + 3023.510278742331, + 12.6462046026237, + 2.947994227820671, + 0.20773125285964233, + 2.461840639002616 + ], + [ + 7762.412180957578, + 3021.625673471118, + 12.772853096631115, + -0.7309188742787317, + 0.9486810683328266, + 0.25728753971921137 + ], + [ + 7762.804362293252, + 3022.1275203491587, + 12.286145422586426, + 0.6048123766956418, + 0.3832709642613894, + 1.0390424884580283 + ], + [ + 7763.28705708557, + 3021.928529316727, + 12.67423628836612, + 0.02897682874883319, + 0.6180828932341403, + 0.6339273445446378 + ], + [ + 7763.785459477388, + 3020.992891301222, + 13.191492872876081, + -1.8941565769832232, + 1.6249034676524496, + -0.6511981965215314 + ], + [ + 7764.33802707927, + 3021.37877649767, + 13.255450587466715, + -0.9984322746583311, + 1.5346309430914284, + -0.07838239824846056 + ], + [ + 7764.898292464132, + 3023.0595947129964, + 12.941502470547134, + 2.3161453139037724, + 0.8301545442928884, + 2.2046489146134824 + ], + [ + 7765.353246045438, + 3021.5767348174313, + 13.134171021082302, + -0.9967881317287037, + 1.3827176746693928, + -0.18961643110585355 + ], + [ + 7765.899616623249, + 3022.267362972569, + 13.221124130633276, + 0.5856998163993221, + 1.4673840242106415, + 0.9516998519805769 + ], + [ + 7766.184969338073, + 3021.476634970368, + 12.544053908467387, + -0.6521442970966326, + 0.9326847261702862, + 0.4309450900029248 + ], + [ + 7766.696858813336, + 3021.4420664806175, + 12.668438216524546, + -1.0817209604492002, + 1.1259746086968654, + 0.1867084346527686 + ], + [ + 7767.386884778216, + 3022.132890354313, + 13.11959762351461, + 0.1938036390496983, + 1.4317529977280268, + 0.6791603906853074 + ], + [ + 7767.8177829996275, + 3021.7100034897035, + 13.112025676512502, + -0.6096473315170154, + 1.6391614902073448, + 0.3458724806799041 + ], + [ + 7768.322477306662, + 3021.4823722854203, + 13.328718793520876, + -1.089909204159109, + 1.8634485699258398, + 0.2517832870028072 + ], + [ + 7768.720464497357, + 3020.9654665551884, + 13.015511805608277, + -2.125689965606738, + 1.5100026289550235, + -0.42414273994776347 + ], + [ + 7769.290011926066, + 3021.389587216952, + 12.916410654183384, + -0.8790114916768333, + 1.5470747482569758, + 0.4439269906698142 + ], + [ + 7769.562968425715, + 3020.3074445542957, + 12.411351960297056, + -2.88544256979951, + 1.3434485331747845, + -0.7669206498530416 + ], + [ + 7770.217249236531, + 3021.5204595983555, + 12.897395626120266, + -0.5956489377489865, + 1.1492060259477042, + 0.1822122246319221 + ], + [ + 7770.535195014329, + 3019.947043647043, + 12.880153567084141, + -3.8923934961256017, + 1.564190831360621, + -2.0092201607468616 + ], + [ + 7771.054941952099, + 3022.094566566253, + 11.941332497858163, + 1.016485334085144, + -0.0054562687008805014, + 1.4381228915539752 + ], + [ + 7771.567248638519, + 3021.042986582337, + 12.457107641520043, + -1.528427527766109, + 0.688391079343253, + -0.15102375608177532 + ], + [ + 7772.156472359511, + 3022.209618657079, + 12.357219887375166, + 0.5593185342891489, + 0.14117855645833519, + 1.2002080111922897 + ], + [ + 7772.581348691044, + 3022.5064108470942, + 12.244308910105284, + 1.116966693824459, + 0.23635471139976927, + 1.716064984470249 + ], + [ + 7773.150622642027, + 3020.305262968231, + 13.570495537409572, + -4.17998506077677, + 2.1873646061847754, + -2.1807115865416518 + ], + [ + 7773.6256152010765, + 3022.0969549624847, + 12.830690196432469, + -0.3874525757856032, + 1.0653136373951757, + 0.5147445737482464 + ], + [ + 7774.235717347427, + 3020.899353945321, + 13.916352153417503, + -3.071595288843351, + 2.177493398995068, + -1.600499220302316 + ], + [ + 7774.640618729073, + 3020.779399938221, + 13.558393763305807, + -3.2285914756477725, + 1.9292293590500087, + -1.5619035131986059 + ], + [ + 7775.1432441466895, + 3021.6834305862485, + 13.315358362263007, + -0.8926146635141005, + 1.442023452691105, + 0.017201752342398912 + ], + [ + 7775.511814354134, + 3020.753793648615, + 13.182238771047261, + -2.8375915990410743, + 1.6430969297549203, + -1.178423463193811 + ], + [ + 7775.979153260056, + 3022.03609508234, + 12.481558229949597, + 0.14795459128190003, + 0.459216926405433, + 1.0327249782994516 + ], + [ + 7776.628100010425, + 3023.1224567658924, + 12.766760354055215, + 1.9739730736851155, + 0.39508934373322724, + 2.1464383998182455 + ], + [ + 7776.998941591224, + 3021.803804831715, + 12.911324179252157, + -0.8447048566122481, + 0.8538616152932252, + 0.17056176643009985 + ], + [ + 7777.356198423012, + 3022.0776685285837, + 12.140529027118939, + 0.041347749119936404, + -0.01669634582319997, + 0.8426235572687977 + ], + [ + 7777.938202193884, + 3022.519153570371, + 12.574556711521106, + 0.4320572994143638, + 0.24749003170296502, + 1.0770498394885235 + ], + [ + 7778.528238014687, + 3021.6358377077145, + 13.25325004908614, + -1.505774203194103, + 1.1369547407343956, + -0.4815096861375029 + ], + [ + 7778.968918273764, + 3021.3181467365894, + 13.296330159306349, + -2.20039802790378, + 1.3267290574072883, + -0.8752381898819711 + ], + [ + 7779.321842114034, + 3019.131913485482, + 13.6050706640089, + -6.424314427557727, + 2.1365301193614026, + -3.8733855785628286 + ], + [ + 7779.978922633574, + 3019.4239561263803, + 14.158758691626998, + -6.100885521874113, + 2.794318818501096, + -3.711432237572429 + ], + [ + 7780.426632978048, + 3019.916482390455, + 13.619536336723167, + -4.507506289812864, + 2.1307116869715306, + -2.7131020503137884 + ], + [ + 7781.086807248815, + 3021.668108244077, + 13.88065242390157, + -1.2888423625965653, + 1.9344461711157772, + -0.43825345545469674 + ], + [ + 7781.316489420205, + 3021.1519440601337, + 12.85003471085202, + -1.6327816380590015, + 1.1078133076678658, + -0.4210396221877886 + ], + [ + 7781.756015363061, + 3020.7303167382374, + 12.743766789791552, + -2.3858265201748137, + 1.1814909342718778, + -0.8427006860833809 + ], + [ + 7782.524974500251, + 3022.343228233385, + 13.004250931325835, + 0.8065875752242792, + 0.84346732715499, + 1.0079484495606001 + ], + [ + 7782.769073670442, + 3021.085095355542, + 12.604101087787209, + -1.5912165765750106, + 0.9844602565076139, + -0.3274323814995369 + ], + [ + 7783.20670794767, + 3021.5516997074524, + 12.212267287036319, + -0.29451746241697424, + 0.43680504338672177, + 0.7172563714908432 + ], + [ + 7783.949515947161, + 3023.069644847995, + 12.920080915481151, + 2.2981337089894955, + 0.5278232040844182, + 2.113838433855457 + ], + [ + 7784.096955385689, + 3021.1482368221596, + 12.528421280784748, + -1.074896212772189, + 0.8269088461183319, + 0.19396954649917555 + ], + [ + 7784.504102872542, + 3021.0978723445564, + 12.368105343901176, + -1.394626678906718, + 0.599889819114149, + 0.10739977071203428 + ], + [ + 7785.135310142826, + 3022.6659018500854, + 12.589917751466688, + 2.0131844160813084, + 0.18381824708297476, + 2.1798295759219224 + ], + [ + 7785.764270785992, + 3022.6461909490845, + 13.057550678588063, + 1.8466967807684898, + 0.6460369984157865, + 1.821719913779699 + ], + [ + 7786.304532879781, + 3021.6106291112746, + 13.675611187458564, + -0.8781608229261348, + 1.6399589230310745, + -0.0519127482068012 + ], + [ + 7786.7501433429925, + 3020.8991276223555, + 13.444793475790782, + -2.1347324440360205, + 1.5701512519453857, + -0.7571737093729347 + ], + [ + 7787.382096171001, + 3022.380682578703, + 13.544147929306131, + 0.7994518237103029, + 1.3324105698512259, + 1.158939637697992 + ], + [ + 7787.777822701685, + 3021.0058090047023, + 13.843057837341634, + -1.94380967663545, + 1.949701872132505, + -0.8784971436615923 + ], + [ + 7788.334790847546, + 3020.8745376390293, + 13.87838094143007, + -2.608455846037068, + 2.0570782886721344, + -1.2577062634252945 + ], + [ + 7788.911145066057, + 3022.2792806753464, + 13.74239410948039, + 0.2395354940991177, + 1.536503315173301, + 0.7552227889051774 + ], + [ + 7789.414459351932, + 3021.4832269698513, + 14.051214121642182, + -1.461539739472004, + 2.1277306266726423, + -0.45193297924223363 + ], + [ + 7789.829359344499, + 3021.492754832275, + 13.678422583963192, + -1.1239651334757956, + 1.7114893784319127, + -0.1399151721109048 + ], + [ + 7790.347375672966, + 3021.558165353582, + 13.779906024640253, + -1.1762841001535822, + 1.6672706869602048, + -0.3408228999965861 + ], + [ + 7790.794113020494, + 3022.6407792098903, + 13.40985539894541, + 1.2006154941036544, + 0.9722769103455878, + 1.4024985634052003 + ], + [ + 7791.034337374366, + 3020.6041504877735, + 12.863365718538294, + -2.458947079112057, + 1.175647946835724, + -0.8600967273259769 + ], + [ + 7791.790727191743, + 3023.2458013485907, + 13.44640737449124, + 2.376596773498232, + 0.9315583870404256, + 2.2260085829816236 + ], + [ + 7792.188247029806, + 3022.2304007938183, + 13.564965424419734, + 0.2565936287522453, + 1.2705602689165734, + 0.8411275636369435 + ], + [ + 7792.781860142482, + 3023.016579210693, + 13.444486479814563, + 1.5856088874761383, + 1.0021338466339247, + 1.7533492341111827 + ], + [ + 7793.184116507829, + 3021.7423229155015, + 13.465020426908948, + -1.1524948435610352, + 1.5408728220928922, + -0.03611230339234233 + ], + [ + 7793.725779888861, + 3023.2858524586027, + 12.845204847323483, + 2.5918620253328823, + 0.3591787243346143, + 2.4854019844086133 + ], + [ + 7794.340279863956, + 3023.55098050541, + 13.396728933141148, + 3.016883665847073, + 0.7980774040134503, + 2.6341910730608546 + ], + [ + 7794.955003138489, + 3024.435672399386, + 13.550170419485665, + 4.4465416615064575, + 0.6085839344118285, + 3.421663388169861 + ], + [ + 7795.195073843995, + 3023.28960461199, + 13.31132277325295, + 2.4802840285864134, + 0.7766942840826245, + 2.158537106667296 + ], + [ + 7795.481993111772, + 3021.4049829649416, + 13.163589467777577, + -1.2681420816825115, + 1.2053422099461883, + -0.3504451358883365 + ], + [ + 7795.836132017704, + 3022.0521652472858, + 12.802055679581372, + 0.047738563583453957, + 0.8295731668395783, + 0.7538213264425352 + ], + [ + 7796.299051924988, + 3023.8146467536617, + 12.489493929078751, + 3.3959113380614143, + -0.12961986949680057, + 2.925571213841362 + ], + [ + 7796.342429614003, + 3024.265083036303, + 11.333550811936917, + 3.8575765069263355, + -1.3789447559327288, + 3.3066462521142994 + ] + ], + "domain": [ + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ] + ], + "window": [ + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ] + ] + }, + "2": { + "coef": [ + [ + 4506.390073918427, + 1206.5952276153778, + 11.207616460045045 + ], + [ + 4506.180649947286, + 1206.8416006273123, + 11.619359743695926 + ], + [ + 4506.18153243823, + 1207.0548056812622, + 11.808123216312913 + ], + [ + 4506.355777030104, + 1207.1756451877861, + 11.625584706482748 + ], + [ + 4506.557222730276, + 1207.5684846464949, + 11.207197497031146 + ], + [ + 4506.889921230803, + 1207.6014371635472, + 11.137926640194065 + ], + [ + 4507.0940122575885, + 1207.6379527705933, + 11.135573822465552 + ], + [ + 4507.284615843866, + 1207.6420612568402, + 11.186526861896354 + ], + [ + 4507.5076167538755, + 1207.51399629111, + 11.508317679987078 + ], + [ + 4507.84246552857, + 1207.4881440377812, + 11.459997812589341 + ], + [ + 4507.913687042649, + 1207.6942931987314, + 11.22125611486138 + ], + [ + 4508.090621595882, + 1207.6737698869722, + 11.08036260214636 + ], + [ + 4508.468897099885, + 1207.7379822897653, + 10.888088521795774 + ], + [ + 4508.655022851414, + 1207.7361706845768, + 11.16407332334955 + ], + [ + 4508.889562586186, + 1207.77480678339, + 11.295586989346464 + ], + [ + 4509.157521670921, + 1207.6790708848055, + 11.326270757000408 + ], + [ + 4509.4032304464945, + 1207.6284691306494, + 11.344042299156413 + ], + [ + 4509.638336258607, + 1207.6870638112464, + 11.351634823012521 + ], + [ + 4509.916625997945, + 1207.7929680122534, + 11.230634353677562 + ], + [ + 4510.10718360381, + 1207.6709905941682, + 11.280409132136151 + ], + [ + 4510.333730384903, + 1207.851421840962, + 11.066574412171857 + ], + [ + 4510.634110974061, + 1207.7743446333748, + 11.234156224518115 + ], + [ + 4510.843780297085, + 1207.7916271865915, + 11.41377988078168 + ], + [ + 4511.038492015783, + 1207.7868279180038, + 11.50243369812865 + ], + [ + 4511.364725692207, + 1207.8635065127478, + 11.430381184321872 + ], + [ + 4511.588124081724, + 1207.790846842917, + 11.470390562597348 + ], + [ + 4511.832686945188, + 1207.723990301635, + 11.4058540542165 + ], + [ + 4512.158553807079, + 1207.8020998999968, + 11.239202412364175 + ], + [ + 4512.425825471309, + 1207.7206475812018, + 11.287864916719203 + ], + [ + 4512.613743468869, + 1207.6133231274437, + 11.46529573185871 + ], + [ + 4512.893869031834, + 1207.60568027148, + 11.212460305582855 + ], + [ + 4513.09459927574, + 1207.5748054285307, + 11.275097861978875 + ], + [ + 4513.327616148174, + 1207.5705932378155, + 11.363856827436926 + ], + [ + 4513.540806278797, + 1207.5788550942552, + 11.387401186036309 + ], + [ + 4513.743206565757, + 1207.5958072328344, + 11.441784660791264 + ], + [ + 4513.932362575678, + 1207.727971798721, + 11.183801180160467 + ], + [ + 4514.1680299305635, + 1207.829176364818, + 11.185941192341737 + ], + [ + 4514.460599099443, + 1207.7891750022989, + 11.190506472892412 + ], + [ + 4514.668513079948, + 1207.7528199013575, + 11.327098999933881 + ], + [ + 4514.911129770378, + 1207.726973636632, + 11.151246734012208 + ], + [ + 4515.183003731698, + 1207.7531965562523, + 11.32520164850318 + ], + [ + 4515.525758013309, + 1207.686310336005, + 11.42030152361448 + ], + [ + 4515.762145782801, + 1207.8062269749194, + 11.458220810862189 + ], + [ + 4516.008687493555, + 1207.6888412897918, + 11.376510267660976 + ], + [ + 4516.230843630991, + 1207.7500745061625, + 11.398104003690008 + ], + [ + 4516.496308413636, + 1207.8619907393568, + 11.085528523189778 + ], + [ + 4516.676247730911, + 1207.8506669963376, + 11.233950309263774 + ], + [ + 4517.001165570336, + 1207.7304051783556, + 11.347920737537006 + ], + [ + 4517.20883502775, + 1207.70930138857, + 11.421906670274922 + ], + [ + 4517.454941688397, + 1207.9126189452556, + 11.260879353474179 + ], + [ + 4517.72218092986, + 1207.895571923911, + 11.20352781929768 + ], + [ + 4517.944666050722, + 1207.8232047241345, + 11.244069623671322 + ], + [ + 4518.199896440931, + 1207.8865140042824, + 11.20938241884911 + ], + [ + 4518.413634602781, + 1207.617757402603, + 11.37485565037978 + ], + [ + 4518.77649066486, + 1207.6647167273618, + 11.354734451522898 + ], + [ + 4518.99571154745, + 1207.691850749729, + 11.226731326657271 + ], + [ + 4519.272031622899, + 1207.6980982028688, + 11.14833283011157 + ], + [ + 4519.4883461314985, + 1207.5935671154082, + 11.271893299007285 + ], + [ + 4519.72145243645, + 1207.6519191298873, + 11.408409533008294 + ], + [ + 4519.999676997674, + 1207.691923183167, + 11.213211913128161 + ], + [ + 4520.236139498852, + 1207.7800388496696, + 11.14220309941689 + ], + [ + 4520.46709093235, + 1207.7958147775864, + 11.086932995749603 + ], + [ + 4520.741811516435, + 1207.6213981970698, + 11.260317880571481 + ], + [ + 4520.913511387861, + 1207.7508454433635, + 11.233122367859073 + ], + [ + 4521.102530833716, + 1207.7589667338507, + 11.437763748813072 + ], + [ + 4521.279045297262, + 1207.8842539678228, + 11.16337123083848 + ], + [ + 4521.51282369777, + 1207.9130735549334, + 11.172063337027385 + ], + [ + 4521.73967277957, + 1207.9133261203986, + 11.384809640804498 + ], + [ + 4522.020386343056, + 1207.9653030260229, + 11.3137315748234 + ], + [ + 4522.275395012658, + 1207.9011661662466, + 11.276521340552183 + ], + [ + 4522.464341769977, + 1207.8485664572543, + 11.205363469143773 + ], + [ + 4522.766414666508, + 1207.6041644840586, + 11.492530208487011 + ], + [ + 4522.926565456795, + 1207.8138204870868, + 11.359103611672221 + ], + [ + 4523.220487245923, + 1207.8678296414716, + 11.264613376475696 + ], + [ + 4523.462393301371, + 1207.9484056385131, + 11.278242428781281 + ], + [ + 4523.713785561564, + 1207.928876021074, + 11.355856343643687 + ], + [ + 4523.98231490511, + 1207.8915535783394, + 11.315161835481987 + ], + [ + 4524.195884558005, + 1208.0277273164497, + 11.194772819096428 + ], + [ + 4524.526914887896, + 1207.8559655657423, + 11.456767248409342 + ], + [ + 4524.722583880075, + 1208.070209460407, + 11.30519468714851 + ], + [ + 4524.987605005432, + 1208.032664906728, + 11.275823923540582 + ], + [ + 4525.238473644239, + 1207.8799221149825, + 11.411127680295486 + ], + [ + 4525.53780706405, + 1207.924481414596, + 11.32781868993216 + ], + [ + 4525.755969853861, + 1207.97507000979, + 11.195747823050725 + ], + [ + 4525.980003630349, + 1208.073485900236, + 11.089097632988848 + ], + [ + 4526.220056757473, + 1207.9564623355197, + 11.294979430012011 + ], + [ + 4526.529702618311, + 1207.9238073825804, + 11.365833801852347 + ], + [ + 4526.731323286506, + 1207.9541143059557, + 11.281880733758054 + ], + [ + 4527.048418980082, + 1207.8653115240559, + 11.315856687707345 + ], + [ + 4527.325718662866, + 1207.808992737303, + 11.295084697719313 + ], + [ + 4527.508040446782, + 1207.8441610902878, + 11.351956049912848 + ], + [ + 4527.642336503697, + 1207.9988933560844, + 11.47347184748596 + ], + [ + 4527.91398201924, + 1208.143231089201, + 11.203110329563565 + ], + [ + 4528.030327741763, + 1208.2797893901547, + 10.747723254224182 + ], + [ + 4528.125500605493, + 1208.595856327426, + 10.91170007149708 + ] + ], + "domain": [ + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ] + ], + "window": [ + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ] + ] + } + }, + "ogg2m001-en06-20200822-0009-a00.fits.fz": { + "1": { + "coef": [ + [ + 7751.606834017995, + 3021.9444860969065, + 14.10654227616732, + -0.9965950385049969, + 2.0074327842663124, + 0.14780443662346462 + ], + [ + 7751.937323269125, + 3023.1249239868816, + 13.412033068190548, + 1.740759800371233, + 1.1163063082394866, + 1.729863350710848 + ], + [ + 7752.051182144765, + 3022.3062932482203, + 12.705757861174007, + 0.16668204893630853, + 1.0581847335398133, + 0.5965476926626311 + ], + [ + 7752.585722794633, + 3022.710900705793, + 12.811716098357882, + 0.8268470500463193, + 0.9428267010489999, + 0.9803514127126559 + ], + [ + 7752.878021799226, + 3022.68896703314, + 11.819029185297993, + 1.4886824840807424, + 0.009920013944440222, + 1.6910666184855148 + ], + [ + 7753.524339202519, + 3024.793951877717, + 11.445310817449668, + 5.436330417378664, + -1.2053184383352917, + 4.2103935064651905 + ], + [ + 7753.611996407428, + 3021.6209367948713, + 11.069698442427507, + -0.7458445053524224, + -0.63969501894372, + 0.43832820191167804 + ], + [ + 7754.432846702851, + 3021.60579539704, + 12.80403485849839, + -1.4126568886642685, + 0.9031572518120261, + -0.17641904875524675 + ], + [ + 7754.691960378876, + 3023.6917687716873, + 11.208316349889172, + 3.5102652556287097, + -0.973178152091125, + 3.3274651262350585 + ], + [ + 7755.623505998904, + 3023.340967775076, + 12.956637926345342, + 1.8581853995806914, + 0.7553692607293694, + 1.6956621469162845 + ], + [ + 7755.824080379975, + 3021.0899311878193, + 12.599010214248397, + -1.7722628706344516, + 0.9777619799190448, + -0.666539404112892 + ], + [ + 7756.29468879852, + 3021.0554019171136, + 12.456599867468253, + -1.9823000930549743, + 0.9578411928383018, + -0.8126501047563006 + ], + [ + 7756.712982217676, + 3021.6917753324087, + 12.608942669363142, + -0.9981196967807554, + 1.0950973244965947, + 0.10887558281553515 + ], + [ + 7757.244890087921, + 3022.4379342462043, + 12.297367678912126, + 0.5345942244622159, + 0.43119398404123155, + 1.097126696855229 + ], + [ + 7757.689071842509, + 3022.364746095347, + 11.854266484143713, + 0.5225270126505202, + -0.12264413184935313, + 1.2345476155570831 + ], + [ + 7758.226641431007, + 3021.2987115775504, + 12.693206788403945, + -1.9358266465031382, + 1.203232291474474, + -0.4824764004324554 + ], + [ + 7758.74382385992, + 3022.5452962518348, + 12.192374320402195, + 0.9563855706866885, + 0.510377061914633, + 1.4101082682762347 + ], + [ + 7759.017528814031, + 3022.7396159259306, + 11.252515187080292, + 1.9156688753982944, + -0.412747094327482, + 2.23730814872065 + ], + [ + 7759.533112889022, + 3022.1667534198214, + 11.524358151875763, + 0.6114689567975935, + -0.06711697012109195, + 1.2907384683685343 + ], + [ + 7760.087644231354, + 3022.040031532757, + 11.846525040188016, + 0.02396959010469108, + 0.11925482431288935, + 0.5748379266401421 + ], + [ + 7760.728340056924, + 3022.313709867954, + 12.730375340481125, + -0.06591241051282051, + 0.7313179172645863, + 0.7395251149622393 + ], + [ + 7761.53474079764, + 3023.6751903856284, + 13.468763350487958, + 2.250583288709206, + 0.9677341241608464, + 1.9595504967554096 + ], + [ + 7761.796092678012, + 3022.0117340212128, + 13.09723115216012, + -0.9547849393485229, + 1.2473246529727002, + -0.05591941077960617 + ], + [ + 7762.264245264338, + 3022.5273008661466, + 12.892235761866676, + 0.40623534563480534, + 0.9111516532730749, + 0.7895737902548011 + ], + [ + 7762.687841022145, + 3021.6968619128625, + 13.118790170652318, + -1.1104078063528542, + 1.250325437169727, + -0.21451566700746416 + ], + [ + 7762.949107765338, + 3021.9370632114724, + 12.396311610108855, + -0.26293200043324955, + 0.6647413460972654, + 0.583819975723653 + ], + [ + 7763.415305765637, + 3021.5062244851583, + 12.44220642801169, + -1.2093258356741348, + 0.9268841464386517, + -0.08309977292102351 + ], + [ + 7764.294697486286, + 3021.302517667369, + 14.05046793989934, + -2.2969528611796526, + 2.4544498362457388, + -0.9717792734456564 + ], + [ + 7764.657649750265, + 3022.701340751931, + 12.78649737552627, + 0.96427010026741, + 0.7746589471354928, + 1.285460473543854 + ], + [ + 7765.122872077526, + 3020.789558870826, + 13.464843883696616, + -3.453885677642467, + 2.072380193497654, + -1.6582583708741765 + ], + [ + 7765.765774187313, + 3023.8512353054066, + 12.984209649686804, + 3.079717009504594, + 0.8067106110849774, + 2.6479633165164054 + ], + [ + 7765.958224302393, + 3021.38258313722, + 12.381244294744851, + -1.459410650999681, + 0.8212572713599935, + -0.15547814343059832 + ], + [ + 7766.563998515597, + 3022.085045485896, + 12.587468330703317, + 0.07675528064590834, + 0.9540773425414368, + 0.6866526931454051 + ], + [ + 7767.338681626789, + 3022.4501393344876, + 13.762518362942581, + 0.012706122806384012, + 2.038782997654765, + 0.5199575820233554 + ], + [ + 7767.794601444571, + 3022.0591047776593, + 13.805582720428097, + -0.7277303112537924, + 2.1242937222511964, + 0.03439125758715034 + ], + [ + 7767.984589392222, + 3022.2413663412635, + 12.371490837163364, + 0.25424231486415544, + 0.8254787641543561, + 1.3493184110913028 + ], + [ + 7768.731247475223, + 3021.5833249110137, + 13.461597779448615, + -1.7481431187875258, + 1.8712583658043127, + -0.4378011137148518 + ], + [ + 7769.07261661495, + 3020.5550334174527, + 13.152160886211709, + -3.274786352530564, + 1.9762601800016748, + -1.2004612133144665 + ], + [ + 7769.519788552281, + 3020.8339320307728, + 13.14491202250428, + -2.7724197554712275, + 1.8068332012348973, + -1.2435791967882937 + ], + [ + 7769.975580925478, + 3021.1785728328578, + 13.169986848939036, + -1.8871361725508882, + 1.6586743676511988, + -0.5230925042033289 + ], + [ + 7770.432649564188, + 3021.1516140459426, + 13.086199130339354, + -1.8772440093407283, + 1.5560589494971517, + -0.59229916376838 + ], + [ + 7771.000902452831, + 3022.509931012555, + 12.36055633102275, + 0.7269155027689371, + 0.26735323945692707, + 1.1821772305107086 + ], + [ + 7771.384031042397, + 3021.3055458862505, + 12.619702726975575, + -1.5649955029894809, + 0.7527944116976336, + -0.17895767575343965 + ], + [ + 7771.711164126045, + 3020.651379423592, + 11.918424157267145, + -2.988522839200011, + 0.2451630408033059, + -1.1253466559123124 + ], + [ + 7772.339794717937, + 3022.300618008376, + 12.123769658560045, + 0.2248004984674914, + 0.08291983721700809, + 0.9162141808746123 + ], + [ + 7772.635686570684, + 3019.3374639136814, + 12.396262786473123, + -6.072824918216553, + 1.3902834560182442, + -3.2482129609968426 + ], + [ + 7773.409257695723, + 3022.0440170540724, + 12.60647774361747, + -0.8826032829043149, + 0.7078214725207289, + 0.25259038427247943 + ], + [ + 7773.827925586574, + 3022.667103666365, + 12.479464096203264, + 0.7323345731029842, + 0.46785319563186745, + 1.2285662669055366 + ], + [ + 7774.460735543314, + 3022.086264831255, + 13.339499617842842, + -0.7401692722049579, + 1.5034639069609395, + 0.17701031695681615 + ], + [ + 7774.832598438352, + 3021.409379468344, + 13.082151684217314, + -2.0333128360599297, + 1.3826761867726742, + -0.5597437636011159 + ], + [ + 7775.257752893407, + 3020.1676858706205, + 13.251202077122855, + -4.725637080634044, + 2.006450227686792, + -2.301432332519686 + ], + [ + 7775.629192058175, + 3022.2098657314427, + 12.090657396514098, + -0.06385075956266738, + 0.21316429246239893, + 0.9470849875683057 + ], + [ + 7776.496234617752, + 3022.01701925301, + 13.68710176499028, + -1.5504982167778105, + 1.5244710336628557, + -0.658001717787302 + ], + [ + 7776.675455789979, + 3023.395102553113, + 11.715964741654485, + 2.6961906959171813, + -0.8136502508769855, + 2.6642375797507944 + ], + [ + 7777.13891938964, + 3022.417422853366, + 11.898077388638447, + 0.24425646129886955, + -0.3250662947195997, + 1.1977247311206276 + ], + [ + 7777.681312771144, + 3022.413875854904, + 12.301188895017981, + -0.2921982929122346, + 0.10012038725201292, + 0.6075930486863375 + ], + [ + 7778.486893579464, + 3023.0799571180883, + 13.40062237241935, + 0.6292099102468628, + 0.9339494223438373, + 0.868451771116174 + ], + [ + 7778.900221016366, + 3023.3300015289747, + 13.464883170704077, + 1.3823364649583092, + 0.9580994217268703, + 1.4801660505559842 + ], + [ + 7779.3928132347355, + 3020.4992784419974, + 14.510490778971286, + -4.7680270646934275, + 2.7478903516365567, + -2.9095455990113264 + ], + [ + 7779.714321615045, + 3020.265507476927, + 13.734051178876284, + -4.54834866402226, + 2.1794359423181477, + -2.410390719869469 + ], + [ + 7780.189653216118, + 3020.6035479714883, + 13.510579641708492, + -3.677154858395965, + 2.003114691081356, + -1.9551964976258849 + ], + [ + 7780.651712782889, + 3019.8950698080025, + 13.24519064564159, + -4.99254627777339, + 2.0007574540666693, + -2.822297484122536 + ], + [ + 7781.242367694282, + 3022.4674572902195, + 12.83619272292385, + 0.4641997083246721, + 0.7741976105540815, + 0.7315354212614154 + ], + [ + 7781.616396300082, + 3020.3789914021827, + 13.09309325177844, + -3.729953149896832, + 1.7782813504724984, + -1.9149329335507568 + ], + [ + 7782.229000467027, + 3021.8984081994827, + 13.088537808274955, + -0.9391170112474202, + 1.1807736598425007, + -0.047047164785192795 + ], + [ + 7782.781100460103, + 3022.5797940597195, + 12.924071879913498, + 1.107312989877696, + 0.7991617122138626, + 1.3161592836890814 + ], + [ + 7783.124166636073, + 3022.8437239410614, + 12.689090571345849, + 1.7336491159867242, + 0.519899311712254, + 2.149084045481712 + ], + [ + 7783.369197766471, + 3020.2334851307864, + 13.423318744788345, + -4.239447950991447, + 2.101096535836677, + -1.597695821496363 + ], + [ + 7784.071782854282, + 3022.78111362521, + 13.070690305000001, + 1.2169341699439924, + 0.8840327102234118, + 1.4709428215384037 + ], + [ + 7784.445901827649, + 3023.1595177120957, + 12.609594711682062, + 2.2085709607033945, + 0.1649127315965035, + 2.2884584913338317 + ], + [ + 7784.935866360414, + 3021.623195369085, + 12.704351694111466, + -0.6718620060531176, + 0.8777175987207111, + 0.25027693470541157 + ], + [ + 7785.6711956398985, + 3023.535591373108, + 13.130582466774495, + 2.6754552950719455, + 0.6327716846401086, + 2.4430027512176404 + ], + [ + 7786.132008460587, + 3022.613037113372, + 13.085723354673886, + 1.1384163042971622, + 0.7991019004923612, + 1.512056689133716 + ], + [ + 7786.752092424309, + 3022.9063359587312, + 13.68777948261364, + 1.2945797776315513, + 1.3031091300221234, + 1.417978650076477 + ], + [ + 7787.223892965779, + 3021.766849770453, + 13.987687094421414, + -1.285389198588609, + 2.0491115519171443, + -0.46775576130448926 + ], + [ + 7787.709385295623, + 3021.5142336404974, + 14.007617174374245, + -1.6147218299720894, + 2.159694908361239, + -0.8136648402025091 + ], + [ + 7788.184676942194, + 3022.927602638764, + 13.297676105448708, + 1.3053760846437312, + 0.9394651624767916, + 1.5028935037823075 + ], + [ + 7788.742646837817, + 3022.8410130425973, + 13.573370499771322, + 1.2801321384078974, + 1.3069844837848108, + 1.3159129480351752 + ], + [ + 7789.211056110554, + 3022.21069265606, + 14.022400788991332, + -0.26902180630325756, + 2.008894858618112, + 0.32991403189776597 + ], + [ + 7789.637381568409, + 3021.6488599373643, + 13.946915067856553, + -1.5681705975898728, + 1.9905489665043337, + -0.4478174742766587 + ], + [ + 7790.030401277502, + 3021.1611900058497, + 13.76098112507244, + -2.75135780116332, + 1.8265800251814999, + -1.3184098028335827 + ], + [ + 7790.417246583597, + 3021.525343777434, + 13.31677389091972, + -1.3613294958033655, + 1.4538699444872423, + -0.20796618910682702 + ], + [ + 7791.214302320847, + 3023.7119090448236, + 14.018965677936068, + 2.4281721117515884, + 1.3396659381378746, + 2.14919755286641 + ], + [ + 7791.413350663063, + 3023.0243123148143, + 12.819307371528103, + 1.7504850027032461, + 0.3970743397320174, + 2.0619782076631736 + ], + [ + 7792.052408674317, + 3022.594727323203, + 13.98731140684661, + -0.2781760335532223, + 1.7364958047067534, + 0.4234583162897941 + ], + [ + 7792.670601054162, + 3024.763202194757, + 13.187297764225976, + 4.720885935608674, + 0.40776378949964753, + 3.9535315890056886 + ], + [ + 7793.037180428408, + 3021.8722921223953, + 13.689544845564981, + -1.5724525022435885, + 1.7338458358560491, + -0.36202537855363603 + ], + [ + 7793.657117022336, + 3022.5409189688935, + 13.820469188611602, + -0.31120177686010475, + 1.5409754805341553, + 0.22065739079096697 + ], + [ + 7794.254432259947, + 3024.1685880455357, + 13.316274539869525, + 3.577358164146768, + 0.525003654503052, + 2.810708057569881 + ], + [ + 7794.928353896397, + 3026.242965811735, + 13.602954361555588, + 7.638418345488061, + 0.13425472129942084, + 5.490813754538854 + ], + [ + 7794.981531040545, + 3023.1752336780655, + 13.441969184104847, + 1.5058401772802705, + 1.0749482652064732, + 1.492118204730248 + ], + [ + 7795.299437114574, + 3022.724345280757, + 12.910258579783731, + 1.0358090675964147, + 0.7863601284815626, + 1.193196263142384 + ], + [ + 7795.960257442633, + 3024.442722955672, + 13.584415240979277, + 3.5834585524820843, + 0.890109105652884, + 2.6129134596578925 + ], + [ + 7795.920464136112, + 3025.4461331409393, + 11.810863392178089, + 5.529283079289769, + -1.1978246098779886, + 4.430278706742659 + ], + [ + 7795.814235397727, + 3026.705975573766, + 10.526875050066973, + 8.267805342999937, + -3.062138220098372, + 5.9228297357937585 + ] + ], + "domain": [ + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ], + [ + 0.0, + 1734.0 + ] + ], + "window": [ + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ] + ] + }, + "2": { + "coef": [ + [ + 4506.1078529588285, + 1206.6894289608354, + 11.24398293948542 + ], + [ + 4505.978002625716, + 1207.0863234595463, + 11.448881472917785 + ], + [ + 4506.047044374363, + 1207.3958636223963, + 11.580903664952123 + ], + [ + 4506.2649954135095, + 1207.4828441724223, + 11.477637635822822 + ], + [ + 4506.477863073265, + 1207.6178677453267, + 11.174091245769086 + ], + [ + 4506.8125181490495, + 1207.7064638836866, + 11.024275159085542 + ], + [ + 4507.1328167935535, + 1207.4677618460391, + 11.215230663458556 + ], + [ + 4507.165062011286, + 1207.5879489667882, + 11.276281696115955 + ], + [ + 4507.454972328891, + 1207.408883972839, + 11.439594994419375 + ], + [ + 4507.609895698412, + 1207.6870640472066, + 11.327452348117552 + ], + [ + 4507.921324767232, + 1207.8158235862422, + 11.038443083795558 + ], + [ + 4508.074943696939, + 1207.888394056512, + 11.040284698408714 + ], + [ + 4508.365857009468, + 1208.0402806100367, + 11.106881474118008 + ], + [ + 4508.694910275483, + 1207.7167808574632, + 11.244370762117583 + ], + [ + 4508.876125566508, + 1207.8187495285983, + 11.05834621664729 + ], + [ + 4509.18454135499, + 1207.685593939935, + 11.253645074455823 + ], + [ + 4509.3652891497395, + 1207.6928297373975, + 11.330043640726835 + ], + [ + 4509.704930747043, + 1207.6853592649115, + 11.398772748200898 + ], + [ + 4509.785074803967, + 1207.7818730995557, + 11.380304150387405 + ], + [ + 4510.044978022201, + 1207.796998667654, + 11.18922526972916 + ], + [ + 4510.357035536473, + 1207.862636855639, + 11.324502285612763 + ], + [ + 4510.4988888425805, + 1207.7668825155679, + 11.684476023316527 + ], + [ + 4510.754399469275, + 1207.949904000914, + 11.372901779671855 + ], + [ + 4510.906819300111, + 1207.9761507490937, + 11.438443111814253 + ], + [ + 4511.319813749818, + 1207.7621148731394, + 11.411591202502938 + ], + [ + 4511.503973388057, + 1207.8676499065646, + 11.259921721761758 + ], + [ + 4511.867629929313, + 1207.9413208696342, + 11.216461533512646 + ], + [ + 4512.030030993351, + 1207.8022618632174, + 11.176278439834878 + ], + [ + 4512.359411847784, + 1207.7887980486412, + 11.296149240945061 + ], + [ + 4512.567470016896, + 1207.7659484940043, + 11.192227407144054 + ], + [ + 4512.803487632026, + 1207.8317376951234, + 11.08375517955317 + ], + [ + 4512.990339207533, + 1207.756473729178, + 11.223901315367776 + ], + [ + 4513.252834232553, + 1207.815980266701, + 11.220580467575973 + ], + [ + 4513.474746119884, + 1207.7359095583117, + 11.353537273079809 + ], + [ + 4513.670087429637, + 1207.7720941071407, + 11.361743411144639 + ], + [ + 4514.000757711297, + 1207.8434306395263, + 11.238151524805051 + ], + [ + 4514.174670512678, + 1207.905606705762, + 11.161777849481062 + ], + [ + 4514.363945074123, + 1207.79529142078, + 11.36932354371757 + ], + [ + 4514.685652008721, + 1207.7568430019282, + 11.211126636168128 + ], + [ + 4514.881751069915, + 1207.9658680808404, + 10.894083225586671 + ], + [ + 4515.161546816267, + 1207.8069807214638, + 11.189728639063471 + ], + [ + 4515.465461291686, + 1208.004921128713, + 11.028283900873342 + ], + [ + 4515.705684448783, + 1207.9468102319927, + 11.200426169338535 + ], + [ + 4515.933144373609, + 1207.9452986871513, + 11.220845341944377 + ], + [ + 4516.179003765457, + 1207.9051538589372, + 11.367690549700944 + ], + [ + 4516.373158192427, + 1207.9063470856684, + 11.017089900888475 + ], + [ + 4516.660201614663, + 1208.0390497354963, + 10.8214774945401 + ], + [ + 4516.895694245098, + 1207.8423534161432, + 11.144389917241375 + ], + [ + 4517.10616168112, + 1207.9232735676078, + 11.265481197276538 + ], + [ + 4517.297024767948, + 1207.8012083100516, + 11.678708150931486 + ], + [ + 4517.666819970879, + 1207.8038161157435, + 11.568741346450874 + ], + [ + 4517.854295920976, + 1207.8781117613664, + 11.028626905975326 + ], + [ + 4518.204216818761, + 1207.607636040042, + 11.309283468387703 + ], + [ + 4518.393319214756, + 1207.8397567685297, + 11.218399537253054 + ], + [ + 4518.682109668194, + 1207.9435939874936, + 10.996742011810493 + ], + [ + 4518.901395844393, + 1207.920130515215, + 11.060935169560253 + ], + [ + 4519.173367245086, + 1207.74123508144, + 11.143877095507396 + ], + [ + 4519.467178066939, + 1207.7270875230038, + 11.331439607391168 + ], + [ + 4519.688871588075, + 1207.9493623649105, + 10.817318952366413 + ], + [ + 4519.978612157362, + 1207.5834556901345, + 11.504569714701642 + ], + [ + 4520.157740297492, + 1207.9105576698241, + 11.139027403769001 + ], + [ + 4520.372419828535, + 1207.8955877309295, + 11.00822532519196 + ], + [ + 4520.6645206199155, + 1207.7293910712253, + 11.2697911303211 + ], + [ + 4520.902535328852, + 1207.8822751685993, + 11.198963920015673 + ], + [ + 4521.070969851942, + 1207.8790834700596, + 11.356999709304457 + ], + [ + 4521.25741271734, + 1207.9017353714821, + 11.346542101218441 + ], + [ + 4521.571660065227, + 1207.7260969471686, + 11.39157192386243 + ], + [ + 4521.749537106517, + 1207.9731792713656, + 10.98782756417529 + ], + [ + 4521.881226796595, + 1207.918249643487, + 11.439596611022004 + ], + [ + 4522.164175077029, + 1207.8513489682907, + 11.29120199037315 + ], + [ + 4522.392234906849, + 1207.9366359319758, + 11.225102497687823 + ], + [ + 4522.7354821852905, + 1207.7905142983134, + 11.417570268904996 + ], + [ + 4522.967360376698, + 1207.883961146495, + 11.33754798508366 + ], + [ + 4523.245114915793, + 1207.9121560691208, + 11.309538721229863 + ], + [ + 4523.489219694869, + 1208.0486070027625, + 11.37733604850021 + ], + [ + 4523.745449474008, + 1208.1174645037688, + 11.207821075920076 + ], + [ + 4524.006553859489, + 1207.9976834524748, + 11.395297587388198 + ], + [ + 4524.293163733649, + 1207.8343038728467, + 11.330707190966933 + ], + [ + 4524.39270749532, + 1208.1015879195957, + 11.324661389622928 + ], + [ + 4524.7566485187135, + 1207.9654926765843, + 11.406331103324629 + ], + [ + 4525.009622177068, + 1207.9780402069707, + 11.243685790919379 + ], + [ + 4525.281682072187, + 1208.075873540888, + 11.318936685009257 + ], + [ + 4525.43925553812, + 1207.9950417045645, + 11.214764706197878 + ], + [ + 4525.797806366487, + 1208.0799425587031, + 11.409988122699653 + ], + [ + 4526.030238552436, + 1208.0330832313507, + 11.33857265939726 + ], + [ + 4526.202857547271, + 1208.0724526752508, + 11.258788557323731 + ], + [ + 4526.464477503338, + 1208.189332240965, + 10.850602090329257 + ], + [ + 4526.75385998802, + 1207.9410622502085, + 11.144550730759859 + ], + [ + 4526.934630504112, + 1208.0701733564333, + 11.215365494185603 + ], + [ + 4527.166963620202, + 1208.2285694876605, + 11.241897508681632 + ], + [ + 4527.416650583282, + 1208.080732743335, + 11.39626062493622 + ], + [ + 4527.60869285825, + 1208.347233525721, + 10.968053329540764 + ], + [ + 4527.866471817228, + 1208.2102271343103, + 11.108958562749283 + ], + [ + 4527.905336910337, + 1208.7525984200565, + 10.839225596676744 + ], + [ + 4527.726503686425, + 1208.9459113082476, + 10.972617769978543 + ] + ], + "domain": [ + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ], + [ + 495.0, + 1896.0 + ] + ], + "window": [ + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ], + [ + -1.0, + 1.0 + ] + ] + } + } +} \ No newline at end of file diff --git a/banzai_floyds/tests/test_background.py b/banzai_floyds/tests/test_background.py new file mode 100644 index 0000000..87075d7 --- /dev/null +++ b/banzai_floyds/tests/test_background.py @@ -0,0 +1,96 @@ +from banzai_floyds.background import fit_background, set_background_region, BackgroundFitter +from banzai_floyds.extract import set_extraction_region +from banzai_floyds.tests.utils import generate_fake_science_frame +from banzai_floyds.utils.binning_utils import bin_data +import numpy as np +from banzai import context +from collections import namedtuple +from astropy.table import Table +from numpy.polynomial.legendre import Legendre +from banzai_floyds.utils.order_utils import get_order_2d_region + + +def test_background_fitting(): + np.random.seed(234515) + fake_frame = generate_fake_science_frame(include_sky=True) + binned_data = bin_data(fake_frame.data, fake_frame.uncertainty, fake_frame.wavelengths, fake_frame.orders) + fake_frame.binned_data = binned_data + fake_profile_width_funcs = [Legendre(fake_frame.input_profile_sigma,) for _ in fake_frame.input_profile_centers] + fake_frame.profile = fake_frame.input_profile_centers, fake_profile_width_funcs, None + fake_frame.background_windows = [[[-15, -5], [5, 15]], [[-15, -5], [5, 15]]] + set_background_region(fake_frame) + fake_frame.extraction_windows = [[-5.0, 5.0], [-5.0, 5.0]] + set_extraction_region(fake_frame) + fitted_background = fit_background(binned_data, background_order=3) + fake_frame.background = fitted_background + # If we are fitting to the noise, I think the residuals / uncertainty per pixel should + # follow a Gaussian distribution with sigma=1. So check cuts of the residual + # distribution rather than a single cutoff value in assert_allclose + # The residuals still look more correlated especially in the y-profile direction, + # but I guess that shouldn't be surprising given how we are fitting + in_order = fake_frame.orders.data > 0 + residuals = fake_frame.background[in_order] - fake_frame.input_sky[in_order] + residuals /= fake_frame.uncertainty[in_order] + assert (np.abs(residuals) < 3).sum() > 0.99 * in_order.sum() + # We assert that only edge pixels can vary by 5 sigma due to edge effects + for order in [1, 2]: + order_region = get_order_2d_region(fake_frame.orders.data == order) + residuals = fake_frame.background[order_region][-2:2, -2:2] - fake_frame.input_sky[order_region][-2:2, -2:2] + residuals /= fake_frame.uncertainty[order_region][-2:2, -2:2] + assert np.all(np.abs(residuals) < 5) + + +def test_background_stage(): + np.random.seed(15322) + input_context = context.Context({}) + frame = generate_fake_science_frame(flat_spectrum=False, include_sky=True) + frame.binned_data = bin_data(frame.data, frame.uncertainty, frame.wavelengths, frame.orders) + fake_profile_width_funcs = [Legendre(frame.input_profile_sigma,) for _ in frame.input_profile_centers] + frame.profile = frame.input_profile_centers, fake_profile_width_funcs, None + frame.background_windows = [[[-15, -5], [5, 15]], [[-15, -5], [5, 15]]] + set_background_region(frame) + frame.extraction_windows = [[-5.0, 5.0], [-5.0, 5.0]] + set_extraction_region(frame) + stage = BackgroundFitter(input_context) + frame = stage.do_stage(frame) + + in_extract_region = np.zeros_like(frame.data, dtype=bool) + x, y = frame.binned_data['x'], frame.binned_data['y'] + in_extract_region[y, x] = np.logical_and(frame.binned_data['extraction_window'], + frame.binned_data['wavelength_bin'] > 0) + in_order = frame.orders.data > 0 + residuals = frame.background[in_order] - frame.input_sky[in_order] + residuals /= frame.uncertainty[in_order] + assert (np.abs(residuals) < 3).sum() > 0.99 * in_order.sum() + # We assert that only edge pixels can vary by 5 sigma due to edge effects + for order in [1, 2]: + order_region = get_order_2d_region(frame.orders.data == order) + residuals = frame.background[order_region][-2:2, -2:2] - frame.input_sky[order_region][-2:2, -2:2] + residuals /= frame.uncertainty[order_region][-2:2, -2:2] + assert np.all(np.abs(residuals) < 5) + + +def test_background_region(): + FakeImage = namedtuple('FakeImage', ['binned_data', 'meta', 'background_windows']) + nx, ny = 103, 101 + x, y = np.meshgrid(np.arange(nx), np.arange(ny)) + order_centers = [20, 60] + order_height = 21 + orders = np.zeros_like(x) + for order_id in [1, 2]: + in_order = order_centers[order_id - 1] - order_height // 2 <= y + in_order = np.logical_and(y <= order_centers[order_id - 1] + order_height // 2, in_order) + orders[in_order] = order_id + profile_sigma = 1.0 + y_profile = np.zeros_like(x) + for order_id in [1, 2]: + y_profile[orders == order_id] = y[orders == order_id] - order_centers[order_id - 1] + binned_data = Table({'x': x.ravel(), 'y': y.ravel(), 'order': orders.ravel(), + 'profile_sigma': profile_sigma * np.ones(x.size), + 'y_profile': y_profile.ravel()}) + fake_data = FakeImage(binned_data, {}, [[[-15, -5], [5, 15]], [[-15, -5], [5, 15]]]) + set_background_region(fake_data) + # The background region should be +5 to +10 on both sides, so per order, the background should be 12 pixels high + for order in [1, 2]: + in_order = fake_data.binned_data['order'] == order + assert np.sum(fake_data.binned_data['in_background'][in_order]) == 12 * nx diff --git a/banzai_floyds/tests/test_binning.py b/banzai_floyds/tests/test_binning.py new file mode 100644 index 0000000..6e9354e --- /dev/null +++ b/banzai_floyds/tests/test_binning.py @@ -0,0 +1,31 @@ +from banzai_floyds.utils.binning_utils import bin_data +from collections import namedtuple +import numpy as np + + +def test_bin_data(): + data_height = 11 + data_width = 102 + data = np.zeros((data_height, data_width)) + uncertainty = np.zeros_like(data) + FakeOrders = namedtuple('FakeOrders', ['order_ids', 'data', 'center']) + fake_orders = FakeOrders([1,], np.ones_like(data, dtype=int), lambda x: [data_height // 2,]) + FakeWavelengths = namedtuple('FakeWavelengths', ['data', 'bin_edges']) + wavelength_data = np.zeros_like(data) + for i in range(data_width): + wavelength_data[:, i] = i + # Define the bin edges to take 2 columns omitting the first and last column + bin_edges = np.arange(0.5, data_width - 1, 2) + fake_wavelengths = FakeWavelengths(wavelength_data, [bin_edges,]) + + binned_data = bin_data(data, uncertainty, fake_wavelengths, fake_orders) + + points_in_bins = [] + for data_group in binned_data.groups: + points_in_bins.append(len(data_group)) + + # Each bin should have two columns of points in it + # We count the first and last column that isn't really in a bin as the same bin label, so the same expected + # number of points + expected_points_in_bins = [2 * data_height] * (data_width // 2) + np.testing.assert_array_equal(points_in_bins, expected_points_in_bins) diff --git a/banzai_floyds/tests/test_e2e.py b/banzai_floyds/tests/test_e2e.py index af95e15..e3875a5 100644 --- a/banzai_floyds/tests/test_e2e.py +++ b/banzai_floyds/tests/test_e2e.py @@ -2,6 +2,7 @@ import time from banzai.celery import app, stack_calibrations from banzai.tests.utils import FakeResponse +from banzai_floyds.tests.utils import load_manual_region import banzai.dbs import os import pkg_resources @@ -17,6 +18,9 @@ from types import ModuleType import banzai_floyds.dbs import logging +import json +from banzai_floyds.utils.order_utils import get_order_2d_region +from numpy.polynomial.legendre import Legendre logger = logging.getLogger('banzai') @@ -26,6 +30,8 @@ DATA_FILELIST = pkg_resources.resource_filename('banzai_floyds.tests', 'data/test_data.dat') CONFIGDB_FILENAME = pkg_resources.resource_filename('banzai_floyds.tests', 'data/configdb.json') +ORDER_HEIGHT = 95 + def celery_join(): celery_inspector = app.control.inspect() @@ -107,6 +113,25 @@ def test_that_order_mask_exists(self): # Note there are only two orders in floyds assert np.max(hdu['ORDERS'].data) == 2 + def test_that_order_mask_overlaps_manual_reducion(self): + # This uses the by hand measurements in chacterization_testing/ManualReduction.ipynb + test_data = ascii.read(pkg_resources.resource_filename('banzai_floyds.tests', 'data/test_skyflat.dat')) + for row in test_data: + row['filename'] = row['filename'].replace("x00.fits", "f00.fits") + + filenames = expected_filenames(test_data) + manual_fits_filename = pkg_resources.resource_filename('banzai.tests', 'data/orders_e2e_fits.dat') + for filename in filenames: + hdu = fits.open(filename) + site_id = hdu['SCI'].header['SITEID'] + for order_id in [1, 2]: + manual_order_region = load_manual_region(manual_fits_filename, + site_id, order_id, + hdu['SCI'].data.shape, + ORDER_HEIGHT) + found_order = hdu['ORDERS'].data == order_id + assert np.logical_and(manual_order_region, found_order).sum() / found_order.sum() >= 0.99 + @pytest.mark.e2e @pytest.mark.arc_frames @@ -135,6 +160,36 @@ def test_if_arc_frames_were_created(self): if 'a91.fits' in expected_file: assert os.path.exists(expected_file) + def test_if_arc_solution_is_sensible(self): + + with open(pkg_resources.resource_filename('banzai.tests', 'data/wavelength_e2e_fits.dat')) as solution_file: + solution_params = json.load(solution_file) + order_fits_file = pkg_resources.filename('banzai.tests', 'data/orders_e2e_fits.dat') + test_data = ascii.read(DATA_FILELIST) + for expected_file in expected_filenames(test_data): + if 'a91' not in expected_file: + continue + hdu = fits.open(expected_file) + site_id = os.path.basename(expected_file)[:3] + for order_id in [1, 2]: + order_region = load_manual_region(order_fits_file, + site_id, order_id, + hdu['SCI'].data.shape, + ORDER_HEIGHT) + manual_wavelengths = np.zeros(hdu['SCI'].shape) + region = get_order_2d_region(order_region) + for i in range(region.shape[0]): + wavelength_solution = Legendre(coef=solution_params[site_id][order_id]['coef'][i], + domain=solution_params[site_id][order_id]['domain'][i], + window=solution_params[site_id][order_id]['window'][i]) + x_pixels = np.arange(wavelength_solution.domain[0], wavelength_solution.domain[1] + 1) + manual_wavelengths[region][i] = wavelength_solution(x_pixels) + overlap = np.logical_and(hdu['ORDERS'].data == order_id, order_region) + # Require < 0.5 Angstrom tolerance + assert np.testing.assert_allclose(hdu['WAVELENGTHS'][overlap], + manual_wavelengths[overlap], + atol=0.5) + @pytest.mark.e2e @pytest.mark.fringe diff --git a/banzai_floyds/tests/test_extract.py b/banzai_floyds/tests/test_extract.py index fd8bfe6..5ad1050 100644 --- a/banzai_floyds/tests/test_extract.py +++ b/banzai_floyds/tests/test_extract.py @@ -1,94 +1,53 @@ import numpy as np from banzai import context from banzai_floyds.tests.utils import generate_fake_science_frame -from banzai_floyds.extract import Extractor, fit_profile_centers, fit_profile_width, fit_background, extract -from banzai_floyds.extract import get_wavelength_bins, bin_data -from banzai_floyds.utils.profile_utils import profile_fits_to_data +from banzai_floyds.extract import Extractor, extract, set_extraction_region +from banzai_floyds.utils.binning_utils import bin_data from collections import namedtuple +from astropy.table import Table +from numpy.polynomial.legendre import Legendre -from banzai_floyds.utils.fitting_utils import fwhm_to_sigma - -def test_wavelength_bins(): - fakeWavelengths = namedtuple('fakeWavelengths', 'line_tilts bin_edges orders') - fakeOrders = namedtuple('fakeOrders', 'order_heights') - input_wavelengths = fakeWavelengths(line_tilts=np.array([0.0, 0.0]), - bin_edges=[np.arange(0.0, 100.5, step=1), np.arange(100.0, 200.5, step=1)], - orders=fakeOrders(order_heights=np.zeros(2))) - wavelength_bins = get_wavelength_bins(input_wavelengths) - for i, bins in enumerate(wavelength_bins): - expected = np.arange(0.5 + (i * 100.0), 100.0 * (i + 1), step=1) - np.testing.assert_allclose(bins['center'], expected) - np.testing.assert_allclose(bins['width'], 1.0) - - input_wavelengths = fakeWavelengths(line_tilts=np.array([45.0, 45.0]), - bin_edges=[np.arange(0.0, 100.5, step=1), np.arange(100.0, 200.5, step=1)], - orders=fakeOrders(order_heights=np.ones(2) * 10.0 * np.sqrt(2.0))) - wavelength_bins = get_wavelength_bins(input_wavelengths) - for i, bins in enumerate(wavelength_bins): - expected = np.arange(0.5 + (i * 100.0), 100.0 * (i + 1), step=1)[5:-5] - np.testing.assert_allclose(bins['center'], expected) - np.testing.assert_allclose(bins['width'], 1.0) - - -def test_tracing(): - np.random.seed(3656454) - # Make a fake frame with a gaussian profile and make sure we recover the input - fake_frame = generate_fake_science_frame() - wavelength_bins = get_wavelength_bins(fake_frame.wavelengths) - binned_data = bin_data(fake_frame.data, fake_frame.uncertainty, fake_frame.wavelengths, - fake_frame.orders, wavelength_bins) - fitted_profile_centers = fit_profile_centers(binned_data, profile_width=4) - for fitted_center, input_center in zip(fitted_profile_centers, fake_frame.input_profile_centers): - x = np.arange(fitted_center.domain[0], fitted_center.domain[1] + 1) - np.testing.assert_allclose(fitted_center(x), input_center(x), rtol=0.00, atol=0.2) - - -def test_profile_width_fitting(): - np.random.seed(1242315) - fake_frame = generate_fake_science_frame(include_sky=True) - wavelength_bins = get_wavelength_bins(fake_frame.wavelengths) - binned_data = bin_data(fake_frame.data, fake_frame.uncertainty, fake_frame.wavelengths, - fake_frame.orders, wavelength_bins) - fitted_widths = fit_profile_width(binned_data, fake_frame.input_profile_centers) - for fitted_width, bins in zip(fitted_widths, wavelength_bins): - x = np.arange(bins['center'][0], bins['center'][-1] + 1) - np.testing.assert_allclose(fitted_width(x), fwhm_to_sigma(fake_frame.input_profile_width), rtol=0.03) - - -def test_background_fitting(): - np.random.seed(234515) - fake_frame = generate_fake_science_frame(include_sky=True) - wavelength_bins = get_wavelength_bins(fake_frame.wavelengths) - binned_data = bin_data(fake_frame.data, fake_frame.uncertainty, fake_frame.wavelengths, - fake_frame.orders, wavelength_bins) - fake_profile_width_funcs = [lambda _: fwhm_to_sigma(fake_frame.input_profile_width) - for _ in fake_frame.input_profile_centers] - fitted_background = fit_background(binned_data, fake_frame.input_profile_centers, fake_profile_width_funcs) - fake_frame.background = fitted_background - binned_fitted_background = bin_data(fake_frame.background, fake_frame.uncertainty, fake_frame.wavelengths, - fake_frame.orders, wavelength_bins) - binned_input_sky = bin_data(fake_frame.input_sky, fake_frame.uncertainty, fake_frame.wavelengths, - fake_frame.orders, wavelength_bins) - np.testing.assert_allclose(binned_fitted_background['data'].groups.aggregate(np.sum), - binned_input_sky['data'].groups.aggregate(np.sum), - rtol=0.033) +def test_extraction_region(): + FakeImage = namedtuple('FakeImage', ['binned_data', 'meta', 'extraction_windows']) + nx, ny = 103, 101 + x, y = np.meshgrid(np.arange(nx), np.arange(ny)) + order_centers = [20, 60] + order_height = 21 + orders = np.zeros_like(x) + for order_id in [1, 2]: + in_order = order_centers[order_id - 1] - order_height // 2 <= y + in_order = np.logical_and(y <= order_centers[order_id - 1] + order_height // 2, in_order) + orders[in_order] = order_id + profile_sigma = 1.0 + y_profile = np.zeros_like(x) + for order_id in [1, 2]: + y_profile[orders == order_id] = y[orders == order_id] - order_centers[order_id - 1] + binned_data = Table({'x': x.ravel(), 'y': y.ravel(), 'order': orders.ravel(), + 'profile_sigma': profile_sigma * np.ones(x.size), + 'y_profile': y_profile.ravel()}) + fake_data = FakeImage(binned_data, {}, [[-5.0, 5.0], [-5.0, 5.0]]) + set_extraction_region(fake_data) + # The extraction should be +- 5 pixels high so there should be 11 pixels in the extraction region + for order in [1, 2]: + in_order = fake_data.binned_data['order'] == order + assert np.sum(fake_data.binned_data['extraction_window'][in_order]) == 11 * nx def test_extraction(): - np.random.seed(723422) + np.random.seed(3515) fake_frame = generate_fake_science_frame(include_sky=False) - fake_frame.wavelength_bins = get_wavelength_bins(fake_frame.wavelengths) fake_frame.binned_data = bin_data(fake_frame.data, fake_frame.uncertainty, fake_frame.wavelengths, - fake_frame.orders, fake_frame.wavelength_bins) - fake_profile_width_funcs = [lambda _: fwhm_to_sigma(fake_frame.input_profile_width) - for _ in fake_frame.input_profile_centers] - fake_frame.profile_fits = fake_frame.input_profile_centers, fake_profile_width_funcs - fake_frame.profile = profile_fits_to_data(fake_frame.data.shape, fake_frame.input_profile_centers, - fake_profile_width_funcs, fake_frame.orders, fake_frame.wavelengths.data) + fake_frame.orders) + fake_profile_width_funcs = [Legendre(fake_frame.input_profile_sigma,) for _ in fake_frame.input_profile_centers] + fake_frame.profile = fake_frame.input_profile_centers, fake_profile_width_funcs, None + fake_frame.binned_data['background'] = 0.0 + + fake_frame.extraction_windows = [[-5.0, 5.0], [-5.0, 5.0]] + set_extraction_region(fake_frame) extracted = extract(fake_frame.binned_data) - np.testing.assert_allclose(extracted['fluxraw'], 10000.0, rtol=0.05) + np.testing.assert_allclose(extracted['fluxraw'], 10000.0, rtol=0.06) np.testing.assert_allclose(extracted['fluxraw'] / extracted['fluxrawerr'], 100.0, rtol=0.10) @@ -96,13 +55,11 @@ def test_full_extraction_stage(): np.random.seed(192347) input_context = context.Context({}) frame = generate_fake_science_frame(flat_spectrum=False, include_sky=True) - frame.wavelength_bins = get_wavelength_bins(frame.wavelengths) - frame.binned_data = bin_data(frame.data, frame.uncertainty, frame.wavelengths, - frame.orders, frame.wavelength_bins) - fake_profile_width_funcs = [lambda _: fwhm_to_sigma(frame.input_profile_width) for _ in frame.input_profile_centers] - frame.profile_fits = frame.input_profile_centers, fake_profile_width_funcs - frame.profile = profile_fits_to_data(frame.data.shape, frame.input_profile_centers, fake_profile_width_funcs, - frame.orders, frame.wavelengths.data) + frame.binned_data = bin_data(frame.data, frame.uncertainty, frame.wavelengths, frame.orders) + fake_profile_width_funcs = [Legendre(frame.input_profile_sigma,) for _ in frame.input_profile_centers] + frame.profile = frame.input_profile_centers, fake_profile_width_funcs, None + frame.binned_data['background'] = frame.input_sky[frame.binned_data['y'].astype(int), + frame.binned_data['x'].astype(int)] stage = Extractor(input_context) frame = stage.do_stage(frame) expected = np.interp(frame['EXTRACTED'].data['wavelength'], frame.input_spectrum_wavelengths, frame.input_spectrum) diff --git a/banzai_floyds/tests/test_fringing.py b/banzai_floyds/tests/test_fringing.py index bf940f6..610ae29 100644 --- a/banzai_floyds/tests/test_fringing.py +++ b/banzai_floyds/tests/test_fringing.py @@ -59,11 +59,11 @@ def test_create_super_fringe(): # Trim off the edges of the order due to edge effects trimmed_order = frames[0].orders.new(frames[0].orders.order_heights - 20) in_order = trimmed_order.data == 1 - np.testing.assert_allclose(frame.data[in_order], frames[0].input_fringe[in_order], rtol=0.02) + np.testing.assert_allclose(frame.data[in_order], frames[0].input_fringe[in_order], rtol=0.02, atol=0.02) def test_correct_fringe(): - np.random.seed(291523) + np.random.seed(91275) # Make fake fringe data and using a fixed sin fringe pattern but offset in the image frame = generate_fake_science_frame(flat_spectrum=False, include_sky=True, fringe=True, fringe_offset=3.5, include_super_fringe=True) diff --git a/banzai_floyds/tests/test_orders.py b/banzai_floyds/tests/test_orders.py index c1d059b..b2eb050 100644 --- a/banzai_floyds/tests/test_orders.py +++ b/banzai_floyds/tests/test_orders.py @@ -64,8 +64,9 @@ def test_order_solver_stage(): order_solver = OrderSolver(FakeContext()) order_solver.ORDER_HEIGHT = order_height order_solver.CENTER_CUT_WIDTH = 21 - order_solver.ORDER_REGIONS = [(0, nx), (0, nx)] - image = FLOYDSObservationFrame([CCDData(data=data, uncertainty=error, meta=fits.Header({}))], 'foo.fits') + order_solver.ORDER_REGIONS = {'ogg': [(0, nx), (0, nx)]} + image = FLOYDSObservationFrame([CCDData(data=data, uncertainty=error, + meta=fits.Header({'SITEID': 'ogg'}))], 'foo.fits') image = order_solver.do_stage(image) for i, input_region in enumerate(input_order_regions): diff --git a/banzai_floyds/tests/test_profile.py b/banzai_floyds/tests/test_profile.py new file mode 100644 index 0000000..25c705b --- /dev/null +++ b/banzai_floyds/tests/test_profile.py @@ -0,0 +1,29 @@ +from banzai_floyds.profile import fit_profile_centers, fit_profile_sigma +from banzai_floyds.tests.utils import generate_fake_science_frame +from banzai_floyds.utils.binning_utils import bin_data +import numpy as np + + +def test_tracing(): + np.random.seed(20802345) + # Make a fake frame with a gaussian profile and make sure we recover the input + fake_frame = generate_fake_science_frame() + binned_data = bin_data(fake_frame.data, fake_frame.uncertainty, fake_frame.wavelengths, + fake_frame.orders) + domains = [center.domain for center in fake_frame.input_profile_centers] + fitted_profile_centers, measured_points = fit_profile_centers(binned_data, domains, profile_fwhm=4) + for fitted_center, input_center in zip(fitted_profile_centers, fake_frame.input_profile_centers): + x = np.arange(fitted_center.domain[0], fitted_center.domain[1] + 1) + np.testing.assert_allclose(fitted_center(x), input_center(x), atol=0.025, rtol=0.02) + + +def test_profile_width_fitting(): + np.random.seed(1242315) + fake_frame = generate_fake_science_frame(include_sky=True) + binned_data = bin_data(fake_frame.data, fake_frame.uncertainty, fake_frame.wavelengths, + fake_frame.orders) + domains = [center.domain for center in fake_frame.input_profile_centers] + fitted_widths, measured_points = fit_profile_sigma(binned_data, fake_frame.input_profile_centers, domains) + for fitted_width in fitted_widths: + x = np.arange(fitted_width.domain[0], fitted_width.domain[1] + 1) + np.testing.assert_allclose(fitted_width(x), fake_frame.input_profile_sigma, rtol=0.03) diff --git a/banzai_floyds/tests/test_wavelengths.py b/banzai_floyds/tests/test_wavelengths.py index 9aa7623..d4cd791 100644 --- a/banzai_floyds/tests/test_wavelengths.py +++ b/banzai_floyds/tests/test_wavelengths.py @@ -45,24 +45,24 @@ def test_linear_wavelength_solution(): np.random.seed(890154) min_wavelength = 3200 dispersion = 2.5 - line_width = 3 + line_sigma = 3 input_spectrum, lines, test_lines = build_random_spectrum(min_wavelength=min_wavelength, dispersion=dispersion, - line_sigma=line_width) + line_sigma=line_sigma) linear_model = linear_wavelength_solution(input_spectrum, 0.01 * np.ones_like(input_spectrum), lines, - dispersion, sigma_to_fwhm(line_width), np.arange(4000, 5001)) + dispersion, sigma_to_fwhm(line_sigma), np.arange(4000, 5001)) assert linear_model(0) == min_wavelength def test_identify_peaks(): # use well-behaved seed seed = 76856 - line_width = 3 + line_sigma = 3 line_sep = 10 - input_spectrum, lines, test_lines = build_random_spectrum(seed=seed, line_sigma=line_width, nlines=6) + input_spectrum, lines, test_lines = build_random_spectrum(seed=seed, line_sigma=line_sigma, nlines=6) recovered_peaks = identify_peaks(input_spectrum, 0.01 * np.ones_like(input_spectrum), - sigma_to_fwhm(line_width), line_sep) + sigma_to_fwhm(line_sigma), line_sep) # Need to figure out how to handle blurred lines and combined peaks for peak in recovered_peaks: @@ -74,13 +74,13 @@ def test_correlate_peaks(): min_wavelength = 3200 dispersion = 2.5 - line_width = 3 + line_sigma = 3 used_lines = 6 input_spectrum, lines, test_peaks = build_random_spectrum(min_wavelength=min_wavelength, dispersion=dispersion, - line_sigma=line_width) + line_sigma=line_sigma) linear_model = linear_wavelength_solution(input_spectrum, 0.01 * np.ones_like(input_spectrum), lines, - dispersion, sigma_to_fwhm(line_width), np.arange(4000, 5001)) + dispersion, sigma_to_fwhm(line_sigma), np.arange(4000, 5001)) # find corresponding lines with lines missing match_threshold = 5 @@ -106,21 +106,47 @@ def test_correlate_peaks(): def test_refine_peak_centers(): # use well-behaved seed seed = 75827 - line_width = 3 + line_sigma = 3 line_sep = 10 - input_spectrum, lines, test_lines = build_random_spectrum(seed=seed, line_sigma=line_width) + input_spectrum, lines, test_lines = build_random_spectrum(seed=seed, line_sigma=line_sigma) recovered_peaks = identify_peaks(input_spectrum, 0.01 * np.ones_like(input_spectrum), - sigma_to_fwhm(line_width), line_sep) + sigma_to_fwhm(line_sigma), line_sep) fit_list = refine_peak_centers(input_spectrum, 0.01 * np.ones_like(input_spectrum), - recovered_peaks, sigma_to_fwhm(line_width)) + recovered_peaks, sigma_to_fwhm(line_sigma)) # Need to figure out how to handle blurred lines and overlapping peaks. for fit in fit_list: assert np.min(abs(test_lines - fit)) < 0.2 +def test_refine_peak_centers_with_background(): + x = np.arange(2500.0, dtype=float) + flux = np.zeros_like(x, dtype=float) + lines = [] + line_sigma = 2.5 + read_noise = 10.0 + np.random.seed(2193457) + + # Only choose 10 lines here. 20 basically guaruntees that the peaks will overlap + # Thanks birthday problem in statistics + for line in np.random.uniform(10, 2490, size=10): + lines.append(line) + flux += gauss(x, line, line_sigma) * np.random.uniform(1000, 10000) + lines = np.array(lines) + background_poly = Legendre([100.0, 5.0, 10.0, -6], domain=(0, 2501)) + flux += background_poly(x) + flux = np.random.poisson(flux).astype(float) + flux += np.random.normal(0, read_noise, size=flux.shape) + flux_error = np.sqrt(read_noise ** 2 + np.sqrt(np.abs(flux))) + + fit_list = refine_peak_centers(flux, flux_error, lines, sigma_to_fwhm(line_sigma) * 0.7) + + for fit_line in fit_list: + assert np.min(np.abs(lines - fit_line)) < 0.2 + + def test_2d_wavelength_solution(): nx = 501 data = np.zeros((512, nx)) @@ -131,12 +157,14 @@ def test_2d_wavelength_solution(): trace_center = Legendre(input_center_params, domain=(0, data.shape[1] - 1)) input_order_region = order_region(order_height, trace_center, data.shape) + bkg_order_x = 4 + bkg_order_y = 2 min_wavelength = 3200.0 seed = 76856 - line_width = 3 * (2 * np.sqrt(2 * np.log(2))) + line_sigma = 3 dispersion = 2.5 tilt = 15 # degrees - input_spectrum, lines, test_lines = build_random_spectrum(seed=seed, line_sigma=3, + input_spectrum, lines, test_lines = build_random_spectrum(seed=seed, line_sigma=line_sigma, dispersion=dispersion, nlines=6, nx=nx) x1d = np.arange(data.shape[1], dtype=float) x2d, y2d = np.meshgrid(x1d, np.arange(data.shape[0], dtype=float)) @@ -144,19 +172,23 @@ def test_2d_wavelength_solution(): data[input_order_region] = np.interp(tilted_x[input_order_region], x1d, input_spectrum) error[data >= 1.0] = 0.01 * data[data >= 1.0] - # Convert between poly1d and legendre conventions - converted_input_polynomial = Legendre((min_wavelength, dispersion), domain=(0, data.shape[1] - 1), - window=(0, data.shape[1] - 1)).convert(domain=(0, data.shape[1] - 1)) + initial_slope = dispersion * (np.max(x2d) - np.min(x2d)) / 2.0 + # c0 for the Legendre polynomials is in the center of the domain + # Ineresting that the extra offset you need is the same as the slope + initial_offset = min_wavelength + dispersion * (np.max(x2d) - np.min(x2d)) / 2.0 # Note that weight function has the line width in angstroms whereas our line width here is in pixels params = full_wavelength_solution(data[input_order_region], error[input_order_region], x2d[input_order_region], - (y2d - trace_center(x1d))[input_order_region], converted_input_polynomial.coef, - tilt, dispersion * line_width, lines) + (y2d - trace_center(x1d))[input_order_region], (initial_offset, initial_slope), + tilt, sigma_to_fwhm(line_sigma), lines, + background_order_x=bkg_order_x, + background_order_y=bkg_order_y) fit_tilt, fit_line_width, *fit_polynomial_coefficients = params # Assert that the best fit parameters are close to the inputs np.testing.assert_allclose(tilt, fit_tilt, atol=0.1) - np.testing.assert_allclose(dispersion * line_width, fit_line_width, atol=0.3) - np.testing.assert_allclose(converted_input_polynomial.coef, fit_polynomial_coefficients, atol=0.1) + np.testing.assert_allclose(line_sigma, fwhm_to_sigma(fit_line_width), atol=0.3) + np.testing.assert_allclose((initial_offset, initial_slope), fit_polynomial_coefficients[:2], + atol=0.1) def generate_fake_arc_frame(): @@ -172,7 +204,7 @@ def generate_fake_arc_frame(): # make a reasonable wavelength model wavelength_model1 = Legendre((7425, 2950.5, 20., -5., 1.), domain=(0, 1700)) wavelength_model2 = Legendre((4573.5, 1294.6, 15.), domain=(475, 1975)) - line_widths = [CalibrateWavelengths.INITIAL_LINE_WIDTHS[i] for i in range(1, 3)] + line_fwhms = [CalibrateWavelengths.INITIAL_LINE_FWHMS['ogg'][i] for i in range(1, 3)] line_tilts = [CalibrateWavelengths.INITIAL_LINE_TILTS[i] for i in range(1, 3)] dispersions = [CalibrateWavelengths.INITIAL_DISPERSIONS[i] for i in range(1, 3)] flux_scale = 80000.0 @@ -180,11 +212,11 @@ def generate_fake_arc_frame(): # Calculate the tilted coordinates x2d, y2d = np.meshgrid(np.arange(nx), np.arange(ny)) - for order_center, wavelength_model, tilt, line_width, dispersion in \ + for order_center, wavelength_model, tilt, line_fwhm, dispersion in \ zip((order1, order2), (wavelength_model1, wavelength_model2), line_tilts, - line_widths, + line_fwhms, dispersions): input_order_region = order_region(order_height, order_center, (ny, nx)) tilted_x = tilt_coordinates(tilt, x2d[input_order_region], @@ -196,9 +228,10 @@ def generate_fake_arc_frame(): if line['line_strength'] == 'nan': continue wavelengths = wavelength_model(tilted_x) - line_sigma = fwhm_to_sigma(line_width) - data[input_order_region] += line['line_strength'] * gauss(wavelengths, - line['wavelength'], line_sigma) * flux_scale + line_sigma = fwhm_to_sigma(line_fwhm) + line_data = gauss(wavelengths, line['wavelength'], dispersion * line_sigma) * flux_scale + line_data *= line['line_strength'] * flux_scale + data[input_order_region] += line_data # Add poisson noise errors += np.sqrt(data) data = np.random.poisson(data).astype(float) @@ -207,10 +240,10 @@ def generate_fake_arc_frame(): errors = np.sqrt(errors * errors + read_noise) data += np.random.normal(0.0, read_noise, size=(ny, nx)) # save the data, errors, and orders to a floyds frame - frame = FLOYDSObservationFrame([CCDData(data, fits.Header({}), uncertainty=errors)], 'foo.fits') + frame = FLOYDSObservationFrame([CCDData(data, fits.Header({'SITEID': 'ogg'}), uncertainty=errors)], 'foo.fits') frame.orders = orders # return the test frame and the input wavelength solution - return frame, {'models': [wavelength_model1, wavelength_model2], 'tilts': line_tilts, 'widths': line_widths} + return frame, {'models': [wavelength_model1, wavelength_model2], 'tilts': line_tilts, 'fwhms': line_fwhms} def test_full_wavelength_solution(): @@ -219,8 +252,8 @@ def test_full_wavelength_solution(): frame, input_wavelength_solution = generate_fake_arc_frame() stage = CalibrateWavelengths(input_context) frame = stage.do_stage(frame) - for fit_width, input_width in zip(frame.wavelengths.line_widths, input_wavelength_solution['widths']): - np.testing.assert_allclose(fit_width, input_width, atol=0.1) + for fit_fwhm, input_fwhm in zip(frame.wavelengths.line_fwhms, input_wavelength_solution['fwhms']): + np.testing.assert_allclose(fit_fwhm, input_fwhm, atol=0.1) for fit_tilt, input_tilt, in zip(frame.wavelengths.line_tilts, input_wavelength_solution['tilts']): np.testing.assert_allclose(fit_tilt, input_tilt, atol=0.1) @@ -255,7 +288,7 @@ def test_empty_calibrate_wavelengths_stage(): errors = np.sqrt(errors * errors + read_noise) data += np.random.normal(0.0, read_noise, size=(ny, nx)) # save the data, errors, and orders to a floyds Calibration frame - frame = FLOYDSCalibrationFrame([CCDData(data, fits.Header({}), uncertainty=errors)], 'foo.fits') + frame = FLOYDSCalibrationFrame([CCDData(data, fits.Header({'SITEID': 'ogg'}), uncertainty=errors)], 'foo.fits') frame.orders = orders calibrate_wavelengths = CalibrateWavelengths(input_context) diff --git a/banzai_floyds/tests/utils.py b/banzai_floyds/tests/utils.py index 86f442e..2870d35 100644 --- a/banzai_floyds/tests/utils.py +++ b/banzai_floyds/tests/utils.py @@ -18,6 +18,7 @@ from astropy.table import Table from banzai.data import HeaderOnly from astropy.modeling.models import Polynomial1D +import json SKYLINE_LIST = ascii.read(pkg_resources.resource_filename('banzai_floyds.tests', 'data/skylines.dat')) @@ -66,18 +67,18 @@ def generate_fake_science_frame(include_sky=False, flat_spectrum=True, fringe=Fa """ nx = 2048 ny = 512 - INITIAL_LINE_WIDTHS = {1: 15.6, 2: 8.6} + INITIAL_LINE_FWHMS = {1: 15.6, 2: 8.6} # DISPERSIONS = {1: 3.13, 2: 1.72} # Tilts in degrees measured counterclockwise (right-handed coordinates) INITIAL_LINE_TILTS = {1: 8., 2: 8.} - profile_width = 4 + profile_fwhm = 10.0 order_height = 93 read_noise = 6.5 - line_widths = [15.6, 8.6] + line_fwhms_angstroms = [15.6, 8.6] input_fringe_shift = fringe_offset order1 = Legendre((135.4, 81.8, 45.2, -11.4), domain=(0, 1700)) - order2 = Legendre((410, 17, 63, -12), domain=(475, 1975)) + order2 = Legendre((380, 17, 63, -12), domain=(475, 1975)) data = np.zeros((ny, nx)) orders = Orders([order1, order2], (ny, nx), [order_height, order_height]) expanded_order_height = order_height + 20 @@ -85,21 +86,19 @@ def generate_fake_science_frame(include_sky=False, flat_spectrum=True, fringe=Fa wavelength_model1 = Legendre((7487.2, 2662.3, 20., -5., 1.), domain=(0, 1700)) wavelength_model2 = Legendre((4573.5, 1294.6, 15.), domain=(475, 1975)) - trace1 = Legendre((5, 10, 4), - domain=(wavelength_model1(0), wavelength_model1(1700))) - trace2 = Legendre((-10, -8, -3), - domain=(wavelength_model2(475), wavelength_model2(1975))) + trace1 = Legendre((5, 10, 4), domain=(wavelength_model1(0), wavelength_model1(1700))) + trace2 = Legendre((-10, -8, -3), domain=(wavelength_model2(475), wavelength_model2(1975))) profile_centers = [trace1, trace2] # Work out the wavelength solution for larger than the typical order size so that we # can shift the fringe pattern up and down orders.order_heights = np.ones(2) * (order_height + 5) wavelengths = WavelengthSolution([wavelength_model1, wavelength_model2], - [INITIAL_LINE_WIDTHS[i + 1] for i in range(2)], + [INITIAL_LINE_FWHMS[i + 1] for i in range(2)], [INITIAL_LINE_TILTS[i + 1] for i in range(2)], orders=orders.new(expanded_order_height)) x2d, y2d = np.meshgrid(np.arange(nx), np.arange(ny)) - profile_sigma = fwhm_to_sigma(profile_width) + profile_sigma = fwhm_to_sigma(profile_fwhm) flux_normalization = 10000.0 sky_continuum = 800.0 @@ -108,7 +107,7 @@ def generate_fake_science_frame(include_sky=False, flat_spectrum=True, fringe=Fa input_sky = np.zeros_like(data) input_lines = np.random.uniform(3200, 9500, size=10) input_line_strengths = np.random.uniform(20000.0, 200000.0, size=10) - input_line_widths = np.random.uniform(8, 30, size=10) + emission_line_fwhms = np.random.uniform(8, 30, size=10) continuum_polynomial = Legendre((1.0, 0.3, -0.2), domain=(3000.0, 12000.0)) # normalize out the polynomial so it is close to 1 continuum_polynomial /= np.mean( @@ -128,12 +127,10 @@ def generate_fake_science_frame(include_sky=False, flat_spectrum=True, fringe=Fa input_spectrum = flux_normalization input_spectrum *= continuum_polynomial(wavelengths.data[in_order]) input_spectrum *= profile - for input_line, strength, width in zip(input_lines, - input_line_strengths, - input_line_widths): + for input_line, strength, fhwm in zip(input_lines, input_line_strengths, emission_line_fwhms): # add some random emission lines input_spectrum += strength * gauss(wavelengths.data[in_order], - input_line, width) * profile + input_line, fwhm_to_sigma(fhwm)) * profile data[in_order] += input_spectrum data[in_order] += background @@ -143,7 +140,7 @@ def generate_fake_science_frame(include_sky=False, flat_spectrum=True, fringe=Fa sky_spectrum = np.zeros_like(sky_wavelengths) + sky_continuum for line in SKYLINE_LIST: line_spread = gauss(sky_wavelengths, line['wavelength'], - fwhm_to_sigma(line_widths[i])) + fwhm_to_sigma(line_fwhms_angstroms[i])) sky_spectrum += line['line_strength'] * line_spread * sky_normalization # Make a slow illumination gradient to make sure things work even if the sky is not flat illumination = 100 * gauss(slit_coordinates[in_order], 0.0, 48) @@ -175,7 +172,7 @@ def generate_fake_science_frame(include_sky=False, flat_spectrum=True, fringe=Fa uncertainty=errors)], 'foo.fits') frame.input_profile_centers = profile_centers - frame.input_profile_width = profile_width + frame.input_profile_sigma = profile_sigma frame.wavelengths = wavelengths frame.orders = orders frame.instrument = SimpleNamespace(site='ogg', camera='en02') @@ -189,14 +186,11 @@ def generate_fake_science_frame(include_sky=False, flat_spectrum=True, fringe=Fa frame.fringe = super_fringe_frame if not flat_spectrum: frame.input_spectrum_wavelengths = np.arange(3000.0, 12000.0, 0.1) - frame.input_spectrum = flux_normalization * continuum_polynomial( - frame.input_spectrum_wavelengths) - for input_line, strength, width in zip(input_lines, - input_line_strengths, - input_line_widths): + frame.input_spectrum = flux_normalization * continuum_polynomial(frame.input_spectrum_wavelengths) + for input_line, strength, fhwm in zip(input_lines, input_line_strengths, emission_line_fwhms): # add some random emission lines frame.input_spectrum += strength * gauss(frame.input_spectrum_wavelengths, - input_line, width) + input_line, fwhm_to_sigma(fhwm)) return frame @@ -254,6 +248,23 @@ def generate_fake_extracted_frame(do_telluric=False, do_sensitivity=True): return frame +def load_manual_region(region_filename, site_id, order_id, shape, order_height): + with open(region_filename) as region_file: + region_fits = json.load(region_file) + + # Ensure that overlap is 99% between the manual fits and the automatic order fits + manual_order_region = np.zeros(shape, dtype=bool) + x2d, y2d = np.meshgrid(np.arange(shape[1]), np.arange(shape[0])) + order_fit = Legendre(coef=region_fits[site_id][order_id]['coef'], + domain=region_fits[site_id][order_id]['domain'], + window=region_fits[site_id][order_id]['window']) + order_center = np.round(order_fit(x2d)).astype(int) + manual_order_region = np.logical_and(x2d >= order_fit.domain[0], x2d <= order_fit.domain[1]) + manual_order_region = np.logical_and(manual_order_region, y2d >= order_center - order_height // 2) + manual_order_region = np.logical_and(manual_order_region, y2d <= order_center + order_height // 2) + return manual_order_region + + class TestCalibrationFrame(FLOYDSCalibrationFrame): def write(self, context): # Short circuit the write method so we don't actually write anything during testing diff --git a/banzai_floyds/utils/binning_utils.py b/banzai_floyds/utils/binning_utils.py new file mode 100644 index 0000000..aaa1970 --- /dev/null +++ b/banzai_floyds/utils/binning_utils.py @@ -0,0 +1,63 @@ +from astropy.table import Table, vstack +import numpy as np + + +def bin_data(data, uncertainty, wavelengths, orders, mask=None): + if mask is None: + mask = np.zeros_like(data, dtype=int) + binned_data = None + x2d, y2d = np.meshgrid(np.arange(data.shape[1]), np.arange(data.shape[0])) + for order_id, order_bins in zip(orders.order_ids, wavelengths.bin_edges): + in_order = orders.data == order_id + + y_order = y2d[in_order] - orders.center(x2d[in_order])[order_id - 1] + data_table = Table({'data': data[in_order], 'uncertainty': uncertainty[in_order], + 'mask': mask[in_order], 'wavelength': wavelengths.data[in_order], + 'x': x2d[in_order], 'y': y2d[in_order], 'y_order': y_order}) + bin_number = np.digitize(data_table['wavelength'], order_bins) + bin_centers = (order_bins[1:] + order_bins[:-1]) / 2.0 + # Append the first and last bin centers as zero to flag that the + # edge pixels aren't in a bin + bin_centers = np.hstack([0, bin_centers, 0]) + bin_widths = (order_bins[1:] - order_bins[:-1]) + bin_widths = np.hstack([0, bin_widths, 0]) + data_table['wavelength_bin'] = bin_centers[bin_number] + data_table['wavelength_bin_width'] = bin_widths[bin_number] + data_table['order'] = order_id + if binned_data is None: + binned_data = data_table + else: + binned_data = vstack([binned_data, data_table]) + return binned_data.group_by(('order', 'wavelength_bin')) + + +def combine_wavelength_bins(wavelength_bins): + """ + Combine wavelength bins, taking the small delta (higher resolution) bins + """ + # Find the overlapping bins + # Assume that the orders are basically contiguous and monotonically increasing + wavelength_regions = [(min(order_bins['center']), max(order_bins['center'])) for order_bins in wavelength_bins] + + # Assume the smaller of the bin widths are from the blue order + # We assume here we only have 2 orders and that one order does not fully encompass the other + min_wavelength = min(np.array(wavelength_regions).ravel()) + blue_order_index = 0 if min_wavelength in wavelength_regions[0]['center'] else 1 + red_order_index = 0 if blue_order_index else 1 + + overlap_end_index = np.min(np.argwhere(wavelength_bins[red_order_index]['center'] > + np.max(wavelength_regions[blue_order_index]['center']))) + # clean up the middle partial overlaps + middle_bin_upper = wavelength_bins[red_order_index]['center'][overlap_end_index + 1] + middle_bin_upper -= wavelength_bins[red_order_index]['width'][overlap_end_index] / 2.0 + middle_bin_lower = wavelength_bins[blue_order_index]['center'][-1] + middle_bin_lower += wavelength_bins[blue_order_index]['width'] / 2.0 + middle_bin_center = (middle_bin_upper + middle_bin_lower) / 2.0 + middle_bin_width = middle_bin_upper - middle_bin_lower + overlap_end_index += 1 + new_bins = {'center': np.hstack([wavelength_bins[blue_order_index]['center'], [middle_bin_center], + wavelength_bins[red_order_index][overlap_end_index:]['center']]), + 'width': np.hstack([wavelength_bins[blue_order_index]['center'], + [middle_bin_width], + wavelength_bins[red_order_index][overlap_end_index:]['center']])} + return Table(new_bins) diff --git a/banzai_floyds/utils/profile_utils.py b/banzai_floyds/utils/profile_utils.py index ef04556..4359842 100644 --- a/banzai_floyds/utils/profile_utils.py +++ b/banzai_floyds/utils/profile_utils.py @@ -1,16 +1,32 @@ import numpy as np from banzai_floyds.utils.fitting_utils import gauss +from numpy.polynomial.legendre import Legendre -def profile_fits_to_data(data_shape, profile_centers, profile_widths, orders, wavelengths_data): +def profile_fits_to_data(data_shape, profile_centers, profile_sigmas, orders, wavelengths_data): profile_data = np.zeros(data_shape) x2d, y2d = np.meshgrid(np.arange(profile_data.shape[1]), np.arange(profile_data.shape[0])) - order_iter = zip(orders.order_ids, profile_centers, profile_widths, orders.center(x2d)) - for order_id, profile_center, profile_width, order_center in order_iter: + order_iter = zip(orders.order_ids, profile_centers, profile_sigmas, orders.center(x2d)) + for order_id, profile_center, profile_sigma, order_center in order_iter: in_order = orders.data == order_id wavelengths = wavelengths_data[in_order] # TODO: Make sure this is normalized correctly # Note that the widths in the value set here are sigma and not fwhm profile_data[in_order] = gauss(y2d[in_order] - order_center[in_order], - profile_center(wavelengths), profile_width(wavelengths)) + profile_center(wavelengths), profile_sigma(wavelengths)) return profile_data + + +def load_profile_fits(hdu): + centers = [] + sigmas = [] + for order in [1, 2]: + center_order = hdu.meta[f'O{order}CTRO'] + width_order = hdu.meta[f'O{order}SIGO'] + center_coeffs = [hdu.meta[f'O{order}CTR{i:02}'] for i in range(center_order + 1)] + sigma_coeffs = [hdu.meta[f'O{order}SIG{i:02}'] for i in range(width_order + 1)] + center_poly = Legendre(center_coeffs, domain=[hdu.meta[f'O{order}CTRDM0'], hdu.meta[f'O{order}CTRDM1']]) + sigma_poly = Legendre(sigma_coeffs, domain=[hdu.meta[f'O{order}SIGDM0'], hdu.meta[f'O{order}SIGDM1']]) + centers.append(center_poly) + sigmas.append(sigma_poly) + return centers, sigmas, hdu.data diff --git a/banzai_floyds/utils/tests/__init__.py b/banzai_floyds/utils/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/banzai_floyds/utils/wavelength_utils.py b/banzai_floyds/utils/wavelength_utils.py index 7cbfd25..7f5dbd8 100644 --- a/banzai_floyds/utils/wavelength_utils.py +++ b/banzai_floyds/utils/wavelength_utils.py @@ -4,8 +4,8 @@ class WavelengthSolution: - def __init__(self, polymomials, line_widths, line_tilts, orders): - self._line_widths = line_widths + def __init__(self, polymomials, line_fwhms, line_tilts, orders): + self._line_fwhms = line_fwhms self._polynomials = polymomials self._line_tilts = line_tilts self._orders = orders @@ -25,13 +25,13 @@ def data(self): def to_header(self): header = fits.Header() - for i, (polynomial, width, tilt) in enumerate(zip(self._polynomials, self._line_widths, self._line_tilts)): - header[f'LINWIDE{i + 1}'] = width - header[f'LINTILT{i + 1}'] = tilt - header[f'POLYORD{i + 1}'] = polynomial.degree() - header[f'POLYDOM{i + 1}'] = str(list(polynomial.domain)) + for i, (polynomial, fwhm, tilt) in enumerate(zip(self._polynomials, self._line_fwhms, self._line_tilts)): + header[f'FWHM{i + 1}'] = fwhm, f'Line spread FWHM in angstroms for order {i}' + header[f'TILT{i + 1}'] = tilt, f'Tilt angle in deg for order {i}' + header[f'POLYORD{i + 1}'] = polynomial.degree(), f'Wavelength polynomial order for order {i}' + header[f'POLYDOM{i + 1}'] = str(list(polynomial.domain)), f'Wavelength domain order for order {i}' for j, coef in enumerate(polynomial.coef): - header[f'COEF{i + 1}_{j}'] = coef + header[f'COEF{i + 1}_{j}'] = coef, f'Wavelength polynomial coef {j} for order {i}' header['EXTNAME'] = 'WAVELENGTHS' return header @@ -40,26 +40,34 @@ def coefficients(self): return [polynomial.coef for polynomial in self._polynomials] @property - def line_widths(self): - return self._line_widths + def line_fwhms(self): + return self._line_fwhms @property def line_tilts(self): return self._line_tilts + @property + def domains(self): + return [polynomial.domain for polynomial in self._polynomials] + + @property + def wavelength_domains(self): + return [polynomial(polynomial.domain) for polynomial in self._polynomials] + @classmethod def from_header(cls, header, orders): order_ids = np.arange(1, len([x for x in header.keys() if 'POLYORD' in x]) + 1) - line_widths = [] + line_fwhms = [] line_tilts = [] polynomials = [] for order_id in order_ids: - line_tilts.append(header[f'LINTILT{order_id}']) - line_widths.append(header[f'LINWIDE{order_id}']) + line_tilts.append(header[f'TILT{order_id}']) + line_fwhms.append(header[f'FWHM{order_id}']) polynomials.append(Legendre([float(header[f'COEF{order_id}_{i}']) for i in range(int(header[f'POLYORD{order_id}']) + 1)], domain=eval(header[f'POLYDOM{order_id}']))) - return cls(polynomials, line_widths, line_tilts, orders) + return cls(polynomials, line_fwhms, line_tilts, orders) @property def orders(self): @@ -67,7 +75,10 @@ def orders(self): @property def bin_edges(self): - return [model(np.arange(min(model.domain)-0.5, max(model.domain)+1)) for model in self._polynomials] + # By convention, integer numbers are pixel centers. + # Here we take the bin edge between the first and second pixel as the beginning of our bins + # This means that our bin positions are fully in the domain of the wavelength model + return [model(np.arange(min(model.domain)+0.5, max(model.domain))) for model in self._polynomials] def tilt_coordinates(tilt_angle, x, y): diff --git a/banzai_floyds/wavelengths.py b/banzai_floyds/wavelengths.py index 0fe9bff..aff35a1 100644 --- a/banzai_floyds/wavelengths.py +++ b/banzai_floyds/wavelengths.py @@ -6,27 +6,37 @@ from scipy.signal import find_peaks from banzai_floyds.matched_filter import optimize_match_filter from banzai_floyds.frames import FLOYDSCalibrationFrame -from banzai.data import ArrayData +from banzai.data import ArrayData, DataTable +from banzai_floyds.utils.binning_utils import bin_data from banzai_floyds.utils.wavelength_utils import WavelengthSolution, tilt_coordinates -from banzai_floyds.utils.order_utils import get_order_2d_region -from banzai_floyds.arc_lines import arc_lines_table +from banzai_floyds.arc_lines import arc_lines_table, used_lines from banzai_floyds.utils.fitting_utils import gauss, fwhm_to_sigma +from banzai_floyds.extract import extract from scipy.special import erf from copy import copy +from astropy.table import Table +from banzai.logs import get_logger -def wavelength_model_weights(theta, x, lines, line_width): +logger = get_logger() + + +def wavelength_model_weights(theta, x, lines, line_sigma): + """ + line_sigma: in pixels + """ wavelength_model = Legendre(theta, domain=(np.min(x), np.max(x))) wavelengths = wavelength_model(x) weights = np.zeros(x.shape) for line in lines: if line['used']: - # TODO: Make sure this line width is in sigma, not fwhm - weights += line['strength'] * gauss(wavelengths, line['wavelength'], line_width) + # We need the line sigma in angstroms, we use delta lambda = dlambda/dx delta x + wavelength_sigma = wavelength_model.deriv(1)(x) * line_sigma + weights += line['strength'] * gauss(wavelengths, line['wavelength'], wavelength_sigma) return weights -def linear_wavelength_solution(data, error, lines, dispersion, line_width, offset_range, domain=None): +def linear_wavelength_solution(data, error, lines, dispersion, line_fwhm, offset_range, domain=None): """ Get best fit first-order wavelength solution @@ -38,7 +48,7 @@ def linear_wavelength_solution(data, error, lines, dispersion, line_width, offse lines: table containing 'wavelength' and 'strength' for each standard line dispersion: float Guess of Angstroms per pixel - line_width: average line width in angstroms + line_fwhm: average line width in pixels offset_range: list Range of values to search for the offset in the linear wavelength solution domain: tuple @@ -52,13 +62,12 @@ def linear_wavelength_solution(data, error, lines, dispersion, line_width, offse # Step the model spectrum metric through each of the offsets and find the peak slope = dispersion * (len(data) // 2) metrics = [matched_filter_metric((offset, slope), data, error, wavelength_model_weights, None, None, - np.arange(data.size), lines, fwhm_to_sigma(line_width)) for offset in offset_range] + np.arange(data.size), lines, fwhm_to_sigma(line_fwhm)) for offset in offset_range] best_fit_offset = offset_range[np.argmax(metrics)] - return Legendre((best_fit_offset, slope), domain=domain) -def identify_peaks(data, error, line_width, line_sep, domain=None, snr_threshold=25.0): +def identify_peaks(data, error, line_fwhm, line_sep, domain=None, snr_threshold=5.0): """ Detect peaks in spectrum extraction @@ -67,10 +76,12 @@ def identify_peaks(data, error, line_width, line_sep, domain=None, snr_threshold data: array of 1D raw spectrum extraction error: array of uncertainties Same shapes as the input data array - line_width: average line width (fwhm) in pixels + line_fwhm: average line width (fwhm) in pixels line_sep: minimum separation distance before lines are determined to be unique in pixels domain: tuple min and max x-values of the order + snr_threshold: float + cutoff for the peak detection in signal to noise Returns ------- @@ -80,23 +91,27 @@ def identify_peaks(data, error, line_width, line_sep, domain=None, snr_threshold domain = (0, len(data) - 1) # extract peak locations # Assume +- 3 sigma for the kernel width - kernel_half_width = int(3 * fwhm_to_sigma(line_width)) + kernel_half_width = int(3 * fwhm_to_sigma(line_fwhm)) kernel_x = np.arange(-kernel_half_width, kernel_half_width + 1, 1)[::-1] - kernel = gauss(kernel_x, 0.0, fwhm_to_sigma(line_width)) + kernel = gauss(kernel_x, 0.0, fwhm_to_sigma(line_fwhm)) signal = np.convolve(kernel, data / error / error, mode='same') normalization = np.convolve(kernel * kernel, 1.0 / error / error, mode='same') ** 0.5 metric = signal / normalization - peaks, peak_properties = find_peaks(metric, height=snr_threshold, distance=line_sep) + peaks, _ = find_peaks(metric, height=snr_threshold, distance=line_sep) peaks += int(min(domain)) return peaks -def centroiding_weights(theta, x): - center, sigma = theta +def centroiding_weights(theta, x, line_sigma): + n_lines = len(theta) // 2 + centers = theta[:n_lines] + strengths = theta[n_lines:] + # Originally we just used the gaussian, but we really need the gaussian integrated over pixels # which is what this is. This should be more numerically stable without being too much slower + # It also may be overkill upper_pixel_limits = np.zeros_like(x, dtype=float) upper_pixel_limits[:-1] = (x[:-1] + x[1:]) / 2.0 # attach the last limit on the array @@ -104,13 +119,17 @@ def centroiding_weights(theta, x): lower_pixel_limits = np.zeros_like(x, dtype=float) lower_pixel_limits[1:] = (x[:-1] + x[1:]) / 2.0 lower_pixel_limits[0] = x[0] - (lower_pixel_limits[1] - x[0]) - integrated_gauss = -erf((-upper_pixel_limits + center) / (np.sqrt(2) * sigma)) - integrated_gauss += erf((center - lower_pixel_limits) / (np.sqrt(2) * sigma)) - integrated_gauss /= 2.0 - return integrated_gauss + + weights = np.zeros(len(x)) + for center, strength in zip(centers, strengths): + integrated_gauss = -erf((-upper_pixel_limits + center) / (np.sqrt(2) * line_sigma)) + integrated_gauss += erf((center - lower_pixel_limits) / (np.sqrt(2) * line_sigma)) + integrated_gauss /= 2.0 + weights += integrated_gauss * strength + return weights -def refine_peak_centers(data, error, peaks, line_width, domain=None): +def refine_peak_centers(data, error, peaks, line_fwhm, domain=None): """ Find a precise center and width based on a gaussian fit to data @@ -120,7 +139,7 @@ def refine_peak_centers(data, error, peaks, line_width, domain=None): error: array of uncertainties Same shapes as the input data array peaks: array containing the pixel location of detected peaks - line_width: average line width in angstroms + line_fwhm: average line full-width half maximum in pixels Returns ------- @@ -128,20 +147,12 @@ def refine_peak_centers(data, error, peaks, line_width, domain=None): """ if domain is None: domain = (0, len(data) - 1) - line_sigma = fwhm_to_sigma(line_width) - half_fit_window = int(3 * line_sigma) - centers = [] - x = np.arange(len(data)) - for peak in peaks - min(domain): - window = slice(int(peak - half_fit_window), int(peak + half_fit_window + 1), 1) - data_window = data[window] - error_window = error[window] - best_fit_center, _ = optimize_match_filter((0, line_sigma), data_window, error_window, - centroiding_weights, x[window] - peak, - bounds=[(np.min(x[window] - peak), np.max(x[window] - peak)), - (1, 3 * line_sigma)]) - centers.append(best_fit_center + peak) - centers = np.array(centers) + min(domain) + line_sigma = fwhm_to_sigma(line_fwhm) + + x = np.arange(len(data)) + min(domain) + best_fits_parameters = optimize_match_filter(np.hstack([peaks, np.ones(len(peaks))]), data, error, + centroiding_weights, x, args=(line_sigma,)) + centers = best_fits_parameters[:len(peaks)] return centers @@ -183,13 +194,13 @@ def estimate_distortion(peaks, corresponding_wavelengths, domain, order=4): return Legendre.fit(deg=order, x=peaks, y=corresponding_wavelengths, domain=domain) -def full_wavelength_solution_weights(theta, coordinates, lines, line_slices): +def full_wavelength_solution_weights(theta, coordinates, lines, line_slices, bkg_order_x, bkg_order_y): """ Produce a 2d model of arc fluxes given a line list and a wavelength solution polynomial, a tilt, and a line width Parameters ---------- - theta: tuple: tilt, line_width, *polynomial_coefficients + theta: tuple: tilt, line_fwhm, *polynomial_coefficients, *background_coefficients, *line_strengths coordinates: tuple of 2d arrays x, y. x and y are the coordinates of the data array for the model lines: astropy table of the lines in the line list with wavelength (in angstroms) and strength @@ -197,24 +208,43 @@ def full_wavelength_solution_weights(theta, coordinates, lines, line_slices): ------- model array: 2d array with the match filter weights given the wavelength solution model """ - tilt, line_width, *polynomial_coefficients = theta + tilt, line_fwhm = theta[:2] + n_bkg_coefficients = bkg_order_x + 1 + bkg_order_y + 1 + polynomial_coefficients = theta[2:-n_bkg_coefficients - len(lines)] + bkg_x_low_index = 2 + len(polynomial_coefficients) + bkg_x_high_index = 2 + len(polynomial_coefficients) + 1 + bkg_order_x + bkg_coefficients_x = theta[bkg_x_low_index: bkg_x_high_index] + bkg_coefficients_y = theta[bkg_x_high_index:bkg_x_high_index + bkg_order_y + 1] + line_strengths = theta[-len(lines):] x, y = coordinates tilted_x = tilt_coordinates(tilt, x, y) # We could cache the domain of the function wavelength_polynomial = Legendre(polynomial_coefficients, domain=(np.min(x), np.max(x))) model_wavelengths = wavelength_polynomial(tilted_x) - model = np.zeros_like(model_wavelengths) - line_sigma = fwhm_to_sigma(line_width) + bkg_polynomial_x = Legendre(bkg_coefficients_x, domain=(np.min(x), np.max(x))) + bkg_polynomial_y = Legendre(bkg_coefficients_y, domain=(np.min(y), np.max(y))) + + line_sigma = fwhm_to_sigma(line_fwhm) + # Convert line sigma in pixels to wavelengths + line_sigma = line_sigma * wavelength_polynomial.deriv(1)(tilted_x) + model = np.zeros(x.shape) # Some possible optimizations are to truncate around each line (caching which indicies are for each line) # say +- 5 sigma around each line - for line, line_slice in zip(lines, line_slices): + # We fit a relative strength of each line here to capture variations of the lamp + for line, line_slice, line_strength in zip(lines, line_slices, line_strengths): # in principle we should set the resolution to be a constant, i.e. delta lambda / lambda, not the overall width - model[line_slice] += line['strength'] * gauss(model_wavelengths[line_slice], line['wavelength'], line_sigma) + model[line_slice] += line_strength * gauss(model_wavelengths[line_slice], line['wavelength'], + line_sigma[line_slice]) + + used_region = np.where(model != 0.0) + model[used_region] += bkg_polynomial_x(x[used_region]) * bkg_polynomial_y(y[used_region]) + # TODO: There is probably some annoying normalization here that is unconstrained + # so we probably need 1 fewer free parameters return model -def full_wavelength_solution(data, error, x, y, initial_polynomial_coefficients, initial_tilt, initial_line_width, - lines): +def full_wavelength_solution(data, error, x, y, initial_polynomial_coefficients, initial_tilt, initial_line_fwhm, + lines, background_order_x=4, background_order_y=2): """ Use a match filter to estimate the best fit 2-d wavelength solution @@ -226,24 +256,59 @@ def full_wavelength_solution(data, error, x, y, initial_polynomial_coefficients, y: 2-d array, y-coordinates of the data, same, shape as data initial_polynomial_coefficients: 1d array of the initial polynomial coefficients for the wavelength solution initial_tilt: float: initial angle measured clockwise of up in degrees - initial_line_width: float: initial estimate of fwhm of the lines in angstroms + initial_line_fwhm: float: initial estimate of fwhm of the lines in pixels lines: astropy table: must have the columns of catalog center in angstroms, and strength + background_order_x: int: order of the polynomial in x to fit the background + background_order_y: int: order of the polynomial in y to fit the background Returns ------- - best_fit_params: 1-d array: (best_fit_tilt, best_fit_line_width, *best_fit_polynomial_coefficients) + best_fit_params: 1-d array: (best_fit_tilt, best_fit_line_width, *best_fit_polynomial_coefficients, *bkg_x, *bkg_y) """ + # TODO: Add a backround component to the model so that the line widths are accurate tilted_x = tilt_coordinates(initial_tilt, x, y) # We could cache the domain of the function wavelength_polynomial = Legendre(initial_polynomial_coefficients, domain=(np.min(x), np.max(x))) - model_wavelengths = wavelength_polynomial(tilted_x) - line_sigma = fwhm_to_sigma(initial_line_width) + line_sigma = fwhm_to_sigma(initial_line_fwhm) + pixels = np.arange(wavelength_polynomial.domain[0], wavelength_polynomial.domain[1] + 1, 1.0) + inverted_wavelength_polynomial = Legendre.fit(x=wavelength_polynomial(pixels), y=pixels, + deg=wavelength_polynomial.degree(), + domain=wavelength_polynomial.domain) + line_pixel_positions = np.array([inverted_wavelength_polynomial(line['wavelength']) for line in lines]) + line_in_order = np.logical_and(np.min(x) <= line_pixel_positions, line_pixel_positions <= np.max(x)) + lines = lines[line_in_order] + line_pixel_positions = line_pixel_positions[line_in_order] # Cache where we think the lines are going to be. If we don't have a good initial solution this will create issues # but that's true even if we don't cache here - line_slices = [np.where(np.logical_and(model_wavelengths > line['wavelength'] - 5.0 * line_sigma, - model_wavelengths < line['wavelength'] + 5.0 * line_sigma)) - for line in lines] - best_fit_params = optimize_match_filter((initial_tilt, initial_line_width, *initial_polynomial_coefficients), data, - error, full_wavelength_solution_weights, (x, y), args=(lines, line_slices)) + line_slices = [np.where(np.logical_and(tilted_x > line_pixel - 5.0 * line_sigma, + tilted_x < line_pixel + 5.0 * line_sigma)) + for line_pixel in line_pixel_positions] + bkg_coefficients_x = np.zeros(background_order_x + 1) + bkg_coefficients_y = np.zeros(background_order_y + 1) + bkg_coefficients_x[0] = np.median(data) + bkg_coefficients_y[0] = 1.0 + + initial_line_strengths = [] + + for line_pixel in line_pixel_positions: + line_flux = np.median(data[np.abs(tilted_x - line_pixel) <= 1.0]) + line_flux -= bkg_coefficients_x[0] + line_flux /= (1 / np.sqrt(2.0 * np.pi) / line_sigma) + initial_line_strengths.append(line_flux) + + bounds = [(-90., 90), [0.0, None]] + for _ in initial_polynomial_coefficients: + bounds.append([None, None]) + for _ in bkg_coefficients_x: + bounds.append([None, None]) + for _ in bkg_coefficients_y: + bounds.append([None, None]) + for _ in initial_line_strengths: + bounds.append([0.0, None]) + best_fit_params = optimize_match_filter((initial_tilt, initial_line_fwhm, *initial_polynomial_coefficients, + *bkg_coefficients_x, *bkg_coefficients_y, *initial_line_strengths), data, + error, full_wavelength_solution_weights, (x, y), + args=(lines, line_slices, background_order_x, background_order_y), + bounds=bounds) return best_fit_params @@ -267,56 +332,121 @@ def apply_master_calibration(self, image: FLOYDSCalibrationFrame, super_calibrat return image +def estimate_line_centers(wavelengths, flux, flux_errors, lines, line_fwhm, line_separation): + # Note line_separation is in pixels here. + reference_wavelengths = [] + measured_wavelengths = [] + peaks = np.array(identify_peaks(flux, flux_errors, line_fwhm, line_separation, snr_threshold=15.0)) + for line in lines: + if line['wavelength'] > np.max(wavelengths) or line['wavelength'] < np.min(wavelengths): + continue + closest_peak = peaks[np.argmin(np.abs(wavelengths[peaks] - line['wavelength']))] + closest_peak_wavelength = wavelengths[closest_peak] + if np.abs(closest_peak_wavelength - line['wavelength']) <= 5: + refined_peak = refine_peak_centers(flux, flux_errors, np.array([closest_peak]), line_fwhm)[0] + if not np.isfinite(refined_peak): + continue + if np.abs(refined_peak - closest_peak) > 5: + continue + refined_peak = np.interp(refined_peak, np.arange(len(wavelengths)), wavelengths) + measured_wavelengths.append(refined_peak) + reference_wavelengths.append(line['wavelength']) + return np.array(reference_wavelengths), np.array(measured_wavelengths) + + +def estimate_residuals(image, min_line_separation=5.0): + # Note min_line_separation is in pixels here. + reference_wavelengths = [] + measured_wavelengths = [] + orders = [] + + for order in [1, 2]: + where_order = image.extracted['order'] == order + order_reference_wavelengths, order_measured_wavelengths = estimate_line_centers( + image.extracted['wavelength'][where_order], + image.extracted['fluxraw'][where_order], + image.extracted['fluxrawerr'][where_order], + used_lines, image.wavelengths.line_fwhms[order - 1], + min_line_separation + ) + reference_wavelengths = np.hstack([reference_wavelengths, order_reference_wavelengths]) + measured_wavelengths = np.hstack([measured_wavelengths, order_measured_wavelengths]) + orders += [order] * len(order_reference_wavelengths) + return Table({'measured_wavelength': measured_wavelengths, + 'reference_wavelength': reference_wavelengths, + 'order': orders}) + + class CalibrateWavelengths(Stage): - EXTRACTION_HEIGHT = 5 LINES = arc_lines_table() - # All in angstroms, measured by Curtis McCully - # FWHM is , 5 pixels - INITIAL_LINE_WIDTHS = {1: 10, 2: 6} + # FWHM is in pixels + INITIAL_LINE_FWHMS = {'coj': {1: 6.65, 2: 5.92}, 'ogg': {1: 4.78, 2: 5.02}} INITIAL_DISPERSIONS = {1: 3.51, 2: 1.72} # Tilts in degrees measured counterclockwise (right-handed coordinates) INITIAL_LINE_TILTS = {1: 8., 2: 8.} - OFFSET_RANGES = {1: np.arange(7200.0, 7700.0, 0.5), 2: np.arange(4300, 4800, 0.5)} + OFFSET_RANGES = {1: np.arange(7200.0, 8000.0, 0.5), 2: np.arange(4300, 5200, 0.5)} # These thresholds were set using the data processed by the characterization tests. # The notebook is in the diagnostics folder MATCH_THRESHOLDS = {1: 50.0, 2: 25.0} - # In pixels - MIN_LINE_SEPARATIONS = {1: 5.0, 2: 5.0} - FIT_ORDERS = {1: 3, 2: 2} + # In units of the line fwhm (converted to sigma) + MIN_LINE_SEPARATION_N_SIGMA = 7.5 + # In units of median signal to noise in the spectrum + PEAK_SNR_THRESHOLD = 10.0 + FIT_ORDERS = {1: 5, 2: 2} # Success Metrics MATCH_SUCCESS_THRESHOLD = 3 # matched lines required to consider solution success + # Background polynomial orders + BKG_ORDER_X = 2 + BKG_ORDER_Y = 2 """ Stage that uses Arcs to fit wavelength solution """ def do_stage(self, image): order_ids = np.unique(image.orders.data) order_ids = order_ids[order_ids != 0] - initial_wavelength_solutions = [] # Do a quick extraction by medianing the central region of the order extraction_orders = copy(image.orders) - extraction_orders.order_heights = self.EXTRACTION_HEIGHT * np.ones_like(order_ids) + + best_fit_polynomials = [] + best_fit_tilts = [] + best_fit_fwhms = [] + for i, order in enumerate(order_ids): - order_region = get_order_2d_region(extraction_orders.data == order) - # Note that his flux has an x origin at the x = 0 instead of the domain of the order + order_region = extraction_orders.data == order + + # Bin the data using titled coordinates in pixel space + x2d, y2d = np.meshgrid(np.arange(image.data.shape[1]), np.arange(image.data.shape[0])) + order_center = image.orders.center(x2d[order_region])[i] + tilt_ys = y2d[order_region] - order_center + tilted_x = tilt_coordinates(self.INITIAL_LINE_TILTS[order], x2d[order_region], tilt_ys) + # The 1.0 at the end is arbitrarily larger than +0.5 so the sequence knows when to stop + # with the final bin edge = last pixel + 0.5 + bins = np.arange(np.min(image.orders.domains[i]) - 0.5, np.max(image.orders.domains[i]) + 1.0) + # Do a average over all the bins to get better signal to noise. This just does it without for loops + flux_1d = np.histogram(tilted_x, bins=bins, weights=image.data[order_region], density=False)[0] + flux_1d /= np.histogram(tilted_x, bins=bins, density=False)[0] + + # Note that this flux has an x origin at the x = 0 instead of the domain of the order # I don't think it matters though - flux_1d = np.median(image.data[order_region], axis=0) - # This 1.2533 is from Rider 1960 DOI: 10.1080/01621459.1960.10482056 and converts the standard error - # to error on the median - flux_1d_error = 1.2533 * np.median(image.uncertainty[order_region], axis=0) - flux_1d_error /= np.sqrt(self.EXTRACTION_HEIGHT) + + flux_1d_error = np.histogram(tilted_x, bins=bins, weights=image.uncertainty[order_region] ** 2.0, + density=False)[0] + flux_1d_error /= np.histogram(tilted_x, bins=bins, density=False)[0] + flux_1d_error **= 0.5 linear_solution = linear_wavelength_solution(flux_1d, flux_1d_error, self.LINES[self.LINES['used']], self.INITIAL_DISPERSIONS[order], - self.INITIAL_LINE_WIDTHS[order], + self.INITIAL_LINE_FWHMS[image.site][order], self.OFFSET_RANGES[order], domain=image.orders.domains[i]) # from 1D estimate linear solution # Estimate 1D distortion with higher order polynomials + min_line_separation = fwhm_to_sigma(self.INITIAL_LINE_FWHMS[image.site][order]) + min_line_separation *= self.MIN_LINE_SEPARATION_N_SIGMA peaks = identify_peaks(flux_1d, flux_1d_error, - self.INITIAL_LINE_WIDTHS[order] / self.INITIAL_DISPERSIONS[order], - self.MIN_LINE_SEPARATIONS[order], domain=image.orders.domains[i]) - peaks = refine_peak_centers(flux_1d, flux_1d_error, peaks, - self.INITIAL_LINE_WIDTHS[order] / self.INITIAL_DISPERSIONS[order], - domain=image.orders.domains[i]) + self.INITIAL_LINE_FWHMS[image.site][order] / self.INITIAL_DISPERSIONS[order], + min_line_separation, domain=image.orders.domains[i], + snr_threshold=self.PEAK_SNR_THRESHOLD) + corresponding_lines = np.array(correlate_peaks(peaks, linear_solution, self.LINES[self.LINES['used']], self.MATCH_THRESHOLDS[order])).astype(float) successful_matches = np.isfinite(corresponding_lines) @@ -325,42 +455,53 @@ def do_stage(self, image): # too few lines for good wavelength solution image.is_bad = True return image - initial_wavelength_solutions.append(estimate_distortion(peaks[successful_matches], - corresponding_lines[successful_matches], - image.orders.domains[i], - order=self.FIT_ORDERS[order])) - image.wavelengths = WavelengthSolution(initial_wavelength_solutions, - [self.INITIAL_LINE_WIDTHS[order] for order in order_ids], - [self.INITIAL_LINE_TILTS[order] for order in order_ids], order_ids) - best_fit_polynomials = [] - best_fit_tilts = [] - best_fit_widths = [] - - for order, order_center, input_coefficients, input_tilt, input_width in \ - zip(order_ids, image.orders._models, image.wavelengths.coefficients, image.wavelengths.line_tilts, - image.wavelengths.line_widths): - x2d, y2d = np.meshgrid(np.arange(image.data.shape[1]), np.arange(image.data.shape[0])) + peaks = refine_peak_centers(flux_1d, flux_1d_error, peaks, + self.INITIAL_LINE_FWHMS[image.site][order], + domain=image.orders.domains[i]) - tilt_ys = y2d[image.orders.data == order] - order_center(x2d[image.orders.data == order]) + initial_solution = estimate_distortion(peaks[successful_matches], + corresponding_lines[successful_matches], + image.orders.domains[i], + order=self.FIT_ORDERS[order]) + # Do a final fit that allows the fwhm, the line tilt, the strength of the catalog lines, + # the background, and a single set of polynomial coeffs,to vary. + # The background and line strengths are just nuisance parameters + # Limit the fit to only include +-5 sigma from known lines. This probably needs to be slit width + # dependent. This is so the lines that we don't include in the model don't pull the background fits. # Fit 2D wavelength solution using initial guess either loaded or from 1D extraction - tilt, width, *coefficients = full_wavelength_solution(image.data[image.orders.data == order], - image.uncertainty[image.orders.data == order], - x2d[image.orders.data == order], - tilt_ys, - input_coefficients, input_tilt, input_width, - self.LINES[self.LINES['used']]) + tilt, fwhm, *coefficients = full_wavelength_solution( + image.data[image.orders.data == order], + image.uncertainty[image.orders.data == order], + x2d[order_region], tilt_ys, + initial_solution.coef, self.INITIAL_LINE_TILTS[order], + self.INITIAL_LINE_FWHMS[image.site][order], + self.LINES[self.LINES['used']], + background_order_x=self.BKG_ORDER_X, + background_order_y=self.BKG_ORDER_Y + ) # evaluate wavelength solution at all pixels in 2D order # TODO: Make sure that the domain here doesn't mess up the tilts - polynomial = Legendre(coefficients, domain=(min(x2d[image.orders.data == order]), - max(x2d[image.orders.data == order]))) - + polynomial = Legendre(coefficients[:self.FIT_ORDERS[order] + 1], + domain=(min(x2d[image.orders.data == order]), + max(x2d[image.orders.data == order]))) best_fit_polynomials.append(polynomial) best_fit_tilts.append(tilt) - best_fit_widths.append(width) - - image.wavelengths = WavelengthSolution(best_fit_polynomials, best_fit_widths, best_fit_tilts, image.orders) + best_fit_fwhms.append(fwhm) + image.wavelengths = WavelengthSolution(best_fit_polynomials, best_fit_fwhms, best_fit_tilts, image.orders) image.add_or_update(ArrayData(image.wavelengths.data, name='WAVELENGTHS', meta=image.wavelengths.to_header())) image.is_master = True + + # Extract the data + binned_data = bin_data(image.data, image.uncertainty, image.wavelengths, image.orders) + binned_data['background'] = 0.0 + binned_data['weights'] = 1.0 + binned_data['extraction_window'] = True + image.extracted = extract(binned_data) + + min_line_separation = fwhm_to_sigma(np.max(list(self.INITIAL_LINE_FWHMS[image.site].values()))) + min_line_separation *= self.MIN_LINE_SEPARATION_N_SIGMA + image.add_or_update(DataTable(estimate_residuals(image, min_line_separation=min_line_separation), + name='LINESUSED')) return image diff --git a/characterization_testing/ManualReduction.ipynb b/characterization_testing/ManualReduction.ipynb new file mode 100644 index 0000000..8aba2ce --- /dev/null +++ b/characterization_testing/ManualReduction.ipynb @@ -0,0 +1,478 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f5a6dcad-d3c7-40c3-911f-1d8a7e28c805", + "metadata": {}, + "outputs": [], + "source": [ + "from astropy.io import ascii, fits\n", + "import os\n", + "import requests\n", + "import importlib\n", + "import io\n", + "import numpy as np\n", + "from numpy.lib.stride_tricks import sliding_window_view\n", + "from skimage import measure\n", + "from numpy.polynomial.legendre import Legendre\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65f8db9b-e664-4f69-a2cd-44a6b8781f55", + "metadata": {}, + "outputs": [], + "source": [ + "archive_api = 'https://archive-api.lco.global/frames/'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29181d14-72dd-4ace-a2f6-7aef2b573e3c", + "metadata": {}, + "outputs": [], + "source": [ + "def download_frame(frame_id, filename, archive_api):\n", + " response = requests.get(archive_api + str(frame_id)).json()\n", + " buffer = io.BytesIO()\n", + " buffer.write(requests.get(response['url'], stream=True).content)\n", + " buffer.seek(0)\n", + " return buffer" + ] + }, + { + "cell_type": "markdown", + "id": "1c4f1698-ffd7-4aa6-b1cb-fce65d97ec21", + "metadata": {}, + "source": [ + "Download the flat fields to get the location of the orders" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ec6e3d5-a3b9-4aa0-a748-e3a8dfa5cb84", + "metadata": {}, + "outputs": [], + "source": [ + "skyflat_files = ascii.read(os.path.join(importlib.resources.files('banzai_floyds.tests'), 'data/test_skyflat.dat'))\n", + "for skyflat in skyflat_files:\n", + " skyflat_info = dict(skyflat) \n", + " skyflat_hdu = fits.open(download_frame(skyflat_info['frameid'], skyflat_info['filename'], archive_api))\n", + "\n", + " # Munge the data to be OBSTYPE SKYFLAT\n", + " skyflat_hdu['SCI'].header['OBSTYPE'] = 'SKYFLAT'\n", + " skyflat_name = skyflat_info[\"filename\"].replace(\"x00.fits\", \"f00.fits\")\n", + " filename = os.path.join('manual_reduction', f'{skyflat_name}')\n", + " skyflat_hdu.writeto(filename, overwrite=True)\n", + " skyflat_hdu.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36932505-abfa-4fc6-bd8f-0a0065f84e3c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c01e24d-c1a2-4791-8da8-e3d5cc012150", + "metadata": {}, + "outputs": [], + "source": [ + "ogg_hdu = fits.open('manual_reduction/ogg2m001-en06-20190329-0018-f00.fits.fz')\n", + "px.imshow(ogg_hdu['SCI'].data.astype(float), origin='lower')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7991aa4-ac88-40b0-84f1-82dd0388d370", + "metadata": {}, + "outputs": [], + "source": [ + "convolved = np.sum(sliding_window_view(ogg_hdu['SCI'].data, window_shape = 95, axis=0), axis=2)\n", + "fig = px.imshow(convolved, origin='lower')\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "453f1873-b7bf-4ead-a836-6ddaf3758479", + "metadata": {}, + "outputs": [], + "source": [ + "# Do a makeshift max filter in only the y-direction\n", + "window_view_summed = sliding_window_view(convolved, window_shape=7, axis=0)\n", + "max_filtered = np.max(window_view_summed, axis=2) == convolved[3:-3]\n", + "\n", + "# Detect the maxima and only filter out everything that isn't the main order centers\n", + "ogg_center_labels = measure.label(max_filtered)\n", + "ogg_center_properties = measure.regionprops(ogg_center_labels)\n", + "for prop in ogg_center_properties:\n", + " if prop.area > 500:\n", + " edge = ogg_center_labels == prop.label\n", + " fig = px.imshow(edge, origin='lower')\n", + " fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "44728b42-d9d8-477e-b02a-e5e136198f1b", + "metadata": {}, + "source": [ + "Fit legendre polynomials to the edges, test to make sure that 99% of the pipeline region pixels fall between those two curves" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1e26ec6", + "metadata": {}, + "outputs": [], + "source": [ + "orders = {'coj': {1: {}, 2: {}}, 'ogg': {1: {}, 2: {}}}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6afffbc0-9cf3-4412-b15a-bdff8e0b73e8", + "metadata": {}, + "outputs": [], + "source": [ + "x2d, y2d = np.meshgrid(np.arange(ogg_hdu['SCI'].data.shape[1]), np.arange(ogg_hdu['SCI'].data.shape[0]))\n", + "order_id = 1\n", + "for prop in ogg_center_properties:\n", + " if prop.area > 500:\n", + " edge = np.zeros(ogg_hdu['SCI'].data.shape, dtype=bool)\n", + " # We have to convert back to original image coordinates\n", + " # We do this by taking the 95 pixel wide filter size and the 7 pixel wide max filter size\n", + " # and padding the filtered data by the half width of each\n", + " edge[95//2 + 7//2:-95//2 - 7//2 + 1] = ogg_center_labels == prop.label\n", + " # Fit a Legendre polynomial to the center of the order\n", + " x, y = x2d[edge], y2d[edge]\n", + " best_fit = Legendre.fit(x, y, 5)\n", + " coeffs = \",\".join(map(str, best_fit.coef))\n", + " domain = \",\".join(map(str, best_fit.domain))\n", + " range_str = \",\".join(map(str, best_fit.window))\n", + " print(f'Legendre(coef=[{coeffs}], domain=[{domain}], range=[{range_str}])') \n", + " orders['ogg'][order_id]['coeffs'] = best_fit.coef\n", + " orders['ogg'][order_id]['domain'] = best_fit.domain\n", + " orders['ogg'][order_id]['window'] = best_fit.window\n", + " order_id += 1\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8764532-0d5e-4260-a598-803a363f27d5", + "metadata": {}, + "outputs": [], + "source": [ + "# Do the same procedure for COJ\n", + "coj_hdu = fits.open('manual_reduction/coj2m002-en12-20220313-0002-f00.fits.fz')\n", + "px.imshow(coj_hdu['SCI'].data.astype(float), origin='lower')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67840839", + "metadata": {}, + "outputs": [], + "source": [ + "convolved = np.sum(sliding_window_view(coj_hdu['SCI'].data, window_shape = 95, axis=0), axis=2)\n", + "\n", + "# Do a makeshift max filter in only the y-direction\n", + "window_view_summed = sliding_window_view(convolved, window_shape=7, axis=0)\n", + "max_filtered = np.max(window_view_summed, axis=2) == convolved[3:-3]\n", + "\n", + "# Detect the maxima and only filter out everything that isn't the main order centers\n", + "coj_center_labels = measure.label(max_filtered)\n", + "coj_center_properties = measure.regionprops(coj_center_labels)\n", + "for prop in coj_center_properties:\n", + " if prop.area > 500:\n", + " edge = coj_center_labels == prop.label\n", + " fig = px.imshow(edge, origin='lower')\n", + " fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "831a8987", + "metadata": {}, + "outputs": [], + "source": [ + "x2d, y2d = np.meshgrid(np.arange(coj_hdu['SCI'].data.shape[1]), np.arange(coj_hdu['SCI'].data.shape[0]))\n", + "order_id = 1\n", + "for prop in coj_center_properties:\n", + " if prop.area > 500:\n", + " edge = np.zeros(coj_hdu['SCI'].data.shape, dtype=bool)\n", + " # We have to convert back to original image coordinates\n", + " # We do this by taking the 95 pixel wide filter size and the 7 pixel wide max filter size\n", + " # and padding the filtered data by the half width of each\n", + " edge[95//2 + 7//2:-95//2 - 7//2 + 1] = coj_center_labels == prop.label\n", + " # Fit a Legendre polynomial to the center of the order\n", + " x, y = x2d[edge], y2d[edge]\n", + " best_fit = Legendre.fit(x, y, 5)\n", + " coeffs = \",\".join(map(str, best_fit.coef))\n", + " domain = \",\".join(map(str, best_fit.domain))\n", + " range_str = \",\".join(map(str, best_fit.window))\n", + " print(f'Legendre(coef=[{coeffs}], domain=[{domain}], range=[{range_str}])') \n", + "\n", + " orders['coj'][order_id]['coeffs'] = best_fit.coef\n", + " orders['coj'][order_id]['domain'] = best_fit.domain\n", + " orders['coj'][order_id]['window'] = best_fit.window\n", + " order_id += 1\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e3cc6cc5", + "metadata": {}, + "outputs": [], + "source": [ + "test_files = ascii.read(os.path.join(importlib.resources.files('banzai_floyds.tests'), 'data/test_data.dat'))\n", + "for frame in test_files:\n", + " frame_info = dict(frame) \n", + " hdu = fits.open(download_frame(frame_info['frameid'], frame_info['filename'], archive_api))\n", + " hdu.writeto(os.path.join('manual_reduction', f'{frame_info[\"filename\"]}'), overwrite=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43e123f8", + "metadata": {}, + "outputs": [], + "source": [ + "from glob import glob\n", + "from banzai_floyds.utils.order_utils import get_order_2d_region\n", + "from banzai_floyds.arc_lines import used_lines\n", + "from banzai_floyds.wavelengths import refine_peak_centers\n", + "import plotly.graph_objs as go\n", + "import plotly.express as px\n", + "import os \n", + "import json \n", + "\n", + "order_height = 95\n", + "\n", + "print([os.path.basename(filename) for filename in glob('manual_reduction/*a00.fits*')])\n", + "\n", + "for filename in glob('manual_reduction/*a00.fits*'):\n", + " hdu = fits.open(filename)\n", + " for order in [1, 2]:\n", + " x2d, y2d = np.meshgrid(np.arange(hdu['SCI'].data.shape[1]), np.arange(hdu['SCI'].data.shape[0]))\n", + " site = hdu['SCI'].header['SITEID']\n", + " order_center = Legendre(coef=orders[site][order]['coeffs'], domain=orders[site][order]['domain'],\n", + " window=orders[site][order]['window'])\n", + " \n", + " order_mask = np.logical_and(x2d >= min(order_center.domain), x2d <= max(order_center.domain))\n", + " order_mask = np.logical_and(order_mask, y2d - order_center(x2d) >= -order_height // 2)\n", + " order_mask = np.logical_and(order_mask, y2d - order_center(x2d) <= order_height // 2)\n", + " order_region = get_order_2d_region(order_mask)\n", + " \n", + " starting_flux = hdu['SCI'].data[order_region][0]\n", + " pixel = np.arange(len(starting_flux))\n", + " fig = px.line(x=pixel, y=starting_flux)\n", + " fig.show()\n", + "\n", + "# Lines manually measured by Curtis 2024-09-09 (Same order as glob)\n", + "\n", + "catalog_lines = {\n", + " 1: np.array([used_line['wavelength'] for used_line in used_lines[4:]]),\n", + " 2: np.array([used_line['wavelength'] for used_line in used_lines[:5]])\n", + "}\n", + "\n", + "\n", + "starting_line_locations = {\n", + " 'coj2m002-en12-20200813-0028-a00.fits.fz': \n", + " {1: np.array([295, 728, 758, 782, 816, 918, 1007, 1097, 1171, \n", + " 1213, 1342, 1373, 1495, 1531, 1634]),\n", + " 2: np.array([183, 411, 431, 591, 1229])\n", + " },\n", + " 'coj2m002-en12-20200813-0015-a00.fits.fz': \n", + " {1: np.array([297, 730, 759, 782, 818, 921, 1010, 1099, 1172,\n", + " 1213, 1344, 1373, 1497, 1534, 1634]),\n", + " 2: np.array([183, 413, 430, 594, 1231]),\n", + " },\n", + " 'ogg2m001-en06-20200822-0028-a00.fits.fz':\n", + " {1: np.array([208, 644, 673, 695, 731, 836, 925, 1016, 1088, 1130,\n", + " 1261, 1291, 1415, 1451, 1551]),\n", + " 2: np.array([201, 435, 456, 617, 1252]),\n", + " },\n", + " 'ogg2m001-en06-20200822-0009-a00.fits.fz':\n", + " {1: np.array([208, 644, 672, 696, 731, 836, 925, 1016, 1089, 1131,\n", + " 1262, 1291, 1414, 1450, 1551]),\n", + " 2: np.array([201, 435, 456, 618, 1252])\n", + " }\n", + " }\n", + "\n", + "\n", + "# Measured by eye by Curtis\n", + "line_fwhms = {\n", + " 'coj2m002-en12-20200813-0028-a00.fits.fz': {2: 596.2 - 589.5, 1: 1345.2 - 1340.2},\n", + " 'coj2m002-en12-20200813-0015-a00.fits.fz': {2: 597.7 - 590.8, 1: 1346.5 - 1341.5},\n", + " 'ogg2m001-en06-20200822-0028-a00.fits.fz': {2: 619.9 - 615.3, 1: 1263.4 - 1258.8},\n", + " 'ogg2m001-en06-20200822-0009-a00.fits.fz': {2: 620.25 - 615.4, 1: 1263.5 - 1258.75}\n", + "}\n", + "poly_order = {\n", + " 1: 5,\n", + " 2: 2\n", + "}\n", + "\n", + "wavelength_fits = {\n", + " 'coj2m002-en12-20200813-0028-a00.fits.fz': {1: {'coef': [], 'domain': [], 'window': []}, 2: {'coef': [], 'domain': [], 'window': []}},\n", + " 'coj2m002-en12-20200813-0015-a00.fits.fz': {1: {'coef': [], 'domain': [], 'window': []}, 2: {'coef': [], 'domain': [], 'window': []}},\n", + " 'ogg2m001-en06-20200822-0028-a00.fits.fz': {1: {'coef': [], 'domain': [], 'window': []}, 2: {'coef': [], 'domain': [], 'window': []}},\n", + " 'ogg2m001-en06-20200822-0009-a00.fits.fz': {1: {'coef': [], 'domain': [], 'window': []}, 2: {'coef': [], 'domain': [], 'window': []}}\n", + "}\n", + "\n", + "\n", + "# and then fit row by row, a set of lines measured by hand for all 4 arc lamps\n", + "for filename in starting_line_locations:\n", + " hdu = fits.open(os.path.join('manual_reduction', filename))\n", + " #then starting from the previous best fit, fit the next row\n", + " x2d, y2d = np.meshgrid(np.arange(hdu['SCI'].data.shape[1]), np.arange(hdu['SCI'].data.shape[0]))\n", + " site = hdu['SCI'].header['SITEID']\n", + " for order in [1, 2]:\n", + " figure = go.Figure()\n", + " order_center = Legendre(coef=orders[site][order]['coeffs'], domain=orders[site][order]['domain'],\n", + " window=orders[site][order]['window'])\n", + " \n", + " order_mask = np.logical_and(x2d >= min(order_center.domain), x2d <= max(order_center.domain))\n", + " order_mask = np.logical_and(order_mask, y2d - order_center(x2d) >= -order_height // 2)\n", + " order_mask = np.logical_and(order_mask, y2d - order_center(x2d) <= order_height // 2)\n", + " order_region = get_order_2d_region(order_mask)\n", + "\n", + " last_line_locations = starting_line_locations[filename][order]\n", + " n_orig_lines = len(starting_line_locations[filename][order])\n", + "\n", + " # I have removed these lines in the current line list so no need to do it manually anymore\n", + " # Remove the 3rd line in the blue because it is often blended and not high enough s/n\n", + " #if order == 2:\n", + " # last_line_locations = last_line_locations[np.arange(n_orig_lines) != 2]\n", + " # this_catalog = catalog_lines[order][np.arange(n_orig_lines) != 2]\n", + " # In the red, remove the 4th line which is too low of s/n and too close to nearby lines\n", + " # I worry about the last line in the red, but it seems happier since it is isolated \n", + " #if order == 1:\n", + " # last_line_locations = last_line_locations[np.arange(n_orig_lines) != 3]\n", + " # this_catalog = catalog_lines[order][np.arange(n_orig_lines) != 3]\n", + "\n", + " for j in range(order_height):\n", + " data = hdu['SCI'].data[order_region][j].astype(float)\n", + " gain = float(hdu['SCI'].header['GAIN'])\n", + " error = np.sqrt(hdu['SCI'].data[order_region][j].astype(float) * gain + hdu['SCI'].header['RDNOISE'] ** 2) / gain\n", + " data -= np.median(data)\n", + " measured_lines = refine_peak_centers(data, error, last_line_locations, line_fwhms[filename][order])\n", + " if j == 0:\n", + " pixel = np.arange(len(data))\n", + " fig = px.line(x=pixel, y=data)\n", + " for measured_line in measured_lines:\n", + " data_height = data[int(np.round(measured_line))]\n", + " fig.add_trace(go.Scatter(x=[measured_line, measured_line], y=[data_height * 1.1, data_height * 1.4], mode='lines', line=dict(color='salmon')))\n", + " fig.show()\n", + " best_fit = Legendre.fit(measured_lines, this_catalog, deg=poly_order[order],\n", + " domain=(0, len(data) - 1))\n", + " # save the Legendre polynomial for each row to include in the e2e test data\n", + " wavelength_fits[filename][order]['coef'].append(list(best_fit.coef))\n", + " wavelength_fits[filename][order]['domain'].append(list(orders[site][order]['domain']))\n", + " wavelength_fits[filename][order]['window'].append(list(best_fit.window)) \n", + " last_line_locations = measured_lines\n", + " # make a slider plot of each fit for each row to manually check each fit to make sure they look reasonable\n", + " residuals = best_fit(measured_lines) - this_catalog\n", + " figure.add_trace(go.Scatter(x=best_fit(measured_lines), y=residuals, mode='markers', name=f'{filename} Order {order} Row {j}', visible=(j == 0)))\n", + " # Create slider steps\n", + " steps = []\n", + " for i in range(order_height):\n", + " step = dict(\n", + " method=\"update\",\n", + " args=[{\"visible\": [k == i for k in range(order_height)]},\n", + " {\"title\": f\"Fit for Row {i}\"}], \n", + " )\n", + " steps.append(step)\n", + "\n", + " # Create slider\n", + " sliders = [dict(\n", + " active=0,\n", + " currentvalue={\"prefix\": \"Row: \"},\n", + " pad={\"t\": 50},\n", + " steps=steps\n", + " )]\n", + "\n", + " # Update layout with slider\n", + " figure.update_layout(\n", + " sliders=sliders,\n", + " xaxis_title=\"Wavelength (Angstroms)\",\n", + " yaxis_title=\"Residuals (Angstroms)\"\n", + " )\n", + "\n", + " figure.show()\n", + "with open('../banzai_floyds/tests/data/wavelength_e2e_fits.dat', 'w') as f:\n", + " f.writelines(json.dumps(wavelength_fits, indent=4))" + ] + }, + { + "cell_type": "markdown", + "id": "ec7f0501-423d-43dc-87f8-1ff869ff8739", + "metadata": {}, + "source": [ + "TODO: check the 2d fft of the fringe image? I wonder if fitting phase parameters might actually work for the shifting to get rid of the lamp continuum\n", + "\n", + "TODO: Define a regression test" + ] + }, + { + "cell_type": "markdown", + "id": "0d872007-d554-495c-8522-122b660d491e", + "metadata": {}, + "source": [ + "TODO: Use specreduce to extract each source, with quadratic background\n", + "\n", + "TODO: Regression test is that banzai extraction is within the noise of the manual extraction" + ] + }, + { + "cell_type": "markdown", + "id": "cd4a4e38-da28-4903-9041-7eb41f139a4b", + "metadata": {}, + "source": [ + "TODO: Define regression tests for telluric correction and flux calibration" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "banzai-floyds", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/characterization_testing/WavelengthCalibration.ipynb b/characterization_testing/WavelengthCalibration.ipynb index e730e4f..a1f529d 100644 --- a/characterization_testing/WavelengthCalibration.ipynb +++ b/characterization_testing/WavelengthCalibration.ipynb @@ -67,8 +67,8 @@ "os.system(f'banzai_floyds_create_db --db-address={os.environ[\"DB_ADDRESS\"]}');\n", "os.system(f'banzai_add_site --site ogg --latitude 20.7069444444\t--longitude -156.258055556 --elevation 3065 --timezone -10 --db-address={os.environ[\"DB_ADDRESS\"]}');\n", "os.system(f'banzai_add_site --site coj --latitude -31.272932 --longitude 149.070648 --elevation 1116 --timezone 10 --db-address={os.environ[\"DB_ADDRESS\"]}');\n", - "os.system(f'banzai_add_instrument --site ogg --camera en06 --name floyds01 --instrument-type 2m0-FLOYDS-SciCam --db-address={os.environ[\"DB_ADDRESS\"]}');\n", - "os.system(f'banzai_add_instrument --site coj --camera en12 --name floyds02 --instrument-type 2m0-FLOYDS-SciCam --db-address={os.environ[\"DB_ADDRESS\"]}');" + "os.system(f'banzai_add_instrument --site ogg --camera en06 --name floyds01 --instrument-type 2m0-FLOYDS-SciCam --db-address={os.environ[\"DB_ADDRESS\"]} --nx 2079 --ny 512');\n", + "os.system(f'banzai_add_instrument --site coj --camera en12 --name floyds02 --instrument-type 2m0-FLOYDS-SciCam --db-address={os.environ[\"DB_ADDRESS\"]} --nx 2079 --ny 512');" ] }, { @@ -193,7 +193,8 @@ "source": [ "from banzai_floyds.orders import orders_from_fits\n", "from banzai_floyds.wavelengths import WavelengthSolution\n", - "from banzai_floyds.extract import get_wavelength_bins, extract, bin_data" + "from banzai_floyds.extract import extract\n", + "from banzai_floyds.utils.binning_utils import bin_data" ] }, { @@ -240,8 +241,7 @@ " new_hdu = fits.open(new_filename)\n", " orders = orders_from_fits(new_hdu['ORDER_COEFFS'].data, new_hdu['ORDER_COEFFS'].header, new_hdu['SCI'].data.shape)\n", " wavelengths = WavelengthSolution.from_header(new_hdu['WAVELENGTHS'].header, orders)\n", - " wavelength_bins = get_wavelength_bins(wavelengths)\n", - " binned_data = bin_data(new_hdu['SCI'].data, new_hdu['ERR'].data, wavelengths, orders, wavelength_bins)\n", + " binned_data = bin_data(new_hdu['SCI'].data, new_hdu['ERR'].data, wavelengths, orders)\n", " binned_data['background'] = 0.0\n", " binned_data['weights'] = 1.0\n", " extracted_data = extract(binned_data)\n", @@ -379,8 +379,7 @@ " new_hdu = fits.open(new_filename)\n", " orders = orders_from_fits(new_hdu['ORDER_COEFFS'].data, new_hdu['ORDER_COEFFS'].header, new_hdu['SCI'].data.shape)\n", " wavelengths = WavelengthSolution.from_header(new_hdu['WAVELENGTHS'].header, orders)\n", - " wavelength_bins = get_wavelength_bins(wavelengths)\n", - " binned_data = bin_data(new_hdu['SCI'].data, new_hdu['ERR'].data, wavelengths, orders, wavelength_bins)\n", + " binned_data = bin_data(new_hdu['SCI'].data, new_hdu['ERR'].data, wavelengths, orders)\n", " binned_data['background'] = 0.0\n", " binned_data['weights'] = 1.0\n", " extracted_data = extract(binned_data)\n", diff --git a/setup.cfg b/setup.cfg index f99507b..75382e4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,11 +15,12 @@ github_project = lcogt/banzai-floyds zip_safe = False packages = find: python_requires = >=3.7 -setup_requires = setuptools_scm +setup_requires = + setuptools_scm + psycopg2-binary install_requires = astropy >= 4.3 - banzai @ git+https://github.com/lcogt/banzai.git@fix/header-only-fits - astroscrappy + banzai @ git+https://github.com/lcogt/banzai.git@1.19.1 matplotlib [options.extras_require]