Repository: obspy/obspy

Issue:
Endianness Issue with SciPy's FFT
SciPy's FFT cannot deal with big endian floats. We'll just have to enforce certain flags when passing to it.

Code snippet to reproduce:

``` python
import obspy
import numpy as np

tr = obspy.read()[0]
tr.data = np.require(tr.data, dtype=">f4")
tr.resample(10)
```

Came up on the mailing list: http://lists.swapbytes.de/archives/obspy-users/2014-December/001602.html



Discussion:
Mentioned at the end of #913, too.


Resolving patch:
diff --git a/obspy/core/trace.py b/obspy/core/trace.py
--- a/obspy/core/trace.py
+++ b/obspy/core/trace.py
@@ -1519,8 +1519,11 @@ def resample(self, sampling_rate, window='hanning', no_filter=True,
             freq = self.stats.sampling_rate * 0.5 / float(factor)
             self.filter('lowpassCheby2', freq=freq, maxorder=12)
 
+        orig_dtype = self.data.dtype
+        new_dtype = np.float32 if orig_dtype.itemsize == 4 else np.float64
+
         # resample in the frequency domain
-        X = rfft(self.data)
+        X = rfft(np.require(self.data, dtype=new_dtype))
         X = np.insert(X, 1, 0)
         if self.stats.npts % 2 == 0:
             X = np.append(X, [0])
@@ -1557,6 +1560,7 @@ def resample(self, sampling_rate, window='hanning', no_filter=True,
         if num % 2 == 0:
             Y = np.delete(Y, -1)
         self.data = irfft(Y) * (float(num) / float(self.stats.npts))
+        self.data = np.require(self.data, dtype=orig_dtype)
         self.stats.sampling_rate = sampling_rate
 
         return self
diff --git a/obspy/signal/freqattributes.py b/obspy/signal/freqattributes.py
--- a/obspy/signal/freqattributes.py
+++ b/obspy/signal/freqattributes.py
@@ -182,6 +182,9 @@ def bwith(data, fs, smoothie, fk):
     :return: **bwith[, dbwithd]** - Bandwidth, Time derivative of predominant
         period (windowed only).
     """
+    new_dtype = np.float32 if data.dtype.itemsize == 4 else np.float64
+    data = np.require(data, dtype=new_dtype)
+
     nfft = util.nextpow2(data.shape[1])
     freqaxis = np.linspace(0, fs, nfft + 1)
     bwith = np.zeros(data.shape[0])
@@ -235,6 +238,9 @@ def domperiod(data, fs, smoothie, fk):
     :return: **dperiod[, ddperiod]** - Predominant period, Time derivative of
         predominant period (windowed only).
     """
+    new_dtype = np.float32 if data.dtype.itemsize == 4 else np.float64
+    data = np.require(data, dtype=new_dtype)
+
     nfft = 1024
     # nfft = util.nextpow2(data.shape[1])
     freqaxis = np.linspace(0, fs, nfft + 1)
@@ -338,6 +344,9 @@ def logcep(data, fs, nc, p, n, w):  # @UnusedVariable: n is never used!!!
     :param n: Number of data windows.
     :return: Cepstral coefficients.
     """
+    new_dtype = np.float32 if data.dtype.itemsize == 4 else np.float64
+    data = np.require(data, dtype=new_dtype)
+
     dataT = np.transpose(data)
     nfft = util.nextpow2(dataT.shape[0])
     fc = fftpack.fft(dataT, nfft, 0)
diff --git a/obspy/signal/hoctavbands.py b/obspy/signal/hoctavbands.py
--- a/obspy/signal/hoctavbands.py
+++ b/obspy/signal/hoctavbands.py
@@ -43,30 +43,21 @@ def sonogram(data, fs, fc1, nofb, no_win):
     :param no_win: Number of data windows.
     :return: Half octave bands.
     """
-    fc = np.zeros([nofb])
-    fmin = np.zeros([nofb])
-    fmax = np.zeros([nofb])
+    fc = float(fc1) * 1.5**np.arange(nofb)
+    fmin = fc / np.sqrt(5. / 3.)
+    fmax = fc * np.sqrt(5. / 3.)
 
-    fc[0] = float(fc1)
-    fmin[0] = fc[0] / np.sqrt(float(5. / 3.))
-    fmax[0] = fc[0] * np.sqrt(float(5. / 3.))
-    for i in range(1, nofb):
-        fc[i] = fc[i - 1] * 1.5
-        fmin[i] = fc[i] / np.sqrt(float(5. / 3.))
-        fmax[i] = fc[i] * np.sqrt(float(5. / 3.))
-    nfft = util.nextpow2(data.shape[np.size(data.shape) - 1])
-    # c = np.zeros((data.shape), dtype=np.complex64)
+    nfft = util.nextpow2(data.shape[-1])
+    new_dtype = np.float32 if data.dtype.itemsize == 4 else np.float64
+    data = np.require(data, dtype=new_dtype)
     c = fftpack.fft(data, nfft)
-    z = np.zeros([len(c[:, 1]), nofb])
-    z_tot = np.zeros(len(c[:, 1]))
-    hob = np.zeros([no_win, nofb])
-    for k in range(no_win):
-        for j in range(len(c[1, :])):
-            z_tot[k] = z_tot[k] + pow(np.abs(c[k, j]), 2)
-        for i in range(nofb):
-            start = int(round(fmin[i] * nfft * 1. / float(fs), 0))
-            end = int(round(fmax[i] * nfft * 1. / float(fs), 0)) + 1
-            for j in range(start, end):
-                z[k, i] = z[k, i] + pow(np.abs(c[k, j - 1]), 2)
-            hob[k, i] = np.log(z[k, i] / z_tot[k])
+    z_tot = np.sum(np.abs(c)**2, axis=1)
+
+    start = np.around(fmin * nfft / fs, 0).astype(int) - 1
+    end = np.around(fmax * nfft / fs, 0).astype(int)
+    z = np.zeros([c.shape[0], nofb])
+    for i in range(nofb):
+        z[:, i] = np.sum(np.abs(c[:, start[i]:end[i]])**2, axis=1)
+
+    hob = np.log(z / z_tot[:, np.newaxis])
     return hob


Test patch:
diff --git a/obspy/core/tests/test_trace.py b/obspy/core/tests/test_trace.py
--- a/obspy/core/tests/test_trace.py
+++ b/obspy/core/tests/test_trace.py
@@ -1500,6 +1500,14 @@ def test_resample(self):
         self.assertEqual(tr_3.stats.sampling_rate, 10.0)
         self.assertEqual(tr_3.stats.starttime, tr.stats.starttime)
 
+        tr_4 = tr.copy()
+        tr_4.data = np.require(tr_4.data,
+                               dtype=tr_4.data.dtype.newbyteorder('>'))
+        tr_4 = tr_4.resample(sampling_rate=10.0)
+        self.assertEqual(tr_4.stats.endtime, tr.stats.endtime - 9.0 / 100.0)
+        self.assertEqual(tr_4.stats.sampling_rate, 10.0)
+        self.assertEqual(tr_4.stats.starttime, tr.stats.starttime)
+
     def test_method_chaining(self):
         """
         Tests that method chaining works for all methods on the Trace object