void ConfigStabilizationWidget::resetToDefaults()
{
    StabilizationSettings stabDefaults;
    StabilizationSettings::DataFields defaults = stabDefaults.getData();
    bool dirty=isDirty();
    refreshUIValues(defaults);
    setDirty(dirty);
}
void VehicleConfigurationHelper::applyStabilizationConfiguration()
{
    StabilizationSettings *stabSettings    = StabilizationSettings::GetInstance(m_uavoManager);
    Q_ASSERT(stabSettings);

    StabilizationSettings defaultSettings;
    stabSettings->setData(defaultSettings.getData());
    addModifiedObject(stabSettings, tr("Writing stabilization settings"));
}
/**
  * Apply the stabilization settings computed
  */
void ConfigAutotuneWidget::saveStabilization()
{
    StabilizationSettings *stabilizationSettings = StabilizationSettings::GetInstance(getObjectManager());
    Q_ASSERT(stabilizationSettings);
    if(!stabilizationSettings)
        return;

    // Check the settings are reasonable, or if not have the
    // user confirm they want to continue.
    SystemIdent *systemIdent = SystemIdent::GetInstance(getObjectManager());
    Q_ASSERT(systemIdent);
    if(!systemIdent)
        return;
    if (approveSettings(systemIdent->getData()) == false)
        return;

    // Make sure to recompute in case the other stab settings changed since
    // the last time
    recomputeStabilization();

    // Apply this data to the board
    stabilizationSettings->setData(stabSettings);
    stabilizationSettings->updated();
}
/**
  * Called whenever the gain ratios or measured values
  * are changed
  */
