/*
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃                                                                                                 ┃
┃            Class Name: GammaSmith.cpp                                                           ┃
┃                Author: Michael Krämer                                                           ┃
┃                 Email: Michael@dk8pp.de                                                         ┃
┃                  Date: 16.04.2025 17:00:14                                                      ┃
┃                  Tool: Qt                                                                       ┃
┃                Device: Windows/Linux                                                            ┃
┃               Version: 0.9                                                                      ┃
┃        Program status: still testing                                                            ┃
┃                                                                                                 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃                                                                                                 ┃
┃  Purpose: Construct and display a gamma chart and a smith chart                                 ┃
┃                                                                                                 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃                                                                                                 ┃
┃  © Copyright 2025 Michael Krämer, DK8PP                                                         ┃
┃                                                                                                 ┃
┃  This program is free software: you can redistribute it and/or modify it under the terms of     ┃
┃  the GNU Lesser General Public License as published by the Free Software Foundation, either     ┃
┃  version 3 of the License, or (at your option) any later version.                               ┃
┃                                                                                                 ┃
┃  This program 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 Lesser General Public License for more details.                                    ┃
┃                                                                                                 ┃
┃  You should have received a copy of the GNU Lesser General Public License along with this       ┃
┃  program.   If not, see <http://www.gnu.org/licenses/>.                                         ┃
┃                                                                                                 ┃
┃  ============================================================================================   ┃
┃                                                                                                 ┃
┃  Dieses Programm ist Freie Software: Sie können es unter den Bedingungen der GNU Lesser         ┃
┃  General Public License, wie von der Free Software Foundation, Version 3 der Lizenz oder        ┃
┃  (nach Ihrer Wahl) jeder neueren veröffentlichten Version, weiter verteilen und/oder            ┃
┃  modifizieren.                                                                                  ┃
┃                                                                                                 ┃
┃  Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch OHNE   ┃
┃  JEDE GEWÄHR,; sogar ohne die implizite Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN        ┃
┃  BESTIMMTEN ZWECK. Siehe die GNU Lesser General Public License für weitere Einzelheiten.        ┃
┃                                                                                                 ┃
┃  Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit diesem Programm      ┃
┃  erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.                             ┃
┃                                                                                                 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

ToDo:

RLC Schieberegler und Line-Edits auswerten
Alle Line Edits mit get_double() auswerten
ToolTips updaten
mehrsprachige Version erzeugen
Einheiten konsequent im Line Edit Feld anzeigen

*/

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Includes                                                                                  │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
#include "GammaSmith.h"

#include <cmath>
#include "ui_GammaSmith.h"

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Defines                                                                                   │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
/* Note: Using an untransformed view: one unit on the scene is represented by one pixel on the screen. */
#define SDIAM 250.0     /* diameter of smith chart in number of pixels */
#define SMARGIN 5.0     /* additional margin around chart */
#define ICOUNT 6        /* number of impedance and admittance lines */

