KiCad PCB EDA Suite
Loading...
Searching...
No Matches
simulator_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-2023 CERN
5 * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Tomasz Wlostowski <[email protected]>
7 * @author Maciej Suminski <[email protected]>
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/debug.h>
28
29// For some obscure reason, needed on msys2 with some wxWidgets versions (3.0) to avoid
30// undefined symbol at link stage (due to use of #include <pegtl.hpp>)
31// Should not create issues on other platforms
32#include <wx/menu.h>
33
35#include <sch_edit_frame.h>
36#include <kiway.h>
37#include <confirm.h>
38#include <bitmaps.h>
42#include <widgets/wx_grid.h>
43#include <grid_tricks.h>
44#include <eda_pattern_match.h>
45#include <tool/tool_manager.h>
47#include <tool/action_manager.h>
48#include <tool/action_toolbar.h>
49#include <tool/common_control.h>
51#include <tools/ee_actions.h>
52#include <string_utils.h>
53#include <pgm_base.h>
54#include "ngspice.h"
55#include "simulator_frame.h"
56#include "sim_plot_panel.h"
57#include "spice_simulator.h"
58#include "spice_reporter.h"
59#include "core/kicad_algo.h"
60#include "fmt/format.h"
62#include <eeschema_settings.h>
63#include <advanced_config.h>
64
65#include <memory>
66
67
69{
70 int res = (int) aFirst | (int) aSecond;
71
72 return (SIM_TRACE_TYPE) res;
73}
74
75
77{
78public:
80 m_parent( aParent )
81 {
82 }
83
84 REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
85 {
86 wxCommandEvent* event = new wxCommandEvent( EVT_SIM_REPORT );
87 event->SetString( aText );
88 wxQueueEvent( m_parent, event );
89 return *this;
90 }
91
92 bool HasMessage() const override
93 {
94 return false; // Technically "indeterminate" rather than false.
95 }
96
97 void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) override
98 {
99 wxCommandEvent* event = nullptr;
100
101 switch( aNewState )
102 {
103 case SIM_IDLE: event = new wxCommandEvent( EVT_SIM_FINISHED ); break;
104 case SIM_RUNNING: event = new wxCommandEvent( EVT_SIM_STARTED ); break;
105 default: wxFAIL; return;
106 }
107
108 wxQueueEvent( m_parent, event );
109 }
110
111private:
113};
114
115
117{
124
125
127{
133
134
136{
141
142
143enum
144{
153
157
158
160{
161public:
163 GRID_TRICKS( aGrid ),
164 m_parent( aParent ),
165 m_menuRow( 0 ),
166 m_menuCol( 0 )
167 {}
168
169protected:
170 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent ) override;
171 void doPopupSelection( wxCommandEvent& event ) override;
172
173protected:
177};
178
179
180void SIGNALS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
181{
182 m_menuRow = aEvent.GetRow();
183 m_menuCol = aEvent.GetCol();
184
186 {
187 if( !( m_grid->IsInSelection( m_menuRow, m_menuCol ) ) )
188 m_grid->ClearSelection();
189
190 m_grid->SetGridCursor( m_menuRow, m_menuCol );
191
192 wxString msg = m_grid->GetCellValue( m_menuRow, m_menuCol );
193
194 menu.Append( MYID_MEASURE_MIN, _( "Measure Min" ) );
195 menu.Append( MYID_MEASURE_MAX, _( "Measure Max" ) );
196 menu.Append( MYID_MEASURE_AVG, _( "Measure Average" ) );
197 menu.Append( MYID_MEASURE_RMS, _( "Measure RMS" ) );
198 menu.Append( MYID_MEASURE_PP, _( "Measure Peak-to-peak" ) );
199 menu.Append( MYID_MEASURE_MIN_AT, _( "Measure Time of Min" ) );
200 menu.Append( MYID_MEASURE_MAX_AT, _( "Measure Time of Max" ) );
201 menu.Append( MYID_MEASURE_INTEGRAL, _( "Measure Integral" ) );
202
203 menu.AppendSeparator();
204 }
205
206 GRID_TRICKS::showPopupMenu( menu, aEvent );
207}
208
209
210void SIGNALS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
211{
212 std::vector<wxString> signals;
213
214 wxGridCellCoordsArray cells1 = m_grid->GetSelectionBlockTopLeft();
215 wxGridCellCoordsArray cells2 = m_grid->GetSelectionBlockBottomRight();
216
217 for( size_t i = 0; i < cells1.Count(); i++ )
218 {
219 if( cells1[i].GetCol() == COL_SIGNAL_NAME )
220 {
221 for( int j = cells1[i].GetRow(); j < cells2[i].GetRow() + 1; j++ )
222 {
223 signals.push_back( m_grid->GetCellValue( j, cells1[i].GetCol() ) );
224 }
225 }
226 }
227
228 wxGridCellCoordsArray cells3 = m_grid->GetSelectedCells();
229
230 for( size_t i = 0; i < cells3.Count(); i++ )
231 {
232 if( cells3[i].GetCol() == COL_SIGNAL_NAME )
233 signals.push_back( m_grid->GetCellValue( cells3[i].GetRow(), cells3[i].GetCol() ) );
234 }
235
236 if( signals.size() < 1 )
237 signals.push_back( m_grid->GetCellValue( m_menuRow, m_menuCol ) );
238
239 if( event.GetId() == MYID_MEASURE_MIN )
240 {
241 for( const wxString& signal : signals )
242 m_parent->AddMeasurement( wxString::Format( wxS( "MIN %s" ), signal ) );
243 }
244 else if( event.GetId() == MYID_MEASURE_MAX )
245 {
246 for( const wxString& signal : signals )
247 m_parent->AddMeasurement( wxString::Format( wxS( "MAX %s" ), signal ) );
248 }
249 else if( event.GetId() == MYID_MEASURE_AVG )
250 {
251 for( const wxString& signal : signals )
252 m_parent->AddMeasurement( wxString::Format( wxS( "AVG %s" ), signal ) );
253 }
254 else if( event.GetId() == MYID_MEASURE_RMS )
255 {
256 for( const wxString& signal : signals )
257 m_parent->AddMeasurement( wxString::Format( wxS( "RMS %s" ), signal ) );
258 }
259 else if( event.GetId() == MYID_MEASURE_PP )
260 {
261 for( const wxString& signal : signals )
262 m_parent->AddMeasurement( wxString::Format( wxS( "PP %s" ), signal ) );
263 }
264 else if( event.GetId() == MYID_MEASURE_MIN_AT )
265 {
266 for( const wxString& signal : signals )
267 m_parent->AddMeasurement( wxString::Format( wxS( "MIN_AT %s" ), signal ) );
268 }
269 else if( event.GetId() == MYID_MEASURE_MAX_AT )
270 {
271 for( const wxString& signal : signals )
272 m_parent->AddMeasurement( wxString::Format( wxS( "MAX_AT %s" ), signal ) );
273 }
274 else if( event.GetId() == MYID_MEASURE_INTEGRAL )
275 {
276 for( const wxString& signal : signals )
277 m_parent->AddMeasurement( wxString::Format( wxS( "INTEG %s" ), signal ) );
278 }
279 else
281}
282
283
285{
286public:
288 GRID_TRICKS( aGrid ),
289 m_parent( aParent ),
290 m_menuRow( 0 ),
291 m_menuCol( 0 )
292 {}
293
294protected:
295 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent ) override;
296 void doPopupSelection( wxCommandEvent& event ) override;
297
298protected:
302};
303
304
305void CURSORS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
306{
307 m_menuRow = aEvent.GetRow();
308 m_menuCol = aEvent.GetCol();
309
311 {
312 wxString msg = m_grid->GetColLabelValue( m_menuCol );
313
314 menu.Append( MYID_FORMAT_VALUE, wxString::Format( _( "Format %s..." ), msg ) );
315 menu.AppendSeparator();
316 }
317
318 GRID_TRICKS::showPopupMenu( menu, aEvent );
319}
320
321
322void CURSORS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
323{
324 if( event.GetId() == MYID_FORMAT_VALUE )
325 {
326 int cursorId = m_menuRow;
327 int cursorAxis = m_menuCol - COL_CURSOR_X;
328 SPICE_VALUE_FORMAT format = m_parent->GetCursorFormat( cursorId, cursorAxis );
329 DIALOG_SIM_FORMAT_VALUE formatDialog( m_parent, &format );
330
331 if( formatDialog.ShowModal() == wxID_OK )
332 m_parent->SetCursorFormat( cursorId, cursorAxis, format );
333 }
334 else
335 {
337 }
338}
339
340
342{
343public:
345 GRID_TRICKS( aGrid ),
346 m_parent( aParent ),
347 m_menuRow( 0 ),
348 m_menuCol( 0 )
349 {}
350
351protected:
352 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent ) override;
353 void doPopupSelection( wxCommandEvent& event ) override;
354
355protected:
359};
360
361
362void MEASUREMENTS_GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
363{
364 m_menuRow = aEvent.GetRow();
365 m_menuCol = aEvent.GetCol();
366
367 if( !( m_grid->IsInSelection( m_menuRow, m_menuCol ) ) )
368 m_grid->ClearSelection();
369
370 m_grid->SetGridCursor( m_menuRow, m_menuCol );
371
373 menu.Append( MYID_FORMAT_VALUE, _( "Format Value..." ) );
374
375 if( m_menuRow < ( m_grid->GetNumberRows() - 1 ) )
376 menu.Append( MYID_DELETE_MEASUREMENT, _( "Delete Measurement" ) );
377
378 menu.AppendSeparator();
379
380 GRID_TRICKS::showPopupMenu( menu, aEvent );
381}
382
383
385{
386 if( event.GetId() == MYID_FORMAT_VALUE )
387 {
389 DIALOG_SIM_FORMAT_VALUE formatDialog( m_parent, &format );
390
391 if( formatDialog.ShowModal() == wxID_OK )
392 {
395 }
396 }
397 else if( event.GetId() == MYID_DELETE_MEASUREMENT )
398 {
399 std::vector<int> measurements;
400
401 wxGridCellCoordsArray cells1 = m_grid->GetSelectionBlockTopLeft();
402 wxGridCellCoordsArray cells2 = m_grid->GetSelectionBlockBottomRight();
403
404 for( size_t i = 0; i < cells1.Count(); i++ )
405 {
406 if( cells1[i].GetCol() == COL_MEASUREMENT )
407 {
408 for( int j = cells1[i].GetRow(); j < cells2[i].GetRow() + 1; j++ )
409 {
410 measurements.push_back( j );
411 }
412 }
413 }
414
415 wxGridCellCoordsArray cells3 = m_grid->GetSelectedCells();
416
417 for( size_t i = 0; i < cells3.Count(); i++ )
418 {
419 if( cells3[i].GetCol() == COL_MEASUREMENT )
420 measurements.push_back( cells3[i].GetRow() );
421 }
422
423 if( measurements.size() < 1 )
424 measurements.push_back( m_menuRow );
425
426 // When deleting a row, we'll change the indexes.
427 // To avoid problems, we can start with the highest indexes.
428 sort( measurements.begin(), measurements.end(), std::greater<>() );
429
430 for( int row : measurements )
432
433 m_grid->ClearSelection();
434 }
435 else
436 {
438 }
439}
440
441
443{
444public:
446 m_frame( aFrame )
447 {
449 }
450
452 {
454 }
455
456private:
458};
459
460
461BEGIN_EVENT_TABLE( SIMULATOR_FRAME, SIMULATOR_FRAME_BASE )
462 EVT_MENU( wxID_EXIT, SIMULATOR_FRAME::onExit )
463 EVT_MENU( wxID_CLOSE, SIMULATOR_FRAME::onExit )
464END_EVENT_TABLE()
465
466
467SIMULATOR_FRAME::SIMULATOR_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
468 SIMULATOR_FRAME_BASE( aParent ),
469 m_SuppressGridEvents( 0 ),
470 m_lastSimPlot( nullptr ),
471 m_darkMode( true ),
472 m_plotNumber( 0 ),
473 m_simFinished( false ),
474 m_workbookModified( false )
475{
476 SetKiway( this, aKiway );
477
478 m_schematicFrame = (SCH_EDIT_FRAME*) Kiway().Player( FRAME_SCH, false );
479 wxASSERT( m_schematicFrame );
480
481 // Give an icon
482 wxIcon icon;
483 icon.CopyFromBitmap( KiBitmap( BITMAPS::simulator ) );
484 SetIcon( icon );
485
486 m_simulator = SIMULATOR::CreateInstance( "ngspice" );
487 wxASSERT( m_simulator );
488
489 // Get the previous size and position of windows:
490 LoadSettings( config() );
491
492 m_filter->SetHint( _( "Filter" ) );
493
494 m_signalsGrid->wxGrid::SetLabelFont( KIUI::GetStatusFont( this ) );
495 m_cursorsGrid->wxGrid::SetLabelFont( KIUI::GetStatusFont( this ) );
496 m_measurementsGrid->wxGrid::SetLabelFont( KIUI::GetStatusFont( this ) );
497
498 m_signalsGrid->PushEventHandler( new SIGNALS_GRID_TRICKS( this, m_signalsGrid ) );
499 m_cursorsGrid->PushEventHandler( new CURSORS_GRID_TRICKS( this, m_cursorsGrid ) );
500 m_measurementsGrid->PushEventHandler( new MEASUREMENTS_GRID_TRICKS( this, m_measurementsGrid ) );
501
502 wxGridCellAttr* attr = new wxGridCellAttr;
503 attr->SetReadOnly();
504 m_signalsGrid->SetColAttr( COL_SIGNAL_NAME, attr );
505
506 attr = new wxGridCellAttr;
507 attr->SetReadOnly();
508 m_cursorsGrid->SetColAttr( COL_CURSOR_NAME, attr );
509
510 attr = new wxGridCellAttr;
511 attr->SetReadOnly();
512 m_cursorsGrid->SetColAttr( COL_CURSOR_SIGNAL, attr );
513
514 attr = new wxGridCellAttr;
515 attr->SetReadOnly();
516 m_cursorsGrid->SetColAttr( COL_CURSOR_Y, attr );
517
518 for( int cursorId = 0; cursorId < 3; ++cursorId )
519 {
520 m_cursorFormats[ cursorId ][ 0 ] = { 3, wxS( "~s" ) };
521 m_cursorFormats[ cursorId ][ 1 ] = { 3, wxS( "~V" ) };
522 }
523
524 attr = new wxGridCellAttr;
525 attr->SetReadOnly();
526 m_measurementsGrid->SetColAttr( COL_MEASUREMENT_VALUE, attr );
527
528 // Prepare the color list to plot traces
530
532 dynamic_cast<NGSPICE_SIMULATOR_SETTINGS*>( m_simulator->Settings().get() );
533
534 wxCHECK2( settings, /* do nothing in release builds*/ );
535
536 if( settings && settings->GetWorkbookFilename().IsEmpty() )
538
539 m_simulator->Init();
540
541 m_reporter = new SIM_THREAD_REPORTER( this );
542 m_simulator->SetReporter( m_reporter );
543
544 m_circuitModel = std::make_shared<NGSPICE_CIRCUIT_MODEL>( &m_schematicFrame->Schematic(), this );
545
546 setupTools();
547 setupUIConditions();
548
549 ReCreateHToolbar();
550 ReCreateMenuBar();
551
552 Bind( wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( SIMULATOR_FRAME::onExit ), this,
553 wxID_EXIT );
554
555 Bind( EVT_SIM_UPDATE, &SIMULATOR_FRAME::onSimUpdate, this );
556 Bind( EVT_SIM_REPORT, &SIMULATOR_FRAME::onSimReport, this );
557 Bind( EVT_SIM_STARTED, &SIMULATOR_FRAME::onSimStarted, this );
558 Bind( EVT_SIM_FINISHED, &SIMULATOR_FRAME::onSimFinished, this );
559 Bind( EVT_SIM_CURSOR_UPDATE, &SIMULATOR_FRAME::onCursorUpdate, this );
560
561 Bind( EVT_WORKBOOK_MODIFIED, &SIMULATOR_FRAME::onNotebookModified, this );
562
563#ifndef wxHAS_NATIVE_TABART
564 // Default non-native tab art has ugly gradients we don't want
565 m_plotNotebook->SetArtProvider( new wxAuiSimpleTabArt() );
566#endif
567
568 // Ensure new items are taken in account by sizers:
569 Layout();
570
571 // resize the subwindows size. At least on Windows, calling wxSafeYield before
572 // resizing the subwindows forces the wxSplitWindows size events automatically generated
573 // by wxWidgets to be executed before our resize code.
574 // Otherwise, the changes made by setSubWindowsSashSize are overwritten by one these
575 // events
576 wxSafeYield();
577 setSubWindowsSashSize();
578
579 // Ensure the window is on top
580 Raise();
581
582 initWorkbook();
583 updateTitle();
584}
585
586
588{
589 // Delete the GRID_TRICKS.
590 m_signalsGrid->PopEventHandler( true );
591 m_cursorsGrid->PopEventHandler( true );
592 m_measurementsGrid->PopEventHandler( true );
593
594 NULL_REPORTER devnull;
595
596 m_simulator->Attach( nullptr, devnull );
597 m_simulator->SetReporter( nullptr );
598 delete m_reporter;
599}
600
601
603{
604 // Create the manager
606 m_toolManager->SetEnvironment( nullptr, nullptr, nullptr, config(), this );
607
609
610 // Attach the events to the tool dispatcher
612 Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
613
614 // Register tools
618}
619
620
622{
624
625 updateTitle();
626
627 for( int ii = 0; ii < (int) m_plotNotebook->GetPageCount(); ++ii )
628 {
629 SIM_PANEL_BASE* plot = dynamic_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetPage( ii ) );
630
631 wxCHECK( plot, /* void */ );
632
633 plot->OnLanguageChanged();
634
635 wxString pageTitle( m_simulator->TypeToName( plot->GetType(), true ) );
636 pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), ii+1 /* 1-based */ ) );
637
638 m_plotNotebook->SetPageText( ii, pageTitle );
639 }
640
641 m_filter->SetHint( _( "Filter" ) );
642
643 m_signalsGrid->SetColLabelValue( COL_SIGNAL_NAME, _( "Signal" ) );
644 m_signalsGrid->SetColLabelValue( COL_SIGNAL_SHOW, _( "Plot" ) );
645 m_signalsGrid->SetColLabelValue( COL_SIGNAL_COLOR, _( "Color" ) );
646 m_signalsGrid->SetColLabelValue( COL_CURSOR_1, _( "Cursor 1" ) );
647 m_signalsGrid->SetColLabelValue( COL_CURSOR_2, _( "Cursor 2" ) );
648
649 m_cursorsGrid->SetColLabelValue( COL_CURSOR_NAME, _( "Cursor" ) );
650 m_cursorsGrid->SetColLabelValue( COL_CURSOR_SIGNAL, _( "Signal" ) );
651 m_cursorsGrid->SetColLabelValue( COL_CURSOR_X, _( "Time" ) );
652 m_cursorsGrid->SetColLabelValue( COL_CURSOR_Y, _( "Value" ) );
654
655 for( TUNER_SLIDER* tuner : m_tuners )
656 tuner->ShowChangedLanguage();
657}
658
659
661{
662 EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
663 wxASSERT( cfg );
664
665 if( cfg )
666 {
668
669 // Read subwindows sizes (should be > 0 )
676 }
677
679
680 NGSPICE* currentSim = dynamic_cast<NGSPICE*>( m_simulator.get() );
681
682 if( currentSim )
683 m_simulator->Settings() = project.m_SchematicSettings->m_NgspiceSimulatorSettings;
684}
685
686
688{
689 EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
690 wxASSERT( cfg );
691
692 if( cfg )
693 {
695
696 cfg->m_Simulator.plot_panel_width = m_splitterLeftRight->GetSashPosition();
698 cfg->m_Simulator.signal_panel_height = m_splitterSignals->GetSashPosition();
699 cfg->m_Simulator.cursors_panel_height = m_splitterCursors->GetSashPosition();
702 }
703
705
706 if( project.m_SchematicSettings )
707 {
708 bool modified = project.m_SchematicSettings->m_NgspiceSimulatorSettings->SaveToFile();
709
710 if( m_schematicFrame && modified )
712 }
713}
714
715
717{
718 EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
719 wxASSERT( cfg );
720
721 return cfg ? &cfg->m_Simulator.window : nullptr;
722}
723
724
726{
727 if( !m_simulator->Settings()->GetWorkbookFilename().IsEmpty() )
728 {
729 wxFileName filename = m_simulator->Settings()->GetWorkbookFilename();
730 filename.SetPath( Prj().GetProjectPath() );
731
732 if( !LoadWorkbook( filename.GetFullPath() ) )
733 m_simulator->Settings()->SetWorkbookFilename( "" );
734 }
735 else if( LoadSimulator() )
736 {
737 if( !m_circuitModel->GetSchTextSimCommand().IsEmpty() )
738 NewPlotPanel( m_circuitModel->GetSchTextSimCommand(), m_circuitModel->GetSimOptions() );
739
741 rebuildSignalsGrid( m_filter->GetValue() );
742 }
743}
744
745
747{
748 bool unsaved = true;
749 bool readOnly = false;
750 wxString title;
751
752 if( m_simulator && m_simulator->Settings() )
753 {
754 wxFileName filename = Prj().AbsolutePath( m_simulator->Settings()->GetWorkbookFilename() );
755
756 if( filename.IsOk() && filename.FileExists() )
757 {
758 unsaved = false;
759 readOnly = !filename.IsFileWritable();
760 }
761
763 title = wxT( "*" ) + filename.GetName();
764 else
765 title = filename.GetName();
766 }
767
768 if( readOnly )
769 title += wxS( " " ) + _( "[Read Only]" );
770
771 if( unsaved )
772 title += wxS( " " ) + _( "[Unsaved]" );
773
774 title += wxT( " \u2014 " ) + _( "Spice Simulator" );
775
776 SetTitle( title );
777}
778
779
781{
784
787
790
793
796}
797
798
800{
801 SUPPRESS_GRID_CELL_EVENTS raii( this );
802
804
805 if( aFilter.IsEmpty() )
806 aFilter = wxS( "*" );
807
808 EDA_COMBINED_MATCHER matcher( aFilter, CTX_SIGNAL );
809 SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
810 int row = 0;
811
812 for( const wxString& signal : m_signals )
813 {
814 if( matcher.StartsWith( signal ) )
815 {
816 int traceType = SPT_UNKNOWN;
817 wxString vectorName = vectorNameFromSignalName( signal, &traceType );
818 TRACE* trace = plotPanel ? plotPanel->GetTrace( vectorName, traceType ) : nullptr;
819
820 m_signalsGrid->AppendRows( 1 );
821 m_signalsGrid->SetCellValue( row, COL_SIGNAL_NAME, signal );
822
823 if( !plotPanel )
824 {
825 wxGridCellAttr* attr = new wxGridCellAttr;
826 attr->SetReadOnly();
827 m_signalsGrid->SetAttr( row, COL_SIGNAL_SHOW, attr );
828 }
829 else
830 {
831 wxGridCellAttr* attr = new wxGridCellAttr;
832 attr->SetRenderer( new wxGridCellBoolRenderer() );
833 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
834 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
835 m_signalsGrid->SetAttr( row, COL_SIGNAL_SHOW, attr );
836 }
837
838 if( trace )
839 m_signalsGrid->SetCellValue( row, COL_SIGNAL_SHOW, wxS( "1" ) );
840
841 if( !plotPanel || !trace )
842 {
843 wxGridCellAttr* attr = new wxGridCellAttr;
844 attr->SetReadOnly();
845 m_signalsGrid->SetAttr( row, COL_SIGNAL_COLOR, attr );
846 m_signalsGrid->SetCellValue( row, COL_SIGNAL_COLOR, wxEmptyString );
847
848 attr = new wxGridCellAttr;
849 attr->SetReadOnly();
850 m_signalsGrid->SetAttr( row, COL_CURSOR_1, attr );
851
852 attr = new wxGridCellAttr;
853 attr->SetReadOnly();
854 m_signalsGrid->SetAttr( row, COL_CURSOR_2, attr );
855 }
856 else
857 {
858 wxGridCellAttr* attr = new wxGridCellAttr;
859 attr = new wxGridCellAttr;
860 attr->SetRenderer( new GRID_CELL_COLOR_RENDERER( this ) );
861 attr->SetEditor( new GRID_CELL_COLOR_SELECTOR( this, m_signalsGrid ) );
862 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
863 m_signalsGrid->SetAttr( row, COL_SIGNAL_COLOR, attr );
864 KIGFX::COLOR4D color( trace->GetPen().GetColour() );
865 m_signalsGrid->SetCellValue( row, COL_SIGNAL_COLOR, color.ToCSSString() );
866
867 attr = new wxGridCellAttr;
868 attr->SetRenderer( new wxGridCellBoolRenderer() );
869 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
870 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
871 m_signalsGrid->SetAttr( row, COL_CURSOR_1, attr );
872
873 attr = new wxGridCellAttr;
874 attr->SetRenderer( new wxGridCellBoolRenderer() );
875 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
876 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
877 m_signalsGrid->SetAttr( row, COL_CURSOR_2, attr );
878 }
879
880 row++;
881 }
882 }
883}
884
885
887{
888 m_signals.clear();
889
890 int options = GetCurrentOptions();
892 wxString unconnected = wxString( wxS( "unconnected-(" ) );
893
894 if( simType == ST_UNKNOWN )
895 simType = ST_TRANSIENT;
896
897 unconnected.Replace( '(', '_' ); // Convert to SPICE markup
898
899 auto addSignal =
900 [&]( const wxString& aSignalName )
901 {
902 if( simType == ST_AC )
903 {
904 m_signals.push_back( aSignalName + _( " (gain)" ) );
905 m_signals.push_back( aSignalName + _( " (phase)" ) );
906 }
907 else
908 {
909 m_signals.push_back( aSignalName );
910 }
911 };
912
914 {
915 for( const std::string& net : m_circuitModel->GetNets() )
916 {
917 // netnames are escaped (can contain "{slash}" for '/') Unscape them:
918 wxString netname = UnescapeString( net );
919
920 if( netname == "GND" || netname == "0" || netname.StartsWith( unconnected ) )
921 continue;
922
923 m_quotedNetnames[ netname ] = wxString::Format( wxS( "\"%s\"" ), netname );
924 addSignal( wxString::Format( wxS( "V(%s)" ), netname ) );
925 }
926 }
927
929 && ( simType == ST_TRANSIENT || simType == ST_DC ) )
930 {
931 for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
932 {
933 // Add all possible currents for the device.
934 for( const std::string& name : item.model->SpiceGenerator().CurrentNames( item ) )
935 addSignal( name );
936 }
937 }
938
940 && ( simType == ST_TRANSIENT || simType == ST_DC ) )
941 {
942 for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
943 {
944 if( item.model->GetPinCount() >= 2 )
945 {
946 wxString name = item.model->SpiceGenerator().ItemName( item );
947 addSignal( wxString::Format( wxS( "P(%s)" ), name ) );
948 }
949 }
950 }
951
952 // Add .PROBE directives
953 for( const wxString& directive : m_circuitModel->GetDirectives() )
954 {
955 wxStringTokenizer tokenizer( directive, wxT( "\r\n" ), wxTOKEN_STRTOK );
956
957 while( tokenizer.HasMoreTokens() )
958 {
959 wxString line = tokenizer.GetNextToken();
960 wxString directiveParams;
961
962 if( line.Upper().StartsWith( wxS( ".PROBE" ), &directiveParams ) )
963 addSignal( directiveParams.Trim( true ).Trim( false ) );
964 }
965 }
966
967 // JEY TODO: find and add SPICE "LET" commands
968
969 // Add user-defined signals
970 for( const auto& [ signalId, signalName ] : m_userDefinedSignals )
971 addSignal( signalName );
972
973 std::sort( m_signals.begin(), m_signals.end(),
974 []( const wxString& lhs, const wxString& rhs )
975 {
976 // Sort voltages first
977 if( lhs.Upper().StartsWith( 'V' ) && !rhs.Upper().StartsWith( 'V' ) )
978 return true;
979 else if( !lhs.Upper().StartsWith( 'V' ) && rhs.Upper().StartsWith( 'V' ) )
980 return false;
981
982 return StrNumCmp( lhs, rhs, true /* ignore case */ ) < 0;
983 } );
984}
985
986
988{
989 wxString errors;
990 WX_STRING_REPORTER reporter( &errors );
991
992 if( !m_schematicFrame->ReadyToNetlist( _( "Simulator requires a fully annotated schematic." ) ) )
993 return false;
994
995 // If we are using the new connectivity, make sure that we do a full-rebuild
996 if( ADVANCED_CFG::GetCfg().m_IncrementalConnectivity )
998
999 if( !m_simulator->Attach( m_circuitModel, reporter ) )
1000 {
1001 DisplayErrorMessage( this, _( "Errors during netlist generation.\n\n" ) + errors );
1002 return false;
1003 }
1004
1005 return true;
1006}
1007
1008
1010{
1011 if( m_circuitModel->CommandToSimType( GetCurrentSimCommand() ) == ST_UNKNOWN )
1012 {
1013 if( !EditSimCommand() )
1014 return;
1015
1016 if( m_circuitModel->CommandToSimType( GetCurrentSimCommand() ) == ST_UNKNOWN )
1017 return;
1018 }
1019
1020 wxString schTextSimCommand = m_circuitModel->GetSchTextSimCommand();
1021 SIM_TYPE schTextSimType = NGSPICE_CIRCUIT_MODEL::CommandToSimType( schTextSimCommand );
1022 SIM_PANEL_BASE* plotWindow = getCurrentPlotWindow();
1023
1024 if( !plotWindow )
1025 {
1026 plotWindow = NewPlotPanel( schTextSimCommand, m_circuitModel->GetSimOptions() );
1027 OnModify();
1028 }
1029 else
1030 {
1031 m_circuitModel->SetSimCommandOverride( plotWindow->GetSimCommand() );
1032
1033 if( plotWindow->GetType() == schTextSimType
1034 && schTextSimCommand != m_circuitModel->GetLastSchTextSimCommand() )
1035 {
1036 if( IsOK( this, _( "Schematic sheet simulation command directive has changed. "
1037 "Do you wish to update the Simulation Command?" ) ) )
1038 {
1039 m_circuitModel->SetSimCommandOverride( wxEmptyString );
1040 plotWindow->SetSimCommand( schTextSimCommand );
1041 OnModify();
1042 }
1043 }
1044 }
1045
1046 m_circuitModel->SetSimOptions( GetCurrentOptions() );
1047
1048 if( !LoadSimulator() )
1049 return;
1050
1051 std::unique_lock<std::mutex> simulatorLock( m_simulator->GetMutex(), std::try_to_lock );
1052
1053 if( simulatorLock.owns_lock() )
1054 {
1055 wxBusyCursor toggle;
1056
1057 m_simConsole->Clear();
1058
1059 applyTuners();
1060
1061 // Prevents memory leak on succeding simulations by deleting old vectors
1062 m_simulator->Clean();
1063 m_simulator->Run();
1064 }
1065 else
1066 {
1067 DisplayErrorMessage( this, _( "Another simulation is already running." ) );
1068 }
1069}
1070
1071
1072SIM_PANEL_BASE* SIMULATOR_FRAME::NewPlotPanel( const wxString& aSimCommand, int aOptions )
1073{
1074 SIM_PANEL_BASE* plotPanel = nullptr;
1075 SIM_TYPE simType = NGSPICE_CIRCUIT_MODEL::CommandToSimType( aSimCommand );
1076
1077 if( SIM_PANEL_BASE::IsPlottable( simType ) )
1078 {
1079 SIM_PLOT_PANEL* panel = new SIM_PLOT_PANEL( aSimCommand, aOptions, m_plotNotebook, wxID_ANY );
1080 plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel );
1081
1082 COMMON_SETTINGS::INPUT cfg = Pgm().GetCommonSettings()->m_Input;
1084 }
1085 else
1086 {
1087 SIM_NOPLOT_PANEL* panel = new SIM_NOPLOT_PANEL( aSimCommand, aOptions, m_plotNotebook, wxID_ANY );
1088 plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel );
1089 }
1090
1091 wxString pageTitle( m_simulator->TypeToName( simType, true ) );
1092 pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), (unsigned int) ++m_plotNumber ) );
1093
1094 m_plotNotebook->AddPage( dynamic_cast<wxWindow*>( plotPanel ), pageTitle, true );
1095
1096 OnModify();
1097 return plotPanel;
1098}
1099
1100
1101void SIMULATOR_FRAME::OnFilterText( wxCommandEvent& aEvent )
1102{
1103 rebuildSignalsGrid( m_filter->GetValue() );
1104}
1105
1106
1107void SIMULATOR_FRAME::OnFilterMouseMoved( wxMouseEvent& aEvent )
1108{
1109 wxPoint pos = aEvent.GetPosition();
1110 wxRect ctrlRect = m_filter->GetScreenRect();
1111 int buttonWidth = ctrlRect.GetHeight(); // Presume buttons are square
1112
1113 if( m_filter->IsSearchButtonVisible() && pos.x < buttonWidth )
1114 SetCursor( wxCURSOR_ARROW );
1115 else if( m_filter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
1116 SetCursor( wxCURSOR_ARROW );
1117 else
1118 SetCursor( wxCURSOR_IBEAM );
1119}
1120
1121
1122wxString vectorNameFromSignalId( int aUserDefinedSignalId )
1123{
1124 return wxString::Format( wxS( "user%d" ), aUserDefinedSignalId );
1125}
1126
1127
1132wxString SIMULATOR_FRAME::vectorNameFromSignalName( const wxString& aSignalName, int* aTraceType )
1133{
1134 std::map<wxString, int> suffixes;
1135 suffixes[ _( " (gain)" ) ] = SPT_AC_MAG;
1136 suffixes[ _( " (phase)" ) ] = SPT_AC_PHASE;
1137
1138 if( aTraceType )
1139 {
1140 wxUniChar firstChar = aSignalName.Upper()[0];
1141
1142 if( firstChar == 'V' )
1143 *aTraceType = SPT_VOLTAGE;
1144 else if( firstChar == 'I' )
1145 *aTraceType = SPT_CURRENT;
1146 else if( firstChar == 'P' )
1147 *aTraceType = SPT_POWER;
1148 }
1149
1150 wxString suffix;
1151 wxString name = aSignalName;
1152
1153 for( const auto& [ candidate, type ] : suffixes )
1154 {
1155 if( name.EndsWith( candidate ) )
1156 {
1157 name = name.Left( name.Length() - candidate.Length() );
1158
1159 if( aTraceType )
1160 *aTraceType |= type;
1161
1162 break;
1163 }
1164 }
1165
1166 for( const auto& [ id, signal ] : m_userDefinedSignals )
1167 {
1168 if( name == signal )
1169 return vectorNameFromSignalId( id );
1170 }
1171
1172 return name;
1173};
1174
1175
1177{
1178 if( m_SuppressGridEvents > 0 )
1179 return;
1180
1181 int row = aEvent.GetRow();
1182 int col = aEvent.GetCol();
1183 wxString text = m_signalsGrid->GetCellValue( row, col );
1185 wxString signalName = m_signalsGrid->GetCellValue( row, COL_SIGNAL_NAME );
1186 int traceType = SPT_UNKNOWN;
1187 wxString vectorName = vectorNameFromSignalName( signalName, &traceType );
1188
1189 if( col == COL_SIGNAL_SHOW )
1190 {
1191 if( text == wxS( "1" ) )
1192 updateTrace( vectorName, traceType, plot );
1193 else
1194 plot->DeleteTrace( vectorName, traceType );
1195
1196 // Update enabled/visible states of other controls
1198 updateCursors();
1199 OnModify();
1200 }
1201 else if( col == COL_SIGNAL_COLOR )
1202 {
1203 KIGFX::COLOR4D color( m_signalsGrid->GetCellValue( row, COL_SIGNAL_COLOR ) );
1204 TRACE* trace = plot->GetTrace( vectorName, traceType );
1205
1206 if( trace )
1207 {
1208 trace->SetTraceColour( color.ToColour() );
1209 plot->UpdateTraceStyle( trace );
1210 plot->UpdatePlotColors();
1211 OnModify();
1212 }
1213 }
1214 else if( col == COL_CURSOR_1 || col == COL_CURSOR_2 )
1215 {
1216 for( int ii = 0; ii < m_signalsGrid->GetNumberRows(); ++ii )
1217 {
1218 signalName = m_signalsGrid->GetCellValue( ii, COL_SIGNAL_NAME );
1219 vectorName = vectorNameFromSignalName( signalName, &traceType );
1220
1221 int id = col == COL_CURSOR_1 ? 1 : 2;
1222 bool enable = ii == row && text == wxS( "1" );
1223
1224 plot->EnableCursor( vectorName, traceType, id, enable, signalName );
1225 OnModify();
1226 }
1227
1228 // Update cursor checkboxes (which are really radio buttons)
1230 }
1231}
1232
1233
1235{
1236 if( m_SuppressGridEvents > 0 )
1237 return;
1238
1239 SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
1240
1241 if( !plotPanel )
1242 return;
1243
1244 int row = aEvent.GetRow();
1245 int col = aEvent.GetCol();
1246 wxString text = m_cursorsGrid->GetCellValue( row, col );
1247 wxString cursorName = m_cursorsGrid->GetCellValue( row, COL_CURSOR_NAME );
1248
1249 if( col == COL_CURSOR_X )
1250 {
1251 CURSOR* cursor1 = nullptr;
1252 CURSOR* cursor2 = nullptr;
1253
1254 for( const auto& [name, trace] : plotPanel->GetTraces() )
1255 {
1256 if( CURSOR* cursor = trace->GetCursor( 1 ) )
1257 cursor1 = cursor;
1258
1259 if( CURSOR* cursor = trace->GetCursor( 2 ) )
1260 cursor2 = cursor;
1261 }
1262
1263 double value = SPICE_VALUE( text ).ToDouble();
1264
1265 if( cursorName == wxS( "1" ) && cursor1 )
1266 cursor1->SetCoordX( value );
1267 else if( cursorName == wxS( "2" ) && cursor2 )
1268 cursor2->SetCoordX( value );
1269 else if( cursorName == _( "Diff" ) && cursor1 && cursor2 )
1270 cursor2->SetCoordX( cursor1->GetCoords().x + value );
1271
1272 updateCursors();
1273 OnModify();
1274 }
1275 else
1276 {
1277 wxFAIL_MSG( wxT( "All other columns are supposed to be read-only!" ) );
1278 }
1279}
1280
1281
1283{
1284 SPICE_VALUE_FORMAT result;
1285 result.FromString( m_measurementsGrid->GetCellValue( aRow, COL_MEASUREMENT_FORMAT ) );
1286 return result;
1287}
1288
1289
1291{
1292 m_measurementsGrid->SetCellValue( aRow, COL_MEASUREMENT_FORMAT, aFormat.ToString() );
1293 OnModify();
1294}
1295
1296
1298{
1299 if( aRow < ( m_measurementsGrid->GetNumberRows() - 1 ) )
1300 {
1301 m_measurementsGrid->DeleteRows( aRow, 1 );
1302 OnModify();
1303 }
1304}
1305
1306
1308{
1309 SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
1310
1311 if( !plotPanel )
1312 return;
1313
1314 int row = aEvent.GetRow();
1315 int col = aEvent.GetCol();
1316 wxString text = m_measurementsGrid->GetCellValue( row, col );
1317
1318 if( col == COL_MEASUREMENT )
1319 {
1320 UpdateMeasurement( row );
1321 OnModify();
1322 }
1323 else
1324 {
1325 wxFAIL_MSG( wxT( "All other columns are supposed to be read-only!" ) );
1326 }
1327
1328 // Always leave a single empty row for type-in
1329
1330 int rowCount = (int) m_measurementsGrid->GetNumberRows();
1331 int emptyRows = 0;
1332
1333 for( row = rowCount - 1; row >= 0; row-- )
1334 {
1335 if( m_measurementsGrid->GetCellValue( row, COL_MEASUREMENT ).IsEmpty() )
1336 emptyRows++;
1337 else
1338 break;
1339 }
1340
1341 if( emptyRows > 1 )
1342 {
1343 int killRows = emptyRows - 1;
1344 m_measurementsGrid->DeleteRows( rowCount - killRows, killRows );
1345 }
1346 else if( emptyRows == 0 )
1347 {
1348 m_measurementsGrid->AppendRows( 1 );
1349 }
1350}
1351
1352
1368{
1369 static wxRegEx measureParamsRegEx( wxT( "^"
1370 " *"
1371 "([a-zA-Z_]+)"
1372 " +"
1373 "([a-zA-Z])\\(([^\\)]+)\\)" ) );
1374
1375 SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
1376
1377 if( !plotPanel )
1378 return;
1379
1380 wxString text = m_measurementsGrid->GetCellValue( aRow, COL_MEASUREMENT );
1381
1382 if( text.IsEmpty() )
1383 {
1384 m_measurementsGrid->SetCellValue( aRow, COL_MEASUREMENT_VALUE, wxEmptyString );
1385 return;
1386 }
1387
1388 wxString simType = m_simulator->TypeToName( plotPanel->GetType(), true );
1389 wxString resultName = wxString::Format( wxS( "meas_result_%u" ), aRow );
1390 wxString result = wxS( "?" );
1391
1392 if( measureParamsRegEx.Matches( text ) )
1393 {
1394 wxString func = measureParamsRegEx.GetMatch( text, 1 ).Upper();
1395 wxUniChar signalType = measureParamsRegEx.GetMatch( text, 2 ).Upper()[0];
1396 wxString deviceName = measureParamsRegEx.GetMatch( text, 3 );
1397 wxString units;
1399
1400 if( signalType == 'I' )
1401 units = wxS( "A" );
1402 else if( signalType == 'P' )
1403 {
1404 units = wxS( "W" );
1405 // Our syntax is different from ngspice for power signals
1406 text = func + " " + deviceName + ":power";
1407 }
1408 else
1409 units = wxS( "V" );
1410
1411 if( func.EndsWith( wxS( "_AT" ) ) )
1412 units = wxS( "s" );
1413 else if( func.StartsWith( wxS( "INTEG" ) ) )
1414 {
1415 switch( plotPanel->GetType() )
1416 {
1417 case SIM_TYPE::ST_TRANSIENT:
1418 if ( signalType == 'P' )
1419 units = wxS( "J" );
1420 else
1421 units += wxS( ".s" );
1422 break;
1423 case SIM_TYPE::ST_AC:
1424 case SIM_TYPE::ST_DISTORTION:
1425 case SIM_TYPE::ST_NOISE:
1426 case SIM_TYPE::ST_SENSITIVITY: // If there is a vector, it is frequency
1427 units += wxS( "·Hz" );
1428 break;
1429 case SIM_TYPE::ST_DC: // Could be a lot of things : V, A, deg C, ohm, ...
1430 case SIM_TYPE::ST_OP: // There is no vector for integration
1431 case SIM_TYPE::ST_POLE_ZERO: // There is no vector for integration
1432 case SIM_TYPE::ST_TRANS_FUNC: // There is no vector for integration
1433 default:
1434
1435 units += wxS( "·?" );
1436 break;
1437 }
1438 }
1439
1440 fmt.UpdateUnits( units );
1441 SetMeasureFormat( aRow, fmt );
1442 }
1443
1444 if( m_simFinished )
1445 {
1446 wxString cmd = wxString::Format( wxS( "meas %s %s %s" ), simType, resultName, text );
1447 m_simulator->Command( "echo " + cmd.ToStdString() );
1448 m_simulator->Command( cmd.ToStdString() );
1449
1450 std::vector<double> resultVec = m_simulator->GetMagPlot( resultName.ToStdString() );
1451
1452 if( resultVec.size() > 0 )
1453 result = SPICE_VALUE( resultVec[0] ).ToString( GetMeasureFormat( aRow ) );
1454 }
1455
1456 m_measurementsGrid->SetCellValue( aRow, COL_MEASUREMENT_VALUE, result );
1457}
1458
1459
1460void SIMULATOR_FRAME::AddVoltageTrace( const wxString& aNetName )
1461{
1462 doAddTrace( aNetName, SPT_VOLTAGE );
1463}
1464
1465
1466void SIMULATOR_FRAME::AddCurrentTrace( const wxString& aDeviceName )
1467{
1468 doAddTrace( aDeviceName, SPT_CURRENT );
1469}
1470
1471
1472void SIMULATOR_FRAME::AddTuner( const SCH_SHEET_PATH& aSheetPath, SCH_SYMBOL* aSymbol )
1473{
1474 SIM_PANEL_BASE* plotPanel = getCurrentPlotWindow();
1475
1476 if( !plotPanel )
1477 return;
1478
1479 wxString ref = aSymbol->GetRef( &aSheetPath );
1480
1481 // Do not add multiple instances for the same component.
1482 for( TUNER_SLIDER* tuner : m_tuners )
1483 {
1484 if( tuner->GetSymbolRef() == ref )
1485 return;
1486 }
1487
1488 const SPICE_ITEM* item = GetExporter()->FindItem( std::string( ref.ToUTF8() ) );
1489
1490 // Do nothing if the symbol is not tunable.
1491 if( !item || !item->model->GetTunerParam() )
1492 return;
1493
1494 try
1495 {
1496 TUNER_SLIDER* tuner = new TUNER_SLIDER( this, m_panelTuners, aSheetPath, aSymbol );
1497 m_sizerTuners->Add( tuner );
1498 m_tuners.push_back( tuner );
1499 m_panelTuners->Layout();
1500 OnModify();
1501 }
1502 catch( const KI_PARAM_ERROR& e )
1503 {
1504 DisplayErrorMessage( nullptr, e.What() );
1505 }
1506}
1507
1508
1509void SIMULATOR_FRAME::UpdateTunerValue( const SCH_SHEET_PATH& aSheetPath, const KIID& aSymbol,
1510 const wxString& aRef, const wxString& aValue )
1511{
1512 SCH_ITEM* item = aSheetPath.GetItem( aSymbol );
1513 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item );
1514
1515 if( !symbol )
1516 {
1517 DisplayErrorMessage( this, _( "Could not apply tuned value(s):" ) + wxS( " " )
1518 + wxString::Format( _( "%s not found" ), aRef ) );
1519 return;
1520 }
1521
1522 SIM_LIB_MGR mgr( &Prj() );
1523 SIM_MODEL& model = mgr.CreateModel( &aSheetPath, *symbol ).model;
1524
1525 const SIM_MODEL::PARAM* tunerParam = model.GetTunerParam();
1526
1527 if( !tunerParam )
1528 {
1529 DisplayErrorMessage( this, _( "Could not apply tuned value(s):" ) + wxS( " " )
1530 + wxString::Format( _( "%s is not tunable" ), aRef ) );
1531 return;
1532 }
1533
1534 model.SetParamValue( tunerParam->info.name, std::string( aValue.ToUTF8() ) );
1535 model.WriteFields( symbol->GetFields() );
1536
1537 m_schematicFrame->UpdateItem( symbol, false, true );
1539}
1540
1541
1543{
1544 m_tuners.remove( aTuner );
1545 aTuner->Destroy();
1546 m_panelTuners->Layout();
1547 OnModify();
1548}
1549
1550
1551void SIMULATOR_FRAME::AddMeasurement( const wxString& aCmd )
1552{
1553 // -1 because the last one is for user input
1554 for( int i = 0; i < m_measurementsGrid->GetNumberRows(); i++ )
1555 {
1556 if ( m_measurementsGrid->GetCellValue( i, COL_MEASUREMENT ) == aCmd )
1557 return; // Don't create duplicates
1558 }
1559
1560 SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
1561
1562 if( !plotPanel )
1563 return;
1564
1565 wxString simType = m_simulator->TypeToName( plotPanel->GetType(), true );
1566 int row;
1567
1568 for( row = 0; row < m_measurementsGrid->GetNumberRows(); ++row )
1569 {
1570 if( m_measurementsGrid->GetCellValue( row, COL_MEASUREMENT ).IsEmpty() )
1571 break;
1572 }
1573
1574 if( !m_measurementsGrid->GetCellValue( row, COL_MEASUREMENT ).IsEmpty() )
1575 {
1576 m_measurementsGrid->AppendRows( 1 );
1577 row = m_measurementsGrid->GetNumberRows() - 1;
1578 }
1579
1580 m_measurementsGrid->SetCellValue( row, COL_MEASUREMENT, aCmd );
1581 SetMeasureFormat( row, { 3, wxS( "~V" ) } );
1582
1583 UpdateMeasurement( row );
1584 OnModify();
1585
1586 // Always leave at least one empty row for type-in:
1587 row = m_measurementsGrid->GetNumberRows() - 1;
1588
1589 if( !m_measurementsGrid->GetCellValue( row, COL_MEASUREMENT ).IsEmpty() )
1590 m_measurementsGrid->AppendRows( 1 );
1591}
1592
1593
1595{
1597
1598 return !curPage || curPage->GetType() == ST_UNKNOWN ? nullptr
1599 : dynamic_cast<SIM_PLOT_PANEL*>( curPage );
1600}
1601
1602
1604{
1605 return m_circuitModel.get();
1606}
1607
1608
1609void SIMULATOR_FRAME::doAddTrace( const wxString& aName, SIM_TRACE_TYPE aType )
1610{
1611 SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
1612
1613 if( !plotPanel )
1614 {
1615 m_simConsole->AppendText( _( "Error: no current simulation.\n" ) );
1616 m_simConsole->SetInsertionPointEnd();
1617 return;
1618 }
1619
1621
1622 if( simType == ST_UNKNOWN )
1623 {
1624 m_simConsole->AppendText( _( "Error: simulation type not defined.\n" ) );
1625 m_simConsole->SetInsertionPointEnd();
1626 return;
1627 }
1628 else if( !SIM_PANEL_BASE::IsPlottable( simType ) )
1629 {
1630 m_simConsole->AppendText( _( "Error: simulation type doesn't support plotting.\n" ) );
1631 m_simConsole->SetInsertionPointEnd();
1632 return;
1633 }
1634
1635 if( simType == ST_AC )
1636 {
1637 updateTrace( aName, aType | SPT_AC_MAG, plotPanel );
1638 updateTrace( aName, aType | SPT_AC_PHASE, plotPanel );
1639 }
1640 else
1641 {
1642 updateTrace( aName, aType, plotPanel );
1643 }
1644
1646 OnModify();
1647}
1648
1649
1650void SIMULATOR_FRAME::SetUserDefinedSignals( const std::map<int, wxString>& aNewSignals )
1651{
1652 for( size_t ii = 0; ii < m_plotNotebook->GetPageCount(); ++ii )
1653 {
1654 SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetPage( ii ) );
1655
1656 if( !plotPanel )
1657 continue;
1658
1659 for( const auto& [ id, existingSignal ] : m_userDefinedSignals )
1660 {
1661 int traceType = SPT_UNKNOWN;
1662 wxString vectorName = vectorNameFromSignalName( existingSignal, &traceType );
1663
1664 if( aNewSignals.count( id ) == 0 )
1665 {
1666 if( plotPanel->GetType() == ST_AC )
1667 {
1668 for( int subType : { SPT_AC_MAG, SPT_AC_PHASE } )
1669 plotPanel->DeleteTrace( vectorName, traceType | subType );
1670 }
1671 else
1672 {
1673 plotPanel->DeleteTrace( vectorName, traceType );
1674 }
1675 }
1676 else
1677 {
1678 if( plotPanel->GetType() == ST_AC )
1679 {
1680 for( int subType : { SPT_AC_MAG, SPT_AC_PHASE } )
1681 {
1682 if( TRACE* trace = plotPanel->GetTrace( vectorName, traceType | subType ) )
1683 trace->SetName( aNewSignals.at( id ) );
1684 }
1685 }
1686 else
1687 {
1688 if( TRACE* trace = plotPanel->GetTrace( vectorName, traceType ) )
1689 trace->SetName( aNewSignals.at( id ) );
1690 }
1691 }
1692 }
1693 }
1694
1695 m_userDefinedSignals = aNewSignals;
1696
1697 if( m_simFinished )
1699
1701 rebuildSignalsGrid( m_filter->GetValue() );
1703 updateCursors();
1704 OnModify();
1705}
1706
1707
1708void SIMULATOR_FRAME::updateTrace( const wxString& aVectorName, int aTraceType,
1709 SIM_PLOT_PANEL* aPlotPanel )
1710{
1712
1713 aTraceType &= aTraceType & SPT_Y_AXIS_MASK;
1714 aTraceType |= getXAxisType( simType );
1715
1716 wxString simVectorName = aVectorName;
1717
1718 if( aTraceType & SPT_POWER )
1719 simVectorName = simVectorName.AfterFirst( '(' ).BeforeLast( ')' ) + wxS( ":power" );
1720
1721 if( !SIM_PANEL_BASE::IsPlottable( simType ) )
1722 {
1723 // There is no plot to be shown
1724 m_simulator->Command( wxString::Format( wxT( "print %s" ), aVectorName ).ToStdString() );
1725
1726 return;
1727 }
1728
1729 // First, handle the x axis
1730 wxString xAxisName( m_simulator->GetXAxis( simType ) );
1731
1732 if( xAxisName.IsEmpty() )
1733 return;
1734
1735 std::vector<double> data_x;
1736 std::vector<double> data_y;
1737
1738 if( m_simFinished )
1739 {
1740 data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() );
1741
1742 switch( simType )
1743 {
1744 case ST_AC:
1745 if( aTraceType & SPT_AC_MAG )
1746 data_y = m_simulator->GetMagPlot( (const char*) simVectorName.c_str() );
1747 else if( aTraceType & SPT_AC_PHASE )
1748 data_y = m_simulator->GetPhasePlot( (const char*) simVectorName.c_str() );
1749 else
1750 wxFAIL_MSG( wxT( "Plot type missing AC_PHASE or AC_MAG bit" ) );
1751
1752 break;
1753
1754 case ST_NOISE:
1755 case ST_DC:
1756 case ST_TRANSIENT:
1757 data_y = m_simulator->GetMagPlot( (const char*) simVectorName.c_str() );
1758 break;
1759
1760 default:
1761 wxFAIL_MSG( wxT( "Unhandled plot type" ) );
1762 }
1763 }
1764
1765 unsigned int size = data_x.size();
1766
1767 // If we did a two-source DC analysis, we need to split the resulting vector and add traces
1768 // for each input step
1769 SPICE_DC_PARAMS source1, source2;
1770
1771 if( simType == ST_DC
1772 && m_circuitModel->ParseDCCommand( m_circuitModel->GetSimCommand(), &source1, &source2 )
1773 && !source2.m_source.IsEmpty() )
1774 {
1775 // Source 1 is the inner loop, so lets add traces for each Source 2 (outer loop) step
1776 SPICE_VALUE v = source2.m_vstart;
1777
1778 size_t offset = 0;
1779 size_t outer = ( size_t )( ( source2.m_vend - v ) / source2.m_vincrement ).ToDouble();
1780 size_t inner = data_x.size() / ( outer + 1 );
1781
1782 wxASSERT( data_x.size() % ( outer + 1 ) == 0 );
1783
1784 for( size_t idx = 0; idx <= outer; idx++ )
1785 {
1786 if( TRACE* trace = aPlotPanel->AddTrace( aVectorName, aTraceType ) )
1787 {
1788 if( data_y.size() >= size )
1789 {
1790 std::vector<double> sub_x( data_x.begin() + offset,
1791 data_x.begin() + offset + inner );
1792 std::vector<double> sub_y( data_y.begin() + offset,
1793 data_y.begin() + offset + inner );
1794
1795 aPlotPanel->SetTraceData( trace, inner, sub_x.data(), sub_y.data() );
1796 }
1797 }
1798
1799 v = v + source2.m_vincrement;
1800 offset += inner;
1801 }
1802 }
1803 else if( TRACE* trace = aPlotPanel->AddTrace( aVectorName, aTraceType ) )
1804 {
1805 if( data_y.size() >= size )
1806 aPlotPanel->SetTraceData( trace, size, data_x.data(), data_y.data() );
1807 }
1808}
1809
1810
1812{
1814
1815 for( int row = 0; row < m_signalsGrid->GetNumberRows(); ++row )
1816 {
1817 wxString signalName = m_signalsGrid->GetCellValue( row, COL_SIGNAL_NAME );
1818 int traceType = SPT_UNKNOWN;
1819 wxString vectorName = vectorNameFromSignalName( signalName, &traceType );
1820
1821 if( TRACE* trace = plot ? plot->GetTrace( vectorName, traceType ) : nullptr )
1822 {
1823 m_signalsGrid->SetCellValue( row, COL_SIGNAL_SHOW, wxS( "1" ) );
1824
1825 wxGridCellAttr* attr = new wxGridCellAttr;
1826 attr->SetRenderer( new GRID_CELL_COLOR_RENDERER( this ) );
1827 attr->SetEditor( new GRID_CELL_COLOR_SELECTOR( this, m_signalsGrid ) );
1828 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1829 m_signalsGrid->SetAttr( row, COL_SIGNAL_COLOR, attr );
1830
1831 KIGFX::COLOR4D color( trace->GetPen().GetColour() );
1832 m_signalsGrid->SetCellValue( row, COL_SIGNAL_COLOR, color.ToCSSString() );
1833
1834 attr = new wxGridCellAttr;
1835 attr->SetRenderer( new wxGridCellBoolRenderer() );
1836 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
1837 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1838 m_signalsGrid->SetAttr( row, COL_CURSOR_1, attr );
1839
1840 attr = new wxGridCellAttr;
1841 attr->SetRenderer( new wxGridCellBoolRenderer() );
1842 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
1843 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
1844 m_signalsGrid->SetAttr( row, COL_CURSOR_2, attr );
1845
1846 if( trace->HasCursor( 1 ) )
1847 m_signalsGrid->SetCellValue( row, COL_CURSOR_1, wxS( "1" ) );
1848 else
1849 m_signalsGrid->SetCellValue( row, COL_CURSOR_1, wxEmptyString );
1850
1851 if( trace->HasCursor( 2 ) )
1852 m_signalsGrid->SetCellValue( row, COL_CURSOR_2, wxS( "1" ) );
1853 else
1854 m_signalsGrid->SetCellValue( row, COL_CURSOR_2, wxEmptyString );
1855 }
1856 else
1857 {
1858 m_signalsGrid->SetCellValue( row, COL_SIGNAL_SHOW, wxEmptyString );
1859
1860 wxGridCellAttr* attr = new wxGridCellAttr;
1861 attr->SetReadOnly();
1862 m_signalsGrid->SetAttr( row, COL_SIGNAL_COLOR, attr );
1863 m_signalsGrid->SetCellValue( row, COL_SIGNAL_COLOR, wxEmptyString );
1864
1865 attr = new wxGridCellAttr;
1866 attr->SetReadOnly();
1867 m_signalsGrid->SetAttr( row, COL_CURSOR_1, attr );
1868 m_signalsGrid->SetCellValue( row, COL_CURSOR_1, wxEmptyString );
1869
1870 attr = new wxGridCellAttr;
1871 attr->SetReadOnly();
1872 m_signalsGrid->SetAttr( row, COL_CURSOR_2, attr );
1873 m_signalsGrid->SetCellValue( row, COL_CURSOR_2, wxEmptyString );
1874 }
1875 }
1876}
1877
1878
1880{
1881 auto quoteNetNames =
1882 [&]( wxString aExpression ) -> wxString
1883 {
1884 for( const auto& [netname, quotedNetname] : m_quotedNetnames )
1885 aExpression.Replace( netname, quotedNetname );
1886
1887 return aExpression;
1888 };
1889
1890 for( const auto& [ id, signal ] : m_userDefinedSignals )
1891 {
1892 std::string cmd = "let user{} = {}";
1893
1894 m_simulator->Command( "echo " + fmt::format( cmd, id, signal.ToStdString() ) );
1895 m_simulator->Command( fmt::format( cmd, id, quoteNetNames( signal ).ToStdString() ) );
1896 }
1897}
1898
1899
1901{
1902 wxString errors;
1903 WX_STRING_REPORTER reporter( &errors );
1904
1905 for( const TUNER_SLIDER* tuner : m_tuners )
1906 {
1907 SCH_SHEET_PATH sheetPath;
1908 wxString ref = tuner->GetSymbolRef();
1909 KIID symbolId = tuner->GetSymbol( &sheetPath );
1910 SCH_ITEM* schItem = sheetPath.GetItem( symbolId );
1911 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( schItem );
1912
1913 if( !symbol )
1914 {
1915 reporter.Report( wxString::Format( _( "%s not found" ), ref ) );
1916 continue;
1917 }
1918
1919 const SPICE_ITEM* item = GetExporter()->FindItem( tuner->GetSymbolRef().ToStdString() );
1920
1921 if( !item || !item->model->GetTunerParam() )
1922 {
1923 reporter.Report( wxString::Format( _( "%s is not tunable" ), ref ) );
1924 continue;
1925 }
1926
1927 double floatVal = tuner->GetValue().ToDouble();
1928
1929 m_simulator->Command( item->model->SpiceGenerator().TunerCommand( *item, floatVal ) );
1930 }
1931
1932 if( reporter.HasMessage() )
1933 DisplayErrorMessage( this, _( "Could not apply tuned value(s):" ) + wxS( "\n" ) + errors );
1934}
1935
1936
1938 const wxString& aSignalName, const wxString& aParams )
1939{
1940 auto addCursor =
1941 [&]( int aCursorId, double x )
1942 {
1943 CURSOR* cursor = new CURSOR( aTrace, aPlotPanel );
1944
1945 cursor->SetName( aSignalName );
1946 cursor->SetPen( wxPen( aTrace->GetTraceColour() ) );
1947 cursor->SetCoordX( x );
1948
1949 aTrace->SetCursor( aCursorId, cursor );
1950 aPlotPanel->GetPlotWin()->AddLayer( cursor );
1951 };
1952
1953 wxArrayString items = wxSplit( aParams, '|' );
1954
1955 for( const wxString& item : items )
1956 {
1957 if( item.StartsWith( wxS( "rgb" ) ) )
1958 {
1959 wxColour color;
1960 color.Set( item );
1961 aTrace->SetTraceColour( color );
1962 aPlotPanel->UpdateTraceStyle( aTrace );
1963 }
1964 else if( item.StartsWith( wxS( "cursor1" ) ) )
1965 {
1966 wxArrayString parts = wxSplit( item, ':' );
1967 double val;
1968
1969 if( parts.size() == 3 )
1970 {
1971 parts[0].AfterFirst( '=' ).ToDouble( &val );
1972 m_cursorFormats[0][0].FromString( parts[1] );
1973 m_cursorFormats[0][1].FromString( parts[2] );
1974 addCursor( 1, val );
1975 }
1976 }
1977 else if( item.StartsWith( wxS( "cursor2" ) ) )
1978 {
1979 wxArrayString parts = wxSplit( item, ':' );
1980 double val;
1981
1982 if( parts.size() == 3 )
1983 {
1984 parts[0].AfterFirst( '=' ).ToDouble( &val );
1985 m_cursorFormats[1][0].FromString( parts[1] );
1986 m_cursorFormats[1][1].FromString( parts[2] );
1987 addCursor( 2, val );
1988 }
1989 }
1990 else if( item.StartsWith( wxS( "cursorD" ) ) )
1991 {
1992 wxArrayString parts = wxSplit( item, ':' );
1993
1994 if( parts.size() == 3 )
1995 {
1996 m_cursorFormats[2][0].FromString( parts[1] );
1997 m_cursorFormats[2][1].FromString( parts[2] );
1998 }
1999 }
2000 else if( item == wxS( "dottedSecondary" ) )
2001 {
2002 aPlotPanel->SetDottedSecondary( true );
2003 }
2004 else if( item == wxS( "hideGrid" ) )
2005 {
2006 aPlotPanel->ShowGrid( false );
2007 }
2008 }
2009}
2010
2011
2012bool SIMULATOR_FRAME::LoadWorkbook( const wxString& aPath )
2013{
2015
2016 wxTextFile file( aPath );
2017
2018#define DISPLAY_LOAD_ERROR( fmt ) DisplayErrorMessage( this, wxString::Format( _( fmt ), \
2019 file.GetCurrentLine()+1 ) )
2020
2021 if( !file.Open() )
2022 return false;
2023
2024 long version = 1;
2025 wxString firstLine = file.GetFirstLine();
2026 wxString plotCountLine;
2027
2028 if( firstLine.StartsWith( wxT( "version " ) ) )
2029 {
2030 if( !firstLine.substr( 8 ).ToLong( &version ) )
2031 {
2032 DISPLAY_LOAD_ERROR( "Error loading workbook: Line %d is not an integer." );
2033 file.Close();
2034
2035 return false;
2036 }
2037
2038 plotCountLine = file.GetNextLine();
2039 }
2040 else
2041 {
2042 plotCountLine = firstLine;
2043 }
2044
2045 long plotsCount;
2046
2047 if( !plotCountLine.ToLong( &plotsCount ) )
2048 {
2049 DISPLAY_LOAD_ERROR( "Error loading workbook: Line %d is not an integer." );
2050 file.Close();
2051
2052 return false;
2053 }
2054
2055 std::map<SIM_PLOT_PANEL*, std::vector<std::tuple<long, wxString, wxString>>> traceInfo;
2056
2057 for( long i = 0; i < plotsCount; ++i )
2058 {
2059 long plotType, tracesCount;
2060
2061 if( !file.GetNextLine().ToLong( &plotType ) )
2062 {
2063 DISPLAY_LOAD_ERROR( "Error loading workbook: Line %d is not an integer." );
2064 file.Close();
2065
2066 return false;
2067 }
2068
2069 wxString command = UnescapeString( file.GetNextLine() );
2070 wxString simCommand;
2072 wxStringTokenizer tokenizer( command, wxT( "\r\n" ), wxTOKEN_STRTOK );
2073
2074 if( version >= 2 )
2075 {
2076 simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS;
2077 simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES;
2078 simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
2079 }
2080
2081 if( version >= 3 )
2082 {
2083 simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
2084 }
2085
2086 while( tokenizer.HasMoreTokens() )
2087 {
2088 wxString line = tokenizer.GetNextToken();
2089
2090 if( line.StartsWith( wxT( ".kicad adjustpaths" ) ) )
2092 else if( line.StartsWith( wxT( ".save all" ) ) )
2094 else if( line.StartsWith( wxT( ".probe alli" ) ) )
2096 else if( line.StartsWith( wxT( ".probe allp" ) ) )
2098 else
2099 simCommand += line + wxT( "\n" );
2100 }
2101
2102 SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( simCommand,
2103 simOptions ) );
2104
2105 if( !file.GetNextLine().ToLong( &tracesCount ) )
2106 {
2107 DISPLAY_LOAD_ERROR( "Error loading workbook: Line %d is not an integer." );
2108 file.Close();
2109
2110 return false;
2111 }
2112
2113 if( plotPanel )
2114 traceInfo[ plotPanel ] = {};
2115
2116 for( long j = 0; j < tracesCount; ++j )
2117 {
2118 long traceType;
2119 wxString name, param;
2120
2121 if( !file.GetNextLine().ToLong( &traceType ) )
2122 {
2123 DISPLAY_LOAD_ERROR( "Error loading workbook: Line %d is not an integer." );
2124 file.Close();
2125
2126 return false;
2127 }
2128
2129 name = file.GetNextLine();
2130
2131 if( name.IsEmpty() )
2132 {
2133 DISPLAY_LOAD_ERROR( "Error loading workbook: Line %d is empty." );
2134 file.Close();
2135
2136 return false;
2137 }
2138
2139 param = file.GetNextLine();
2140
2141 if( plotPanel )
2142 traceInfo[ plotPanel ].emplace_back( std::make_tuple( traceType, name, param ) );
2143 }
2144 }
2145
2146 long userDefinedSignalCount;
2147 long measurementCount;
2148
2149 if( file.GetNextLine().ToLong( &userDefinedSignalCount ) )
2150 {
2151 for( int ii = 0; ii < (int) userDefinedSignalCount; ++ii )
2152 m_userDefinedSignals[ ii ] = file.GetNextLine();
2153
2154 file.GetNextLine().ToLong( &measurementCount );
2155
2157 m_measurementsGrid->AppendRows( (int) measurementCount + 1 /* empty row at end */ );
2158
2159 for( int row = 0; row < (int) measurementCount; ++row )
2160 {
2161 m_measurementsGrid->SetCellValue( row, COL_MEASUREMENT, file.GetNextLine() );
2162 m_measurementsGrid->SetCellValue( row, COL_MEASUREMENT_FORMAT, file.GetNextLine() );
2163 }
2164 }
2165
2166 for( const auto& [ plotPanel, traceInfoVector ] : traceInfo )
2167 {
2168 for( const auto& [ traceType, signalName, param ] : traceInfoVector )
2169 {
2170 if( traceType == SPT_UNKNOWN && signalName == wxS( "$LEGEND" ) )
2171 {
2172 wxArrayString coords = wxSplit( param, ' ' );
2173
2174 if( coords.size() >= 2 )
2175 {
2176 long x = 0;
2177 long y = 0;
2178
2179 coords[0].ToLong( &x );
2180 coords[1].ToLong( &y );
2181 plotPanel->SetLegendPosition( wxPoint( (int) x, (int) y ) );
2182 }
2183
2184 plotPanel->ShowLegend( true );
2185 }
2186 else
2187 {
2188 wxString vectorName = vectorNameFromSignalName( signalName, nullptr );
2189 TRACE* trace = plotPanel->AddTrace( vectorName, (int) traceType );
2190
2191 if( version >= 4 && trace )
2192 parseTraceParams( plotPanel, trace, signalName, param );
2193 }
2194 }
2195
2196 plotPanel->UpdatePlotColors();
2197 }
2198
2199 LoadSimulator();
2200
2201 wxString schTextSimCommand = m_circuitModel->GetSchTextSimCommand().Lower();
2202 SIM_TYPE schTextSimType = NGSPICE_CIRCUIT_MODEL::CommandToSimType( schTextSimCommand );
2203
2204 if( schTextSimType != ST_UNKNOWN )
2205 {
2206 bool found = false;
2207
2208 for( int ii = 0; ii < (int) m_plotNotebook->GetPageCount(); ++ii )
2209 {
2210 auto* plot = dynamic_cast<const SIM_PANEL_BASE*>( m_plotNotebook->GetPage( ii ) );
2211
2212 if( plot && plot->GetType() == schTextSimType )
2213 {
2214 if( schTextSimType == ST_DC )
2215 {
2216 wxString plotSimCommand = plot->GetSimCommand().Lower();
2217 found = plotSimCommand.GetChar( 4 ) == schTextSimCommand.GetChar( 4 );
2218 }
2219 else
2220 {
2221 found = true;
2222 }
2223 }
2224 }
2225
2226 if( !found )
2227 NewPlotPanel( schTextSimCommand, m_circuitModel->GetSimOptions() );
2228 }
2229
2231
2232 rebuildSignalsGrid( m_filter->GetValue() );
2234 updateCursors();
2235
2236 file.Close();
2237
2238 wxFileName filename( aPath );
2239 filename.MakeRelativeTo( Prj().GetProjectPath() );
2240
2241 // Remember the loaded workbook filename.
2242 m_simulator->Settings()->SetWorkbookFilename( filename.GetFullPath() );
2243
2244 updateTitle();
2245
2246 // Successfully loading a workbook does not count as modifying it. Clear the modified
2247 // flag after all the EVT_WORKBOOK_MODIFIED events have been processed.
2248 CallAfter( [=]()
2249 {
2250 m_workbookModified = false;
2251 } );
2252
2253 return true;
2254}
2255
2256
2257bool SIMULATOR_FRAME::SaveWorkbook( const wxString& aPath )
2258{
2259 wxFileName filename = aPath;
2260 filename.SetExt( WorkbookFileExtension );
2261
2262 wxTextFile file( filename.GetFullPath() );
2263
2264 if( file.Exists() )
2265 {
2266 if( !file.Open() )
2267 return false;
2268
2269 file.Clear();
2270 }
2271 else
2272 {
2273 file.Create();
2274 }
2275
2276 file.AddLine( wxT( "version 4" ) );
2277
2278 file.AddLine( wxString::Format( wxT( "%llu" ), m_plotNotebook->GetPageCount() ) );
2279
2280 for( size_t i = 0; i < m_plotNotebook->GetPageCount(); i++ )
2281 {
2282 const SIM_PANEL_BASE* basePanel = dynamic_cast<const SIM_PANEL_BASE*>( m_plotNotebook->GetPage( i ) );
2283
2284 if( !basePanel )
2285 {
2286 file.AddLine( wxString::Format( wxT( "%llu" ), 0ull ) );
2287 continue;
2288 }
2289
2290 file.AddLine( wxString::Format( wxT( "%d" ), basePanel->GetType() ) );
2291
2292 wxString command = basePanel->GetSimCommand();
2293 int options = basePanel->GetSimOptions();
2294
2296 command += wxT( "\n.kicad adjustpaths" );
2297
2299 command += wxT( "\n.save all" );
2300
2302 command += wxT( "\n.probe alli" );
2303
2305 command += wxT( "\n.probe allp" );
2306
2307 file.AddLine( EscapeString( command, CTX_LINE ) );
2308
2309 const SIM_PLOT_PANEL* plotPanel = dynamic_cast<const SIM_PLOT_PANEL*>( basePanel );
2310
2311 if( !plotPanel )
2312 {
2313 file.AddLine( wxString::Format( wxT( "%llu" ), 0ull ) );
2314 continue;
2315 }
2316
2317 size_t traceCount = plotPanel->GetTraces().size();
2318
2319 if( plotPanel->IsLegendShown() )
2320 traceCount++;
2321
2322 file.AddLine( wxString::Format( wxT( "%llu" ), traceCount ) );
2323
2324 auto findSignalName =
2325 [&]( const wxString& aVectorName ) -> wxString
2326 {
2327 for( const auto& [ id, signal ] : m_userDefinedSignals )
2328 {
2329 if( aVectorName == vectorNameFromSignalId( id ) )
2330 return signal;
2331 }
2332
2333 return aVectorName;
2334 };
2335
2336 for( const auto& [name, trace] : plotPanel->GetTraces() )
2337 {
2338 file.AddLine( wxString::Format( wxT( "%d" ), trace->GetType() ) );
2339 file.AddLine( findSignalName( trace->GetName() ) );
2340
2341 wxString msg = COLOR4D( trace->GetTraceColour() ).ToCSSString();
2342
2343 if( CURSOR* cursor = trace->GetCursor( 1 ) )
2344 {
2345 msg += wxString::Format( wxS( "|cursor1=%E:%s:%s" ),
2346 cursor->GetCoords().x,
2347 m_cursorFormats[0][0].ToString(),
2348 m_cursorFormats[0][1].ToString() );
2349 }
2350
2351 if( CURSOR* cursor = trace->GetCursor( 2 ) )
2352 {
2353 msg += wxString::Format( wxS( "|cursor2=%E:%s:%s" ),
2354 cursor->GetCoords().x,
2355 m_cursorFormats[1][0].ToString(),
2356 m_cursorFormats[1][1].ToString() );
2357 }
2358
2359 if( trace->GetCursor( 1 ) || trace->GetCursor( 2 ) )
2360 {
2361 msg += wxString::Format( wxS( "|cursorD:%s:%s" ),
2362 m_cursorFormats[2][0].ToString(),
2363 m_cursorFormats[2][1].ToString() );
2364 }
2365
2366 if( plotPanel->GetDottedSecondary() )
2367 msg += wxS( "|dottedSecondary" );
2368
2369 if( !plotPanel->IsGridShown() )
2370 msg += wxS( "|hideGrid" );
2371
2372 file.AddLine( msg );
2373 }
2374
2375 if( plotPanel->IsLegendShown() )
2376 {
2377 file.AddLine( wxString::Format( wxT( "%d" ), SPT_UNKNOWN ) );
2378 file.AddLine( wxT( "$LEGEND" ) );
2379 file.AddLine( wxString::Format( wxT( "%d %d" ),
2380 plotPanel->GetLegendPosition().x,
2381 plotPanel->GetLegendPosition().y - 40 ) );
2382 }
2383 }
2384
2385 file.AddLine( wxString::Format( wxT( "%llu" ), m_userDefinedSignals.size() ) );
2386
2387 for( const auto& [ id, signal ] : m_userDefinedSignals )
2388 file.AddLine( signal );
2389
2390 std::vector<wxString> measurements;
2391 std::vector<wxString> formats;
2392
2393 for( int i = 0; i < m_measurementsGrid->GetNumberRows(); ++i )
2394 {
2395 if( !m_measurementsGrid->GetCellValue( i, COL_MEASUREMENT ).IsEmpty() )
2396 {
2397 measurements.push_back( m_measurementsGrid->GetCellValue( i, COL_MEASUREMENT ) );
2398 formats.push_back( m_measurementsGrid->GetCellValue( i, COL_MEASUREMENT_FORMAT ) );
2399 }
2400 }
2401
2402 file.AddLine( wxString::Format( wxT( "%llu" ), measurements.size() ) );
2403
2404 for( size_t i = 0; i < measurements.size(); i++ )
2405 {
2406 file.AddLine( measurements[i] );
2407 file.AddLine( formats[i] );
2408 }
2409
2410 bool res = file.Write();
2411 file.Close();
2412
2413 // Store the filename of the last saved workbook.
2414 if( res )
2415 {
2416 filename.MakeRelativeTo( Prj().GetProjectPath() );
2417 m_simulator->Settings()->SetWorkbookFilename( filename.GetFullPath() );
2418 }
2419
2420 m_workbookModified = false;
2421 updateTitle();
2422
2423 return res;
2424}
2425
2426
2428{
2429 switch( aType )
2430 {
2432 case ST_AC: return SPT_LIN_FREQUENCY;
2433 case ST_DC: return SPT_SWEEP;
2434 case ST_TRANSIENT: return SPT_TIME;
2435 default: wxFAIL_MSG( wxS( "Unhandled simulation type" ) ); return SPT_UNKNOWN;
2436 }
2437}
2438
2439
2441{
2443
2444 // Rebuild the color list to plot traces
2446
2447 // Now send changes to all SIM_PLOT_PANEL
2448 for( size_t page = 0; page < m_plotNotebook->GetPageCount(); page++ )
2449 {
2450 wxWindow* curPage = m_plotNotebook->GetPage( page );
2451
2452 // ensure it is truly a plot panel and not the (zero plots) placeholder
2453 // which is only SIM_PLOT_PANEL_BASE
2454 SIM_PLOT_PANEL* panel = dynamic_cast<SIM_PLOT_PANEL*>( curPage );
2455
2456 if( panel )
2457 panel->UpdatePlotColors();
2458 }
2459}
2460
2461
2462void SIMULATOR_FRAME::onPlotClose( wxAuiNotebookEvent& event )
2463{
2464}
2465
2466
2467void SIMULATOR_FRAME::onPlotClosed( wxAuiNotebookEvent& event )
2468{
2469 CallAfter( [this]()
2470 {
2472 rebuildSignalsGrid( m_filter->GetValue() );
2473 updateCursors();
2474
2476
2477 if( !panel || panel->GetType() != ST_OP )
2478 {
2479 SCHEMATIC& schematic = m_schematicFrame->Schematic();
2480 schematic.ClearOperatingPoints();
2483 }
2484 } );
2485}
2486
2487
2488void SIMULATOR_FRAME::onPlotChanged( wxAuiNotebookEvent& event )
2489{
2491 rebuildSignalsGrid( m_filter->GetValue() );
2492 updateCursors();
2493}
2494
2495
2496void SIMULATOR_FRAME::onPlotDragged( wxAuiNotebookEvent& event )
2497{
2498}
2499
2500
2501void SIMULATOR_FRAME::onNotebookModified( wxCommandEvent& event )
2502{
2503 OnModify();
2504 updateTitle();
2505}
2506
2507
2509{
2510 SIM_PANEL_BASE* plotPanelWindow = getCurrentPlotWindow();
2511 DIALOG_SIM_COMMAND dlg( this, m_circuitModel, m_simulator->Settings() );
2512 wxString errors;
2513 WX_STRING_REPORTER reporter( &errors );
2514
2515 if( !m_circuitModel->ReadSchematicAndLibraries( NETLIST_EXPORTER_SPICE::OPTION_DEFAULT_FLAGS,
2516 reporter ) )
2517 {
2518 DisplayErrorMessage( this, _( "Errors during netlist generation.\n\n" )
2519 + errors );
2520 }
2521
2522 if( m_plotNotebook->GetPageIndex( plotPanelWindow ) != wxNOT_FOUND )
2523 {
2524 dlg.SetSimCommand( plotPanelWindow->GetSimCommand() );
2525 dlg.SetSimOptions( plotPanelWindow->GetSimOptions() );
2526 }
2527 else
2528 {
2530 }
2531
2532 if( dlg.ShowModal() == wxID_OK )
2533 {
2534 wxString oldCommand;
2535
2536 if( m_plotNotebook->GetPageIndex( plotPanelWindow ) != wxNOT_FOUND )
2537 oldCommand = plotPanelWindow->GetSimCommand();
2538 else
2539 oldCommand = wxString();
2540
2541 const wxString& newCommand = dlg.GetSimCommand();
2542 int newOptions = dlg.GetSimOptions();
2543 SIM_TYPE newSimType = NGSPICE_CIRCUIT_MODEL::CommandToSimType( newCommand );
2544
2545 if( !plotPanelWindow )
2546 {
2547 m_circuitModel->SetSimCommandOverride( newCommand );
2548 m_circuitModel->SetSimOptions( newOptions );
2549 plotPanelWindow = NewPlotPanel( newCommand, newOptions );
2550 }
2551 // If it is a new simulation type, open a new plot. For the DC sim, check if sweep
2552 // source type has changed (char 4 will contain 'v', 'i', 'r' or 't'.
2553 else if( plotPanelWindow->GetType() != newSimType
2554 || ( newSimType == ST_DC
2555 && oldCommand.Lower().GetChar( 4 ) != newCommand.Lower().GetChar( 4 ) ) )
2556 {
2557 plotPanelWindow = NewPlotPanel( newCommand, newOptions );
2558 }
2559 else
2560 {
2561 if( m_plotNotebook->GetPageIndex( plotPanelWindow ) == 0 )
2562 m_circuitModel->SetSimCommandOverride( newCommand );
2563
2564 // Update simulation command in the current plot
2565 plotPanelWindow->SetSimCommand( newCommand );
2566 plotPanelWindow->SetSimOptions( newOptions );
2567 }
2568
2569 OnModify();
2570 m_simulator->Init();
2571 return true;
2572 }
2573
2574 return false;
2575}
2576
2577
2578bool SIMULATOR_FRAME::canCloseWindow( wxCloseEvent& aEvent )
2579{
2580 if( m_workbookModified )
2581 {
2582 wxFileName filename = m_simulator->Settings()->GetWorkbookFilename();
2583
2584 if( filename.GetName().IsEmpty() )
2585 {
2586 if( Prj().GetProjectName().IsEmpty() )
2587 filename.SetFullName( wxT( "noname.wbk" ) );
2588 else
2589 filename.SetFullName( Prj().GetProjectName() + wxT( ".wbk" ) );
2590 }
2591
2592 return HandleUnsavedChanges( this, _( "Save changes to workbook?" ),
2593 [&]() -> bool
2594 {
2595 return SaveWorkbook( Prj().AbsolutePath( filename.GetFullName() ) );
2596 } );
2597 }
2598
2599 return true;
2600}
2601
2602
2604{
2605 if( m_simulator->IsRunning() )
2606 m_simulator->Stop();
2607
2608 // Prevent memory leak on exit by deleting all simulation vectors
2609 m_simulator->Clean();
2610
2611 // Cancel a running simProbe or simTune tool
2613
2614 SaveSettings( config() );
2615
2616 m_simulator->Settings() = nullptr;
2617
2618 Destroy();
2619}
2620
2621
2623{
2624 SUPPRESS_GRID_CELL_EVENTS raii( this );
2625
2627
2628 SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
2629
2630 if( !plotPanel )
2631 return;
2632
2633 // Update cursor values
2634 CURSOR* cursor1 = nullptr;
2635 wxString cursor1Name;
2636 wxString cursor1Units;
2637 CURSOR* cursor2 = nullptr;
2638 wxString cursor2Name;
2639 wxString cursor2Units;
2640
2641 auto getUnitsY =
2642 [&]( TRACE* aTrace ) -> wxString
2643 {
2644 if( ( aTrace->GetType() & SPT_AC_PHASE ) || ( aTrace->GetType() & SPT_CURRENT ) )
2645 return plotPanel->GetUnitsY2();
2646 else if( aTrace->GetType() & SPT_POWER )
2647 return plotPanel->GetUnitsY3();
2648 else
2649 return plotPanel->GetUnitsY1();
2650 };
2651
2652 auto getNameY =
2653 [&]( TRACE* aTrace ) -> wxString
2654 {
2655 if( ( aTrace->GetType() & SPT_AC_PHASE ) || ( aTrace->GetType() & SPT_CURRENT ) )
2656 return plotPanel->GetLabelY2();
2657 else if( aTrace->GetType() & SPT_POWER )
2658 return plotPanel->GetLabelY3();
2659 else
2660 return plotPanel->GetLabelY1();
2661 };
2662
2663 auto formatValue =
2664 [this]( double aValue, int aCursorId, int aCol ) -> wxString
2665 {
2666 if( !m_simFinished && aCol == 1 )
2667 return wxS( "--" );
2668 else
2669 return SPICE_VALUE( aValue ).ToString( m_cursorFormats[ aCursorId ][ aCol ] );
2670 };
2671
2672 for( const auto& [name, trace] : plotPanel->GetTraces() )
2673 {
2674 if( CURSOR* cursor = trace->GetCursor( 1 ) )
2675 {
2676 cursor1 = cursor;
2677 cursor1Name = getNameY( trace );
2678 cursor1Units = getUnitsY( trace );
2679
2680 wxRealPoint coords = cursor->GetCoords();
2681 int row = m_cursorsGrid->GetNumberRows();
2682
2683 m_cursorFormats[0][0].UpdateUnits( plotPanel->GetUnitsX() );
2684 m_cursorFormats[0][1].UpdateUnits( cursor1Units );
2685
2686 m_cursorsGrid->AppendRows( 1 );
2687 m_cursorsGrid->SetCellValue( row, COL_CURSOR_NAME, wxS( "1" ) );
2688 m_cursorsGrid->SetCellValue( row, COL_CURSOR_SIGNAL, cursor->GetName() );
2689 m_cursorsGrid->SetCellValue( row, COL_CURSOR_X, formatValue( coords.x, 0, 0 ) );
2690 m_cursorsGrid->SetCellValue( row, COL_CURSOR_Y, formatValue( coords.y, 0, 1 ) );
2691 break;
2692 }
2693 }
2694
2695 for( const auto& [name, trace] : plotPanel->GetTraces() )
2696 {
2697 if( CURSOR* cursor = trace->GetCursor( 2 ) )
2698 {
2699 cursor2 = cursor;
2700 cursor2Name = getNameY( trace );
2701 cursor2Units = getUnitsY( trace );
2702
2703 wxRealPoint coords = cursor->GetCoords();
2704 int row = m_cursorsGrid->GetNumberRows();
2705
2706 m_cursorFormats[1][0].UpdateUnits( plotPanel->GetUnitsX() );
2707 m_cursorFormats[1][1].UpdateUnits( cursor2Units );
2708
2709 m_cursorsGrid->AppendRows( 1 );
2710 m_cursorsGrid->SetCellValue( row, COL_CURSOR_NAME, wxS( "2" ) );
2711 m_cursorsGrid->SetCellValue( row, COL_CURSOR_SIGNAL, cursor->GetName() );
2712 m_cursorsGrid->SetCellValue( row, COL_CURSOR_X, formatValue( coords.x, 1, 0 ) );
2713 m_cursorsGrid->SetCellValue( row, COL_CURSOR_Y, formatValue( coords.y, 1, 1 ) );
2714 break;
2715 }
2716 }
2717
2718 if( cursor1 && cursor2 && cursor1Units == cursor2Units )
2719 {
2720 wxRealPoint coords = cursor2->GetCoords() - cursor1->GetCoords();
2721 wxString signal;
2722
2723 m_cursorFormats[2][0].UpdateUnits( plotPanel->GetUnitsX() );
2724 m_cursorFormats[2][1].UpdateUnits( cursor1Units );
2725
2726 if( cursor1->GetName() == cursor2->GetName() )
2727 signal = wxString::Format( wxS( "%s[2 - 1]" ), cursor2->GetName() );
2728 else
2729 signal = wxString::Format( wxS( "%s - %s" ), cursor2->GetName(), cursor1->GetName() );
2730
2731 m_cursorsGrid->AppendRows( 1 );
2732 m_cursorsGrid->SetCellValue( 2, COL_CURSOR_NAME, _( "Diff" ) );
2733 m_cursorsGrid->SetCellValue( 2, COL_CURSOR_SIGNAL, signal );
2734 m_cursorsGrid->SetCellValue( 2, COL_CURSOR_X, formatValue( coords.x, 2, 0 ) );
2735 m_cursorsGrid->SetCellValue( 2, COL_CURSOR_Y, formatValue( coords.y, 2, 1 ) );
2736 }
2737
2738 // Set up the labels
2739 m_cursorsGrid->SetColLabelValue( COL_CURSOR_X, plotPanel->GetLabelX() );
2740
2741 wxString valColName = _( "Value" );
2742
2743 if( !cursor1Name.IsEmpty() )
2744 {
2745 if( cursor2Name.IsEmpty() || cursor1Name == cursor2Name )
2746 valColName = cursor1Name;
2747 }
2748 else if( !cursor2Name.IsEmpty() )
2749 {
2750 valColName = cursor2Name;
2751 }
2752
2753 m_cursorsGrid->SetColLabelValue( COL_CURSOR_Y, valColName );
2754}
2755
2756
2757void SIMULATOR_FRAME::onCursorUpdate( wxCommandEvent& aEvent )
2758{
2759 updateCursors();
2760 OnModify();
2761}
2762
2763
2765{
2767
2769 wxASSERT( mgr );
2770
2771 auto showGridCondition =
2772 [this]( const SELECTION& aSel )
2773 {
2775 return plot && plot->IsGridShown();
2776 };
2777
2778 auto showLegendCondition =
2779 [this]( const SELECTION& aSel )
2780 {
2782 return plot && plot->IsLegendShown();
2783 };
2784
2785 auto showDottedCondition =
2786 [this]( const SELECTION& aSel )
2787 {
2789 return plot && plot->GetDottedSecondary();
2790 };
2791
2792 auto darkModePlotCondition =
2793 [this]( const SELECTION& aSel )
2794 {
2795 return m_darkMode;
2796 };
2797
2798 auto simRunning =
2799 [this]( const SELECTION& aSel )
2800 {
2801 return m_simulator && m_simulator->IsRunning();
2802 };
2803
2804 auto simFinished =
2805 [this]( const SELECTION& aSel )
2806 {
2807 return m_simFinished;
2808 };
2809
2810 auto havePlot =
2811 [this]( const SELECTION& aSel )
2812 {
2813 return GetCurrentPlot() != nullptr;
2814 };
2815
2816#define ENABLE( x ) ACTION_CONDITIONS().Enable( x )
2817#define CHECK( x ) ACTION_CONDITIONS().Check( x )
2818
2822
2825
2826 mgr->SetConditions( EE_ACTIONS::toggleGrid, CHECK( showGridCondition ) );
2827 mgr->SetConditions( EE_ACTIONS::toggleLegend, CHECK( showLegendCondition ) );
2828 mgr->SetConditions( EE_ACTIONS::toggleDottedSecondary, CHECK( showDottedCondition ) );
2829 mgr->SetConditions( EE_ACTIONS::toggleDarkModePlots, CHECK( darkModePlotCondition ) );
2830
2832 mgr->SetConditions( EE_ACTIONS::runSimulation, ENABLE( !simRunning ) );
2833 mgr->SetConditions( EE_ACTIONS::stopSimulation, ENABLE( simRunning ) );
2834 mgr->SetConditions( EE_ACTIONS::simProbe, ENABLE( simFinished ) );
2835 mgr->SetConditions( EE_ACTIONS::simTune, ENABLE( simFinished ) );
2837
2838#undef CHECK
2839#undef ENABLE
2840}
2841
2842
2843void SIMULATOR_FRAME::onSimStarted( wxCommandEvent& aEvent )
2844{
2845 SetCursor( wxCURSOR_ARROWWAIT );
2846}
2847
2848
2849void SIMULATOR_FRAME::onSimFinished( wxCommandEvent& aEvent )
2850{
2851 SetCursor( wxCURSOR_ARROW );
2852
2853 SIM_TYPE simType = m_circuitModel->GetSimType();
2854
2855 if( simType == ST_UNKNOWN )
2856 return;
2857
2858 SIM_PANEL_BASE* plotPanelWindow = getCurrentPlotWindow();
2859
2860 if( !plotPanelWindow || plotPanelWindow->GetType() != simType )
2861 {
2862 plotPanelWindow = NewPlotPanel( m_circuitModel->GetSimCommand(),
2863 m_circuitModel->GetSimOptions() );
2864 }
2865
2866 // Sometimes (for instance with a directive like wrdata my_file.csv "my_signal")
2867 // the simulator is in idle state (simulation is finished), but still running, during
2868 // the time the file is written. So gives a slice of time to fully finish the work:
2869 if( m_simulator->IsRunning() )
2870 {
2871 int max_time = 40; // For a max timeout = 2s
2872
2873 do
2874 {
2875 wxMilliSleep( 50 );
2876 wxYield();
2877
2878 if( max_time )
2879 max_time--;
2880
2881 } while( max_time && m_simulator->IsRunning() );
2882 }
2883 // Is a warning message useful if the simulatior is still running?
2884
2885 m_simFinished = true;
2886
2887 std::vector<wxString> oldSignals = m_signals;
2888
2891
2892 SCHEMATIC& schematic = m_schematicFrame->Schematic();
2893 schematic.ClearOperatingPoints();
2894
2895 // If there are any signals plotted, update them
2896 if( SIM_PANEL_BASE::IsPlottable( simType ) )
2897 {
2898 SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( plotPanelWindow );
2899 wxCHECK_RET( plotPanel, wxT( "not a SIM_PLOT_PANEL" ) );
2900
2901 // Map of TRACE* to { vectorName, traceType }
2902 std::map<TRACE*, std::pair<wxString, int>> traceMap;
2903
2904 for( const auto& [ name, trace ] : plotPanel->GetTraces() )
2905 traceMap[ trace ] = { wxEmptyString, SPT_UNKNOWN };
2906
2907 for( const wxString& signal : m_signals )
2908 {
2909 int traceType = SPT_UNKNOWN;
2910 wxString vectorName = vectorNameFromSignalName( signal, &traceType );
2911
2912 if( simType == ST_AC )
2913 {
2914 for( int subType : { SPT_AC_MAG, SPT_AC_PHASE } )
2915 {
2916 if( TRACE* trace = plotPanel->GetTrace( vectorName, traceType | subType ) )
2917 traceMap[ trace ] = { vectorName, traceType };
2918 }
2919 }
2920 else
2921 {
2922 if( TRACE* trace = plotPanel->GetTrace( vectorName, traceType ) )
2923 traceMap[ trace ] = { vectorName, traceType };
2924 }
2925 }
2926
2927 // Two passes so that DC-sweep sub-traces get deleted and re-created:
2928
2929 for( const auto& [ trace, traceInfo ] : traceMap )
2930 {
2931 if( traceInfo.first.IsEmpty() )
2932 plotPanel->DeleteTrace( trace );
2933 }
2934
2935 for( const auto& [ trace, traceInfo ] : traceMap )
2936 {
2937 if( !traceInfo.first.IsEmpty() )
2938 updateTrace( traceInfo.first, traceInfo.second, plotPanel );
2939 }
2940
2941 rebuildSignalsGrid( m_filter->GetValue() );
2943
2944 plotPanel->GetPlotWin()->UpdateAll();
2945 plotPanel->ResetScales();
2946 plotPanel->GetPlotWin()->Fit();
2947 }
2948 else if( simType == ST_OP )
2949 {
2950 m_simConsole->AppendText( _( "\n\nSimulation results:\n\n" ) );
2951 m_simConsole->SetInsertionPointEnd();
2952
2953 for( const std::string& vec : m_simulator->AllPlots() )
2954 {
2955 std::vector<double> val_list = m_simulator->GetRealPlot( vec, 1 );
2956
2957 if( val_list.size() == 0 ) // The list of values can be empty!
2958 continue;
2959
2960 wxString value = SPICE_VALUE( val_list.at( 0 ) ).ToSpiceString();
2961 wxString msg;
2962 wxString signal;
2963 SIM_TRACE_TYPE type = m_circuitModel->VectorToSignal( vec, signal );
2964
2965 const size_t tab = 25; //characters
2966 size_t padding = ( signal.length() < tab ) ? ( tab - signal.length() ) : 1;
2967
2968 value.Append( type == SPT_CURRENT ? wxS( "A" ) : wxS( "V" ) );
2969
2970 msg.Printf( wxT( "%s%s\n" ),
2971 ( signal + wxT( ":" ) ).Pad( padding, wxUniChar( ' ' ) ),
2972 value );
2973
2974 m_simConsole->AppendText( msg );
2975 m_simConsole->SetInsertionPointEnd();
2976
2977 if( signal.StartsWith( wxS( "V(" ) ) || signal.StartsWith( wxS( "I(" ) ) )
2978 signal = signal.SubString( 2, signal.Length() - 2 );
2979
2980 schematic.SetOperatingPoint( signal, val_list.at( 0 ) );
2981 }
2982 }
2983
2986
2987 updateCursors();
2988
2989 for( int row = 0; row < m_measurementsGrid->GetNumberRows(); ++row )
2990 UpdateMeasurement( row );
2991
2992 m_lastSimPlot = plotPanelWindow;
2993}
2994
2995
2996void SIMULATOR_FRAME::onSimUpdate( wxCommandEvent& aEvent )
2997{
2998 static bool updateInProgress = false;
2999
3000 // skip update when events are triggered too often and previous call didn't end yet
3001 if( updateInProgress )
3002 return;
3003
3004 updateInProgress = true;
3005
3006 if( m_simulator->IsRunning() )
3007 m_simulator->Stop();
3008
3010 {
3011 // We need to rerun simulation, as the simulator currently stores
3012 // results for another plot
3014 }
3015 else
3016 {
3017 std::unique_lock<std::mutex> simulatorLock( m_simulator->GetMutex(), std::try_to_lock );
3018
3019 if( simulatorLock.owns_lock() )
3020 {
3021 // Incremental update
3022 m_simConsole->Clear();
3023
3024 // Do not export netlist, it is already stored in the simulator
3025 applyTuners();
3026
3027 m_simulator->Run();
3028 }
3029 else
3030 {
3031 DisplayErrorMessage( this, _( "Another simulation is already running." ) );
3032 }
3033 }
3034 updateInProgress = false;
3035}
3036
3037
3038void SIMULATOR_FRAME::onSimReport( wxCommandEvent& aEvent )
3039{
3040 m_simConsole->AppendText( aEvent.GetString() + "\n" );
3041 m_simConsole->SetInsertionPointEnd();
3042}
3043
3044
3045void SIMULATOR_FRAME::onExit( wxCommandEvent& aEvent )
3046{
3047 if( aEvent.GetId() == wxID_EXIT )
3048 Kiway().OnKiCadExit();
3049
3050 if( aEvent.GetId() == wxID_CLOSE )
3051 Close( false );
3052}
3053
3054
3056{
3058 m_workbookModified = true;
3059}
3060
3061
3062wxDEFINE_EVENT( EVT_SIM_UPDATE, wxCommandEvent );
3063wxDEFINE_EVENT( EVT_SIM_REPORT, wxCommandEvent );
3064
3065wxDEFINE_EVENT( EVT_SIM_STARTED, wxCommandEvent );
3066wxDEFINE_EVENT( EVT_SIM_FINISHED, wxCommandEvent );
int color
Definition: DXF_plotter.cpp:57
const char * name
Definition: DXF_plotter.cpp:56
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:106
static TOOL_ACTION toggleGrid
Definition: actions.h:144
static TOOL_ACTION cancelInteractive
Definition: actions.h:63
Manage TOOL_ACTION objects.
void SetConditions(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Set the conditions the UI elements for activating a specific tool action should use for determining t...
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
Definition: app_settings.h:110
Handle actions that are shared between different applications.
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void doPopupSelection(wxCommandEvent &event) override
SIMULATOR_FRAME * m_parent
CURSORS_GRID_TRICKS(SIMULATOR_FRAME *aParent, WX_GRID *aGrid)
The SIMULATOR_FRAME holds the main user-interface for running simulations.
const wxRealPoint & GetCoords() const
void SetCoordX(double aValue)
void SetSimOptions(int aOptions)
bool SetSimCommand(const wxString &aCommand)
const wxString & GetSimCommand() const
virtual APP_SETTINGS_BASE * config() const
Returns the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
void ShowChangedLanguage() override
Redraw the menus and what not in current language.
virtual void setupUIConditions()
Setup the UI conditions for the various actions and their controls in this frame.
virtual void OnModify()
Must be called after a model change in order to set the "modify" flag and do other frame-specific pro...
virtual void LoadSettings(APP_SETTINGS_BASE *aCfg)
Load common frame parameters from a configuration file.
virtual void SaveSettings(APP_SETTINGS_BASE *aCfg)
Save common frame parameters to a configuration data file.
bool StartsWith(const wxString &aTerm)
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
static TOOL_ACTION simCommand
Definition: ee_actions.h:276
static TOOL_ACTION openWorkbook
Definition: ee_actions.h:265
static TOOL_ACTION stopSimulation
Definition: ee_actions.h:278
static TOOL_ACTION toggleLegend
Definition: ee_actions.h:273
static TOOL_ACTION saveWorkbook
Definition: ee_actions.h:266
static TOOL_ACTION saveWorkbookAs
Definition: ee_actions.h:267
static TOOL_ACTION exportPlotAsCSV
Definition: ee_actions.h:269
static TOOL_ACTION simTune
Definition: ee_actions.h:272
static TOOL_ACTION toggleDarkModePlots
Definition: ee_actions.h:275
static TOOL_ACTION exportPlotAsPNG
Definition: ee_actions.h:268
static TOOL_ACTION showNetlist
Definition: ee_actions.h:280
static TOOL_ACTION simProbe
Definition: ee_actions.h:271
static TOOL_ACTION toggleDottedSecondary
Definition: ee_actions.h:274
static TOOL_ACTION runSimulation
Definition: ee_actions.h:277
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:61
virtual void doPopupSelection(wxCommandEvent &event)
virtual void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent)
WX_GRID * m_grid
I don't own the grid, but he owns me.
Definition: grid_tricks.h:125
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
wxString ToCSSString() const
Definition: color4d.cpp:147
Definition: kiid.h:48
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:279
void OnKiCadExit()
Definition: kiway.cpp:733
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:432
Hold a translatable error message and may be used when throwing exceptions containing a translated er...
Definition: ki_exception.h:45
const wxString What() const
Definition: ki_exception.h:57
void doPopupSelection(wxCommandEvent &event) override
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
SIMULATOR_FRAME * m_parent
MEASUREMENTS_GRID_TRICKS(SIMULATOR_FRAME *aParent, WX_GRID *aGrid)
const SPICE_ITEM * FindItem(const std::string &aRefName) const
Find and return the item corresponding to aRefName.
Special netlist exporter flavor that allows one to override simulation commands.
static SIM_TYPE CommandToSimType(const wxString &aCmd)
Return simulation type basing on a simulation command directive.
Container for Ngspice simulator settings.
void SetModelMode(NGSPICE_MODEL_MODE aMode)
A singleton reporter that reports to nowhere.
Definition: reporter.h:223
The backing store for a PROJECT, in JSON format.
Definition: project_file.h:65
virtual PROJECT_FILE & GetProjectFile() const
Definition: project.h:149
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition: project.cpp:305
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
Holds all the data relating to one schematic.
Definition: schematic.h:72
void SetOperatingPoint(const wxString &aSignal, double aValue)
Set operating points from a .op simulation.
Definition: schematic.h:229
void ClearOperatingPoints()
Clear operating points from a .op simulation.
Definition: schematic.h:221
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
Schematic editor (Eeschema) main window.
void RefreshOperatingPointDisplay()
Refresh the display of any operaintg points.
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag and update other data struc...
bool ReadyToNetlist(const wxString &aAnnotateMessage)
Check if we are ready to write a netlist file for the current schematic.
SCHEMATIC & Schematic() const
void RecalculateConnections(SCH_CLEANUP_FLAGS aCleanupFlags)
Generate the connection data for the entire schematic hierarchy.
void UpdateItem(EDA_ITEM *aItem, bool isAddOrDelete=false, bool aUpdateRtree=false) override
Mark an item for refresh.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_ITEM * GetItem(const KIID &aID) const
Fetch a SCH_ITEM by ID.
Schematic symbol object.
Definition: sch_symbol.h:81
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const
Return the reference for the given sheet path.
Definition: sch_symbol.cpp:698
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:939
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
void doPopupSelection(wxCommandEvent &event) override
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
SIMULATOR_FRAME * m_parent
SIGNALS_GRID_TRICKS(SIMULATOR_FRAME *aParent, WX_GRID *aGrid)
Handle actions for the various symbol editor and viewers.
Class SIMULATOR_FRAME_BASE.
wxSplitterWindow * m_splitterMeasurements
wxSplitterWindow * m_splitterCursors
SIM_NOTEBOOK * m_plotNotebook
wxSplitterWindow * m_splitterPlotAndConsole
wxSplitterWindow * m_splitterLeftRight
wxSplitterWindow * m_splitterSignals
The SIMULATOR_FRAME holds the main user-interface for running simulations.
SPICE_VALUE_FORMAT GetMeasureFormat(int aRow) const
Get/Set the format of a value in the measurements grid.
void rebuildSignalsList()
Rebuild the list of signals available from the netlist.
void ShowChangedLanguage() override
bool canCloseWindow(wxCloseEvent &aEvent) override
void doAddTrace(const wxString &aName, SIM_TRACE_TYPE aType)
Add a new trace to the current plot.
void onSimFinished(wxCommandEvent &aEvent)
void onSimUpdate(wxCommandEvent &aEvent)
void onPlotClose(wxAuiNotebookEvent &event) override
void onSimReport(wxCommandEvent &aEvent)
wxString GetCurrentSimCommand() const
SIM_PANEL_BASE * NewPlotPanel(const wxString &aSimCommand, int aSimOptions)
Create a new plot tab for a given simulation type.
void AddMeasurement(const wxString &aCmd)
Add a measurement to the measurements grid.
SIM_TRACE_TYPE getXAxisType(SIM_TYPE aType) const
Return X axis for a given simulation type.
void initWorkbook()
Load the currently active workbook stored in the project settings.
std::shared_ptr< NGSPICE_CIRCUIT_MODEL > m_circuitModel
void onExit(wxCommandEvent &event)
std::vector< wxString > m_signals
std::shared_ptr< SPICE_SIMULATOR > m_simulator
SPICE_VALUE_FORMAT GetCursorFormat(int aCursorId, int aValueCol) const
Get/Set the number of significant digits and the range for formatting a cursor value.
void UpdateMeasurement(int aRow)
Update a measurement in the measurements grid.
void setupUIConditions() override
Setup the UI conditions for the various actions and their controls in this frame.
void SaveSettings(APP_SETTINGS_BASE *aCfg) override
Save common frame parameters to a configuration data file.
std::map< int, wxString > m_userDefinedSignals
void updateSignalsGrid()
Update the values in the signals grid.
void OnFilterMouseMoved(wxMouseEvent &aEvent) override
void AddCurrentTrace(const wxString &aDeviceName)
Add a current trace for a given device to the current plot.
void onPlotClosed(wxAuiNotebookEvent &event) override
int m_splitterPlotAndConsoleSashPosition
void OnModify() override
Must be called after a model change in order to set the "modify" flag and do other frame-specific pro...
bool SaveWorkbook(const wxString &aPath)
Save plot, signal, cursor, measurement, etc.
void updateCursors()
Update the cursor values (in the grid) and graphics (in the plot window).
std::list< TUNER_SLIDER * > m_tuners
SPICE expressions need quoted versions of the netnames since KiCad allows '-' and '/' in netnames.
std::map< wxString, wxString > m_quotedNetnames
Panel that was used as the most recent one for simulations.
void onSignalsGridCellChanged(wxGridEvent &aEvent) override
void doCloseWindow() override
void RemoveTuner(TUNER_SLIDER *aTuner)
Remove an existing tuner.
void AddVoltageTrace(const wxString &aNetName)
Add a voltage trace for a given net to the current plot.
void onCursorUpdate(wxCommandEvent &aEvent)
void onPlotChanged(wxAuiNotebookEvent &event) override
void ToggleDarkModePlots()
Toggle dark-mode of the plot tabs.
SIM_THREAD_REPORTER * m_reporter
void AddTuner(const SCH_SHEET_PATH &aSheetPath, SCH_SYMBOL *aSymbol)
Add a tuner for a symbol.
bool LoadSimulator()
Check and load the current netlist into the simulator.
void onSimStarted(wxCommandEvent &aEvent)
SIM_PANEL_BASE * getCurrentPlotWindow() const
Return the currently opened plot panel (or NULL if there is none).
void LoadSettings(APP_SETTINGS_BASE *aCfg) override
Load common frame parameters from a configuration file.
SIM_PLOT_PANEL * GetCurrentPlot() const
Return the current tab (or NULL if there is none).
wxString vectorNameFromSignalName(const wxString &aSignalName, int *aTraceType)
Get the simulator output vector name for a given signal name and type.
void onMeasurementsGridCellChanged(wxGridEvent &aEvent) override
int m_splitterSignalsSashPosition
void updateTitle()
Set the main window title bar text.
void parseTraceParams(SIM_PLOT_PANEL *aPlotPanel, TRACE *aTrace, const wxString &aSignalName, const wxString &aParams)
void DeleteMeasurement(int aRow)
Delete a row from the measurements grid.
void onNotebookModified(wxCommandEvent &event)
const NGSPICE_CIRCUIT_MODEL * GetExporter() const
Return the netlist exporter object used for simulations.
int GetCurrentOptions() const
bool LoadWorkbook(const wxString &aPath)
Load plot, signal, cursor, measurement, etc.
void SetMeasureFormat(int aRow, const SPICE_VALUE_FORMAT &aFormat)
void applyUserDefinedSignals()
Apply user-defined signals to the SPICE session.
WINDOW_SETTINGS * GetWindowSettings(APP_SETTINGS_BASE *aCfg) override
Return a pointer to the window settings for this frame.
void onCursorsGridCellChanged(wxGridEvent &aEvent) override
void updateTrace(const wxString &aVectorName, int aTraceType, SIM_PLOT_PANEL *aPlotPanel)
Update a trace in a particular SIM_PLOT_PANEL.
bool EditSimCommand()
Shows a dialog for editing the current tab's simulation command, or creating a new tab with a differe...
int m_splitterTuneValuesSashPosition
int m_splitterLeftRightSashPosition
SPICE_VALUE_FORMAT m_cursorFormats[3][2]
void SetUserDefinedSignals(const std::map< int, wxString > &aSignals)
void OnFilterText(wxCommandEvent &aEvent) override
SIM_PANEL_BASE * m_lastSimPlot
void applyTuners()
Apply component values specified using tuner sliders to the current netlist.
void rebuildSignalsGrid(wxString aFilter)
Rebuild the filtered list of signals in the signals grid.
void onPlotDragged(wxAuiNotebookEvent &event) override
int m_splitterCursorsSashPosition
unsigned int m_plotNumber
SCH_EDIT_FRAME * m_schematicFrame
void SetCursorFormat(int aCursorId, int aValueCol, const SPICE_VALUE_FORMAT &aFormat)
void UpdateTunerValue(const SCH_SHEET_PATH &aSheetPath, const KIID &aSymbol, const wxString &aRef, const wxString &aValue)
Safely update a field of the associated symbol without dereferencing the symbol.
static std::shared_ptr< SPICE_SIMULATOR > CreateInstance(const std::string &aName)
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, const std::vector< LIB_PIN * > &aPins)
virtual const PARAM * GetTunerParam() const
Definition: sim_model.h:480
const SPICE_GENERATOR & SpiceGenerator() const
Definition: sim_model.h:430
void SetParamValue(int aParamIndex, const std::string &aValue, SIM_VALUE::NOTATION aNotation=SIM_VALUE::NOTATION::SI)
Definition: sim_model.cpp:862
void WriteFields(std::vector< T > &aFields) const
bool DeleteAllPages() override
bool AddPage(wxWindow *aPage, const wxString &aCaption, bool aSelect=false, const wxBitmap &aBitmap=wxNullBitmap)
static bool IsPlottable(SIM_TYPE aSimType)
SIM_TYPE GetType() const
const wxString & GetSimCommand() const
void SetSimCommand(const wxString &aSimCommand)
void SetSimOptions(int aOptions)
int GetSimOptions() const
static void FillDefaultColorList(bool aWhiteBg)
Fills m_colorList by a default set of colors.
wxPoint GetLegendPosition() const
void UpdateTraceStyle(TRACE *trace)
Update plot colors.
void ResetScales()
Update trace line style.
bool IsGridShown() const
wxString GetLabelY2() const
wxString GetUnitsX() const
TRACE * AddTrace(const wxString &aVectorName, int aType)
wxString GetLabelY3() const
bool DeleteTrace(const wxString &aVectorName, int aTraceType)
bool IsLegendShown() const
TRACE * GetTrace(const wxString &aVecName, int aType) const
wxString GetUnitsY3() const
void SetDottedSecondary(bool aEnable)
Draw secondary signal traces (current or phase) with dotted lines.
bool GetDottedSecondary() const
Toggle cursor for a particular trace.
const std::map< wxString, TRACE * > & GetTraces() const
void SetTraceData(TRACE *aTrace, unsigned int aPoints, const double *aX, const double *aY)
wxString GetUnitsY2() const
mpWindow * GetPlotWin() const
void ShowGrid(bool aEnable)
wxString GetLabelY1() const
wxString GetUnitsY1() const
wxString GetLabelX() const
SIMULATOR_FRAME * m_parent
SIM_THREAD_REPORTER(SIMULATOR_FRAME *aParent)
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
void OnSimStateChange(SPICE_SIMULATOR *aObject, SIM_STATE aNewState) override
bool HasMessage() const override
Returns true if the reporter client is non-empty.
virtual std::string TunerCommand(const SPICE_ITEM &aItem, double aValue) const
Interface to receive simulation updates from SPICE_SIMULATOR class.
wxString GetWorkbookFilename() const
Helper class to recognize Spice formatted values.
Definition: spice_value.h:56
wxString ToString() const
Return string value as when converting double to string (e.g.
wxString ToSpiceString() const
Return string value in Spice format (e.g.
double ToDouble() const
SUPPRESS_GRID_CELL_EVENTS(SIMULATOR_FRAME *aFrame)
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:170
TOOL_DISPATCHER * m_toolDispatcher
Definition: tools_holder.h:172
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
virtual void DispatchWxEvent(wxEvent &aEvent)
Process wxEvents (mostly UI events), translate them to TOOL_EVENTs, and make tools handle those.
Master controller class:
Definition: tool_manager.h:55
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
ACTION_MANAGER * GetActionManager() const
Definition: tool_manager.h:196
void RegisterTool(TOOL_BASE *aTool)
Add a tool to the manager set and sets it up.
void SetEnvironment(EDA_ITEM *aModel, KIGFX::VIEW *aView, KIGFX::VIEW_CONTROLS *aViewControls, APP_SETTINGS_BASE *aSettings, TOOLS_HOLDER *aFrame)
Set the work environment (model, view, view controls and the parent window).
void InitTools()
Initializes all registered tools.
void SetTraceColour(const wxColour &aColour)
void SetCursor(int aCursorId, CURSOR *aCursor)
wxColour GetTraceColour() const
Custom widget to handle quick component values modification and simulation on the fly.
Definition: tuner_slider.h:44
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:147
A wrapper for reporting to a wxString object.
Definition: reporter.h:164
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:69
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: reporter.cpp:59
const wxString & GetName() const
Get layer name.
Definition: mathplot.h:242
const wxPen & GetPen() const
Get pen set for this layer.
Definition: mathplot.h:257
void UpdateAll()
Refresh display.
Definition: mathplot.cpp:2468
void EnableMouseWheelPan(bool enabled)
Enable/disable trackpad friendly panning (2-axis scroll wheel)
Definition: mathplot.h:1233
bool AddLayer(mpLayer *layer, bool refreshDisplay=true)
Add a plot layer to the canvas.
Definition: mathplot.cpp:2314
void Fit() override
Set view to fit global bounding box of all plot layers and refresh display.
Definition: mathplot.cpp:1968
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:363
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition: confirm.cpp:243
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:308
This file is part of the common library.
#define CHECK(x)
#define ENABLE(x)
#define _(s)
Abstract pattern-matching tool and implementations.
@ CTX_SIGNAL
@ FRAME_SCH
Definition: frame_type.h:34
@ GRIDTRICKS_FIRST_CLIENT_ID
Definition: grid_tricks.h:48
const std::string WorkbookFileExtension
KIWAY Kiway
wxFont GetStatusFont(wxWindow *aWindow)
Definition: ui_common.cpp:132
see class PGM_BASE
SEVERITY
@ RPT_SEVERITY_UNDEFINED
@ GLOBAL_CLEANUP
SIM_TRACE_TYPE
Definition: sim_types.h:47
@ SPT_TIME
Definition: sim_types.h:57
@ SPT_AC_PHASE
Definition: sim_types.h:51
@ SPT_SWEEP
Definition: sim_types.h:60
@ SPT_UNKNOWN
Definition: sim_types.h:63
@ SPT_Y_AXIS_MASK
Definition: sim_types.h:54
@ SPT_VOLTAGE
Definition: sim_types.h:49
@ SPT_POWER
Definition: sim_types.h:53
@ SPT_CURRENT
Definition: sim_types.h:50
@ SPT_AC_MAG
Definition: sim_types.h:52
@ SPT_LIN_FREQUENCY
Definition: sim_types.h:58
SIM_TYPE
< Possible simulation types
Definition: sim_types.h:32
@ ST_TRANSIENT
Definition: sim_types.h:42
@ ST_UNKNOWN
Definition: sim_types.h:33
@ ST_NOISE
Definition: sim_types.h:37
@ ST_AC
Definition: sim_types.h:34
@ ST_DC
Definition: sim_types.h:35
@ ST_OP
Definition: sim_types.h:38
#define DISPLAY_LOAD_ERROR(fmt)
wxDEFINE_EVENT(EVT_SIM_UPDATE, wxCommandEvent)
wxString vectorNameFromSignalId(int aUserDefinedSignalId)
MEASUREMENTS_GIRD_COLUMNS
@ COL_MEASUREMENT_FORMAT
@ COL_MEASUREMENT_VALUE
@ COL_MEASUREMENT
CURSORS_GRID_COLUMNS
@ COL_CURSOR_NAME
@ COL_CURSOR_SIGNAL
@ COL_CURSOR_X
@ COL_CURSOR_Y
SIGNALS_GRID_COLUMNS
@ COL_SIGNAL_SHOW
@ COL_SIGNAL_NAME
@ COL_CURSOR_1
@ COL_SIGNAL_COLOR
@ COL_CURSOR_2
@ MYID_MEASURE_INTEGRAL
@ MYID_MEASURE_MAX_AT
@ MYID_MEASURE_AVG
@ MYID_MEASURE_MAX
@ MYID_FORMAT_VALUE
@ MYID_MEASURE_RMS
@ MYID_DELETE_MEASUREMENT
@ MYID_MEASURE_MIN
@ MYID_MEASURE_MIN_AT
@ MYID_MEASURE_PP
SIM_TRACE_TYPE operator|(SIM_TRACE_TYPE aFirst, SIM_TRACE_TYPE aSecond)
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:115
SIM_STATE
@ SIM_IDLE
@ SIM_RUNNING
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LINE
Definition: string_utils.h:60
const INFO & info
Definition: sim_model.h:392
const SIM_MODEL * model
A SPICE_VALUE_FORMAT holds precision and range info for formatting values.Helper class to handle Spic...
Definition: spice_value.h:43
wxString ToString() const
Definition: spice_value.cpp:50
void UpdateUnits(const wxString &aUnits)
Definition: spice_value.cpp:56
void FromString(const wxString &aString)
Definition: spice_value.cpp:41
Stores the common settings that are saved and loaded for each window / frame.
Definition: app_settings.h:92
VECTOR3I res
Definition of file extensions used in Kicad.