You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

904 lines
24 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 CERN
  5. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  6. * @author Maciej Suminski <maciej.suminski@cern.ch>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 3
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * https://www.gnu.org/licenses/gpl-3.0.html
  21. * or you may search the http://www.gnu.org website for the version 3 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <schframe.h>
  26. #include <eeschema_id.h>
  27. #include <kiway.h>
  28. #include <confirm.h>
  29. #include <bitmaps.h>
  30. #include <widgets/tuner_slider.h>
  31. #include <dialogs/dialog_signal_list.h>
  32. #include "netlist_exporter_pspice_sim.h"
  33. #include "sim_plot_frame.h"
  34. #include "sim_plot_panel.h"
  35. #include "spice_simulator.h"
  36. #include "spice_reporter.h"
  37. SIM_PLOT_TYPE operator|( SIM_PLOT_TYPE aFirst, SIM_PLOT_TYPE aSecond )
  38. {
  39. int res = (int) aFirst | (int) aSecond;
  40. return (SIM_PLOT_TYPE) res;
  41. }
  42. class SIM_THREAD_REPORTER : public SPICE_REPORTER
  43. {
  44. public:
  45. SIM_THREAD_REPORTER( SIM_PLOT_FRAME* aParent )
  46. : m_parent( aParent )
  47. {
  48. }
  49. REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_UNDEFINED ) override
  50. {
  51. wxCommandEvent* event = new wxCommandEvent( EVT_SIM_REPORT );
  52. event->SetString( aText );
  53. wxQueueEvent( m_parent, event );
  54. return *this;
  55. }
  56. void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) override
  57. {
  58. wxCommandEvent* event = NULL;
  59. switch( aNewState )
  60. {
  61. case SIM_IDLE:
  62. event = new wxCommandEvent( EVT_SIM_FINISHED );
  63. break;
  64. case SIM_RUNNING:
  65. event = new wxCommandEvent( EVT_SIM_STARTED );
  66. break;
  67. }
  68. wxQueueEvent( m_parent, event );
  69. }
  70. private:
  71. SIM_PLOT_FRAME* m_parent;
  72. };
  73. TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName,
  74. SIM_PLOT_TYPE aType, const wxString& aParam )
  75. : m_name( aName ), m_type( aType ), m_param( aParam )
  76. {
  77. // Spice vector generation
  78. m_spiceVector = aExporter.GetSpiceVector( aName, aType, aParam );
  79. // Title generation
  80. m_title = wxString::Format( "%s(%s)", aParam, aName );
  81. if( aType & SPT_AC_MAG )
  82. m_title += " (mag)";
  83. else if( aType & SPT_AC_PHASE )
  84. m_title += " (phase)";
  85. }
  86. SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
  87. : SIM_PLOT_FRAME_BASE( aParent ), m_settingsDlg( this )
  88. {
  89. SetKiway( this, aKiway );
  90. m_schematicFrame = (SCH_EDIT_FRAME*) Kiway().Player( FRAME_SCH, false );
  91. if( m_schematicFrame == NULL )
  92. throw std::runtime_error( "There is no schematic window" );
  93. updateNetlistExporter();
  94. Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SIM_PLOT_FRAME::onClose ), NULL, this );
  95. Connect( EVT_SIM_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onSimUpdate ), NULL, this );
  96. Connect( EVT_SIM_REPORT, wxCommandEventHandler( SIM_PLOT_FRAME::onSimReport ), NULL, this );
  97. Connect( EVT_SIM_STARTED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimStarted ), NULL, this );
  98. Connect( EVT_SIM_FINISHED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimFinished ), NULL, this );
  99. Connect( EVT_SIM_CURSOR_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onCursorUpdate ), NULL, this );
  100. m_toolSimulate = m_toolBar->AddTool( ID_SIM_RUN, _("Run/Stop Simulation"), KiBitmap( sim_run_xpm ), _("Run Simulation"), wxITEM_NORMAL );
  101. m_toolAddSignals = m_toolBar->AddTool( ID_SIM_ADD_SIGNALS, _("Add Signals"), KiBitmap( sim_add_signal_xpm ), _("Add signals to plot"), wxITEM_NORMAL );
  102. m_toolProbe = m_toolBar->AddTool( ID_SIM_PROBE, _("Probe"), KiBitmap( sim_probe_xpm ),_("Probe signals on the schematic"), wxITEM_NORMAL );
  103. m_toolTune = m_toolBar->AddTool( ID_SIM_TUNE, _("Tune"), KiBitmap( sim_tune_xpm ), _("Tune component values"), wxITEM_NORMAL );
  104. m_toolSettings = m_toolBar->AddTool( wxID_ANY, _("Settings"), KiBitmap( sim_settings_xpm ), _("Simulation settings"), wxITEM_NORMAL );
  105. Connect( m_toolSimulate->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimulate ), NULL, this );
  106. Connect( m_toolAddSignals->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( SIM_PLOT_FRAME::onAddSignal ), NULL, this );
  107. Connect( m_toolProbe->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( SIM_PLOT_FRAME::onProbe ), NULL, this );
  108. Connect( m_toolTune->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( SIM_PLOT_FRAME::onTune ), NULL, this );
  109. Connect( m_toolSettings->GetId(), wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( SIM_PLOT_FRAME::onSettings ), NULL, this );
  110. m_toolBar->Realize();
  111. m_plotNotebook->SetPageText(0, _("Welcome!") );
  112. }
  113. SIM_PLOT_FRAME::~SIM_PLOT_FRAME()
  114. {
  115. }
  116. void SIM_PLOT_FRAME::StartSimulation()
  117. {
  118. STRING_FORMATTER formatter;
  119. m_simConsole->Clear();
  120. updateNetlistExporter();
  121. m_exporter->SetSimCommand( m_settingsDlg.GetSimCommand() );
  122. if( !m_exporter->Format( &formatter, m_settingsDlg.GetNetlistOptions() ) )
  123. {
  124. DisplayError( this, wxT( "There were errors during netlist export, aborted." ) );
  125. return;
  126. }
  127. if( m_exporter->GetSimType() == ST_UNKNOWN )
  128. {
  129. DisplayInfoMessage( this, wxT( "You need to select the simulation settings first." ) );
  130. return;
  131. }
  132. /// @todo is it necessary to recreate simulator every time?
  133. m_simulator.reset( SPICE_SIMULATOR::CreateInstance( "ngspice" ) );
  134. if( !m_simulator )
  135. return;
  136. m_simulator->SetReporter( new SIM_THREAD_REPORTER( this ) );
  137. m_simulator->Init();
  138. m_simulator->LoadNetlist( formatter.GetString() );
  139. updateTuners();
  140. applyTuners();
  141. m_simulator->Run();
  142. Layout();
  143. }
  144. void SIM_PLOT_FRAME::StopSimulation()
  145. {
  146. if( m_simulator )
  147. m_simulator->Stop();
  148. }
  149. bool SIM_PLOT_FRAME::IsSimulationRunning()
  150. {
  151. return m_simulator ? m_simulator->IsRunning() : false;
  152. }
  153. SIM_PLOT_PANEL* SIM_PLOT_FRAME::NewPlotPanel( SIM_TYPE aSimType )
  154. {
  155. SIM_PLOT_PANEL* plot = new SIM_PLOT_PANEL( aSimType, m_plotNotebook, wxID_ANY );
  156. if( m_welcomePanel )
  157. {
  158. m_plotNotebook->DeletePage( 0 );
  159. m_welcomePanel = nullptr;
  160. }
  161. m_plotNotebook->AddPage( plot, wxString::Format( wxT( "Plot%u" ),
  162. (unsigned int) m_plotNotebook->GetPageCount() + 1 ), true );
  163. return plot;
  164. }
  165. void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName )
  166. {
  167. addPlot( aNetName, SPT_VOLTAGE, "V" );
  168. }
  169. void SIM_PLOT_FRAME::AddCurrentPlot( const wxString& aDeviceName, const wxString& aParam )
  170. {
  171. addPlot( aDeviceName, SPT_CURRENT, aParam );
  172. }
  173. void SIM_PLOT_FRAME::AddTuner( SCH_COMPONENT* aComponent )
  174. {
  175. SIM_PLOT_PANEL* plotPanel = CurrentPlot();
  176. if( !plotPanel )
  177. return;
  178. // For now limit the tuner tool to RLC components
  179. char primitiveType = NETLIST_EXPORTER_PSPICE::GetSpiceField( SF_PRIMITIVE, aComponent, 0 )[0];
  180. if( primitiveType != SP_RESISTOR && primitiveType != SP_CAPACITOR && primitiveType != SP_INDUCTOR )
  181. return;
  182. const wxString& componentName = aComponent->GetField( REFERENCE )->GetText();
  183. // Do not add multiple instances for the same component
  184. auto tunerIt = std::find_if( m_tuners.begin(), m_tuners.end(), [&]( const TUNER_SLIDER* t )
  185. {
  186. return t->GetComponentName() == componentName;
  187. }
  188. );
  189. if( tunerIt != m_tuners.end() )
  190. return; // We already have it
  191. try
  192. {
  193. TUNER_SLIDER* tuner = new TUNER_SLIDER( this, m_sidePanel, aComponent );
  194. m_tuneSizer->Add( tuner );
  195. m_tuners.push_back( tuner );
  196. m_sidePanel->Layout();
  197. }
  198. catch( ... )
  199. {
  200. // Sorry, no bonus
  201. }
  202. }
  203. void SIM_PLOT_FRAME::RemoveTuner( TUNER_SLIDER* aTuner, bool aErase )
  204. {
  205. if( aErase )
  206. m_tuners.remove( aTuner );
  207. aTuner->Destroy();
  208. m_sidePanel->Layout();
  209. }
  210. SIM_PLOT_PANEL* SIM_PLOT_FRAME::CurrentPlot() const
  211. {
  212. return static_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetCurrentPage() );
  213. }
  214. void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam )
  215. {
  216. SIM_TYPE simType = m_exporter->GetSimType();
  217. if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
  218. return; // TODO else write out in console?
  219. // Create a new plot if the current one displays a different type
  220. SIM_PLOT_PANEL* plotPanel = CurrentPlot();
  221. if( !plotPanel || plotPanel->GetType() != simType )
  222. plotPanel = NewPlotPanel( simType );
  223. TRACE_DESC descriptor( *m_exporter, aName, aType, aParam );
  224. bool updated = false;
  225. SIM_PLOT_TYPE xAxisType = GetXAxisType( simType );
  226. if( xAxisType == SPT_LIN_FREQUENCY || xAxisType == SPT_LOG_FREQUENCY )
  227. {
  228. // Add two plots: magnitude & phase
  229. TRACE_DESC mag_desc( *m_exporter, descriptor, descriptor.GetType() | SPT_AC_MAG );
  230. TRACE_DESC phase_desc( *m_exporter, descriptor, descriptor.GetType() | SPT_AC_PHASE );
  231. updated |= updatePlot( mag_desc, plotPanel );
  232. updated |= updatePlot( phase_desc, plotPanel );
  233. }
  234. else
  235. {
  236. updated = updatePlot( descriptor, plotPanel );
  237. }
  238. if( updated )
  239. {
  240. updateSignalList();
  241. }
  242. }
  243. void SIM_PLOT_FRAME::removePlot( const wxString& aPlotName, bool aErase )
  244. {
  245. SIM_PLOT_PANEL* plotPanel = CurrentPlot();
  246. if( aErase )
  247. {
  248. auto& traceMap = m_plots[plotPanel].m_traces;
  249. auto traceIt = traceMap.find( aPlotName );
  250. wxASSERT( traceIt != traceMap.end() );
  251. traceMap.erase( traceIt );
  252. }
  253. wxASSERT( plotPanel->IsShown( aPlotName ) );
  254. plotPanel->DeleteTrace( aPlotName );
  255. plotPanel->Fit();
  256. updateSignalList();
  257. updateCursors();
  258. }
  259. void SIM_PLOT_FRAME::updateNetlistExporter()
  260. {
  261. m_exporter.reset( new NETLIST_EXPORTER_PSPICE_SIM( m_schematicFrame->BuildNetListBase(),
  262. Prj().SchLibs(), Prj().SchSearchS() ) );
  263. }
  264. bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* aPanel )
  265. {
  266. if( !m_simulator )
  267. return false;
  268. SIM_TYPE simType = m_exporter->GetSimType();
  269. wxString spiceVector = aDescriptor.GetSpiceVector();
  270. if( !SIM_PLOT_PANEL::IsPlottable( simType ) )
  271. {
  272. // There is no plot to be shown
  273. m_simulator->Command( wxString::Format( "print %s", spiceVector ).ToStdString() );
  274. return false;
  275. }
  276. // First, handle the x axis
  277. wxString xAxisName( m_simulator->GetXAxis( simType ) );
  278. if( xAxisName.IsEmpty() )
  279. return false;
  280. auto data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() );
  281. unsigned int size = data_x.size();
  282. if( data_x.empty() )
  283. return false;
  284. SIM_PLOT_TYPE plotType = aDescriptor.GetType();
  285. std::vector<double> data_y;
  286. // Now, Y axis data
  287. switch( m_exporter->GetSimType() )
  288. {
  289. case ST_AC:
  290. {
  291. wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ),
  292. "Cannot set both AC_PHASE and AC_MAG bits" );
  293. if( plotType & SPT_AC_MAG )
  294. data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
  295. else if( plotType & SPT_AC_PHASE )
  296. data_y = m_simulator->GetPhasePlot( (const char*) spiceVector.c_str() );
  297. else
  298. wxASSERT_MSG( false, "Plot type missing AC_PHASE or AC_MAG bit" );
  299. }
  300. break;
  301. case ST_NOISE:
  302. case ST_DC:
  303. case ST_TRANSIENT:
  304. {
  305. data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
  306. }
  307. break;
  308. default:
  309. wxASSERT_MSG( false, "Unhandled plot type" );
  310. return false;
  311. }
  312. if( data_y.size() != size )
  313. return false;
  314. if( aPanel->AddTrace( aDescriptor.GetTitle(), size,
  315. data_x.data(), data_y.data(), aDescriptor.GetType() ) )
  316. {
  317. m_plots[aPanel].m_traces.insert( std::make_pair( aDescriptor.GetTitle(), aDescriptor ) );
  318. }
  319. return true;
  320. }
  321. void SIM_PLOT_FRAME::updateSignalList()
  322. {
  323. SIM_PLOT_PANEL* plotPanel = CurrentPlot();
  324. if( !plotPanel )
  325. return;
  326. // Fill the signals listbox
  327. m_signals->Clear();
  328. for( const auto& trace : m_plots[plotPanel].m_traces )
  329. m_signals->Append( trace.first );
  330. }
  331. void SIM_PLOT_FRAME::updateCursors()
  332. {
  333. wxQueueEvent( this, new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
  334. }
  335. void SIM_PLOT_FRAME::updateTuners()
  336. {
  337. const auto& spiceItems = m_exporter->GetSpiceItems();
  338. for( auto it = m_tuners.begin(); it != m_tuners.end(); /* iteration inside the loop */ )
  339. {
  340. const wxString& ref = (*it)->GetComponentName();
  341. if( std::find_if( spiceItems.begin(), spiceItems.end(), [&]( const SPICE_ITEM& item )
  342. {
  343. return item.m_refName == ref;
  344. }) == spiceItems.end() )
  345. {
  346. // The component does not exist anymore, remove the associated tuner
  347. TUNER_SLIDER* tuner = *it;
  348. it = m_tuners.erase( it );
  349. RemoveTuner( tuner, false );
  350. }
  351. else
  352. {
  353. ++it;
  354. }
  355. }
  356. }
  357. void SIM_PLOT_FRAME::applyTuners()
  358. {
  359. for( auto& tuner : m_tuners )
  360. {
  361. /// @todo no ngspice hardcoding
  362. std::string command( "alter @" + tuner->GetSpiceName()
  363. + "=" + tuner->GetValue().ToSpiceString() );
  364. m_simulator->Command( command );
  365. }
  366. }
  367. SIM_PLOT_TYPE SIM_PLOT_FRAME::GetXAxisType( SIM_TYPE aType ) const
  368. {
  369. switch( aType )
  370. {
  371. case ST_AC:
  372. return SPT_LIN_FREQUENCY;
  373. /// @todo SPT_LOG_FREQUENCY
  374. case ST_DC:
  375. return SPT_SWEEP;
  376. case ST_TRANSIENT:
  377. return SPT_TIME;
  378. default:
  379. wxASSERT_MSG( false, "Unhandled simulation type" );
  380. return (SIM_PLOT_TYPE) 0;
  381. }
  382. }
  383. void SIM_PLOT_FRAME::menuNewPlot( wxCommandEvent& aEvent )
  384. {
  385. SIM_TYPE type = m_exporter->GetSimType();
  386. if( SIM_PLOT_PANEL::IsPlottable( type ) )
  387. NewPlotPanel( type );
  388. }
  389. void SIM_PLOT_FRAME::menuSaveImage( wxCommandEvent& event )
  390. {
  391. wxFileDialog saveDlg( this, wxT( "Save plot as image" ), "", "",
  392. "PNG file (*.png)|*.png", wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  393. if( saveDlg.ShowModal() == wxID_CANCEL )
  394. return;
  395. CurrentPlot()->SaveScreenshot( saveDlg.GetPath(), wxBITMAP_TYPE_PNG );
  396. }
  397. void SIM_PLOT_FRAME::menuSaveCsv( wxCommandEvent& event )
  398. {
  399. const wxChar SEPARATOR = ';';
  400. wxFileDialog saveDlg( this, wxT( "Save plot data" ), "", "",
  401. "CSV file (*.csv)|*.csv", wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  402. if( saveDlg.ShowModal() == wxID_CANCEL )
  403. return;
  404. wxFile out( saveDlg.GetPath(), wxFile::write );
  405. bool timeWritten = false;
  406. for( const auto& t : CurrentPlot()->GetTraces() )
  407. {
  408. const TRACE* trace = t.second;
  409. if( !timeWritten )
  410. {
  411. out.Write( wxString::Format( "Time%c", SEPARATOR ) );
  412. for( double v : trace->GetDataX() )
  413. out.Write( wxString::Format( "%f%c", v, SEPARATOR ) );
  414. out.Write( "\r\n" );
  415. timeWritten = true;
  416. }
  417. out.Write( wxString::Format( "%s%c", t.first, SEPARATOR ) );
  418. for( double v : trace->GetDataY() )
  419. out.Write( wxString::Format( "%f%c", v, SEPARATOR ) );
  420. out.Write( "\r\n" );
  421. }
  422. out.Close();
  423. }
  424. void SIM_PLOT_FRAME::menuZoomIn( wxCommandEvent& event )
  425. {
  426. CurrentPlot()->ZoomIn();
  427. }
  428. void SIM_PLOT_FRAME::menuZoomOut( wxCommandEvent& event )
  429. {
  430. CurrentPlot()->ZoomOut();
  431. }
  432. void SIM_PLOT_FRAME::menuZoomFit( wxCommandEvent& event )
  433. {
  434. CurrentPlot()->Fit();
  435. }
  436. void SIM_PLOT_FRAME::menuShowGrid( wxCommandEvent& event )
  437. {
  438. SIM_PLOT_PANEL* plot = CurrentPlot();
  439. plot->ShowGrid( !plot->IsGridShown() );
  440. }
  441. void SIM_PLOT_FRAME::menuShowGridUpdate( wxUpdateUIEvent& event )
  442. {
  443. SIM_PLOT_PANEL* plot = CurrentPlot();
  444. event.Check( plot ? plot->IsGridShown() : false );
  445. }
  446. void SIM_PLOT_FRAME::menuShowLegend( wxCommandEvent& event )
  447. {
  448. SIM_PLOT_PANEL* plot = CurrentPlot();
  449. plot->ShowLegend( !plot->IsLegendShown() );
  450. }
  451. void SIM_PLOT_FRAME::menuShowLegendUpdate( wxUpdateUIEvent& event )
  452. {
  453. SIM_PLOT_PANEL* plot = CurrentPlot();
  454. event.Check( plot ? plot->IsLegendShown() : false );
  455. }
  456. #if 0
  457. void SIM_PLOT_FRAME::menuShowCoords( wxCommandEvent& event )
  458. {
  459. SIM_PLOT_PANEL* plot = CurrentPlot();
  460. plot->ShowCoords( !plot->IsCoordsShown() );
  461. }
  462. void SIM_PLOT_FRAME::menuShowCoordsUpdate( wxUpdateUIEvent& event )
  463. {
  464. SIM_PLOT_PANEL* plot = CurrentPlot();
  465. event.Check( plot ? plot->IsCoordsShown() : false );
  466. }
  467. #endif
  468. void SIM_PLOT_FRAME::onPlotClose( wxAuiNotebookEvent& event )
  469. {
  470. int idx = event.GetSelection();
  471. if( idx == wxNOT_FOUND )
  472. return;
  473. SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetPage( idx ) );
  474. if( !plotPanel )
  475. return;
  476. m_plots.erase( plotPanel );
  477. updateSignalList();
  478. updateCursors();
  479. }
  480. void SIM_PLOT_FRAME::onPlotChanged( wxAuiNotebookEvent& event )
  481. {
  482. updateSignalList();
  483. updateCursors();
  484. }
  485. void SIM_PLOT_FRAME::onSignalDblClick( wxCommandEvent& event )
  486. {
  487. // Remove signal from the plot panel when double clicked
  488. int idx = m_signals->GetSelection();
  489. if( idx != wxNOT_FOUND )
  490. removePlot( m_signals->GetString( idx ) );
  491. }
  492. void SIM_PLOT_FRAME::onSignalRClick( wxMouseEvent& event )
  493. {
  494. int idx = m_signals->HitTest( event.GetPosition() );
  495. if( idx != wxNOT_FOUND )
  496. m_signals->SetSelection( idx );
  497. idx = m_signals->GetSelection();
  498. if( idx != wxNOT_FOUND )
  499. {
  500. const wxString& netName = m_signals->GetString( idx );
  501. SIGNAL_CONTEXT_MENU ctxMenu( netName, this );
  502. m_signals->PopupMenu( &ctxMenu );
  503. }
  504. }
  505. void SIM_PLOT_FRAME::onSimulate( wxCommandEvent& event )
  506. {
  507. if( IsSimulationRunning() )
  508. StopSimulation();
  509. else
  510. StartSimulation();
  511. }
  512. void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
  513. {
  514. // Initial processing is required to e.g. display a list of power sources
  515. updateNetlistExporter();
  516. if( !m_exporter->ProcessNetlist( NET_ALL_FLAGS ) )
  517. {
  518. DisplayError( this, wxT( "There were errors during netlist export, aborted." ) );
  519. return;
  520. }
  521. m_settingsDlg.SetNetlistExporter( m_exporter.get() );
  522. m_settingsDlg.ShowModal();
  523. }
  524. void SIM_PLOT_FRAME::onAddSignal( wxCommandEvent& event )
  525. {
  526. SIM_PLOT_PANEL* plotPanel = CurrentPlot();
  527. if( !plotPanel || !m_exporter || plotPanel->GetType() != m_exporter->GetSimType() )
  528. {
  529. DisplayInfoMessage( this, wxT( "You need to run simulation first." ) );
  530. return;
  531. }
  532. DIALOG_SIGNAL_LIST dialog( this, m_exporter.get() );
  533. dialog.ShowModal();
  534. }
  535. void SIM_PLOT_FRAME::onProbe( wxCommandEvent& event )
  536. {
  537. if( m_schematicFrame == NULL )
  538. return;
  539. wxQueueEvent( m_schematicFrame, new wxCommandEvent( wxEVT_TOOL, ID_SIM_PROBE ) );
  540. }
  541. void SIM_PLOT_FRAME::onTune( wxCommandEvent& event )
  542. {
  543. if( m_schematicFrame == NULL )
  544. return;
  545. wxQueueEvent( m_schematicFrame, new wxCommandEvent( wxEVT_TOOL, ID_SIM_TUNE ) );
  546. }
  547. void SIM_PLOT_FRAME::onClose( wxCloseEvent& aEvent )
  548. {
  549. if( IsSimulationRunning() )
  550. m_simulator->Stop();
  551. Destroy();
  552. }
  553. void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
  554. {
  555. wxSize size = m_cursors->GetClientSize();
  556. SIM_PLOT_PANEL* plotPanel = CurrentPlot();
  557. m_cursors->ClearAll();
  558. if( !plotPanel )
  559. return;
  560. const long SIGNAL_COL = m_cursors->AppendColumn( wxT( "Signal" ), wxLIST_FORMAT_LEFT, size.x / 2 );
  561. const long X_COL = m_cursors->AppendColumn( plotPanel->GetLabelX(), wxLIST_FORMAT_LEFT, size.x / 4 );
  562. wxString labelY1 = plotPanel->GetLabelY1();
  563. wxString labelY2 = plotPanel->GetLabelY2();
  564. wxString labelY;
  565. if( !labelY2.IsEmpty() )
  566. labelY = labelY1 + " / " + labelY2;
  567. else
  568. labelY = labelY1;
  569. const long Y_COL = m_cursors->AppendColumn( labelY, wxLIST_FORMAT_LEFT, size.x / 4 );
  570. // Update cursor values
  571. for( const auto& trace : plotPanel->GetTraces() )
  572. {
  573. if( CURSOR* cursor = trace.second->GetCursor() )
  574. {
  575. const wxRealPoint coords = cursor->GetCoords();
  576. long idx = m_cursors->InsertItem( SIGNAL_COL, trace.first );
  577. m_cursors->SetItem( idx, X_COL, SPICE_VALUE( coords.x ).ToSpiceString() );
  578. m_cursors->SetItem( idx, Y_COL, SPICE_VALUE( coords.y ).ToSpiceString() );
  579. }
  580. }
  581. }
  582. void SIM_PLOT_FRAME::onSimStarted( wxCommandEvent& aEvent )
  583. {
  584. m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_stop_xpm ) );
  585. SetCursor( wxCURSOR_ARROWWAIT );
  586. }
  587. void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
  588. {
  589. m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_run_xpm ) );
  590. SetCursor( wxCURSOR_ARROW );
  591. SIM_TYPE simType = m_exporter->GetSimType();
  592. if( simType == ST_UNKNOWN )
  593. return;
  594. SIM_PLOT_PANEL* plotPanel = CurrentPlot();
  595. if( !plotPanel || plotPanel->GetType() != simType )
  596. plotPanel = NewPlotPanel( simType );
  597. // If there are any signals plotted, update them
  598. if( SIM_PLOT_PANEL::IsPlottable( simType ) )
  599. {
  600. TRACE_MAP& traceMap = m_plots[plotPanel].m_traces;
  601. for( auto it = traceMap.begin(); it != traceMap.end(); /* iteration occurs in the loop */)
  602. {
  603. if( !updatePlot( it->second, plotPanel ) )
  604. {
  605. removePlot( it->first, false );
  606. it = traceMap.erase( it ); // remove a plot that does not exist anymore
  607. }
  608. else
  609. {
  610. ++it;
  611. }
  612. }
  613. updateSignalList();
  614. plotPanel->UpdateAll();
  615. plotPanel->ResetScales();
  616. }
  617. else
  618. {
  619. /// @todo do not make it hardcoded for ngspice
  620. for( const auto& net : m_exporter->GetNetIndexMap() )
  621. {
  622. int node = net.second;
  623. if( node > 0 )
  624. m_simulator->Command( wxString::Format( "print v(%d)", node ).ToStdString() );
  625. }
  626. }
  627. }
  628. void SIM_PLOT_FRAME::onSimUpdate( wxCommandEvent& aEvent )
  629. {
  630. if( !m_simulator )
  631. return;
  632. if( IsSimulationRunning() )
  633. StopSimulation();
  634. m_simConsole->Clear();
  635. // Do not export netlist, it is already stored in the simulator
  636. applyTuners();
  637. m_simulator->Run();
  638. }
  639. void SIM_PLOT_FRAME::onSimReport( wxCommandEvent& aEvent )
  640. {
  641. std::cout << aEvent.GetString() << std::endl;
  642. m_simConsole->AppendText( aEvent.GetString() + "\n" );
  643. m_simConsole->SetInsertionPointEnd();
  644. }
  645. SIM_PLOT_FRAME::SIGNAL_CONTEXT_MENU::SIGNAL_CONTEXT_MENU( const wxString& aSignal,
  646. SIM_PLOT_FRAME* aPlotFrame )
  647. : m_signal( aSignal ), m_plotFrame( aPlotFrame )
  648. {
  649. SIM_PLOT_PANEL* plot = m_plotFrame->CurrentPlot();
  650. Append( HIDE_SIGNAL, wxT( "Hide signal" ) );
  651. TRACE* trace = plot->GetTrace( m_signal );
  652. if( trace->HasCursor() )
  653. Append( HIDE_CURSOR, wxT( "Hide cursor" ) );
  654. else
  655. Append( SHOW_CURSOR, wxT( "Show cursor" ) );
  656. Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ), NULL, this );
  657. }
  658. void SIM_PLOT_FRAME::SIGNAL_CONTEXT_MENU::onMenuEvent( wxMenuEvent& aEvent )
  659. {
  660. SIM_PLOT_PANEL* plot = m_plotFrame->CurrentPlot();
  661. switch( aEvent.GetId() )
  662. {
  663. case HIDE_SIGNAL:
  664. m_plotFrame->removePlot( m_signal );
  665. break;
  666. case SHOW_CURSOR:
  667. plot->EnableCursor( m_signal, true );
  668. break;
  669. case HIDE_CURSOR:
  670. plot->EnableCursor( m_signal, false );
  671. break;
  672. }
  673. }
  674. wxDEFINE_EVENT( EVT_SIM_UPDATE, wxCommandEvent );
  675. wxDEFINE_EVENT( EVT_SIM_REPORT, wxCommandEvent );
  676. wxDEFINE_EVENT( EVT_SIM_STARTED, wxCommandEvent );
  677. wxDEFINE_EVENT( EVT_SIM_FINISHED, wxCommandEvent );