diff --git a/CHANGELOG.md b/CHANGELOG.md index b5776df1..ad7e0fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). ## [0.X.X] - 2023-XX-XX +* New Instruments + * REACH Dosimeter * Bug Fix * New window needs to be integer for calculate_imf_steadiness * Documentation diff --git a/docs/supported_instruments.rst b/docs/supported_instruments.rst index 0980414e..deee1932 100644 --- a/docs/supported_instruments.rst +++ b/docs/supported_instruments.rst @@ -186,6 +186,14 @@ OMNI HRO .. automodule:: pysatNASA.instruments.omni_hro :members: calculate_clock_angle, calculate_imf_steadiness, time_shift_to_magnetic_poles +.. _reach_dosimeter: + +REACH DOSIMETER +---------- + +.. automodule:: pysatNASA.instruments.reach_dosimeter + :members: + .. _ses14_gold: SES14 GOLD diff --git a/pysatNASA/instruments/__init__.py b/pysatNASA/instruments/__init__.py index 5407b031..e914912a 100644 --- a/pysatNASA/instruments/__init__.py +++ b/pysatNASA/instruments/__init__.py @@ -11,8 +11,8 @@ 'de2_lang', 'de2_nacs', 'de2_rpa', 'de2_vefi', 'de2_wats', 'dmsp_ssusi', 'formosat1_ivm', 'icon_euv', 'icon_fuv', 'icon_ivm', 'icon_mighti', - 'igs_gps', 'iss_fpmu', 'jpl_gps', 'omni_hro', 'ses14_gold', - 'timed_guvi', 'timed_saber', 'timed_see'] + 'igs_gps', 'iss_fpmu', 'jpl_gps', 'omni_hro', 'reach_dosimeter', + 'ses14_gold', 'timed_guvi', 'timed_saber', 'timed_see'] for inst in __all__: exec("from pysatNASA.instruments import {x}".format(x=inst)) diff --git a/pysatNASA/instruments/methods/__init__.py b/pysatNASA/instruments/methods/__init__.py index 4459d353..a1910352 100644 --- a/pysatNASA/instruments/methods/__init__.py +++ b/pysatNASA/instruments/methods/__init__.py @@ -12,5 +12,6 @@ from pysatNASA.instruments.methods import iss # noqa F401 from pysatNASA.instruments.methods import jhuapl # noqa F401 from pysatNASA.instruments.methods import omni # noqa F401 +from pysatNASA.instruments.methods import reach # noqa F401 from pysatNASA.instruments.methods import ses14 # noqa F401 from pysatNASA.instruments.methods import timed # noqa F401 diff --git a/pysatNASA/instruments/methods/formosat.py b/pysatNASA/instruments/methods/formosat.py index 41ee0f22..840acd12 100644 --- a/pysatNASA/instruments/methods/formosat.py +++ b/pysatNASA/instruments/methods/formosat.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Provides non-instrument specific routines for C/NOFS data.""" +"""Provides non-instrument specific routines for FORMOSAT data.""" ackn_str = ' '.join(('Data provided through NASA CDAWeb Key Parameters -', 'Shin-Yi Su (Institute of Space Science, National Central', diff --git a/pysatNASA/instruments/methods/igs.py b/pysatNASA/instruments/methods/igs.py index 63ad4a5d..c050e447 100644 --- a/pysatNASA/instruments/methods/igs.py +++ b/pysatNASA/instruments/methods/igs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Provides non-instrument specific routines for JPL ROTI data.""" +"""Provides non-instrument specific routines for IGS GPS data.""" ackn_str = ' '.join(("The GPS Total Electron Content (TEC) data", "produced by the International Global Navigation", diff --git a/pysatNASA/instruments/methods/iss.py b/pysatNASA/instruments/methods/iss.py index 24ed41ce..dd7553e4 100644 --- a/pysatNASA/instruments/methods/iss.py +++ b/pysatNASA/instruments/methods/iss.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Provides non-instrument specific routines for C/NOFS data.""" +"""Provides non-instrument specific routines for ISS data.""" ackn_str = ' '.join(("R.M. Suggs, S.L. Koontz, NASA Johnson Space Center", "Contact Rob Suggs for support and use.", diff --git a/pysatNASA/instruments/methods/reach.py b/pysatNASA/instruments/methods/reach.py new file mode 100644 index 00000000..cc9b11e1 --- /dev/null +++ b/pysatNASA/instruments/methods/reach.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +"""Provides non-instrument specific routines for REACH data.""" + +ackn_str = "Please load a file for full acknowledgments." + +refs = {'dosimeter': + '\n'.join((' '.join(("Guild, T., O'Brien, T.P., Boyd,", + "A.J., Mazur, J.E., Halford, A.J., (2019)", + "Intra-calibration of REACH Dosimeters,", + "AEROSPACE REPORT NO. TOR-2019-02361")), + ' '.join(("Halford, A.J., Guild, T., O'Brien, T.P., Boyd,", + "A.J., Mazur, J.E. (2019)", + "REACH Maps and Indices for UDL: Version 1,", + "AEROSPACE REPORT NO. TOR-2019-02650")), + ' '.join(("Guild, T., O'Brien, T.P., Boyd, A.J., Mazur,", + "J.E. (2021)", + "REACH Maps and Indices for UDL: Version 1", + "AEROSPACE REPORT NO. TOR-2021-01076"))))} diff --git a/pysatNASA/instruments/reach_dosimeter.py b/pysatNASA/instruments/reach_dosimeter.py new file mode 100644 index 00000000..c7cebdcb --- /dev/null +++ b/pysatNASA/instruments/reach_dosimeter.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +"""The REACH dosimeter instrument. + +Supports the dosimeter instrument on the Responsive Environmental Assessment +Commercially Hosted (REACH) mission. + +The Responsive Environmental Assessment Commercially Hosted (REACH) +constellation is collection of 32 small sensors hosted on six orbital planes of +the Iridium-Next space vehicles in low earth orbit. Each sensor contains two +micro-dosimeters sensitive to the passage of charged particles from the Earth's +radiation belts. There are six distinct dosimeter types spread among the 64 +individual sensors, which are unique in shielding and electronic threshold. +When taken together, this effectively enables a high time-cadence measurement +of protons and electrons in six integral energy channels over the entire globe. + +Properties +---------- +platform + 'reach' +name + 'dosimeter' +tag + None Supported +inst_id + '101', '102', '105', '108', '113', '114', '115', '116', '133', '134', '135', + '136', '137', '138', '139', '140', '148', '149', '162', '163', '164', '165', + '166', '169', '170', '171', '172', '173', '175', '176', '180', '181' + + +""" + +import datetime as dt +import functools +import numpy as np + +from pysat._meta import MetaHeader +from pysat.instruments.methods import general as mm_gen +from pysat.utils.io import load_netcdf + +from pysatNASA.instruments.methods import cdaweb as cdw +from pysatNASA.instruments.methods import general as mm_nasa +from pysatNASA.instruments.methods import reach as mm_reach + +# ---------------------------------------------------------------------------- +# Instrument attributes + +platform = 'reach' +name = 'dosimeter' +tags = {'': 'Dosimeter data from the REACH mission'} +iids = ['101', '102', '105', '108', '113', '114', '115', '116', '133', '134', + '135', '136', '137', '138', '139', '140', '148', '149', '162', '163', + '164', '165', '166', '169', '170', '171', '172', '173', '175', '176', + '180', '181'] +inst_ids = {iid: [tag for tag in tags.keys()] for iid in iids} + +# ---------------------------------------------------------------------------- +# Instrument test attributes + +_test_dates = {iid: {tag: dt.datetime(2019, 12, 1) for tag in tags.keys()} + for iid in inst_ids.keys()} + +# ---------------------------------------------------------------------------- +# Instrument methods + + +# Use standard init routine +init = functools.partial(mm_nasa.init, module=mm_reach, name=name) + +# Use default clean +clean = mm_nasa.clean + + +def preprocess(self): + """Update acknowledgement with info from file.""" + + self.acknowledgements = self.meta.header.Acknowledgement + + return + + +# ---------------------------------------------------------------------------- +# Instrument functions +# +# Use the default CDAWeb and pysat methods + +# Set the list_files routine +datestr = '{year:04d}{month:02d}{day:02d}' +fname = 'reach-vid-{inst_id}_dosimeter-l1c_{datestr}_v{{version:01d}}.nc' +supported_tags = {iid: {'': fname.format(inst_id=iid, datestr=datestr)} + for iid in inst_ids.keys()} +list_files = functools.partial(mm_gen.list_files, + supported_tags=supported_tags) + + +def load(fnames, tag=None, inst_id=None): + """Load REACH data into `pandas.DataFrame` and `pysat.Meta` objects. + + This routine is called as needed by pysat. It is not intended + for direct user interaction. + + Parameters + ---------- + fnames : array-like + iterable of filename strings, full path, to data files to be loaded. + This input is nominally provided by pysat itself. + tag : str + tag name used to identify particular data set to be loaded. + This input is nominally provided by pysat itself. + inst_id : str + Satellite ID used to identify particular data set to be loaded. + This input is nominally provided by pysat itself. + + Returns + ------- + data : pds.DataFrame + A pandas DataFrame with data prepared for the pysat.Instrument + meta : pysat.Meta + Metadata formatted for a pysat.Instrument object. + + Note + ---- + Any additional keyword arguments passed to pysat.Instrument + upon instantiation are passed along to this routine. + + Examples + -------- + :: + + inst = pysat.Instrument('reach', 'dosimeter', inst_id='101', tag='') + inst.load(2020, 1) + + """ + + # Use standard netcdf interface + labels = {'units': ('UNITS', str), 'name': ('LONG_NAME', str), + 'notes': ('VAR_NOTES', str), 'desc': ('CATDESC', str), + 'min_val': ('VALIDMIN', (int, float)), + 'max_val': ('VALIDMAX', (int, float)), + 'fill_val': ('_FillValue', (int, float))} + data, meta = load_netcdf(fnames, epoch_name='Epoch', + meta_kwargs={'labels': labels}) + + # Update header variables + header = meta.header.to_dict() + new_header = {} + for key in header.keys(): + new_key = key.replace('-', '_to_') + new_header[new_key] = header[key] + if np.isnan(new_header['Notes']): + new_header['Notes'] = '' + + meta.header = MetaHeader(new_header) + + return data, meta + + +# Support download routine +download_tags = {iid: {'': 'REACH-VID-{iid}_DOSIMETER-L1C'.format(iid=iid)} + for iid in inst_ids.keys()} +download = functools.partial(cdw.cdas_download, supported_tags=download_tags) + +# Support listing files currently on CDAWeb +list_remote_files = functools.partial(cdw.cdas_list_remote_files, + supported_tags=download_tags)