/***************************************************************************
**
**  This file is part of gpfksimulator.
**
**  gpfksimulator 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.
**
**  gpfksimulator 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: 2006-03-27
**  Copyright: 2006-2019
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <ArrayCore.h>

#include "Simulator.h"
#include "FKArrayMap.h"
#include "MediumParameters.h"
#include "SourceParameters.h"
#include "SourceItemModel.h"
#include "SourceDelegate.h"

Simulator::Simulator(QWidget *parent, Qt::WindowFlags f)
    : QWidget(parent, f | Qt::Window)
{
  TRACE;

  setupUi(this);

  SourceItemModel * model=new SourceItemModel(&_sources, this);
  connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(setSource(QModelIndex,QModelIndex)));
  sourceTable->setModel(model);
  SourceDelegate * delegate=new SourceDelegate(this);
  connect(sourceTable, SIGNAL(clicked(const QModelIndex&)), delegate, SLOT(colorClicked(QModelIndex)));
  sourceTable->setItemDelegate(delegate);
  sourceTable->setSelectionBehavior(QAbstractItemView::SelectItems);
  sourceTable->setSelectionMode(QAbstractItemView::SingleSelection);
  sourceTable->setEditTriggers(QAbstractItemView::AllEditTriggers);

  connect(planeWavesButton, SIGNAL(toggled(bool)), this, SLOT(setWaveModel()));
  connect(circularWavesButton, SIGNAL(toggled(bool)), this, SLOT(setWaveModel()));
  connect(saveSources, SIGNAL(clicked(bool)), this, SLOT(save()));
  connect(loadSources, SIGNAL(clicked(bool)), this, SLOT(load()));

  _gridLayer=nullptr;
  _maxLayer=nullptr;
  _velocityLayer=nullptr;
  _azimuthLayer=nullptr;
  _kLayer=nullptr;
  _fkMeshLayer=nullptr;
  _fkmap=nullptr;
  _kmaxSolver=nullptr;
  _kmin=0.0;
  _kmax=0.0;
  _globalMax=false;
  _crossSection=false;

  // Temporary removal of DirectSteering
  int i=polarizationEdit->findText("Direct", Qt::MatchContains);
  while(i!=-1) {
    polarizationEdit->removeItem(i);
    i=polarizationEdit->findText("direct", Qt::MatchContains);
  }
}

Simulator::~Simulator()
{
  TRACE;
}

void Simulator::resizeEvent (QResizeEvent * )
{
  TRACE;
}

void Simulator::init(const QVector<Point2D>& array, double sampling)
{
  TRACE;

  // Wavenumber map
  waveNumMap->xAxis()->setTitle(tr("Wave number X (rad/m)"));
  waveNumMap->xAxis()->setTitleInversedScale(tr("Wave length X/(2*pi) (m/rad)"));
  waveNumMap->yAxis()->setTitle(tr("Wave number Y (rad/m)"));
  waveNumMap->yAxis()->setTitleInversedScale(tr("Wave length Y/(2*pi) (m/rad)"));

  _velocityLayer=new CircleViewer(waveNumMap);
  _velocityLayer->setObjectName("velocity");
  _azimuthLayer=new LineLayer(waveNumMap);
  _azimuthLayer->setObjectName("azimuth");
  PlotLine2D * line=new PlotLine2D;
  line->setPen(Pen(Qt::black, 0.6, Qt::SolidLine));
  line->setSymbol(Symbol());
  _azimuthLayer->setReferenceLine(line);

  _gridLayer=new LiveGridLayer(waveNumMap);
  _sampling=sampling;
  if(sampling<1.0) {
    sampling=1.0;
  }
  _gridLayer->setSampling(qRound(sampling));
  _gridLayer->setVariables(LiveGridLayer::Value_XYMaxZ);
  _gridLayer->setOpacity(0.8);
  _fkmap=new FKArrayMap;
  _fkmap->setStations(array);
  _gridLayer->setFunction(_fkmap);
  _gridLayer->setObjectName("wave number map");
  ColorMap& map=_gridLayer->beginColorMapChange();
  map.generateColorScale(20, ColorPalette::McNames, true);
  map.setVLinear(0.0, 1.0);
  _gridLayer->endColorMapChange();

  _maxLayer=new NameLineLayer(waveNumMap);
  _maxLayer->setObjectName("max");
  NameLine * lineMax=new NameLine;
  lineMax->setPen(Pen(Qt::NoPen));
  lineMax->setSymbol(Symbol(Symbol::Circle, 4, Pen(Qt::black, 0.5)));
  _maxLayer->setReferenceLine(lineMax);
  _maxLayer->addLine();

  _kLayer=new CircleMask(waveNumMap);
  _kLayer->setObjectName("grid size");
  _kLayer->setOpacity(0.75);

  _fkMeshLayer=new MeshLayer(waveNumMap);
  _fkMeshLayer->setObjectName("fk mesh");
  _fkMeshLayer->setOpacity(0.0); // de-activated by default for educational purpose

  // Estimate kmax from station coordinates
  KminSolver kminSolver(array);
  bool ok=true;
  _kmin=kminSolver.calculate(ok);
  if(ok && _kmin>0.0) {
    _kmaxSolver=new KmaxSolver(array, _kmin, 0.25);
    connect(&_kmaxTimer, SIGNAL(timeout()), this, SLOT(setKmax()));
    connect(_kmaxSolver, SIGNAL(finished()), this, SLOT(setKmax()));
    _kmaxSolver->calculate();
    _kmaxTimer.start(1000);
  } else {
    _kmaxSolver=nullptr;
    App::log(tr("Error computing kmin for station coordinates\n") );
    setKmax(5.0*_fkmap->theoreticalKmax());
  }

  setWaveModel();
  on_addSource_clicked(); // force initialization with at least one source
  on_frequencyEdit_valueChanged(frequencyEdit->value());
  on_gridSizeEdit_valueChanged(gridSizeEdit->value());
  on_polarizationEdit_currentIndexChanged(polarizationEdit->currentIndex());
  on_attenuationEdit_valueChanged(attenuationEdit->value());
}

void Simulator::setKmax()
{
  TRACE;
  if(_kmaxSolver) {
    bool ok;
    double kmax=_kmaxSolver->kmax(ok);
    if(ok || !_kmaxSolver->isRunning()) {
      _kmaxTimer.stop();
      _kmaxSolver->deleteLater();
      _kmaxSolver=nullptr;
    }
    setKmax(kmax);
  }
}

void Simulator::setKmax(double kmax)
{
  TRACE;
  _kmax=kmax;
  gridSizeEdit->setValue(_kmax);
}

void Simulator::setWaveModel()
{
  TRACE;
  if(!_fkmap) {
    return;
  }
  if(planeWavesButton->isChecked()) {
    attenuationEdit->setEnabled(false);
    attenuationButton->setEnabled(false);
    static_cast<SourceItemModel *>(sourceTable->model())->setWaveModel(SourceParameters::PlaneWaves);
    static_cast<SourceDelegate *>(sourceTable->itemDelegate())->setWaveModel(SourceParameters::PlaneWaves);
    _fkmap->setWaveModel(SourceParameters::PlaneWaves);
  } else {
    attenuationButton->setEnabled(true);
    attenuationEdit->setEnabled(attenuationButton->isChecked());
    static_cast<SourceItemModel *>(sourceTable->model())->setWaveModel(SourceParameters::CircularWaves);
    static_cast<SourceDelegate *>(sourceTable->itemDelegate())->setWaveModel(SourceParameters::CircularWaves);
    _fkmap->setWaveModel(SourceParameters::CircularWaves);
  }
  setAllSources();
}

void Simulator::on_frequencyEdit_valueChanged(double f)
{
  TRACE;
  _medium.setFrequency(f);
  setAllSources();
}

void Simulator::on_polarizationEdit_currentIndexChanged(int index)
{
  TRACE;
  crossSectionBut->setEnabled(false);
  _gridLayer->lockDelayPainting();
  switch(index) {
  case 1:
    _fkmap->setPolarization(FKArrayMap::FK_Radial);
    break;
  case 2:
    _fkmap->setPolarization(FKArrayMap::FK_Transverse);
    break;
  case 3:
    _fkmap->setPolarization(FKArrayMap::FK_Rayleigh);
    break;
  case 4:
    _fkmap->setPolarization(FKArrayMap::FK_RayleighPositive);
    break;
  case 5:
    _fkmap->setPolarization(FKArrayMap::FK_RayleighNegative);
    break;
  case 6:
    _fkmap->setPolarization(FKArrayMap::FK_EllipticitySign);
    break;
  case 7:
    _fkmap->setPolarization(FKArrayMap::HRFK_Vertical);
    break;
  case 8:
    _fkmap->setPolarization(FKArrayMap::HRFK_Radial);
    break;
  case 9:
    _fkmap->setPolarization(FKArrayMap::HRFK_Transverse);
    break;
  case 10:
    _fkmap->setPolarization(FKArrayMap::HRFK_Rayleigh);
    crossSectionBut->setEnabled(true);
    break;
  case 11:
    _fkmap->setPolarization(FKArrayMap::HRFK_Ellipticity);
    break;
  case 12:
    _fkmap->setPolarization(FKArrayMap::HRFK_RayleighNoise);
    break;
  case 13:
    _fkmap->setPolarization(FKArrayMap::HRFK_PoggiEllipticity);
    break;
  case 14:
    _fkmap->setPolarization(FKArrayMap::HRFK2_Rayleigh);
    break;
  case 15:
    _fkmap->setPolarization(FKArrayMap::HRFK2_Ellipticity);
    break;
  case 16:
    _fkmap->setPolarization(FKArrayMap::HRFK2_AbsEllipticity);
    break;
  case 17:
    _fkmap->setPolarization(FKArrayMap::HRFK_DirectLove);
    break;
  case 18:
    _fkmap->setPolarization(FKArrayMap::HRFK_DirectRayleigh);
    break;
  case 19:
    _fkmap->setPolarization(FKArrayMap::HRFK_DirectEllipticity);
    break;
  case 20:
    _fkmap->setPolarization(FKArrayMap::HRFK_DirectAbsEllipticity);
    break;
  case 21:
    _fkmap->setPolarization(FKArrayMap::HRFK2_DirectRayleigh);
    break;
  case 22:
    _fkmap->setPolarization(FKArrayMap::HRFK2_DirectEllipticity);
    break;
  case 23:
    _fkmap->setPolarization(FKArrayMap::HRFK2_DirectAbsEllipticity);
    break;
  default:
    _fkmap->setPolarization(FKArrayMap::FK_Vertical);
    break;
  }
  _fkmap->setStationSignals(noiseRatioEdit->value(), normalizeEdit->isChecked(), blockCountEdit->value());
  _gridLayer->unlock();
  updateMap();
}

void Simulator::on_sensorRotationEdit_valueChanged(double a)
{
  TRACE;
  _fkmap->setSensorRotation(a);
  _gridLayer->lockDelayPainting();
  _fkmap->setStationSignals(noiseRatioEdit->value(), normalizeEdit->isChecked(), blockCountEdit->value());
  _gridLayer->unlock();
  updateMap();
}

void Simulator::on_noiseRatioEdit_valueChanged(double a)
{
  TRACE;
  _gridLayer->lockDelayPainting();
  _fkmap->setStationSignals(a, normalizeEdit->isChecked(), blockCountEdit->value());
  _gridLayer->unlock();
  updateMap();
}

void Simulator::on_normalizeEdit_toggled(bool checked)
{
  TRACE;
  _gridLayer->lockDelayPainting();
  _fkmap->setStationSignals(noiseRatioEdit->value(), checked, blockCountEdit->value());
  _gridLayer->unlock();
  updateMap();
}

void Simulator::on_blockCountEdit_valueChanged(int)
{
  TRACE;
  setAllSources();
}

void Simulator::on_attenuationEdit_valueChanged(double q)
{
  TRACE;
  _medium.setAttenuation(q);
  setAllSources();
}

void Simulator::on_attenuationButton_toggled(bool checked)
{
  TRACE;
  if(checked) {
    _medium.setAttenuation(attenuationEdit->value());
    attenuationEdit->setEnabled(true);
  } else {
    _medium.setAttenuation(0.0);
    attenuationEdit->setEnabled(false);
  }
  setAllSources();
}

void Simulator::on_gridSizeEdit_valueChanged(double k)
{
  TRACE;
  _kLayer->set(0, 0, k, k, Qt::white);
  _kLayer->deepUpdate();
  if(_kmaxSolver) {
    _kmaxTimer.stop();
    _kmaxSolver->terminate();
    _kmaxSolver->deleteLater();
    _kmaxSolver=nullptr;
  }
  waveNumMap->xAxis()->setRange(-k, k);
  waveNumMap->yAxis()->setRange(-k, k);
}

void Simulator::on_addSource_clicked()
{
  TRACE;
  static_cast<SourceItemModel *>(sourceTable->model())->addSource();
  _fkmap->addSource();
  addSourceMarkers();
  setSource(_fkmap->sourceCount()-1);
}

void Simulator::addSourceMarkers()
{
  TRACE;
  while(_velocityLayer->count()<_sources.count()) {
    _velocityLayer->add(0, 0, 0, 0, 0.0, Qt::black);
    AbstractLine * l=_azimuthLayer->addLine();
    l->append();
    l->append();
    l->setX(0, 0.0);
    l->setY(0, 0.0, nullptr);
  }
}

void Simulator::on_removeSource_clicked()
{
  TRACE;
  int index=sourceTable->currentIndex().row();
  if(index>=0) {
    static_cast<SourceItemModel *>(sourceTable->model())->removeSource(index);
    _fkmap->removeSource(index);
    _gridLayer->lockDelayPainting();
    _fkmap->setStationSignals(noiseRatioEdit->value(), normalizeEdit->isChecked(), blockCountEdit->value());
    _gridLayer->unlock();
    _velocityLayer->remove(index);
    _azimuthLayer->removeLine(index);
    updateMap();
  }
}

void Simulator::on_crossSectionBut_clicked()
{
  TRACE;
  QString fileName=Message::getSaveFileName(tr("Cross-section"), tr("Text file (*)"));
  if(!fileName.isEmpty()) {
    double xmin=waveNumMap->xAxis()->minimum();
    double dx=(waveNumMap->xAxis()->maximum()-xmin)/200.0;
    Point k;
    QFile f(fileName);
    if(f.open(QIODevice::WriteOnly)) {
      QTextStream s(&f);
      /*
      s << "#\n";
      for(int i=0; i<=200; i++) {
        k.setX(xmin+(double)i*dx);
        s << QString::number(k.x(), 'f', 20) << " " << QString::number(_fkmap->HRFKRayleigh(k), 'f', 20) << ::endl;
      }
      */
      /*
      for(double v=190; v<=210; v+=2) {
        k.setX(10.0*2.0*M_PI/v);
        s << "#\n";
        for(double ell=-2.5; ell<=2.5; ell+=0.01) {
          k.setZ(ell);
          s << QString::number(k.z(), 'f', 20) << " " << QString::number(_fkmap->HRFKRayleigh(k), 'f', 20) << ::endl;
        }
      }
      */
      k.setX(10.0*2.0*M_PI/200);
      for(double ell=-2.5; ell<=2.5; ell+=0.01) {
        k.setZ(ell);
        s << QString::number(k.z(), 'f', 20) << " " << QString::number(_fkmap->HRFKRayleigh(k), 'f', 20) << ::endl;
      }
    }
  }
}

