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 
48 {
49  int res = (int) aFirst | (int) aSecond;
50 
51  return (SIM_PLOT_TYPE) res;
52 }
53 
54 
56 {
57 public:
59  : m_parent( aParent )
60  {
61  }
62 
63  REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_UNDEFINED ) override
64  {
65  wxCommandEvent* event = new wxCommandEvent( EVT_SIM_REPORT );
66  event->SetString( aText );
67  wxQueueEvent( m_parent, event );
68  return *this;
69  }
70 
71  bool HasMessage() const override
72  {
73  return false; // Technically "indeterminate" rather than false.
74  }
75 
76  void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) override
77  {
78  wxCommandEvent* event = NULL;
79 
80  switch( aNewState )
81  {
82  case SIM_IDLE:
83  event = new wxCommandEvent( EVT_SIM_FINISHED );
84  break;
85 
86  case SIM_RUNNING:
87  event = new wxCommandEvent( EVT_SIM_STARTED );
88  break;
89 
90  default:
91  wxFAIL;
92  return;
93  }
94 
95  wxQueueEvent( m_parent, event );
96  }
97 
98 private:
100 };
101 
102 
103 TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName,
104  SIM_PLOT_TYPE aType, const wxString& aParam )
105  : m_name( aName ), m_type( aType ), m_param( aParam )
106 {
107  // Title generation
108  m_title = wxString::Format( "%s(%s)", aParam, aName );
109 
110  if( aType & SPT_AC_MAG )
111  m_title += " (mag)";
112  else if( aType & SPT_AC_PHASE )
113  m_title += " (phase)";
114 }
115 
116 // Store the path of saved workbooks during the session
118 
119 SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
120  : SIM_PLOT_FRAME_BASE( aParent ), m_lastSimPlot( nullptr )
121 {
122  SetKiway( this, aKiway );
124 
126 
127  if( m_schematicFrame == NULL )
128  throw std::runtime_error( "There is no schematic window" );
129 
130  // Give an icon
131  wxIcon icon;
132  icon.CopyFromBitmap( KiBitmap( simulator_xpm ) );
133  SetIcon( icon );
134 
135  // Gives a minimal size
136  SetSizeHints( 500, 400, -1, -1, -1, -1 );
137 
138  // Get the previous size and position of windows:
139  LoadSettings( config() );
140 
141  // Give icons to menuitems
143 
145 
146  if( !m_simulator )
147  {
148  throw std::runtime_error( "Could not create simulator instance" );
149  return;
150  }
151 
152  m_simulator->Init();
153 
154  if( m_savedWorkbooksPath.IsEmpty() )
155  {
157  }
158 
159  m_reporter = new SIM_THREAD_REPORTER( this );
160  m_simulator->SetReporter( m_reporter );
161 
163 
164  Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SIM_PLOT_FRAME::onClose ), NULL, this );
165  Connect( EVT_SIM_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onSimUpdate ), NULL, this );
166  Connect( EVT_SIM_REPORT, wxCommandEventHandler( SIM_PLOT_FRAME::onSimReport ), NULL, this );
167  Connect( EVT_SIM_STARTED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimStarted ), NULL, this );
168  Connect( EVT_SIM_FINISHED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimFinished ), NULL, this );
169  Connect( EVT_SIM_CURSOR_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onCursorUpdate ), NULL, this );
170 
171  // Toolbar buttons
172  m_toolSimulate = m_toolBar->AddTool( ID_SIM_RUN, _( "Run/Stop Simulation" ),
173  KiBitmap( sim_run_xpm ), _( "Run Simulation" ), wxITEM_NORMAL );
174  m_toolAddSignals = m_toolBar->AddTool( ID_SIM_ADD_SIGNALS, _( "Add Signals" ),
175  KiBitmap( sim_add_signal_xpm ), _( "Add signals to plot" ), wxITEM_NORMAL );
176  m_toolProbe = m_toolBar->AddTool( ID_SIM_PROBE, _( "Probe" ),
177  KiBitmap( sim_probe_xpm ), _( "Probe signals on the schematic" ), wxITEM_NORMAL );
178  m_toolTune = m_toolBar->AddTool( ID_SIM_TUNE, _( "Tune" ),
179  KiBitmap( sim_tune_xpm ), _( "Tune component values" ), wxITEM_NORMAL );
180  m_toolSettings = m_toolBar->AddTool( wxID_ANY, _( "Settings" ),
181  KiBitmap( sim_settings_xpm ), _( "Simulation settings" ), wxITEM_NORMAL );
182 
183  Connect( m_toolSimulate->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
184  wxCommandEventHandler( SIM_PLOT_FRAME::onSimulate ), NULL, this );
185  Connect( m_toolAddSignals->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
186  wxCommandEventHandler( SIM_PLOT_FRAME::onAddSignal ), NULL, this );
187  Connect( m_toolProbe->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
188  wxCommandEventHandler( SIM_PLOT_FRAME::onProbe ), NULL, this );
189  Connect( m_toolTune->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
190  wxCommandEventHandler( SIM_PLOT_FRAME::onTune ), NULL, this );
191  Connect( m_toolSettings->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
192  wxCommandEventHandler( SIM_PLOT_FRAME::onSettings ), NULL, this );
193 
194  // Bind toolbar buttons event to existing menu event handlers, so they behave the same
195  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSimulate, this, m_runSimulation->GetId() );
196  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onAddSignal, this, m_addSignals->GetId() );
197  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onProbe, this, m_probeSignals->GetId() );
198  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onTune, this, m_tuneValue->GetId() );
199  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onShowNetlist, this, m_showNetlist->GetId() );
200  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSettings, this, m_settings->GetId() );
201 
202  m_toolBar->Realize();
203  m_plotNotebook->SetPageText( 0, _( "Welcome!" ) );
204 
205  // the settings dialog will be created later, on demand.
206  // if created in the ctor, for some obscure reason, there is an issue
207  // on Windows: when open it, the simulator frame is sent to the background.
208  // instead of being behind the dialog frame (as it does)
210 
211  // resize the subwindows size. At least on Windows, calling wxSafeYield before
212  // resizing the subwindows forces the wxSplitWindows size events automatically generated
213  // by wxWidgets to be executed before our resize code.
214  // Otherwise, the changes made by setSubWindowsSashSize are overwritten by one these
215  // events
216  wxSafeYield();
218 }
219 
220 
222 {
223  m_simulator->SetReporter( nullptr );
224  delete m_reporter;
225  delete m_signalsIconColorList;
226 
227  if( m_settingsDlg )
228  m_settingsDlg->Destroy();
229 }
230 
231 #define PLOT_PANEL_WIDTH_ENTRY "SimPlotPanelWidth"
232 #define PLOT_PANEL_HEIGHT_ENTRY "SimPlotPanelHeight"
233 #define SIGNALS_PANEL_HEIGHT_ENTRY "SimSignalPanelHeight"
234 #define CURSORS_PANEL_HEIGHT_ENTRY "SimCursorsPanelHeight"
235 
236 void SIM_PLOT_FRAME::SaveSettings( wxConfigBase* aCfg )
237 {
238  // Save main frame size and position:
240 
241  // Save subwindows sizes
242  aCfg->Write( PLOT_PANEL_WIDTH_ENTRY, m_splitterLeftRight->GetSashPosition() );
243  aCfg->Write( PLOT_PANEL_HEIGHT_ENTRY, m_splitterPlotAndConsole->GetSashPosition() );
244  aCfg->Write( SIGNALS_PANEL_HEIGHT_ENTRY, m_splitterSignals->GetSashPosition() );
245  aCfg->Write( CURSORS_PANEL_HEIGHT_ENTRY, m_splitterTuneValues->GetSashPosition() );
246 }
247 
248 
249 void SIM_PLOT_FRAME::LoadSettings( wxConfigBase* aCfg )
250 {
251  // Read main frame size and position:
253  SetSize( m_FramePos.x, m_FramePos.y, m_FrameSize.x, m_FrameSize.y );
254 
255  // Read subwindows sizes (should be > 0 )
260 }
261 
262 
263 // A small helper struct to handle bitmaps initialisation in menus
265 {
266  int m_MenuId;
268 };
269 
270 
272 {
273  // Give icons to menuitems of the main menubar
274  BM_MENU_INIT_ITEM bm_list[]
275  {
276  // File menu:
277  { wxID_NEW, simulator_xpm },
278  { wxID_OPEN,directory_browser_xpm },
279  { wxID_SAVE, directory_xpm},
282  { wxID_CLOSE, exit_xpm},
283 
284  // simulator menu:
291 
292  // View menu
293  { wxID_ZOOM_IN, zoom_in_xpm},
294  { wxID_ZOOM_OUT, zoom_out_xpm},
295  { wxID_ZOOM_FIT, zoom_fit_in_page_xpm},
298 
299  { 0, nullptr } // Sentinel
300  };
301 
302  // wxMenuItems are already created and attached to the m_mainMenu wxMenuBar.
303  // A problem is the fact setting bitmaps in wxMenuItems after they are attached
304  // to a wxMenu do not work in all cases.
305  // So the trick is:
306  // Remove the wxMenuItem from its wxMenu
307  // Set the bitmap
308  // Insert the modified wxMenuItem to its previous place
309  for( int ii = 0; bm_list[ii].m_MenuId; ++ii )
310  {
311  wxMenuItem* item = m_mainMenu->FindItem( bm_list[ii].m_MenuId );
312 
313  if( !item || !bm_list[ii].m_Bitmap)
314  continue;
315 
316  wxMenu* menu = item->GetMenu();
317  // Calculate the initial index of item inside the wxMenu parent
318  wxMenuItemList& mlist = menu->GetMenuItems();
319  int mpos = mlist.IndexOf( item );
320 
321  if( mpos >= 0 ) // Should be always the case
322  {
323  // Modify the bitmap
324  menu->Remove( item );
325  AddBitmapToMenuItem( item, KiBitmap( bm_list[ii].m_Bitmap ) );
326  // Insert item to its the initial index
327  menu->Insert( mpos, item );
328  }
329  }
330 }
331 
332 
334 {
337 
340 
343 
346 }
347 
348 
350 {
351  STRING_FORMATTER formatter;
352  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
353 
354  if( !m_settingsDlg )
355  m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
356 
357  m_simConsole->Clear();
359 
360  if( plotPanel )
361  m_exporter->SetSimCommand( m_plots[plotPanel].m_simCommand );
362 
363  if( !m_exporter->Format( &formatter, m_settingsDlg->GetNetlistOptions() ) )
364  {
365  DisplayError( this, _( "There were errors during netlist export, aborted." ) );
366  return;
367  }
368 
369  if( m_exporter->GetSimType() == ST_UNKNOWN )
370  {
371  DisplayInfoMessage( this, _( "You need to select the simulation settings first." ) );
372  return;
373  }
374 
375  m_simulator->LoadNetlist( formatter.GetString() );
376  updateTuners();
377  applyTuners();
378  m_simulator->Run();
379 }
380 
381 
383 {
384  m_simulator->Stop();
385 }
386 
387 
389 {
390  return m_simulator ? m_simulator->IsRunning() : false;
391 }
392 
393 
395 {
396  SIM_PLOT_PANEL* plotPanel = new SIM_PLOT_PANEL( aSimType, m_plotNotebook, wxID_ANY );
397 
398  plotPanel->EnableMouseWheelPan(
400 
401  if( m_welcomePanel )
402  {
403  m_plotNotebook->DeletePage( 0 );
404  m_welcomePanel = nullptr;
405  }
406 
407  m_plotNotebook->AddPage( plotPanel, wxString::Format( _( "Plot%u" ),
408  (unsigned int) m_plotNotebook->GetPageCount() + 1 ), true );
409 
410  m_plots[plotPanel] = PLOT_INFO();
411 
412  return plotPanel;
413 }
414 
415 
416 void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName )
417 {
418  addPlot( aNetName, SPT_VOLTAGE, "V" );
419 }
420 
421 
422 void SIM_PLOT_FRAME::AddCurrentPlot( const wxString& aDeviceName, const wxString& aParam )
423 {
424  addPlot( aDeviceName, SPT_CURRENT, aParam );
425 }
426 
427 
429 {
430  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
431 
432  if( !plotPanel )
433  return;
434 
435  // For now limit the tuner tool to RLC components
436  char primitiveType = NETLIST_EXPORTER_PSPICE::GetSpiceField( SF_PRIMITIVE, aComponent, 0 )[0];
437 
438  if( primitiveType != SP_RESISTOR && primitiveType != SP_CAPACITOR && primitiveType != SP_INDUCTOR )
439  return;
440 
441  const wxString componentName = aComponent->GetField( REFERENCE )->GetText();
442 
443  // Do not add multiple instances for the same component
444  auto tunerIt = std::find_if( m_tuners.begin(), m_tuners.end(), [&]( const TUNER_SLIDER* t )
445  {
446  return t->GetComponentName() == componentName;
447  }
448  );
449 
450  if( tunerIt != m_tuners.end() )
451  return; // We already have it
452 
453  try
454  {
455  TUNER_SLIDER* tuner = new TUNER_SLIDER( this, m_tunePanel, aComponent );
456  m_tuneSizer->Add( tuner );
457  m_tuners.push_back( tuner );
458  m_tunePanel->Layout();
459  }
460  catch( const KI_PARAM_ERROR& e )
461  {
462  // Sorry, no bonus
463  DisplayError( nullptr, e.What() );
464  }
465 }
466 
467 
468 void SIM_PLOT_FRAME::RemoveTuner( TUNER_SLIDER* aTuner, bool aErase )
469 {
470  if( aErase )
471  m_tuners.remove( aTuner );
472 
473  aTuner->Destroy();
474  m_tunePanel->Layout();
475 }
476 
477 
479 {
480  wxWindow* curPage = m_plotNotebook->GetCurrentPage();
481 
482  return ( curPage == m_welcomePanel ) ? nullptr : static_cast<SIM_PLOT_PANEL*>( curPage );
483 }
484 
485 
487 {
488  return m_exporter.get();
489 }
490 
491 
492 void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam )
493 {
494  SIM_TYPE simType = m_exporter->GetSimType();
495 
496  if( simType == ST_UNKNOWN )
497  {
498  m_simConsole->AppendText( _( "Error: simulation type not defined!\n" ) );
499  m_simConsole->SetInsertionPointEnd();
500  return;
501  }
502  else if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
503  {
504  m_simConsole->AppendText( _( "Error: simulation type doesn't support plotting!\n" ) );
505  m_simConsole->SetInsertionPointEnd();
506  return;
507  }
508 
509  // Create a new plot if the current one displays a different type
510  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
511 
512  if( !plotPanel || plotPanel->GetType() != simType )
513  plotPanel = NewPlotPanel( simType );
514 
515  TRACE_DESC descriptor( *m_exporter, aName, aType, aParam );
516 
517  bool updated = false;
518  SIM_PLOT_TYPE xAxisType = GetXAxisType( simType );
519 
520  if( xAxisType == SPT_LIN_FREQUENCY || xAxisType == SPT_LOG_FREQUENCY )
521  {
522  int baseType = descriptor.GetType() & ~( SPT_AC_MAG | SPT_AC_PHASE );
523 
524  // Add two plots: magnitude & phase
525  TRACE_DESC mag_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_MAG ) );
526  TRACE_DESC phase_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_PHASE ) );
527 
528  updated |= updatePlot( mag_desc, plotPanel );
529  updated |= updatePlot( phase_desc, plotPanel );
530  }
531  else
532  {
533  updated = updatePlot( descriptor, plotPanel );
534  }
535 
536  if( updated )
537  {
539  }
540 }
541 
542 
543 void SIM_PLOT_FRAME::removePlot( const wxString& aPlotName, bool aErase )
544 {
545  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
546 
547  if( !plotPanel )
548  return;
549 
550  if( aErase )
551  {
552  auto& traceMap = m_plots[plotPanel].m_traces;
553  auto traceIt = traceMap.find( aPlotName );
554  wxASSERT( traceIt != traceMap.end() );
555  traceMap.erase( traceIt );
556  }
557 
558  wxASSERT( plotPanel->TraceShown( aPlotName ) );
559  plotPanel->DeleteTrace( aPlotName );
560  plotPanel->Fit();
561 
563  updateCursors();
564 }
565 
566 
568 {
570 }
571 
572 
573 bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* aPanel )
574 {
575  SIM_TYPE simType = m_exporter->GetSimType();
576  wxString spiceVector = m_exporter->GetSpiceVector( aDescriptor.GetName(),
577  aDescriptor.GetType(), aDescriptor.GetParam() );
578 
579  if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
580  {
581  // There is no plot to be shown
582  m_simulator->Command( wxString::Format( "print %s", spiceVector ).ToStdString() );
583 
584  return false;
585  }
586 
587  // First, handle the x axis
588  wxString xAxisName( m_simulator->GetXAxis( simType ) );
589 
590  if( xAxisName.IsEmpty() )
591  return false;
592 
593  auto data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() );
594  unsigned int size = data_x.size();
595 
596  if( data_x.empty() )
597  return false;
598 
599  SIM_PLOT_TYPE plotType = aDescriptor.GetType();
600  std::vector<double> data_y;
601 
602  // Now, Y axis data
603  switch( m_exporter->GetSimType() )
604  {
605  case ST_AC:
606  {
607  wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ),
608  "Cannot set both AC_PHASE and AC_MAG bits" );
609 
610  if( plotType & SPT_AC_MAG )
611  data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
612  else if( plotType & SPT_AC_PHASE )
613  data_y = m_simulator->GetPhasePlot( (const char*) spiceVector.c_str() );
614  else
615  wxASSERT_MSG( false, "Plot type missing AC_PHASE or AC_MAG bit" );
616  }
617  break;
618 
619  case ST_NOISE:
620  case ST_DC:
621  case ST_TRANSIENT:
622  {
623  data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
624  }
625  break;
626 
627  default:
628  wxASSERT_MSG( false, "Unhandled plot type" );
629  return false;
630  }
631 
632  if( data_y.size() != size )
633  return false;
634 
635  // If we did a two-source DC analysis, we need to split the resulting vector and add traces
636  // for each input step
637  SPICE_DC_PARAMS source1, source2;
638 
639  if( m_exporter->GetSimType() == ST_DC &&
640  m_exporter->ParseDCCommand( m_exporter->GetUsedSimCommand(), &source1, &source2 ) )
641  {
642  if( !source2.m_source.IsEmpty() )
643  {
644  // Source 1 is the inner loop, so lets add traces for each Source 2 (outer loop) step
645  SPICE_VALUE v = source2.m_vstart;
646  wxString name;
647 
648  size_t offset = 0;
649  size_t outer = ( size_t )( ( source2.m_vend - v ) / source2.m_vincrement ).ToDouble();
650  size_t inner = data_x.size() / ( outer + 1 );
651 
652  wxASSERT( data_x.size() % ( outer + 1 ) == 0 );
653 
654  for( size_t idx = 0; idx <= outer; idx++ )
655  {
656  name = wxString::Format( "%s (%s = %s V)", aDescriptor.GetTitle(),
657  source2.m_source, v.ToString() );
658 
659  std::vector<double> sub_x( data_x.begin() + offset,
660  data_x.begin() + offset + inner );
661  std::vector<double> sub_y( data_y.begin() + offset,
662  data_y.begin() + offset + inner );
663 
664  if( aPanel->AddTrace( name, inner,
665  sub_x.data(), sub_y.data(), aDescriptor.GetType() ) )
666  {
667  m_plots[aPanel].m_traces.insert( std::make_pair( name, aDescriptor ) );
668  }
669 
670  v = v + source2.m_vincrement;
671  offset += inner;
672  }
673 
674  return true;
675  }
676  }
677 
678  if( aPanel->AddTrace( aDescriptor.GetTitle(), size,
679  data_x.data(), data_y.data(), aDescriptor.GetType() ) )
680  {
681  m_plots[aPanel].m_traces.insert( std::make_pair( aDescriptor.GetTitle(), aDescriptor ) );
682  }
683 
684  return true;
685 }
686 
687 
689 {
690  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
691 
692  if( !plotPanel )
693  return;
694 
695  m_signals->ClearAll();
696 
697  wxSize size = m_signals->GetClientSize();
698  m_signals->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x );
699 
700  // Build an image list, to show the color of the corresponding trace
701  // in the plot panel
702  // This image list is used for trace and cursor lists
703  wxMemoryDC bmDC;
704  const int isize = bmDC.GetCharHeight();
705 
707  m_signalsIconColorList = new wxImageList( isize, isize, false );
708  else
709  m_signalsIconColorList->RemoveAll();
710 
711  for( const auto& trace : CurrentPlot()->GetTraces() )
712  {
713  wxBitmap bitmap( isize, isize );
714  bmDC.SelectObject( bitmap );
715  wxColour tcolor = trace.second->GetTraceColour();
716 
717  wxColour bgColor = m_signals->wxWindow::GetBackgroundColour();
718  bmDC.SetPen( wxPen( bgColor ) );
719  bmDC.SetBrush( wxBrush( bgColor ) );
720  bmDC.DrawRectangle( 0, 0, isize, isize ); // because bmDC.Clear() does not work in wxGTK
721 
722  bmDC.SetPen( wxPen( tcolor ) );
723  bmDC.SetBrush( wxBrush( tcolor ) );
724  bmDC.DrawRectangle( 0, isize / 4 + 1, isize, isize / 2 );
725 
726  bmDC.SelectObject( wxNullBitmap ); // Needed to initialize bitmap
727 
728  bitmap.SetMask( new wxMask( bitmap, *wxBLACK ) );
729  m_signalsIconColorList->Add( bitmap );
730  }
731 
732  if( bmDC.IsOk() )
733  {
734  bmDC.SetBrush( wxNullBrush );
735  bmDC.SetPen( wxNullPen );
736  }
737 
738  m_signals->SetImageList( m_signalsIconColorList, wxIMAGE_LIST_SMALL );
739 
740  // Fill the signals listctrl. Keep the order of names and
741  // the order of icon color identical, because the icons
742  // are also used in cursor list, and the color index is
743  // calculated from the trace name index
744  int imgidx = 0;
745 
746  for( const auto& trace : m_plots[plotPanel].m_traces )
747  {
748  m_signals->InsertItem( imgidx, trace.first, imgidx );
749  imgidx++;
750  }
751 }
752 
753 
755 {
756  wxQueueEvent( this, new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
757 }
758 
759 
761 {
762  const auto& spiceItems = m_exporter->GetSpiceItems();
763 
764  for( auto it = m_tuners.begin(); it != m_tuners.end(); /* iteration inside the loop */ )
765  {
766  const wxString& ref = (*it)->GetComponentName();
767 
768  if( std::find_if( spiceItems.begin(), spiceItems.end(), [&]( const SPICE_ITEM& item )
769  {
770  return item.m_refName == ref;
771  }) == spiceItems.end() )
772  {
773  // The component does not exist anymore, remove the associated tuner
774  TUNER_SLIDER* tuner = *it;
775  it = m_tuners.erase( it );
776  RemoveTuner( tuner, false );
777  }
778  else
779  {
780  ++it;
781  }
782  }
783 }
784 
785 
787 {
788  for( auto& tuner : m_tuners )
789  {
791  std::string command( "alter @" + tuner->GetSpiceName()
792  + "=" + tuner->GetValue().ToSpiceString() );
793 
794  m_simulator->Command( command );
795  }
796 }
797 
798 
799 bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath )
800 {
801  m_plots.clear();
802  m_plotNotebook->DeleteAllPages();
803 
804  wxTextFile file( aPath );
805 
806  if( !file.Open() )
807  return false;
808 
809  long plotsCount;
810 
811  if( !file.GetFirstLine().ToLong( &plotsCount ) ) // GetFirstLine instead of GetNextLine
812  return false;
813 
814  for( long i = 0; i < plotsCount; ++i )
815  {
816  long plotType, tracesCount;
817 
818  if( !file.GetNextLine().ToLong( &plotType ) )
819  return false;
820 
821  SIM_PLOT_PANEL* plotPanel = NewPlotPanel( (SIM_TYPE) plotType );
822  m_plots[plotPanel].m_simCommand = file.GetNextLine();
823  StartSimulation();
824 
825  // Perform simulation, so plots can be added with values
826  do
827  {
828  wxThread::This()->Sleep( 50 );
829  }
830  while( IsSimulationRunning() );
831 
832  if( !file.GetNextLine().ToLong( &tracesCount ) )
833  return false;
834 
835  for( long j = 0; j < tracesCount; ++j )
836  {
837  long traceType;
838  wxString name, param;
839 
840  if( !file.GetNextLine().ToLong( &traceType ) )
841  return false;
842 
843  name = file.GetNextLine();
844  param = file.GetNextLine();
845 
846  if( name.IsEmpty() || param.IsEmpty() )
847  return false;
848 
849  addPlot( name, (SIM_PLOT_TYPE) traceType, param );
850  }
851  }
852 
853  return true;
854 }
855 
856 
857 bool SIM_PLOT_FRAME::saveWorkbook( const wxString& aPath )
858 {
859 
860  wxString savePath = aPath;
861 
862  if( !savePath.Lower().EndsWith(".wbk"))
863  {
864  savePath += ".wbk";
865  };
866 
867 
868  wxTextFile file( savePath );
869 
870  if( file.Exists() )
871  {
872  if( !file.Open() )
873  return false;
874 
875  file.Clear();
876  }
877  else
878  {
879  file.Create();
880  }
881 
882  file.AddLine( wxString::Format( "%lu", m_plots.size() ) );
883 
884  for( const auto& plot : m_plots )
885  {
886  file.AddLine( wxString::Format( "%d", plot.first->GetType() ) );
887  file.AddLine( plot.second.m_simCommand );
888  file.AddLine( wxString::Format( "%lu", plot.second.m_traces.size() ) );
889 
890  for( const auto& trace : plot.second.m_traces )
891  {
892  file.AddLine( wxString::Format( "%d", trace.second.GetType() ) );
893  file.AddLine( trace.second.GetName() );
894  file.AddLine( trace.second.GetParam() );
895  }
896  }
897 
898  bool res = file.Write();
899  file.Close();
900 
901  return res;
902 }
903 
904 
906 {
907  switch( aType )
908  {
909  case ST_AC:
910  return SPT_LIN_FREQUENCY;
912 
913  case ST_DC:
914  return SPT_SWEEP;
915 
916  case ST_TRANSIENT:
917  return SPT_TIME;
918 
919  default:
920  wxASSERT_MSG( false, "Unhandled simulation type" );
921  return (SIM_PLOT_TYPE) 0;
922  }
923 }
924 
925 
926 void SIM_PLOT_FRAME::menuNewPlot( wxCommandEvent& aEvent )
927 {
928  SIM_TYPE type = m_exporter->GetSimType();
929 
930  if( SIM_PLOT_PANEL::IsPlottable( type ) )
931  {
932  SIM_PLOT_PANEL* prevPlot = CurrentPlot();
933  SIM_PLOT_PANEL* newPlot = NewPlotPanel( type );
934 
935  // If the previous plot had the same type, copy the simulation command
936  if( prevPlot )
937  m_plots[newPlot].m_simCommand = m_plots[prevPlot].m_simCommand;
938  }
939 }
940 
941 
942 void SIM_PLOT_FRAME::menuOpenWorkbook( wxCommandEvent& event )
943 {
944  wxFileDialog openDlg( this, _( "Open simulation workbook" ), m_savedWorkbooksPath, "",
945  WorkbookFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
946 
947  if( openDlg.ShowModal() == wxID_CANCEL )
948  return;
949 
950  m_savedWorkbooksPath = openDlg.GetDirectory();
951 
952  if( !loadWorkbook( openDlg.GetPath() ) )
953  DisplayError( this, _( "There was an error while opening the workbook file" ) );
954 }
955 
956 
957 void SIM_PLOT_FRAME::menuSaveWorkbook( wxCommandEvent& event )
958 {
959  if( !CurrentPlot() )
960  return;
961 
962  wxFileDialog saveDlg( this, _( "Save Simulation Workbook" ), m_savedWorkbooksPath, "",
963  WorkbookFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
964 
965  if( saveDlg.ShowModal() == wxID_CANCEL )
966  return;
967 
968  m_savedWorkbooksPath = saveDlg.GetDirectory();
969 
970  if( !saveWorkbook( saveDlg.GetPath() ) )
971  DisplayError( this, _( "There was an error while saving the workbook file" ) );
972 }
973 
974 
975 void SIM_PLOT_FRAME::menuSaveImage( wxCommandEvent& event )
976 {
977  if( !CurrentPlot() )
978  return;
979 
980  wxFileDialog saveDlg( this, _( "Save Plot as Image" ), "", "",
981  PngFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
982 
983  if( saveDlg.ShowModal() == wxID_CANCEL )
984  return;
985 
986  CurrentPlot()->SaveScreenshot( saveDlg.GetPath(), wxBITMAP_TYPE_PNG );
987 }
988 
989 
990 void SIM_PLOT_FRAME::menuSaveCsv( wxCommandEvent& event )
991 {
992  if( !CurrentPlot() )
993  return;
994 
995  const wxChar SEPARATOR = ';';
996 
997  wxFileDialog saveDlg( this, _( "Save Plot Data" ), "", "",
998  CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
999 
1000  if( saveDlg.ShowModal() == wxID_CANCEL )
1001  return;
1002 
1003  wxFile out( saveDlg.GetPath(), wxFile::write );
1004  bool timeWritten = false;
1005 
1006  for( const auto& t : CurrentPlot()->GetTraces() )
1007  {
1008  const TRACE* trace = t.second;
1009 
1010  if( !timeWritten )
1011  {
1012  out.Write( wxString::Format( "Time%c", SEPARATOR ) );
1013 
1014  for( double v : trace->GetDataX() )
1015  out.Write( wxString::Format( "%f%c", v, SEPARATOR ) );
1016 
1017  out.Write( "\r\n" );
1018  timeWritten = true;
1019  }
1020 
1021  out.Write( wxString::Format( "%s%c", t.first, SEPARATOR ) );
1022 
1023  for( double v : trace->GetDataY() )
1024  out.Write( wxString::Format( "%f%c", v, SEPARATOR ) );
1025 
1026  out.Write( "\r\n" );
1027  }
1028 
1029  out.Close();
1030 }
1031 
1032 
1033 void SIM_PLOT_FRAME::menuZoomIn( wxCommandEvent& event )
1034 {
1035  if( CurrentPlot() )
1036  CurrentPlot()->ZoomIn();
1037 }
1038 
1039 
1040 void SIM_PLOT_FRAME::menuZoomOut( wxCommandEvent& event )
1041 {
1042  if( CurrentPlot() )
1043  CurrentPlot()->ZoomOut();
1044 }
1045 
1046 
1047 void SIM_PLOT_FRAME::menuZoomFit( wxCommandEvent& event )
1048 {
1049  if( CurrentPlot() )
1050  CurrentPlot()->Fit();
1051 }
1052 
1053 
1054 void SIM_PLOT_FRAME::menuShowGrid( wxCommandEvent& event )
1055 {
1056  SIM_PLOT_PANEL* plot = CurrentPlot();
1057 
1058  if( plot )
1059  plot->ShowGrid( !plot->IsGridShown() );
1060 }
1061 
1062 
1063 void SIM_PLOT_FRAME::menuShowGridUpdate( wxUpdateUIEvent& event )
1064 {
1065  SIM_PLOT_PANEL* plot = CurrentPlot();
1066 
1067  event.Check( plot ? plot->IsGridShown() : false );
1068 }
1069 
1070 
1071 void SIM_PLOT_FRAME::menuShowLegend( wxCommandEvent& event )
1072 {
1073  SIM_PLOT_PANEL* plot = CurrentPlot();
1074 
1075  if( plot )
1076  plot->ShowLegend( !plot->IsLegendShown() );
1077 }
1078 
1079 
1080 void SIM_PLOT_FRAME::menuShowLegendUpdate( wxUpdateUIEvent& event )
1081 {
1082  SIM_PLOT_PANEL* plot = CurrentPlot();
1083  event.Check( plot ? plot->IsLegendShown() : false );
1084 }
1085 
1086 
1087 void SIM_PLOT_FRAME::menuShowDotted( wxCommandEvent& event )
1088 {
1089  SIM_PLOT_PANEL* plot = CurrentPlot();
1090 
1091  if( plot )
1092  plot->SetDottedCurrentPhase( !plot->GetDottedCurrentPhase() );
1093 }
1094 
1095 
1096 void SIM_PLOT_FRAME::menuShowDottedUpdate( wxUpdateUIEvent& event )
1097 {
1098  SIM_PLOT_PANEL* plot = CurrentPlot();
1099 
1100  event.Check( plot ? plot->GetDottedCurrentPhase() : false );
1101 }
1102 
1103 
1104 void SIM_PLOT_FRAME::onPlotClose( wxAuiNotebookEvent& event )
1105 {
1106  int idx = event.GetSelection();
1107 
1108  if( idx == wxNOT_FOUND )
1109  return;
1110 
1111  SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetPage( idx ) );
1112 
1113  if( !plotPanel )
1114  return;
1115 
1116  m_plots.erase( plotPanel );
1117  updateSignalList();
1118  updateCursors();
1119 }
1120 
1121 
1122 void SIM_PLOT_FRAME::onPlotChanged( wxAuiNotebookEvent& event )
1123 {
1124  updateSignalList();
1125  updateCursors();
1126 }
1127 
1128 
1129 void SIM_PLOT_FRAME::onSignalDblClick( wxMouseEvent& event )
1130 {
1131  // Remove signal from the plot panel when double clicked
1132  long idx = m_signals->GetFocusedItem();
1133 
1134  if( idx != wxNOT_FOUND )
1135  removePlot( m_signals->GetItemText( idx, 0 ) );
1136 }
1137 
1138 
1139 void SIM_PLOT_FRAME::onSignalRClick( wxListEvent& event )
1140 {
1141  int idx = event.GetIndex();
1142 
1143  if( idx != wxNOT_FOUND )
1144  m_signals->Select( idx );
1145 
1146  idx = m_signals->GetFirstSelected();
1147 
1148  if( idx != wxNOT_FOUND )
1149  {
1150  const wxString& netName = m_signals->GetItemText( idx, 0 );
1151  SIGNAL_CONTEXT_MENU ctxMenu( netName, this );
1152  m_signals->PopupMenu( &ctxMenu );
1153  }
1154 }
1155 
1156 
1157 void SIM_PLOT_FRAME::onSimulate( wxCommandEvent& event )
1158 {
1159  if( IsSimulationRunning() )
1160  StopSimulation();
1161  else
1162  StartSimulation();
1163 }
1164 
1165 
1166 void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
1167 {
1168  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1169 
1170  // Initial processing is required to e.g. display a list of power sources
1172 
1173  if( !m_exporter->ProcessNetlist( NET_ALL_FLAGS ) )
1174  {
1175  DisplayError( this, _( "There were errors during netlist export, aborted." ) );
1176  return;
1177  }
1178 
1179  if( !m_settingsDlg )
1180  m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
1181 
1182  if( plotPanel )
1183  m_settingsDlg->SetSimCommand( m_plots[plotPanel].m_simCommand );
1184 
1186 
1187  if( m_settingsDlg->ShowModal() == wxID_OK )
1188  {
1189  wxString newCommand = m_settingsDlg->GetSimCommand();
1190  SIM_TYPE newSimType = NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( newCommand );
1191 
1192  // If it is a new simulation type, open a new plot
1193  if( !plotPanel || ( plotPanel && plotPanel->GetType() != newSimType ) )
1194  {
1195  plotPanel = NewPlotPanel( newSimType );
1196  }
1197 
1198  m_plots[plotPanel].m_simCommand = newCommand;
1199  }
1200 }
1201 
1202 
1203 void SIM_PLOT_FRAME::onAddSignal( wxCommandEvent& event )
1204 {
1205  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1206 
1207  if( !plotPanel || !m_exporter || plotPanel->GetType() != m_exporter->GetSimType() )
1208  {
1209  DisplayInfoMessage( this, _( "You need to run simulation first." ) );
1210  return;
1211  }
1212 
1213  DIALOG_SIGNAL_LIST dialog( this, m_exporter.get() );
1214  dialog.ShowModal();
1215 }
1216 
1217 
1218 void SIM_PLOT_FRAME::onProbe( wxCommandEvent& event )
1219 {
1220  if( m_schematicFrame == NULL )
1221  return;
1222 
1224  m_schematicFrame->Raise();
1225 }
1226 
1227 
1228 void SIM_PLOT_FRAME::onTune( wxCommandEvent& event )
1229 {
1230  if( m_schematicFrame == NULL )
1231  return;
1232 
1234  m_schematicFrame->Raise();
1235 }
1236 
1237 void SIM_PLOT_FRAME::onShowNetlist( wxCommandEvent& event )
1238 {
1239  class NETLIST_VIEW_DIALOG : public wxDialog
1240  {
1241  public:
1242  enum
1243  {
1244  MARGIN_LINE_NUMBERS
1245  };
1246 
1247  void onClose( wxCloseEvent& evt )
1248  {
1249  EndModal( GetReturnCode() );
1250  }
1251 
1252  NETLIST_VIEW_DIALOG(wxWindow* parent, wxString source) :
1253  wxDialog(parent, wxID_ANY, "SPICE Netlist",
1254  wxDefaultPosition, wxSize(1500,900),
1255  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
1256  {
1257  wxStyledTextCtrl* text = new wxStyledTextCtrl( this, wxID_ANY );
1258 
1259  text->SetMarginWidth( MARGIN_LINE_NUMBERS, 50 );
1260  text->StyleSetForeground( wxSTC_STYLE_LINENUMBER, wxColour( 75, 75, 75 ) );
1261  text->StyleSetBackground( wxSTC_STYLE_LINENUMBER, wxColour( 220, 220, 220 ) );
1262  text->SetMarginType( MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER );
1263 
1264  text->SetWrapMode( wxSTC_WRAP_WORD );
1265 
1266  text->SetText( source );
1267 
1268  text->StyleClearAll();
1269  text->SetLexer( wxSTC_LEX_SPICE );
1270 
1271  wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
1272  sizer->Add( text, 1, wxEXPAND );
1273  SetSizer( sizer );
1274 
1275  Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( NETLIST_VIEW_DIALOG::onClose ), NULL,
1276  this );
1277  }
1278  };
1279 
1280  if( m_schematicFrame == NULL || m_simulator == NULL )
1281  return;
1282 
1283  NETLIST_VIEW_DIALOG dlg( this, m_simulator->GetNetlist() );
1284  dlg.ShowModal();
1285 }
1286 
1287 
1288 void SIM_PLOT_FRAME::onClose( wxCloseEvent& aEvent )
1289 {
1290  SaveSettings( config() );
1291 
1292  if( IsSimulationRunning() )
1293  m_simulator->Stop();
1294 
1295  // Cancel a running simProbe or simTune tool
1297 
1298  Destroy();
1299 }
1300 
1301 
1302 void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
1303 {
1304  wxSize size = m_cursors->GetClientSize();
1305  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1306  m_cursors->ClearAll();
1307 
1308  if( !plotPanel )
1309  return;
1310 
1312  m_cursors->SetImageList(m_signalsIconColorList, wxIMAGE_LIST_SMALL);
1313 
1314  // Fill the signals listctrl
1315  m_cursors->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x / 2 );
1316  const long X_COL = m_cursors->AppendColumn( plotPanel->GetLabelX(), wxLIST_FORMAT_LEFT, size.x / 4 );
1317 
1318  wxString labelY1 = plotPanel->GetLabelY1();
1319  wxString labelY2 = plotPanel->GetLabelY2();
1320  wxString labelY;
1321 
1322  if( !labelY2.IsEmpty() )
1323  labelY = labelY1 + " / " + labelY2;
1324  else
1325  labelY = labelY1;
1326 
1327  const long Y_COL = m_cursors->AppendColumn( labelY, wxLIST_FORMAT_LEFT, size.x / 4 );
1328 
1329  // Update cursor values
1330  int itemidx = 0;
1331  for( const auto& trace : plotPanel->GetTraces() )
1332  {
1333  if( CURSOR* cursor = trace.second->GetCursor() )
1334  {
1335  // Find the right icon color in list.
1336  // It is the icon used in m_signals list for the same trace
1337  long iconColor = m_signals->FindItem( -1, trace.first );
1338 
1339  const wxRealPoint coords = cursor->GetCoords();
1340  long idx = m_cursors->InsertItem( itemidx++, trace.first, iconColor );
1341  m_cursors->SetItem( idx, X_COL, SPICE_VALUE( coords.x ).ToSpiceString() );
1342  m_cursors->SetItem( idx, Y_COL, SPICE_VALUE( coords.y ).ToSpiceString() );
1343  }
1344  }
1345 }
1346 
1347 
1348 void SIM_PLOT_FRAME::onSimStarted( wxCommandEvent& aEvent )
1349 {
1350  m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_stop_xpm ) );
1351  SetCursor( wxCURSOR_ARROWWAIT );
1352 }
1353 
1354 
1355 void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
1356 {
1357  m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_run_xpm ) );
1358  SetCursor( wxCURSOR_ARROW );
1359 
1360  SIM_TYPE simType = m_exporter->GetSimType();
1361 
1362  if( simType == ST_UNKNOWN )
1363  return;
1364 
1365  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1366 
1367  if( !plotPanel || plotPanel->GetType() != simType )
1368  plotPanel = NewPlotPanel( simType );
1369 
1370  if( IsSimulationRunning() )
1371  return;
1372 
1373  // If there are any signals plotted, update them
1374  if( SIM_PLOT_PANEL::IsPlottable( simType ) )
1375  {
1376  TRACE_MAP& traceMap = m_plots[plotPanel].m_traces;
1377 
1378  for( auto it = traceMap.begin(); it != traceMap.end(); /* iteration occurs in the loop */)
1379  {
1380  if( !updatePlot( it->second, plotPanel ) )
1381  {
1382  removePlot( it->first, false );
1383  it = traceMap.erase( it ); // remove a plot that does not exist anymore
1384  }
1385  else
1386  {
1387  ++it;
1388  }
1389  }
1390 
1391  updateSignalList();
1392  plotPanel->UpdateAll();
1393  plotPanel->ResetScales();
1394  }
1395  else
1396  {
1398  for( const auto& net : m_exporter->GetNetIndexMap() )
1399  {
1400  int node = net.second;
1401 
1402  if( node > 0 )
1403  m_simulator->Command( wxString::Format( "print v(%d)", node ).ToStdString() );
1404  }
1405  }
1406 }
1407 
1408 
1409 void SIM_PLOT_FRAME::onSimUpdate( wxCommandEvent& aEvent )
1410 {
1411  if( IsSimulationRunning() )
1412  StopSimulation();
1413 
1414  if( CurrentPlot() != m_lastSimPlot )
1415  {
1416  // We need to rerun simulation, as the simulator currently stores
1417  // results for another plot
1418  StartSimulation();
1419  }
1420  else
1421  {
1422  // Incremental update
1423  m_simConsole->Clear();
1424  // Do not export netlist, it is already stored in the simulator
1425  applyTuners();
1426  m_simulator->Run();
1427  }
1428 }
1429 
1430 
1431 void SIM_PLOT_FRAME::onSimReport( wxCommandEvent& aEvent )
1432 {
1433  m_simConsole->AppendText( aEvent.GetString() + "\n" );
1434  m_simConsole->SetInsertionPointEnd();
1435 }
1436 
1437 
1439  SIM_PLOT_FRAME* aPlotFrame )
1440  : m_signal( aSignal ), m_plotFrame( aPlotFrame )
1441 {
1443 
1444  AddMenuItem( this, HIDE_SIGNAL, _( "Hide Signal" ),
1445  _( "Erase the signal from plot screen" ),
1446  KiBitmap( delete_xpm ) );
1447 
1448  TRACE* trace = plot->GetTrace( m_signal );
1449 
1450  if( trace->HasCursor() )
1451  AddMenuItem( this, HIDE_CURSOR, _( "Hide Cursor" ), KiBitmap( pcb_target_xpm ) );
1452  else
1453  AddMenuItem( this, SHOW_CURSOR, _( "Show Cursor" ), KiBitmap( pcb_target_xpm ) );
1454 
1455  Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ), NULL, this );
1456 }
1457 
1458 
1460 {
1461  SIM_PLOT_PANEL* plot = m_plotFrame->CurrentPlot();
1462 
1463  switch( aEvent.GetId() )
1464  {
1465  case HIDE_SIGNAL:
1466  m_plotFrame->removePlot( m_signal );
1467  break;
1468 
1469  case SHOW_CURSOR:
1470  plot->EnableCursor( m_signal, true );
1471  break;
1472 
1473  case HIDE_CURSOR:
1474  plot->EnableCursor( m_signal, false );
1475  break;
1476  }
1477 }
1478 
1479 wxDEFINE_EVENT( EVT_SIM_UPDATE, wxCommandEvent );
1480 wxDEFINE_EVENT( EVT_SIM_REPORT, wxCommandEvent );
1481 
1482 wxDEFINE_EVENT( EVT_SIM_STARTED, wxCommandEvent );
1483 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:236
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: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 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_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.
KIGFX::VIEW_CONTROLS * GetViewControls() const
Function GetViewControls() Returns a pointer to the VIEW_CONTROLS instance used in the panel.
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
void EnableMouseWheelPan(bool enabled)
Enable/disable trackpad friendly panning (2-axis scroll wheel)
Definition: mathplot.h:1215
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
bool HasCursor() 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
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.
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:109
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.
virtual wxConfigBase * config()
Returns the wxConfigBase used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
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 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: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: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:79
#define PLOT_PANEL_WIDTH_ENTRY
wxString ToString() const
Returns string value as when converting double to string (e.g.
const BITMAP_OPAQUE sim_add_signal_xpm[1]
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
#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
wxSplitterWindow * m_splitterLeftRight
void ZoomIn(const wxPoint &centerPoint=wxDefaultPosition)
Zoom into current view and refresh display.
Definition: mathplot.cpp:2259
#define CURSORS_PANEL_HEIGHT_ENTRY
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
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
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.
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:274
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
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:31
void updateSignalList()
Updates the list of currently plotted signals.
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:89
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.
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:3066
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)
Display an informational message box with aMessage.
Definition: confirm.cpp:264
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 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)
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:
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