void ConfigAutotuneWidget::recomputeStabilization()
{
    SystemIdent *systemIdent = SystemIdent::GetInstance(getObjectManager());
    Q_ASSERT(systemIdent);
    if(!systemIdent)
        return;

    StabilizationSettings *stabilizationSettings = StabilizationSettings::GetInstance(getObjectManager());
    Q_ASSERT(stabilizationSettings);
    if(!stabilizationSettings)
        return;

    SystemIdent::DataFields systemIdentData = systemIdent->getData();
    stabSettings = stabilizationSettings->getData();

    // These three parameters define the desired response properties
    // - rate scale in the fraction of the natural speed of the system
    //   to strive for.
    // - damp is the amount of damping in the system. higher values
    //   make oscillations less likely
    // - ghf is the amount of high frequency gain and limits the influence
    //   of noise
    const double ghf = m_autotune->rateNoise->value() / 1000.0;
    const double damp = m_autotune->rateDamp->value() / 100.0;

    double tau = exp(systemIdentData.Tau);
    double beta_roll = systemIdentData.Beta[SystemIdent::BETA_ROLL];
    double beta_pitch = systemIdentData.Beta[SystemIdent::BETA_PITCH];

    double wn = 1/tau;
    double tau_d = 0;
    for (int i = 0; i < 30; i++) {
        double tau_d_roll = (2*damp*tau*wn - 1)/(4*tau*damp*damp*wn*wn - 2*damp*wn - tau*wn*wn + exp(beta_roll)*ghf);
        double tau_d_pitch = (2*damp*tau*wn - 1)/(4*tau*damp*damp*wn*wn - 2*damp*wn - tau*wn*wn + exp(beta_pitch)*ghf);

        // Select the slowest filter property
        tau_d = (tau_d_roll > tau_d_pitch) ? tau_d_roll : tau_d_pitch;
        wn = (tau + tau_d) / (tau*tau_d) / (2 * damp + 2);
    }

    // Set the real pole position. The first pole is quite slow, which
    // prevents the integral being too snappy and driving too much
    // overshoot.
    const double a = ((tau+tau_d) / tau / tau_d - 2 * damp * wn) / 20.0;
    const double b = ((tau+tau_d) / tau / tau_d - 2 * damp * wn - a);

    qDebug() << "ghf: " << ghf;
    qDebug() << "wn: " << wn << "tau_d: " << tau_d;
    qDebug() << "a: " << a << " b: " << b;

    // Calculate the gain for the outer loop by approximating the
    // inner loop as a single order lpf. Set the outer loop to be
    // critically damped;
    const double zeta_o = 1.3;
    const double kp_o = 1 / 4.0 / (zeta_o * zeta_o) / (1/wn);

    // For now just run over roll and pitch
    for (int i = 0; i < 2; i++) {
        double beta = exp(systemIdentData.Beta[i]);

        double ki = a * b * wn * wn * tau * tau_d / beta;
        double kp = tau * tau_d * ((a+b)*wn*wn + 2*a*b*damp*wn) / beta - ki*tau_d;
        double kd = (tau * tau_d * (a*b + wn*wn + (a+b)*2*damp*wn) - 1) / beta - kp * tau_d;

        switch(i) {
        case 0: // Roll
            stabSettings.RollRatePID[StabilizationSettings::ROLLRATEPID_KP] = kp;
            stabSettings.RollRatePID[StabilizationSettings::ROLLRATEPID_KI] = ki;
            stabSettings.RollRatePID[StabilizationSettings::ROLLRATEPID_KD] = kd;
            stabSettings.RollPI[StabilizationSettings::ROLLPI_KP] = kp_o;
            stabSettings.RollPI[StabilizationSettings::ROLLPI_KI] = 0;
            break;
        case 1: // Pitch
            stabSettings.PitchRatePID[StabilizationSettings::PITCHRATEPID_KP] = kp;
            stabSettings.PitchRatePID[StabilizationSettings::PITCHRATEPID_KI] = ki;
            stabSettings.PitchRatePID[StabilizationSettings::PITCHRATEPID_KD] = kd;
            stabSettings.PitchPI[StabilizationSettings::PITCHPI_KP] = kp_o;
            stabSettings.PitchPI[StabilizationSettings::PITCHPI_KI] = 0;
            break;
        }
    }
    stabSettings.DerivativeCutoff = 1 / (2*M_PI*tau_d);

    // Display these computed settings
    m_autotune->rollRateKp->setText(QString::number(stabSettings.RollRatePID[StabilizationSettings::ROLLRATEPID_KP]));
    m_autotune->rollRateKi->setText(QString::number(stabSettings.RollRatePID[StabilizationSettings::ROLLRATEPID_KI]));
    m_autotune->rollRateKd->setText(QString::number(stabSettings.RollRatePID[StabilizationSettings::ROLLRATEPID_KD]));
    m_autotune->pitchRateKp->setText(QString::number(stabSettings.PitchRatePID[StabilizationSettings::PITCHRATEPID_KP]));
    m_autotune->pitchRateKi->setText(QString::number(stabSettings.PitchRatePID[StabilizationSettings::PITCHRATEPID_KI]));
    m_autotune->pitchRateKd->setText(QString::number(stabSettings.PitchRatePID[StabilizationSettings::PITCHRATEPID_KD]));
    m_autotune->lblOuterKp->setText(QString::number(stabSettings.RollPI[StabilizationSettings::ROLLPI_KP]));

    m_autotune->derivativeCutoff->setText(QString::number(stabSettings.DerivativeCutoff));
    m_autotune->rollTau->setText(QString::number(tau,'g',3));
    m_autotune->pitchTau->setText(QString::number(tau,'g',3));
    m_autotune->wn->setText(QString::number(wn / 2 / M_PI, 'f', 1));
    m_autotune->lblDamp->setText(QString::number(damp, 'g', 2));
    m_autotune->lblNoise->setText(QString::number(ghf * 100, 'g', 2) + " %");

}
void ConfigStabilizationWidget::setupStabBanksGUI()
{
    StabilizationSettings *stabSettings = qobject_cast<StabilizationSettings *>(getObject("StabilizationSettings"));

    Q_ASSERT(stabSettings);

    m_stabSettingsBankCount = stabSettings->getField("FlightModeMap")->getOptions().count();

    // Set up fake tab widget stuff for pid banks support
    m_stabTabBars.append(ui->basicPIDBankTabBar);
    m_stabTabBars.append(ui->advancedPIDBankTabBar);

    QAction *defaultStabMenuAction = new QAction(QIcon(":configgadget/images/gear.png"), QString(), this);
    QAction *restoreAllAction     = new QAction(tr("all to saved"), this);
    connect(restoreAllAction, SIGNAL(triggered()), this, SLOT(restoreAllStabBanks()));
    QAction *resetAllAction       = new QAction(tr("all to default"), this);
    connect(resetAllAction, SIGNAL(triggered()), this, SLOT(resetAllStabBanks()));
    QAction *restoreCurrentAction = new QAction(tr("to saved"), this);
    connect(restoreCurrentAction, SIGNAL(triggered()), this, SLOT(restoreCurrentAction()));
    QAction *resetCurrentAction   = new QAction(tr("to default"), this);
    connect(resetCurrentAction, SIGNAL(triggered()), this, SLOT(resetCurrentStabBank()));
    QAction *copyCurrentAction    = new QAction(tr("to others"), this);
    connect(copyCurrentAction, SIGNAL(triggered()), this, SLOT(copyCurrentStabBank()));
    connect(&m_stabSettingsCopyFromSignalMapper, SIGNAL(mapped(int)), this, SLOT(copyFromBankToCurrent(int)));
    connect(&m_stabSettingsCopyToSignalMapper, SIGNAL(mapped(int)), this, SLOT(copyToBankFromCurrent(int)));
    connect(&m_stabSettingsSwapSignalMapper, SIGNAL(mapped(int)), this, SLOT(swapBankAndCurrent(int)));

    foreach(QTabBar * tabBar, m_stabTabBars) {
        for (int i = 0; i < m_stabSettingsBankCount; i++) {
            tabBar->addTab(tr("Settings Bank %1").arg(i + 1));
            tabBar->setTabData(i, QString("StabilizationSettingsBank%1").arg(i + 1));
            QToolButton *tabButton = new QToolButton();
            connect(this, SIGNAL(enableControlsChanged(bool)), tabButton, SLOT(setEnabled(bool)));
            tabButton->setDefaultAction(defaultStabMenuAction);
            tabButton->setAutoRaise(true);
            tabButton->setPopupMode(QToolButton::InstantPopup);
            tabButton->setToolTip(tr("The functions in this menu effect all fields in the settings banks,\n"
                                     "not only the ones visible on screen."));
            QMenu *tabMenu     = new QMenu();
            QMenu *restoreMenu = new QMenu(tr("Restore"));
            QMenu *resetMenu   = new QMenu(tr("Reset"));
            QMenu *copyMenu    = new QMenu(tr("Copy"));
            QMenu *swapMenu    = new QMenu(tr("Swap"));
            QAction *menuAction;
            for (int j = 0; j < m_stabSettingsBankCount; j++) {
                if (j == i) {
                    restoreMenu->addAction(restoreCurrentAction);
                    resetMenu->addAction(resetCurrentAction);
                    copyMenu->addAction(copyCurrentAction);
                } else {
                    menuAction = new QAction(tr("from %1").arg(j + 1), this);
                    connect(menuAction, SIGNAL(triggered()), &m_stabSettingsCopyFromSignalMapper, SLOT(map()));
                    m_stabSettingsCopyFromSignalMapper.setMapping(menuAction, j);
                    copyMenu->addAction(menuAction);

                    menuAction = new QAction(tr("to %1").arg(j + 1), this);
                    connect(menuAction, SIGNAL(triggered()), &m_stabSettingsCopyToSignalMapper, SLOT(map()));
                    m_stabSettingsCopyToSignalMapper.setMapping(menuAction, j);
                    copyMenu->addAction(menuAction);

                    menuAction = new QAction(tr("with %1").arg(j + 1), this);
                    connect(menuAction, SIGNAL(triggered()), &m_stabSettingsSwapSignalMapper, SLOT(map()));
                    m_stabSettingsSwapSignalMapper.setMapping(menuAction, j);
                    swapMenu->addAction(menuAction);
                }
            }
            restoreMenu->addAction(restoreAllAction);
            resetMenu->addAction(resetAllAction);
            tabMenu->addMenu(copyMenu);
            tabMenu->addMenu(swapMenu);
            tabMenu->addMenu(resetMenu);
            tabMenu->addMenu(restoreMenu);
            tabButton->setMenu(tabMenu);
            tabBar->setTabButton(i, QTabBar::RightSide, tabButton);
        }
        tabBar->setExpanding(false);
        connect(tabBar, SIGNAL(currentChanged(int)), this, SLOT(stabBankChanged(int)));
    }

    for (int i = 0; i < m_stabSettingsBankCount; i++) {
        if (i > 0) {
            m_stabilizationObjectsString.append(",");
        }
        m_stabilizationObjectsString.append(m_stabTabBars.at(0)->tabData(i).toString());
    }
}