void Simulator::on_maximaBut_clicked()
{
  TRACE;
  GridSearch grid;
  grid.setFunction(_fkmap);
  _fkmap->setKmax(_kmax);
  // 0.005 is designed for a default sampling of 5
  // If the user specify a denser sampling, decrease the search step.
  double dkx=0.005*(_sampling/5.0)*(waveNumMap->xAxis()->visibleMaximum()-waveNumMap->xAxis()->visibleMinimum());
  double dky=0.005*(_sampling/5.0)*(waveNumMap->yAxis()->visibleMaximum()-waveNumMap->yAxis()->visibleMinimum());
  if(dkx<dky) {
    dky=dkx;
  } else {
    dkx=dky;
  }
  grid.setGrid(waveNumMap->xAxis()->visibleMinimum(),
               waveNumMap->xAxis()->visibleMaximum(),
               dkx,
               waveNumMap->yAxis()->visibleMinimum(),
               waveNumMap->yAxis()->visibleMaximum(),
               dky,
               -2.5, 2.5, 0.01);
  _fkMeshLayer->setStep(dkx);
  _fkMeshLayer->setSize(waveNumMap->xAxis()->visibleMaximum());

  Curve<NamedPoint>& plot=static_cast<NameLine *>(_maxLayer->line(0))->curve();
  plot.clear();
  if(globalMaximumEdit->isChecked()) {
    grid.globalMax();
  } else {
    grid.localMaxBest(INT_MAX, 0.0, 5.0);
  }
  FunctionMaximaIterator it;
  double dkMin=std::numeric_limits<double>::infinity();
  for(it=grid.begin(); it!=grid.end(); ++it) {
    NamedPoint kell=(*it)->pos();
    double dk=distanceToSources(kell);
    printf("%12lf %12lf %12lf %12lf\n", (*it)->value(), kell.x(), kell.y(), kell.z());
    kell.setZ(0.0);
    kell.setName(QString::number((*it)->value(), 'g', 2));
    if(dk<dkMin) {
      dkMin=dk;
    }
    plot.append(kell);
  }
  printf("min dist to source %lf\n", dkMin);
  grid.takeFunction();
  _fkmap->setKmax(0.0); // back to plot all peak even > kmax
  _gridLayer->deepUpdate();
}

