From 23fa2731c2564124cfe9e6ae25b1300eadeb780b Mon Sep 17 00:00:00 2001
From: "Mads M. Pedersen" <mmpe@dtu.dk>
Date: Wed, 3 May 2017 11:10:12 +0200
Subject: [PATCH] bugfix + improved revolution trigger

---
 wetb/hawc2/htc_contents.py              |  2 +-
 wetb/signal/fix/_rotor_position.py      | 78 +++++++++++++++++++++++--
 wetb/signal/subset_mean.py              | 62 +++++++++++++++-----
 wetb/signal/tests/test_fix.py           |  2 +
 wetb/signal/tests/test_subset_mean.py   | 17 +++++-
 wetb/wind/__init__.py                   |  1 +
 wetb/wind/turbulence/mann_parameters.py | 11 ++--
 7 files changed, 145 insertions(+), 28 deletions(-)

diff --git a/wetb/hawc2/htc_contents.py b/wetb/hawc2/htc_contents.py
index 5337613d..93b58f07 100644
--- a/wetb/hawc2/htc_contents.py
+++ b/wetb/hawc2/htc_contents.py
@@ -120,7 +120,7 @@ class HTCContents(object):
         self._add_contents(section)
         return section
 
-    def add_line(self, name, values, comments):
+    def add_line(self, name, values, comments=""):
         line = HTCLine(name, values, comments)
         self._add_contents(line)
         return line
