547 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			547 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | #include "EqualizationToolsDialog.hpp"
 | ||
|  | 
 | ||
|  | #include <iterator>
 | ||
|  | #include <algorithm>
 | ||
|  | #include <fstream>
 | ||
|  | #include <limits>
 | ||
|  | #include <cmath>
 | ||
|  | 
 | ||
|  | #include <QDir>
 | ||
|  | #include <QVector>
 | ||
|  | #include <QHBoxLayout>
 | ||
|  | #include <QDialog>
 | ||
|  | #include <QDialogButtonBox>
 | ||
|  | #include <QPushButton>
 | ||
|  | #include <QFileDialog>
 | ||
|  | #include <QSettings>
 | ||
|  | 
 | ||
|  | #include "SettingsGroup.hpp"
 | ||
|  | #include "qcustomplot.h"
 | ||
|  | #include "pimpl_impl.hpp"
 | ||
|  | 
 | ||
|  | namespace | ||
|  | { | ||
|  |   float constexpr PI = 3.1415927f; | ||
|  |   char const * const title = "Equalization Tools"; | ||
|  |   size_t constexpr intervals = 144; | ||
|  | 
 | ||
|  |   // plot data loaders - wraps a plot providing value_type and
 | ||
|  |   // push_back so that a std::back_inserter output iterator can be
 | ||
|  |   // used to load plot data
 | ||
|  |   template<typename T, typename A> | ||
|  |   struct plot_data_loader | ||
|  |   { | ||
|  |   public: | ||
|  |     typedef T value_type; | ||
|  | 
 | ||
|  |     // the adjust argument is a function that is passed the plot
 | ||
|  |     // pointer, the graph index and a data point, it returns a
 | ||
|  |     // possibly adjusted data point and can modify the graph including
 | ||
|  |     // adding extra points or gaps (quiet_NaN)
 | ||
|  |     plot_data_loader (QCustomPlot * plot, int graph_index, A adjust) | ||
|  |       : plot_ {plot} | ||
|  |       , index_ {graph_index} | ||
|  |       , adjust_ (adjust) | ||
|  |     { | ||
|  |     } | ||
|  | 
 | ||
|  |     // load point into graph
 | ||
|  |     void push_back (value_type const& d) | ||
|  |     { | ||
|  |       plot_->graph (index_)->data ()->add (adjust_ (plot_, index_, d)); | ||
|  |     } | ||
|  | 
 | ||
|  |   private: | ||
|  |     QCustomPlot * plot_; | ||
|  |     int index_; | ||
|  |     A adjust_; | ||
|  |   }; | ||
|  |   // helper function template to make a plot_data_loader instance
 | ||
|  |   template<typename A> | ||
|  |   auto make_plot_data_loader (QCustomPlot * plot, int index, A adjust) | ||
|  |     -> plot_data_loader<QCPGraphData, decltype (adjust)> | ||
|  |   { | ||
|  |     return plot_data_loader<QCPGraphData, decltype (adjust)> {plot, index, adjust}; | ||
|  |   } | ||
|  |   // identity adjust function when no adjustment is needed with the
 | ||
|  |   // above instantiation helper
 | ||
|  |   QCPGraphData adjust_identity (QCustomPlot *, int, QCPGraphData const& v) {return v;} | ||
|  | 
 | ||
|  |   // a plot_data_loader adjustment function that wraps Y values of
 | ||
|  |   // (-1..+1) plotting discontinuities as gaps in the graph data
 | ||
|  |   auto wrap_pi = [] (QCustomPlot * plot, int index, QCPGraphData d)  | ||
|  |   { | ||
|  |     double constexpr limit {1}; | ||
|  |     static unsigned wrap_count {0}; | ||
|  |     static double last_x {std::numeric_limits<double>::lowest ()}; | ||
|  | 
 | ||
|  |     d.value += 2 * limit * wrap_count; | ||
|  |     if (d.value > limit) | ||
|  |       { | ||
|  |         // insert a gap in the graph
 | ||
|  |         plot->graph (index)->data ()->add ({last_x + (d.key - last_x) / 2 | ||
|  |               , std::numeric_limits<double>::quiet_NaN ()}); | ||
|  |         while (d.value > limit) | ||
|  |           { | ||
|  |             --wrap_count; | ||
|  |             d.value -= 2 * limit; | ||
|  |           } | ||
|  |       } | ||
|  |     else if (d.value < -limit) | ||
|  |       { | ||
|  |         // insert a gap into the graph
 | ||
|  |         plot->graph (index)->data ()->add ({last_x + (d.key - last_x) / 2 | ||
|  |               , std::numeric_limits<double>::quiet_NaN ()}); | ||
|  |         while (d.value < -limit) | ||
|  |           { | ||
|  |             ++wrap_count; | ||
|  |             d.value += 2 * limit; | ||
|  |           } | ||
|  |       } | ||
|  |     last_x = d.key; | ||
|  |     return d; | ||
|  |   }; | ||
|  | 
 | ||
|  |   // generate points of type R from a function of type F for X in
 | ||
|  |   // (-1..+1) with N intervals and function of type SX to scale X and
 | ||
|  |   // of type SY to scale Y
 | ||
|  |   //
 | ||
|  |   // it is up to the user to call the generator sufficient times which
 | ||
|  |   // is interval+1 times to reach +1
 | ||
|  |   template<typename R, typename F, typename SX, typename SY> | ||
|  |   struct graph_generator | ||
|  |   { | ||
|  |   public: | ||
|  |     graph_generator (F f, size_t intervals, SX x_scaling, SY y_scaling) | ||
|  |       : x_ {0} | ||
|  |       , f_ (f) | ||
|  |       , intervals_ {intervals} | ||
|  |       , x_scaling_ (x_scaling) | ||
|  |       , y_scaling_ (y_scaling) | ||
|  |     { | ||
|  |     } | ||
|  | 
 | ||
|  |     R operator () () | ||
|  |     { | ||
|  |       typename F::value_type x {x_++ * 2.f / intervals_ - 1.f}; | ||
|  |       return {x_scaling_ (x), y_scaling_ (f_ (x))}; | ||
|  |     } | ||
|  | 
 | ||
|  |   private: | ||
|  |     int x_; | ||
|  |     F f_; | ||
|  |     size_t intervals_; | ||
|  |     SX x_scaling_; | ||
|  |     SY y_scaling_; | ||
|  |   }; | ||
|  |   // helper function template to make a graph_generator instance for
 | ||
|  |   // QCPGraphData type points with intervals intervals
 | ||
|  |   template<typename F, typename SX, typename SY> | ||
|  |   auto make_graph_generator (F function, SX x_scaling, SY y_scaling) | ||
|  |     -> graph_generator<QCPGraphData, F, decltype (x_scaling), decltype (y_scaling)> | ||
|  |   { | ||
|  |     return graph_generator<QCPGraphData, F, decltype (x_scaling), decltype (y_scaling)> | ||
|  |       {function, intervals, x_scaling, y_scaling}; | ||
|  |   } | ||
|  | 
 | ||
|  |   // template function object for a polynomial with coefficients
 | ||
|  |   template<typename C> | ||
|  |   class polynomial | ||
|  |   { | ||
|  |   public: | ||
|  |     typedef typename C::value_type value_type; | ||
|  | 
 | ||
|  |     explicit polynomial (C const& coefficients) | ||
|  |       : c_ {coefficients} | ||
|  |     { | ||
|  |     } | ||
|  | 
 | ||
|  |     value_type operator () (value_type const& x) | ||
|  |     { | ||
|  |       value_type y {}; | ||
|  |       for (typename C::size_type i = c_.size (); i > 0; --i) | ||
|  |         { | ||
|  |           y = c_[i - 1] + x * y; | ||
|  |         } | ||
|  |       return y; | ||
|  |     } | ||
|  | 
 | ||
|  |   private: | ||
|  |     C c_; | ||
|  |   }; | ||
|  |   // helper function template to instantiate a polynomial instance
 | ||
|  |   template<typename C> | ||
|  |   auto make_polynomial (C const& coefficients) -> polynomial<C> | ||
|  |   { | ||
|  |     return polynomial<C> (coefficients); | ||
|  |   } | ||
|  | 
 | ||
|  |   // template function object for a group delay with coefficients
 | ||
|  |   template<typename C> | ||
|  |   class group_delay | ||
|  |   { | ||
|  |   public: | ||
|  |     typedef typename C::value_type value_type; | ||
|  | 
 | ||
|  |     explicit group_delay (C const& coefficients) | ||
|  |       : c_ {coefficients} | ||
|  |     { | ||
|  |     } | ||
|  | 
 | ||
|  |     value_type operator () (value_type const& x) | ||
|  |     { | ||
|  |       value_type tau {}; | ||
|  |       for (typename C::size_type i = 2; i < c_.size (); ++i) | ||
|  |         { | ||
|  |           tau += i * c_[i] * std::pow (x, i - 1); | ||
|  |         } | ||
|  |       return -1 / (2 * PI) * tau; | ||
|  |     } | ||
|  | 
 | ||
|  |   private: | ||
|  |     C c_; | ||
|  |   }; | ||
|  |   // helper function template to instantiate a group_delay function
 | ||
|  |   // object
 | ||
|  |   template<typename C> | ||
|  |   auto make_group_delay (C const& coefficients) -> group_delay<C> | ||
|  |   { | ||
|  |     return group_delay<C> (coefficients); | ||
|  |   } | ||
|  | 
 | ||
|  |   // handy identity function
 | ||
|  |   template<typename T> T identity (T const& v) {return v;} | ||
|  | 
 | ||
|  |   // a lambda that scales the X axis from normalized to (500..2500)Hz
 | ||
|  |   auto freq_scaling = [] (float v) -> float {return 1500.f + 1000.f * v;}; | ||
|  | 
 | ||
|  |   // a lambda that scales the phase Y axis from radians to units of Pi
 | ||
|  |   auto pi_scaling = [] (float v) -> float {return v / PI;}; | ||
|  | } | ||
|  | 
 | ||
