PageRenderTime 40ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/sunpy/timeseries/sources/lyra.py

https://github.com/ayshih/sunpy
Python | 225 lines | 156 code | 9 blank | 60 comment | 7 complexity | 9063aa8690815b16008efa139e566d1d MD5 | raw file
  1. """
  2. This module provies Proba-2 `~sunpy.timeseries.TimeSeries` source.
  3. """
  4. import sys
  5. from collections import OrderedDict
  6. import matplotlib.dates as mdates
  7. import matplotlib.pyplot as plt
  8. import numpy as np
  9. import pandas
  10. import astropy.units as u
  11. from astropy.time import TimeDelta
  12. import sunpy.io
  13. from sunpy import config
  14. from sunpy.time import parse_time
  15. from sunpy.timeseries.timeseriesbase import GenericTimeSeries
  16. from sunpy.util.metadata import MetaDict
  17. from sunpy.visualization import peek_show
  18. TIME_FORMAT = config.get("general", "time_format")
  19. __all__ = ['LYRATimeSeries']
  20. class LYRATimeSeries(GenericTimeSeries):
  21. """
  22. Proba-2 LYRA Lightcurve TimeSeries.
  23. LYRA (Large Yield RAdiometer) is an ultraviolet irradiance radiometer that observes the Sun in four passbands,
  24. chosen for their relevance to solar physics and space weather.
  25. LYRA is composed of three (redundant) units, each of them constituted of the same four channels:
  26. * 120-123 nm Lyman-alpha channel
  27. * 190-222 nm Herzberg continuum channel
  28. * Aluminium filter channel (17-80 nm + a contribution below 5 nm), including He II at 30.4 nm
  29. * Zirconium filter channel (6-20 nm + a contribution below 2 nm), rejecting He II
  30. LYRA can take data with cadences chosen in the 100Hz to 0.1Hz interval.
  31. PROBA2 was launched on 2 November 2009.
  32. Examples
  33. --------
  34. >>> import sunpy.timeseries
  35. >>> import sunpy.data.sample # doctest: +REMOTE_DATA
  36. >>> lyra = sunpy.timeseries.TimeSeries(sunpy.data.sample.LYRA_LEVEL3_TIMESERIES) # doctest: +REMOTE_DATA
  37. >>> lyra.peek() # doctest: +SKIP
  38. References
  39. ----------
  40. * `Proba2 SWAP Science Center <http://proba2.sidc.be/about/SWAP/>`_
  41. * `LYRA Data Homepage <http://proba2.sidc.be/data/LYRA>`_
  42. * `LYRA Instrument Homepage <http://proba2.sidc.be/about/LYRA>`_
  43. """
  44. # Class attributes used to specify the source class of the TimeSeries
  45. # and a URL to the mission website.
  46. _source = 'lyra'
  47. _url = "https://proba2.sidc.be/about/LYRA"
  48. def plot(self, axes=None, columns=None, names=3, **kwargs):
  49. """
  50. Plots the LYRA data.
  51. Parameters
  52. ----------
  53. axes : array of `matplotlib.axes.Axes`, optional
  54. The axes on which to plot the TimeSeries.
  55. columns : list[str], optional
  56. If provided, only plot the specified columns.
  57. names : `int`, optional
  58. The number of columns to plot. Defaults to 3.
  59. **kwargs : `dict`
  60. Additional plot keyword arguments that are handed to `~matplotlib.axes.Axes.plot` functions.
  61. Returns
  62. -------
  63. array of `~matplotlib.axes.Axes`
  64. The plot axes.
  65. """
  66. self._validate_data_for_plotting()
  67. lyranames = ({'CHANNEL1': 'Lyman alpha', 'CHANNEL2': 'Herzberg cont.',
  68. 'CHANNEL3': 'Al filter', 'CHANNEL4': 'Zr filter'},
  69. {'CHANNEL1': '120-123nm', 'CHANNEL2': '190-222nm',
  70. 'CHANNEL3': '17-80nm + <5nm', 'CHANNEL4': '6-20nm + <2nm'})
  71. colors = ('tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple',
  72. 'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan')
  73. predefined_axes = False
  74. if columns is None:
  75. columns = self._data.columns
  76. if isinstance(axes, np.ndarray):
  77. predefined_axes = True
  78. elif axes is None:
  79. axes = self.to_dataframe()[columns].plot(subplots=True, sharex=True, **kwargs)
  80. for i, name in enumerate(self.to_dataframe()[columns]):
  81. if predefined_axes:
  82. axes[i].plot(self._data[columns[i]],
  83. color=colors[i % len(colors)],
  84. label=columns[i])
  85. axes[i].legend(loc="upper right")
  86. plt.xticks(rotation=30)
  87. if names < 3:
  88. name = lyranames[names][columns[i]]
  89. else:
  90. name = lyranames[0][columns[i]] + ' \n (' + lyranames[1][columns[i]] + ')'
  91. axes[i].locator_params(axis='y', nbins=6)
  92. axes[i].set_ylabel(f"{name} \n (W/m**2)", fontsize=9.5)
  93. locator = mdates.AutoDateLocator()
  94. formatter = mdates.ConciseDateFormatter(locator)
  95. axes[-1].xaxis.set_major_locator(locator)
  96. axes[-1].xaxis.set_major_formatter(formatter)
  97. return axes
  98. @peek_show
  99. def peek(self, title=None, columns=None, names=3, **kwargs):
  100. """
  101. Displays the LYRA data by calling `~sunpy.timeseries.sources.lyra.LYRATimeSeries.plot`.
  102. .. plot::
  103. import sunpy.timeseries
  104. import sunpy.data.sample
  105. lyra = sunpy.timeseries.TimeSeries(sunpy.data.sample.LYRA_LEVEL3_TIMESERIES, source='LYRA')
  106. lyra.peek()
  107. Parameters
  108. ----------
  109. title : `str`, optional
  110. The title of the plot.
  111. columns : list[str], optional
  112. If provided, only plot the specified columns.
  113. names : `int`, optional
  114. The number of columns to plot. Defaults to 3.
  115. **kwargs : `dict`
  116. Additional plot keyword arguments that are handed to
  117. :meth:`pandas.DataFrame.plot`.
  118. """
  119. axes = self.plot(columns=columns, names=names, **kwargs)
  120. if title is None:
  121. title = "LYRA ({0:{1}})".format(self.to_dataframe().index[0], TIME_FORMAT)
  122. axes[0].set_title(title)
  123. fig = axes[-1].get_figure()
  124. fig.subplots_adjust(left=0.17, top=0.94, right=0.94, bottom=0.15)
  125. fig = axes[0].get_figure()
  126. fig.subplots_adjust(left=0.17, top=0.94, right=0.94, bottom=0.15)
  127. return fig
  128. @classmethod
  129. def _parse_file(cls, filepath):
  130. """
  131. Parses Lyra FITS data files to create TimeSeries.
  132. Parameters
  133. ----------
  134. filepath : `str`
  135. The path to the file you want to parse.
  136. """
  137. hdus = sunpy.io.read_file(filepath)
  138. return cls._parse_hdus(hdus)
  139. @classmethod
  140. def _parse_hdus(cls, hdulist):
  141. """
  142. Parses LYRA `astropy.io.fits.HDUList` from a FITS file.
  143. Parameters
  144. ----------
  145. hdulist : `astropy.io.fits.HDUList`
  146. A HDU list.
  147. """
  148. # Open file with PyFITS
  149. fits_record = hdulist[1].data
  150. metadata = MetaDict(OrderedDict(hdulist[0].header))
  151. start_str = metadata.get('date-obs', metadata.get('date_obs', ''))
  152. start = parse_time(start_str)
  153. # First column are times. For level 2 data, the units are [s].
  154. # For level 3 data, the units are [min]
  155. if hdulist[1].header['TUNIT1'] == 's':
  156. times = start + TimeDelta(fits_record.field(0)*u.second)
  157. elif hdulist[1].header['TUNIT1'] == 'MIN':
  158. td = [int(n) for n in fits_record.field(0)]
  159. times = start + TimeDelta(td*u.minute)
  160. else:
  161. raise ValueError("Time unit in LYRA fits file not recognised. "
  162. "Value = {}".format(hdulist[1].header['TUNIT1']))
  163. # Rest of columns are the data
  164. table = {}
  165. for i, col in enumerate(fits_record.columns[1:-1]):
  166. # temporary patch for big-endian data bug on pandas 0.13
  167. if fits_record.field(i+1).dtype.byteorder == '>' and sys.byteorder == 'little':
  168. table[col.name] = fits_record.field(i + 1).byteswap().newbyteorder()
  169. else:
  170. table[col.name] = fits_record.field(i + 1)
  171. # Return the header and the data
  172. times.precision = 9
  173. data = pandas.DataFrame(table, index=times.isot.astype('datetime64'))
  174. data.sort_index(inplace=True)
  175. # Add the units data
  176. units = OrderedDict([('CHANNEL1', u.W/u.m**2),
  177. ('CHANNEL2', u.W/u.m**2),
  178. ('CHANNEL3', u.W/u.m**2),
  179. ('CHANNEL4', u.W/u.m**2)])
  180. # TODO: check: http://www.wmo-sat.info/oscar/instruments/view/733
  181. return data, metadata, units
  182. @classmethod
  183. def is_datasource_for(cls, **kwargs):
  184. """
  185. Determines if the file corresponds to a LYRA LightCurve
  186. `~sunpy.timeseries.TimeSeries`.
  187. """
  188. # Check if source is explicitly assigned
  189. if 'source' in kwargs.keys():
  190. if kwargs.get('source', ''):
  191. return kwargs.get('source', '').lower().startswith(cls._source)
  192. # Check if HDU defines the source instrument
  193. if 'meta' in kwargs.keys():
  194. return kwargs['meta'].get('INSTRUME', '').startswith('LYRA')