File indexing completed on 2024-12-22 04:18:12
0001 /*************************************************************************** 0002 * * 0003 * copyright : (C) 2007 The University of Toronto * 0004 * netterfield@astro.utoronto.ca * 0005 * copyright : (C) 2005 University of British Columbia * 0006 * dscott@phas.ubc.ca * 0007 * * 0008 * This program is free software; you can redistribute it and/or modify * 0009 * it under the terms of the GNU General Public License as published by * 0010 * the Free Software Foundation; either version 2 of the License, or * 0011 * (at your option) any later version. * 0012 * * 0013 ***************************************************************************/ 0014 0015 0016 #include "fitkneefrequency.h" 0017 #include "objectstore.h" 0018 #include "ui_fitkneefrequencyconfig.h" 0019 0020 #include <gsl/gsl_fit.h> 0021 #include "../common.h" 0022 0023 #define KNEEFREQ_NUMPARAMETERS 5 0024 0025 static const QString& VECTOR_IN_X = "X Vector"; 0026 static const QString& VECTOR_IN_Y = "Y Vector"; 0027 static const QString& SCALAR_IN_MAX = "Max 1/f^a Freq Scalar"; 0028 static const QString& SCALAR_IN_MIN = "Min. White Noise Freq Scalar"; 0029 static const QString& SCALAR_IN_WHITENOISE = "White Noise C Scalar"; 0030 static const QString& VECTOR_OUT_Y_FITTED = "Fit"; 0031 static const QString& VECTOR_OUT_Y_RESIDUALS = "Residuals"; 0032 static const QString& VECTOR_OUT_Y_PARAMETERS = "Parameters Vector"; 0033 0034 class ConfigWidgetFitKneeFrequencyPlugin : public Kst::DataObjectConfigWidget, public Ui_FitKneeFrequencyConfig { 0035 public: 0036 ConfigWidgetFitKneeFrequencyPlugin(QSettings* cfg) : DataObjectConfigWidget(cfg), Ui_FitKneeFrequencyConfig() { 0037 _store = 0; 0038 setupUi(this); 0039 } 0040 0041 ~ConfigWidgetFitKneeFrequencyPlugin() {} 0042 0043 void setObjectStore(Kst::ObjectStore* store) { 0044 _store = store; 0045 _vectorX->setObjectStore(store); 0046 _vectorY->setObjectStore(store); 0047 _scalarMax->setObjectStore(store); 0048 _scalarMin->setObjectStore(store); 0049 _scalarWhiteNoise->setObjectStore(store); 0050 } 0051 0052 void setupSlots(QWidget* dialog) { 0053 if (dialog) { 0054 connect(_vectorX, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified())); 0055 connect(_vectorY, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified())); 0056 connect(_scalarMax, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified())); 0057 connect(_scalarMin, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified())); 0058 connect(_scalarWhiteNoise, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified())); 0059 } 0060 } 0061 0062 0063 void setVectorX(Kst::VectorPtr vector) { 0064 setSelectedVectorX(vector); 0065 } 0066 0067 void setVectorY(Kst::VectorPtr vector) { 0068 setSelectedVectorY(vector); 0069 } 0070 0071 void setVectorsLocked(bool locked = true) { 0072 _vectorX->setEnabled(!locked); 0073 _vectorY->setEnabled(!locked); 0074 } 0075 0076 Kst::VectorPtr selectedVectorX() { return _vectorX->selectedVector(); }; 0077 void setSelectedVectorX(Kst::VectorPtr vector) { return _vectorX->setSelectedVector(vector); }; 0078 0079 Kst::VectorPtr selectedVectorY() { return _vectorY->selectedVector(); }; 0080 void setSelectedVectorY(Kst::VectorPtr vector) { return _vectorY->setSelectedVector(vector); }; 0081 0082 Kst::ScalarPtr selectedScalarMax() { return _scalarMax->selectedScalar(); }; 0083 void setSelectedScalarMax(Kst::ScalarPtr scalar) { return _scalarMax->setSelectedScalar(scalar); }; 0084 0085 Kst::ScalarPtr selectedScalarMin() { return _scalarMin->selectedScalar(); }; 0086 void setSelectedScalarMin(Kst::ScalarPtr scalar) { return _scalarMin->setSelectedScalar(scalar); }; 0087 0088 Kst::ScalarPtr selectedScalarWhiteNoise() { return _scalarWhiteNoise->selectedScalar(); }; 0089 void setSelectedScalarWhiteNoise(Kst::ScalarPtr scalar) { return _scalarWhiteNoise->setSelectedScalar(scalar); }; 0090 0091 virtual void setupFromObject(Kst::Object* dataObject) { 0092 if (FitKneeFrequencySource* source = static_cast<FitKneeFrequencySource*>(dataObject)) { 0093 setSelectedVectorX(source->vectorX()); 0094 setSelectedVectorY(source->vectorY()); 0095 setSelectedScalarMax(source->scalarMax()); 0096 setSelectedScalarMin(source->scalarMin()); 0097 setSelectedScalarWhiteNoise(source->scalarWhiteNoise()); 0098 } 0099 } 0100 0101 virtual bool configurePropertiesFromXml(Kst::ObjectStore *store, QXmlStreamAttributes& attrs) { 0102 Q_UNUSED(store); 0103 Q_UNUSED(attrs); 0104 0105 bool validTag = true; 0106 0107 // QStringRef av; 0108 // av = attrs.value("value"); 0109 // if (!av.isNull()) { 0110 // _configValue = QVariant(av.toString()).toBool(); 0111 // } 0112 0113 return validTag; 0114 } 0115 0116 public slots: 0117 virtual void save() { 0118 if (_cfg) { 0119 _cfg->beginGroup("Fit Knee Frequency Plugin"); 0120 _cfg->setValue("Input Vector X", _vectorX->selectedVector()->Name()); 0121 _cfg->setValue("Input Vector Y", _vectorY->selectedVector()->Name()); 0122 _cfg->setValue("Input Scalar Max 1/f^a Freq", _scalarMax->selectedScalar()->Name()); 0123 _cfg->setValue("Input Scalar Min. White Noise Freq", _scalarMin->selectedScalar()->Name()); 0124 _cfg->setValue("Input Scalar White Noise C", _scalarWhiteNoise->selectedScalar()->Name()); 0125 _cfg->endGroup(); 0126 } 0127 } 0128 0129 virtual void load() { 0130 if (_cfg && _store) { 0131 _cfg->beginGroup("Fit Knee Frequency Plugin"); 0132 QString vectorName = _cfg->value("Input Vector X").toString(); 0133 Kst::Object* object = _store->retrieveObject(vectorName); 0134 Kst::Vector* vectorx = static_cast<Kst::Vector*>(object); 0135 if (vectorx) { 0136 setSelectedVectorX(vectorx); 0137 } 0138 vectorName = _cfg->value("Input Vector Y").toString(); 0139 object = _store->retrieveObject(vectorName); 0140 Kst::Vector* vectory = static_cast<Kst::Vector*>(object); 0141 if (vectory) { 0142 setSelectedVectorX(vectory); 0143 } 0144 QString scalarName = _cfg->value("Input Scalar Max 1/f^a Freq").toString(); 0145 object = _store->retrieveObject(scalarName); 0146 Kst::Scalar* maxScalar = static_cast<Kst::Scalar*>(object); 0147 if (maxScalar) { 0148 setSelectedScalarMax(maxScalar); 0149 } 0150 scalarName = _cfg->value("Input Scalar Min. White Noise Freq").toString(); 0151 object = _store->retrieveObject(scalarName); 0152 Kst::Scalar* minScalar = static_cast<Kst::Scalar*>(object); 0153 if (minScalar) { 0154 setSelectedScalarMin(minScalar); 0155 } 0156 scalarName = _cfg->value("Input Scalar White Noise C").toString(); 0157 object = _store->retrieveObject(scalarName); 0158 Kst::Scalar* whiteNoiseScalar = static_cast<Kst::Scalar*>(object); 0159 if (whiteNoiseScalar) { 0160 setSelectedScalarWhiteNoise(whiteNoiseScalar); 0161 } 0162 _cfg->endGroup(); 0163 } 0164 } 0165 0166 private: 0167 Kst::ObjectStore *_store; 0168 0169 }; 0170 0171 0172 FitKneeFrequencySource::FitKneeFrequencySource(Kst::ObjectStore *store) 0173 : Kst::BasicPlugin(store) { 0174 } 0175 0176 0177 FitKneeFrequencySource::~FitKneeFrequencySource() { 0178 } 0179 0180 0181 QString FitKneeFrequencySource::_automaticDescriptiveName() const { 0182 return tr("Fit Knee Frequency Plugin"); 0183 } 0184 0185 0186 void FitKneeFrequencySource::change(Kst::DataObjectConfigWidget *configWidget) { 0187 if (ConfigWidgetFitKneeFrequencyPlugin* config = static_cast<ConfigWidgetFitKneeFrequencyPlugin*>(configWidget)) { 0188 setInputVector(VECTOR_IN_X, config->selectedVectorX()); 0189 setInputVector(VECTOR_IN_Y, config->selectedVectorY()); 0190 setInputScalar(SCALAR_IN_MAX, config->selectedScalarMax()); 0191 setInputScalar(SCALAR_IN_MIN, config->selectedScalarMin()); 0192 setInputScalar(SCALAR_IN_WHITENOISE, config->selectedScalarWhiteNoise()); 0193 } 0194 } 0195 0196 0197 void FitKneeFrequencySource::setupOutputs() { 0198 setOutputVector(VECTOR_OUT_Y_FITTED, ""); 0199 setOutputVector(VECTOR_OUT_Y_RESIDUALS, ""); 0200 setOutputVector(VECTOR_OUT_Y_PARAMETERS, ""); 0201 } 0202 0203 0204 bool FitKneeFrequencySource::algorithm() { 0205 Kst::VectorPtr inputVectorX = _inputVectors[VECTOR_IN_X]; 0206 Kst::VectorPtr inputVectorY = _inputVectors[VECTOR_IN_Y]; 0207 Kst::ScalarPtr inputScalarMax = _inputScalars[SCALAR_IN_MAX]; 0208 Kst::ScalarPtr inputScalarMin = _inputScalars[SCALAR_IN_MIN]; 0209 Kst::ScalarPtr inputScalarWhiteNoise = _inputScalars[SCALAR_IN_WHITENOISE]; 0210 0211 Kst::VectorPtr outputVectorYFitted = _outputVectors[VECTOR_OUT_Y_FITTED]; 0212 Kst::VectorPtr outputVectorYResiduals = _outputVectors[VECTOR_OUT_Y_RESIDUALS]; 0213 Kst::VectorPtr outputVectorYParameters = _outputVectors[VECTOR_OUT_Y_PARAMETERS]; 0214 0215 if (inputVectorX->length() != inputVectorY->length()) { 0216 _errorString = tr("Error: Input Vector Sizes do not match"); 0217 return false; 0218 } 0219 if (inputVectorX->length() < 1) { 0220 _errorString = tr("Error: Input Vector X invalid"); 0221 return false; 0222 } 0223 0224 int inArraysLength = inputVectorX->length(); 0225 0226 outputVectorYFitted->resize(inArraysLength); 0227 outputVectorYResiduals->resize(inArraysLength); 0228 outputVectorYParameters->resize(KNEEFREQ_NUMPARAMETERS); 0229 0230 double xi, yi; 0231 int i; 0232 double maxOneOverFFreq, minWhiteNoiseFreq, whiteNoiseC; 0233 0234 maxOneOverFFreq = inputScalarMax->value(); 0235 minWhiteNoiseFreq = inputScalarMin->value(); 0236 whiteNoiseC = inputScalarWhiteNoise->value(); 0237 0238 int maxOneOverFIndex, minWhiteNoiseIndex; 0239 0240 //fast calculation of index for maxOneOverFFreq 0241 int i_bot = 0; 0242 int i_top = inArraysLength - 1; 0243 0244 while (i_bot + 1 < i_top) { 0245 int i0 = (i_top + i_bot)/2; 0246 if (maxOneOverFFreq < inputVectorX->value()[i0]) { 0247 i_top = i0; 0248 } else { 0249 i_bot = i0; 0250 } 0251 } 0252 maxOneOverFIndex = i_top; //top because we use i_bot+1. 0253 0254 //fast calculation of index for minWhiteNoiseFreq 0255 i_bot = 0; 0256 i_top = inArraysLength - 1; 0257 0258 while (i_bot + 1 < i_top) { 0259 int i0 = (i_top + i_bot)/2; 0260 if (minWhiteNoiseFreq < inputVectorX->value()[i0]) { 0261 i_top = i0; 0262 } else { 0263 i_bot = i0; 0264 } 0265 } 0266 minWhiteNoiseIndex = i_top; 0267 0268 //verify calculated indices. 0269 if ( !(maxOneOverFIndex>0) || !(minWhiteNoiseIndex>=maxOneOverFIndex) || !(minWhiteNoiseIndex<(inArraysLength-1)) ) { 0270 _errorString = tr("Error: Input Frequencies are Invalid\n"); 0271 return false; 0272 } 0273 0274 // calculate white noise limit 0275 double sumY, sumY2; 0276 sumY = sumY2 = 0; 0277 0278 for (i = minWhiteNoiseIndex; i < inArraysLength; ++i) { 0279 yi = inputVectorY->value()[i]; 0280 sumY += yi; 0281 sumY2 += pow(yi,2); 0282 } 0283 0284 double ybar, ysigma; 0285 ybar = sumY/(inArraysLength - minWhiteNoiseIndex); 0286 ysigma = sqrt((sumY2 - 2*ybar*sumY + pow(ybar,2)*(inArraysLength - minWhiteNoiseIndex))/(inArraysLength - minWhiteNoiseIndex)); 0287 // end calculate white noise limit 0288 0289 // fit 1/f noise 0290 double sumLnXLnY, sumLnX, sumLnY, sumLnX2; 0291 sumLnXLnY = sumLnX = sumLnY = sumLnX2 = 0; 0292 0293 for (i = 0; i < maxOneOverFIndex; ++i) { 0294 xi = inputVectorX->value()[i]; 0295 yi = inputVectorY->value()[i]; 0296 0297 if (!(xi>0) || !((yi-ybar)>0)) { 0298 _errorString = tr("Error: Input Data Invalid.\n"); 0299 return false; 0300 } 0301 0302 sumLnXLnY += log(xi)*log(yi-ybar); //-ybar to isolate 1/f noise. 0303 sumLnX += log(xi); 0304 sumLnY += log(yi-ybar); 0305 sumLnX2 += pow(log(xi),2); 0306 } 0307 0308 double a, b; 0309 a = (maxOneOverFIndex*sumLnXLnY - sumLnX*sumLnY)/(maxOneOverFIndex*sumLnX2 - pow(sumLnX,2)); 0310 b = exp((sumLnY - a*sumLnX)/maxOneOverFIndex); 0311 // end fit 1/f noise 0312 0313 double knee_freq = pow(ybar*whiteNoiseC/b,1.0/a); // calculate knee frequency. 0314 0315 // output fit data 0316 for (i = 0; i < maxOneOverFIndex; ++i) { 0317 outputVectorYFitted->raw_V_ptr()[i] = b * pow(inputVectorX->value()[i],a) + ybar; 0318 outputVectorYResiduals->raw_V_ptr()[i] = inputVectorY->value()[i] - outputVectorYFitted->raw_V_ptr()[i]; 0319 } 0320 0321 for (i = maxOneOverFIndex; i < minWhiteNoiseIndex; ++i) { // zeros for unfitted region. 0322 outputVectorYFitted->raw_V_ptr()[i] = 0; 0323 outputVectorYResiduals->raw_V_ptr()[i] = 0; 0324 } 0325 0326 for (i = minWhiteNoiseIndex; i < inArraysLength; ++i) { 0327 outputVectorYFitted->raw_V_ptr()[i] = ybar; 0328 outputVectorYResiduals->raw_V_ptr()[i] = outputVectorYFitted->raw_V_ptr()[i] - ybar; 0329 } 0330 0331 outputVectorYParameters->raw_V_ptr()[0] = ybar; 0332 outputVectorYParameters->raw_V_ptr()[1] = ysigma; 0333 outputVectorYParameters->raw_V_ptr()[2] = b; 0334 outputVectorYParameters->raw_V_ptr()[3] = -a; 0335 outputVectorYParameters->raw_V_ptr()[4] = knee_freq; 0336 0337 return true; 0338 } 0339 0340 0341 Kst::VectorPtr FitKneeFrequencySource::vectorX() const { 0342 return _inputVectors[VECTOR_IN_X]; 0343 } 0344 0345 0346 Kst::VectorPtr FitKneeFrequencySource::vectorY() const { 0347 return _inputVectors[VECTOR_IN_Y]; 0348 } 0349 0350 0351 Kst::ScalarPtr FitKneeFrequencySource::scalarMax() const { 0352 return _inputScalars[SCALAR_IN_MAX]; 0353 } 0354 0355 0356 Kst::ScalarPtr FitKneeFrequencySource::scalarMin() const { 0357 return _inputScalars[SCALAR_IN_MIN]; 0358 } 0359 0360 0361 Kst::ScalarPtr FitKneeFrequencySource::scalarWhiteNoise() const { 0362 return _inputScalars[SCALAR_IN_WHITENOISE]; 0363 } 0364 0365 0366 QStringList FitKneeFrequencySource::inputVectorList() const { 0367 QStringList vectors(VECTOR_IN_X); 0368 vectors += VECTOR_IN_Y; 0369 return vectors; 0370 } 0371 0372 0373 QStringList FitKneeFrequencySource::inputScalarList() const { 0374 QStringList scalars(SCALAR_IN_MAX); 0375 scalars += SCALAR_IN_MIN; 0376 scalars += SCALAR_IN_WHITENOISE; 0377 return scalars; 0378 } 0379 0380 0381 QStringList FitKneeFrequencySource::inputStringList() const { 0382 return QStringList( /*STRING_IN*/ ); 0383 } 0384 0385 0386 QStringList FitKneeFrequencySource::outputVectorList() const { 0387 QStringList vectors(VECTOR_OUT_Y_FITTED); 0388 vectors += VECTOR_OUT_Y_RESIDUALS; 0389 vectors += VECTOR_OUT_Y_PARAMETERS; 0390 vectors += VECTOR_OUT_Y_PARAMETERS; 0391 return vectors; 0392 } 0393 0394 0395 QStringList FitKneeFrequencySource::outputScalarList() const { 0396 return QStringList( /*SCALAR_OUT */); 0397 } 0398 0399 0400 QStringList FitKneeFrequencySource::outputStringList() const { 0401 return QStringList( /*STRING_OUT*/ ); 0402 } 0403 0404 0405 void FitKneeFrequencySource::saveProperties(QXmlStreamWriter &s) { 0406 Q_UNUSED(s); 0407 // s.writeAttribute("value", _configValue); 0408 } 0409 0410 0411 QString FitKneeFrequencySource::parameterName(int index) const { 0412 QString parameter; 0413 switch (index) { 0414 case 0: 0415 parameter = "White Noise Limit"; 0416 break; 0417 case 1: 0418 parameter = "White Noise Sigma"; 0419 break; 0420 case 2: 0421 parameter = "1/f^a Amplitude"; 0422 break; 0423 case 3: 0424 parameter = "1/f^a Power Law a"; 0425 break; 0426 case 4: 0427 parameter = "Knee Frequency"; 0428 break; 0429 } 0430 0431 return parameter; 0432 } 0433 0434 0435 // Name used to identify the plugin. Used when loading the plugin. 0436 QString FitKneeFrequencyPlugin::pluginName() const { return tr("Knee Frequency Fit"); } 0437 QString FitKneeFrequencyPlugin::pluginDescription() const { return tr("Generates a knee frequency fit for a set of data."); } 0438 0439 0440 Kst::DataObject *FitKneeFrequencyPlugin::create(Kst::ObjectStore *store, Kst::DataObjectConfigWidget *configWidget, bool setupInputsOutputs) const { 0441 0442 if (ConfigWidgetFitKneeFrequencyPlugin* config = static_cast<ConfigWidgetFitKneeFrequencyPlugin*>(configWidget)) { 0443 0444 Kst::ScalarPtr max; 0445 Kst::ScalarPtr min; 0446 Kst::ScalarPtr noise; 0447 0448 // access/create scalars before creating plugin 0449 // in order to preserve continuous scalar shortnames 0450 if (setupInputsOutputs) { 0451 max = config->selectedScalarMax(); 0452 min = config->selectedScalarMin(); 0453 noise = config->selectedScalarWhiteNoise(); 0454 } 0455 0456 FitKneeFrequencySource* object = store->createObject<FitKneeFrequencySource>(); 0457 0458 if (setupInputsOutputs) { 0459 object->setInputScalar(SCALAR_IN_MAX, max); 0460 object->setInputScalar(SCALAR_IN_MIN, min); 0461 object->setInputScalar(SCALAR_IN_WHITENOISE, noise); 0462 object->setupOutputs(); 0463 object->setInputVector(VECTOR_IN_X, config->selectedVectorX()); 0464 object->setInputVector(VECTOR_IN_Y, config->selectedVectorY()); 0465 } 0466 0467 object->setPluginName(pluginName()); 0468 0469 object->writeLock(); 0470 object->registerChange(); 0471 object->unlock(); 0472 0473 return object; 0474 } 0475 return 0; 0476 } 0477 0478 0479 Kst::DataObjectConfigWidget *FitKneeFrequencyPlugin::configWidget(QSettings *settingsObject) const { 0480 ConfigWidgetFitKneeFrequencyPlugin *widget = new ConfigWidgetFitKneeFrequencyPlugin(settingsObject); 0481 return widget; 0482 } 0483 0484 #ifndef QT5 0485 Q_EXPORT_PLUGIN2(kstplugin_FitKneeFrequencyPlugin, FitKneeFrequencyPlugin) 0486 #endif 0487 0488 // vim: ts=2 sw=2 et