Repository: mne-tools/mne-python

Issue:
.sfp import with long ch names
The .sfp chan loc importer currently truncates long channel names. I came across this in a file with eye tracker channels included - 'L_GAZE_X' and 'L_GAZE_Y', which were truncated to have the same name, leading to all kinds of problems.

Could we simply do `dtype = np.dtype('S16, f8, f8, f8')` instead of the `dtype = np.dtype('S4, f8, f8, f8')` we currently have? 

This would still break at very long names. Possibly, a safer writer would process the strings line by line. But maybe long ch names are so rare it's not worth it.



Discussion:
This possibly also applies to .elp, .hpts, .eloc/.loc.

> Possibly, a safer writer would process the strings line by line. 

This seems like the safest solution to me. If we want to keep an upper limit we can use that of the max FIF channel name length (not sure what that is).

+1

On Tue, Jul 5, 2016 at 10:14 PM, Eric Larson notifications@github.com
wrote:

> Possibly, a safer writer would process the strings line by line.
> 
> This seems like the safest solution to me. If we want to keep an upper
> limit we can use that of the max FIF channel name length (not sure what
> that is).
> 
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> https://github.com/mne-tools/mne-python/issues/3385#issuecomment-230589421,
> or mute the thread
> https://github.com/notifications/unsubscribe/AAJ1HBWO3a6a3hg8rkslmk40wQV8MyWqks5qSrsWgaJpZM4JFe9-
> .

Ok, I can do a string-based processor at least for .sfp (which I know), and possibly .loc. If I don't manage the others, I'll ping this thread again. Thanks guys.

@teonbrooks maybe you wanna do this, otherwise I'll start doing something horrible with .split and stuff.

@jona-sassenhagen we use `.split` elsewhere, it could be fine here


Resolving patch:
diff --git a/mne/channels/montage.py b/mne/channels/montage.py
--- a/mne/channels/montage.py
+++ b/mne/channels/montage.py
@@ -187,10 +187,19 @@ def read_montage(kind, ch_names=None, path=None, unit='m', transform=False):
 
     if ext == '.sfp':
         # EGI geodesic
-        dtype = np.dtype('S4, f8, f8, f8')
-        data = np.loadtxt(fname, dtype=dtype)
-        pos = np.c_[data['f1'], data['f2'], data['f3']]
-        ch_names_ = data['f0'].astype(np.str)
+        with open(fname, 'r') as f:
+            lines = f.read().replace('\t', ' ').split("\n")
+
+        ch_names_, pos = [], []
+        for ii, line in enumerate(lines):
+            line = line.strip().split()
+            if len(line) > 0:  # skip empty lines
+                if len(line) != 4:  # name, x, y, z
+                    raise ValueError("Malformed .sfp file in line " + str(ii))
+                this_name, x, y, z = line
+                ch_names_.append(this_name)
+                pos.append([float(cord) for cord in (x, y, z)])
+        pos = np.asarray(pos)
     elif ext == '.elc':
         # 10-5 system
         ch_names_ = []


Test patch:
diff --git a/mne/channels/tests/test_montage.py b/mne/channels/tests/test_montage.py
--- a/mne/channels/tests/test_montage.py
+++ b/mne/channels/tests/test_montage.py
@@ -41,7 +41,7 @@ def test_montage():
     # no pep8
     input_str = ["""FidNz 0.00000 10.56381 -2.05108
     FidT9 -7.82694 0.45386 -3.76056
-    FidT10 7.82694 0.45386 -3.76056""",
+    very_very_very_long_name 7.82694 0.45386 -3.76056""",
     """// MatLab   Sphere coordinates [degrees]         Cartesian coordinates
     // Label       Theta       Phi    Radius         X         Y         Z       off sphere surface
       E1      37.700     -14.000       1.000    0.7677    0.5934   -0.2419  -0.00000000000000011
@@ -63,7 +63,7 @@ def test_montage():
     """Site  Theta  Phi
     Fp1  -92    -72
     Fp2   92     72
-    F3   -60    -51
+    very_very_very_long_name   -60    -51
     """,
     """346
      EEG	      F3	 -62.027	 -50.053	      85
@@ -82,6 +82,8 @@ def test_montage():
         with open(fname, 'w') as fid:
             fid.write(text)
         montage = read_montage(fname)
+        if ".sfp" in kind or ".txt" in kind:
+            assert_true('very_very_very_long_name' in montage.ch_names)
         assert_equal(len(montage.ch_names), 3)
         assert_equal(len(montage.ch_names), len(montage.pos))
         assert_equal(montage.pos.shape, (3, 3))