diff --git a/.travis.yml b/.travis.yml index 027346f8311408e148fbbcf3870bb0ed67090061..9fce481a544bacae0bdfe49eaf0ff598e9d5bce4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,6 +43,7 @@ install: # Install not using env because build needs to be in root env - conda config --add channels conda-forge - conda config --add channels jlaura + - conda install python=$PYTHON_VERSION - conda install -c conda-forge gdal h5py - conda install pandas sqlalchemy pyyaml networkx - conda install -c jlaura pvl protobuf diff --git a/README.rst b/README.rst index b20dfd71fd8f3077ee035b7b8fe138e6f5770549..c15a55c5faa782be65cce5c594ccf38aff2ddfdc 100644 --- a/README.rst +++ b/README.rst @@ -28,12 +28,12 @@ Installation is unfortunately complicated due to the difficulty in installing GD 1. Install Anaconda or Miniconda with Python 3.5 2. In your ~/.condarc file add the following two lines in the channels section. - + `- conda-forge` - + `- jlaura` -3. Create a new environment to ensure that the installed packge is not going to collide with existing packages - - ` conda create --name <somename> python=3` - -3. Install `plio` with `conda install plio`. +3. Create a new environment to ensure that the installed package is not going to collide with existing packages + + ``conda create --name <somename> python=3`` + +3. Install ``plio`` with ``conda install plio``. diff --git a/appveyor.yml b/appveyor.yml index 47dedfaf6f75f9393f5a874551481630c1d5e354..58fa4f2ff020fce49d20478182eb8fd5aa0e33d0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,6 +40,7 @@ install: - cmd: set PYTHONUNBUFFERED=1 - cmd: conda config --set show_channel_urls true + - cmd: conda install --yes python=3.5 - cmd: conda install -c pelson/channel/development --yes --quiet obvious-ci - cmd: conda config --add channels conda-forge - cmd: conda info diff --git a/plio/examples/Tes/pos.fmt b/plio/examples/Tes/pos.fmt new file mode 100755 index 0000000000000000000000000000000000000000..6a50148f13522e3f933f9c1f966630138c396568 --- /dev/null +++ b/plio/examples/Tes/pos.fmt @@ -0,0 +1,87 @@ +NAME = POS +COLUMNS = 6 +ROW_BYTES = 54 +INTERCHANGE_FORMAT = BINARY +PRIMARY_KEY = ( "SPACECRAFT_CLOCK_START_COUNT" ) +DESCRIPTION = " + + The POS table stores the positions of the spacecraft and sun + relative to the planet, the spacecraft's orientation quaternion, + and the Mars body quaternion, all relative to the J2000 system. + + These data are initially derived from the project's SPICE kernels, + but may be corrected from various other sources. This table + may also include interpolated values where SPICE data were unavailable." + +OBJECT = COLUMN + NAME = SPACECRAFT_CLOCK_START_COUNT + DATA_TYPE = MSB_UNSIGNED_INTEGER + START_BYTE = 1 + BYTES = 4 + ALIAS_NAME = sclk_time + DESCRIPTION = "The value of the spacecraft clock at the + beginning of the observation" +END_OBJECT = COLUMN + + +OBJECT = COLUMN + NAME = EPHEMERIS_TIME + DATA_TYPE = IEEE_REAL + START_BYTE = 5 + BYTES = 8 + ALIAS_NAME = et + DESCRIPTION = "Ephemeris time, seconds since 1/1/2000" + UNIT = "Seconds" +END_OBJECT = COLUMN + +OBJECT = COLUMN + NAME = SPACECRAFT_POSITION + DATA_TYPE = IEEE_REAL + START_BYTE = 13 + BYTES = 12 + ITEMS = 3 + ITEM_BYTES = 4 + ALIAS_NAME = pos + DESCRIPTION = "Spacecraft position vector relative to Mars + in the J2000 reference frame" + UNIT = "KM" +END_OBJECT = COLUMN + +OBJECT = COLUMN + NAME = SUN_POSITION + DATA_TYPE = IEEE_REAL + START_BYTE = 25 + BYTES = 12 + ITEMS = 3 + ITEM_BYTES = 4 + ALIAS_NAME = sun + DESCRIPTION = "Sun position vector relative to Mars in the J2000 + reference frame" + UNIT = "KM" +END_OBJECT = COLUMN + +OBJECT = COLUMN + NAME = SPACECRAFT_QUATERNION + DATA_TYPE = IEEE_REAL + START_BYTE = 37 + BYTES = 16 + ITEMS = 4 + ITEM_BYTES = 4 + ALIAS_NAME = quat + DESCRIPTION = "Spacecraft pointing quaternion in the J2000 + reference frame" +END_OBJECT = COLUMN + +OBJECT = COLUMN + NAME = POSITION_SOURCE_ID + DATA_TYPE = CHARACTER + START_BYTE = 53 + BYTES = 2 + ITEMS = 2 + ITEM_BYTES = 1 + ALIAS_NAME = id + DESCRIPTION = "2-character source ID. + First character is source of positions. + Second character is source of pointing. + See ancillary table for details." +END_OBJECT = COLUMN diff --git a/plio/examples/Tes/pos10001.tab b/plio/examples/Tes/pos10001.tab new file mode 100755 index 0000000000000000000000000000000000000000..04ea14e022940553d566ca2ac4ff8e1c5b5d2ba2 Binary files /dev/null and b/plio/examples/Tes/pos10001.tab differ diff --git a/plio/io/io_tes.py b/plio/io/io_tes.py new file mode 100644 index 0000000000000000000000000000000000000000..66a6cd141d77ca7c281168e0fab7dd6b09d97c83 --- /dev/null +++ b/plio/io/io_tes.py @@ -0,0 +1,246 @@ +import numpy as np +import pandas as pd +import pvl + +import sys +import functools +import json + +from plio.io.io_json import read_json + +from plio.utils._tes2numpy import tes_dtype_map +from plio.utils._tes2numpy import tes_columns +from plio.utils._tes2numpy import tes_scaling_factors + +class Tes(object): + """ + Attributes + ---------- + + spectra : panel + A pandas panel containing n individual spectra. + + ancillary_data : dataframe + A pandas DataFrame of the parsed ancillary data (PVL label) + + label : object + The raw PVL label object + + """ + + + def __init__(self, input_data, var_file = None): + """ + Read the .spc file, parse the label, and extract the spectra + + Parameters + ---------- + + input_data : string + The PATH to the input .tab file + + """ + def expand_column(df, expand_column, columns): # pragma: no cover + array = np.asarray([np.asarray(list(tup[0])) for tup in df[expand_column].as_matrix()], dtype=np.uint8) + new_df = pd.concat([df, pd.DataFrame(array, columns=columns)], axis=1) + del new_df[expand_column] + return new_df + + def bolquality2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:3]), bit2bool(bitarr[3:4]))] + + types = [('BOLOMETRIC_INERTIA_RATING', '>u1'), ('BOLOMETER_LAMP_ANOMALY', 'bool_')] + arr = np.array(lis, dtype=types) + return arr + + def obsquality2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:2]), bitarr2int(bitarr[2:5]), + bitarr2int(bitarr[5:6]), bitarr2int(bitarr[6:7]), + bitarr2int(bitarr[7:8]), bitarr2int(bitarr[8:9]))] + + types = [('HGA_MOTION', '>u1'), ('SOLAR_PANEL_MOTION', '>u1'), ('ALGOR_PATCH', '>u1'), + ('IMC_PATCH', '>u1'), ('MOMENTUM_DESATURATION', '>u1'), ('EQUALIZATION_TABLE', '>u1')] + arr = np.array(lis, dtype=types) + return arr + + def obsclass2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:3]), bitarr2int(bitarr[3:7]), + bitarr2int(bitarr[7:11]), bitarr2int(bitarr[11:13]), + bitarr2int(bitarr[13:14]), bitarr2int(bitarr[14:16]), + bitarr2int(bitarr[16:]))] + + types = [('MISSION_PHASE', '>u1'), ('INTENDED_TARGET', '>u1'), ('TES_SEQUENCE', '>u1'), + ('NEON_LAMP_STATUS', '>u1'), ('TIMING_ACCURACY', '>u1'), ('SPARE', '>u1'), ('CLASSIFICATION_VALUE', '>u2')] + arr = np.array(lis, dtype=types) + return arr + + def radquality2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:1]), bitarr2int(bitarr[1:2]), + bitarr2int(bitarr[2:3]), bitarr2int(bitarr[3:5]), + bitarr2int(bitarr[5:7]), bitarr2int(bitarr[5:8]), + bitarr2int(bitarr[8:9]))] + + types = [('MAJOR_PHASE_INVERSION', '>u1'), ('ALGOR_RISK', '>u1'), ('CALIBRATION_FAILURE', '>u1'), + ('CALIBRATION_QUALITY', '>u1'), ('SPECTROMETER_NOISE', '>u1'), ('SPECTRAL_INERTIA_RATING', '>u1'), + ('DETECTOR_MASK_PROBLEM', '>u1')] + arr = np.array(lis, dtype=types) + return arr + + def atmquality2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:2]), bitarr2int(bitarr[2:4]))] + + types = [('TEMPERATURE_PROFILE_RATING', '>u1'), ('ATMOSPHERIC_OPACITY_RATING', '>u1')] + arr = np.array(lis, dtype=types) + return arr + + def expand_column(df, expand_column, columns): # pragma: no cover + array = np.asarray([np.asarray(list(tup[0])) for tup in df[expand_column].as_matrix()], dtype=np.uint8) + new_df = pd.concat([df, pd.DataFrame(array, columns=columns)], axis=1) + del new_df[expand_column] + return new_df + + def bolquality2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:3]), bit2bool(bitarr[3:4]))] + + types = [('BOLOMETRIC_INERTIA_RATING', '>u1'), ('BOLOMETER_LAMP_ANOMALY', 'bool_')] + arr = np.array(lis, dtype=types) + return arr + + def obsquality2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:2]), bitarr2int(bitarr[2:5]), + bitarr2int(bitarr[5:6]), bitarr2int(bitarr[6:7]), + bitarr2int(bitarr[7:8]), bitarr2int(bitarr[8:9]))] + + types = [('HGA_MOTION', '>u1'), ('SOLAR_PANEL_MOTION', '>u1'), ('ALGOR_PATCH', '>u1'), + ('IMC_PATCH', '>u1'), ('MOMENTUM_DESATURATION', '>u1'), ('EQUALIZATION_TABLE', '>u1')] + arr = np.array(lis, dtype=types) + return arr + + def obsclass2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:3]), bitarr2int(bitarr[3:7]), + bitarr2int(bitarr[7:11]), bitarr2int(bitarr[11:13]), + bitarr2int(bitarr[13:14]), bitarr2int(bitarr[14:16]), + bitarr2int(bitarr[16:]))] + + types = [('MISSION_PHASE', '>u1'), ('INTENDED_TARGET', '>u1'), ('TES_SEQUENCE', '>u1'), + ('NEON_LAMP_STATUS', '>u1'), ('TIMING_ACCURACY', '>u1'), ('SPARE', '>u1'), ('CLASSIFICATION_VALUE', '>u2')] + arr = np.array(lis, dtype=types) + return arr + + def radquality2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:1]), bitarr2int(bitarr[1:2]), + bitarr2int(bitarr[2:3]), bitarr2int(bitarr[3:5]), + bitarr2int(bitarr[5:7]), bitarr2int(bitarr[5:8]), + bitarr2int(bitarr[8:9]))] + + types = [('MAJOR_PHASE_INVERSION', '>u1'), ('ALGOR_RISK', '>u1'), ('CALIBRATION_FAILURE', '>u1'), + ('CALIBRATION_QUALITY', '>u1'), ('SPECTROMETER_NOISE', '>u1'), ('SPECTRAL_INERTIA_RATING', '>u1'), + ('DETECTOR_MASK_PROBLEM', '>u1')] + arr = np.array(lis, dtype=types) + return arr + + def atmquality2arr(arr): # pragma: no cover + bitarr = np.unpackbits(np.asarray(arr, dtype=np.uint8)) + lis = [(bitarr2int(bitarr[0:2]), bitarr2int(bitarr[2:4]))] + + types = [('TEMPERATURE_PROFILE_RATING', '>u1'), ('ATMOSPHERIC_OPACITY_RATING', '>u1')] + arr = np.array(lis, dtype=types) + return arr + + def bitarr2int(arr): # pragma: no cover + arr = "".join(str(i) for i in arr) + return np.uint8(int(arr,2)) + + def bit2bool(bit): # pragma: no cover + return np.bool_(bit) + + def expand_bitstrings(df, dataset): # pragma: no cover + if dataset == 'BOL': + quality_columns = ['ti_bol_rating', 'bol_ref_lamp'] + df['quality'] = df['quality'].apply(bolquality2arr) + return expand_column(df, 'quality', quality_columns) + + elif dataset == 'OBS': + quality_columns = ['hga_motion', 'pnl_motion', 'algor_patch', 'imc_patch', + 'momentum', 'equal_tab'] + class_columns = ['phase', 'type', 'sequence', + 'lamp_status', 'timing', 'spare', 'class_value'] + + df['quality'] = df['quality'].apply(obsquality2arr) + df['class'] = df['class'].apply(obsclass2arr) + + new_df = expand_column(df, 'quality', quality_columns) + new_df = expand_column(new_df, 'class', class_columns) + return new_df + + elif dataset == 'RAD': + quality_columns = ['phase_inversion', 'algor_risk', 'calib_fail', 'calib_quality', + 'spect_noise', 'ti_spc_rating', 'det_mask_problem'] + + df['quality'] = df['quality'].apply(radquality2arr) + + return expand_column(df, 'quality', quality_columns) + + elif dataset == 'ATM': + quality_columns = ['atm_pt_rating', 'atm_opacity_rating'] + + df['quality'] = df['quality'].apply(atmquality2arr) + return expand_column(df, 'quality', quality_columns) + + else: + return df + + self.label = pvl.load(input_data) + nrecords = self.label['TABLE']['ROWS'] + nbytes_per_rec = self.label['RECORD_BYTES'] + data_start = self.label['LABEL_RECORDS'] * self.label['RECORD_BYTES'] + dataset = self.label['TABLE']['^STRUCTURE'].split('.')[0] + + numpy_dtypes = tes_dtype_map + columns = tes_columns + scaling_factors = tes_scaling_factors + + with open(input_data, 'rb') as file: + file.seek(data_start) + buffer = file.read(nrecords*nbytes_per_rec) + array = np.frombuffer(buffer, dtype=numpy_dtypes[dataset.upper()]).byteswap().newbyteorder() + + df = pd.DataFrame(data=array, columns=columns[dataset.upper()]) + + # Read Radiance array if applicable + if dataset.upper() == 'RAD': # pragma: no cover + with open('{}.var'.format(path.splitext(f)[0]) , 'rb') as file: + buffer = file.read() + def process_rad(index): + if index is -1: + return None + + length = np.frombuffer(buffer[index:index+2], dtype='>u2')[0] + exp = np.frombuffer(buffer[index+2:index+4], dtype='>i2')[0] + + radarr = np.frombuffer(buffer[index+4:index+4+length-2], dtype='>i2') * (2**(exp-15)) + if np.frombuffer(buffer[index+4+length-2:index+4+length], dtype='>u2')[0] != length: + warnings.warn("Last element did not match the length for file index {} in file {}".format(index, f)) + return radarr + + df["raw_rad"] = df["raw_rad"].apply(process_rad) + df["cal_rad"] = df["cal_rad"].apply(process_rad) + + # Apply scaling factors + for column in scaling_factors[dataset]: # pragma: no cover + def scale(x): + return np.multiply(x, scaling_factors[dataset][column]) + df[column] = df[column].apply(scale) + + df = expand_bitstrings(df, dataset.upper()) + + self.data = df diff --git a/plio/io/tests/test_io_spectral_profiler.py b/plio/io/tests/test_io_spectral_profiler.py index 3d05e05c4241e3fbc900aa9579072238a3bb06d6..772712de822305bc48f466675b3ee3f4c1d7208e 100644 --- a/plio/io/tests/test_io_spectral_profiler.py +++ b/plio/io/tests/test_io_spectral_profiler.py @@ -11,10 +11,10 @@ from plio.io import io_spectral_profiler from plio.io.io_gdal import GeoDataset class Test_Spectral_Profiler_IO(unittest.TestCase): - + def setUp(self): self.examplefile = get_path('SP_2C_02_02358_S138_E3586.spc') - + def test_openspc(self): ds = io_spectral_profiler.Spectral_Profiler(self.examplefile) self.assertEqual(ds.nspectra, 38) diff --git a/plio/io/tests/test_io_tes.py b/plio/io/tests/test_io_tes.py new file mode 100644 index 0000000000000000000000000000000000000000..7fa5d8d3cde9f673c2a069f8b0206355d27754eb --- /dev/null +++ b/plio/io/tests/test_io_tes.py @@ -0,0 +1,34 @@ +import os +import sys +import unittest + +import pandas as pd + +sys.path.insert(0, os.path.abspath('..')) + +from plio.examples import get_path +from plio.io import io_tes +from plio.io.io_gdal import GeoDataset +from plio.utils._tes2numpy import tes2numpy + + +class Test_Tes_IO(unittest.TestCase): + + def setUp(self): + self.examplefile = get_path('pos10001.tab') + + def test_opentab(self): + ds = io_tes.Tes(self.examplefile) + self.assertEqual(ds.data.size, 119106) + self.assertIsInstance(ds.data, pd.DataFrame) + self.assertEqual(ds.data.columns.tolist(), ['sclk_time', 'et', 'pos', 'sun', 'quat', 'id']) + + def test_tes2numpy(self): + self.assertEqual(tes2numpy('MSB_UNSIGNED_INTEGER', 4), '>u4') + self.assertEqual(tes2numpy('MSB_UNSIGNED_INTEGER', 4, 2), [('elem0', '>u4'), ('elem1', '>u4')]) + + with self.assertRaises(Exception): + tes2numpy('IEEE_REAL', 5) + +if __name__ == '__main__': + unittest.main() diff --git a/plio/utils/_tes2numpy.py b/plio/utils/_tes2numpy.py new file mode 100644 index 0000000000000000000000000000000000000000..bb74ab80607ee9c414f7b4aaca8ed46687d110fc --- /dev/null +++ b/plio/utils/_tes2numpy.py @@ -0,0 +1,405 @@ +def tes2numpy(msb_type, num_bytes, nelems=1): + """ + Converts a MSB data type to a numpy datatype + + """ + valid_bytes = { + 'MSB_UNSIGNED_INTEGER': [1,2,4,8,16,32,64], + 'MSB_INTEGER': [1,2,4,8,16,32,64], + 'IEEE_REAL': [1,2,4,8,16,32,64], + 'CHARACTER': range(1,128), + 'MSB_BIT_STRING': range(1,128) + } + + msb_bit_string_type = [('byte{}'.format(i), '>u1') for i in range(num_bytes)] + + dtype_map = { + 'MSB_UNSIGNED_INTEGER': '>u{}'.format(num_bytes), + 'MSB_INTEGER': '>i{}'.format(num_bytes), + 'IEEE_REAL': '>f{}'.format(num_bytes), + 'CHARACTER': 'a{}'.format(num_bytes), + 'MSB_BIT_STRING': msb_bit_string_type + } + + if num_bytes not in valid_bytes[msb_type] and nelems == 1: + raise Exception('invalid byte ({}) count for type ({})'.format(num_bytes, msb_type)) + + if nelems > 1: + # Must be an array + return [('elem{}'.format(i), dtype_map[msb_type]) for i in range(nelems)] + + + return dtype_map[msb_type] + + +tes_dtype_map = {'ATM': [('sclk_time', '>u4'), + ('srf_pressure', '>u2'), + ('nadir_pt', + [('elem0', '>u2'), + ('elem1', '>u2'), + ('elem2', '>u2'), + ('elem3', '>u2'), + ('elem4', '>u2'), + ('elem5', '>u2'), + ('elem6', '>u2'), + ('elem7', '>u2'), + ('elem8', '>u2'), + ('elem9', '>u2'), + ('elem10', '>u2'), + ('elem11', '>u2'), + ('elem12', '>u2'), + ('elem13', '>u2'), + ('elem14', '>u2'), + ('elem15', '>u2'), + ('elem16', '>u2'), + ('elem17', '>u2'), + ('elem18', '>u2'), + ('elem19', '>u2'), + ('elem20', '>u2'), + ('elem21', '>u2'), + ('elem22', '>u2'), + ('elem23', '>u2'), + ('elem24', '>u2'), + ('elem25', '>u2'), + ('elem26', '>u2'), + ('elem27', '>u2'), + ('elem28', '>u2'), + ('elem29', '>u2'), + ('elem30', '>u2'), + ('elem31', '>u2'), + ('elem32', '>u2'), + ('elem33', '>u2'), + ('elem34', '>u2'), + ('elem35', '>u2'), + ('elem36', '>u2'), + ('elem37', '>u2')]), + ('co2_cont_temp', '>u2'), + ('srf_temp_est', '>u2'), + ('rms_pt', '>f4'), + ('best_fit_opacities', + [('elem0', '>i2'), + ('elem1', '>i2'), + ('elem2', '>i2'), + ('elem3', '>i2'), + ('elem4', '>i2'), + ('elem5', '>i2'), + ('elem6', '>i2'), + ('elem7', '>i2'), + ('elem8', '>i2')]), + ('rms_opacities', '>f4'), + ('co2_dw_flux', '>f4'), + ('total_dw_flux', '>f4'), + ('quality', [('byte0', '>u1'), ('byte1', '>u1')]), + ('srf_radiance', '>i4'), + ('version_id', 'a4')], + 'BOL': [('sclk_time', '>u4'), + ('detector', '>u1'), + ('tic_count', '>u1'), + ('vbol', '>i2'), + ('tbol', '>i2'), + ('cal_vbol', '>f4'), + ('lambert_alb', '>f4'), + ('ti_bol', '>f4'), + ('brightness_temp_bol', '>u2'), + ('vbol_version_id', 'a2'), + ('tbol_version_id', 'a2'), + ('quality', [('byte0', '>u1'), ('byte1', '>u1')])], + 'GEO': [('sclk_time', '>u4'), + ('detector', '>u1'), + ('longitude', '>u2'), + ('latitude', '>i2'), + ('phase', '>u2'), + ('emission', '>u2'), + ('incidence', '>u2'), + ('planetary_phase', '>u2'), + ('heliocentric_lon', '>u2'), + ('sub_sc_lon', '>u2'), + ('sub_sc_lat', '>i2'), + ('sub_solar_lon', '>u2'), + ('sub_solar_lat', '>i2'), + ('target_distance', '>u2'), + ('height', '>u2'), + ('altitude', '>u2'), + ('local_time', '>u2'), + ('solar_distance', '>u2'), + ('angular_semidiameter', '>u2'), + ('version_id', 'a4')], + 'IFG': [('sclk_time', '>u4'), ('detector', '>u1'), ('ifgm', '>i4')], + 'OBS': [('sclk_time', '>u4'), + ('orbit', '>u2'), + ('ock', '>u2'), + ('ick', '>u4'), + ('tic', '>u1'), + ('pnt_angle', '>i2'), + ('pnt_imc', '>u1'), + ('pnt_view', 'a1'), + ('scan_len', 'a1'), + ('pckt_type', 'a1'), + ('schedule_type', 'a1'), + ('spc_gain', 'a1'), + ('vbol_gain', 'a1'), + ('tbol_gain', 'a1'), + ('comp_pp', '>u1'), + ('det_mask', '>u1'), + ('class', + [('byte0', '>u1'), ('byte1', '>u1'), ('byte2', '>u1'), ('byte3', '>u1')]), + ('quality', + [('byte0', '>u1'), ('byte1', '>u1'), ('byte2', '>u1'), ('byte3', '>u1')]), + ('temps', + [('elem0', '>u2'), ('elem1', '>u2'), ('elem2', '>u2'), ('elem3', '>u2')]), + ('ffti', '>u1')], + 'POS': [('sclk_time', '>u4'), + ('et', '>f8'), + ('pos', [('elem0', '>f4'), ('elem1', '>f4'), ('elem2', '>f4')]), + ('sun', [('elem0', '>f4'), ('elem1', '>f4'), ('elem2', '>f4')]), + ('quat', + [('elem0', '>f4'), ('elem1', '>f4'), ('elem2', '>f4'), ('elem3', '>f4')]), + ('id', [('elem0', 'a1'), ('elem1', 'a1')])], + 'RAD': [('sclk_time', '>u4'), + ('detector', '>u1'), + ('spectral_mask', '>u1'), + ('cmode', '>u2'), + ('raw_rad', '>i4'), + ('cal_rad', '>i4'), + ('tdet', '>u2'), + ('target_temp', '>u2'), + ('ti_spc', '>f4'), + ('version_id', 'a4'), + ('quality', + [('byte0', '>u1'), ('byte1', '>u1'), ('byte2', '>u1'), ('byte3', '>u1')])], + 'TLM': [('sclk_time', '>u4'), + ('aux_temps', + [('elem0', '>u2'), + ('elem1', '>u2'), + ('elem2', '>u2'), + ('elem3', '>u2'), + ('elem4', '>u2'), + ('elem5', '>u2'), + ('elem6', '>u2'), + ('elem7', '>u2'), + ('elem8', '>u2'), + ('elem9', '>u2'), + ('elem10', '>u2'), + ('elem11', '>u2')]), + ('ifgm_max', + [('elem0', '>i2'), + ('elem1', '>i2'), + ('elem2', '>i2'), + ('elem3', '>i2'), + ('elem4', '>i2'), + ('elem5', '>i2')]), + ('ifgm_min', + [('elem0', '>i2'), + ('elem1', '>i2'), + ('elem2', '>i2'), + ('elem3', '>i2'), + ('elem4', '>i2'), + ('elem5', '>i2')]), + ('dsp_log', + [('elem0', '>u2'), + ('elem1', '>u2'), + ('elem2', '>u2'), + ('elem3', '>u2'), + ('elem4', '>u2'), + ('elem5', '>u2')]), + ('V1', '>i1'), + ('V2', '>i1'), + ('V3', '>i1'), + ('V4', '>i1'), + ('V5', '>i1'), + ('V6', '>i1'), + ('V7', '>i1'), + ('V8', '>i1'), + ('V9', '>i1'), + ('V10', '>i1'), + ('V11', '>i1'), + ('V12', '>i1'), + ('V13', '>i1'), + ('V14', '>i1'), + ('V15', '>i1'), + ('V16', '>i1'), + ('V17', '>i1'), + ('V18', '>i1'), + ('V19', '>i1'), + ('V20', '>i1'), + ('neon_lamp', '>u1'), + ('neon_gain', 'a1'), + ('neon_amp', '>i1'), + ('neon_zpd', '>u2'), + ('ifgm_zpd', + [('elem0', '>u2'), + ('elem1', '>u2'), + ('elem2', '>u2'), + ('elem3', '>u2'), + ('elem4', '>u2'), + ('elem5', '>u2')]), + ('ifgm_end', + [('elem0', '>u2'), + ('elem1', '>u2'), + ('elem2', '>u2'), + ('elem3', '>u2'), + ('elem4', '>u2'), + ('elem5', '>u2')])]} + +tes_columns = {'ATM': ['sclk_time', + 'srf_pressure', + 'nadir_pt', + 'co2_cont_temp', + 'srf_temp_est', + 'rms_pt', + 'best_fit_opacities', + 'rms_opacities', + 'co2_dw_flux', + 'total_dw_flux', + 'quality', + 'srf_radiance', + 'version_id'], + 'BOL': ['sclk_time', + 'detector', + 'tic_count', + 'vbol', + 'tbol', + 'cal_vbol', + 'lambert_alb', + 'ti_bol', + 'brightness_temp_bol', + 'vbol_version_id', + 'tbol_version_id', + 'quality'], + 'GEO': ['sclk_time', + 'detector', + 'longitude', + 'latitude', + 'phase', + 'emission', + 'incidence', + 'planetary_phase', + 'heliocentric_lon', + 'sub_sc_lon', + 'sub_sc_lat', + 'sub_solar_lon', + 'sub_solar_lat', + 'target_distance', + 'height', + 'altitude', + 'local_time', + 'solar_distance', + 'angular_semidiameter', + 'version_id'], + 'IFG': ['sclk_time', 'detector', 'ifgm'], + 'OBS': ['sclk_time', + 'orbit', + 'ock', + 'ick', + 'tic', + 'pnt_angle', + 'pnt_imc', + 'pnt_view', + 'scan_len', + 'pckt_type', + 'schedule_type', + 'spc_gain', + 'vbol_gain', + 'tbol_gain', + 'comp_pp', + 'det_mask', + 'class', + 'quality', + 'temps', + 'ffti'], + 'POS': ['sclk_time', 'et', 'pos', 'sun', 'quat', 'id'], + 'RAD': ['sclk_time', + 'detector', + 'spectral_mask', + 'cmode', + 'raw_rad', + 'cal_rad', + 'tdet', + 'target_temp', + 'ti_spc', + 'version_id', + 'quality'], + 'TLM': ['sclk_time', + 'aux_temps', + 'ifgm_max', + 'ifgm_min', + 'dsp_log', + 'V1', + 'V2', + 'V3', + 'V4', + 'V5', + 'V6', + 'V7', + 'V8', + 'V9', + 'V10', + 'V11', + 'V12', + 'V13', + 'V14', + 'V15', + 'V16', + 'V17', + 'V18', + 'V19', + 'V20', + 'neon_lamp', + 'neon_gain', + 'neon_amp', + 'neon_zpd', + 'ifgm_zpd', + 'ifgm_end']} + +tes_scaling_factors = {'ATM': {'best_fit_opacities': 0.001, + 'co2_cont_temp': 0.01, + 'nadir_pt': 0.01, + 'srf_pressure': 0.001, + 'srf_temp_est': 0.01}, + 'BOL': {'brightness_temp_bol': 0.01, + 'tbol': 0.000152587890625, + 'vbol': 0.000152587890625}, + 'CMP': {}, + 'GEO': {'angular_semidiameter': 0.01, + 'emission': 0.01, + 'height': 0.01, + 'heliocentric_lon': 0.01, + 'incidence': 0.01, + 'latitude': 0.01, + 'local_time': 0.001, + 'longitude': 0.01, + 'phase': 0.01, + 'planetary_phase': 0.01, + 'solar_distance': 10000, + 'sub_sc_lat': 0.01, + 'sub_sc_lon': 0.01, + 'sub_solar_lat': 0.01, + 'sub_solar_lon': 0.01}, + 'IFG': {}, + 'OBS': {'pnt_angle': 0.046875, 'temps': 0.01}, + 'PCT': {}, + 'POS': {}, + 'RAD': {'target_temp': 0.01, 'tdet': 0.01}, + 'TLM': {'V1': 3.90625, + 'V10': -0.15625, + 'V11': 0.0976055, + 'V12': -0.0985813, + 'V13': 0.976562, + 'V14': 0.0648437, + 'V15': 0.045727, + 'V16': 0.0480992, + 'V17': 0.0478277, + 'V18': 0.0488039, + 'V19': 0.141966, + 'V2': 1.95312, + 'V20': -0.149688, + 'V3': 0.278906, + 'V4': 0.278906, + 'V5': 4.45312, + 'V6': 0.652344, + 'V7': 0.119457, + 'V8': -0.103067, + 'V9': 0.15576, + 'aux_temps': 0.01, + 'ifgm_max': 0.000152587890625, + 'ifgm_min': 0.000152587890625}}