|  | class EqualizationToolsDialog::impl final | ||
|  |   : public QDialog | ||
|  | { | ||
|  |   Q_OBJECT | ||
|  | 
 | ||
|  | public: | ||
|  |   explicit impl (EqualizationToolsDialog * self, QSettings * settings | ||
|  |                  , QDir const& data_directory, QVector<double> const& coefficients | ||
|  |                  , QWidget * parent); | ||
|  |   ~impl () {save_window_state ();} | ||
|  | 
 | ||
|  | protected: | ||
|  |   void closeEvent (QCloseEvent * e) override | ||
|  |   { | ||
|  |     save_window_state (); | ||
|  |     QDialog::closeEvent (e); | ||
|  |   } | ||
|  | 
 | ||
|  | private: | ||
|  |   void save_window_state () | ||
|  |   { | ||
|  |     SettingsGroup g (settings_, title); | ||
|  |     settings_->setValue ("geometry", saveGeometry ()); | ||
|  |   } | ||
|  | 
 | ||
|  |   void plot_current (); | ||
|  |   void plot_phase (); | ||
|  |   void plot_amplitude (); | ||
|  | 
 | ||
|  |   EqualizationToolsDialog * self_; | ||
|  |   QSettings * settings_; | ||
|  |   QDir data_directory_; | ||
|  |   QHBoxLayout layout_; | ||
|  |   QVector<double> current_coefficients_; | ||
|  |   QVector<double> new_coefficients_; | ||
|  |   unsigned amp_poly_low_; | ||
|  |   unsigned amp_poly_high_; | ||
|  |   QVector<double> amp_coefficients_; | ||
|  |   QCustomPlot plot_; | ||
|  |   QDialogButtonBox button_box_; | ||
|  | }; | ||
|  | 
 | ||
