Repository: obspy/obspy

Issue:
Polarization Analysis issues
I've found a couple problems with `obspy.signal.polarization.polarization_analysis`
1) `polarization_analysis` requires parameter `frqlow` and `frqhigh` (regardless of `method`), however looking in the source code those parameters are only used if `method="vidale"`. Reading the documentation I expected these to filter the stream I pass the function. (Also I feel like these parameters should be named `freqmin` and `freqmax` in order to match the names parameters that would be passed to a bandpass filter, for example)
2) On lines 508-513 of the source code (obspy/signal/polarization.py):
```
            if "Z" in tr.stats.channel:
                z = dat.copy()
            if "N" in tr.stats.channel:
                n = dat.copy()
            if "E" in tr.stats.channel:
                e = dat.copy()
```
   I've got a channel named "ENZ", this channel will get all mixed up because all of the above instances are true (other channels like "ELZ" or "HNE" would also get mixed up). I would recommend replacing this with:
```
            if tr.stats.channel[-1] == "Z":
                z = dat.copy()
            elif tr.stats.channel[-1] == "N":
                n = dat.copy()
            elif tr.stats.channel[-1] == "E":
                e = dat.copy()
            else:
                raise NameError
```

Version info:
  -  ObsPy 1.1.0 (installed from anaconda)
  -  Python 3.6.6
  -  Ubuntu 18.04.2

Thanks for all you guys do!


Discussion:
@jwassermann this one is for you!
> `polarization_analysis` requires parameter `frqlow` and `frqhigh` (regardless of `method`), however looking in the source code those parameters are only used if `method="vidale"`. Reading the documentation I expected these to filter the stream I pass the function. (Also I feel like these parameters should be named `freqmin` and `freqmax` in order to match the names parameters that would be passed to a bandpass filter, for example)

Yes, documentation should be clear that these are not even used with 2/3 methods, not even the default one. It's really unfortunate that these were made positional arguments, they should rather be kwargs but we can't change this without major backwards compatibility workarounds now..

> I've got a channel named "ENZ", this channel will get all mixed up because all of the above instances are true (other channels like "ELZ" or "HNE" would also get mixed up). I would recommend replacing this with:

I agree, this definitly needs to be changed like proposed, but the error raised should be a `ValueError`.

Resolving patch:
diff --git a/obspy/signal/polarization.py b/obspy/signal/polarization.py
--- a/obspy/signal/polarization.py
+++ b/obspy/signal/polarization.py
@@ -447,9 +447,9 @@ def polarization_analysis(stream, win_len, win_frac, frqlow, frqhigh, stime,
     :param var_noise: resembles a sphere of noise in PM where the 3C is
         excluded
     :type var_noise: float
-    :param frqlow: lower frequency for PM
+    :param frqlow: lower frequency. Only used for ``method='vidale'``.
     :type frqlow: float
-    :param frqhigh: higher frequency for PM
+    :param frqhigh: higher frequency. Only used for ``method='vidale'``.
     :type frqhigh: float
     :param stime: Start time of interest
     :type stime: :class:`obspy.core.utcdatetime.UTCDateTime`
@@ -475,6 +475,14 @@ def polarization_analysis(stream, win_len, win_frac, frqlow, frqhigh, stime,
 
     res = []
 
+    if stream.get_gaps():
+        msg = 'Input stream must not include gaps:\n' + str(stream)
+        raise ValueError(msg)
+
+    if len(stream) != 3:
+        msg = 'Input stream expected to be three components:\n' + str(stream)
+        raise ValueError(msg)
+
     # check that sampling rates do not vary
     fs = stream[0].stats.sampling_rate
     if len(stream) != len(stream.select(sampling_rate=fs)):
@@ -498,24 +506,21 @@ def polarization_analysis(stream, win_len, win_frac, frqlow, frqhigh, stime,
         offset = 0
         while (newstart + (nsamp + nstep) / fs) < etime:
             try:
-                data = []
-                z = []
-                n = []
-                e = []
                 for i, tr in enumerate(stream):
                     dat = tr.data[spoint[i] + offset:
                                   spoint[i] + offset + nsamp]
                     dat = (dat - dat.mean()) * tap
-                    if "Z" in tr.stats.channel:
+                    if tr.stats.channel[-1].upper() == "Z":
                         z = dat.copy()
-                    if "N" in tr.stats.channel:
+                    elif tr.stats.channel[-1].upper() == "N":
                         n = dat.copy()
-                    if "E" in tr.stats.channel:
+                    elif tr.stats.channel[-1].upper() == "E":
                         e = dat.copy()
+                    else:
+                        msg = "Unexpected channel code '%s'" % tr.stats.channel
+                        raise ValueError(msg)
 
-                data.append(z)
-                data.append(n)
-                data.append(e)
+                data = [z, n, e]
             except IndexError:
                 break
 


Test patch: