KiCad PCB EDA Suite
sim_plot_frame.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2016 CERN
5  * Copyright (C) 2016-2018 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
7  * @author Maciej Suminski <maciej.suminski@cern.ch>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 3
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * https://www.gnu.org/licenses/gpl-3.0.html
22  * or you may search the http://www.gnu.org website for the version 3 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
27 #include <wx/stc/stc.h>
28 
29 #include <sch_edit_frame.h>
30 #include <eeschema_id.h>
31 #include <kiway.h>
32 #include <confirm.h>
33 #include <bitmaps.h>
35 #include <widgets/tuner_slider.h>
38 #include <pgm_base.h>
39 #include "sim_plot_frame.h"
40 #include "sim_plot_panel.h"
41 #include "spice_simulator.h"
42 #include "spice_reporter.h"
43 #include <menus_helpers.h>
44 #include <tool/tool_manager.h>
45 #include <tools/ee_actions.h>
46 #include <eeschema_settings.h>
47 
49 {
50  int res = (int) aFirst | (int) aSecond;
51 
52  return (SIM_PLOT_TYPE) res;
53 }
54 
55 
57 {
58 public:
60  : m_parent( aParent )
61  {
62  }
63 
64  REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
65  {
66  wxCommandEvent* event = new wxCommandEvent( EVT_SIM_REPORT );
67  event->SetString( aText );
68  wxQueueEvent( m_parent, event );
69  return *this;
70  }
71 
72  bool HasMessage() const override
73  {
74  return false; // Technically "indeterminate" rather than false.
75  }
76 
77  void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) override
78  {
79  wxCommandEvent* event = NULL;
80 
81  switch( aNewState )
82  {
83  case SIM_IDLE:
84  event = new wxCommandEvent( EVT_SIM_FINISHED );
85  break;
86 
87  case SIM_RUNNING:
88  event = new wxCommandEvent( EVT_SIM_STARTED );
89  break;
90 
91  default:
92  wxFAIL;
93  return;
94  }
95 
96  wxQueueEvent( m_parent, event );
97  }
98 
99 private:
101 };
102 
103 
104 TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName,
105  SIM_PLOT_TYPE aType, const wxString& aParam )
106  : m_name( aName ), m_type( aType ), m_param( aParam )
107 {
108  // Title generation
109  m_title = wxString::Format( "%s(%s)", aParam, aName );
110 
111  if( aType & SPT_AC_MAG )
112  m_title += " (mag)";
113  else if( aType & SPT_AC_PHASE )
114  m_title += " (phase)";
115 }
116 
117 // Store the path of saved workbooks during the session
119 
120 SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
121  : SIM_PLOT_FRAME_BASE( aParent ), m_lastSimPlot( nullptr )
122 {
123  SetKiway( this, aKiway );
125 
127 
128  if( m_schematicFrame == NULL )
129  throw std::runtime_error( "There is no schematic window" );
130 
131  // Give an icon
132  wxIcon icon;
133  icon.CopyFromBitmap( KiBitmap( simulator_xpm ) );
134  SetIcon( icon );
135 
136  // Gives a minimal size
137  SetSizeHints( 500, 400, -1, -1, -1, -1 );
138 
139  // Get the previous size and position of windows:
140  LoadSettings( config() );
141 
142  // Prepare the color list to plot traces
144 
145  // Give icons to menuitems
147 
149 
150  if( !m_simulator )
151  {
152  throw std::runtime_error( "Could not create simulator instance" );
153  return;
154  }
155 
156  m_simulator->Init();
157 
158  if( m_savedWorkbooksPath.IsEmpty() )
159  {
161  }
162 
163  m_reporter = new SIM_THREAD_REPORTER( this );
164  m_simulator->SetReporter( m_reporter );
165 
167 
168  Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SIM_PLOT_FRAME::onClose ), NULL, this );
169  Connect( EVT_SIM_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onSimUpdate ), NULL, this );
170  Connect( EVT_SIM_REPORT, wxCommandEventHandler( SIM_PLOT_FRAME::onSimReport ), NULL, this );
171  Connect( EVT_SIM_STARTED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimStarted ), NULL, this );
172  Connect( EVT_SIM_FINISHED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimFinished ), NULL, this );
173  Connect( EVT_SIM_CURSOR_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onCursorUpdate ), NULL, this );
174 
175  // Toolbar buttons
176  m_toolSimulate = m_toolBar->AddTool( ID_SIM_RUN, _( "Run/Stop Simulation" ),
177  KiBitmap( sim_run_xpm ), _( "Run Simulation" ), wxITEM_NORMAL );
178  m_toolAddSignals = m_toolBar->AddTool( ID_SIM_ADD_SIGNALS, _( "Add Signals" ),
179  KiBitmap( sim_add_signal_xpm ), _( "Add signals to plot" ), wxITEM_NORMAL );
180  m_toolProbe = m_toolBar->AddTool( ID_SIM_PROBE, _( "Probe" ),
181  KiBitmap( sim_probe_xpm ), _( "Probe signals on the schematic" ), wxITEM_NORMAL );
182  m_toolTune = m_toolBar->AddTool( ID_SIM_TUNE, _( "Tune" ),
183  KiBitmap( sim_tune_xpm ), _( "Tune component values" ), wxITEM_NORMAL );
184  m_toolSettings = m_toolBar->AddTool( wxID_ANY, _( "Settings" ),
185  KiBitmap( sim_settings_xpm ), _( "Simulation settings" ), wxITEM_NORMAL );
186 
187  Connect( m_toolSimulate->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
188  wxCommandEventHandler( SIM_PLOT_FRAME::onSimulate ), NULL, this );
189  Connect( m_toolAddSignals->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
190  wxCommandEventHandler( SIM_PLOT_FRAME::onAddSignal ), NULL, this );
191  Connect( m_toolProbe->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
192  wxCommandEventHandler( SIM_PLOT_FRAME::onProbe ), NULL, this );
193  Connect( m_toolTune->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
194  wxCommandEventHandler( SIM_PLOT_FRAME::onTune ), NULL, this );
195  Connect( m_toolSettings->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
196  wxCommandEventHandler( SIM_PLOT_FRAME::onSettings ), NULL, this );
197 
198  // Bind toolbar buttons event to existing menu event handlers, so they behave the same
199  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSimulate, this, m_runSimulation->GetId() );
200  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onAddSignal, this, m_addSignals->GetId() );
201  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onProbe, this, m_probeSignals->GetId() );
202  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onTune, this, m_tuneValue->GetId() );
203  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onShowNetlist, this, m_showNetlist->GetId() );
204  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSettings, this, m_settings->GetId() );
205 
206  m_toolBar->Realize();
207  m_plotNotebook->SetPageText( 0, _( "Welcome!" ) );
208 
209  // the settings dialog will be created later, on demand.
210  // if created in the ctor, for some obscure reason, there is an issue
211  // on Windows: when open it, the simulator frame is sent to the background.
212  // instead of being behind the dialog frame (as it does)
214 
215  // resize the subwindows size. At least on Windows, calling wxSafeYield before
216  // resizing the subwindows forces the wxSplitWindows size events automatically generated
217  // by wxWidgets to be executed before our resize code.
218  // Otherwise, the changes made by setSubWindowsSashSize are overwritten by one these
219  // events
220  wxSafeYield();
222 }
223 
224 
226 {
227  m_simulator->SetReporter( nullptr );
228  delete m_reporter;
229  delete m_signalsIconColorList;
230 
231  if( m_settingsDlg )
232  m_settingsDlg->Destroy();
233 }
234 
235 
237 {
238  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
239  wxASSERT( cfg );
240 
242 
243  cfg->m_Simulator.plot_panel_width = m_splitterLeftRight->GetSashPosition();
244  cfg->m_Simulator.plot_panel_height = m_splitterPlotAndConsole->GetSashPosition();
245  cfg->m_Simulator.signal_panel_height = m_splitterSignals->GetSashPosition();
246  cfg->m_Simulator.cursors_panel_height = m_splitterTuneValues->GetSashPosition();
247  cfg->m_Simulator.white_background = m_plotUseWhiteBg;
248 }
249 
250 
252 {
253  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
254  wxASSERT( cfg );
255 
257 
258  SetSize( m_FramePos.x, m_FramePos.y, m_FrameSize.x, m_FrameSize.y );
259 
260  // Read subwindows sizes (should be > 0 )
261  m_splitterLeftRightSashPosition = cfg->m_Simulator.plot_panel_width;
262  m_splitterPlotAndConsoleSashPosition = cfg->m_Simulator.plot_panel_height;
263  m_splitterSignalsSashPosition = cfg->m_Simulator.signal_panel_height;
264  m_splitterTuneValuesSashPosition = cfg->m_Simulator.cursors_panel_height;
265  m_plotUseWhiteBg = cfg->m_Simulator.white_background;
266 }
267 
268 
270 {
271  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
272  wxASSERT( cfg );
273  return &cfg->m_Simulator.window;
274 }
275 
276 
277 // A small helper struct to handle bitmaps initialisation in menus
279 {
280  int m_MenuId;
282 };
283 
284 
286 {
287  // Give icons to menuitems of the main menubar
288  BM_MENU_INIT_ITEM bm_list[]
289  {
290  // File menu:
291  { wxID_NEW, simulator_xpm },
292  { wxID_OPEN,directory_browser_xpm },
293  { wxID_SAVE, directory_xpm},
296  { wxID_CLOSE, exit_xpm},
297 
298  // simulator menu:
305 
306  // View menu
307  { wxID_ZOOM_IN, zoom_in_xpm},
308  { wxID_ZOOM_OUT, zoom_out_xpm},
309  { wxID_ZOOM_FIT, zoom_fit_in_page_xpm},
314 
315  { 0, nullptr } // Sentinel
316  };
317 
318  // wxMenuItems are already created and attached to the m_mainMenu wxMenuBar.
319  // A problem is the fact setting bitmaps in wxMenuItems after they are attached
320  // to a wxMenu do not work in all cases.
321  // So the trick is:
322  // Remove the wxMenuItem from its wxMenu
323  // Set the bitmap
324  // Insert the modified wxMenuItem to its previous place
325  for( int ii = 0; bm_list[ii].m_MenuId; ++ii )
326  {
327  wxMenuItem* item = m_mainMenu->FindItem( bm_list[ii].m_MenuId );
328 
329  if( !item || !bm_list[ii].m_Bitmap)
330  continue;
331 
332  wxMenu* menu = item->GetMenu();
333  // Calculate the initial index of item inside the wxMenu parent
334  wxMenuItemList& mlist = menu->GetMenuItems();
335  int mpos = mlist.IndexOf( item );
336 
337  if( mpos >= 0 ) // Should be always the case
338  {
339  // Modify the bitmap
340  menu->Remove( item );
341  AddBitmapToMenuItem( item, KiBitmap( bm_list[ii].m_Bitmap ) );
342  // Insert item to its the initial index
343  menu->Insert( mpos, item );
344  }
345  }
346 }
347 
348 
350 {
353 
356 
359 
362 }
363 
364 
365 wxColor SIM_PLOT_FRAME::GetPlotColor( int aColorId )
366 {
367  // return the wxColor selected in color list or BLACK is not in list
368  if( aColorId >= 0 && aColorId < (int)m_colorList.size() )
369  return m_colorList[aColorId];
370 
371  return wxColor( 0, 0, 0 );
372 }
373 
374 
376 {
377  m_colorList.clear();
378 
379  if( aWhiteBg )
380  {
381  m_colorList.emplace_back( 255, 255, 255 ); // Bg color
382  m_colorList.emplace_back( 0, 0, 0 ); // Fg color (texts)
383  m_colorList.emplace_back( 130, 130, 130 ); // Axis color
384  m_colorList.emplace_back( 0, 0, 0 ); // cursors color
385  }
386  else
387  {
388  m_colorList.emplace_back( 0, 0, 0 ); // Bg color
389  m_colorList.emplace_back( 255, 255, 255 ); // Fg color (texts)
390  m_colorList.emplace_back( 130, 130, 130 ); // Axis color
391  m_colorList.emplace_back( 255, 255, 255 ); // cursors color
392  }
393 
394  // Add a list of color for traces, starting at index SIM_TRACE_COLOR
395  m_colorList.emplace_back( 0xE4, 0x1A, 0x1C );
396  m_colorList.emplace_back( 0x37, 0x7E, 0xB8 );
397  m_colorList.emplace_back( 0x4D, 0xAF, 0x4A );
398  m_colorList.emplace_back( 0x98, 0x4E, 0xA3 );
399  m_colorList.emplace_back( 0xFF, 0x7F, 0x00 );
400  m_colorList.emplace_back( 0xFF, 0xFF, 0x33 );
401  m_colorList.emplace_back( 0xA6, 0x56, 0x28 );
402  m_colorList.emplace_back( 0xF7, 0x81, 0xBF );
403  m_colorList.emplace_back( 0x66, 0xC2, 0xA5 );
404  m_colorList.emplace_back( 0xFC, 0x8D, 0x62 );
405  m_colorList.emplace_back( 0x8D, 0xA0, 0xCB );
406  m_colorList.emplace_back( 0xE7, 0x8A, 0xC3 );
407  m_colorList.emplace_back( 0xA6, 0xD8, 0x54 );
408  m_colorList.emplace_back( 0xFF, 0xD9, 0x2F );
409  m_colorList.emplace_back( 0xE5, 0xC4, 0x94 );
410  m_colorList.emplace_back( 0xB3, 0xB3, 0xB3 );
411 
412 }
413 
414 
416 {
417  STRING_FORMATTER formatter;
418  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
419 
420  if( !m_settingsDlg )
421  m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
422 
423  m_simConsole->Clear();
425 
426  if( plotPanel )
427  m_exporter->SetSimCommand( m_plots[plotPanel].m_simCommand );
428 
429  if( !m_exporter->Format( &formatter, m_settingsDlg->GetNetlistOptions() ) )
430  {
431  DisplayError( this, _( "There were errors during netlist export, aborted." ) );
432  return;
433  }
434 
435  if( m_exporter->GetSimType() == ST_UNKNOWN )
436  {
437  DisplayInfoMessage( this, _( "You need to select the simulation settings first." ) );
438  return;
439  }
440 
441  m_simulator->LoadNetlist( formatter.GetString() );
442  updateTuners();
443  applyTuners();
444  m_simulator->Run();
445 }
446 
447 
449 {
450  m_simulator->Stop();
451 }
452 
453 
455 {
456  return m_simulator ? m_simulator->IsRunning() : false;
457 }
458 
459 
461 {
462  SIM_PLOT_PANEL* plotPanel = new SIM_PLOT_PANEL( aSimType, m_plotNotebook, this, wxID_ANY );
463 
464  plotPanel->EnableMouseWheelPan(
466 
467  if( m_welcomePanel )
468  {
469  m_plotNotebook->DeletePage( 0 );
470  m_welcomePanel = nullptr;
471  }
472 
473  m_plotNotebook->AddPage( plotPanel, wxString::Format( _( "Plot%u" ),
474  (unsigned int) m_plotNotebook->GetPageCount() + 1 ), true );
475 
476  m_plots[plotPanel] = PLOT_INFO();
477 
478  return plotPanel;
479 }
480 
481 
482 void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName )
483 {
484  addPlot( aNetName, SPT_VOLTAGE, "V" );
485 }
486 
487 
488 void SIM_PLOT_FRAME::AddCurrentPlot( const wxString& aDeviceName, const wxString& aParam )
489 {
490  addPlot( aDeviceName, SPT_CURRENT, aParam );
491 }
492 
493 
495 {
496  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
497 
498  if( !plotPanel )
499  return;
500 
501  // For now limit the tuner tool to RLC components
502  char primitiveType = NETLIST_EXPORTER_PSPICE::GetSpiceField( SF_PRIMITIVE, aComponent, 0 )[0];
503 
504  if( primitiveType != SP_RESISTOR && primitiveType != SP_CAPACITOR && primitiveType != SP_INDUCTOR )
505  return;
506 
507  const wxString componentName = aComponent->GetField( REFERENCE )->GetText();
508 
509  // Do not add multiple instances for the same component
510  auto tunerIt = std::find_if( m_tuners.begin(), m_tuners.end(), [&]( const TUNER_SLIDER* t )
511  {
512  return t->GetComponentName() == componentName;
513  }
514  );
515 
516  if( tunerIt != m_tuners.end() )
517  return; // We already have it
518 
519  try
520  {
521  TUNER_SLIDER* tuner = new TUNER_SLIDER( this, m_tunePanel, aComponent );
522  m_tuneSizer->Add( tuner );
523  m_tuners.push_back( tuner );
524  m_tunePanel->Layout();
525  }
526  catch( const KI_PARAM_ERROR& e )
527  {
528  // Sorry, no bonus
529  DisplayError( nullptr, e.What() );
530  }
531 }
532 
533 
534 void SIM_PLOT_FRAME::RemoveTuner( TUNER_SLIDER* aTuner, bool aErase )
535 {
536  if( aErase )
537  m_tuners.remove( aTuner );
538 
539  aTuner->Destroy();
540  m_tunePanel->Layout();
541 }
542 
543 
545 {
546  wxWindow* curPage = m_plotNotebook->GetCurrentPage();
547 
548  return ( curPage == m_welcomePanel ) ? nullptr : static_cast<SIM_PLOT_PANEL*>( curPage );
549 }
550 
551 
553 {
554  return m_exporter.get();
555 }
556 
557 
558 void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam )
559 {
560  SIM_TYPE simType = m_exporter->GetSimType();
561 
562  if( simType == ST_UNKNOWN )
563  {
564  m_simConsole->AppendText( _( "Error: simulation type not defined!\n" ) );
565  m_simConsole->SetInsertionPointEnd();
566  return;
567  }
568  else if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
569  {
570  m_simConsole->AppendText( _( "Error: simulation type doesn't support plotting!\n" ) );
571  m_simConsole->SetInsertionPointEnd();
572  return;
573  }
574 
575  // Create a new plot if the current one displays a different type
576  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
577 
578  if( !plotPanel || plotPanel->GetType() != simType )
579  plotPanel = NewPlotPanel( simType );
580 
581  TRACE_DESC descriptor( *m_exporter, aName, aType, aParam );
582 
583  bool updated = false;
584  SIM_PLOT_TYPE xAxisType = GetXAxisType( simType );
585 
586  if( xAxisType == SPT_LIN_FREQUENCY || xAxisType == SPT_LOG_FREQUENCY )
587  {
588  int baseType = descriptor.GetType() & ~( SPT_AC_MAG | SPT_AC_PHASE );
589 
590  // Add two plots: magnitude & phase
591  TRACE_DESC mag_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_MAG ) );
592  TRACE_DESC phase_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_PHASE ) );
593 
594  updated |= updatePlot( mag_desc, plotPanel );
595  updated |= updatePlot( phase_desc, plotPanel );
596  }
597  else
598  {
599  updated = updatePlot( descriptor, plotPanel );
600  }
601 
602  if( updated )
603  {
605  }
606 }
607 
608 
609 void SIM_PLOT_FRAME::removePlot( const wxString& aPlotName, bool aErase )
610 {
611  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
612 
613  if( !plotPanel )
614  return;
615 
616  if( aErase )
617  {
618  auto& traceMap = m_plots[plotPanel].m_traces;
619  auto traceIt = traceMap.find( aPlotName );
620  wxASSERT( traceIt != traceMap.end() );
621  traceMap.erase( traceIt );
622  }
623 
624  wxASSERT( plotPanel->TraceShown( aPlotName ) );
625  plotPanel->DeleteTrace( aPlotName );
626  plotPanel->Fit();
627 
629  updateCursors();
630 }
631 
632 
634 {
636 }
637 
638 
639 bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* aPanel )
640 {
641  SIM_TYPE simType = m_exporter->GetSimType();
642  wxString spiceVector = m_exporter->GetSpiceVector( aDescriptor.GetName(),
643  aDescriptor.GetType(), aDescriptor.GetParam() );
644 
645  if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
646  {
647  // There is no plot to be shown
648  m_simulator->Command( wxString::Format( "print %s", spiceVector ).ToStdString() );
649 
650  return false;
651  }
652 
653  // First, handle the x axis
654  wxString xAxisName( m_simulator->GetXAxis( simType ) );
655 
656  if( xAxisName.IsEmpty() )
657  return false;
658 
659  auto data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() );
660  unsigned int size = data_x.size();
661 
662  if( data_x.empty() )
663  return false;
664 
665  SIM_PLOT_TYPE plotType = aDescriptor.GetType();
666  std::vector<double> data_y;
667 
668  // Now, Y axis data
669  switch( m_exporter->GetSimType() )
670  {
671  case ST_AC:
672  {
673  wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ),
674  "Cannot set both AC_PHASE and AC_MAG bits" );
675 
676  if( plotType & SPT_AC_MAG )
677  data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
678  else if( plotType & SPT_AC_PHASE )
679  data_y = m_simulator->GetPhasePlot( (const char*) spiceVector.c_str() );
680  else
681  wxASSERT_MSG( false, "Plot type missing AC_PHASE or AC_MAG bit" );
682  }
683  break;
684 
685  case ST_NOISE:
686  case ST_DC:
687  case ST_TRANSIENT:
688  {
689  data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
690  }
691  break;
692 
693  default:
694  wxASSERT_MSG( false, "Unhandled plot type" );
695  return false;
696  }
697 
698  if( data_y.size() != size )
699  return false;
700 
701  // If we did a two-source DC analysis, we need to split the resulting vector and add traces
702  // for each input step
703  SPICE_DC_PARAMS source1, source2;
704 
705  if( m_exporter->GetSimType() == ST_DC &&
706  m_exporter->ParseDCCommand( m_exporter->GetUsedSimCommand(), &source1, &source2 ) )
707  {
708  if( !source2.m_source.IsEmpty() )
709  {
710  // Source 1 is the inner loop, so lets add traces for each Source 2 (outer loop) step
711  SPICE_VALUE v = source2.m_vstart;
712  wxString name;
713 
714  size_t offset = 0;
715  size_t outer = ( size_t )( ( source2.m_vend - v ) / source2.m_vincrement ).ToDouble();
716  size_t inner = data_x.size() / ( outer + 1 );
717 
718  wxASSERT( data_x.size() % ( outer + 1 ) == 0 );
719 
720  for( size_t idx = 0; idx <= outer; idx++ )
721  {
722  name = wxString::Format( "%s (%s = %s V)", aDescriptor.GetTitle(),
723  source2.m_source, v.ToString() );
724 
725  std::vector<double> sub_x( data_x.begin() + offset,
726  data_x.begin() + offset + inner );
727  std::vector<double> sub_y( data_y.begin() + offset,
728  data_y.begin() + offset + inner );
729 
730  if( aPanel->AddTrace( name, inner,
731  sub_x.data(), sub_y.data(), aDescriptor.GetType() ) )
732  {
733  m_plots[aPanel].m_traces.insert( std::make_pair( name, aDescriptor ) );
734  }
735 
736  v = v + source2.m_vincrement;
737  offset += inner;
738  }
739 
740  return true;
741  }
742  }
743 
744  if( aPanel->AddTrace( aDescriptor.GetTitle(), size,
745  data_x.data(), data_y.data(), aDescriptor.GetType() ) )
746  {
747  m_plots[aPanel].m_traces.insert( std::make_pair( aDescriptor.GetTitle(), aDescriptor ) );
748  }
749 
750  return true;
751 }
752 
753 
755 {
756  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
757 
758  if( !plotPanel )
759  return;
760 
761  m_signals->ClearAll();
762 
763  wxSize size = m_signals->GetClientSize();
764  m_signals->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x );
765 
766  // Build an image list, to show the color of the corresponding trace
767  // in the plot panel
768  // This image list is used for trace and cursor lists
769  wxMemoryDC bmDC;
770  const int isize = bmDC.GetCharHeight();
771 
773  m_signalsIconColorList = new wxImageList( isize, isize, false );
774  else
775  m_signalsIconColorList->RemoveAll();
776 
777  for( const auto& trace : CurrentPlot()->GetTraces() )
778  {
779  wxBitmap bitmap( isize, isize );
780  bmDC.SelectObject( bitmap );
781  wxColour tcolor = trace.second->GetTraceColour();
782 
783  wxColour bgColor = m_signals->wxWindow::GetBackgroundColour();
784  bmDC.SetPen( wxPen( bgColor ) );
785  bmDC.SetBrush( wxBrush( bgColor ) );
786  bmDC.DrawRectangle( 0, 0, isize, isize ); // because bmDC.Clear() does not work in wxGTK
787 
788  bmDC.SetPen( wxPen( tcolor ) );
789  bmDC.SetBrush( wxBrush( tcolor ) );
790  bmDC.DrawRectangle( 0, isize / 4 + 1, isize, isize / 2 );
791 
792  bmDC.SelectObject( wxNullBitmap ); // Needed to initialize bitmap
793 
794  bitmap.SetMask( new wxMask( bitmap, *wxBLACK ) );
795  m_signalsIconColorList->Add( bitmap );
796  }
797 
798  if( bmDC.IsOk() )
799  {
800  bmDC.SetBrush( wxNullBrush );
801  bmDC.SetPen( wxNullPen );
802  }
803 
804  m_signals->SetImageList( m_signalsIconColorList, wxIMAGE_LIST_SMALL );
805 
806  // Fill the signals listctrl. Keep the order of names and
807  // the order of icon color identical, because the icons
808  // are also used in cursor list, and the color index is
809  // calculated from the trace name index
810  int imgidx = 0;
811 
812  for( const auto& trace : m_plots[plotPanel].m_traces )
813  {
814  m_signals->InsertItem( imgidx, trace.first, imgidx );
815  imgidx++;
816  }
817 }
818 
819 
821 {
822  wxQueueEvent( this, new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
823 }
824 
825 
827 {
828  const auto& spiceItems = m_exporter->GetSpiceItems();
829 
830  for( auto it = m_tuners.begin(); it != m_tuners.end(); /* iteration inside the loop */ )
831  {
832  const wxString& ref = (*it)->GetComponentName();
833 
834  if( std::find_if( spiceItems.begin(), spiceItems.end(), [&]( const SPICE_ITEM& item )
835  {
836  return item.m_refName == ref;
837  }) == spiceItems.end() )
838  {
839  // The component does not exist anymore, remove the associated tuner
840  TUNER_SLIDER* tuner = *it;
841  it = m_tuners.erase( it );
842  RemoveTuner( tuner, false );
843  }
844  else
845  {
846  ++it;
847  }
848  }
849 }
850 
851 
853 {
854  for( auto& tuner : m_tuners )
855  {
857  std::string command( "alter @" + tuner->GetSpiceName()
858  + "=" + tuner->GetValue().ToSpiceString() );
859 
860  m_simulator->Command( command );
861  }
862 }
863 
864 
865 bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath )
866 {
867  m_plots.clear();
868  m_plotNotebook->DeleteAllPages();
869 
870  wxTextFile file( aPath );
871 
872  if( !file.Open() )
873  return false;
874 
875  long plotsCount;
876 
877  if( !file.GetFirstLine().ToLong( &plotsCount ) ) // GetFirstLine instead of GetNextLine
878  return false;
879 
880  for( long i = 0; i < plotsCount; ++i )
881  {
882  long plotType, tracesCount;
883 
884  if( !file.GetNextLine().ToLong( &plotType ) )
885  return false;
886 
887  SIM_PLOT_PANEL* plotPanel = NewPlotPanel( (SIM_TYPE) plotType );
888  m_plots[plotPanel].m_simCommand = file.GetNextLine();
889  StartSimulation();
890 
891  // Perform simulation, so plots can be added with values
892  do
893  {
894  wxThread::This()->Sleep( 50 );
895  }
896  while( IsSimulationRunning() );
897 
898  if( !file.GetNextLine().ToLong( &tracesCount ) )
899  return false;
900 
901  for( long j = 0; j < tracesCount; ++j )
902  {
903  long traceType;
904  wxString name, param;
905 
906  if( !file.GetNextLine().ToLong( &traceType ) )
907  return false;
908 
909  name = file.GetNextLine();
910  param = file.GetNextLine();
911 
912  if( name.IsEmpty() || param.IsEmpty() )
913  return false;
914 
915  addPlot( name, (SIM_PLOT_TYPE) traceType, param );
916  }
917  }
918 
919  return true;
920 }
921 
922 
923 bool SIM_PLOT_FRAME::saveWorkbook( const wxString& aPath )
924 {
925 
926  wxString savePath = aPath;
927 
928  if( !savePath.Lower().EndsWith(".wbk"))
929  {
930  savePath += ".wbk";
931  };
932 
933 
934  wxTextFile file( savePath );
935 
936  if( file.Exists() )
937  {
938  if( !file.Open() )
939  return false;
940 
941  file.Clear();
942  }
943  else
944  {
945  file.Create();
946  }
947 
948  file.AddLine( wxString::Format( "%lu", m_plots.size() ) );
949 
950  for( const auto& plot : m_plots )
951  {
952  file.AddLine( wxString::Format( "%d", plot.first->GetType() ) );
953  file.AddLine( plot.second.m_simCommand );
954  file.AddLine( wxString::Format( "%lu", plot.second.m_traces.size() ) );
955 
956  for( const auto& trace : plot.second.m_traces )
957  {
958  file.AddLine( wxString::Format( "%d", trace.second.GetType() ) );
959  file.AddLine( trace.second.GetName() );
960  file.AddLine( trace.second.GetParam() );
961  }
962  }
963 
964  bool res = file.Write();
965  file.Close();
966 
967  return res;
968 }
969 
970 
972 {
973  switch( aType )
974  {
975  case ST_AC:
976  return SPT_LIN_FREQUENCY;
978 
979  case ST_DC:
980  return SPT_SWEEP;
981 
982  case ST_TRANSIENT:
983  return SPT_TIME;
984 
985  default:
986  wxASSERT_MSG( false, "Unhandled simulation type" );
987  return (SIM_PLOT_TYPE) 0;
988  }
989 }
990 
991 
992 void SIM_PLOT_FRAME::menuNewPlot( wxCommandEvent& aEvent )
993 {
994  SIM_TYPE type = m_exporter->GetSimType();
995 
996  if( SIM_PLOT_PANEL::IsPlottable( type ) )
997  {
998  SIM_PLOT_PANEL* prevPlot = CurrentPlot();
999  SIM_PLOT_PANEL* newPlot = NewPlotPanel( type );
1000 
1001  // If the previous plot had the same type, copy the simulation command
1002  if( prevPlot )
1003  m_plots[newPlot].m_simCommand = m_plots[prevPlot].m_simCommand;
1004  }
1005 }
1006 
1007 
1008 void SIM_PLOT_FRAME::menuOpenWorkbook( wxCommandEvent& event )
1009 {
1010  wxFileDialog openDlg( this, _( "Open simulation workbook" ), m_savedWorkbooksPath, "",
1011  WorkbookFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
1012 
1013  if( openDlg.ShowModal() == wxID_CANCEL )
1014  return;
1015 
1016  m_savedWorkbooksPath = openDlg.GetDirectory();
1017 
1018  if( !loadWorkbook( openDlg.GetPath() ) )
1019  DisplayError( this, _( "There was an error while opening the workbook file" ) );
1020 }
1021 
1022 
1023 void SIM_PLOT_FRAME::menuSaveWorkbook( wxCommandEvent& event )
1024 {
1025  if( !CurrentPlot() )
1026  return;
1027 
1028  wxFileDialog saveDlg( this, _( "Save Simulation Workbook" ), m_savedWorkbooksPath, "",
1029  WorkbookFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1030 
1031  if( saveDlg.ShowModal() == wxID_CANCEL )
1032  return;
1033 
1034  m_savedWorkbooksPath = saveDlg.GetDirectory();
1035 
1036  if( !saveWorkbook( saveDlg.GetPath() ) )
1037  DisplayError( this, _( "There was an error while saving the workbook file" ) );
1038 }
1039 
1040 
1041 void SIM_PLOT_FRAME::menuSaveImage( wxCommandEvent& event )
1042 {
1043  if( !CurrentPlot() )
1044  return;
1045 
1046  wxFileDialog saveDlg( this, _( "Save Plot as Image" ), "", "",
1047  PngFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1048 
1049  if( saveDlg.ShowModal() == wxID_CANCEL )
1050  return;
1051 
1052  CurrentPlot()->SaveScreenshot( saveDlg.GetPath(), wxBITMAP_TYPE_PNG );
1053 }
1054 
1055 
1056 void SIM_PLOT_FRAME::menuSaveCsv( wxCommandEvent& event )
1057 {
1058  if( !CurrentPlot() )
1059  return;
1060 
1061  const wxChar SEPARATOR = ';';
1062 
1063  wxFileDialog saveDlg( this, _( "Save Plot Data" ), "", "",
1064  CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1065 
1066  if( saveDlg.ShowModal() == wxID_CANCEL )
1067  return;
1068 
1069  wxFile out( saveDlg.GetPath(), wxFile::write );
1070  bool timeWritten = false;
1071 
1072  for( const auto& t : CurrentPlot()->GetTraces() )
1073  {
1074  const TRACE* trace = t.second;
1075 
1076  if( !timeWritten )
1077  {
1078  out.Write( wxString::Format( "Time%c", SEPARATOR ) );
1079 
1080  for( double v : trace->GetDataX() )
1081  out.Write( wxString::Format( "%f%c", v, SEPARATOR ) );
1082 
1083  out.Write( "\r\n" );
1084  timeWritten = true;
1085  }
1086 
1087  out.Write( wxString::Format( "%s%c", t.first, SEPARATOR ) );
1088 
1089  for( double v : trace->GetDataY() )
1090  out.Write( wxString::Format( "%f%c", v, SEPARATOR ) );
1091 
1092  out.Write( "\r\n" );
1093  }
1094 
1095  out.Close();
1096 }
1097 
1098 
1099 void SIM_PLOT_FRAME::menuZoomIn( wxCommandEvent& event )
1100 {
1101  if( CurrentPlot() )
1102  CurrentPlot()->ZoomIn();
1103 }
1104 
1105 
1106 void SIM_PLOT_FRAME::menuZoomOut( wxCommandEvent& event )
1107 {
1108  if( CurrentPlot() )
1109  CurrentPlot()->ZoomOut();
1110 }
1111 
1112 
1113 void SIM_PLOT_FRAME::menuZoomFit( wxCommandEvent& event )
1114 {
1115  if( CurrentPlot() )
1116  CurrentPlot()->Fit();
1117 }
1118 
1119 
1120 void SIM_PLOT_FRAME::menuShowGrid( wxCommandEvent& event )
1121 {
1122  SIM_PLOT_PANEL* plot = CurrentPlot();
1123 
1124  if( plot )
1125  plot->ShowGrid( !plot->IsGridShown() );
1126 }
1127 
1128 
1129 void SIM_PLOT_FRAME::menuShowGridUpdate( wxUpdateUIEvent& event )
1130 {
1131  SIM_PLOT_PANEL* plot = CurrentPlot();
1132 
1133  event.Check( plot ? plot->IsGridShown() : false );
1134 }
1135 
1136 
1137 void SIM_PLOT_FRAME::menuShowLegend( wxCommandEvent& event )
1138 {
1139  SIM_PLOT_PANEL* plot = CurrentPlot();
1140 
1141  if( plot )
1142  plot->ShowLegend( !plot->IsLegendShown() );
1143 }
1144 
1145 
1146 void SIM_PLOT_FRAME::menuShowLegendUpdate( wxUpdateUIEvent& event )
1147 {
1148  SIM_PLOT_PANEL* plot = CurrentPlot();
1149  event.Check( plot ? plot->IsLegendShown() : false );
1150 }
1151 
1152 
1153 void SIM_PLOT_FRAME::menuShowDotted( wxCommandEvent& event )
1154 {
1155  SIM_PLOT_PANEL* plot = CurrentPlot();
1156 
1157  if( plot )
1158  plot->SetDottedCurrentPhase( !plot->GetDottedCurrentPhase() );
1159 }
1160 
1161 
1162 void SIM_PLOT_FRAME::menuShowDottedUpdate( wxUpdateUIEvent& event )
1163 {
1164  SIM_PLOT_PANEL* plot = CurrentPlot();
1165 
1166  event.Check( plot ? plot->GetDottedCurrentPhase() : false );
1167 }
1168 
1169 
1170 void SIM_PLOT_FRAME::menuWhiteBackground( wxCommandEvent& event )
1171 {
1173 
1174  // Rebuild the color list to plot traces
1176 
1177  // Now send changes to all SIM_PLOT_PANEL
1178  for( size_t page = 0; page < m_plotNotebook->GetPageCount(); page++ )
1179  {
1180  wxWindow* curPage = m_plotNotebook->GetPage( page );
1181 
1182  if( curPage == m_welcomePanel )
1183  continue;
1184 
1185  static_cast<SIM_PLOT_PANEL*>( curPage )->UpdatePlotColors();
1186  }
1187 }
1188 
1189 
1190 void SIM_PLOT_FRAME::onPlotClose( wxAuiNotebookEvent& event )
1191 {
1192  int idx = event.GetSelection();
1193 
1194  if( idx == wxNOT_FOUND )
1195  return;
1196 
1197  SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetPage( idx ) );
1198 
1199  if( !plotPanel )
1200  return;
1201 
1202  m_plots.erase( plotPanel );
1203  updateSignalList();
1204  updateCursors();
1205 }
1206 
1207 
1208 void SIM_PLOT_FRAME::onPlotChanged( wxAuiNotebookEvent& event )
1209 {
1210  updateSignalList();
1211  updateCursors();
1212 }
1213 
1214 
1215 void SIM_PLOT_FRAME::onSignalDblClick( wxMouseEvent& event )
1216 {
1217  // Remove signal from the plot panel when double clicked
1218  long idx = m_signals->GetFocusedItem();
1219 
1220  if( idx != wxNOT_FOUND )
1221  removePlot( m_signals->GetItemText( idx, 0 ) );
1222 }
1223 
1224 
1225 void SIM_PLOT_FRAME::onSignalRClick( wxListEvent& event )
1226 {
1227  int idx = event.GetIndex();
1228 
1229  if( idx != wxNOT_FOUND )
1230  m_signals->Select( idx );
1231 
1232  idx = m_signals->GetFirstSelected();
1233 
1234  if( idx != wxNOT_FOUND )
1235  {
1236  const wxString& netName = m_signals->GetItemText( idx, 0 );
1237  SIGNAL_CONTEXT_MENU ctxMenu( netName, this );
1238  m_signals->PopupMenu( &ctxMenu );
1239  }
1240 }
1241 
1242 
1243 void SIM_PLOT_FRAME::onSimulate( wxCommandEvent& event )
1244 {
1245  if( IsSimulationRunning() )
1246  StopSimulation();
1247  else
1248  StartSimulation();
1249 }
1250 
1251 
1252 void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
1253 {
1254  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1255 
1256  // Initial processing is required to e.g. display a list of power sources
1258 
1259  if( !m_exporter->ProcessNetlist( NET_ALL_FLAGS ) )
1260  {
1261  DisplayError( this, _( "There were errors during netlist export, aborted." ) );
1262  return;
1263  }
1264 
1265  if( !m_settingsDlg )
1266  m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
1267 
1268  if( plotPanel )
1269  m_settingsDlg->SetSimCommand( m_plots[plotPanel].m_simCommand );
1270 
1272 
1273  if( m_settingsDlg->ShowModal() == wxID_OK )
1274  {
1275  wxString newCommand = m_settingsDlg->GetSimCommand();
1276  SIM_TYPE newSimType = NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( newCommand );
1277 
1278  // If it is a new simulation type, open a new plot
1279  if( !plotPanel || ( plotPanel && plotPanel->GetType() != newSimType ) )
1280  {
1281  plotPanel = NewPlotPanel( newSimType );
1282  }
1283 
1284  m_plots[plotPanel].m_simCommand = newCommand;
1285  }
1286 }
1287 
1288 
1289 void SIM_PLOT_FRAME::onAddSignal( wxCommandEvent& event )
1290 {
1291  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1292 
1293  if( !plotPanel || !m_exporter || plotPanel->GetType() != m_exporter->GetSimType() )
1294  {
1295  DisplayInfoMessage( this, _( "You need to run simulation first." ) );
1296  return;
1297  }
1298 
1299  DIALOG_SIGNAL_LIST dialog( this, m_exporter.get() );
1300  dialog.ShowModal();
1301 }
1302 
1303 
1304 void SIM_PLOT_FRAME::onProbe( wxCommandEvent& event )
1305 {
1306  if( m_schematicFrame == NULL )
1307  return;
1308 
1310  m_schematicFrame->Raise();
1311 }
1312 
1313 
1314 void SIM_PLOT_FRAME::onTune( wxCommandEvent& event )
1315 {
1316  if( m_schematicFrame == NULL )
1317  return;
1318 
1320  m_schematicFrame->Raise();
1321 }
1322 
1323 void SIM_PLOT_FRAME::onShowNetlist( wxCommandEvent& event )
1324 {
1325  class NETLIST_VIEW_DIALOG : public wxDialog
1326  {
1327  public:
1328  enum
1329  {
1330  MARGIN_LINE_NUMBERS
1331  };
1332 
1333  void onClose( wxCloseEvent& evt )
1334  {
1335  EndModal( GetReturnCode() );
1336  }
1337 
1338  NETLIST_VIEW_DIALOG(wxWindow* parent, wxString source) :
1339  wxDialog(parent, wxID_ANY, "SPICE Netlist",
1340  wxDefaultPosition, wxSize(1500,900),
1341  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1342  {
1343  wxStyledTextCtrl* text = new wxStyledTextCtrl( this, wxID_ANY );
1344 
1345  text->SetMarginWidth( MARGIN_LINE_NUMBERS, 50 );
1346  text->StyleSetForeground( wxSTC_STYLE_LINENUMBER, wxColour( 75, 75, 75 ) );
1347  text->StyleSetBackground( wxSTC_STYLE_LINENUMBER, wxColour( 220, 220, 220 ) );
1348  text->SetMarginType( MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER );
1349 
1350  text->SetWrapMode( wxSTC_WRAP_WORD );
1351 
1352  text->SetText( source );
1353 
1354  text->StyleClearAll();
1355  text->SetLexer( wxSTC_LEX_SPICE );
1356 
1357  wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
1358  sizer->Add( text, 1, wxEXPAND );
1359  SetSizer( sizer );
1360 
1361  Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( NETLIST_VIEW_DIALOG::onClose ), NULL,
1362  this );
1363  }
1364  };
1365 
1366  if( m_schematicFrame == NULL || m_simulator == NULL )
1367  return;
1368 
1369  NETLIST_VIEW_DIALOG dlg( this, m_simulator->GetNetlist() );
1370  dlg.ShowModal();
1371 }
1372 
1373 
1374 void SIM_PLOT_FRAME::onClose( wxCloseEvent& aEvent )
1375 {
1376  SaveSettings( config() );
1377 
1378  if( IsSimulationRunning() )
1379  m_simulator->Stop();
1380 
1381  // Cancel a running simProbe or simTune tool
1383 
1384  Destroy();
1385 }
1386 
1387 
1388 void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
1389 {
1390  wxSize size = m_cursors->GetClientSize();
1391  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1392  m_cursors->ClearAll();
1393 
1394  if( !plotPanel )
1395  return;
1396 
1398  m_cursors->SetImageList(m_signalsIconColorList, wxIMAGE_LIST_SMALL);
1399 
1400  // Fill the signals listctrl
1401  m_cursors->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x / 2 );
1402  const long X_COL = m_cursors->AppendColumn( plotPanel->GetLabelX(), wxLIST_FORMAT_LEFT, size.x / 4 );
1403 
1404  wxString labelY1 = plotPanel->GetLabelY1();
1405  wxString labelY2 = plotPanel->GetLabelY2();
1406  wxString labelY;
1407 
1408  if( !labelY2.IsEmpty() )
1409  labelY = labelY1 + " / " + labelY2;
1410  else
1411  labelY = labelY1;
1412 
1413  const long Y_COL = m_cursors->AppendColumn( labelY, wxLIST_FORMAT_LEFT, size.x / 4 );
1414 
1415  // Update cursor values
1416  int itemidx = 0;
1417  for( const auto& trace : plotPanel->GetTraces() )
1418  {
1419  if( CURSOR* cursor = trace.second->GetCursor() )
1420  {
1421  // Find the right icon color in list.
1422  // It is the icon used in m_signals list for the same trace
1423  long iconColor = m_signals->FindItem( -1, trace.first );
1424 
1425  const wxRealPoint coords = cursor->GetCoords();
1426  long idx = m_cursors->InsertItem( itemidx++, trace.first, iconColor );
1427  m_cursors->SetItem( idx, X_COL, SPICE_VALUE( coords.x ).ToSpiceString() );
1428  m_cursors->SetItem( idx, Y_COL, SPICE_VALUE( coords.y ).ToSpiceString() );
1429  }
1430  }
1431 }
1432 
1433 
1434 void SIM_PLOT_FRAME::onSimStarted( wxCommandEvent& aEvent )
1435 {
1436  m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_stop_xpm ) );
1437  SetCursor( wxCURSOR_ARROWWAIT );
1438 }
1439 
1440 
1441 void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
1442 {
1443  m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_run_xpm ) );
1444  SetCursor( wxCURSOR_ARROW );
1445 
1446  SIM_TYPE simType = m_exporter->GetSimType();
1447 
1448  if( simType == ST_UNKNOWN )
1449  return;
1450 
1451  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1452 
1453  if( !plotPanel || plotPanel->GetType() != simType )
1454  plotPanel = NewPlotPanel( simType );
1455 
1456  if( IsSimulationRunning() )
1457  return;
1458 
1459  // If there are any signals plotted, update them
1460  if( SIM_PLOT_PANEL::IsPlottable( simType ) )
1461  {
1462  TRACE_MAP& traceMap = m_plots[plotPanel].m_traces;
1463 
1464  for( auto it = traceMap.begin(); it != traceMap.end(); /* iteration occurs in the loop */)
1465  {
1466  if( !updatePlot( it->second, plotPanel ) )
1467  {
1468  removePlot( it->first, false );
1469  it = traceMap.erase( it ); // remove a plot that does not exist anymore
1470  }
1471  else
1472  {
1473  ++it;
1474  }
1475  }
1476 
1477  updateSignalList();
1478  plotPanel->UpdateAll();
1479  plotPanel->ResetScales();
1480  }
1481  else
1482  {
1484  for( const auto& net : m_exporter->GetNetIndexMap() )
1485  {
1486  int node = net.second;
1487 
1488  if( node > 0 )
1489  m_simulator->Command( wxString::Format( "print v(%d)", node ).ToStdString() );
1490  }
1491  }
1492 }
1493 
1494 
1495 void SIM_PLOT_FRAME::onSimUpdate( wxCommandEvent& aEvent )
1496 {
1497  if( IsSimulationRunning() )
1498  StopSimulation();
1499 
1500  if( CurrentPlot() != m_lastSimPlot )
1501  {
1502  // We need to rerun simulation, as the simulator currently stores
1503  // results for another plot
1504  StartSimulation();
1505  }
1506  else
1507  {
1508  // Incremental update
1509  m_simConsole->Clear();
1510  // Do not export netlist, it is already stored in the simulator
1511  applyTuners();
1512  m_simulator->Run();
1513  }
1514 }
1515 
1516 
1517 void SIM_PLOT_FRAME::onSimReport( wxCommandEvent& aEvent )
1518 {
1519  m_simConsole->AppendText( aEvent.GetString() + "\n" );
1520  m_simConsole->SetInsertionPointEnd();
1521 }
1522 
1523 
1525  SIM_PLOT_FRAME* aPlotFrame )
1526  : m_signal( aSignal ), m_plotFrame( aPlotFrame )
1527 {
1529 
1530  AddMenuItem( this, HIDE_SIGNAL, _( "Hide Signal" ),
1531  _( "Erase the signal from plot screen" ),
1532  KiBitmap( delete_xpm ) );
1533 
1534  TRACE* trace = plot->GetTrace( m_signal );
1535 
1536  if( trace->HasCursor() )
1537  AddMenuItem( this, HIDE_CURSOR, _( "Hide Cursor" ), KiBitmap( pcb_target_xpm ) );
1538  else
1539  AddMenuItem( this, SHOW_CURSOR, _( "Show Cursor" ), KiBitmap( pcb_target_xpm ) );
1540 
1541  Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ), NULL, this );
1542 }
1543 
1544 
1546 {
1547  SIM_PLOT_PANEL* plot = m_plotFrame->CurrentPlot();
1548 
1549  switch( aEvent.GetId() )
1550  {
1551  case HIDE_SIGNAL:
1552  m_plotFrame->removePlot( m_signal );
1553  break;
1554 
1555  case SHOW_CURSOR:
1556  plot->EnableCursor( m_signal, true );
1557  break;
1558 
1559  case HIDE_CURSOR:
1560  plot->EnableCursor( m_signal, false );
1561  break;
1562  }
1563 }
1564 
1565 wxDEFINE_EVENT( EVT_SIM_UPDATE, wxCommandEvent );
1566 wxDEFINE_EVENT( EVT_SIM_REPORT, wxCommandEvent );
1567 
1568 wxDEFINE_EVENT( EVT_SIM_STARTED, wxCommandEvent );
1569 wxDEFINE_EVENT( EVT_SIM_FINISHED, wxCommandEvent );
const BITMAP_OPAQUE simulator_xpm[1]
Definition: simulator.cpp:52
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:239
void onAddSignal(wxCommandEvent &event)
#define ID_MENU_PROBE_SIGNALS
const BITMAP_OPAQUE swap_layer_xpm[1]
Definition: swap_layer.cpp:33
#define ID_SAVE_AS_IMAGE
void ZoomOut(const wxPoint &centerPoint=wxDefaultPosition)
Zoom out current view and refresh display.
Definition: mathplot.cpp:2322
bool IsLegendShown() const
bool saveWorkbook(const wxString &aPath)
Saves plot settings to a file.
wxString ToSpiceString() const
Returns string value in Spice format (e.g.
void SetKiway(wxWindow *aDest, KIWAY *aKiway)
Function SetKiway.
wxToolBarToolBase * m_toolProbe
void setIconsForMenuItems()
Give icons to menuitems of the main menubar.
void onSimStarted(wxCommandEvent &aEvent)
KIWAY & Kiway() const
Function Kiway returns a reference to the KIWAY that this object has an opportunity to participate in...
Definition: kiway_holder.h:56
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
NETLIST_OBJECT_LIST * BuildNetListBase(bool updateStatusText=true)
Create a flat list which stores all connected objects.
void RemoveTuner(TUNER_SLIDER *aTuner, bool aErase=true)
Removes an existing tuner.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Function Report is a pure virtual function to override in the derived object.
KIGFX::VIEW_CONTROLS * GetViewControls() const
Function GetViewControls() Returns a pointer to the VIEW_CONTROLS instance used in the panel.
bool GetPlotBgOpt() const
const BITMAP_OPAQUE pcb_target_xpm[1]
Definition: pcb_target.cpp:42
wxImageList * m_signalsIconColorList
imagelists uset to add a small coloured icon to signal names and cursors name, the same color as the ...
SIM_PLOT_TYPE operator|(SIM_PLOT_TYPE aFirst, SIM_PLOT_TYPE aSecond)
void onPlotChanged(wxAuiNotebookEvent &event) override
PNG memory record (file in memory).
Definition: bitmap_def.h:29
SIM_PLOT_FRAME * m_parent
const BITMAP_OPAQUE add_dashed_line_xpm[1]
void EnableMouseWheelPan(bool enabled)
Enable/disable trackpad friendly panning (2-axis scroll wheel)
Definition: mathplot.h:1215
void menuWhiteBackground(wxCommandEvent &event) override
void onSignalDblClick(wxMouseEvent &event) override
bool SetSimCommand(const wxString &aCommand)
virtual void SaveSettings(APP_SETTINGS_BASE *aCfg)
Saves common frame parameters to a configuration data file.
This file is part of the common library.
wxSplitterWindow * m_splitterSignals
const wxString & GetTitle() const
bool HasCursor() const
SEVERITY
Definition: ui_common.h:45
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Function AddMenuItem is an inline helper function to create and insert a menu item with an icon into ...
Definition: bitmap.cpp:250
void ShowLegend(bool aEnable)
int m_splitterPlotAndConsoleSashPosition
void menuZoomFit(wxCommandEvent &event) override
Structure to represent a schematic component in the Spice simulation.
const std::vector< double > & GetDataX() const
static bool IsPlottable(SIM_TYPE aSimType)
std::list< TUNER_SLIDER * > m_tuners
List of currently displayed tuners
static TOOL_ACTION cancelInteractive
Definition: actions.h:65
SIM_PLOT_TYPE GetType() const
wxString m_title
Title displayed in the signal list/plot legend
Class SIM_PLOT_FRAME_BASE.
const BITMAP_OPAQUE directory_browser_xpm[1]
const BITMAP_OPAQUE text_xpm[1]
Definition: text.cpp:27
bool IsGridShown() const
bool GetDottedCurrentPhase() const
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Function RunAction() Runs the specified action.
Definition: tool_manager.h:139
WINDOW_SETTINGS * GetWindowSettings(APP_SETTINGS_BASE *aCfg) override
Returns a pointer to the window settings for this frame.
void menuNewPlot(wxCommandEvent &aEvent) override
void applyTuners()
Applies component values specified using tunder sliders to the current netlist.
const BITMAP_OPAQUE zoom_fit_in_page_xpm[1]
void AddVoltagePlot(const wxString &aNetName)
Adds a voltage plot for a given net name.
void AddCurrentPlot(const wxString &aDeviceName, const wxString &aParam)
Adds a current plot for a particular device.
#define ID_MENU_WHITE_BG
Trace descriptor class
Schematic editor (Eeschema) main window.
wxString CsvFileWildcard()
void onSimUpdate(wxCommandEvent &aEvent)
void menuOpenWorkbook(wxCommandEvent &event) override
void SetDottedCurrentPhase(bool aEnable)
void menuShowGrid(wxCommandEvent &event) override
void menuZoomIn(wxCommandEvent &event) override
REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:62
void menuZoomOut(wxCommandEvent &event) override
SIM_THREAD_REPORTER * m_reporter
void OnSimStateChange(SPICE_SIMULATOR *aObject, SIM_STATE aNewState) override
std::shared_ptr< SPICE_SIMULATOR > m_simulator
bool updatePlot(const TRACE_DESC &aDescriptor, SIM_PLOT_PANEL *aPanel)
Updates plot in a particular SIM_PLOT_PANEL.
TRACE * GetTrace(const wxString &aName) const
#define ID_SAVE_AS_CSV
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:102
Field Reference of part, i.e. "IC21".
wxString GetLabelY1() const
void updateCursors()
Updates the cursor values list.
wxSplitterWindow * m_splitterTuneValues
static SIM_TYPE CommandToSimType(const wxString &aCmd)
Returns simulation type basing on a simulation command directive.
SIM_PLOT_TYPE GetXAxisType(SIM_TYPE aType) const
Returns X axis for a given simulation type.
void menuSaveCsv(wxCommandEvent &event) override
void onPlotClose(wxAuiNotebookEvent &event) override
const wxString & GetParam() const
wxAuiNotebook * m_plotNotebook
SCH_EDIT_FRAME * m_schematicFrame
SCH_FIELD * GetField(int aFieldNdx) const
Returns a field in this symbol.
bool DeleteTrace(const wxString &aName)
void UpdateAll()
Refresh display.
Definition: mathplot.cpp:2836
void EnableCursor(const wxString &aName, bool aEnable)
Toggles cursor for a particular trace.
SIM_STATE
const wxString & GetSimCommand() const
void setSubWindowsSashSize()
const BITMAP_OPAQUE zoom_out_xpm[1]
Definition: zoom_out.cpp:58
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:80
Stores the common settings that are saved and loaded for each window / frame.
Definition: app_settings.h:52
wxString ToString() const
Returns string value as when converting double to string (e.g.
const BITMAP_OPAQUE sim_add_signal_xpm[1]
std::map< SIM_PLOT_PANEL *, PLOT_INFO > m_plots
Map of plot panels and associated data
SIM_PLOT_PANEL * m_lastSimPlot
Panel that was used as the most recent one for simulations
wxString GetLabelX() const
void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmap &aImage)
Add a bitmap to a menuitem.
Definition: bitmap.cpp:223
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
Definition: app_settings.h:75
#define NULL
VTBL_ENTRY KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=NULL)
Function Player returns the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:341
#define ID_MENU_SHOW_NETLIST
wxString GetLabelY2() const
static TOOL_ACTION simTune
Definition: ee_actions.h:199
Helper class to handle Spice way of expressing values (e.g. 10.5 Meg)
Definition: spice_value.h:32
wxColor GetPlotColor(int aColorId)
wxSplitterWindow * m_splitterLeftRight
void ZoomIn(const wxPoint &centerPoint=wxDefaultPosition)
Zoom into current view and refresh display.
Definition: mathplot.cpp:2259
wxMenuItem * m_runSimulation
#define ID_MENU_TUNE_SIGNALS
void onSimReport(wxCommandEvent &aEvent)
const BITMAP_OPAQUE exit_xpm[1]
Definition: exit.cpp:63
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
SIM_TYPE
Possible simulation types
Definition: sim_types.h:29
std::vector< wxColour > m_colorList
The color list to draw traces, bg, fg, axis...
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void onSimFinished(wxCommandEvent &aEvent)
Interface to receive simulation updates from SPICE_SIMULATOR class.
void fillDefaultColorList(bool aWhiteBg)
Fills m_colorList by a default set of colors.
const BITMAP_OPAQUE grid_xpm[1]
Definition: grid.cpp:21
const BITMAP_OPAQUE export_xpm[1]
Definition: export.cpp:75
bool AddTrace(const wxString &aName, int aPoints, const double *aX, const double *aY, SIM_PLOT_TYPE aFlags)
Definition of file extensions used in Kicad.
void onShowNetlist(wxCommandEvent &event)
SIM_THREAD_REPORTER(SIM_PLOT_FRAME *aParent)
void menuShowLegendUpdate(wxUpdateUIEvent &event) override
KIWAY is a minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the s...
Definition: kiway.h:273
wxToolBarToolBase * m_toolAddSignals
Subclass of SIM_PLOT_FRAME_BASE, which is generated by wxFormBuilder.
int m_splitterTuneValuesSashPosition
void SaveSettings(APP_SETTINGS_BASE *aCfg) override
Saves common frame parameters to a configuration data file.
static std::shared_ptr< SPICE_SIMULATOR > CreateInstance(const std::string &aName)
Creates a simulator instance of particular type (currently only ngspice is handled)
TRACE_DESC(const NETLIST_EXPORTER_PSPICE_SIM &aExporter, const wxString &aName, SIM_PLOT_TYPE aType, const wxString &aParam)
const std::string & GetString()
Definition: richio.h:475
void onSignalRClick(wxListEvent &event) override
void ResetScales()
Resets scale ranges to fit the current traces
bool IsSimulationRunning()
#define ID_MENU_SHOW_LEGEND
void menuShowLegend(wxCommandEvent &event) override
void ShowGrid(bool aEnable)
void onTune(wxCommandEvent &event)
wxToolBarToolBase * m_toolSimulate
void menuSaveImage(wxCommandEvent &event) override
#define ID_MENU_SET_SIMUL
const BITMAP_OPAQUE directory_xpm[1]
Definition: directory.cpp:30
wxString PngFileWildcard()
Implementing SIM_PLOT_FRAME_BASE.
bool loadWorkbook(const wxString &aPath)
Loads plot settings from a file.
void SetNetlistExporter(NETLIST_EXPORTER_PSPICE_SIM *aExporter)
static wxString m_savedWorkbooksPath
A string to store the path of saved workbooks during a session
void onCursorUpdate(wxCommandEvent &aEvent)
DIALOG_SIM_SETTINGS * m_settingsDlg
void Fit() override
Set view to fit global bounding box of all plot layers and refresh display.
Definition: mathplot.cpp:2014
const wxString & GetName() const
const wxString What() const
Definition: ki_exception.h:58
const BITMAP_OPAQUE sim_probe_xpm[1]
Definition: sim_probe.cpp:52
#define ID_MENU_ADD_SIGNAL
const BITMAP_OPAQUE sim_stop_xpm[1]
Definition: sim_stop.cpp:20
std::unique_ptr< NETLIST_EXPORTER_PSPICE_SIM > m_exporter
void updateNetlistExporter()
Reloads the current schematic for the netlist exporter.
see class PGM_BASE
void onSimulate(wxCommandEvent &event)
const BITMAP_OPAQUE sim_tune_xpm[1]
Definition: sim_tune.cpp:39
const char * name
Definition: DXF_plotter.cpp:60
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
SIGNAL_CONTEXT_MENU(const wxString &aSignal, SIM_PLOT_FRAME *aPlotFrame)
void onProbe(wxCommandEvent &event)
#define ID_MENU_SHOW_GRID
virtual bool IsMousewheelPanEnabled() const
Function IsMousewheelPanEnabled()
#define _(s)
Definition: 3d_actions.cpp:33
void updateSignalList()
Updates the list of currently plotted signals.
#define ID_MENU_DOTTED
const BITMAP_OPAQUE netlist_xpm[1]
static wxString GetSpiceField(SPICE_FIELD aField, SCH_COMPONENT *aComponent, unsigned aCtl)
Retrieves either the requested field value or the default value.
SCH_COMPONENT describes a real schematic component.
Definition: sch_component.h:99
int m_splitterSignalsSashPosition
const BITMAP_OPAQUE sim_run_xpm[1]
Definition: sim_run.cpp:35
SIM_PLOT_PANEL * CurrentPlot() const
Returns the currently opened plot panel (or NULL if there is none).
std::map< wxString, TRACE_DESC > TRACE_MAP
void menuShowDotted(wxCommandEvent &event) override
wxDEFINE_EVENT(EVT_SIM_UPDATE, wxCommandEvent)
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
SIM_TYPE GetType() const
const std::vector< double > & GetDataY() const
int m_splitterLeftRightSashPosition
void menuShowDottedUpdate(wxUpdateUIEvent &event) override
static TOOL_ACTION simProbe
Definition: ee_actions.h:198
const BITMAP_OPAQUE sim_settings_xpm[1]
const NETLIST_EXPORTER_PSPICE_SIM * GetExporter() const
Returns the netlist exporter object used for simulations.
void removePlot(const wxString &aPlotName, bool aErase=true)
Removes a plot with a specific title.
Custom widget to handle quick component values modification and simulation on the fly.
Definition: tuner_slider.h:40
void AddTuner(SCH_COMPONENT *aComponent)
Adds a tuner for a component.
wxString WorkbookFileWildcard()
bool SaveScreenshot(const wxString &filename, wxBitmapType type=wxBITMAP_TYPE_BMP, wxSize imageSize=wxDefaultSize, bool fit=false)
Draw the window on a wxBitmap, then save it to a file.
Definition: mathplot.cpp:3066
const std::map< wxString, TRACE * > & GetTraces() const
wxSplitterWindow * m_splitterPlotAndConsole
void menuShowGridUpdate(wxUpdateUIEvent &event) override
virtual APP_SETTINGS_BASE * config()
Returns the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
bool HasMessage() const override
Function HasMessage Returns true if the reporter client is non-empty.
void menuSaveWorkbook(wxCommandEvent &event) override
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition: confirm.cpp:267
STRING_FORMATTER implements OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:445
SIM_PLOT_TYPE
Possible plot types
Definition: sim_types.h:35
#define ID_MENU_RUN_SIM
void addPlot(const wxString &aName, SIM_PLOT_TYPE aType, const wxString &aParam)
Adds a new plot to the current panel.
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:123
void onSettings(wxCommandEvent &event)
void onClose(wxCloseEvent &aEvent)
void LoadSettings(APP_SETTINGS_BASE *aCfg) override
Load common frame parameters from a configuration file.
wxToolBarToolBase * m_toolTune
void onMenuEvent(wxMenuEvent &aEvent)
SIM_PLOT_PANEL * NewPlotPanel(SIM_TYPE aSimType)
Creates a new plot panel for a given simulation type and adds it to the main notebook.
bool TraceShown(const wxString &aName) const
Cursor attached to a trace to follow its values:
virtual void LoadSettings(APP_SETTINGS_BASE *aCfg)
Load common frame parameters from a configuration file.
const BITMAP_OPAQUE delete_xpm[1]
Definition: delete.cpp:62
Special netlist exporter flavor that allows one to override simulation commands.
void updateTuners()
Filters out tuners for components that do not exist anymore.
KI_PARAM_ERROR is a class used to hold a translatable error message and may be used when throwing exc...
Definition: ki_exception.h:45
wxToolBarToolBase * m_toolSettings
SIM_PLOT_FRAME(KIWAY *aKiway, wxWindow *aParent)
Constructor.
const BITMAP_OPAQUE zoom_in_xpm[1]
Definition: zoom_in.cpp:59