void Simulator::displaySource(int index)
{
  TRACE;
  const SourceParameters& src=_sources.at(index);
  QColor col;
  guiColor(src.color(), col);
  double v=src.velocity();
  double k=2.0*M_PI*_medium.frequency()/v;
  double a=src.azimuthMath(_fkmap->waveModel());
  _velocityLayer->set(index, 0, 0, k, k, 0.0, col);
  AbstractLine * l=_azimuthLayer->line(index);
  l->setPen(Pen(col, 0.6, Qt::SolidLine));
  l->setX(1, k*cos(a));
  l->setY(1, k*sin(a), nullptr);
}

void Simulator::setSource(QModelIndex topLeft, QModelIndex)
{
  TRACE;
  setSource(topLeft.row());
  displaySource(topLeft.row());
}

void Simulator::setSource(int iSrc)
{
  TRACE;
  _gridLayer->lockDelayPainting();
  _fkmap->setSourceSignals(iSrc, _sources.at(iSrc), _medium, blockCountEdit->value());
  _fkmap->setStationSignals(noiseRatioEdit->value(), normalizeEdit->isChecked(), blockCountEdit->value());
  _gridLayer->unlock();
  updateMap();
}

void Simulator::setAllSources()
{
  TRACE;
  _gridLayer->lockDelayPainting();
  int n=_fkmap->sourceCount();
  for(int i=0; i<n; i++) {
    _fkmap->setSourceSignals(i, _sources.at(i), _medium, blockCountEdit->value());
    displaySource(i);
  }
  _fkmap->setStationSignals(noiseRatioEdit->value(), normalizeEdit->isChecked(), blockCountEdit->value());
  _gridLayer->unlock();
  updateMap();
}