#define QSF_MINFREQ 1000        /* minimum frequency in Hz for frequency slider */
#define QSF_DECADES 7           /* number of decades for frequency slider */

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       GammaSmith                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Constructor of GammaSmith class                                                                 │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::GammaSmith)
{
   ui->setupUi(this) ;
   QCoreApplication::setOrganizationName(OrganizationName) ;    /* defines registry tree */
   QCoreApplication::setApplicationName(ApplicationName) ;      /* defines registry tree */
   loadSettings() ;                                             /* read initialization from registry */

   Init_Controls() ;

   init_graphics_view(ui->QGV_Chart) ; /* initialize QGraphicsView */

//         ╭──────────────────────────────────────────────────────────────────────────────── const QObject *sender
//         │                           ╭──────────────────────────────────────────────────── const char *signal
//         │                           │                                 ╭────────────────── const QObject *receiver
//         │                           │                                 │             ╭──── const char *method/slot
//         │                           │                                 │             │
   connect(ui->QS_R,                   &QSlider::valueChanged,           this,         &MainWindow::QS_setR);
   connect(ui->QS_X,                   &QSlider::valueChanged,           this,         &MainWindow::QS_setX);
   connect(ui->QS_G,                   &QSlider::valueChanged,           this,         &MainWindow::QS_setG);
   connect(ui->QS_B,                   &QSlider::valueChanged,           this,         &MainWindow::QS_setB);
   connect(ui->QS_RP,                  &QSlider::valueChanged,           this,         &MainWindow::QS_setRP);
   connect(ui->QS_XP,                  &QSlider::valueChanged,           this,         &MainWindow::QS_setXP);
   connect(ui->QS_RS,                  &QSlider::valueChanged,           this,         &MainWindow::QS_setRS);
   connect(ui->QS_XS,                  &QSlider::valueChanged,           this,         &MainWindow::QS_setXS);
   connect(ui->QS_F,                   &QSlider::valueChanged,           this,         &MainWindow::QS_setF);
   connect(ui->LED_R,                  &QLineEdit::editingFinished,      this,         &MainWindow::LED_setRX) ;
   connect(ui->LED_X,                  &QLineEdit::editingFinished,      this,         &MainWindow::LED_setRX) ;
   connect(ui->LED_G,                  &QLineEdit::editingFinished,      this,         &MainWindow::LED_setGB) ;
   connect(ui->LED_B,                  &QLineEdit::editingFinished,      this,         &MainWindow::LED_setGB) ;
   connect(ui->LED_RP,                 &QLineEdit::editingFinished,      this,         &MainWindow::LED_setRP);
   connect(ui->LED_RS,                 &QLineEdit::editingFinished,      this,         &MainWindow::LED_setRS);
   connect(ui->LED_XP,                 &QLineEdit::editingFinished,      this,         &MainWindow::LED_setXP);
   connect(ui->LED_XS,                 &QLineEdit::editingFinished,      this,         &MainWindow::LED_setXS);
   connect(ui->LED_GAMMAR,             &QLineEdit::editingFinished,      this,         &MainWindow::LED_setGammaR);
   connect(ui->LED_GAMMAI,             &QLineEdit::editingFinished,      this,         &MainWindow::LED_setGammaI);
   connect(ui->LED_GAMMAM,             &QLineEdit::editingFinished,      this,         &MainWindow::LED_setGammaM);
   connect(ui->LED_GAMMAP,             &QLineEdit::editingFinished,      this,         &MainWindow::LED_setGammaP);
   connect(ui->LED_Z0,                 &QLineEdit::editingFinished,      this,         &MainWindow::LED_setZ0);
   connect(ui->LED_FREQUENZ,           &QLineEdit::editingFinished,      this,         &MainWindow::LED_setF);
   connect(ui->SPB_SWR1,               &QDoubleSpinBox::valueChanged,    this,         &MainWindow::read_spb);
   connect(ui->SPB_SWR2,               &QDoubleSpinBox::valueChanged,    this,         &MainWindow::read_spb);
   connect(ui->SPB_SWR3,               &QDoubleSpinBox::valueChanged,    this,         &MainWindow::read_spb);
   connect(ui->PUB_CLOSE,              &QPushButton::clicked,            this,         &MainWindow::close_smith);
   connect(ui->QS_OPACITY,             &QSlider::valueChanged,           this,         &MainWindow::opacity);
   connect(this->scene,                &GraphicsScene::NewGamma,         this,         &MainWindow::NewGamma);

   open_smith() ;

   updateSlider(UPD_UI_ALL) ;
   updateMarker(&gamma) ;
   updateLE(UPD_UI_ALL) ;

   LE_default_style = ui->LED_FREQUENZ->styleSheet() ; /* get default style */

}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       ~MainWindow                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Destructor of MainWindow class                                                                  │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
MainWindow::~MainWindow()
{
    saveSettings() ; /* write ini-file */
    delete ui ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Init_Controls                                                                             │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Initialize controls of this window                                                              │
│ Smith / Gamma chart is not yet open                                                             │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::Init_Controls()
{
   ui->SPB_SWR1->setValue(swr.at(0)) ;
   ui->SPB_SWR2->setValue(swr.at(1)) ;
   ui->SPB_SWR3->setValue(swr.at(2)) ;
   updateLE(UPD_UI_ALL) ;
   updateSlider(UPD_UI_ALL) ;
   F_setQS() ; /* init frequency slider */
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       open_smith()                                                                              │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Open Smith chart window                                                                         │
│                                                                                                 │
│ This function is called from the parent to initialize the smith chart.                          │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::open_smith()
{
qreal startangle ;
qreal inc_angle ;
complex_dbl my_z = {0.0, 0.0};     /* local impedance */
complex_dbl my_gamma = {0.0, 0.0}; /* local gamma */
int i ;
   if (!isOpen())
   {
      this->show() ;
      DrawSmithchart(real(z0)) ;
      setOpen() ;
      addSwrItems(swr_circle_count) ;
      updateSWR(swr.begin(), swr_circle_count) ;
      DrawGamma() ;
      addMarker(true) ; /* add moveable marker */
      ui->QGV_Chart->fitInView(sceneRect(),Qt::KeepAspectRatio) ;
/*
generate a list of resistances which are equally spaced on the circumference of a
unity circle. That list is used to construct all other circles in the diagram.
*/
      inc_angle = 180.0 / RES_COUNT ;
      startangle = inc_angle / 2.0 ;

      for (i=0; i<RES_COUNT; i++)
      { /* just half a circle from 180° down to 0° */
         my_gamma.real(cos((180.0-startangle-i*inc_angle)*M_PI/180.0)) ;
         my_gamma.imag(sin((180.0-startangle-i*inc_angle)*M_PI/180.0)) ;
         my_z = z0 * (complex_1+my_gamma)/(complex_1-my_gamma) ;
         reslist.at(i) = my_z.imag() ;
      }
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       close_smith()                                                                             │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Close Smith chart window                                                                        │
│                                                                                                 │
│ This slot is called when the close button was pressed.                                          │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::close_smith()
{
   this->close() ; /* will send a closeEvent */
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       on_PUB_Info_clicked                                                                       │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::on_PUB_Info_clicked()
{
    InfoDialog InfoDialog(nullptr, &ProgramInfo) ;
//    SecDialog.setModal(true) ; (no access to first window when 2nd is open, all data is local on stack)
    InfoDialog.exec() ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       read_spb                                                                                  │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ A spinbox has been changed. Update the local variables according to spinbox settings.           │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::read_spb()
{
   swr.at(0) = ui->SPB_SWR1->value() ;
   swr.at(1) = ui->SPB_SWR2->value() ;
   swr.at(2) = ui->SPB_SWR3->value() ;
   updateSWR(swr.begin(), swr_circle_count) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       opacity                                                                                   │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Opacity has changed                                                                             │
│                                                                                                 │
│ This slot is called when the opacity was changed.                                               │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::opacity(int value)
{
   set_opacity(1.0 - (((qreal) value) / 100.0)) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       QS_setR()                                                                                 │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Slider value for resistance has changed                                                         │
│                                                                                                 │
│ Update gamma and all dependent controls including marker.                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::QS_setR(int value)
{
std::complex<qreal> z = {NAN, NAN} ;
int minval = 0 ;
int maxval = 0 ;
int index = 0 ;

   if (!updating_ZL)
   { /* ignore during update by code */
      minval = ui->QS_R->minimum() ;
      maxval = ui->QS_R->maximum() ;
      index = (int) (((qreal) ((RES_COUNT-1) * (value - minval))) / ((qreal) (maxval - minval))) ;
      z = z0 * (1.0 + gamma) / (1.0 - gamma) ;
      z.real(reslist.at(index)) ;
      gamma = (z-z0)/(z+z0) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
      updateSlider(UPD_UI_ALL & ~UPD_QS_R) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       QS_setRP()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Slider value for parallel resistance has changed                                                │
│                                                                                                 │
│ Update gamma and all dependent controls including marker.                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::QS_setRP(int value)
{
std::complex<qreal> z = {NAN, NAN} ;
std::complex<qreal> z1 = {NAN, NAN} ;
std::complex<qreal> z2 = {NAN, NAN} ;
int minval = 0 ;
int maxval = 0 ;
int index = 0 ;

   if (!updating_ZL)
   { /* ignore during update by code */
      minval = ui->QS_RP->minimum() ;
      maxval = ui->QS_RP->maximum() ;
      index = (int) (((qreal) ((RES_COUNT-1) * (value - minval))) / ((qreal) (maxval - minval))) ;

      r_par = reslist.at(index) ;
      z1.real(r_par) ;
      z1.imag(0.0) ;
      z2.real(0.0) ;
      z2.imag(x_par) ;

      z = complex_1 / ((complex_1 / z1) + (complex_1 / z2)) ;

      gammaFromZ(&z, &gamma, &z0) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
      updateSlider(UPD_UI_ALL & ~UPD_QS_RP) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       QS_setRS()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Slider value for serial resistance has changed                                                  │
│                                                                                                 │
│ Update gamma and all dependent controls including marker.                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::QS_setRS(int value)
{
std::complex<qreal> z = {NAN, NAN} ;
int minval = 0 ;
int maxval = 0 ;
int index = 0 ;

   if (!updating_ZL)
   { /* ignore during update by code */
      minval = ui->QS_RS->minimum() ;
      maxval = ui->QS_RS->maximum() ;
      index = (int) (((qreal) ((RES_COUNT-1) * (value - minval))) / ((qreal) (maxval - minval))) ;

      r_ser = reslist.at(index) ;
      z.real(r_ser) ;
      z.imag(x_ser) ;
      gammaFromZ(&z, &gamma, &z0) ;

      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
      updateSlider(UPD_UI_ALL & ~UPD_QS_RS) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       QS_setXS()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Slider value for serial impedance has changed                                                   │
│                                                                                                 │
│ Update gamma and all dependent controls including marker.                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::QS_setXS(int value)
{
std::complex<qreal> z = {NAN, NAN} ;
int minval = 0 ;
int maxval = 0 ;
qreal log_span ;
qreal ticks_per_decade ;

   if (!updating_ZL)
   { /* ignore during update by code */
      minval = ui->QS_XS->minimum() ;
      maxval = ui->QS_XS->maximum() ;
      log_span = std::log10(MAX_X/MIN_X) ;

      if (value == 0)
      { /* no impedance */
         x_ser = 0 ;
      }

      if (value >= 1)
      {
         ticks_per_decade = ((qreal) (maxval - 1)) / log_span ;
         x_ser = MIN_X * std::pow(10.0, (qreal) (value - 1) / ticks_per_decade) ;
      }

      if (value < 0)
      { /* capacitor */
         ticks_per_decade = ((qreal) (abs(minval) - 1)) / log_span ;
         x_ser = -MIN_X * std::pow(10.0, (qreal) (abs(value) - 1) / ticks_per_decade) ;
      }

      z.real(r_ser) ;
      z.imag(x_ser) ;
      gammaFromZ(&z, &gamma, &z0) ;

      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
      updateSlider(UPD_UI_ALL & ~UPD_QS_XS) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       QS_setXP()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Slider value for parallel impedance has changed                                                 │
│                                                                                                 │
│ Update gamma and all dependent controls including marker.                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::QS_setXP(int value)
{
std::complex<qreal> z  = {NAN, NAN} ;
std::complex<qreal> z1 = {NAN, NAN} ;
std::complex<qreal> z2 = {NAN, NAN} ;
int minval = 0 ;
int maxval = 0 ;
qreal log_span ;
qreal ticks_per_decade ;

   if (!updating_ZL)
   { /* ignore during update by code */
      minval = ui->QS_XP->minimum() ;
      maxval = ui->QS_XP->maximum() ;
      log_span = std::log10(MAX_X/MIN_X) ;

      if (value == 0)
      { /* no impedance */
         x_par = 0 ;
      }

      if (value >= 1)
      {
         ticks_per_decade = ((qreal) (maxval - 1)) / log_span ;
         x_par = MIN_X * std::pow(10.0, (qreal) (value - 1) / ticks_per_decade) ;
      }

      if (value < 0)
      { /* capacitor */
         ticks_per_decade = ((qreal) (abs(minval) - 1)) / log_span ;
         x_par = -MIN_X * std::pow(10.0, (qreal) (abs(value) - 1) / ticks_per_decade) ;
      }

      z1.real(r_par) ;
      z1.imag(0.0) ;
      z2.real(0.0) ;
      z2.imag(x_par) ;
      z = complex_1 / ((complex_1 / z1) + (complex_1 / z2)) ;

      gammaFromZ(&z, &gamma, &z0) ;

      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
      updateSlider(UPD_UI_ALL & ~UPD_QS_XP) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       QS_setX()                                                                                 │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Slider value for reactance has changed                                                          │
│                                                                                                 │
│ Update gamma and all dependent controls including marker.                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::QS_setX(int value)
{
qreal x = NAN ;
std::complex<qreal> z = {NAN, NAN} ;
int minval = 0 ;
int maxval = 0 ;
int midval = 0 ;
int index = 0 ;

   if (!updating_ZL)
   { /* ignore during update by code */
      minval = ui->QS_X->minimum() ;
      maxval = ui->QS_X->maximum() ;
      midval = minval + (maxval - minval) / 2 ;
      index = (int) (((qreal) ((RES_COUNT-1) * (value - midval))) / ((qreal) (maxval - midval))) ;
      if (index >= 0) x =  reslist.at( index) ;
      else            x = -reslist.at(-index) ;
      z = z0 * (1.0 + gamma) / (1.0 - gamma) ;
      z.imag(x) ;
      gamma = (z-z0)/(z+z0) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
      updateSlider(UPD_UI_ALL & ~UPD_QS_X) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       QS_setG()                                                                                 │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Slider value for conductance has changed                                                        │
│                                                                                                 │
│ Update gamma and all dependent controls including marker.                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::QS_setG(int value)
{
qreal g = NAN;
std::complex<qreal> y = {NAN, NAN} ;
std::complex<qreal> z = {NAN, NAN} ;
int minval = 0 ;
int maxval = 0 ;
int index = 0 ;
QLocale default_locale ;

   if (!updating_ZL)
   { /* ignore during update by code */
      minval = ui->QS_G->minimum() ;
      maxval = ui->QS_G->maximum() ;
      index = (int) (((qreal) ((RES_COUNT-1) * (value - minval))) / ((qreal) (maxval - minval))) ;
      g = 1.0 / reslist.at(RES_COUNT-1-index) ;
      z = z0 * (1.0 + gamma) / (1.0 - gamma) ;
      y = complex_1 / z ;
      y.real(g) ;
      z = complex_1 / y ;
      gamma = (z-z0)/(z+z0) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
      updateSlider(UPD_UI_ALL & ~UPD_QS_G) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       QS_setB()                                                                                 │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Slider value for susceptance has changed                                                        │
│                                                                                                 │
│ Update gamma and all dependent controls including marker.                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::QS_setB(int value)
{
qreal b = NAN ;
std::complex<qreal> y = {NAN, NAN} ;
std::complex<qreal> z = {NAN, NAN} ;
int minval = 0 ;
int maxval = 0 ;
int midval = 0 ;
int index = 0 ;
QLocale default_locale ;

   if (!updating_ZL)
   { /* ignore during update by code */
      minval = ui->QS_B->minimum() ;
      maxval = ui->QS_B->maximum() ;
      midval = minval + (maxval - minval) / 2 ;
      index = (int) (((qreal) ((RES_COUNT-1) * (value - midval))) / ((qreal) (maxval - midval))) ;
      if (index > 0) b =  1.0 / reslist.at(RES_COUNT-index) ;
      else           b = -1.0 / reslist.at(RES_COUNT-1+index) ;
      z = z0 * (1.0 + gamma) / (1.0 - gamma) ;
      y = complex_1 / z ;
      y.imag(b) ;
      z = complex_1 / y ;
      gamma = (z-z0)/(z+z0) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
      updateSlider(UPD_UI_ALL & ~UPD_QS_B) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       QS_setF()                                                                                 │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Slider position for frequency has changed, update frequency                                     │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::QS_setF(int value)
{
qreal temp = NAN ;
int minval = 0 ;
int maxval = 0 ;
QLocale default_locale ;
QString message ;

   if (!updating_F)
   { /* ignore during update by code */
      minval = ui->QS_F->minimum() ;
      maxval = ui->QS_F->maximum() ;
      if (value > maxval) value = maxval ;
      if (value < minval) value = minval ;
      temp = (((qreal) value) / ((qreal) (maxval - minval))) * ((qreal) QSF_DECADES) ;
      freq = std::pow(10.0, temp) * ((qreal) QSF_MINFREQ) ;
      ui->LED_FREQUENZ->setText(FrqToStr(freq)) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       F_setQS()                                                                                 │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Frequency has changed, update slider position                                                   │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::F_setQS()
{
int minval = 0 ;
int maxval = 0 ;
int value = 0 ;
QLocale default_locale ;
QString message ;

   updating_F = true ;

   minval = ui->QS_F->minimum() ;
   maxval = ui->QS_F->maximum() ;
   value = (int) (((qreal) (maxval - minval)) / ((qreal) QSF_DECADES)) * std::log10(fabs(freq/((qreal) QSF_MINFREQ))) ;
   if (value > maxval) value = maxval ;
   if (value < minval) value = minval ;
   ui->QS_F->setValue(value) ;

   updating_F = false ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setF()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User updated frequency value in the edit control                                                │
│ Update slider positions and marker position accordingly                                         │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setF()
{
CONVERSION conv ;

   conv.str = ui->LED_FREQUENZ->text() ;
   conv.deflt = "1.0 MHz" ;
   conv.required = false ;
   conv.positive = true ;

   get_double(&conv) ;

   if ((!conv.error) && (!conv.empty))
   {
      freq = conv.value ;
      ui->LED_FREQUENZ->setText(FrqToStr(freq)) ;
      ui->LED_FREQUENZ->setStyleSheet(LE_default_style) ;
      updateLE(UPD_UI_ALL) ;
      F_setQS() ;
   }
   else
   {
      ui->LED_FREQUENZ->setStyleSheet(LE_error_style) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setZ0()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User updated Z0 value in the edit control                                                       │
│ Update Smith chart                                                                              │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setZ0()
{
bool res_ok = false;
qreal r = NAN;
QLocale default_locale ;

   r = default_locale.toDouble(ui->LED_Z0->text(), &res_ok) ; /* get resistance */
   if (!res_ok || (r < 0.0)) r = 50.0 ;
   if (real(z0) != r)
   {
      z0.real(r) ;
      z0.imag(0.0) ;
      close_smith() ;
      open_smith() ;
   }
   updateMarker(&gamma) ;
   updateLE(UPD_UI_ALL) ;
   updateSlider(UPD_UI_ALL) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setRX()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User updated R or X value in the edit control                                                   │
│ Update gamma and UI accordingly                                                                 │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setRX()
{
std::complex<qreal> z = {NAN, NAN} ;
CONVERSION convr ;
CONVERSION convx ;

   convr.str = ui->LED_R->text() ;
   convr.deflt = "50 " CHAR_CAP_OMEGA ;
   convr.required = false ;
   convr.positive = true ;

   convx.str = ui->LED_X->text() ;
   convx.deflt = "0 j" CHAR_CAP_OMEGA ;
   convx.required = false ;
   convx.positive = false ;

   get_double(&convr) ;
   get_double(&convx) ;

   if ((convr.error) || (convr.empty)) ui->LED_R->setStyleSheet(LE_error_style) ;
   if ((convx.error) || (convx.empty)) ui->LED_X->setStyleSheet(LE_error_style) ;

   if ((!convr.error) && (!convr.empty) && (!convx.error) && (!convx.empty))
   {
      ui->LED_R->setStyleSheet(LE_default_style) ;
      ui->LED_X->setStyleSheet(LE_default_style) ;
      z.real(convr.value) ;
      z.imag(convx.value) ;
      gammaFromZ(&z, &gamma, &z0) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setRP()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User updated Rparallel value in the edit control                                                │
│ Update gamma and UI accordingly                                                                 │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setRP()
{
std::complex<qreal> z = {NAN, NAN} ;
std::complex<qreal> z1 = {NAN, NAN} ;
std::complex<qreal> z2 = {NAN, NAN} ;
CONVERSION conv ;

   conv.str = ui->LED_RP->text() ;
   conv.deflt = "50 " CHAR_CAP_OMEGA ;
   conv.required = false ;
   conv.positive = true ;

   get_double(&conv) ;

   if ((conv.error) || (conv.empty))
      {ui->LED_RP->setStyleSheet(LE_error_style) ;}
   else
   {
      ui->LED_RP->setStyleSheet(LE_default_style) ;
      r_par = conv.value ;
      z1.real(r_par) ;
      z1.imag(0.0) ;
      z2.real(0.0) ;
      z2.imag(x_par) ;
      z = complex_1 / ((complex_1 / z1) + (complex_1 / z2)) ;
      gammaFromZ(&z, &gamma, &z0) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setRS()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User updated Rserial value in the edit control                                                  │
│ Update gamma and UI accordingly                                                                 │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setRS()
{
std::complex<qreal> z = {NAN, NAN} ;
CONVERSION conv ;

   conv.str = ui->LED_RS->text() ;
   conv.deflt = "50 " CHAR_CAP_OMEGA ;
   conv.required = false ;
   conv.positive = true ;

   get_double(&conv) ;

   if ((conv.error) || (conv.empty))
      {ui->LED_RS->setStyleSheet(LE_error_style) ;}
   else
   {
      ui->LED_RS->setStyleSheet(LE_default_style) ;
      r_ser = conv.value ;
      z.real(r_ser) ;
      z.imag(x_ser) ;
      gammaFromZ(&z, &gamma, &z0) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setXS()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User updated Xserial value in the edit control                                                  │
│ Update gamma and UI accordingly                                                                 │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setXS()
{
std::complex<qreal> z  = {NAN, NAN} ;
CONVERSION conv ;
qreal omega = NAN ;

   omega = 2.0 * M_PI * freq ;

   conv.str = ui->LED_XS->text() ;
   conv.deflt = "0 j" CHAR_CAP_OMEGA ;
   conv.required = false ;
   conv.positive = false ;

   get_double(&conv) ;

   if ((conv.error) || (conv.empty))
      {ui->LED_XS->setStyleSheet(LE_error_style) ;}
   else
   {
      ui->LED_XS->setStyleSheet(LE_default_style) ;

      if (conv.str.contains('F'))
      { /* x is capacitive */
         x_ser = -1.0 / (omega * conv.value) ;
      }
      else
      { /* x is inductive */
         x_ser = omega * conv.value ;
      }

      z.real(r_ser) ;
      z.imag(x_ser) ;
      gammaFromZ(&z, &gamma, &z0) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setXP()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User updated Xparallel value in the edit control                                                │
│ Update gamma and UI accordingly                                                                 │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setXP()
{
std::complex<qreal> z = {NAN, NAN} ;
std::complex<qreal> z1 = {NAN, NAN} ;
std::complex<qreal> z2 = {NAN, NAN} ;
CONVERSION conv ;
qreal omega = NAN ;

   omega = 2.0 * M_PI * freq ;

   conv.str = ui->LED_XP->text() ;
   conv.deflt = "0 j" CHAR_CAP_OMEGA ;
   conv.required = false ;
   conv.positive = false ;

   get_double(&conv) ;

   if ((conv.error) || (conv.empty))
      {ui->LED_XP->setStyleSheet(LE_error_style) ;}
   else
   {
      ui->LED_XP->setStyleSheet(LE_default_style) ;

      if (conv.str.contains('F'))
      { /* x is capacitive */
         x_par = -1.0 / (omega * conv.value) ;
      }
      else
      { /* x is inductive */
         x_par = omega * conv.value ;
      }

      z1.real(r_par) ;
      z1.imag(0.0) ;
      z2.real(0.0) ;
      z2.imag(x_par) ;

      z = complex_1 / ((complex_1 / z1) + (complex_1 / z2)) ;
      gammaFromZ(&z, &gamma, &z0) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setGB()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User updated G or B value in the edit control                                                   │
│ Update slider positions and marker position accordingly                                         │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setGB()
{
std::complex<qreal> y = {NAN, NAN} ;
std::complex<qreal> z = {NAN, NAN} ;
CONVERSION convg ;
CONVERSION convb ;

   convg.str = ui->LED_G->text() ;
   convg.deflt = "0 S" ;
   convg.required = false ;
   convg.positive = true ;

   convb.str = ui->LED_B->text() ;
   convb.deflt = "0 jS" ;
   convb.required = false ;
   convb.positive = false ;

   get_double(&convg) ;
   get_double(&convb) ;

   if ((convg.error) || (convg.empty)) ui->LED_G->setStyleSheet(LE_error_style) ;
   if ((convb.error) || (convb.empty)) ui->LED_B->setStyleSheet(LE_error_style) ;

   if ((!convg.error) && (!convg.empty) && (!convb.error) && (!convb.empty))
   {
      ui->LED_G->setStyleSheet(LE_default_style) ;
      ui->LED_B->setStyleSheet(LE_default_style) ;
      y.real(convg.value) ;
      y.imag(convb.value) ;
      z = 1.0 / y ;
      gamma = (z-z0)/(z+z0) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setGammaR                                                                             │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ New gamma real part value entered                                                               │
│                                                                                                 │
│ It is assumed that the imag part is valid                                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setGammaR()
{
bool ok = false ;
qreal tmp ;
QLocale default_locale ;
   tmp = default_locale.toDouble(ui->LED_GAMMAR->text(), &ok) ;
   if (ok)
   {
      gamma.real(fmin(tmp, 1.0)) ;
      gamma.real(fmax(gamma.real(),-1.0)) ;
      ui->LED_GAMMAR->setText(QString("%L1").arg(gamma.real(), 0, 'f', 3)) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setGammaI                                                                             │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ New gamma imag part value entered                                                               │
│                                                                                                 │
│ It is assumed that the real part is valid                                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setGammaI()
{
bool ok = false ;
qreal tmp ;
QLocale default_locale ;
   tmp = default_locale.toDouble(ui->LED_GAMMAI->text(), &ok) ;
   if (ok)
   {
      gamma.imag(fmin(tmp, 1.0)) ;
      gamma.imag(fmax(gamma.imag(),-1.0)) ;
      ui->LED_GAMMAI->setText(QString("%L1").arg(gamma.imag(), 0, 'f', 3)) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setGammaM                                                                             │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ New magnitude entered                                                                           │
│                                                                                                 │
│ It is assumed that the phase is valid                                                           │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setGammaM()
{
bool ok = false ;
qreal g_real = NAN ;
qreal g_imag = NAN ;
qreal g_mag = NAN ;
qreal g_phase = NAN ;
QLocale default_locale ;

   g_real = gamma.real() ;
   g_imag = gamma.imag() ;
   g_mag = sqrt((g_real * g_real) + (g_imag * g_imag)) ;
   g_phase = atan2 (g_imag, g_real) * 180.0 / M_PI ;

   g_mag = default_locale.toDouble(ui->LED_GAMMAM->text(), &ok) ; /* get magnitude */
   if (ok)
   {
      g_mag = fmin(g_mag, 1.0) ;
      g_mag = fmax(g_mag, 0.0) ;
      gamma.real(g_mag * cos(M_PI * g_phase/180.0)) ;
      gamma.imag(g_mag * sin(M_PI * g_phase/180.0)) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       LED_setGammaP                                                                             │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ New phase entered                                                                               │
│                                                                                                 │
│ It is assumed that the magnitude is valid                                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::LED_setGammaP()
{
bool ok = false ;
qreal g_real = NAN ;
qreal g_imag = NAN ;
qreal g_mag = NAN ;
qreal g_phase = NAN ;
QLocale default_locale ;

   g_real = gamma.real() ;
   g_imag = gamma.imag() ;
   g_mag = sqrt((g_real * g_real) + (g_imag * g_imag)) ;
   g_phase = atan2 (g_imag, g_real) * 180.0 / M_PI ;

   g_phase = default_locale.toDouble(ui->LED_GAMMAP->text(), &ok) ; /* get phase */
   if (ok)
   {
      gamma.real(g_mag * cos(M_PI * g_phase/180.0)) ;
      gamma.imag(g_mag * sin(M_PI * g_phase/180.0)) ;
      updateSlider(UPD_UI_ALL) ;
      updateMarker(&gamma) ;
      updateLE(UPD_UI_ALL) ;
   }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       updateSlider()                                                                            │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Update slider positions according to gamma                                                      │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::updateSlider(uint sel)
{
std::complex<qreal> y(0.0, 0.0);
std::complex<qreal> z(0.0, 0.0);
qreal minval = NAN;
qreal maxval = NAN;
int newpos = 0;
bool found = false ;
int i ;
qreal log_span ;
qreal ticks_per_decade ;

   updating_ZL = true;

   z = z0 * (complex_1 + gamma) / (complex_1 - gamma) ;
   y = complex_1 / z ;

   if ((sel & UPD_QS_R) != 0)
   { /* update QS_R */
      found = false ;
      for (i=0; (i < RES_COUNT) && (!found); i++)
      { /* search index of z.real() in reslist */
         found = reslist.at(i) >= z.real() ;
      }
      minval = (qreal) ui->QS_R->minimum() ;
      maxval = (qreal) ui->QS_R->maximum() ;
      newpos = (int) (minval + ((qreal) i / (qreal) RES_COUNT) * (maxval - minval)) ;
      ui->QS_R->setSliderPosition(newpos);
   }

   if ((sel & UPD_QS_X) != 0)
   { /* update QS_X */
      found = false ;
      for (i=0; (i < RES_COUNT-1) && (!found); i++)
      { /* search index of abs(z.imag()) in reslist */
         found = reslist.at(i) >= abs(z.imag()) ;
      }
      minval = (qreal) ui->QS_X->minimum() ;
      maxval = (qreal) ui->QS_X->maximum() ;

      if (z.imag() >= 0.0) i = RES_COUNT + i ; /* upper half */
      else                 i = RES_COUNT - i ; /* lower half */

      newpos = (int) (minval + ((qreal) i / (qreal) (2.0 * RES_COUNT)) * (maxval - minval)) ;
      ui->QS_X->setSliderPosition(newpos);
   }


   if ((sel & UPD_QS_G) != 0)
   { /* update QS_G */
      found = false ;
      for (i=0; (i < RES_COUNT) && (!found); i++)
      { /* search index of z.real() in reslist */
         found = reslist.at(i) >= 1.0 / y.real() ;
      }
      minval = (qreal) ui->QS_G->minimum() ;
      maxval = (qreal) ui->QS_G->maximum() ;
      newpos = (int) (maxval - ((qreal) i / (qreal) RES_COUNT) * (maxval - minval)) ;
      ui->QS_G->setSliderPosition(newpos);
   }

   if ((sel & UPD_QS_B) != 0)
   { /* update QS_B */
      found = false ;
      for (i=0; (i < RES_COUNT-1) && (!found); i++)
      { /* search index of abs(z.imag()) in reslist */
         found = reslist.at(i) >= 1.0 / abs(y.imag()) ;
      }
      minval = (qreal) ui->QS_B->minimum() ;
      maxval = (qreal) ui->QS_B->maximum() ;

      if (y.imag() > 0.0) i = 2*RES_COUNT - i - 1 ; /* upper half of slider */

      newpos = (int) (minval + ((qreal) i / (qreal) (2.0 * RES_COUNT)) * (maxval - minval)) ;
      ui->QS_B->setSliderPosition(newpos);
   }

   if ((sel & UPD_QS_RP) != 0)
   { /* update QS_RP */
      found = false ;
      for (i=0; (i < RES_COUNT) && (!found); i++)
      { /* search index of z.real() in reslist */
         found = reslist.at(i) >= r_par ;
      }
      minval = (qreal) ui->QS_RP->minimum() ;
      maxval = (qreal) ui->QS_RP->maximum() ;
      newpos = (int) (minval + ((qreal) i / (qreal) RES_COUNT) * (maxval - minval)) ;
      ui->QS_RP->setSliderPosition(newpos);
   }

   if ((sel & UPD_QS_RS) != 0)
   { /* update QS_RS */
      found = false ;
      for (i=0; (i < RES_COUNT) && (!found); i++)
      { /* search index of z.real() in reslist */
         found = reslist.at(i) >= r_ser ;
      }
      minval = (qreal) ui->QS_RS->minimum() ;
      maxval = (qreal) ui->QS_RS->maximum() ;
      newpos = (int) (minval + ((qreal) i / (qreal) RES_COUNT) * (maxval - minval)) ;
      ui->QS_RS->setSliderPosition(newpos);
   }

   if ((sel & UPD_QS_XS) != 0)
   { /* update QS_XS */
      minval = (qreal) ui->QS_XS->minimum() ;
      maxval = (qreal) ui->QS_XS->maximum() ;
      log_span = std::log10(MAX_X/MIN_X) ;

      if (x_ser == 0) newpos = 0 ;

      if (x_ser > 0)
      { /* inductive */
         ticks_per_decade = ((qreal) (maxval - 1)) / log_span ;
         newpos = (int) (std::log10(x_ser / MIN_X) * ticks_per_decade) + 1 ;
         if (newpos > maxval) newpos = maxval ;
         if (newpos < 1) newpos = 1 ;
      }
      else
      { /* capacitive */
         ticks_per_decade = ((qreal) (abs(minval) - 1)) / log_span ;
         newpos = -((int) (std::log10(-x_ser / MIN_X) * ticks_per_decade) + 1) ;
         if (newpos < minval) newpos = minval ;
         if (newpos > -1) newpos = -1 ;
      }
      ui->QS_XS->setSliderPosition(newpos);
   }

   if ((sel & UPD_QS_XP) != 0)
   { /* update QS_XP */
      minval = (qreal) ui->QS_XP->minimum() ;
      maxval = (qreal) ui->QS_XP->maximum() ;
      log_span = std::log10(MAX_X/MIN_X) ;

      if (x_par == 0) newpos = 0 ;

      if (x_par > 0)
      { /* inductive */
         ticks_per_decade = ((qreal) (maxval - 1)) / log_span ;
         newpos = (int) (std::log10(x_par / MIN_X) * ticks_per_decade) + 1 ;
         if (newpos > maxval) newpos = maxval ;
         if (newpos < 1) newpos = 1 ;
      }
      else
      { /* capacitive */
         ticks_per_decade = ((qreal) (abs(minval) - 1)) / log_span ;
         newpos = -((int) (std::log10(-x_par / MIN_X) * ticks_per_decade) + 1) ;
         if (newpos < minval) newpos = minval ;
         if (newpos > -1) newpos = -1 ;
      }
      ui->QS_XP->setSliderPosition(newpos);
   }

   updating_ZL = false;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       updateLE()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Update selected line edit controls and spin boxes according to gamma                            │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::updateLE(uint le)
{
complex_dbl yl = {NAN, NAN} ;
complex_dbl y1 = {NAN, NAN} ;
complex_dbl y2 = {NAN, NAN} ;
complex_dbl zl = {NAN, NAN} ;
qreal g_real = NAN ;
qreal g_imag = NAN ;
qreal g_mag = NAN ;
qreal g_phase = NAN ;
qreal omega = NAN ;
QString imp ;

  /* calculate load impedance ZL = Z0 * (1+Γ)/(1-Γ) */
   zl = z0 * (complex_1+gamma)/(complex_1-gamma) ;
   yl = complex_1 / zl ;
   omega = 2.0 * M_PI * freq ;

   if ((le & UPD_LED_R) != 0) ui->LED_R->setText(ResToStr(zl.real(),2)) ;
   if ((le & UPD_LED_X) != 0) ui->LED_X->setText(ResToStr(zl.imag(),2,true)) ;
   if ((le & UPD_LED_G) != 0) ui->LED_G->setText(QString("%L1 mS").arg(1000.0 * yl.real(), 0, 'f', 1)) ; /* mS */
   if ((le & UPD_LED_B) != 0) ui->LED_B->setText(QString("%L1 jmS").arg(1000.0 * yl.imag(), 0, 'f', 1)) ; /* mS */

   g_real = gamma.real() ;
   g_imag = gamma.imag() ;
   g_mag = sqrt((g_real * g_real) + (g_imag * g_imag)) ;
   g_phase = atan2 (g_imag, g_real) * 180.0 / M_PI ;
   if ((le & UPD_LED_GAMMAR) != 0) ui->LED_GAMMAR->setText(QString("%L1").arg(g_real,  0, 'f', 3)) ;
   if ((le & UPD_LED_GAMMAI) != 0) ui->LED_GAMMAI->setText(QString("%L1").arg(g_imag,  0, 'f', 3)) ;
   if ((le & UPD_LED_GAMMAM) != 0) ui->LED_GAMMAM->setText(QString("%L1").arg(g_mag,   0, 'f', 3)) ;
   if ((le & UPD_LED_GAMMAP) != 0) ui->LED_GAMMAP->setText(QString("%L1").arg(g_phase, 0, 'f', 1)) ;

   if ((le & UPD_LED_Z0) != 0)     ui->LED_Z0->setText(QString("%L1").arg(real(z0), 0, 'f', 1)) ;
   if ((le & UPD_LED_FREQ) != 0)   ui->LED_FREQUENZ->setText(FrqToStr(freq)) ;

   r_ser = real(zl) ;
   x_ser = imag(zl) ;

   y1 = {real(yl), 0.0} ;
   y2 = {0.0, imag(yl)} ;

   r_par = real(complex_1 / (yl - y2)) ;
   x_par = imag(complex_1 / (yl - y1)) ;

// simplified but less intuitive version:
//   r_par = 1.0 / real(yl) ;
//   x_par = -1.0 / imag(yl) ; /* remeber: 1/j = -j */

   if (x_ser != 0.0)
   {
      /* serial connection: ZL = RL+jXL */
      if ((le & UPD_LED_RS) != 0) ui->LED_RS->setText(ResToStr(r_ser,2)) ;
      if (x_ser < 0.0) {if ((le & UPD_LED_XS) != 0) ui->LED_XS->setText(CapToStr(-1.0 / (omega * x_ser))) ;} /* load is capacitive */
      else             {if ((le & UPD_LED_XS) != 0) ui->LED_XS->setText(IndToStr(x_ser / omega)) ;} /* load is inductive */

      /* parallel connection: 1/ZL = 1/RL+1/jXL */
      if ((le & UPD_LED_RP) != 0) ui->LED_RP->setText(ResToStr(r_par,2)) ;
      if (x_par < 0.0) {if ((le & UPD_LED_XP) != 0) ui->LED_XP->setText(CapToStr(1.0 / (omega * x_par))) ;} /* load is capacitive */
      else             {if ((le & UPD_LED_XP) != 0) ui->LED_XP->setText(IndToStr(x_par / omega)) ;} /* load is inductive */
   }
   else
   { /* no reactive load */
      if ((le & UPD_LED_RS) != 0) ui->LED_RS->setText(ResToStr(real(zl),2)) ;
      if ((le & UPD_LED_RP) != 0) ui->LED_RP->setText(ResToStr(real(zl),2)) ;
      if ((le & UPD_LED_XP) != 0) ui->LED_XP->clear() ;
      if ((le & UPD_LED_XS) != 0) ui->LED_XS->clear() ;
   }

}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       get_double                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Parse and decode an input string with a numeric value and an optional suffix (k,M,G...).        │
│ Decimal points are supported as '.' or ','.                                                     │
│                                                                                                 │
│ return true on success, false on decoding error.                                                │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
CONVERSION *MainWindow::get_double(CONVERSION *conv)
{
bool ok = false ;
QString match1 ;
QString match2 ;
qreal mult  = NAN;
//QRegularExpression re(QStringLiteral(u"([-+]?\\d*[.,]?\\d*)\\s*([TGMkmuµnp%]?\\D*)")) ;
QRegularExpression const re(QStringLiteral(u"([-+]?[\\d.]*,?\\d*e?[+-]?\\d+)\\s*[ij]?([TGMkmuµnp%]?\\D*)")) ;
QRegularExpressionMatch match ;
QLocale const default_locale ;

   conv->empty = true ;
   conv->relative = false ;
   conv->error = false ;
   conv->value = 0.0 ;
   mult = 1.0 ;

   if (conv->str.isEmpty() && (!conv->required)) return(conv) ;

   conv->error = true ;

   match = re.match(conv->str) ;

   if (!match.hasMatch()) conv->error = true ;
   else
   {
      match1 = match.captured(1) ;
      conv->empty = match1.isEmpty() ;
      if (!conv->empty)
      {
         conv->str = match1 ;
         match2 = match.captured(2) ;
         if (!match2.isEmpty())
         {
            conv->str = conv->str+" "+match2 ;
            if (match2[0] == u'T') mult = 1e12  ;
            if (match2[0] == u'G') mult = 1e9  ;
            if (match2[0] == u'M') mult = 1e6  ;
            if (match2[0] == u'k') mult = 1e3  ;
            if (match2[0] == u'm') mult = 1e-3  ;
            if (match2[0] == u'u') mult = 1e-6  ;
            if (match2[0] == u'µ') mult = 1e-6  ;
            if (match2[0] == u'n') mult = 1e-9  ;
            if (match2[0] == u'p') mult = 1e-12  ;
            if (match2[0] == u'f') mult = 1e-15  ;
            if (match2[0] == u'%') {mult = 1e-2  ; conv->relative = true ;}
         }

         if (useLocale)
            conv->value = mult * default_locale.toDouble(match1, &ok) ; /* get value in default locale */
         else
            conv->value = mult * match1.toDouble(&ok) ; /* get value in C locale */
         conv->error = !ok ;
         conv->error |= ((conv->positive) && (conv->value < 0.0)) ;
      }
      else
      { /* empty, set error only if required */
         conv->error = conv->required ;
      }
   }

   return(conv) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       CapToStr()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Generate formatted string from capacity in Farad                                                │
│                                                                                                 │
│ Rounding leads to strange results occasionally. Therefore 0.9999 is used instead of 1.0         │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
QString MainWindow::CapToStr(qreal val)
{
QString str ;
   val = fabs(val) ;
   if  (val <  0.9999e-12)                       str = QString("%L1 fF").arg(val*1.0e15, 0, 'f', 2) ;
   if ((val >= 0.9999e-12) && (val < 0.9999e-9)) str = QString("%L1 pF").arg(val*1.0e12, 0, 'f', 2) ;
   if ((val >= 0.9999e-9)  && (val < 0.9999e-6)) str = QString("%L1 nF").arg(val*1.0e9,  0, 'f', 2) ;
   if ((val >= 0.9999e-6)  && (val < 0.9999e-3)) str = QString("%L1 µF").arg(val*1.0e6,  0, 'f', 2) ;
   if ((val >= 0.9999e-3)  && (val < 0.9999))    str = QString("%L1 mF").arg(val*1.0e3,  0, 'f', 2) ;
   if  (val >= 0.9999)                           str = QString("%L1 F" ).arg(val,        0, 'f', 2) ;
   return(str) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       IndToStr()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Generate formatted string from inductivity in Henry                                             │
│                                                                                                 │
│ Rounding leads to strange results occasionally. Therefore 0.9999 is used instead of 1.0         │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
QString MainWindow::IndToStr(qreal val)
{
QString str ;
   val = fabs(val) ;
   if  (val <  0.9999e-9)                       str = QString("%L1 pH").arg(val*1.0e12, 0, 'f', 2) ;
   if ((val >= 0.9999e-9) && (val < 0.9999e-6)) str = QString("%L1 nH").arg(val*1.0e9,  0, 'f', 2) ;
   if ((val >= 0.9999e-6) && (val < 0.9999e-3)) str = QString("%L1 µH").arg(val*1.0e6,  0, 'f', 2) ;
   if ((val >= 0.9999e-3) && (val < 0.9999))    str = QString("%L1 mH").arg(val*1.0e3,  0, 'f', 2) ;
   if  (val >= 0.9999)                          str = QString("%L1 H" ).arg(val,        0, 'f', 2) ;
   return(str) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       ResToStr()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Generate formatted string from resistance in Ohm                                                │
│                                                                                                 │
│ Rounding leads to strange results occasionally. Therefore 0.9999 is used instead of 1.0         │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
QString MainWindow::ResToStr(qreal val, int prec, bool imag)
{
QString str ;
QString s_imag = "" ;
   if (imag) s_imag = "j" ;
//   val = fabs(val) ;
   if  (fabs(val) <  0.9999)                               str = QString("%L1 %2m" CHAR_CAP_OMEGA).arg(val*1.0e3, 0, 'f', prec).arg(s_imag) ;
   if ((fabs(val) >= 0.9999)    && (fabs(val) < 0.9999e3)) str = QString("%L1 %2"  CHAR_CAP_OMEGA).arg(val,       0, 'f', prec).arg(s_imag) ;
   if ((fabs(val) >= 0.9999e3)  && (fabs(val) < 0.9999e6)) str = QString("%L1 %2k" CHAR_CAP_OMEGA).arg(val/1.0e3, 0, 'f', prec).arg(s_imag) ;
   if ((fabs(val) >= 0.9999e6)  && (fabs(val) < 0.9999e9)) str = QString("%L1 %2M" CHAR_CAP_OMEGA).arg(val/1.0e6, 0, 'f', prec).arg(s_imag) ;
   if  (fabs(val) >= 0.9999e9)                             str = QString("%L1 %2G" CHAR_CAP_OMEGA).arg(val/1.0e9, 0, 'f', prec).arg(s_imag) ;
   return(str) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       FrqToStr()                                                                                │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Generate formatted string from frequency in Hz                                                  │
│                                                                                                 │
│ Rounding leads to strange results occasionally. Therefore 0.9999 is used instead of 1.0         │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
QString MainWindow::FrqToStr(qreal val)
{
QString str ;
   val = fabs(val) ;
   if  (val <  0.9999e3)                      str = QString("%L1 Hz" ).arg(val,       0, 'f', 3) ;
   if ((val >= 0.9999e3) && (val < 0.9999e6)) str = QString("%L1 kHz").arg(val/1.0e3, 0, 'f', 3) ;
   if ((val >= 0.9999e6) && (val < 0.9999e9)) str = QString("%L1 MHz").arg(val/1.0e6, 0, 'f', 3) ;
   if  (val >= 0.9999e9)                      str = QString("%L1 GHz").arg(val/1.0e9, 0, 'f', 3) ;
   return(str) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       NewGamma                                                                                  │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ New gamma value entered                                                                         │
│                                                                                                 │
│ This slot is connected to a signal from the scene that the marker was dragged.                  │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::NewGamma(complex_dbl new_gamma)
{
gamma = new_gamma ;
   updateSlider(UPD_UI_ALL) ;
   updateLE(UPD_UI_ALL) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       closeEvent()                                                                              │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ The window shall be closed                                                                      │
│                                                                                                 │
│ This event is received when clicking the X title bar button or when the QWidget::close()        │
│ is called.                                                                                      │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::closeEvent(QCloseEvent * /*event*/)
{
   clear() ; /* Removes and deletes (!) all items from the scene */
   setClosed() ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       resizeEvent()                                                                             │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ The window is being resized                                                                     │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::resizeEvent(QResizeEvent *event)
{
   ui->QGV_Chart->fitInView(scene->sceneRect(),Qt::KeepAspectRatio) ;
   event->accept() ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       loadSettings                                                                              │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Load initialization settings from registry or ini-file and set dialogs accordingly.             │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::loadSettings()
{
   QSettings const settings;

   QSettings::setDefaultFormat(QSettings::NativeFormat);

   restoreGeometry(settings.value(KEY_PGM + "geometry").toByteArray());
   restoreState(settings.value(KEY_PGM + "windowState").toByteArray());

   swr.at(0) = settings.value(KEY_SMITH + "SWR1",      1.0).toDouble() ;
   swr.at(1) = settings.value(KEY_SMITH + "SWR2",      1.0).toDouble() ;
   swr.at(2) = settings.value(KEY_SMITH + "SWR3",      1.0).toDouble() ;
   z0.real(    settings.value(KEY_SMITH + "Z0",       50.0).toDouble()) ;
   gamma.real( settings.value(KEY_SMITH + "GAMMAR",    0.0).toDouble()) ;
   gamma.imag( settings.value(KEY_SMITH + "GAMMAI",    0.0).toDouble()) ;
   freq =    ( settings.value(KEY_SMITH + "Frequency", 1.0e6).toDouble()) ;

}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       saveSettings                                                                              │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Save initialization settings to registry or ini-file.                                           │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void MainWindow::saveSettings()
{
QSettings settings ;

   QSettings::setDefaultFormat(QSettings::NativeFormat) ;

   settings.setValue(KEY_PGM  + "geometry", saveGeometry()) ;
   settings.setValue(KEY_PGM  + "windowState", saveState()) ;

   settings.setValue(KEY_SMITH  + "SWR1",      swr.at(0)) ;
   settings.setValue(KEY_SMITH  + "SWR2",      swr.at(1)) ;
   settings.setValue(KEY_SMITH  + "SWR3",      swr.at(2)) ;
   settings.setValue(KEY_SMITH  + "Z0",        real(z0)) ;
   settings.setValue(KEY_SMITH  + "GAMMAR",    real(gamma)) ;
   settings.setValue(KEY_SMITH  + "GAMMAI",    imag(gamma)) ;
   settings.setValue(KEY_SMITH  + "Frequency", freq) ;

}

