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 
36 #include <widgets/tuner_slider.h>
39 #include <pgm_base.h>
40 
41 #include "sim_plot_frame.h"
42 #include "sim_plot_panel.h"
43 #include "spice_simulator.h"
44 #include "spice_reporter.h"
45 
46 #include <menus_helpers.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_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 );
124  m_signalsIconColorList = NULL;
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  // Give icons to menuitems
144 
146 
147  if( !m_simulator )
148  {
149  throw std::runtime_error( "Could not create simulator instance" );
150  return;
151  }
152 
153  m_simulator->Init();
154 
155  if( m_savedWorkbooksPath.IsEmpty() )
156  {
158  }
159 
160  m_reporter = new SIM_THREAD_REPORTER( this );
161  m_simulator->SetReporter( m_reporter );
162 
164 
165  Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SIM_PLOT_FRAME::onClose ), NULL, this );
166  Connect( EVT_SIM_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onSimUpdate ), NULL, this );
167  Connect( EVT_SIM_REPORT, wxCommandEventHandler( SIM_PLOT_FRAME::onSimReport ), NULL, this );
168  Connect( EVT_SIM_STARTED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimStarted ), NULL, this );
169  Connect( EVT_SIM_FINISHED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimFinished ), NULL, this );
170  Connect( EVT_SIM_CURSOR_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onCursorUpdate ), NULL, this );
171 
172  // Toolbar buttons
173  m_toolSimulate = m_toolBar->AddTool( ID_SIM_RUN, _( "Run/Stop Simulation" ),
174  KiBitmap( sim_run_xpm ), _( "Run Simulation" ), wxITEM_NORMAL );
175  m_toolAddSignals = m_toolBar->AddTool( ID_SIM_ADD_SIGNALS, _( "Add Signals" ),
176  KiBitmap( sim_add_signal_xpm ), _( "Add signals to plot" ), wxITEM_NORMAL );
177  m_toolProbe = m_toolBar->AddTool( ID_SIM_PROBE, _( "Probe" ),
178  KiBitmap( sim_probe_xpm ), _( "Probe signals on the schematic" ), wxITEM_NORMAL );
179  m_toolTune = m_toolBar->AddTool( ID_SIM_TUNE, _( "Tune" ),
180  KiBitmap( sim_tune_xpm ), _( "Tune component values" ), wxITEM_NORMAL );
181  m_toolSettings = m_toolBar->AddTool( wxID_ANY, _( "Settings" ),
182  KiBitmap( sim_settings_xpm ), _( "Simulation settings" ), wxITEM_NORMAL );
183 
184  Connect( m_toolSimulate->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
185  wxCommandEventHandler( SIM_PLOT_FRAME::onSimulate ), NULL, this );
186  Connect( m_toolAddSignals->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
187  wxCommandEventHandler( SIM_PLOT_FRAME::onAddSignal ), NULL, this );
188  Connect( m_toolProbe->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
189  wxCommandEventHandler( SIM_PLOT_FRAME::onProbe ), NULL, this );
190  Connect( m_toolTune->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
191  wxCommandEventHandler( SIM_PLOT_FRAME::onTune ), NULL, this );
192  Connect( m_toolSettings->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
193  wxCommandEventHandler( SIM_PLOT_FRAME::onSettings ), NULL, this );
194 
195  // Bind toolbar buttons event to existing menu event handlers, so they behave the same
196  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSimulate, this, m_runSimulation->GetId() );
197  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onAddSignal, this, m_addSignals->GetId() );
198  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onProbe, this, m_probeSignals->GetId() );
199  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onTune, this, m_tuneValue->GetId() );
200  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onShowNetlist, this, m_showNetlist->GetId() );
201  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSettings, this, m_settings->GetId() );
202 
203  m_toolBar->Realize();
204  m_plotNotebook->SetPageText( 0, _( "Welcome!" ) );
205 
206  // the settings dialog will be created later, on demand.
207  // if created in the ctor, for some obscure reason, there is an issue
208  // on Windows: when open it, the simulator frame is sent to the background.
209  // instead of being behind the dialog frame (as it does)
210  m_settingsDlg = NULL;
211 
212  // resize the subwindows size. At least on Windows, calling wxSafeYield before
213  // resizing the subwindows forces the wxSplitWindows size events automatically generated
214  // by wxWidgets to be executed before our resize code.
215  // Otherwise, the changes made by setSubWindowsSashSize are overwritten by one these
216  // events
217  wxSafeYield();
219 }
220 
221 
223 {
224  m_simulator->SetReporter( nullptr );
225  delete m_reporter;
226  delete m_signalsIconColorList;
227 
228  if( m_settingsDlg )
229  m_settingsDlg->Destroy();
230 }
231 
232 #define PLOT_PANEL_WIDTH_ENTRY "SimPlotPanelWidth"
233 #define PLOT_PANEL_HEIGHT_ENTRY "SimPlotPanelHeight"
234 #define SIGNALS_PANEL_HEIGHT_ENTRY "SimSignalPanelHeight"
235 #define CURSORS_PANEL_HEIGHT_ENTRY "SimCursorsPanelHeight"
236 
237 void SIM_PLOT_FRAME::SaveSettings( wxConfigBase* aCfg )
238 {
239  // Save main frame size and position:
241 
242  // Save subwindows sizes
243  aCfg->Write( PLOT_PANEL_WIDTH_ENTRY, m_splitterLeftRight->GetSashPosition() );
244  aCfg->Write( PLOT_PANEL_HEIGHT_ENTRY, m_splitterPlotAndConsole->GetSashPosition() );
245  aCfg->Write( SIGNALS_PANEL_HEIGHT_ENTRY, m_splitterSignals->GetSashPosition() );
246  aCfg->Write( CURSORS_PANEL_HEIGHT_ENTRY, m_splitterTuneValues->GetSashPosition() );
247 }
248 
249 
250 void SIM_PLOT_FRAME::LoadSettings( wxConfigBase* aCfg )
251 {
252  // Read main frame size and position:
254  SetSize( m_FramePos.x, m_FramePos.y, m_FrameSize.x, m_FrameSize.y );
255 
256  // Read subwindows sizes (should be > 0 )
261 }
262 
263 
264 // A small helper struct to handle bitmaps initialisation in menus
266 {
267  int m_MenuId;
268  BITMAP_DEF m_Bitmap;
269 };
270 
271 
273 {
274  // Give icons to menuitems of the main menubar
275  BM_MENU_INIT_ITEM bm_list[]
276  {
277  // File menu:
278  { wxID_NEW, simulator_xpm },
279  { wxID_OPEN,directory_browser_xpm },
280  { wxID_SAVE, directory_xpm},
281  { ID_SAVE_AS_IMAGE, export_xpm},
282  { ID_SAVE_AS_CSV, export_xpm},
283  { wxID_CLOSE, exit_xpm},
284 
285  // simulator menu:
286  { ID_MENU_RUN_SIM, sim_run_xpm},
287  { ID_MENU_ADD_SIGNAL, sim_add_signal_xpm},
288  { ID_MENU_PROBE_SIGNALS, sim_probe_xpm},
289  { ID_MENU_TUNE_SIGNALS, sim_tune_xpm},
290  { ID_MENU_SHOW_NETLIST, netlist_xpm},
291  { ID_MENU_SET_SIMUL, sim_settings_xpm},
292 
293  // View menu
294  { wxID_ZOOM_IN, zoom_in_xpm},
295  { wxID_ZOOM_OUT, zoom_out_xpm},
296  { wxID_ZOOM_FIT, zoom_fit_in_page_xpm},
297  { ID_MENU_SHOW_GRID, grid_xpm},
299 
300  { 0, nullptr } // Sentinel
301  };
302 
303  // wxMenuItems are already created and attached to the m_mainMenu wxMenuBar.
304  // A problem is the fact setting bitmaps in wxMenuItems after they are attached
305  // to a wxMenu do not work in all cases.
306  // So the trick is:
307  // Remove the wxMenuItem from its wxMenu
308  // Set the bitmap
309  // Insert the modified wxMenuItem to its previous place
310  for( int ii = 0; bm_list[ii].m_MenuId; ++ii )
311  {
312  wxMenuItem* item = m_mainMenu->FindItem( bm_list[ii].m_MenuId );
313 
314  if( !item || !bm_list[ii].m_Bitmap)
315  continue;
316 
317  wxMenu* menu = item->GetMenu();
318  // Calculate the initial index of item inside the wxMenu parent
319  wxMenuItemList& mlist = menu->GetMenuItems();
320  int mpos = mlist.IndexOf( item );
321 
322  if( mpos >= 0 ) // Should be always the case
323  {
324  // Modify the bitmap
325  menu->Remove( item );
326  AddBitmapToMenuItem( item, KiBitmap( bm_list[ii].m_Bitmap ) );
327  // Insert item to its the initial index
328  menu->Insert( mpos, item );
329  }
330  }
331 }
332 
333 
335 {
338 
341 
344 
347 }
348 
349 
351 {
352  STRING_FORMATTER formatter;
353  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
354 
355  if( !m_settingsDlg )
356  m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
357 
358  m_simConsole->Clear();
360 
361  if( plotPanel )
362  m_exporter->SetSimCommand( m_plots[plotPanel].m_simCommand );
363 
364  if( !m_exporter->Format( &formatter, m_settingsDlg->GetNetlistOptions() ) )
365  {
366  DisplayError( this, _( "There were errors during netlist export, aborted." ) );
367  return;
368  }
369 
370  if( m_exporter->GetSimType() == ST_UNKNOWN )
371  {
372  DisplayInfoMessage( this, _( "You need to select the simulation settings first." ) );
373  return;
374  }
375 
376  m_simulator->LoadNetlist( formatter.GetString() );
377  updateTuners();
378  applyTuners();
379  m_simulator->Run();
380 }
381 
382 
384 {
385  m_simulator->Stop();
386 }
387 
388 
390 {
391  return m_simulator ? m_simulator->IsRunning() : false;
392 }
393 
394 
396 {
397  SIM_PLOT_PANEL* plotPanel = new SIM_PLOT_PANEL( aSimType, m_plotNotebook, wxID_ANY );
398 
399  if( m_welcomePanel )
400  {
401  m_plotNotebook->DeletePage( 0 );
402  m_welcomePanel = nullptr;
403  }
404 
405  m_plotNotebook->AddPage( plotPanel, wxString::Format( _( "Plot%u" ),
406  (unsigned int) m_plotNotebook->GetPageCount() + 1 ), true );
407 
408  m_plots[plotPanel] = PLOT_INFO();
409 
410  return plotPanel;
411 }
412 
413 
414 void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName )
415 {
416  addPlot( aNetName, SPT_VOLTAGE, "V" );
417 }
418 
419 
420 void SIM_PLOT_FRAME::AddCurrentPlot( const wxString& aDeviceName, const wxString& aParam )
421 {
422  addPlot( aDeviceName, SPT_CURRENT, aParam );
423 }
424 
425 
427 {
428  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
429 
430  if( !plotPanel )
431  return;
432 
433  // For now limit the tuner tool to RLC components
434  char primitiveType = NETLIST_EXPORTER_PSPICE::GetSpiceField( SF_PRIMITIVE, aComponent, 0 )[0];
435 
436  if( primitiveType != SP_RESISTOR && primitiveType != SP_CAPACITOR && primitiveType != SP_INDUCTOR )
437  return;
438 
439  const wxString componentName = aComponent->GetField( REFERENCE )->GetText();
440 
441  // Do not add multiple instances for the same component
442  auto tunerIt = std::find_if( m_tuners.begin(), m_tuners.end(), [&]( const TUNER_SLIDER* t )
443  {
444  return t->GetComponentName() == componentName;
445  }
446  );
447 
448  if( tunerIt != m_tuners.end() )
449  return; // We already have it
450 
451  try
452  {
453  TUNER_SLIDER* tuner = new TUNER_SLIDER( this, m_tunePanel, aComponent );
454  m_tuneSizer->Add( tuner );
455  m_tuners.push_back( tuner );
456  m_tunePanel->Layout();
457  }
458  catch( const KI_PARAM_ERROR& e )
459  {
460  // Sorry, no bonus
461  DisplayError( nullptr, e.What() );
462  }
463 }
464 
465 
466 void SIM_PLOT_FRAME::RemoveTuner( TUNER_SLIDER* aTuner, bool aErase )
467 {
468  if( aErase )
469  m_tuners.remove( aTuner );
470 
471  aTuner->Destroy();
472  m_tunePanel->Layout();
473 }
474 
475 
477 {
478  wxWindow* curPage = m_plotNotebook->GetCurrentPage();
479 
480  return ( curPage == m_welcomePanel ) ? nullptr : static_cast<SIM_PLOT_PANEL*>( curPage );
481 }
482 
483 
485 {
486  return m_exporter.get();
487 }
488 
489 
490 void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam )
491 {
492  SIM_TYPE simType = m_exporter->GetSimType();
493 
494  if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
495  return; // TODO else write out in console?
496 
497  // Create a new plot if the current one displays a different type
498  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
499 
500  if( !plotPanel || plotPanel->GetType() != simType )
501  plotPanel = NewPlotPanel( simType );
502 
503  TRACE_DESC descriptor( *m_exporter, aName, aType, aParam );
504 
505  bool updated = false;
506  SIM_PLOT_TYPE xAxisType = GetXAxisType( simType );
507 
508  if( xAxisType == SPT_LIN_FREQUENCY || xAxisType == SPT_LOG_FREQUENCY )
509  {
510  int baseType = descriptor.GetType() & ~( SPT_AC_MAG | SPT_AC_PHASE );
511 
512  // Add two plots: magnitude & phase
513  TRACE_DESC mag_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_MAG ) );
514  TRACE_DESC phase_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_PHASE ) );
515 
516  updated |= updatePlot( mag_desc, plotPanel );
517  updated |= updatePlot( phase_desc, plotPanel );
518  }
519  else
520  {
521  updated = updatePlot( descriptor, plotPanel );
522  }
523 
524  if( updated )
525  {
527  }
528 }
529 
530 
531 void SIM_PLOT_FRAME::removePlot( const wxString& aPlotName, bool aErase )
532 {
533  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
534 
535  if( !plotPanel )
536  return;
537 
538  if( aErase )
539  {
540  auto& traceMap = m_plots[plotPanel].m_traces;
541  auto traceIt = traceMap.find( aPlotName );
542  wxASSERT( traceIt != traceMap.end() );
543  traceMap.erase( traceIt );
544  }
545 
546  wxASSERT( plotPanel->TraceShown( aPlotName ) );
547  plotPanel->DeleteTrace( aPlotName );
548  plotPanel->Fit();
549 
551  updateCursors();
552 }
553 
554 
556 {
558 }
559 
560 
561 bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* aPanel )
562 {
563  SIM_TYPE simType = m_exporter->GetSimType();
564  wxString spiceVector = m_exporter->GetSpiceVector( aDescriptor.GetName(),
565  aDescriptor.GetType(), aDescriptor.GetParam() );
566 
567  if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
568  {
569  // There is no plot to be shown
570  m_simulator->Command( wxString::Format( "print %s", spiceVector ).ToStdString() );
571 
572  return false;
573  }
574 
575  // First, handle the x axis
576  wxString xAxisName( m_simulator->GetXAxis( simType ) );
577 
578  if( xAxisName.IsEmpty() )
579  return false;
580 
581  auto data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() );
582  unsigned int size = data_x.size();
583 
584  if( data_x.empty() )
585  return false;
586 
587  SIM_PLOT_TYPE plotType = aDescriptor.GetType();
588  std::vector<double> data_y;
589 
590  // Now, Y axis data
591  switch( m_exporter->GetSimType() )
592  {
593  case ST_AC:
594  {
595  wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ),
596  "Cannot set both AC_PHASE and AC_MAG bits" );
597 
598  if( plotType & SPT_AC_MAG )
599  data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
600  else if( plotType & SPT_AC_PHASE )
601  data_y = m_simulator->GetPhasePlot( (const char*) spiceVector.c_str() );
602  else
603  wxASSERT_MSG( false, "Plot type missing AC_PHASE or AC_MAG bit" );
604  }
605  break;
606 
607  case ST_NOISE:
608  case ST_DC:
609  case ST_TRANSIENT:
610  {
611  data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
612  }
613  break;
614 
615  default:
616  wxASSERT_MSG( false, "Unhandled plot type" );
617  return false;
618  }
619 
620  if( data_y.size() != size )
621  return false;
622 
623  if( aPanel->AddTrace( aDescriptor.GetTitle(), size,
624  data_x.data(), data_y.data(), aDescriptor.GetType() ) )
625  {
626  m_plots[aPanel].m_traces.insert( std::make_pair( aDescriptor.GetTitle(), aDescriptor ) );
627  }
628 
629  return true;
630 }
631 
632 
634 {
635  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
636 
637  if( !plotPanel )
638  return;
639 
640  m_signals->ClearAll();
641 
642  wxSize size = m_signals->GetClientSize();
643  m_signals->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x );
644 
645  // Build an image list, to show the color of the corresponding trace
646  // in the plot panel
647  // This image list is used for trace and cursor lists
648  wxMemoryDC bmDC;
649  const int isize = bmDC.GetCharHeight();
650 
651  if( m_signalsIconColorList == NULL )
652  m_signalsIconColorList = new wxImageList( isize, isize, false );
653  else
654  m_signalsIconColorList->RemoveAll();
655 
656  for( const auto& trace : CurrentPlot()->GetTraces() )
657  {
658  wxBitmap bitmap( isize, isize );
659  bmDC.SelectObject( bitmap );
660  wxColour tcolor = trace.second->GetTraceColour();
661 
662  wxColour bgColor = m_signals->wxWindow::GetBackgroundColour();
663  bmDC.SetPen( wxPen( bgColor ) );
664  bmDC.SetBrush( wxBrush( bgColor ) );
665  bmDC.DrawRectangle( 0, 0, isize, isize ); // because bmDC.Clear() does not work in wxGTK
666 
667  bmDC.SetPen( wxPen( tcolor ) );
668  bmDC.SetBrush( wxBrush( tcolor ) );
669  bmDC.DrawRectangle( 0, isize / 4 + 1, isize, isize / 2 );
670 
671  bmDC.SelectObject( wxNullBitmap ); // Needed to initialize bitmap
672 
673  bitmap.SetMask( new wxMask( bitmap, *wxBLACK ) );
674  m_signalsIconColorList->Add( bitmap );
675  }
676 
677  if( bmDC.IsOk() )
678  {
679  bmDC.SetBrush( wxNullBrush );
680  bmDC.SetPen( wxNullPen );
681  }
682 
683  m_signals->SetImageList( m_signalsIconColorList, wxIMAGE_LIST_SMALL );
684 
685  // Fill the signals listctrl. Keep the order of names and
686  // the order of icon color identical, because the icons
687  // are also used in cursor list, and the color index is
688  // calculated from the trace name index
689  int imgidx = 0;
690 
691  for( const auto& trace : m_plots[plotPanel].m_traces )
692  {
693  m_signals->InsertItem( imgidx, trace.first, imgidx );
694  imgidx++;
695  }
696 }
697 
698 
700 {
701  wxQueueEvent( this, new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
702 }
703 
704 
706 {
707  const auto& spiceItems = m_exporter->GetSpiceItems();
708 
709  for( auto it = m_tuners.begin(); it != m_tuners.end(); /* iteration inside the loop */ )
710  {
711  const wxString& ref = (*it)->GetComponentName();
712 
713  if( std::find_if( spiceItems.begin(), spiceItems.end(), [&]( const SPICE_ITEM& item )
714  {
715  return item.m_refName == ref;
716  }) == spiceItems.end() )
717  {
718  // The component does not exist anymore, remove the associated tuner
719  TUNER_SLIDER* tuner = *it;
720  it = m_tuners.erase( it );
721  RemoveTuner( tuner, false );
722  }
723  else
724  {
725  ++it;
726  }
727  }
728 }
729 
730 
732 {
733  for( auto& tuner : m_tuners )
734  {
736  std::string command( "alter @" + tuner->GetSpiceName()
737  + "=" + tuner->GetValue().ToSpiceString() );
738 
739  m_simulator->Command( command );
740  }
741 }
742 
743 
744 bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath )
745 {
746  m_plots.clear();
747  m_plotNotebook->DeleteAllPages();
748 
749  wxTextFile file( aPath );
750 
751  if( !file.Open() )
752  return false;
753 
754  long plotsCount;
755 
756  if( !file.GetFirstLine().ToLong( &plotsCount ) ) // GetFirstLine instead of GetNextLine
757  return false;
758 
759  for( long i = 0; i < plotsCount; ++i )
760  {
761  long plotType, tracesCount;
762 
763  if( !file.GetNextLine().ToLong( &plotType ) )
764  return false;
765 
766  SIM_PLOT_PANEL* plotPanel = NewPlotPanel( (SIM_TYPE) plotType );
767  m_plots[plotPanel].m_simCommand = file.GetNextLine();
768  StartSimulation();
769 
770  // Perform simulation, so plots can be added with values
771  do
772  {
773  wxThread::This()->Sleep( 50 );
774  }
775  while( IsSimulationRunning() );
776 
777  if( !file.GetNextLine().ToLong( &tracesCount ) )
778  return false;
779 
780  for( long j = 0; j < tracesCount; ++j )
781  {
782  long traceType;
783  wxString name, param;
784 
785  if( !file.GetNextLine().ToLong( &traceType ) )
786  return false;
787 
788  name = file.GetNextLine();
789  param = file.GetNextLine();
790 
791  if( name.IsEmpty() || param.IsEmpty() )
792  return false;
793 
794  addPlot( name, (SIM_PLOT_TYPE) traceType, param );
795  }
796  }
797 
798  return true;
799 }
800 
801 
802 bool SIM_PLOT_FRAME::saveWorkbook( const wxString& aPath )
803 {
804 
805  wxString savePath = aPath;
806 
807  if( !savePath.Lower().EndsWith(".wbk"))
808  {
809  savePath += ".wbk";
810  };
811 
812 
813  wxTextFile file( savePath );
814 
815  if( file.Exists() )
816  {
817  if( !file.Open() )
818  return false;
819 
820  file.Clear();
821  }
822  else
823  {
824  file.Create();
825  }
826 
827  file.AddLine( wxString::Format( "%lu", m_plots.size() ) );
828 
829  for( const auto& plot : m_plots )
830  {
831  file.AddLine( wxString::Format( "%d", plot.first->GetType() ) );
832  file.AddLine( plot.second.m_simCommand );
833  file.AddLine( wxString::Format( "%lu", plot.second.m_traces.size() ) );
834 
835  for( const auto& trace : plot.second.m_traces )
836  {
837  file.AddLine( wxString::Format( "%d", trace.second.GetType() ) );
838  file.AddLine( trace.second.GetName() );
839  file.AddLine( trace.second.GetParam() );
840  }
841  }
842 
843  bool res = file.Write();
844  file.Close();
845 
846  return res;
847 }
848 
849 
851 {
852  switch( aType )
853  {
854  case ST_AC:
855  return SPT_LIN_FREQUENCY;
857 
858  case ST_DC:
859  return SPT_SWEEP;
860 
861  case ST_TRANSIENT:
862  return SPT_TIME;
863 
864  default:
865  wxASSERT_MSG( false, "Unhandled simulation type" );
866  return (SIM_PLOT_TYPE) 0;
867  }
868 }
869 
870 
871 void SIM_PLOT_FRAME::menuNewPlot( wxCommandEvent& aEvent )
872 {
873  SIM_TYPE type = m_exporter->GetSimType();
874 
875  if( SIM_PLOT_PANEL::IsPlottable( type ) )
876  {
877  SIM_PLOT_PANEL* prevPlot = CurrentPlot();
878  SIM_PLOT_PANEL* newPlot = NewPlotPanel( type );
879 
880  // If the previous plot had the same type, copy the simulation command
881  if( prevPlot )
882  m_plots[newPlot].m_simCommand = m_plots[prevPlot].m_simCommand;
883  }
884 }
885 
886 
887 void SIM_PLOT_FRAME::menuOpenWorkbook( wxCommandEvent& event )
888 {
889  wxFileDialog openDlg( this, _( "Open simulation workbook" ), m_savedWorkbooksPath, "",
890  WorkbookFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
891 
892  if( openDlg.ShowModal() == wxID_CANCEL )
893  return;
894 
895  m_savedWorkbooksPath = openDlg.GetDirectory();
896 
897  if( !loadWorkbook( openDlg.GetPath() ) )
898  DisplayError( this, _( "There was an error while opening the workbook file" ) );
899 }
900 
901 
902 void SIM_PLOT_FRAME::menuSaveWorkbook( wxCommandEvent& event )
903 {
904  if( !CurrentPlot() )
905  return;
906 
907  wxFileDialog saveDlg( this, _( "Save Simulation Workbook" ), m_savedWorkbooksPath, "",
908  WorkbookFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
909 
910  if( saveDlg.ShowModal() == wxID_CANCEL )
911  return;
912 
913  m_savedWorkbooksPath = saveDlg.GetDirectory();
914 
915  if( !saveWorkbook( saveDlg.GetPath() ) )
916  DisplayError( this, _( "There was an error while saving the workbook file" ) );
917 }
918 
919 
920 void SIM_PLOT_FRAME::menuSaveImage( wxCommandEvent& event )
921 {
922  if( !CurrentPlot() )
923  return;
924 
925  wxFileDialog saveDlg( this, _( "Save Plot as Image" ), "", "",
926  PngFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
927 
928  if( saveDlg.ShowModal() == wxID_CANCEL )
929  return;
930 
931  CurrentPlot()->SaveScreenshot( saveDlg.GetPath(), wxBITMAP_TYPE_PNG );
932 }
933 
934 
935 void SIM_PLOT_FRAME::menuSaveCsv( wxCommandEvent& event )
936 {
937  if( !CurrentPlot() )
938  return;
939 
940  const wxChar SEPARATOR = ';';
941 
942  wxFileDialog saveDlg( this, _( "Save Plot Data" ), "", "",
943  CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
944 
945  if( saveDlg.ShowModal() == wxID_CANCEL )
946  return;
947 
948  wxFile out( saveDlg.GetPath(), wxFile::write );
949  bool timeWritten = false;
950 
951  for( const auto& t : CurrentPlot()->GetTraces() )
952  {
953  const TRACE* trace = t.second;
954 
955  if( !timeWritten )
956  {
957  out.Write( wxString::Format( "Time%c", SEPARATOR ) );
958 
959  for( double v : trace->GetDataX() )
960  out.Write( wxString::Format( "%f%c", v, SEPARATOR ) );
961 
962  out.Write( "\r\n" );
963  timeWritten = true;
964  }
965 
966  out.Write( wxString::Format( "%s%c", t.first, SEPARATOR ) );
967 
968  for( double v : trace->GetDataY() )
969  out.Write( wxString::Format( "%f%c", v, SEPARATOR ) );
970 
971  out.Write( "\r\n" );
972  }
973 
974  out.Close();
975 }
976 
977 
978 void SIM_PLOT_FRAME::menuZoomIn( wxCommandEvent& event )
979 {
980  if( CurrentPlot() )
981  CurrentPlot()->ZoomIn();
982 }
983 
984 
985 void SIM_PLOT_FRAME::menuZoomOut( wxCommandEvent& event )
986 {
987  if( CurrentPlot() )
988  CurrentPlot()->ZoomOut();
989 }
990 
991 
992 void SIM_PLOT_FRAME::menuZoomFit( wxCommandEvent& event )
993 {
994  if( CurrentPlot() )
995  CurrentPlot()->Fit();
996 }
997 
998 
999 void SIM_PLOT_FRAME::menuShowGrid( wxCommandEvent& event )
1000 {
1001  SIM_PLOT_PANEL* plot = CurrentPlot();
1002 
1003  if( plot )
1004  plot->ShowGrid( !plot->IsGridShown() );
1005 }
1006 
1007 
1008 void SIM_PLOT_FRAME::menuShowGridUpdate( wxUpdateUIEvent& event )
1009 {
1010  SIM_PLOT_PANEL* plot = CurrentPlot();
1011 
1012  event.Check( plot ? plot->IsGridShown() : false );
1013 }
1014 
1015 
1016 void SIM_PLOT_FRAME::menuShowLegend( wxCommandEvent& event )
1017 {
1018  SIM_PLOT_PANEL* plot = CurrentPlot();
1019 
1020  if( plot )
1021  plot->ShowLegend( !plot->IsLegendShown() );
1022 }
1023 
1024 
1025 void SIM_PLOT_FRAME::menuShowLegendUpdate( wxUpdateUIEvent& event )
1026 {
1027  SIM_PLOT_PANEL* plot = CurrentPlot();
1028  event.Check( plot ? plot->IsLegendShown() : false );
1029 }
1030 
1031 
1032 void SIM_PLOT_FRAME::onPlotClose( wxAuiNotebookEvent& event )
1033 {
1034  int idx = event.GetSelection();
1035 
1036  if( idx == wxNOT_FOUND )
1037  return;
1038 
1039  SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetPage( idx ) );
1040 
1041  if( !plotPanel )
1042  return;
1043 
1044  m_plots.erase( plotPanel );
1045  updateSignalList();
1046  updateCursors();
1047 }
1048 
1049 
1050 void SIM_PLOT_FRAME::onPlotChanged( wxAuiNotebookEvent& event )
1051 {
1052  updateSignalList();
1053  updateCursors();
1054 }
1055 
1056 
1057 void SIM_PLOT_FRAME::onSignalDblClick( wxMouseEvent& event )
1058 {
1059  // Remove signal from the plot panel when double clicked
1060  long idx = m_signals->GetFocusedItem();
1061 
1062  if( idx != wxNOT_FOUND )
1063  removePlot( m_signals->GetItemText( idx, 0 ) );
1064 }
1065 
1066 
1067 void SIM_PLOT_FRAME::onSignalRClick( wxListEvent& event )
1068 {
1069  int idx = event.GetIndex();
1070 
1071  if( idx != wxNOT_FOUND )
1072  m_signals->Select( idx );
1073 
1074  idx = m_signals->GetFirstSelected();
1075 
1076  if( idx != wxNOT_FOUND )
1077  {
1078  const wxString& netName = m_signals->GetItemText( idx, 0 );
1079  SIGNAL_CONTEXT_MENU ctxMenu( netName, this );
1080  m_signals->PopupMenu( &ctxMenu );
1081  }
1082 }
1083 
1084 
1085 void SIM_PLOT_FRAME::onSimulate( wxCommandEvent& event )
1086 {
1087  if( IsSimulationRunning() )
1088  StopSimulation();
1089  else
1090  StartSimulation();
1091 }
1092 
1093 
1094 void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
1095 {
1096  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1097 
1098  // Initial processing is required to e.g. display a list of power sources
1100 
1101  if( !m_exporter->ProcessNetlist( NET_ALL_FLAGS ) )
1102  {
1103  DisplayError( this, _( "There were errors during netlist export, aborted." ) );
1104  return;
1105  }
1106 
1107  if( !m_settingsDlg )
1108  m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
1109 
1110  if( plotPanel )
1111  m_settingsDlg->SetSimCommand( m_plots[plotPanel].m_simCommand );
1112 
1114 
1115  if( m_settingsDlg->ShowModal() == wxID_OK )
1116  {
1117  wxString newCommand = m_settingsDlg->GetSimCommand();
1118  SIM_TYPE newSimType = NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( newCommand );
1119 
1120  // If it is a new simulation type, open a new plot
1121  if( !plotPanel || ( plotPanel && plotPanel->GetType() != newSimType ) )
1122  {
1123  plotPanel = NewPlotPanel( newSimType );
1124  }
1125 
1126  m_plots[plotPanel].m_simCommand = newCommand;
1127  }
1128 }
1129 
1130 
1131 void SIM_PLOT_FRAME::onAddSignal( wxCommandEvent& event )
1132 {
1133  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1134 
1135  if( !plotPanel || !m_exporter || plotPanel->GetType() != m_exporter->GetSimType() )
1136  {
1137  DisplayInfoMessage( this, _( "You need to run simulation first." ) );
1138  return;
1139  }
1140 
1141  DIALOG_SIGNAL_LIST dialog( this, m_exporter.get() );
1142  dialog.ShowModal();
1143 }
1144 
1145 
1146 void SIM_PLOT_FRAME::onProbe( wxCommandEvent& event )
1147 {
1148  if( m_schematicFrame == NULL )
1149  return;
1150 
1151  wxQueueEvent( m_schematicFrame, new wxCommandEvent( wxEVT_TOOL, ID_SIM_PROBE ) );
1152  m_schematicFrame->Raise();
1153 }
1154 
1155 
1156 void SIM_PLOT_FRAME::onTune( wxCommandEvent& event )
1157 {
1158  if( m_schematicFrame == NULL )
1159  return;
1160 
1161  wxQueueEvent( m_schematicFrame, new wxCommandEvent( wxEVT_TOOL, ID_SIM_TUNE ) );
1162  m_schematicFrame->Raise();
1163 }
1164 
1165 void SIM_PLOT_FRAME::onShowNetlist( wxCommandEvent& event )
1166 {
1167  class NETLIST_VIEW_DIALOG : public wxDialog
1168  {
1169  public:
1170  enum
1171  {
1172  MARGIN_LINE_NUMBERS
1173  };
1174 
1175  void onClose( wxCloseEvent& evt )
1176  {
1177  EndModal( GetReturnCode() );
1178  }
1179 
1180  NETLIST_VIEW_DIALOG(wxWindow* parent, wxString source) :
1181  wxDialog(parent, wxID_ANY, "SPICE Netlist",
1182  wxDefaultPosition, wxSize(1500,900),
1183  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1184  {
1185  wxStyledTextCtrl* text = new wxStyledTextCtrl( this, wxID_ANY );
1186 
1187  text->SetMarginWidth( MARGIN_LINE_NUMBERS, 50 );
1188  text->StyleSetForeground( wxSTC_STYLE_LINENUMBER, wxColour( 75, 75, 75 ) );
1189  text->StyleSetBackground( wxSTC_STYLE_LINENUMBER, wxColour( 220, 220, 220 ) );
1190  text->SetMarginType( MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER );
1191 
1192  text->SetWrapMode( wxSTC_WRAP_WORD );
1193 
1194  text->SetText( source );
1195 
1196  text->StyleClearAll();
1197  text->SetLexer( wxSTC_LEX_SPICE );
1198 
1199  wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
1200  sizer->Add( text, 1, wxEXPAND );
1201  SetSizer( sizer );
1202 
1203  Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( NETLIST_VIEW_DIALOG::onClose ), NULL,
1204  this );
1205  }
1206  };
1207 
1208  if( m_schematicFrame == NULL || m_simulator == NULL )
1209  return;
1210 
1211  NETLIST_VIEW_DIALOG dlg( this, m_simulator->GetNetlist() );
1212  dlg.ShowModal();
1213 }
1214 
1215 
1216 void SIM_PLOT_FRAME::onClose( wxCloseEvent& aEvent )
1217 {
1218  SaveSettings( config() );
1219 
1220  if( IsSimulationRunning() )
1221  m_simulator->Stop();
1222 
1223  Destroy();
1224 }
1225 
1226 
1227 void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
1228 {
1229  wxSize size = m_cursors->GetClientSize();
1230  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1231  m_cursors->ClearAll();
1232 
1233  if( !plotPanel )
1234  return;
1235 
1237  m_cursors->SetImageList(m_signalsIconColorList, wxIMAGE_LIST_SMALL);
1238 
1239  // Fill the signals listctrl
1240  m_cursors->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x / 2 );
1241  const long X_COL = m_cursors->AppendColumn( plotPanel->GetLabelX(), wxLIST_FORMAT_LEFT, size.x / 4 );
1242 
1243  wxString labelY1 = plotPanel->GetLabelY1();
1244  wxString labelY2 = plotPanel->GetLabelY2();
1245  wxString labelY;
1246 
1247  if( !labelY2.IsEmpty() )
1248  labelY = labelY1 + " / " + labelY2;
1249  else
1250  labelY = labelY1;
1251 
1252  const long Y_COL = m_cursors->AppendColumn( labelY, wxLIST_FORMAT_LEFT, size.x / 4 );
1253 
1254  // Update cursor values
1255  int itemidx = 0;
1256  for( const auto& trace : plotPanel->GetTraces() )
1257  {
1258  if( CURSOR* cursor = trace.second->GetCursor() )
1259  {
1260  // Find the right icon color in list.
1261  // It is the icon used in m_signals list for the same trace
1262  long iconColor = m_signals->FindItem( -1, trace.first );
1263 
1264  const wxRealPoint coords = cursor->GetCoords();
1265  long idx = m_cursors->InsertItem( itemidx++, trace.first, iconColor );
1266  m_cursors->SetItem( idx, X_COL, SPICE_VALUE( coords.x ).ToSpiceString() );
1267  m_cursors->SetItem( idx, Y_COL, SPICE_VALUE( coords.y ).ToSpiceString() );
1268  }
1269  }
1270 }
1271 
1272 
1273 void SIM_PLOT_FRAME::onSimStarted( wxCommandEvent& aEvent )
1274 {
1275  m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_stop_xpm ) );
1276  SetCursor( wxCURSOR_ARROWWAIT );
1277 }
1278 
1279 
1280 void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
1281 {
1282  m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_run_xpm ) );
1283  SetCursor( wxCURSOR_ARROW );
1284 
1285  SIM_TYPE simType = m_exporter->GetSimType();
1286 
1287  if( simType == ST_UNKNOWN )
1288  return;
1289 
1290  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1291 
1292  if( !plotPanel || plotPanel->GetType() != simType )
1293  plotPanel = NewPlotPanel( simType );
1294 
1295  if( IsSimulationRunning() )
1296  return;
1297 
1298  // If there are any signals plotted, update them
1299  if( SIM_PLOT_PANEL::IsPlottable( simType ) )
1300  {
1301  TRACE_MAP& traceMap = m_plots[plotPanel].m_traces;
1302 
1303  for( auto it = traceMap.begin(); it != traceMap.end(); /* iteration occurs in the loop */)
1304  {
1305  if( !updatePlot( it->second, plotPanel ) )
1306  {
1307  removePlot( it->first, false );
1308  it = traceMap.erase( it ); // remove a plot that does not exist anymore
1309  }
1310  else
1311  {
1312  ++it;
1313  }
1314  }
1315 
1316  updateSignalList();
1317  plotPanel->UpdateAll();
1318  plotPanel->ResetScales();
1319  }
1320  else
1321  {
1323  for( const auto& net : m_exporter->GetNetIndexMap() )
1324  {
1325  int node = net.second;
1326 
1327  if( node > 0 )
1328  m_simulator->Command( wxString::Format( "print v(%d)", node ).ToStdString() );
1329  }
1330  }
1331 }
1332 
1333 
1334 void SIM_PLOT_FRAME::onSimUpdate( wxCommandEvent& aEvent )
1335 {
1336  if( IsSimulationRunning() )
1337  StopSimulation();
1338 
1339  if( CurrentPlot() != m_lastSimPlot )
1340  {
1341  // We need to rerun simulation, as the simulator currently stores
1342  // results for another plot
1343  StartSimulation();
1344  }
1345  else
1346  {
1347  // Incremental update
1348  m_simConsole->Clear();
1349  // Do not export netlist, it is already stored in the simulator
1350  applyTuners();
1351  m_simulator->Run();
1352  }
1353 }
1354 
1355 
1356 void SIM_PLOT_FRAME::onSimReport( wxCommandEvent& aEvent )
1357 {
1358  m_simConsole->AppendText( aEvent.GetString() + "\n" );
1359  m_simConsole->SetInsertionPointEnd();
1360 }
1361 
1362 
1364  SIM_PLOT_FRAME* aPlotFrame )
1365  : m_signal( aSignal ), m_plotFrame( aPlotFrame )
1366 {
1368 
1369  AddMenuItem( this, HIDE_SIGNAL, _( "Hide Signal" ),
1370  _( "Erase the signal from plot screen" ),
1371  KiBitmap( delete_xpm ) );
1372 
1373  TRACE* trace = plot->GetTrace( m_signal );
1374 
1375  if( trace->HasCursor() )
1376  AddMenuItem( this, HIDE_CURSOR, _( "Hide Cursor" ), KiBitmap( pcb_target_xpm ) );
1377  else
1378  AddMenuItem( this, SHOW_CURSOR, _( "Show Cursor" ), KiBitmap( pcb_target_xpm ) );
1379 
1380  Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ), NULL, this );
1381 }
1382 
1383 
1385 {
1386  SIM_PLOT_PANEL* plot = m_plotFrame->CurrentPlot();
1387 
1388  switch( aEvent.GetId() )
1389  {
1390  case HIDE_SIGNAL:
1391  m_plotFrame->removePlot( m_signal );
1392  break;
1393 
1394  case SHOW_CURSOR:
1395  plot->EnableCursor( m_signal, true );
1396  break;
1397 
1398  case HIDE_CURSOR:
1399  plot->EnableCursor( m_signal, false );
1400  break;
1401  }
1402 }
1403 
1404 wxDEFINE_EVENT( EVT_SIM_UPDATE, wxCommandEvent );
1405 wxDEFINE_EVENT( EVT_SIM_REPORT, wxCommandEvent );
1406 
1407 wxDEFINE_EVENT( EVT_SIM_STARTED, wxCommandEvent );
1408 wxDEFINE_EVENT( EVT_SIM_FINISHED, wxCommandEvent );
void onAddSignal(wxCommandEvent &event)
#define ID_MENU_PROBE_SIGNALS
#define ID_SAVE_AS_IMAGE
void ZoomOut(const wxPoint &centerPoint=wxDefaultPosition)
Zoom out current view and refresh display.
Definition: mathplot.cpp:2295
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 LoadSettings(wxConfigBase *aCfg) override
Load common frame parameters from a configuration file.
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_player.h:61
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.
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
long trace
Definition: solve.cpp:232
SIM_PLOT_FRAME * m_parent
void onSignalDblClick(wxMouseEvent &event) override
bool SetSimCommand(const wxString &aCommand)
This file is part of the common library.
wxSplitterWindow * m_splitterSignals
const wxString & GetTitle() const
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:251
#define SIGNALS_PANEL_HEIGHT_ENTRY
const wxString GetText() const override
Function GetText returns the string associated with the text object.
Definition: sch_field.cpp:105
void ShowLegend(bool aEnable)
int m_splitterPlotAndConsoleSashPosition
void menuZoomFit(wxCommandEvent &event) override
Structure to represent a schematic component in the Spice simulation.
virtual void SaveSettings(wxConfigBase *aCfg)
Saves common frame parameters to a configuration data file.
static bool IsPlottable(SIM_TYPE aSimType)
std::list< TUNER_SLIDER * > m_tuners
List of currently displayed tuners
SIM_PLOT_TYPE GetType() const
wxString m_title
Title displayed in the signal list/plot legend
Class SIM_PLOT_FRAME_BASE.
bool IsGridShown() const
void menuNewPlot(wxCommandEvent &aEvent) override
void applyTuners()
Applies component values specified using tunder sliders to the current netlist.
void AddVoltagePlot(const wxString &aNetName)
Adds a voltage plot for a given net name.
virtual wxConfigBase * config()
Return the wxConfigBase used in SaveSettings().
void AddCurrentPlot(const wxString &aDeviceName, const wxString &aParam)
Adds a current plot for a particular device.
Trace descriptor class
Schematic editor (Eeschema) main window.
wxString CsvFileWildcard()
void onSimUpdate(wxCommandEvent &aEvent)
void menuOpenWorkbook(wxCommandEvent &event) override
void menuShowGrid(wxCommandEvent &event) override
void menuZoomIn(wxCommandEvent &event) override
Class REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:61
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:2803
void EnableCursor(const wxString &aName, bool aEnable)
Toggles cursor for a particular trace.
SIM_STATE
const wxString & GetSimCommand() const
void setSubWindowsSashSize()
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:79
#define PLOT_PANEL_WIDTH_ENTRY
SEVERITY
Severity of the reported messages.
Definition: reporter.h:74
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
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:321
#define ID_MENU_SHOW_NETLIST
wxString GetLabelY2() const
Helper class to handle Spice way of expressing values (e.g. 10.5 Meg)
Definition: spice_value.h:32
wxSplitterWindow * m_splitterLeftRight
void ZoomIn(const wxPoint &centerPoint=wxDefaultPosition)
Zoom into current view and refresh display.
Definition: mathplot.cpp:2238
#define CURSORS_PANEL_HEIGHT_ENTRY
wxMenuItem * m_runSimulation
#define ID_MENU_TUNE_SIGNALS
void onSimReport(wxCommandEvent &aEvent)
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
virtual void LoadSettings(wxConfigBase *aCfg)
Load common frame parameters from a configuration file.
void onSimFinished(wxCommandEvent &aEvent)
Interface to receive simulation updates from SPICE_SIMULATOR class.
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
Class KIWAY is a minimalistic software bus for communications between various DLLs/DSOs (DSOs) within...
Definition: kiway.h:258
wxToolBarToolBase * m_toolAddSignals
Subclass of SIM_PLOT_FRAME_BASE, which is generated by wxFormBuilder.
int m_splitterTuneValuesSashPosition
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
#define PLOT_PANEL_HEIGHT_ENTRY
void ShowGrid(bool aEnable)
void onTune(wxCommandEvent &event)
wxToolBarToolBase * m_toolSimulate
void menuSaveImage(wxCommandEvent &event) override
#define ID_MENU_SET_SIMUL
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:1995
const wxString & GetName() const
const wxString What() const
Definition: ki_exception.h:58
#define ID_MENU_ADD_SIGNAL
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 char * name
Definition: DXF_plotter.cpp:61
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
void updateSignalList()
Updates the list of currently plotted signals.
size_t i
Definition: json11.cpp:597
static wxString GetSpiceField(SPICE_FIELD aField, SCH_COMPONENT *aComponent, unsigned aCtl)
Retrieves either the requested field value or the default value.
Class SCH_COMPONENT describes a real schematic component.
Definition: sch_component.h:73
int m_splitterSignalsSashPosition
SIM_PLOT_PANEL * CurrentPlot() const
Returns the currently opened plot panel (or NULL if there is none).
std::map< wxString, TRACE_DESC > TRACE_MAP
wxDEFINE_EVENT(EVT_SIM_UPDATE, wxCommandEvent)
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
SIM_TYPE GetType() const
int m_splitterLeftRightSashPosition
const NETLIST_EXPORTER_PSPICE_SIM * GetExporter() const
Returns the netlist exporter object used for simulations.
static const char * text_xpm[]
void removePlot(const wxString &aPlotName, bool aErase=true)
Removes a plot with a specific title.
void SaveSettings(wxConfigBase *aCfg) override
Saves common frame parameters to a configuration data file.
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:3033
const std::map< wxString, TRACE * > & GetTraces() const
wxSplitterWindow * m_splitterPlotAndConsole
void menuShowGridUpdate(wxUpdateUIEvent &event) override
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)
Function DisplayInfoMessage displays an informational message box with aMessage.
Definition: confirm.cpp:276
Class STRING_FORMATTER implements OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:445
SIM_PLOT_TYPE
Possible plot types
Definition: sim_types.h:35
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_UNDEFINED) override
Function Report is a pure virtual function to override in the derived object.
#define ID_MENU_RUN_SIM
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:244
void addPlot(const wxString &aName, SIM_PLOT_TYPE aType, const wxString &aParam)
Adds a new plot to the current panel.
void onSettings(wxCommandEvent &event)
void onClose(wxCloseEvent &aEvent)
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:
Special netlist exporter flavor that allows one to override simulation commands.
void updateTuners()
Filters out tuners for components that do not exist anymore.
class KI_PARAM_ERROR is a class used to hold a translatable error message and may be used when throwi...
Definition: ki_exception.h:45
wxToolBarToolBase * m_toolSettings
SIM_PLOT_FRAME(KIWAY *aKiway, wxWindow *aParent)
Constructor.