void Simulator::updateMap()
{
  TRACE;
  //if(_fkmap->polarization()==FKArrayMap::RayleighEllSign) {
  //  ellipticitySign();
  //} else {
    updateMax();
  //}
  waveNumMap->graphContent()->deepUpdate();
}

/*!
  Look for the highest peak in the current map area
*/
void Simulator::updateMax()
{
  TRACE;
  if(_kmin>0.0 && _kmax>0.0) {
    Curve<NamedPoint>& plot=static_cast<NameLine *>(_maxLayer->line(0))->curve();
    plot.clear();
  }
}

/*!
  Look for ellipticity sign at the peaks
*/
void Simulator::ellipticitySign()
{
  TRACE;
//  if(_kmin>0.0 && _kmax>0.0) {
//    GridSearch grid;
//    grid.setFunction(_fkmap);
//    _fkmap->setPolarization(FKArrayMap::Rayleigh);
//    _fkmap->setKmax(_kmax);
//    grid.setGrid(waveNumMap->xAxis()->visibleMinimum(),
//                 waveNumMap->xAxis()->visibleMaximum(),
//                 0.25*_kmin,
//                 waveNumMap->yAxis()->visibleMinimum(),
//                 waveNumMap->yAxis()->visibleMaximum(),
//                 0.25*_kmin);
//    Point2D k;
//    if(_globalMax) {
//      grid.globalMax();
//      k=grid.pos();
//      _fkmap->setPolarization(FKArrayMap::RayleighEllSign);
//      printf(" %lf %lf %i\n", k.x(), k.y(), _fkmap->value(k)==1.0 ? 1 : -1);
//    } else {
//      grid.localMax(INT_MAX, 0.0, 90);
//      FunctionMaximaIterator it;
//      int globalPol=-2, pol;
//      for(it=grid.begin(); it!=grid.end(); ++it) {
//        k=(*it)->pos();
//        // Check that wavenumber corresponds to a source
//        double dk=distanceToSources(k);
//        if(dk<_kmin) {
//          _fkmap->setPolarization(FKArrayMap::RayleighEllSign);
//          pol=_fkmap->value(k)==1.0 ? 1 : -1;
//          _fkmap->setPolarization(FKArrayMap::Rayleigh);
//          if(globalPol!=pol) {
//            if(globalPol==-2) {
//              globalPol=pol;
//            } else {
//              globalPol=0;
//            }
//          }
//        }
//        printf(" %i\n", globalPol);
//      }
//    }
//    grid.takeFunction();
//    _fkmap->setPolarization(FKArrayMap::RayleighEllSign);
//    _fkmap->setKmax(0.0); // back to plot all peak even > kmax
//  }
}