|  | #include "EqualizationToolsDialog.moc"
 | ||
|  | 
 | ||
|  | EqualizationToolsDialog::EqualizationToolsDialog (QSettings * settings | ||
|  |                                                   , QDir const& data_directory | ||
|  |                                                   , QVector<double> const& coefficients | ||
|  |                                                   , QWidget * parent) | ||
|  |   : m_ {this, settings, data_directory, coefficients, parent} | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | void EqualizationToolsDialog::show () | ||
|  | { | ||
|  |   m_->show (); | ||
|  | } | ||
|  | 
 | ||
|  | EqualizationToolsDialog::impl::impl (EqualizationToolsDialog * self | ||
|  |                                      , QSettings * settings | ||
|  |                                      , QDir const& data_directory | ||
|  |                                      , QVector<double> const& coefficients | ||
|  |                                      , QWidget * parent) | ||
|  |   : QDialog {parent} | ||
|  |   , self_ {self} | ||
|  |   , settings_ {settings} | ||
|  |   , data_directory_ {data_directory} | ||
|  |   , current_coefficients_ {coefficients} | ||
|  |   , amp_poly_low_ {0} | ||
|  |   , amp_poly_high_ {6000} | ||
|  |   , button_box_ {QDialogButtonBox::Discard | QDialogButtonBox::Apply | ||
|  |         | QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Close | ||
|  |         , Qt::Vertical} | ||
|  | { | ||
|  |   setWindowTitle (windowTitle () + ' ' + tr (title)); | ||
|  |   resize (500, 600); | ||
|  |   { | ||
|  |     SettingsGroup g {settings_, title}; | ||
|  |     restoreGeometry (settings_->value ("geometry", saveGeometry ()).toByteArray ()); | ||
|  |   } | ||
|  | 
 | ||
|  |   auto legend_title = new QCPTextElement {&plot_, tr ("Phase"), QFont {"sans", 9, QFont::Bold}}; | ||
|  |   legend_title->setLayer (plot_.legend->layer ()); | ||
|  |   plot_.legend->addElement (0, 0, legend_title); | ||
|  |   plot_.legend->setVisible (true); | ||
|  | 
 | ||
|  |   plot_.xAxis->setLabel (tr ("Freq (Hz)")); | ||
|  |   plot_.xAxis->setRange (500, 2500); | ||
|  |   plot_.yAxis->setLabel (tr ("Phase (Π)")); | ||
|  |   plot_.yAxis->setRange (-1, +1); | ||
|  |   plot_.yAxis2->setLabel (tr ("Delay (ms)")); | ||
|  |   plot_.axisRect ()->setRangeDrag (Qt::Vertical); | ||
|  |   plot_.axisRect ()->setRangeZoom (Qt::Vertical); | ||
|  |   plot_.yAxis2->setVisible (true); | ||
|  |   plot_.axisRect ()->setRangeDragAxes (nullptr, plot_.yAxis2); | ||
|  |   plot_.axisRect ()->setRangeZoomAxes (nullptr, plot_.yAxis2); | ||
|  |   plot_.axisRect ()->insetLayout ()->setInsetAlignment (0, Qt::AlignBottom|Qt::AlignRight); | ||
|  |   plot_.setInteractions (QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); | ||
|  | 
 | ||
|  |   plot_.addGraph ()->setName (tr ("Measured")); | ||
|  |   plot_.graph ()->setPen (QPen {Qt::blue}); | ||
|  |   plot_.graph ()->setVisible (false); | ||
|  |   plot_.graph ()->removeFromLegend (); | ||
|  | 
 | ||
|  |   plot_.addGraph ()->setName (tr ("Proposed")); | ||
|  |   plot_.graph ()->setPen (QPen {Qt::red}); | ||
|  |   plot_.graph ()->setVisible (false); | ||
|  |   plot_.graph ()->removeFromLegend (); | ||
|  | 
 | ||
|  |   plot_.addGraph ()->setName (tr ("Current")); | ||
|  |   plot_.graph ()->setPen (QPen {Qt::green}); | ||
|  | 
 | ||
|  |   plot_.addGraph (plot_.xAxis, plot_.yAxis2)->setName (tr ("Group Delay")); | ||
|  |   plot_.graph ()->setPen (QPen {Qt::darkGreen}); | ||
|  | 
 | ||
|  |   plot_.plotLayout ()->addElement (new QCPAxisRect {&plot_}); | ||
|  |   plot_.plotLayout ()->setRowStretchFactor (1, 0.5); | ||
|  | 
 | ||
|  |   auto amp_legend = new QCPLegend; | ||
|  |   plot_.axisRect (1)->insetLayout ()->addElement (amp_legend, Qt::AlignTop | Qt::AlignRight); | ||
|  |   plot_.axisRect (1)->insetLayout ()->setMargins (QMargins {12, 12, 12, 12}); | ||
|  |   amp_legend->setVisible (true); | ||
|  |   amp_legend->setLayer (QLatin1String {"legend"}); | ||
|  |   legend_title = new QCPTextElement {&plot_, tr ("Amplitude"), QFont {"sans", 9, QFont::Bold}}; | ||
|  |   legend_title->setLayer (amp_legend->layer ()); | ||
|  |   amp_legend->addElement (0, 0, legend_title); | ||
|  | 
 | ||
|  |   plot_.axisRect (1)->axis (QCPAxis::atBottom)->setLabel (tr ("Freq (Hz)")); | ||
|  |   plot_.axisRect (1)->axis (QCPAxis::atBottom)->setRange (0, 6000); | ||
|  |   plot_.axisRect (1)->axis (QCPAxis::atLeft)->setLabel (tr ("Relative Power (dB)")); | ||
|  |   plot_.axisRect (1)->axis (QCPAxis::atLeft)->setRangeLower (0); | ||
|  |   plot_.axisRect (1)->setRangeDragAxes (nullptr, nullptr); | ||
|  |   plot_.axisRect (1)->setRangeZoomAxes (nullptr, nullptr); | ||
|  | 
 | ||
|  |   plot_.addGraph (plot_.axisRect (1)->axis (QCPAxis::atBottom) | ||
|  |                   , plot_.axisRect (1)->axis (QCPAxis::atLeft))->setName (tr ("Reference")); | ||
|  |   plot_.graph ()->setPen (QPen {Qt::blue}); | ||
|  |   plot_.graph ()->removeFromLegend (); | ||
|  |   plot_.graph ()->addToLegend (amp_legend); | ||
|  | 
 | ||
|  |   layout_.addWidget (&plot_); | ||
|  | 
 | ||
|  |   auto load_phase_button = button_box_.addButton (tr ("Phase ..."), QDialogButtonBox::ActionRole); | ||
|  |   auto refresh_button = button_box_.addButton (tr ("Refresh"), QDialogButtonBox::ActionRole); | ||
|  |   layout_.addWidget (&button_box_); | ||
|  |   setLayout (&layout_); | ||
|  | 
 | ||
|  |   connect (&button_box_, &QDialogButtonBox::rejected, this, &QDialog::reject); | ||
|  |   connect (&button_box_, &QDialogButtonBox::clicked, [=] (QAbstractButton * button) { | ||
|  |       if (button == load_phase_button) | ||
|  |         { | ||
|  |           plot_phase (); | ||
|  |         } | ||
|  |       else if (button == refresh_button) | ||
|  |         { | ||
|  |           plot_current (); | ||
|  |         } | ||
|  |       else if (button == button_box_.button (QDialogButtonBox::Apply)) | ||
|  |         { | ||
|  |           if (plot_.graph (0)->dataCount ()) // something loaded
 | ||
|  |             { | ||
|  |               current_coefficients_ = new_coefficients_; | ||
|  |               Q_EMIT self_->phase_equalization_changed (current_coefficients_); | ||
|  |               plot_current (); | ||
|  |             } | ||
|  |         } | ||
|  |       else if (button == button_box_.button (QDialogButtonBox::RestoreDefaults)) | ||
|  |         { | ||
|  |           current_coefficients_ = QVector<double> {0., 0., 0., 0., 0.}; | ||
|  |           Q_EMIT self_->phase_equalization_changed (current_coefficients_); | ||
|  |           plot_current (); | ||
|  |         } | ||
|  |       else if (button == button_box_.button (QDialogButtonBox::Discard)) | ||
|  |         { | ||
|  |           new_coefficients_ = QVector<double> {0., 0., 0., 0., 0.}; | ||
|  | 
 | ||
|  |           plot_.graph (0)->data ()->clear (); | ||
|  |           plot_.graph (0)->setVisible (false); | ||
|  |           plot_.graph (0)->removeFromLegend (); | ||
|  | 
 | ||
|  |           plot_.graph (1)->data ()->clear (); | ||
|  |           plot_.graph (1)->setVisible (false); | ||
|  |           plot_.graph (1)->removeFromLegend (); | ||
|  | 
 | ||
|  |           plot_.replot (); | ||
|  |         } | ||
|  |     }); | ||
|  | 
 | ||
|  |   plot_current (); | ||
|  | } | ||
|  | 
 | ||