diff --git a/wetb/signal/fix/_rotor_position.py b/wetb/signal/fix/_rotor_position.py
index 22aac76d..3876204d 100644
--- a/wetb/signal/fix/_rotor_position.py
+++ b/wetb/signal/fix/_rotor_position.py
@@ -5,6 +5,8 @@ Created on 30. mar. 2017
 '''
 import numpy as np
 from wetb.signal.fit._spline_fit import spline_fit
+from wetb.signal.filters._differentiation import differentiation
+
 def fix_rotor_position(rotor_position, sample_frq, rotor_speed, fix_dt=None, plt=None):
     """Rotor position fitted with spline
     
@@ -33,7 +35,7 @@ def fix_rotor_position(rotor_position, sample_frq, rotor_speed, fix_dt=None, plt
     from wetb.signal.subset_mean import revolution_trigger
     
     t = np.arange(len(rotor_position))
-    indexes = revolution_trigger(rotor_position[:], sample_frq, rotor_speed, max_no_round_diff=4)
+    indexes = revolution_trigger(rotor_position[:], sample_frq, rotor_speed, max_rev_diff=4)
     
     rp = rotor_position[:].copy()
     
@@ -69,7 +71,9 @@ def fix_rotor_position(rotor_position, sample_frq, rotor_speed, fix_dt=None, plt
         plt.plot(t/sample_frq, spline_fit(x,y)(t)-t*a, label='Spline (detrended)')
         plt.legend()
         plt.show()
-    return spline_fit(x,y)(t)%360
+    fit = spline_fit(x,y)(t)
+    fit[differentiation(fit, "left")<0]=np.nan
+    return fit%360
 
 def find_fix_dt(rotor_position, sample_frq, rotor_speed, plt=None):
     """Find the optimal fix_dt parameter for fix_rotor_position (function above).
@@ -95,7 +99,8 @@ def find_fix_dt(rotor_position, sample_frq, rotor_speed, plt=None):
     """
     from wetb.signal.filters import differentiation
     def err(i):
-        rpm_pos = differentiation(fix_rotor_position(rotor_position, sample_frq, rotor_speed, i))%180 / 360 * sample_frq * 60
+        drp = differentiation(fix_rotor_position(rotor_position, sample_frq, rotor_speed, i))
+        rpm_pos = drp%180 / 360 * sample_frq * 60
         return np.sum((rpm_pos - rotor_speed)**2)
     
     best = 27
@@ -110,4 +115,69 @@ def find_fix_dt(rotor_position, sample_frq, rotor_speed, plt=None):
         plt.show()
     
     return best 
-    
\ No newline at end of file
+
+def check_rotor_position(rotor_position, sample_frq, rotor_speed, max_rev_diff=1, plt=None):
+    """Rotor position fitted with spline
+    
+    Parameters
+    ----------
+    rotor_position : array_like
+        Rotor position [deg] (0-360)
+    sample_frq : int or float
+        Sample frequency [Hz]
+    rotor_speed : array_like
+        Rotor speed [RPM]
+    fix_dt : int, float or None, optional
+        Time distance [s] between spline fix points\n
+        If None (default) a range of seconds is tested and the result that minimize the RMS 
+        between differentiated rotor position fit and rotor speed is used.\n 
+        Note that a significant speed up is achievable by specifying the parameter
+    plt : PyPlot or None
+        If PyPlot a visual interpretation is plotted
+            
+    Returns
+    -------
+    y : nd_array
+        Fitted rotor position
+    """
+    
+    from wetb.signal.subset_mean import revolution_trigger
+    
+    t = np.arange(len(rotor_position))/sample_frq
+    indexes = revolution_trigger(rotor_position[:], sample_frq, rotor_speed, max_rev_diff=max_rev_diff)
+    
+    rotor_position = rotor_position[:]%360
+        
+    rp_fit = fix_rotor_position(rotor_position, sample_frq, rotor_speed, None)
+        
+    if plt:
+        #a = (rp.max()-rp.min())/t.max()
+        #plt.plot(t/sample_frq,rp-t*a,label='Continus rotor position (detrended)')
+        f, axarr = plt.subplots(2)
+        print (rp_fit)
+        axarr[0].plot(t, rotor_position)
+        axarr[0].plot(indexes/sample_frq, rotor_position[indexes],'.')
+        axarr[0].plot(t, rp_fit)
+        axarr[1].plot(t, rotor_speed)
+        axarr[1].plot(indexes/sample_frq,60/( differentiation(indexes)/sample_frq))
+        print (t[170:200])
+        drp = differentiation(rp_fit)
+        #drp[(drp<0)]= 0
+        axarr[1].plot(t, drp%180 / 360 * sample_frq * 60, label='fix')
+        plt.legend()
+    
+    i1,i2 = indexes[0], indexes[-1]
+    print ("Rev from rotor position", np.sum(np.diff(rotor_position[i1:i2])%180)/360)
+    print ("Rev from rotor speed", np.mean(rotor_speed[i1:i2])*(i2-i1)/60/sample_frq)
+    print ("Rev from fitted rotor position", np.sum(np.diff(rp_fit[i1:i2])%180)/360)
+    
+    print ("Mean RPM from rotor speed", np.mean(rotor_speed))
+    print ("Mean RPM from fitted rotor position", np.sum(np.diff(rp_fit[i1:i2])%180)/360 / ((i2-i1)/60/sample_frq))
+    
+    spr1 = (np.diff(indexes)/sample_frq)
+    
+    #rs1 = 60/( np.diff(indexes)/sample_frq)
+    spr2 = np.array([60/rotor_speed[i1:i2].mean() for i1,i2 in zip(indexes[:-1], indexes[1:])])
+    
+    err = spr1-spr2
+    print (err.max())
\ No newline at end of file
diff --git a/wetb/signal/subset_mean.py b/wetb/signal/subset_mean.py
index 0de3ea89..7578c11a 100644
--- a/wetb/signal/subset_mean.py
+++ b/wetb/signal/subset_mean.py
@@ -9,6 +9,7 @@ import unittest
 from wetb.utils.geometry import rpm2rads
 from _collections import deque
 from tables.tests.test_index_backcompat import Indexes2_0TestCase
+from wetb.signal.filters._differentiation import differentiation
 
 
 def power_mean(power, trigger_indexes, I, rotor_speed, time, air_density=1.225, rotor_speed_mean_samples=1) :
@@ -136,7 +137,7 @@ def cycle_trigger(values, trigger_value=None, step=1, ascending=True, tolerance=
     else:
         return np.where((values[1:] < trigger_value - tolerance) & (values[:-1] >= trigger_value + tolerance))[0][::step]
 
-def revolution_trigger(rotor_position, sample_frq, rotor_speed, max_no_round_diff=1):
+def revolution_trigger(rotor_position, sample_frq, rotor_speed, max_rev_diff=1):
     """Returns one index per revolution (minimum rotor position)
     
     Parameters
@@ -155,16 +156,12 @@ def revolution_trigger(rotor_position, sample_frq, rotor_speed, max_no_round_dif
     if isinstance(rotor_speed, (float, int)):
         rotor_speed = np.ones_like(rotor_position)*rotor_speed
     deg_per_sample = rotor_speed*360/60/sample_frq
-    sample_per_round = 1/(rotor_speed/60/sample_frq)
-    thresshold = deg_per_sample.max()*2
-    
-    nround_rotor_speed = np.nansum(rotor_speed/60/sample_frq)
-    
-    mod = [v for v in [5,10,30,60,90] if v>thresshold][0]
+    thresshold = deg_per_sample.max()*3
+    drp = np.diff(rotor_position)%360
+    rp = rotor_position
+
     
-    nround_rotor_position = np.nansum(np.diff(rotor_position)%mod)/360
-    #assert abs(nround_rotor_position-nround_rotor_speed)<max_no_round_diff, "No of rounds from rotor_position (%.2f) mismatch with no_rounds from rotor_speed (%.2f)"%(nround_rotor_position, nround_rotor_speed)
-    #print (nround_rotor_position, nround_rotor_speed)
+
     
     rp = np.array(rotor_position).copy()
     #filter degree increase > thresshold
@@ -176,10 +173,47 @@ def revolution_trigger(rotor_position, sample_frq, rotor_speed, max_no_round_dif
     # Best lower is the first lower after upper
     best_lower = lower_indexes[np.searchsorted(lower_indexes, upper_indexes)]
     upper2lower = best_lower - upper_indexes
-    best_lower = best_lower[upper2lower<upper2lower.mean()*2]
-    #max_dist_error = max([np.abs((i2-i1)- np.mean(sample_per_round[i1:i2])) for i1,i2 in zip(best_lower[:-1], best_lower[1:])])
-    #assert max_dist_error < sample_frq/5, max_dist_error
-    return best_lower
+    trigger_indexes = best_lower[upper2lower<upper2lower.mean()*2].tolist()
+
+    if len(trigger_indexes)>1:
+        
+    
+        
+        rpm_rs = np.array([rev.mean() for rev in np.split(rotor_speed, trigger_indexes)[1:-1]])
+        rpm_i = 1/np.diff(trigger_indexes)*60*sample_frq
+        spr_rs = np.array([rev.mean() for rev in np.split(1/rotor_speed*60*sample_frq, trigger_indexes)[1:-1]])
+        spr_i = np.diff(trigger_indexes)
+        
+
+        while np.any(spr_rs>spr_i*1.9):
+            i = np.where(spr_rs>spr_i*1.9)[0][0]
+            if np.abs(spr_i[i-1] + spr_i[i] - spr_rs[i])< np.abs(spr_i[i] + spr_i[i+1] - spr_rs[i]):
+                del trigger_indexes[i]
+            else:
+                del trigger_indexes[i+1]
+            spr_i = np.diff(trigger_indexes)
+            spr_rs = np.array([rev.mean() for rev in np.split(1/rotor_speed*60*sample_frq, trigger_indexes)[1:-1]])
+        
+            
+        
+        # if a revolution is too long add trigger
+        if np.any(rpm_rs>rpm_i*2.1):
+            # >1 missing triggers
+            raise NotImplementedError
+        trigger_indexes.extend([np.mean(trigger_indexes[i:i+2]) for i in np.where(rpm_rs>rpm_i*1.9)[0]])
+        trigger_indexes = np.sort(trigger_indexes).astype(np.int)
+    
+    
+        i1,i2 = trigger_indexes[0], trigger_indexes[-1]
+        nround_rotor_speed = np.nansum(rotor_speed[i1:i2]/60/sample_frq)
+        #mod = [v for v in [5,10,30,60,90] if v>thresshold][0]    
+        nround_rotor_position = len(trigger_indexes)-1 #np.nansum(np.diff(rotor_position[i1:i2])%mod)/360
+        if max_rev_diff is not None:
+            diff_pct = abs(nround_rotor_position-nround_rotor_speed)/nround_rotor_position*100
+            assert diff_pct<max_rev_diff, "No of revolution mismatch: rotor_position (%d), rotor_speed (%.1f), diff %.1f%%"%(nround_rotor_position, nround_rotor_speed, diff_pct)
+         
+    
+    return trigger_indexes
     
 
 def revolution_trigger_old(values, rpm_dt=None, dmin=5, dmax=10, ):
diff --git a/wetb/signal/tests/test_fix.py b/wetb/signal/tests/test_fix.py
index 86639862..86c9ce47 100644
--- a/wetb/signal/tests/test_fix.py
+++ b/wetb/signal/tests/test_fix.py
@@ -42,6 +42,8 @@ class TestFix(unittest.TestCase):
          
         self.assertEqual(find_fix_dt(ds.azi, sample_frq, ds.Rot_cor), 4)
         
+        
+        
 
 if __name__ == "__main__":
     #import sys;sys.argv = ['', 'Test.testRotorPositionFix']
diff --git a/wetb/signal/tests/test_subset_mean.py b/wetb/signal/tests/test_subset_mean.py
index 23642ae7..8c4ecd86 100644
--- a/wetb/signal/tests/test_subset_mean.py
+++ b/wetb/signal/tests/test_subset_mean.py
@@ -92,21 +92,32 @@ class TestSubsetMean(unittest.TestCase):
             x2 = np.random.randint(0, len(rotor_position),10)
             print (list(x2))
         else:
-            x1 = [447, 854, 595, 804, 847, 488, 412, 199, 675, 766]
+            x1 = [447, 854,   847, 488, 412, 199, 675]
             x2 = [92, 647, 821, 422, 33, 159, 369, 99, 157, 464]
             
         rotor_position[x1] += 360
         rotor_position[x2] -= 360
         rotor_position[90] = 180
+        rotor_position[270:276] = 15
+        rotor_position[598:602] = [360,360,0,0]
+        rotor_position[748:752] = [360,360,0,0]
         
         indexes = revolution_trigger(rotor_position, 20,1/(90/20/60))
-        np.testing.assert_array_equal(indexes, [ 91, 180, 270, 360, 450, 540, 630, 720, 810])
         if 0:
             import matplotlib.pyplot as plt
             plt.plot(rotor_position)
             plt.plot(indexes, np.zeros_like(indexes),'.')
             plt.show()
+        np.testing.assert_array_equal(indexes, [ 91, 180, 270, 360, 450, 540, 630, 720, 810])
+        
+    def test_revolution_trigger_max_rev_diff(self):
+        rotor_position = np.arange(0.,360*10,4)
+        rotor_position += np.random.random(len(rotor_position))
+        rotor_position = rotor_position % 360
+        rotor_speed = 1/(90/20/60)
+        revolution_trigger(rotor_position, 20, rotor_speed*1.02, max_rev_diff=3)
+        self.assertRaisesRegex(AssertionError, "No of revolution mismatch", revolution_trigger, rotor_position, 20, rotor_speed*1.02, max_rev_diff=1)
+        
         
-
 if __name__ == "__main__":
     unittest.main()
diff --git a/wetb/wind/__init__.py b/wetb/wind/__init__.py
index e69de29b..ae17e9a5 100644
--- a/wetb/wind/__init__.py
+++ b/wetb/wind/__init__.py
@@ -0,0 +1 @@
+from .turbulence import *
\ No newline at end of file
diff --git a/wetb/wind/turbulence/mann_parameters.py b/wetb/wind/turbulence/mann_parameters.py
index b6a1d806..b0a94bc7 100644
--- a/wetb/wind/turbulence/mann_parameters.py
+++ b/wetb/wind/turbulence/mann_parameters.py
@@ -254,20 +254,18 @@ def fit_ae(sf, u, L, G, min_bin_count=None, plt=False):
     return ae
 
 
-def plot_spectra(ae, L, G, k1, uu, vv, ww=None, uw=None, mean_u=1, log10_bin_size=.2, show=True, plt=None):
+def plot_spectra(ae, L, G, k1, uu, vv=None, ww=None, uw=None, mean_u=1, log10_bin_size=.2, plt=None):
 #    if plt is None:
 #        import matplotlib.pyplot as plt
     _plot_spectra(k1, uu, vv, ww, uw, mean_u, log10_bin_size, plt)
     plot_mann_spectra(ae, L, G, "-", mean_u, plt)
-    if show:
-        plt.show()
 
-def _plot_spectra(k1, uu, vv, ww=None, uw=None, mean_u=1, log10_bin_size=.2, plt=None):
+def _plot_spectra(k1, uu, vv=None, ww=None, uw=None, mean_u=1, log10_bin_size=.2, plt=None):
     if plt is None:
         import matplotlib.pyplot as plt
     bk1, buu, bvv, bww, buw = logbin_spectra(k1, uu, vv, ww, uw, log10_bin_size)
     def plot(xx, label, color, plt):
-        plt.semilogx(bk1, bk1 * xx * 10 ** 0 / mean_u ** 2 , '.' + color)
+        plt.semilogx(bk1, bk1 * xx * 10 ** 0 / mean_u ** 2 , '.' + color, label=label)
     plot(buu, 'uu', 'r', plt)
     if (bvv) is not None:
         plot(bvv, 'vv', 'g', plt)
@@ -280,8 +278,9 @@ def plot_mann_spectra(ae, L, G, style='-', u_ref=1, plt=None, spectra=['uu', 'vv
         import matplotlib.pyplot as plt
     mf = 10 ** (np.linspace(-4, 1, 1000))
     muu, mvv, mww, muw = get_mann_model_spectra(ae, L, G, mf)
+    plt.title("ae: %.3f, L: %.2f, G:%.2f"%(ae,L,G))
     if 'uu' in spectra: plt.semilogx(mf, mf * muu * 10 ** 0 / u_ref ** 2, 'r' + style)
-    if 'vv' in spectra:     plt.semilogx(mf, mf * mvv * 10 ** 0 / u_ref ** 2, 'g' + style)
+    if 'vv' in spectra: plt.semilogx(mf, mf * mvv * 10 ** 0 / u_ref ** 2, 'g' + style)
     if 'ww' in spectra: plt.semilogx(mf, mf * mww * 10 ** 0 / u_ref ** 2, 'b' + style)
     if 'uw' in spectra: plt.semilogx(mf, mf * muw * 10 ** 0 / u_ref ** 2, 'm' + style)
 
-- 
GitLab