double Simulator::distanceToSources(const Point2D &k)
{
  TRACE;
  int n=_sources.count();
  double dkMin=std::numeric_limits<double>::infinity();
  for(int i=0; i<n; i++) {
    const SourceParameters& src=_sources.at(i);
    if(src.polarization()!=Mode::Transverse) {
      double v=src.velocity();
      double ks=2.0*M_PI*_medium.frequency()/v;
      double a=src.azimuthMath(_fkmap->waveModel());
      Point2D ksv(ks*cos(a), ks*sin(a));
      double dk=k.distanceTo(ksv);
      if(dk<dkMin) {
        dkMin=dk;
      }
    }
  }
  return dkMin;
}

bool Simulator::save(QString fileName)
{
  TRACE;
  static const QString title=tr("Save source parameters");
  if(fileName.isEmpty()) {
    fileName=Message::getSaveFileName(title, tr("Source parameters (*.src)"));
  }
  if(!fileName.isEmpty()) {
    XMLHeader hdr(&_sources);
    XMLClass::Error err=hdr.xml_saveFile(fileName, nullptr);
    if(err==XMLClass::NoError) {
      return true;
    } else {
      Message::warning(MSG_ID, title, XMLClass::message(err, fileName));
    }
  }
  return false;
}

bool Simulator::load(QString fileName)
{
  TRACE;
  static const QString title=tr("Open an existing source list");
  if(fileName.isEmpty()) {
    fileName=Message::getOpenFileName(title, tr("Source parameters (*.src)"));
  }
  if(!fileName.isEmpty()) {
    SourceList list;
    XMLHeader hdr(&list);
    XMLClass::Error err=hdr.xml_restoreFile(fileName);
    if(err==XMLClass::NoError) {
      static_cast<SourceItemModel *>(sourceTable->model())->setSources(list);
      _fkmap->setSourceCount(_sources.count());
      _velocityLayer->clear();
      _azimuthLayer->clear();
      addSourceMarkers();
      setAllSources();
      return true;
    } else {
      Message::warning(MSG_ID, title, XMLClass::message(err, fileName));
    }
  }
  return false;
}