|  | struct PowerSpectrumPoint | ||
|  | { | ||
|  |   operator QCPGraphData () const | ||
|  |   { | ||
|  |     return QCPGraphData {freq_, power_}; | ||
|  |   } | ||
|  | 
 | ||
|  |   float freq_; | ||
|  |   float power_; | ||
|  | }; | ||
|  | 
 | ||
|  | // read an amplitude point line from a stream (refspec.dat)
 | ||
|  | std::istream& operator >> (std::istream& is, PowerSpectrumPoint& r) | ||
|  | { | ||
|  |   float y1, y3, y4;             // discard these
 | ||
|  |   is >> r.freq_ >> y1 >> r.power_ >> y3 >> y4; | ||
|  |   return is; | ||
|  | } | ||
|  | 
 | ||
|  | void EqualizationToolsDialog::impl::plot_current () | ||
|  | { | ||
|  |   auto phase_graph = make_plot_data_loader (&plot_, 2, wrap_pi); | ||
|  |   plot_.graph (2)->data ()->clear (); | ||
|  |   std::generate_n (std::back_inserter (phase_graph), intervals + 1 | ||
|  |                    , make_graph_generator (make_polynomial (current_coefficients_), freq_scaling, pi_scaling)); | ||
|  | 
 | ||
|  |   auto group_delay_graph = make_plot_data_loader (&plot_, 3, adjust_identity); | ||
|  |   plot_.graph (3)->data ()->clear (); | ||
|  |   std::generate_n (std::back_inserter (group_delay_graph), intervals + 1 | ||
|  |                    , make_graph_generator (make_group_delay (current_coefficients_), freq_scaling, identity<double>)); | ||
|  |   plot_.graph (3)->rescaleValueAxis (); | ||
|  | 
 | ||
|  |   QFileInfo refspec_file_info {data_directory_.absoluteFilePath ("refspec.dat")}; | ||
|  |   std::ifstream refspec_file (refspec_file_info.absoluteFilePath ().toLatin1 ().constData (), std::ifstream::in); | ||
|  |   unsigned n; | ||
|  |   if (refspec_file >> amp_poly_low_ >> amp_poly_high_ >> n) | ||
|  |     { | ||
|  |       std::istream_iterator<double> isi {refspec_file}; | ||
|  |       amp_coefficients_.clear (); | ||
|  |       std::copy_n (isi, n, std::back_inserter (amp_coefficients_)); | ||
|  |     } | ||
|  |   else | ||
|  |     { | ||
|  |       // may be old format refspec.dat with no header so rewind
 | ||
|  |       refspec_file.clear (); | ||
|  |       refspec_file.seekg (0); | ||
|  |     } | ||
|  | 
 | ||
|  |   auto reference_spectrum_graph = make_plot_data_loader (&plot_, 4, adjust_identity); | ||
|  |   plot_.graph (4)->data ()->clear (); | ||
|  |   std::copy (std::istream_iterator<PowerSpectrumPoint> {refspec_file}, | ||
|  |              std::istream_iterator<PowerSpectrumPoint> {}, | ||
|  |              std::back_inserter (reference_spectrum_graph)); | ||
|  |   plot_.graph (4)->rescaleValueAxis (true); | ||
|  | 
 | ||
|  |   plot_.replot (); | ||
|  | } | ||
|  | 
 | ||
