Repository: mne-tools/mne-python

Issue:
Add ref channel aliases for `RawMFF` (EGI), to `mne.io.constants.CHANNEL_LOC_ALIASES`
#### Describe the new feature or enhancement
*Please provide a clear and concise description of what you want to add or
change.*

`mne.io.raw.set_montage` includes a parameter `match_alias`, which searches `mne.io.constants.CHANNEL_LOC_ALIASES` for known channel aliases, to match unrecognized channel location names to their known aliases.

In EGI EEG files (RawMFF), the REF channel is given the last channel name (i.e. 'E129' for the GSN-Hydrocel-129, and 'E257' for GSN-Hydrocel-257). However in the MNE-python standard montages for both these nets, the REF channel is named 'Cz', which makes sense as that is what we colloquially call the REF channel.

This discrepancy has caused errors for me when setting the standard montage, and seems to [confuse other users too](https://mne.discourse.group/t/what-eeg-montage-to-use-for-257-channels/5093/11).

#### Describe your proposed implementation


 I propose to add 'E129' and 'E257'  to `mne.io.constants.CHANNEL_LOC_ALIASES` so that `raw.set_montage` can handle the discrepancy in the REF channel name. @drammock this is something that I can work on during the code sprint, and hopefully should be pretty simple.




Discussion:
I loaded and added a montage to a 257 channels recording with the EGI software last week, and IIRC I tried the `"EGI_256"` build-in montage that lacks the reference. 

I notice now that there are `GSN-HydroCel` montages: `GSN-HydroCel-128`, `GSN-HydroCel-129`, `GSN-HydroCel-256`, `GSN-HydroCel-257`, ... It looks to me like those are duplicates or subset without the reference. **If this is the case**, maybe it would be worth cleaning a bit those default montages, at least by deprecating the duplicates (removing them from the documented list of build-in montages) and keeping only one build-in montage per cap.
now that #10898 is merged to main I will return to this. Since the reference channel for EGI nets will now be read in as `VREF`, but the reference channel is named `Cz` in the standard montage, I will add this alias to `mne.io.constants.CHANNEL_LOC_ALIASES`. 

Unless anyone thinks the correct fix would be to directly edit the default EGI montages so that the reference channel is called `VREF`.
> I will add this alias to mne.io.constants.CHANNEL_LOC_ALIASES.

My worry is that `Cz` is a common, non-ref name in many montages so I'm not sure if something will break. For example would this cause other montages like `standard_1020` to be read in with `Cz` set as the ref, even if it wasn't? I don't know.

If this is the case, we might need to make `CHANNEL_LOC_ALIASES` somehow montage-specific :(

Resolving patch:
diff --git a/mne/io/constants.py b/mne/io/constants.py
--- a/mne/io/constants.py
+++ b/mne/io/constants.py
@@ -1056,7 +1056,11 @@
     'T5': 'T9',
     'T6': 'T10',
     'M1': 'TP9',
-    'M2': 'TP10'
+    'M2': 'TP10',
+    # EGI ref chan is named VREF/Vertex Ref.
+    # In the standard montages for EGI, the ref is named Cz
+    'VREF': 'Cz',
+    'Vertex Reference': 'Cz'
     # add a comment here (with doi of a published source) above any new
     # aliases, as they are added
 }


Test patch:
diff --git a/mne/io/egi/tests/test_egi.py b/mne/io/egi/tests/test_egi.py
--- a/mne/io/egi/tests/test_egi.py
+++ b/mne/io/egi/tests/test_egi.py
@@ -475,3 +475,20 @@ def test_meas_date(fname, timestamp, utc_offset):
     assert raw.info['meas_date'] == measdate
     assert raw.info['utc_offset'] == utc_offset
     assert local_utc_diff == int(utc_offset[:-2])
+
+
+@requires_testing_data
+@pytest.mark.parametrize('fname, standard_montage', [
+    (egi_mff_fname, 'GSN-HydroCel-129'),  # 129 chan EGI file
+    (egi_mff_pns_fname, 'GSN-HydroCel-257')  # 257 chan EGI file
+])
+def test_set_standard_montage(fname, standard_montage):
+    """Test setting a standard montage."""
+    raw = read_raw_egi(fname, verbose='warning')
+    dig_before_mon = raw.info['dig']
+
+    raw.set_montage(standard_montage, match_alias=True, on_missing='ignore')
+    dig_after_mon = raw.info['dig']
+
+    # No dig entries should have been dropped while setting montage
+    assert len(dig_before_mon) == len(dig_after_mon)