Commit 6496a718 authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

Merge branch 'master' into enhancement/improve_ecmwf_crawler

parents ec52e2a8 84b1efd1
......@@ -6,10 +6,12 @@ History
--------------------
New features:
*
* Multiprocessing mode of SICOR available again on macOS.
* Option to choose between two solar irradiance models: 'new_kurucz' and 'fontenla'.
Bugfixes:
*
* Added missing initializer to multiprocessing pool in empirical line calculation and set multiprocessing start method to fork.
* Disabled water vapor first guess retrieval over water surfaces in case SICOR is running for land+water pixels.
0.16.4 (2021-06-18)
......
......@@ -99,7 +99,8 @@ setup(
"sicor/sensors/S2MSI/data/S2A_SNR_model.xlsx",
"sicor/AC/data/k_liquid_water_ice.xlsx",
"sicor/AC/data/newkur_EnMAP.dat",
"sicor/AC/data/solar_irradiances_400_2500_1.dill"
"sicor/AC/data/fontenla_EnMAP.dat",
"sicor/AC/data/conv_fac_fon_lut.dill"
])],
keywords=["SICOR", "EnMAP", "EnMAP-Box", "hyperspectral", "remote sensing", "satellite", "atmospheric correction"],
include_package_data=True,
......
......@@ -28,7 +28,7 @@ import logging
import numpy as np
import pandas as pd
import dill
from multiprocessing import Pool
import multiprocessing as mp
from itertools import product
from time import time
import warnings
......@@ -41,8 +41,8 @@ import platform
from py_tools_ds.processing.progress_mon import ProgressBar
from sicor.Tools.EnMAP.metadata import varsol
from sicor.Tools.EnMAP.LUT import get_data_file, read_lut_enmap_formatted, download_LUT, interpol_lut_c
from sicor.Tools.EnMAP.LUT import reduce_lut, interpol_lut_c_red
from sicor.Tools.EnMAP.LUT import get_data_file, read_lut_enmap_formatted, download_LUT, interpol_lut
from sicor.Tools.EnMAP.LUT import reduce_lut, interpol_lut_red
from sicor.Tools.EnMAP.conversion import generate_filter, table_to_array
from sicor.Tools.EnMAP.optimal_estimation import invert_function
from sicor.Tools.EnMAP.multiprocessing import SharedNdarray, initializer, mp_progress_bar
......@@ -284,14 +284,8 @@ class FoGen(object):
# LUT interpolation
vtest = np.asarray([pt[0], pt[1], pt[2], pt[3], pt[4], xx[0]])
f_int = interpol_lut_c(lut1=self.lut1_fit,
lut2=self.lut2_fit,
xnodes=self.xnodes,
nm_nodes=self.nm_nodes,
ndim=self.ndim,
x_cell=self.x_cell,
vtest=vtest,
intp_wvl=self.wvl_sel)
f_int = interpol_lut(lut1=self.lut1_fit, lut2=self.lut2_fit, xnodes=self.xnodes, nm_nodes=self.nm_nodes,
ndim=self.ndim, x_cell=self.x_cell, vtest=vtest, intp_wvl=self.wvl_sel)
f_int_l0 = f_int[0, :] * 1.e+3
f_int_edir = f_int[1, :] * 1.e+3
......@@ -326,23 +320,11 @@ class FoGen(object):
vtest = np.asarray([pt[0], pt[1], pt[2], pt[3], pt[4], xx[0]])
if mode == "full":
f_int = interpol_lut_c(lut1=self.lut1_full,
lut2=self.lut2_full,
xnodes=self.xnodes,
nm_nodes=self.nm_nodes,
ndim=self.ndim,
x_cell=self.x_cell,
vtest=vtest,
intp_wvl=self.wvl)
f_int = interpol_lut(lut1=self.lut1_full, lut2=self.lut2_full, xnodes=self.xnodes, nm_nodes=self.nm_nodes,
ndim=self.ndim, x_cell=self.x_cell, vtest=vtest, intp_wvl=self.wvl)
else:
f_int = interpol_lut_c(lut1=self.lut1_fit,
lut2=self.lut2_fit,
xnodes=self.xnodes,
nm_nodes=self.nm_nodes,
ndim=self.ndim,
x_cell=self.x_cell,
vtest=vtest,
intp_wvl=self.wvl_sel)
f_int = interpol_lut(lut1=self.lut1_fit, lut2=self.lut2_fit, xnodes=self.xnodes, nm_nodes=self.nm_nodes,
ndim=self.ndim, x_cell=self.x_cell, vtest=vtest, intp_wvl=self.wvl_sel)
f_int_l0 = f_int[0, :] * 1.e+3 * self.fac
f_int_edir = f_int[1, :] * 1.e+3 * self.fac
......@@ -606,23 +588,43 @@ class Fo(object):
self.wvl_swir = np.array(enmap_l1b.swir.detector_meta.wvl_center[:])
self.fwhm_swir = np.array(enmap_l1b.swir.detector_meta.fwhm[:])
# get solar irradiances for absorption feature shoulders wavelengths
self.logger.info("Getting solar irradiances for absorption feature shoulders wavelengths...")
new_kur_fn = get_data_file(module_name="sicor", file_basename="newkur_EnMAP.dat")
new_kur = pd.read_csv(new_kur_fn, delim_whitespace=True)
freq_0 = 10e6 / self.wvl_sel[0]
freq_1 = 10e6 / self.wvl_sel[-1]
solar_0 = np.zeros(1)
solar_1 = np.zeros(1)
# load solar irradiance model
self.logger.info("Loading solar irradiance model...")
self.sol_model = options["retrieval"]["sol_model"]
for ii in range(1, new_kur.shape[0]):
if int(freq_0) - int(new_kur.at[ii, "FREQ"]) == 0:
solar_0 = new_kur.at[ii, "SOLAR"]
if int(freq_1) - int(new_kur.at[ii, "FREQ"]) == 0:
solar_1 = new_kur.at[ii, "SOLAR"]
try:
assert self.sol_model == "new_kurucz" or self.sol_model == "fontenla"
except AssertionError:
raise AssertionError("No valid solar model is provided. Please indicate either 'new_kurucz' or 'fontenla' "
"in the options file.")
self.s0 = float(solar_0) * (10e6 / self.wvl_sel[0]) ** 2
self.s1 = float(solar_1) * (10e6 / self.wvl_sel[-1]) ** 2
if self.sol_model == "new_kurucz":
new_kur_fn = get_data_file(module_name="sicor", file_basename="newkur_EnMAP.dat")
new_kur = pd.read_csv(new_kur_fn, delim_whitespace=True)
new_kur_wvl = 10e6 / np.asarray(new_kur["FREQ"][1:], dtype=float)
new_kur_sol = np.asarray(new_kur["SOLAR"][1:], dtype=float) * (10e6 / new_kur_wvl) ** 2
self.solar_lut = {"wvl": new_kur_wvl, "sol_irr": new_kur_sol}
# get solar irradiances for shoulders of 1140 nm water absorption feature
s_norm_new_kur_fit = generate_filter(wvl_m=new_kur_wvl, wvl=self.wvl_sel, wl_resol=self.fwhm_sel)
new_kur_sol_fit = new_kur_sol @ s_norm_new_kur_fit
self.s0 = new_kur_sol_fit[0]
self.s1 = new_kur_sol_fit[-1]
elif self.sol_model == "fontenla":
fon_fn = get_data_file(module_name="sicor", file_basename="fontenla_EnMAP.dat")
fon = pd.read_csv(fon_fn, delim_whitespace=True)
fon_wvl = np.asarray(fon["um=micron"], dtype=float) * 1000
fon_sol = np.asarray(fon["E0"], dtype=float) * 10
self.solar_lut = {"wvl": fon_wvl, "sol_irr": fon_sol}
# get solar irradiances for shoulders of 1140 nm water absorption feature
s_norm_fon_fit = generate_filter(wvl_m=fon_wvl, wvl=self.wvl_sel, wl_resol=self.fwhm_sel)
new_kur_sol_fit = fon_sol @ s_norm_fon_fit
self.s0 = new_kur_sol_fit[0]
self.s1 = new_kur_sol_fit[-1]
# load RT LUT
# check if LUT file is available
......@@ -655,6 +657,33 @@ class Fo(object):
self.ndim = ndim
self.x_cell = x_cell
# applying spectral conversion factor to LUT in case Fontenla solar model is to be used
if self.sol_model == "fontenla":
self.logger.info("Converting LUT to Fontenla solar model...")
path_conv_fac = get_data_file(module_name="sicor", file_basename="conv_fac_fon_lut.dill")
with open(path_conv_fac, "rb") as fl:
conv_fac_fon_lut = dill.load(fl)
lut1_fon = np.zeros(lut1.shape)
lut2_fon = np.zeros(lut2.shape)
for ii in range(5):
for jj in range(6):
for kk in range(4):
for ll in range(6):
for mm in range(7):
lut1_fon[ii, jj, kk, ll, mm, 0, :, 0] = lut1[ii, jj, kk, ll, mm, 0, :, 0] * \
conv_fac_fon_lut
for nn in range(2):
lut2_fon[ii, jj, kk, ll, 0, mm, :, nn] = lut2[ii, jj, kk, ll, 0, mm, :, nn] * \
conv_fac_fon_lut
for ii in range(2):
lut2_fon[:, :, :, :, :, :, :, ii + 2] = lut2[:, :, :, :, :, :, :, ii + 2]
lut1 = lut1_fon
lut2 = lut2_fon
# resampling LUT to EnMAP wavelengths
self.logger.info("Resampling LUT to EnMAP wavelengths...")
......@@ -715,45 +744,27 @@ class Fo(object):
self.abs_co_w = 4 * np.pi * self.kw / self.wvl_sel
self.abs_co_i = 4 * np.pi * self.ki / self.wvl_sel
# calculate mean solar exoatmospheric irradiances
self.logger.info("Calculating mean solar exoatmospheric irradiances...")
path_sol = get_data_file(module_name="sicor", file_basename="solar_irradiances_400_2500_1.dill")
with open(path_sol, "rb") as fl:
solar_lut = dill.load(fl)
self.solar_res = solar_lut @ self.s_norm_fit
# perform first guess water vapor retrieval based on a common band ratio using VNIR data
warnings.filterwarnings("ignore")
if self.use_prior_mean[0]:
self.cwv_fg = np.full(self.data_vnir.shape[:2], self.prior_mean[0])
else:
logger.info("Performing first guess water vapor retrieval based on a common band ratio using VNIR data...")
self.cwv_fg = wv_band_ratio(data=self.data_vnir,
water_msk=self.water_mask_vnir,
land_only=self.land_only,
fn_table=self.fn_table,
vza=self.pt[:, :, 0].mean(),
sza=self.pt[:, :, 1].mean(),
dem=self.hsf["vnir"].mean(),
aot=self.pt[:, :, 3].mean(),
raa=self.pt[:, :, 4].mean(),
intp_wvl=self.wvl_vnir,
intp_fwhm=self.fwhm_vnir,
jday=self.jday,
month=self.month,
idx=[73, 76, 81],
disable=self.disable_progressbars)
self.cwv_fg = wv_band_ratio(data=self.data_vnir, water_msk=self.water_mask_vnir, fn_table=self.fn_table,
sol_model=self.solar_lut, vza=self.pt[:, :, 0].mean(),
sza=self.pt[:, :, 1].mean(), dem=self.hsf["vnir"].mean(),
aot=self.pt[:, :, 3].mean(), raa=self.pt[:, :, 4].mean(),
intp_wvl=self.wvl_vnir, intp_fwhm=self.fwhm_vnir, jday=self.jday,
month=self.month, idx=[73, 76, 81], disable=self.disable_progressbars)
self.cwv_fg[np.logical_or(np.isnan(self.cwv_fg), np.isinf(self.cwv_fg))] = self.prior_mean[0]
self.cwv_fg[self.water_mask_vnir != 1] = self.prior_mean[0]
# transform CWV first guess map to SWIR sensor geometry to enable segmentation and 3 phases of water retrieval
logger.info("Transforming CWV first guess map to SWIR sensor geometry to enable segmentation and 3 phases of "
"water retrieval...")
self.cwv_fg_trans = enmap_l1b.transform_vnir_to_swir_raster(array_vnirsensorgeo=self.cwv_fg,
resamp_alg=self.resamp_alg,
respect_keystone=False)
resamp_alg=self.resamp_alg, respect_keystone=False)
self.cwv_fg_trans[self.cwv_fg_trans == 0.0] = self.prior_mean[0]
......@@ -815,8 +826,7 @@ class Fo(object):
if self.segmentation:
self.logger.info("Segmenting SWIR L1B spectra to enhance processing speed...")
self.X, self.segs, self.labels = SLIC_segmentation(data_rad_all=self.data_swir,
n_pca=self.n_pca,
self.X, self.segs, self.labels = SLIC_segmentation(data_rad_all=self.data_swir, n_pca=self.n_pca,
segs=self.segs)
# prepare segmented SWIR L1B data cube
......@@ -886,9 +896,9 @@ class Fo(object):
# LUT interpolation
vtest = np.asarray([pt[2], xx[0]])
f_int = interpol_lut_c_red(lut1=self.lut_red_fit[:, :, :, 0], lut2=self.lut_red_fit[:, :, :, 1:],
xnodes=self.xnodes_red, nm_nodes=self.nm_nodes_red, ndim=self.ndim_red,
x_cell=self.x_cell_red, vtest=vtest, intp_wvl=self.wvl_sel)
f_int = interpol_lut_red(lut1=self.lut_red_fit[:, :, :, 0], lut2=self.lut_red_fit[:, :, :, 1:],
xnodes=self.xnodes_red, nm_nodes=self.nm_nodes_red, ndim=self.ndim_red,
x_cell=self.x_cell_red, vtest=vtest, intp_wvl=self.wvl_sel)
f_int_l0 = f_int[0, :] * 1.e+3
f_int_edir = f_int[1, :] * 1.e+3
......@@ -923,17 +933,17 @@ class Fo(object):
vtest = np.asarray([pt[2], xx])
if mode == "vnir":
f_int = interpol_lut_c_red(lut1=self.lut_red_vnir[:, :, :, 0], lut2=self.lut_red_vnir[:, :, :, 1:],
xnodes=self.xnodes_red, nm_nodes=self.nm_nodes_red, ndim=self.ndim_red,
x_cell=self.x_cell_red, vtest=vtest, intp_wvl=self.wvl_vnir)
f_int = interpol_lut_red(lut1=self.lut_red_vnir[:, :, :, 0], lut2=self.lut_red_vnir[:, :, :, 1:],
xnodes=self.xnodes_red, nm_nodes=self.nm_nodes_red, ndim=self.ndim_red,
x_cell=self.x_cell_red, vtest=vtest, intp_wvl=self.wvl_vnir)
elif mode == "swir":
f_int = interpol_lut_c_red(lut1=self.lut_red_swir[:, :, :, 0], lut2=self.lut_red_swir[:, :, :, 1:],
xnodes=self.xnodes_red, nm_nodes=self.nm_nodes_red, ndim=self.ndim_red,
x_cell=self.x_cell_red, vtest=vtest, intp_wvl=self.wvl_swir)
f_int = interpol_lut_red(lut1=self.lut_red_swir[:, :, :, 0], lut2=self.lut_red_swir[:, :, :, 1:],
xnodes=self.xnodes_red, nm_nodes=self.nm_nodes_red, ndim=self.ndim_red,
x_cell=self.x_cell_red, vtest=vtest, intp_wvl=self.wvl_swir)
else:
f_int = interpol_lut_c_red(lut1=self.lut_red_fit[:, :, :, 0], lut2=self.lut_red_fit[:, :, :, 1:],
xnodes=self.xnodes_red, nm_nodes=self.nm_nodes_red, ndim=self.ndim_red,
x_cell=self.x_cell_red, vtest=vtest, intp_wvl=self.wvl_sel)
f_int = interpol_lut_red(lut1=self.lut_red_fit[:, :, :, 0], lut2=self.lut_red_fit[:, :, :, 1:],
xnodes=self.xnodes_red, nm_nodes=self.nm_nodes_red, ndim=self.ndim_red,
x_cell=self.x_cell_red, vtest=vtest, intp_wvl=self.wvl_sel)
f_int_l0 = f_int[0, :] * 1.e+3 * self.fac
f_int_edir = f_int[1, :] * 1.e+3 * self.fac
......@@ -1127,9 +1137,7 @@ def __minimize__(fo, opt_func, unknowns=False, logger=None):
if platform.system() == "Windows" and processes > 1:
logger.warning('Multiprocessing is currently not available on Windows.')
if platform.system() == "Darwin" and processes > 1:
logger.warning('Multiprocessing is currently not available on macOS.')
if platform.system() == "Windows" or platform.system() == "Darwin" or processes == 1:
if platform.system() == "Windows" or processes == 1:
logger.info("Singleprocessing on 1 cpu")
else:
logger.info("Setting up multiprocessing...")
......@@ -1234,11 +1242,12 @@ def __minimize__(fo, opt_func, unknowns=False, logger=None):
# check if operating system is 'Windows'; in that case, multiprocessing is currently not working
# TODO: enable Windows compatibility for multiprocessing
if platform.system() == "Windows" or platform.system() == "Darwin" or processes == 1:
if platform.system() == "Windows" or processes == 1:
initializer(globals(), globs)
[mp_fun(ii) for ii in tqdm(rng, disable=fo.disable_progressbars)]
else:
with closing(Pool(processes=processes, initializer=initializer, initargs=(globals(), globs,))) as pl:
with closing(mp.get_context("fork").Pool(processes=processes, initializer=initializer, initargs=(globals(),
globs))) as pl:
results = pl.map_async(mp_fun, rng, chunksize=1)
if not fo.disable_progressbars:
bar = ProgressBar(prefix='\tprogress:')
......@@ -1354,25 +1363,11 @@ def __oe__(ii):
__retrieval_noise__[i1, i2, :, :] = np.full((__state_dim__, __state_dim__), np.nan)
__smoothing_error__[i1, i2, :, :] = np.full((__state_dim__, __state_dim__), np.nan)
else:
se = __calc_se__(xx=__xa__[i1, i2, :],
dt=__data__[i1, i2, :],
pt=__pt__[i1, i2, :],
sb=__sb__,
num_bd=len(__fit_wvl__),
snr=__snr__,
unknowns=__unknowns__)
res = __inv_func__(yy=__data__[i1, i2, :],
fparam=__pt__[i1, i2, :],
ll=__ll__,
ul=__ul__,
xa=__xa__[i1, i2, :],
sa=__sa__,
se=se,
gnform=__gnform__,
full=__full__,
maxiter=__maxiter__,
eps=__eps__)
se = __calc_se__(xx=__xa__[i1, i2, :], dt=__data__[i1, i2, :], pt=__pt__[i1, i2, :], sb=__sb__,
num_bd=len(__fit_wvl__), snr=__snr__, unknowns=__unknowns__)
res = __inv_func__(yy=__data__[i1, i2, :], fparam=__pt__[i1, i2, :], ll=__ll__, ul=__ul__, xa=__xa__[i1, i2, :],
sa=__sa__, se=se, gnform=__gnform__, full=__full__, maxiter=__maxiter__, eps=__eps__)
model = __forward__(xx=res[0], pt=__pt__[i1, i2, :], dt=__data__[i1, i2, :], model_output=True)
......
um=micron E0 [mW cm-2 um-1]
0.340000 99.5409
0.340400 89.2976
0.340800 78.8881
0.341200 71.5445
0.341600 41.3989
0.342000 85.2311
0.342400 86.4505
0.342800 92.3677
0.343200 88.6984
0.343600 83.6977
0.344000 94.7999
0.344400 68.0211
0.344800 70.2879
0.345200 93.1321
0.345600 91.3573
0.346000 76.4509
0.346400 85.9302
0.346800 100.410
0.347200 103.579
0.347600 98.1658
0.348000 112.979
0.348400 104.043
0.348800 109.563
0.349200 93.3709
0.349600 85.7161
0.350000 96.6514
0.350400 99.9531
0.350800 97.2223
0.351200 80.0129
0.351600 68.5800
0.352000 87.5890
0.352400 55.9125
0.352800 73.6134
0.353200 98.7685
0.353600 110.436
0.354000 107.339
0.354400 108.340
0.354800 114.313
0.355200 107.911
0.355600 115.134
0.356000 111.694
0.356400 97.2204
0.356800 63.5700
0.357200 65.9983
0.357600 100.011
0.358000 84.6363
0.358400 76.8585
0.358800 85.1115
0.359200 103.980
0.359600 97.6458
0.360000 106.255
0.360400 108.610
0.360800 93.5722
0.361200 84.9289
0.361600 102.520
0.362000 61.4782
0.362400 94.5475
0.362800 113.353
0.363200 80.1820
0.363600 107.538
0.364000 111.311
0.364400 111.357
0.364800 102.846
0.365200 119.163
0.365600 130.877
0.366000 129.201
0.366400 133.553
0.366800 128.804
0.367200 108.587
0.367600 113.722
0.368000 105.789
0.368400 113.349
0.368800 99.9201
0.369200 113.900
0.369600 130.075
0.370000 128.090
0.370400 106.575
0.370800 93.4782
0.371200 105.525
0.371600 133.255
0.372000 90.8171
0.372400 82.3366
0.372800 106.945
0.373200 100.787
0.373600 52.1067
0.374000 87.8736
0.374400 93.5405
0.374800 66.1538
0.375200 87.2177
0.375600 116.657
0.376000 84.2087
0.376400 99.9415
0.376800 100.572
0.377200 112.788
0.377600 122.282
0.378000 135.545
0.378400 135.910
0.378800 109.799
0.379200 117.620
0.379600 102.093
0.380000 89.8601
0.380400 121.583
0.380800 106.395
0.381200 126.302
0.381600 96.6049
0.382000 96.9899
0.382400 93.7464
0.382800 73.6723
0.383200 81.8369
0.383600 69.1736
0.384000 68.8665
0.384400 97.6600
0.384800 102.794
0.385200 102.210
0.385600 102.623
0.386000 70.1074
0.386400 109.354
0.386800 103.341
0.387200 81.9115
0.387600 98.5518
0.388000 88.3204
0.388400 88.4511
0.388800 80.5159
0.389200 110.322
0.389600 114.031
0.390000 113.941
0.390400 113.231
0.390800 118.219
0.391200 137.645
0.391600 132.523
0.392000 120.756
0.392400 98.8580
0.392800 88.5233
0.393200 44.8496
0.393600 36.5956
0.394000 78.5437
0.394400 97.1765
0.394800 123.644
0.395200 127.807
0.395600 128.301
0.396000 120.476
0.396400 82.3930
0.396800 45.5043
0.397200 53.6112
0.397600 102.298
0.398000 139.336
0.398400 148.154
0.398800 163.276
0.399200 164.113
0.399600 153.280
0.400000 153.514
0.400400 168.330
0.400800 152.504
0.401200 168.633
0.401600 175.539
0.402000 174.737
0.402400 171.650
0.402800 181.000
0.403200 178.902
0.403600 182.953
0.404000 189.186
0.404400 175.439
0.404800 151.063
0.405200 185.641
0.405600 178.003
0.406000 174.791
0.406400 148.218
0.406800 157.597
0.407200 149.415
0.407600 165.342
0.408000 174.155
0.408400 175.692
0.408800 177.395
0.409200 169.904
0.409600 161.680
0.410000 141.322
0.410400 138.461
0.410800 155.968
0.411200 160.769
0.411600 172.120
0.412000 161.384
0.412400 171.508
0.412800 174.529
0.413200 169.412
0.413600 163.583
0.414000 182.770
0.414400 165.327
0.414800 178.019
0.415200 180.181
0.415600 177.088
0.416000 182.838
0.416400 185.229
0.416800 187.939
0.417200 168.853
0.417600 161.684
0.418000 172.402
0.418400 168.385
0.418800 161.728
0.419200 183.049
0.419600 180.292
0.420000 155.024
0.420400 164.471
0.420800 181.816
0.421200 182.869
0.421600 179.841
0.422000 176.911
0.422400 171.523
0.422800 137.338
0.423200 179.878
0.423600 169.431
0.424000 178.960
0.424400 179.740
0.424800 176.614
0.425200 161.014
0.425600 181.001
0.426000 171.528
0.426400 181.755
0.426800 176.289
0.427200 144.759
0.427600 159.333
0.428000 169.528
0.428400 159.567
0.428800 145.558
0.429200 137.412
0.429600 155.055
0.430000 129.293
0.430400 113.827
0.430800 110.623
0.431200 113.305
0.431600 173.601
0.432000 200.453
0.432400 165.858
0.432800 152.705
0.433200 187.778
0.433600 190.208
0.434000 142.184
0.434400 157.376
0.434800 181.891
0.435200 154.873
0.435600 164.719
0.436000 185.660
0.436400 193.316
0.436800 184.854
0.437200 184.050
0.437600 182.007
0.438000 183.216
0.438400 143.921
0.438800 162.625
0.439200 172.198
0.439600 178.035
0.440000 188.617
0.440400 169.031
0.440800 165.504
0.441200 192.766
0.441600 171.720
0.442000 194.215
0.442400 196.176
0.442800 202.660
0.443200 196.442
0.443600 172.588