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