void Simulator::scan(const QStringList& sources)
{
  TRACE;
  if(_kmaxSolver) {
    _kmaxSolver->wait();
    CoreApplication::processEvents();
  }
  _gridLayer->lockDelayPainting();

  // Get the list of source to scan
  SourceList refSources;
  QList<int> sourceIndexes;
  int nSrc=_sources.count();
  for(int iSrc=0; iSrc<nSrc; iSrc++) {
    SourceParameters& src=_sources[iSrc];
    if(sources.contains(src.name())) {
      refSources.append(src);
      sourceIndexes.append(iSrc);
    }
  }
  nSrc=sourceIndexes.count();

  for(double az=0.0; az<360.0; az+=10.0) {
    for(double ph=0.0; ph<360; ph+=10) {
      for(int iSrc=0; iSrc<nSrc; iSrc++) {
        SourceParameters& src=_sources[sourceIndexes.at(iSrc)];
        src=refSources.at(iSrc);
        src.setAzimuth(_fkmap->waveModel(), src.azimuth(_fkmap->waveModel())+az);
        src.setPhase(src.phase()+ph);
        _fkmap->setSourceSignals(iSrc, src, _medium, blockCountEdit->value());
      }
      _fkmap->setStationSignals(noiseRatioEdit->value(), normalizeEdit->isChecked(), blockCountEdit->value());
      printf("%lf %lf ", az, ph);
      if(_fkmap->polarization()==FKArrayMap::FK_EllipticitySign) {
        ellipticitySign();
      } else {
        updateMax();
      }
    }
  }
  _gridLayer->unlock();
}
