/***************************************************************************
**
**  This file is part of HVCore.
**
**  HVCore is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  HVCore is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2011-11-15
**  Copyright: 2011-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "NewNoiseModel.h"

namespace HVCore {

  /*!
    \class NewNoiseModel NewNoiseModel.h
    \brief New Noise Model suggested by Peterson 1993

    Full description of class still missing
  */

  const double NewNoiseModel::lowPeriods[]={
                 0.10,
                 0.17,
                 0.40,
                 0.80,
                 1.24,
                 2.40,
                 4.30,
                 5.00,
                 6.00,
                 10.00,
                 12.00,
                 15.60,
                 21.90,
                 31.60,
                 45.00,
                 70.00,
                 101.00,
                 154.00,
                 328.00,
                 600.00,
                 10000.00,
                 100000.00
                 };

  const double NewNoiseModel::lowA[]={
            -162.36,
            -166.70,
            -170.00,
            -166.40,
            -168.60,
            -159.98,
            -141.10,
            -71.36,
            -97.26,
            -132.18,
            -205.27,
            -37.65,
            -114.37,
            -160.58,
            -187.50,
            -216.47,
            -185.00,
            -168.34,
            -217.43,
            -258.28,
            -346.88
            };

  const double NewNoiseModel::lowB[]={
            5.64,
            0.00,
            -8.30,
            28.90,
            52.48,
            29.81,
            0.00,
            -99.77,
            -66.49,
            -31.57,
            36.16,
            -104.33,
            -47.10,
            -16.28,
            0.00,
            15.70,
            0.00,
            -7.61,
            11.90,
            26.60,
            48.75
            };

  const double NewNoiseModel::highPeriods[]={
                 0.10,
                 0.22,
                 0.32,
                 0.80,
                 3.80,
                 4.60,
                 6.30,
                 7.90,
                 15.40,
                 20.00,
                 354.80,
                 100000.00
                 };

  const double NewNoiseModel::highA[]={
            -108.73,
            -150.34,
            -122.31,
            -116.85,
            -108.48,
            -74.66,
            0.66,
            -93.37,
            73.54,
            -151.52,
            -206.66
            };

  const double NewNoiseModel::highB[]={
            -17.23,
            -80.50,
            -23.87,
            32.51,
            18.08,
            -32.95,
            -127.18,
            -22.42,
            -162.98,
            10.01,
            31.63
            };

  double NewNoiseModel::interpole(double period, int n, const double * periods, const double * a, const double * b, bool& ok)
  {
    if(period<periods[0] || period>periods[n-1]) {
      ok=false;
      return 0.0;
    } else {
      // TODO: create a sorted C Vector like SortedVector for fast lookup
      int i=0;
      while(i<n && period>periods[i]) {
        i++;
      };
      i--;
      ok=true;
      return a[i]+b[i]*log10(period);
    }
  }

  double NewNoiseModel::lowAcceleration(double frequency, bool& ok)
  {
    return interpole(1.0/frequency, 22, lowPeriods, lowA, lowB, ok);
  }

  double NewNoiseModel::highAcceleration(double frequency, bool& ok)
  {
    return interpole(1.0/frequency, 12, highPeriods, highA, highB, ok);
  }

  double NewNoiseModel::lowVelocity(double frequency, bool& ok)
  {
    double period=1.0/frequency;
    return interpole(period, 22, lowPeriods, lowA, lowB, ok) + 20.0*log10(period/(2*M_PI));
  }

  double NewNoiseModel::highVelocity(double frequency, bool& ok)
  {
    double period=1.0/frequency;
    return interpole(period, 12, highPeriods, highA, highB, ok) + 20.0*log10(period/(2*M_PI));
  }

  double NewNoiseModel::lowDisplacement(double frequency, bool& ok)
  {
    double period=1.0/frequency;
    return interpole(period, 22, lowPeriods, lowA, lowB, ok) + 20.0*log10(period*period/(4*M_PI*M_PI));
  }

  double NewNoiseModel::highDisplacement(double frequency, bool& ok)
  {
    double period=1.0/frequency;
    return interpole(period, 12, highPeriods, highA, highB, ok) + 20.0*log10(period*period/(4*M_PI*M_PI));
  }

  Curve<Point2D> NewNoiseModel::curve(Type t, const SamplingParameters &sampling)
  {
    TRACE;
    Curve<Point2D> res;

    int n=sampling.count();
    Point2D p;
    for(int i=0; i<n; i++) {
      double x=sampling.value(i);
      double y;
      SAFE_UNINITIALIZED(y, 0.0);
      bool ok;
      switch(t) {
      case LowAcceleration:
        y=lowAcceleration(x, ok);
        break;
      case HighAcceleration:
        y=highAcceleration(x, ok);
        break;
      case LowVelocity:
        y=lowVelocity(x, ok);
        break;
      case HighVelocity:
        y=highVelocity(x, ok);
        break;
      case LowDisplacement:
        y=lowDisplacement(x, ok);
        break;
      case HighDisplacement:
        y=highDisplacement(x, ok);
        break;
      }
      if(ok) {
        // Get rid of dB scale, transforms to m^2/s^4/Hz
        res.append(Point2D(x, pow(10.0, y*0.05)));
      }
    }
    return res;
  }

}
