Repository: sunpy/sunpy

Issue:
Map not recognizing EIT coordinates.
Something is messed up with the EIT map coordinates. The meta data and WCS looks OK at first glance, but the coordinate grid isn't being translated properly. 

`from sunpy.map import Map`
`eit=Map(sunpy.data.sample.EIT_195_IMAGE)`
`eit.peek()`

![eit_example](https://user-images.githubusercontent.com/5497326/35360322-5ac25432-012b-11e8-8554-218c427f171d.png)

MDI observer longitude not properly extracted from header
The MDI FITS header has the observer Carrington longitude under the keyword `OBS_L0` (and `L0`) rather than `CRLN_OBS`, so our code currently misses this information and assumes an Earth-based observer.  We shouldn't use this value blindly, however, because I have reasons to be skeptical about Carrington-longitude calculations.  The good news is that the FITS header *also* has the Earth Carrington longitude under the keyword `EARTH_L0`, and the *difference* of the two longitudes should be an accurate observer Stonyhurst longitude.


Discussion:
This should be fixed by #2398 
I am using version 0.8.2, was it back-ported? 
Yes to the next release which I have not released yet. 
In `mapbase.py`,  `pixel_to_world` also returns `SkyCoord` using `frame=self.coordinate_frame` causing similar issues. 
Does #2398 also fix "Warning: Missing metadata for Carrington longitude: assuming Earth-based observer" when querying the `eit.wcs`? 
No it probably only fixes the plotting issue.
So we might need to change it in mapbase as well? 
@wafels and I are looking into what exactly needs to be changed. 
> In mapbase.py, pixel_to_world also returns SkyCoord using frame=self.coordinate_frame causing similar issues.

Can you elaborate on that? If there is something wrong with that it's a very different bug.
The ephemeris generated by the current coordinate frame (e.g. distance to sun, solar radius, center) is about 0.1% - 1% different than using the EIT image header keywords `HEC_X`, `HEC_Y`, and `HEC_Z`. This means coordinate calculations can be off by about as much as a few pixels, especially near the limb of the sun. 
Interesting. I would be interested to know the source of that discrepancy. Also we should find a way to load the info from the header. 

What coordinate system is HEC?
The HEC co-ordinate system is defined here:

https://soho.nascom.nasa.gov/data/ancillary/index.html#orbitcoordinates



On Jan 25, 2018 6:35 PM, "Stuart Mumford" <notifications@github.com> wrote:

> Interesting. I would be interested to know the source of that discrepancy.
> Also we should find a way to load the info from the header.
>
> What coordinate system is HEC?
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/sunpy/sunpy/issues/2430#issuecomment-360636805>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AA8CFxxzDHq88qO86-i3kDJOar7I-hR7ks5tOQ_XgaJpZM4RsCBF>
> .
>

> The Heliocentric Ecliptic coordinate system is defined as follows: the origin is Sun centered, with the Z axis parallel to the ecliptic pole with positive north of the ecliptic plane; the X-Y plane lies in the ecliptic plane and the X axis points towards the first point of Aries; the Y axis completes a right-handed orthogonal coordinate system.

That is one bonkers coordinate system...
So other than an observer location error, is the plot bug in the original comment fixed?
Fortunately `astropy.coordinates` supports it!
That's as may be, but it is one that Astropy has apparently...

http://docs.astropy.org/en/stable/api/astropy.coordinates.HeliocentricTrueEcliptic.html#astropy.coordinates.HeliocentricTrueEcliptic

On Fri, Jan 26, 2018 at 9:29 AM, Stuart Mumford <notifications@github.com>
wrote:

> The Heliocentric Ecliptic coordinate system is defined as follows: the
> origin is Sun centered, with the Z axis parallel to the ecliptic pole with
> positive north of the ecliptic plane; the X-Y plane lies in the ecliptic
> plane and the X axis points towards the first point of Aries; the Y axis
> completes a right-handed orthogonal coordinate system.
>
> That is one bonkers coordinate system...
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/sunpy/sunpy/issues/2430#issuecomment-360799231>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AA8CFz-ag8zKmZwkza0YBHUYX--TZKkGks5tOeFXgaJpZM4RsCBF>
> .
>

So we think that we can read in the HEC co-ordinate info for EIT and then
use the co-ordinate transformation graph to get the right Stonyhurst
observer location.

On Fri, Jan 26, 2018 at 9:31 AM, Michael Kirk <notifications@github.com>
wrote:

> Fortunately astropy.coordinates supports it!
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/sunpy/sunpy/issues/2430#issuecomment-360799599>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AA8CF9wXgYimt513CqiwEbncjxXCfxSxks5tOeGugaJpZM4RsCBF>
> .
>

@wafels you don't need to explicitly do the transform, just pass the HEC coordinate object in to frame and it will get transformed for you.
Yes - I think that is what I meant.

On Fri, Jan 26, 2018 at 9:34 AM, Stuart Mumford <notifications@github.com>
wrote:

> @wafels <https://github.com/wafels> you don't need to explicitly do the
> transform, just pass the HEC coordinate object in to frame and it will get
> transformed for you.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/sunpy/sunpy/issues/2430#issuecomment-360800348>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AA8CF-LU0UiytCNabkL4R3x72tbcbfnTks5tOeJpgaJpZM4RsCBF>
> .
>

The major difference is that `astropy.coordinates` needs {Lat, Lon, Radius} coordinates and the EIT header has {X,Y,Z}. 
Construct a `CartesianRepresentation` object and pass that in as the only argument, should do the rest for you.
I came up with something.... the final result is



```
*In [4]: hgs_coordOut[4]: <HeliographicStonyhurst Coordinate
(obstime=2011-06-07 20:37:52.600000): (lon, lat, radius) in (deg, deg,
km)    (-0.24687872,  0.06050496,   1.50358425e+08)>*
```
As a sanity check:

```
*In [6]: np.sqrt(hec_x**2 + hec_y**2 + hec_z**2)Out[6]: <Quantity
150358424.9714794 km>*
```
So that looks OK.  The *lon* and *lat* values of *hgs_coord* are
reassuringly small, as they should be since SOHO orbits close to Earth/Sun
line.
Other sanity checks are in the code below.

If this checks out, we can use the code below to calculate the Sun/Earth
distance for EIT

*https://github.com/sunpy/sunpy/blob/master/sunpy/map/sources/soho.py#L82
<https://github.com/sunpy/sunpy/blob/master/sunpy/map/sources/soho.py#L82>*

and also explicitly meta entries for hglt_obs and hgln_obs
```
#
# Example EIT HEC to HGS conversion
#
import astropy.units as u
from astropy.coordinates import CartesianRepresentation,
HeliocentricTrueEcliptic

from sunpy.coordinates import HeliographicStonyhurst, get_sunearth_distance
from sunpy.data.sample import EIT_195_IMAGE
import sunpy.map

m = sunpy.map.Map(EIT_195_IMAGE)

hec_x = m.meta['hec_x'] * u.km
hec_y = m.meta['hec_y'] * u.km
hec_z = m.meta['hec_z'] * u.km

cr = CartesianRepresentation(hec_x, hec_y, hec_z)

hte = HeliocentricTrueEcliptic(cr, obstime=m.date)

hgs_frame = HeliographicStonyhurst(obstime=m.date)

hgs_coord = hte.transform_to(hgs_frame)

hec_distance = np.sqrt(hec_x**2 + hec_y**2 + hec_z**2)

# Should be around 0.99, since SOHO is about 1% closer to
# the Sun than the Earth is
print(hec_distance/get_sunearth_distance(m.obstime))

# Should be very very close to each other, since the two
# co-ordinate systems represent the same co-ordinate in space
print(hec_distance, hgs_coord.radius)

# Should be close to zero since SOHO sits close to the Earth/Sun line
print(hgs_coord.lon, hgs_coord.lat)
```


On Fri, Jan 26, 2018 at 9:44 AM, Stuart Mumford <notifications@github.com>
wrote:

> Construct a CartesianRepresentation object and pass that in as the only
> argument, should do the rest for you.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/sunpy/sunpy/issues/2430#issuecomment-360802869>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AA8CF9vftbZzCTCBg3ydtk-RpSD9mm40ks5tOeS8gaJpZM4RsCBF>
> .
>

That looks very hopeful! I will have a proper read up on it.

I think the best thing to do would be to have the SOHO map sources override the `observer_coordinate` property and just return a HEC coordinate object. That should then do all the work 😁
To complete the API they could then use that property to return `observer_longitude ` and `observer_latitude`
This issue seems to be related to issue #2445 
From Bill: 

> What EIT calls HEC, SunSPICE calls HAE, for Heliocentric Aries Ecliptic.  That's a more common name in the literature. Heliocentric Earth Equatorial (HEEQ or HEQ) is the x,y,z equivalent of Stonyhurst heliographic.

Hmmm - the SOHO SPICE HAE values are not the same as the EIT FITS header HEC values for the date 2017-03-08 01:13:46.667000.  Seems like the EIT FITS header heliographic longitude value always zero, and the EIT FITS header heliographic latitude header is approximately that given by converting either the SOHO SPICE HAE values or the EIT FITS header HEC values to HGS values.



Using SPICE to transform the Cartesian EIT HEC values to their lon, lat, radius equivalent give values almost identical to the HeliocentricTrueEcliptic values (with equinox set the observation date) when passing in the Cartesian EIT HEC values as a CartesianRepresentation object.  This confirms that EIT coordinates need the equinox property set.
I don't understand what you just wrote above.  It sounds like you're just converting between Cartesian and spherical representations without any frame transformation?  I don't get why the equinox matters in what you wrote.
That's right - no frame transformation.  When I convert the EIT HEC Cartesian representation to its spherical equivalent in SSWIDL / SPICE, I get the same answers as converting the EIT HEC Cartesian representation to HeliocentricTrueEcliptic with the equinox property set to the observation date.  It adds more information to the definition of the EIT HEC representation as described in https://soho.nascom.nasa.gov/data/ancillary/index.html#orbitcoordinates .
I just had a conversation with Dean Pesnell who said that the team using MDI to do helioseismology realized that the ephemeris from SOHO was not accurate enough for their purposes. They have been using the SPICE kernels for the last 15 years or so which has improved their science quality over using the MDI header information. I think this means in order to get the _right_ answer, an implementation of `spiceypy` is necessary. 
> That's right - no frame transformation. When I convert the EIT HEC Cartesian representation to its spherical equivalent in SSWIDL / SPICE, I get the same answers as converting the EIT HEC Cartesian representation to HeliocentricTrueEcliptic with the equinox property set to the observation date. It adds more information to the definition of the EIT HEC representation as described in https://soho.nascom.nasa.gov/data/ancillary/index.html#orbitcoordinates .

I disagree with the conclusion you draw here.  If you're not doing a frame transformation, the equinox attribute doesn't factor in.
> I think this means in order to get the right answer, an implementation of spiceypy is necessary.

See https://github.com/sunpy/sunpy/issues/2445#issuecomment-364531159 for my confirmation that SunPy/Astropy agrees with SunSPICE.
You are right about my comment

_"That's right - no frame transformation. When I convert the EIT HEC Cartesian representation to its spherical equivalent in SSWIDL / SPICE, I get the same answers as converting the EIT HEC Cartesian representation to HeliocentricTrueEcliptic with the equinox property set to the observation date. It adds more information to the definition of the EIT HEC representation as described in https://soho.nascom.nasa.gov/data/ancillary/index.html#orbitcoordinates ."_

It is erroneous.  Following on from #2445 it is unclear if the EIT HEC vector is using the J2000 position of the vernal equinox or the observation date.
From Bill Thompson: 

> I took a look at some old EIT files that I had on my computer, and 
> compared them against SunSPICE values for heliographic coordinates.  It 
> appears that the radial distance from the Sun is good to about 3-4 
> significant figures, heliographic longitude is good to about a degree, 
> and heliographic latitude (B0) is good to about 0.1 degrees.
> 
> Looking at some data from 2008, I find that the agreement is excellent, 
> particularly if I assume that the EIT values are unprecessed.  With that 
> assumption, the radial distance is good to 6 significant figures, the 
> longitude is good to 0.003 degrees, and the latitude (B0) is good to 
> 0.001 degrees.
> 
Description of MDI keywords: http://soi.stanford.edu/sssc/doc/keywords.html
It also appears that our code also doesn't make use of `OBS_DIST` in the header, and instead performs an inaccurate conversion to approximate the SOHO-Sun distance...
That said, it looks like `OBS_DIST` still disagrees with what SunSPICE reports, but we should still use what's in the header if the calculation isn't actually improving the accuracy.

Resolving patch:
diff --git a/sunpy/map/mapbase.py b/sunpy/map/mapbase.py
--- a/sunpy/map/mapbase.py
+++ b/sunpy/map/mapbase.py
@@ -436,13 +436,21 @@ def instrument(self):
     def measurement(self):
         """Measurement name, defaults to the wavelength of image."""
         return u.Quantity(self.meta.get('wavelnth', 0),
-                          self.meta.get('waveunit', ""))
+                          self.waveunit)
+
+    @property
+    def waveunit(self):
+        """The `~astropy.units.Unit` of the wavelength of this observation."""
+        unit = self.meta.get("waveunit")
+        if unit is None:
+            return u.one
+        return u.Unit(unit)
 
     @property
     def wavelength(self):
         """Wavelength of the observation."""
         return u.Quantity(self.meta.get('wavelnth', 0),
-                          self.meta.get('waveunit', ""))
+                          self.waveunit)
 
     @property
     def observatory(self):
