Repository: sunpy/sunpy

Issue:
Header helper is bugged when creating a PCij matrix from a rotation angle for rectangular pixels
As [noted on the mailing list](https://groups.google.com/d/msgid/sunpy/0352a093-e23d-4681-8113-e560bd2be92an%40googlegroups.com), the calculation in the header helper of a PCij matrix from a rotation angle is incorrect when working with rectangular pixels (`CDELT1` not equal to `CDELT2`) because `lam` is the inverse of what it should be:
https://github.com/sunpy/sunpy/blob/1fa82dd5e39282ab392ae1b8ac4ad3c66b6d65da/sunpy/map/header_helper.py#L156

The analogous bug was fixed in `GenericMap` in #5766, but was missed in the header helper.


Discussion:
@ayshih Did this get fixed?
Er, not as far as I'm aware?
Here's a quick test that could be added along with a fix to check that header_helper and map are consistent:
```python
import sunpy.map
from sunpy.coordinates import frames
from astropy.coordinates import SkyCoord
import astropy.units as u
import numpy as np

rotation_angle = 10*u.deg
data = np.random.rand(1024, 1024)
my_coord = SkyCoord(0*u.arcsec, 0*u.arcsec, obstime="2017-08-01", observer = 'earth', frame=frames.Helioprojective)
header = sunpy.map.make_fitswcs_header(data, my_coord, rotation_angle=rotation_angle, scale=[1, 2] * u.arcsec/u.pix)
header_pc = np.array([[header['pc1_1'], header['pc1_2']], [header['pc2_1'], header['pc2_2']]])

# Create a copy that has CROTA2 instead of PCi_j matrix
header_nopc = header.copy()
for i in [1, 2]:
    for j in [1, 2]:
        del header_nopc[f'pc{i}_{j}']
header_nopc['CROTA2'] = rotation_angle.to_value(u.deg)

smap = sunpy.map.Map(data, header_nopc)

# PC matrix created with header helper and calculated by Map
# should be the same
print(smap.rotation_matrix)
print(header_pc)
```

Currently prints different matrices, verifying the bug. When fixed should print the same matrix:
```
[[ 0.98480775 -0.34729636]
 [ 0.08682409  0.98480775]]
[[ 0.98480775 -0.08682409]
 [ 0.34729636  0.98480775]]
```

Resolving patch:
diff --git a/sunpy/map/header_helper.py b/sunpy/map/header_helper.py
--- a/sunpy/map/header_helper.py
+++ b/sunpy/map/header_helper.py
@@ -169,7 +169,7 @@ def _set_rotation_params(meta_wcs, rotation_angle, rotation_matrix):
         rotation_angle = 0 * u.deg
 
     if rotation_angle is not None:
-        lam = meta_wcs['cdelt1'] / meta_wcs['cdelt2']
+        lam = meta_wcs['cdelt2'] / meta_wcs['cdelt1']
         p = np.deg2rad(rotation_angle)
 
         rotation_matrix = np.array([[np.cos(p), -1 * lam * np.sin(p)],


Test patch:
diff --git a/sunpy/map/tests/test_header_helper.py b/sunpy/map/tests/test_header_helper.py
--- a/sunpy/map/tests/test_header_helper.py
+++ b/sunpy/map/tests/test_header_helper.py
@@ -66,7 +66,14 @@ def test_metakeywords():
     assert isinstance(meta, dict)
 
 
-def test_deafult_rotation(map_data, hpc_coord):
+def test_scale_conversion(map_data, hpc_coord):
+    # The header will have cunit1/2 of arcsec
+    header = make_fitswcs_header(map_data, hpc_coord, scale=[1, 2] * u.arcmin / u.pix)
+    assert header['cdelt1'] == 60
+    assert header['cdelt2'] == 120
+
+
+def test_default_rotation(map_data, hpc_coord):
     header = make_fitswcs_header(map_data, hpc_coord)
     wcs = WCS(header)
     np.testing.assert_allclose(wcs.wcs.pc, [[1, 0], [0, 1]], atol=1e-5)
@@ -79,6 +86,13 @@ def test_rotation_angle(map_data, hpc_coord):
     np.testing.assert_allclose(wcs.wcs.pc, [[0, -1], [1, 0]], atol=1e-5)
 
 
+def test_rotation_angle_rectangular_pixels(map_data, hpc_coord):
+    header = make_fitswcs_header(map_data, hpc_coord, scale=[2, 5] * u.arcsec / u.pix,
+                                 rotation_angle=45*u.deg)
+    wcs = WCS(header)
+    np.testing.assert_allclose(wcs.wcs.pc, np.sqrt(0.5) * np.array([[1, -2.5], [0.4, 1]]), atol=1e-5)
+
+
 def test_rotation_matrix(map_data, hpc_coord):
     header = make_fitswcs_header(map_data, hpc_coord,
                                  rotation_matrix=np.array([[1, 0], [0, 1]]))