/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ /* * File: Spectrum.hh * Author: hacene * * Created on December 31, 2020, 10:14 AM */ #ifndef SPECTRUM_HH #define SPECTRUM_HH #include "Parameter.hh" #include "ParamData.hh" #include "DataTypeMath.hh" #include "Operation.hh" #include "AMDA_exception.hh" #include #include #include namespace AMDA { namespace Parameters { namespace Spectrum { #define AVERAGE_TIME 1200 // (seconds) #define MAX_GAP_SIZE 3600 // (seconds) using namespace std; typedef enum { CENTER, FORWARD, BACKWARD, } samplingEnum; static std::map strToSamplingEnum{ {"center", samplingEnum::CENTER}, {"forward", samplingEnum::FORWARD}, {"backward", samplingEnum::BACKWARD}, {"0", samplingEnum::CENTER}, {"1", samplingEnum::FORWARD}, {"0", samplingEnum::BACKWARD} }; template class SpectrumBase : public Operation { public: /** * @brief Constructor. * @details Create the ParamData type of the input ParamData. */ SpectrumBase(Process& pProcess, TimeIntervalListSPtr pTimeIntervalList, ParamDataSpec¶mInput, double windowtime, std::string sampling = "center") : Operation(pProcess), _timeIntervalList(pTimeIntervalList), _currentTimeInterval(pTimeIntervalList->begin()), _paramInput(paramInput), _paramOutput(new ParamDataSpec>()), _windowtime(windowtime), _sampling(sampling), _needInit(true) { _paramDataOutput = _paramOutput; }; virtual ~SpectrumBase() { } double getWindowTime() { return _windowtime; } bool inWindow(double time) { return (time >= _minWindow && time <= _maxWindow); } double getIntStartTime() { return _currentTimeInterval->_startTime; } double getIntStopTime() { return _currentTimeInterval->_stopTime; } bool inInt(double time) { return (time >= _currentTimeInterval->_startTime && time <= _currentTimeInterval->_stopTime); } int inMem(std::list >& mem, double time_) { int i = 0; for (auto elt : mem) { if (elt.first == time_) return i; i++; } return -1; } double getTarget() { return _target; } DataType getData(double time_) { DataType res; res << NotANumber(); for (auto data : _mem) { if (data.first == time_) res = data.second; } return res; } bool needInit() { return _needInit; } void setNeedInit(bool needInit) { _needInit = needInit; } bool setTarget(double time) { if (!inInt(time)) { return false; } _target = time; switch (strToSamplingEnum[_sampling]) { case samplingEnum::FORWARD: _minWindow = _target; if (_minWindow < _currentTimeInterval->_startTime) { _minWindow = _currentTimeInterval->_startTime; } _maxWindow = _target + _windowtime; if (_maxWindow > _currentTimeInterval->_stopTime) { _maxWindow = _currentTimeInterval->_stopTime; } break; case samplingEnum::BACKWARD: _minWindow = _target - _windowtime; if (_minWindow < _currentTimeInterval->_startTime) { _minWindow = _currentTimeInterval->_startTime; } _maxWindow = _target; if (_maxWindow > _currentTimeInterval->_stopTime) { _maxWindow = _currentTimeInterval->_stopTime; } break; default: _minWindow = _target - getInputParamSampling() - _windowtime / 2.; if (_minWindow < _currentTimeInterval->_startTime) { _minWindow = _currentTimeInterval->_startTime; } _maxWindow = _target + getInputParamSampling() + _windowtime / 2.; if (_maxWindow > _currentTimeInterval->_stopTime) { _maxWindow = _currentTimeInterval->_stopTime; } break; } return true; } /** * @overload Operation::reset(double pStartTime, double pTimeInt) * @brief reset static data to process another TimeInterval */ virtual void reset() { Operation::reset(); if (_currentTimeInterval == _timeIntervalList->end()) { return; } ++_currentTimeInterval; _needInit = true; resetFunc(); } virtual void init() { setTarget(getIntStartTime()); setNeedInit(false); } virtual bool nextTarget() { double target = getTarget() + getWindowTime(); bool res = setTarget(target); while (!_mem.empty() && !inWindow(_mem.front().first)) { _mem.pop_front(); } return res; } virtual bool needToChangeTarget(double crtTime) { return !needInit() && !inWindow(crtTime); } virtual double getSampling() { return getWindowTime(); } virtual void pushData(double time, DataType& elem) { int pos = SpectrumBase::inMem(_mem, time); if (pos != -1) { auto it = _mem.begin(); std::advance(it, pos); _mem.erase(it); } _mem.push_back(std::make_pair(time, elem)); } virtual void resetFunc() { _mem.clear(); } double getInputParamSampling() { return _paramInput.getMinSampling(); } virtual std::vector compute() = 0; /** * @overload Operation::write(ParamDataIndexInfo &pParamDataIndexInfo) */ void write(ParamDataIndexInfo &pParamDataIndexInfo) { if ((pParamDataIndexInfo._nbDataToProcess > 0)) { for (unsigned int _index = pParamDataIndexInfo._startIndex; _index < pParamDataIndexInfo._startIndex + pParamDataIndexInfo._nbDataToProcess; ++_index) { double crtTime = _paramInput.getTime(_index); DataType crtVal = _paramInput.get(_index); if (needToChangeTarget(crtTime)) { _paramOutput->pushTime(getTarget()); _paramOutput->push(compute()); pushData(crtTime, crtVal); nextTarget(); bool skip = false; while (!skip && needToChangeTarget(crtTime)) { _paramOutput->pushTime(getTarget()); _paramOutput->push(compute()); skip = nextTarget(); } } else { pushData(crtTime, crtVal); if (needInit()) { init(); } } } } if (pParamDataIndexInfo._timeIntToProcessChanged || pParamDataIndexInfo._noMoreTimeInt) { if (!needInit()) { do { if (inInt(getTarget())) { _paramOutput->pushTime(getTarget()); _paramOutput->push(compute()); } } while (nextTarget()); } } } protected: TimeIntervalListSPtr _timeIntervalList; TimeIntervalList::iterator _currentTimeInterval; ParamDataSpec& _paramInput; ParamDataSpec>*_paramOutput; double _windowtime; std::string _sampling; double _minWindow; double _maxWindow; double _target; bool _needInit; std::list > _mem; }; template class SpectrumFourier : public SpectrumBase { public: SpectrumFourier(Process& pProcess, TimeIntervalListSPtr pTimeIntervalList, ParamDataSpec¶mInput, double windowtime, std::string sampling = "center") : SpectrumBase(pProcess, pTimeIntervalList, paramInput, windowtime, sampling) { _spectroSize = std::ceil(windowtime / paramInput.getMinSampling()) + 1; } virtual ~SpectrumFourier() { } virtual void init() { if (!_targets.empty()) { SpectrumBase::setTarget(_targets.front()); SpectrumBase::setNeedInit(false); _targets.pop_front(); } } virtual bool nextTarget() { if (!_targets.empty()) { bool res = SpectrumBase::setTarget(_targets.front()); _targets.pop_front(); while (!_mem.empty() && !SpectrumBase::inWindow(_mem.front().first)) { _mem.pop_front(); } return res; } return false; } virtual bool needToChangeTarget(double crtTime) { return !SpectrumBase::needInit() && !SpectrumBase::inWindow(crtTime) && !_targets.empty(); } virtual double getSampling() { return SpectrumBase::getInputParamSampling(); } virtual void pushData(double time, DataType& elem) { int pos = SpectrumBase::inMem(_mem, time); if (pos != -1) { auto it = _mem.begin(); std::advance(it, pos); _mem.erase(it); } _mem.push_back(std::make_pair(time, elem)); _targets.push_back(time); } virtual void resetFunc() { _mem.clear(); _targets.clear(); } bool isUniform() { if (_mem.empty()) return false; auto it_1 = _mem.begin(); it_1++; for (auto it = _mem.begin(); it != _mem.end(); it++) { if (*it != _mem.back()) { if (it_1->first - it->first != getSampling()) return false; it_1++; } } return true; } std::vector fft(std::vector &xn, int N) { /** * * @param N number of points in the dft * @param xn date vector */ int len = xn.size(); std::vector output; output.resize(N); output << NotANumber(); double Xr; double Xi; int counter; int k, n = 0; for (k = 0; k < N; k++) { Xr = 0; Xi = 0; counter = 0; for (n = 0; n < len; n++) { if (!isNAN(xn[n])) { Xr = Xr + (OutputType) xn[n] * cos(2 * 3.141592 * k * n / N); Xi = Xi + (OutputType) xn[n] * sin(2 * 3.141592 * k * n / N); counter += 1; } } if (counter == 0) break; output[k] = std::sqrt(Xr * Xr + Xi * Xi); } return output; } DataType computeInterpolation(std::list > & input, double time) { double min_t = time - AVERAGE_TIME / 2.; double max_t = time + AVERAGE_TIME / 2.; std::vector > values_for_mean; DataType nanVal; nanVal << NotANumber(); std::pair prev_value(NAN, nanVal); std::pair next_value(NAN, nanVal); DataType value = nanVal; for (auto it = input.begin(); it != input.end(); ++it) { if (it->first == time) { value = it->second; return value; break; } else if (isNAN(it->second)) continue; else if (it->first > max_t) { next_value = *it; break; } else if (it->first < min_t) { prev_value = *it; } else { values_for_mean.push_back(*it); } } if (!values_for_mean.empty()) { //Compute mean DataType sum = 0; for (auto it = values_for_mean.begin(); it != values_for_mean.end(); ++it) { sum += it->second; } value = sum / (DataType) values_for_mean.size(); } else { if (!isNAN(prev_value.first) && !isNAN(next_value.first) && (next_value.first - prev_value.first <= MAX_GAP_SIZE)) { //Compute interpolated value value = prev_value.second + (time - prev_value.first) / (next_value.first - prev_value.first) * (next_value.second - prev_value.second); } } return value; } void standarize(std::list > & mem, int size, double target, double inputSampling, std::string sampling) { double time_; std::list> newMem; for (int i = 0; i < size; i++) { switch (strToSamplingEnum[sampling]) { case samplingEnum::FORWARD: time_ = target + i*inputSampling; break; case samplingEnum::BACKWARD: time_ = target + (i - size + 1) * inputSampling; break; default: time_ = target + (i - size / 2 + 1) * inputSampling; break; } if (SpectrumBase::inMem(mem, time_) != -1) { DataType val_ = SpectrumBase::getData(time_); if (isNAN(val_)) newMem.push_back(std::make_pair(time_, computeInterpolation(mem, time_))); else newMem.push_back(std::make_pair(time_, val_)); } else { newMem.push_back(std::make_pair(time_, computeInterpolation(mem, time_))); } } if (newMem.empty() || newMem.size() != size) BOOST_THROW_EXCEPTION(AMDA::AMDA_exception() << AMDA::errno_code(AMDA_PROCESS_ERR) << AMDA::ex_msg(std::string("Spectrum:: ERROR at data interpolation"))); mem.clear(); mem = newMem; // sort bien comme il faut avec lambda mem.sort([] (const std::pair& a, const std::pair& b ){ return a.first < b.first; }); return; } int numberOfNanValues(std::list > & mem) { int res = 0; for (auto elt : mem) { if (isNAN(elt.second)) res++; } return res; } std::vector compute() { std::vector res; res.resize(_spectroSize); res << NotANumber(); bool is_uniform = isUniform(); int nans = numberOfNanValues(_mem); if (_mem.empty() || _mem.size() < 2 * _spectroSize / 3 || (_spectroSize - nans) < 2 * _spectroSize / 3) return res; if (!is_uniform || _mem.size() < _spectroSize || (_spectroSize - nans) < _spectroSize) standarize(_mem, _spectroSize, SpectrumBase::_target, SpectrumBase::getInputParamSampling(), SpectrumBase::_sampling); std::vector inputData; for (auto elt : _mem) inputData.push_back(elt.second); res = fft(inputData, _spectroSize); return res; } std::vector getFrequencies(){ std::vector res(_spectroSize); for(int i =0; i<_spectroSize; i++ ) res[i] = (double) 1./SpectrumBase::getInputParamSampling()/(double) i; return res; } protected: std::list _targets; std::list > _mem; int _spectroSize; }; } // end Spectrum } // end Parameters } // end AMDA #endif /* SPECTRUM_HH */