KiCad PCB EDA Suite
Loading...
Searching...
No Matches
tuner_slider.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2016 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
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
28#include <ki_exception.h>
30#include <sim/simulator_frame.h>
31#include <sch_symbol.h>
32#include <template_fieldnames.h>
35
36#include <algorithm>
37#include <cmath> // log log1p expm1
38#include <complex> // norm
39
40// Must be after other includes to avoid conflict with a window header on msys2
41#include "tuner_slider.h"
42#include "core/kicad_algo.h"
43
45 const SCH_SHEET_PATH& aSheetPath, SCH_SYMBOL* aSymbol ) :
46 TUNER_SLIDER_BASE( aParent ),
47 m_symbol( aSymbol->m_Uuid ),
48 m_sheetPath( aSheetPath ),
49 m_ref( aSymbol->GetRef( &aSheetPath ) ),
50 m_min( 0.0 ),
51 m_max( 0.0 ),
52 m_value( 0.0 ),
54 m_frame( aFrame )
55{
56#if _WIN32
57 // BORDER_RAISED/SUNKEN look pretty on every platform but Windows
58 long style = GetWindowStyleFlag();
59 style &= ~wxBORDER_MASK;
60 style |= wxBORDER_SIMPLE;
61 SetWindowStyleFlag( style );
62#endif // _WIN32
63
64 const SPICE_ITEM* item = m_frame->GetExporter()->FindItem( m_ref );
65
66 if( !item )
67 throw KI_PARAM_ERROR( wxString::Format( _( "%s not found" ), m_ref ) );
68
69 m_name->SetLabel( wxString::Format( _( "Tune %s" ), m_ref ) );
71
72 m_e24->SetBitmap( KiBitmapBundle( BITMAPS::e_24 ) );
73 m_e24->SetIsCheckButton();
74 m_separator->SetIsSeparator();
75 m_e48->SetBitmap( KiBitmapBundle( BITMAPS::e_48 ) );
76 m_e48->SetIsCheckButton();
77 m_e96->SetBitmap( KiBitmapBundle( BITMAPS::e_96 ) );
78 m_e96->SetIsCheckButton();
79 m_e192->SetBitmap( KiBitmapBundle( BITMAPS::e_192 ) );
80 m_e192->SetIsCheckButton();
81
82 m_modeChoice->SetSelection( 0 );
83 m_stepCount->SetRange( 2, 100 );
84 m_stepCount->SetValue( 5 );
86
87 const SIM_MODEL::PARAM* tunerParam = item->model->GetTunerParam();
88
89 if( !tunerParam )
90 {
91 throw KI_PARAM_ERROR( wxString::Format( _( "%s has simulation model of type '%s %s'.\n\n"
92 "Only passive R, L, C models and voltage and "
93 "current sources may be tuned." ),
94 m_ref,
95 item->model->GetDeviceInfo().fieldValue,
96 item->model->GetTypeInfo().fieldValue ) );
97 }
98
99 // Special case for potentiometers because we don't have value ranges implemented yet.
100 if( item->model->GetType() == SIM_MODEL::TYPE::R_POT )
101 {
102 std::string valueStr = SIM_VALUE::ToSpice( item->model->GetTunerParam()->value );
103
104 if( valueStr != "" )
105 m_value = SPICE_VALUE( valueStr );
106 else
107 m_value = SPICE_VALUE( "0.5" );
108
109 m_min = SPICE_VALUE( 0 );
110 m_max = SPICE_VALUE( 1 );
111 }
112 else
113 {
115 m_min = SPICE_VALUE( 0.5 ) * m_value;
116 m_max = SPICE_VALUE( 2.0 ) * m_value;
117 }
118
119 m_minText->SetValue( m_min.ToOrigString() );
120 m_maxText->SetValue( m_max.ToOrigString() );
121
123 updateSlider();
124
125 Layout();
126}
127
128
130{
131 return std::max( 2, m_stepCount->GetValue() );
132}
133
134
136{
137 m_name->SetLabel( wxString::Format( _( "Tune %s" ), m_ref ) );
138
139 wxArrayString modeChoices;
140 modeChoices.Add( _( "Single Run" ) );
141 modeChoices.Add( _( "Multi Run" ) );
142
143 int selection = m_modeChoice->GetSelection();
144
145 m_modeChoice->Set( modeChoices );
146
147 if( selection < 0 || selection >= static_cast<int>( modeChoices.size() ) )
148 selection = 0;
149
150 m_modeChoice->SetSelection( selection );
151
152 m_stepsLabel->SetLabel( _( "Steps" ) );
153
155}
156
157
158void TUNER_SLIDER::onESeries( wxCommandEvent& event )
159{
160 for( BITMAP_BUTTON* btn : { m_e24, m_e48, m_e96, m_e192 } )
161 {
162 if( btn != event.GetEventObject() )
163 btn->Check( false );
164 }
165
166 wxString oldValue = m_valueText->GetValue();
167
169
170 if( m_valueText->GetValue() != oldValue )
172
173 event.Skip();
174}
175
176
177void TUNER_SLIDER::onRunModeChanged( wxCommandEvent& event )
178{
179 int selection = m_modeChoice->GetSelection();
180
181 if( selection == wxNOT_FOUND )
182 selection = 0;
183
184 m_runMode = ( selection == 1 ) ? RUN_MODE::MULTI : RUN_MODE::SINGLE;
185
188
189 event.Skip();
190}
191
192
194{
195 // Get the value into the current range boundaries
196 if( aVal > m_max )
197 m_value = m_max;
198 else if( aVal < m_min )
199 m_value = m_min;
200 else
201 m_value = aVal;
202
204 updateSlider();
206
207 return true;
208}
209
210
212{
213 if( aVal >= m_max )
214 return false;
215
216 m_min = aVal;
217
218 if( m_value < aVal ) // Limit the current value
219 SetValue( aVal );
220
221 m_minText->SetValue( aVal.ToOrigString() );
222 updateSlider();
223
224 return true;
225}
226
227
229{
230 if( aVal <= m_min )
231 return false;
232
233 m_max = aVal;
234
235 if( m_value > aVal ) // Limit the current value
236 SetValue( aVal );
237
238 m_maxText->SetValue( aVal.ToOrigString() );
239 updateSlider();
240
241 return true;
242}
243
244
246{
247 wxQueueEvent( m_frame, new wxCommandEvent( EVT_SIM_UPDATE ) );
248}
249
251{
252 bool enableSteps = ( m_runMode == RUN_MODE::MULTI );
253
254 m_stepCount->Enable( enableSteps );
255 m_stepsLabel->Enable( enableSteps );
256
257 // In Multi Run mode, the middle value text and slider are not directly editable,
258 // and save actions are disabled. Re-enable for Single Run.
259 bool enableDirectControls = ( m_runMode == RUN_MODE::SINGLE );
260 m_valueText->Enable( enableDirectControls );
261 m_slider->Enable( enableDirectControls );
262 m_saveBtn->Enable( enableDirectControls );
263 m_closeBtn->Enable( true );
264}
265
266
268{
269 wxASSERT( m_max >= m_value && m_value >= m_min );
270 double value = ( ( m_value - m_min ) / ( m_max - m_min ) ).ToDouble();
271 m_slider->SetValue( KiROUND( value * 100.0 ) );
272}
273
274
276{
277 static std::vector<double> e24 = { 1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0,
278 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1 };
279
280 static std::vector<double> e192 = { 1.00, 1.01, 1.02, 1.04, 1.05, 1.06, 1.07, 1.09, 1.10, 1.11,
281 1.13, 1.14, 1.15, 1.17, 1.18, 1.20, 1.21, 1.23, 1.24, 1.26,
282 1.27, 1.29, 1.30, 1.32, 1.33, 1.35, 1.37, 1.38, 1.40, 1.42,
283 1.43, 1.45, 1.47, 1.49, 1.50, 1.52, 1.54, 1.56, 1.58, 1.60,
284 1.62, 1.64, 1.65, 1.67, 1.69, 1.72, 1.74, 1.76, 1.78, 1.80,
285 1.82, 1.84, 1.87, 1.89, 1.91, 1.93, 1.96, 1.98, 2.00, 2.03,
286 2.05, 2.08, 2.10, 2.13, 2.15, 2.18, 2.21, 2.23, 2.26, 2.29,
287 2.32, 2.34, 2.37, 2.40, 2.43, 2.46, 2.49, 2.52, 2.55, 2.58,
288 2.61, 2.64, 2.67, 2.71, 2.74, 2.77, 2.80, 2.84, 2.87, 2.91,
289 2.94, 2.98, 3.01, 3.05, 3.09, 3.12, 3.16, 3.20, 3.24, 3.28,
290 3.32, 3.36, 3.40, 3.44, 3.48, 3.52, 3.57, 3.61, 3.65, 3.70,
291 3.74, 3.79, 3.83, 3.88, 3.92, 3.97, 4.02, 4.07, 4.12, 4.17,
292 4.22, 4.27, 4.32, 4.37, 4.42, 4.48, 4.53, 4.59, 4.64, 4.70,
293 4.75, 4.81, 4.87, 4.93, 4.99, 5.05, 5.11, 5.17, 5.23, 5.30,
294 5.36, 5.42, 5.49, 5.56, 5.62, 5.69, 5.76, 5.83, 5.90, 5.97,
295 6.04, 6.12, 6.19, 6.26, 6.34, 6.42, 6.49, 6.57, 6.65, 6.73,
296 6.81, 6.90, 6.98, 7.06, 7.15, 7.23, 7.32, 7.41, 7.50, 7.59,
297 7.68, 7.77, 7.87, 7.96, 8.06, 8.16, 8.25, 8.35, 8.45, 8.56,
298 8.66, 8.76, 8.87, 8.98, 9.09, 9.20, 9.31, 9.42, 9.53, 9.65,
299 9.76, 9.88 };
300
301 int precision = 3;
302 wxString prefix;
303 double value = m_value.ToNormalizedDouble( &prefix );
304
305 bool e_24 = m_e24->IsChecked();
306 bool e_extended = m_e48->IsChecked() || m_e96->IsChecked() || m_e192->IsChecked();
307
308 if( e_24 || e_extended )
309 {
310 std::vector<double> table;
311 table.reserve( 192 + 24 + 1 /* worst case */ );
312
313 if( e_extended )
314 {
315 int step = m_e48->IsChecked() ? 4 : m_e96->IsChecked() ? 2 : 1;
316
317 for( size_t ii = 0; ii < e192.size(); ii += step )
318 table.push_back( e192[ii] );
319 }
320
321 if( e_24 )
322 table.insert( table.end(), e24.begin(), e24.end() );
323
324 table.push_back( 10.0 );
325
326 std::sort( table.begin(), table.end() );
328
329 for( double decade : { 1.0, 10.0, 100.0 } )
330 {
331 for( size_t ii = 0; ii < table.size() - 1; ++ii )
332 {
333 if( value < ( table[ii] + table[ii+1] ) * decade / 2 )
334 {
335 precision = 0;
336
337 if( decade == 1.0 )
338 precision++;
339
340 if( e_extended && decade != 100.0 )
341 precision++;
342
343 m_valueText->SetValue( wxString::Format( wxT( "%.*f%s" ),
344 precision,
345 table[ii] * decade,
346 prefix ) );
347 return;
348 }
349 }
350 }
351 }
352
353 wxString valueStr = wxString::Format( wxT( "%.3f" ), value );
354 SPICE_VALUE::StripZeros( valueStr );
355 m_valueText->SetValue( valueStr + prefix );
356}
357
358
360{
361 try
362 {
363 SPICE_VALUE newMax( m_maxText->GetValue() );
364 SetMax( newMax );
365 // If in Multi Run mode, changing the range should trigger a re-run
368 }
369 catch( const KI_PARAM_ERROR& )
370 {
371 // Restore the previous value
372 m_maxText->SetValue( m_max.ToOrigString() );
373 }
374}
375
376
378{
379 try
380 {
381 SPICE_VALUE newCur( m_valueText->GetValue() );
382 SetValue( newCur );
383 }
384 catch( const KI_PARAM_ERROR& )
385 {
386 // Restore the previous value
387 m_valueText->SetValue( m_value.ToOrigString() );
388 }
389}
390
391
393{
394 try
395 {
396 SPICE_VALUE newMin( m_minText->GetValue() );
397 SetMin( newMin );
398 // If in Multi Run mode, changing the range should trigger a re-run
401 }
402 catch( const KI_PARAM_ERROR& )
403 {
404 // Restore the previous value
405 m_minText->SetValue( m_min.ToOrigString() );
406 }
407}
408
409
410void TUNER_SLIDER::onClose( wxCommandEvent& event )
411{
412 m_frame->RemoveTuner( this );
413}
414
415
416void TUNER_SLIDER::onSave( wxCommandEvent& event )
417{
418 m_frame->UpdateTunerValue( m_sheetPath, m_symbol, GetSymbolRef(), m_valueText->GetValue() );
419}
420
421
422void TUNER_SLIDER::onSliderScroll( wxScrollEvent& event )
423{
424 m_value = m_min + ( m_max - m_min ) * SPICE_VALUE( m_slider->GetValue() / 100.0 );
426}
427
428
429void TUNER_SLIDER::onSliderChanged( wxScrollEvent& event )
430{
431 m_value = m_min + ( m_max - m_min ) * SPICE_VALUE( m_slider->GetValue() / 100.0 );
434}
435
436
437void TUNER_SLIDER::onMaxKillFocus( wxFocusEvent& event )
438{
439 updateMax();
440 event.Skip(); // Mandatory in wxFocusEvent
441}
442
443
444void TUNER_SLIDER::onValueKillFocus( wxFocusEvent& event )
445{
446 updateValue();
447 event.Skip(); // Mandatory in wxFocusEvent
448}
449
450
451void TUNER_SLIDER::onMinKillFocus( wxFocusEvent& event )
452{
453 updateMin();
454 event.Skip(); // Mandatory in wxFocusEvent
455}
456
457
458void TUNER_SLIDER::onMaxTextEnter( wxCommandEvent& event )
459{
460 updateMax();
461 event.Skip(); // Mandatory in wxFocusEvent
462}
463
464
465void TUNER_SLIDER::onValueTextEnter( wxCommandEvent& event )
466{
467 updateValue();
468}
469
470
471void TUNER_SLIDER::onMinTextEnter( wxCommandEvent& event )
472{
473 updateMin();
474}
475
476
477void TUNER_SLIDER::onStepsChanged( wxSpinEvent& event )
478{
480 event.Skip();
481}
482
483
484void TUNER_SLIDER::onStepsTextEnter( wxCommandEvent& event )
485{
486 long steps;
487
488 if( !event.GetString().ToLong( &steps ) )
489 steps = m_stepCount->GetValue();
490
491 if( steps < 2 )
492 steps = 2;
493
494 m_stepCount->SetValue( static_cast<int>( steps ) );
495
497 event.Skip();
498}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
A bitmap button widget that behaves like an AUI toolbar item's button when it is drawn.
Hold a translatable error message and may be used when throwing exceptions containing a translated er...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Schematic symbol object.
Definition sch_symbol.h:75
The SIMULATOR_FRAME_UI holds the main user-interface for running simulations.
virtual const PARAM * GetTunerParam() const
Definition sim_model.h:480
DEVICE_INFO GetDeviceInfo() const
Definition sim_model.h:456
INFO GetTypeInfo() const
Definition sim_model.h:457
TYPE GetType() const
Definition sim_model.h:460
static std::string ToSpice(const std::string &aString)
Helper class to recognize Spice formatted values.
Definition spice_value.h:56
static void StripZeros(wxString &aString)
wxString ToOrigString() const
Return either a normal string or Spice format string, depending on the original value format.
STD_BITMAP_BUTTON * m_closeBtn
BITMAP_BUTTON * m_separator
wxStaticText * m_stepsLabel
BITMAP_BUTTON * m_e96
wxSpinCtrl * m_stepCount
wxTextCtrl * m_valueText
TUNER_SLIDER_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxBORDER_NONE|wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
BITMAP_BUTTON * m_e48
BITMAP_BUTTON * m_e24
BITMAP_BUTTON * m_e192
wxStaticText * m_name
void onESeries(wxCommandEvent &event) override
bool SetMin(const SPICE_VALUE &aVal)
int GetStepCount() const
void onStepsTextEnter(wxCommandEvent &event) override
void onRunModeChanged(wxCommandEvent &event) override
void onValueTextEnter(wxCommandEvent &event) override
void onMinKillFocus(wxFocusEvent &event) override
wxString m_ref
void onValueKillFocus(wxFocusEvent &event) override
bool SetValue(const SPICE_VALUE &aVal)
void onSave(wxCommandEvent &event) override
void onStepsChanged(wxSpinEvent &event) override
TUNER_SLIDER(SIMULATOR_FRAME_UI *aPanel, wxWindow *aParent, const SCH_SHEET_PATH &aSheetPath, SCH_SYMBOL *aSymbol)
wxString GetSymbolRef() const
SPICE_VALUE m_max
void onMaxTextEnter(wxCommandEvent &event) override
void updateComponentValue()
bool SetMax(const SPICE_VALUE &aVal)
void updateValueText()
void onMaxKillFocus(wxFocusEvent &event) override
void onSliderScroll(wxScrollEvent &event) override
SCH_SHEET_PATH m_sheetPath
void onMinTextEnter(wxCommandEvent &event) override
void onClose(wxCommandEvent &event) override
SIMULATOR_FRAME_UI * m_frame
SPICE_VALUE m_value
SPICE_VALUE m_min
RUN_MODE m_runMode
void onSliderChanged(wxScrollEvent &event) override
void ShowChangedLanguage()
void updateModeControls()
#define _(s)
void remove_duplicates(_Container &__c)
Deletes all duplicate values from __c.
Definition kicad_algo.h:161
std::string value
Definition sim_model.h:400
const SIM_MODEL * model