|  | struct PhasePoint | ||
|  | { | ||
|  |   operator QCPGraphData () const | ||
|  |   { | ||
|  |     return QCPGraphData {freq_, phase_}; | ||
|  |   } | ||
|  | 
 | ||
|  |   double freq_; | ||
|  |   double phase_; | ||
|  | }; | ||
|  | 
 | ||
|  | // read a phase point line from a stream (pcoeff file)
 | ||
|  | std::istream& operator >> (std::istream& is, PhasePoint& c) | ||
|  | { | ||
|  |   double pp, sigmay;            // discard these
 | ||
|  |   if (is >> c.freq_ >> pp >> c.phase_ >> sigmay) | ||
|  |     { | ||
|  |       c.freq_ = 1500. + 1000. * c.freq_; // scale frequency to Hz
 | ||
|  |       c.phase_ /= PI;                    // scale to units of Pi
 | ||
|  |     } | ||
|  |   return is; | ||
|  | } | ||
|  | 
 | ||
|  | void EqualizationToolsDialog::impl::plot_phase () | ||
|  | { | ||
|  |   auto const& phase_file_name = QFileDialog::getOpenFileName (this | ||
|  |                                                               , "Select Phase Response Coefficients" | ||
|  |                                                               , data_directory_.absolutePath () | ||
|  |                                                               , "Phase Coefficient Files (*.pcoeff)"); | ||
|  |   if (!phase_file_name.size ()) return; | ||
|  | 
 | ||
|  |   std::ifstream phase_file (phase_file_name.toLatin1 ().constData (), std::ifstream::in); | ||
|  |   int n; | ||
|  |   float chi; | ||
|  |   float rmsdiff; | ||
|  |   unsigned freq_low; | ||
|  |   unsigned freq_high; | ||
|  |   unsigned terms; | ||
|  |   // read header information
 | ||
|  |   if (phase_file >> n >> chi >> rmsdiff >> freq_low >> freq_high >> terms) | ||
|  |     { | ||
|  |       std::istream_iterator<double> isi {phase_file}; | ||
|  |       new_coefficients_.clear (); | ||
|  |       std::copy_n (isi, terms, std::back_inserter (new_coefficients_)); | ||
|  | 
 | ||
|  |       if (phase_file) | ||
|  |         { | ||
|  |           plot_.graph (0)->data ()->clear (); | ||
|  |           plot_.graph (1)->data ()->clear (); | ||
|  | 
 | ||
|  |           // read the phase data and plot as graph 0
 | ||
|  |           auto graph = make_plot_data_loader (&plot_, 0, adjust_identity); | ||
|  |           std::copy_n (std::istream_iterator<PhasePoint> {phase_file}, | ||
|  |                        intervals + 1, std::back_inserter (graph)); | ||
|  | 
 | ||
|  |           if (phase_file) | ||
|  |             { | ||
|  |               plot_.graph(0)->setLineStyle(QCPGraph::lsNone); | ||
|  |               plot_.graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 4)); | ||
|  |               plot_.graph (0)->setVisible (true); | ||
|  |               plot_.graph (0)->addToLegend (); | ||
|  | 
 | ||
|  |               // generate the proposed polynomial plot as graph 1
 | ||
|  |               auto graph = make_plot_data_loader (&plot_, 1, wrap_pi); | ||
|  |               std::generate_n (std::back_inserter (graph), intervals + 1 | ||
|  |                                , make_graph_generator (make_polynomial (new_coefficients_) | ||
|  |                                                        , freq_scaling, pi_scaling)); | ||
|  |               plot_.graph (1)->setVisible (true); | ||
|  |               plot_.graph (1)->addToLegend (); | ||
|  |             } | ||
|  | 
 | ||
|  |           plot_.replot (); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | #include "moc_EqualizationToolsDialog.cpp"
 |