@@ -739,7 +747,7 @@ def _fix_bitpix(self):
 
     def _get_cmap_name(self):
         """Build the default color map name."""
-        cmap_string = (self.observatory + self.meta['detector'] +
+        cmap_string = (self.observatory + self.detector +
                        str(int(self.wavelength.to('angstrom').value)))
         return cmap_string.lower()
 
diff --git a/sunpy/map/sources/soho.py b/sunpy/map/sources/soho.py
--- a/sunpy/map/sources/soho.py
+++ b/sunpy/map/sources/soho.py
@@ -1,49 +1,31 @@
 """SOHO Map subclass definitions"""
-# pylint: disable=W0221,W0222,E1101,E1121
 
-__author__ = "Keith Hughitt"
-__email__ = "keith.hughitt@nasa.gov"
-
-import numpy as np
 import matplotlib.pyplot as plt
+import numpy as np
 from matplotlib import colors
 
-from astropy.units import Quantity
+import astropy.units as u
+from astropy.coordinates import CartesianRepresentation, SkyCoord
 from astropy.visualization import PowerStretch
 from astropy.visualization.mpl_normalize import ImageNormalize
 
 from sunpy.map import GenericMap
-from sunpy.sun import constants
-from sunpy.sun import sun
 from sunpy.map.sources.source_type import source_stretch
-from sunpy.coordinates import get_sunearth_distance
-
-__all__ = ['EITMap', 'LASCOMap', 'MDIMap']
 
+# Versions of Astropy that do not have HeliocentricMeanEcliptic have the same frame
+# with the incorrect name HeliocentricTrueEcliptic
+try:
+    from astropy.coordinates import HeliocentricMeanEcliptic
+except ImportError:
+    from astropy.coordinates import HeliocentricTrueEcliptic as HeliocentricMeanEcliptic
 
-def _dsunAtSoho(date, rad_d, rad_1au=None):
-    r"""Determines the distance to the Sun from SOhO following
-    d_{\sun,Object} =
-            D_{\sun\earth} \frac{\tan(radius_{1au}[rad])}{\tan(radius_{d}[rad])}
-    though tan x ~ x for x << 1
-    d_{\sun,Object} =
-            D_{\sun\eart} \frac{radius_{1au}[rad]}{radius_{d}[rad]}
-    since radius_{1au} and radius_{d} are dividing each other we can use [arcsec]
-    instead.
 
-    ---
-    TODO: Does this apply just to observations on the same Earth-Sun line?
-    If not it can be moved outside here.
-    """
-    if not rad_1au:
-        rad_1au = sun.solar_semidiameter_angular_size(date)
-    dsun = get_sunearth_distance(date) * constants.au * (rad_1au / rad_d)
-    # return scalar value not astropy.quantity
-    return dsun.value
+__all__ = ['EITMap', 'LASCOMap', 'MDIMap']
 
 
 class EITMap(GenericMap):
-    """SOHO EIT Image Map.
+    """
+    SOHO EIT Image Map.
 
     SOHO EIT is an extreme ultraviolet (EUV) imager able to image the solar
     transition region and inner corona in four selected bandpasses,
@@ -57,6 +39,7 @@ class EITMap(GenericMap):
     * `SOHO Mission Page <https://sohowww.nascom.nasa.gov/>`_
     * `SOHO EIT Instrument Page <https://umbra.nascom.nasa.gov/eit/>`_
     * `SOHO EIT User Guide <https://umbra.nascom.nasa.gov/eit/eit_guide/>`_
+
     """
 
     def __init__(self, data, header, **kwargs):
@@ -64,26 +47,37 @@ def __init__(self, data, header, **kwargs):
         header['cunit1'] = header.get('cunit1', 'arcsec')
         header['cunit2'] = header.get('cunit2', 'arcsec')
 
-        GenericMap.__init__(self, data, header, **kwargs)
-        # Fill in some missing info
-        self.meta['detector'] = "EIT"
-        self.meta['waveunit'] = "Angstrom"
+        super().__init__(data, header, **kwargs)
 
-        self._fix_dsun()
         self._nickname = self.detector
         self.plot_settings['cmap'] = plt.get_cmap(self._get_cmap_name())
         self.plot_settings['norm'] = ImageNormalize(
             stretch=source_stretch(self.meta, PowerStretch(0.5)))
 
+    @property
+    def detector(self):
+        return "EIT"
+
+    @property
+    def waveunit(self):
+        return "Angstrom"
+
     @property
     def rsun_obs(self):
         """
         Returns the solar radius as measured by EIT in arcseconds.
         """
-        return Quantity(self.meta['solar_r'] * self.meta['cdelt1'], 'arcsec')
+        return u.Quantity(self.meta['solar_r'] * self.meta['cdelt1'], 'arcsec')
 
-    def _fix_dsun(self):
-        self.meta['dsun_obs'] = _dsunAtSoho(self.date, self.rsun_obs)
+    @property
+    def _supported_observer_coordinates(self):
+        return [(('hec_x', 'hec_y', 'hec_z'), {'x': self.meta.get('hec_x'),
+                                               'y': self.meta.get('hec_y'),
+                                               'z': self.meta.get('hec_z'),
+                                               'unit': u.km,
+                                               'representation': CartesianRepresentation,
+                                               'frame': HeliocentricMeanEcliptic})
+        ] + super()._supported_observer_coordinates
 
     @classmethod
     def is_datasource_for(cls, data, header, **kwargs):
@@ -92,7 +86,8 @@ def is_datasource_for(cls, data, header, **kwargs):
 
 
 class LASCOMap(GenericMap):
-    """SOHO LASCO Image Map
+    """
+    SOHO LASCO Image Map
 
     The Large Angle and Spectrometric COronagraph (LASCO) is a set of three
     Lyot-type coronagraphs (C1, C2, and C3) that image the solar corona from
@@ -111,10 +106,10 @@ class LASCOMap(GenericMap):
 
     def __init__(self, data, header, **kwargs):
 
-        GenericMap.__init__(self, data, header, **kwargs)
+        header['cunit1'] = header['cunit1'].lower()
+        header['cunit2'] = header['cunit2'].lower()
 
-        self.meta['CUNIT1'] = self.meta['CUNIT1'].lower()
-        self.meta['CUNIT2'] = self.meta['CUNIT2'].lower()
+        super().__init__(data, header, **kwargs)
 
         # Fill in some missing or broken info
         # Test if change has already been applied
@@ -179,11 +174,9 @@ def __init__(self, data, header, **kwargs):
         # Assume pixel units are arcesc if not given
         header['cunit1'] = header.get('cunit1', 'arcsec')
         header['cunit2'] = header.get('cunit2', 'arcsec')
-        GenericMap.__init__(self, data, header, **kwargs)
+        super().__init__(data, header, **kwargs)
 
         # Fill in some missing or broken info
-        self.meta['detector'] = "MDI"
-        self._fix_dsun()
         self._nickname = self.detector + " " + self.measurement
         vmin = np.nanmin(self.data)
         vmax = np.nanmax(self.data)
@@ -192,6 +185,23 @@ def __init__(self, data, header, **kwargs):
         else:
             self.plot_settings['norm'] = colors.Normalize(-vmax, vmax)
 
+    @property
+    def _supported_observer_coordinates(self):
+        return [(('obs_l0', 'obs_b0', 'obs_dist'), {'lon': self.meta.get('obs_l0'),
+                                                    'lat': self.meta.get('obs_b0'),
+                                                    'radius': self.meta.get('obs_dist'),
+                                                    'unit': (u.deg, u.deg, u.AU),
+                                                    'frame': "heliographic_carrington"}),
+        ] + super()._supported_observer_coordinates
+
+    @property
+    def detector(self):
+        return "MDI"
+
+    @property
+    def waveunit(self):
+        return "Angstrom"
+
     @property
     def measurement(self):
         """
@@ -199,31 +209,6 @@ def measurement(self):
         """
         return "magnetogram" if self.meta.get('dpc_obsr', " ").find('Mag') != -1 else "continuum"
 
-    def _fix_dsun(self):
-        """ Solar radius in arc-seconds at 1 au
-            previous value radius_1au = 959.644
-            radius = constants.average_angular_size
-            There are differences in the keywords in the test FITS data and in
-            the Helioviewer JPEG2000 files.  In both files, MDI stores the
-            the radius of the Sun in image pixels, and a pixel scale size.
-            The names of these keywords are different in the FITS versus the
-            JP2 file.  The code below first looks for the keywords relevant to
-            a FITS file, and then a JPEG2000 file.  For more information on
-            MDI FITS header keywords please go to http://soi.stanford.edu/,
-            http://soi.stanford.edu/data/ and
-            http://soi.stanford.edu/magnetic/Lev1.8/ .
-        """
-        scale = self.meta.get('xscale', self.meta.get('cdelt1'))
-        radius_in_pixels = self.meta.get('r_sun', self.meta.get('radius'))
-        radius = scale * radius_in_pixels
-        self.meta['radius'] = radius
-
-        if not radius:
-            # radius = sun.angular_size(self.date)
-            self.meta['dsun_obs'] = constants.au
-        else:
-            self.meta['dsun_obs'] = _dsunAtSoho(self.date, radius)
-
     @classmethod
     def is_datasource_for(cls, data, header, **kwargs):
         """Determines if header corresponds to an MDI image"""


Test patch:
diff --git a/sunpy/map/sources/tests/test_eit_source.py b/sunpy/map/sources/tests/test_eit_source.py
--- a/sunpy/map/sources/tests/test_eit_source.py
+++ b/sunpy/map/sources/tests/test_eit_source.py
@@ -10,6 +10,8 @@
 
 import pytest
 
+import astropy.units as u
+
 from sunpy.map.sources.soho import EITMap
 from sunpy.map import Map
 import sunpy.data.test
@@ -17,26 +19,36 @@
 path = sunpy.data.test.rootdir
 fitslist = glob.glob(os.path.join(path, "EIT", "*"))
 
+
 @pytest.fixture(scope="module", params=fitslist)
 def createEIT(request):
     """Creates an EITMap from a FITS file."""
     return Map(request.param)
 
+
 # EIT Tests
 def test_fitstoEIT(createEIT):
     """Tests the creation of EITMap using FITS."""
     assert isinstance(createEIT, EITMap)
 
+
 def test_is_datasource_for(createEIT):
     """Test the is_datasource_for method of EITMap.
     Note that header data to be provided as an argument
     can be a MetaDict object."""
     assert createEIT.is_datasource_for(createEIT.data, createEIT.meta)
 
+
 def test_observatory(createEIT):
     """Tests the observatory property of the EITMap object."""
     assert createEIT.observatory == "SOHO"
 
+
 def test_measurement(createEIT):
     """Tests the measurement property of the EITMap object."""
-    assert createEIT.measurement.value in [195, 171]
\ No newline at end of file
+    assert createEIT.measurement.value in [195, 171]
+
+
+def test_rsun(createEIT):
+    """Tests the measurement property of the EITMap object."""
+    assert u.allclose(createEIT.rsun_obs, 979.0701*u.arcsec)
diff --git a/sunpy/map/sources/tests/test_mdi_source.py b/sunpy/map/sources/tests/test_mdi_source.py
--- a/sunpy/map/sources/tests/test_mdi_source.py
+++ b/sunpy/map/sources/tests/test_mdi_source.py
@@ -6,33 +6,52 @@
 import os
 import glob
 
+import pytest
+
+import astropy.units as u
+
+from sunpy.coordinates import frames
 from sunpy.map.sources.soho import MDIMap
 from sunpy.map import Map
 import sunpy.data.test
 
-path = sunpy.data.test.rootdir
-fitspath = glob.glob(os.path.join(path, "mdi_fd_Ic_6h_01d.5871.0000_s.fits"))
-mdi = Map(fitspath)
+
+@pytest.fixture
+def mdi():
+    path = sunpy.data.test.rootdir
+    fitspath = glob.glob(os.path.join(path, "mdi_fd_Ic_6h_01d.5871.0000_s.fits"))
+    return Map(fitspath)
 
 
 # MDI Tests
-def test_fitstoMDI():
+def test_fitstoMDI(mdi):
     """Tests the creation of MDIMap using FITS."""
     assert isinstance(mdi, MDIMap)
 
 
-def test_is_datasource_for():
+def test_is_datasource_for(mdi):
     """Test the is_datasource_for method of MDIMap.
     Note that header data to be provided as an argument
     can be a MetaDict object."""
     assert mdi.is_datasource_for(mdi.data, mdi.meta)
 
 
-def test_observatory():
+def test_observatory(mdi):
     """Tests the observatory property of the MDIMap object."""
     assert mdi.observatory == "SOHO"
 
 
-def test_measurement():
+def test_measurement(mdi):
     """Tests the measurement property of the MDIMap object."""
     assert mdi.measurement == "continuum"
+
+
+def test_waveunit(mdi):
+    assert mdi.waveunit == "Angstrom"
+
+
+def test_observer(mdi):
+    assert isinstance(mdi.observer_coordinate.frame, frames.HeliographicStonyhurst)
+    assert u.allclose(mdi.observer_coordinate.lat, -5.774028172878*u.deg)
+    assert u.allclose(mdi.observer_coordinate.lon, -0.1327673730752963*u.deg)
+    assert u.allclose(mdi.observer_coordinate.radius, 0.9739569156244*u.AU)