24#include <wx/tokenzr.h>
42 fmt.Printf(
"%%.0%df", nDigits );
52static void getSISuffix(
double x,
const wxString& unit,
int& power, wxString& suffix )
54 const int n_powers = 11;
82 for(
int i = 0; i < n_powers - 1; i++ )
84 double r_cur = pow( 10, powers[i].exponent );
86 if( fabs( x ) >= r_cur && fabs( x ) < r_cur * 1000.0 )
88 power = powers[i].exponent;
90 if( powers[i].suffix )
91 suffix = wxString( powers[i].suffix ) + unit;
103 if( std::isnan( x ) )
106 auto countSignificantDigits =
109 while( k && ( k % 10LL ) == 0LL )
123 int64_t k = (int)( ( x - floor( x ) ) * pow( 10.0, (
double) maxDigits ) );
124 int n = countSignificantDigits( k );
127 n = std::min( n, countSignificantDigits( k + 1 ) );
133template <
typename T_PARENT>
138 T_PARENT(
name, flags, false ),
147 double maxVis = T_PARENT::AbsVisibleMaxValue();
152 int constexpr MAX_DIGITS = 3;
153 int constexpr MAX_DISAMBIGUATION_DIGITS = 6;
154 bool duplicateLabels =
false;
158 double sf = pow( 10.0, power );
165 for(
size_t ii = 0; ii < T_PARENT::m_tickLabels.size(); ++ii )
172 if( ii > 0 && l.
label == T_PARENT::m_tickLabels[ii-1].label )
173 duplicateLabels =
true;
176 while( duplicateLabels && ++digits <= MAX_DISAMBIGUATION_DIGITS );
201 LIN_SCALE::ExtendDataRange( minV, maxV );
231template <
typename T_PARENT>
236 T_PARENT(
name, flags, false ),
247 int constexpr MAX_DIGITS = 3;
252 double sf = pow( 10.0, power );
270 int radius = std::min( plotW, plotH ) / 2 - 15;
275 aView.
center = wxPoint( mL + plotW / 2, mT + plotH / 2 );
279 aView.
plotRect = wxRect( mL, mT, plotW, plotH );
290 wxRealPoint pan = view ? view->
GetSmithPan() : wxRealPoint( 0.0, 0.0 );
306 const wxRect& plotRect = view.
plotRect;
307 int mL = plotRect.x, mT = plotRect.y, plotW = plotRect.width, plotH = plotRect.height;
309 aDC.SetClippingRegion( plotRect );
313 [&](
double aR ) ->
int
319 auto drawClippedCircle =
320 [&](
double aCx,
double aCy,
double aR )
322 constexpr double LIMIT = 1.0002;
324 std::vector<wxPoint> run;
325 int segments = segmentsFor( aR );
327 for(
int ii = 0; ii <= segments; ii++ )
329 double angle = 2.0 *
M_PI * ii / segments;
330 double re = aCx + aR * cos( angle );
331 double im = aCy + aR * sin( angle );
333 if( re * re + im * im <= LIMIT )
335 run.push_back( view.
ToScreen( re, im ) );
340 aDC.DrawLines( (
int) run.size(), run.data() );
347 aDC.DrawLines( (
int) run.size(), run.data() );
351 [](
double aValue ) -> wxString
353 return wxString::Format( wxS(
"%g" ), aValue );
357 double labelScale =
m_z0 > 0.0 ?
m_z0 : 1.0;
359 static const std::vector<double> baseVals = { 0.2, 0.5, 1.0, 2.0, 5.0 };
360 std::vector<double> gridVals = baseVals;
362 constexpr double FINEST_SCALE = 2.0 / ( 1.0 + 20.0 );
364 wxRealPoint lowGamma = view.
ToGamma( wxPoint( mL, mT + plotH ) );
365 wxRealPoint highGamma = view.
ToGamma( wxPoint( mL + plotW, mT ) );
370 return std::hypot( std::max( { lowGamma.x - aRe, aRe - highGamma.x, 0.0 } ),
371 std::max( { lowGamma.y, -highGamma.y, 0.0 } ) );
374 bool zoomedIn = ( plotW / 2.0 ) / view.
radius < 1.0;
375 bool converged = zoomedIn && std::min( poleDistance( 1.0 ), poleDistance( -1.0 ) ) < FINEST_SCALE;
377 if( view.
zoom >= 2.0 && !converged )
378 gridVals.insert( gridVals.end(), { 0.1, 0.3, 0.4, 0.7, 1.5, 3.0, 10.0 } );
380 if( view.
zoom >= 5.0 && !converged )
381 gridVals.insert( gridVals.end(), { 0.05, 0.15, 0.6, 0.8, 1.2, 1.7, 2.5, 4.0, 7.0, 20.0 } );
383 wxPen gridPen =
m_pen;
384 gridPen.SetStyle( wxPENSTYLE_DOT );
386 aDC.SetBrush( *wxTRANSPARENT_BRUSH );
388 aDC.SetTextForeground(
m_pen.GetColour() );
389 aDC.SetPen( gridPen );
392 for(
double r : gridVals )
393 drawClippedCircle( r / ( 1.0 + r ), 0.0, 1.0 / ( 1.0 + r ) );
396 for(
double x : gridVals )
398 drawClippedCircle( 1.0, 1.0 / x, 1.0 / x );
399 drawClippedCircle( 1.0, -1.0 / x, 1.0 / x );
408 constexpr double LABEL_LIMIT = 1.0002;
411 [&](
double aRe,
double aIm, wxPoint& aOut ) ->
bool
413 if( aRe * aRe + aIm * aIm > LABEL_LIMIT )
416 wxPoint p = view.
ToScreen( aRe, aIm );
418 if( !plotRect.Contains( p ) )
427 [&](
double aCx,
double aCy,
double aR, wxPoint& aOut ) ->
bool
429 int bestMargin = std::numeric_limits<int>::max();
431 int segments = segmentsFor( aR );
433 for(
int ii = 0; ii <= segments; ii++ )
435 double angle = 2.0 *
M_PI * ii / segments;
436 double re = aCx + aR * cos( angle );
437 double im = aCy + aR * sin( angle );
439 if( re * re + im * im > LABEL_LIMIT )
442 wxPoint p = view.
ToScreen( re, im );
444 if( !plotRect.Contains( p ) )
447 int margin = std::min( { p.x - mL, mL + plotW - p.x, p.y - mT, mT + plotH - p.y } );
449 if( margin < bestMargin )
461 [&](
const wxPoint& aPos,
const wxSize& aExt ) -> wxPoint
463 return wxPoint( std::clamp( aPos.x, mL + 1, mL + plotW - aExt.x - 1 ),
464 std::clamp( aPos.y, mT + 1, mT + plotH - aExt.y - 1 ) );
467 std::vector<wxRect> placed;
470 [&](
const wxString& aLabel,
const wxPoint& aPos,
const wxSize& aExt )
472 wxPoint pos = clampToPlot( aPos, aExt );
473 wxRect box( pos, aExt );
477 for(
const wxRect& seen : placed )
479 if( seen.Intersects( box ) )
483 placed.push_back( box );
484 aDC.DrawText( aLabel, pos );
488 [&](
const wxString& aLabel,
const wxPoint& aAt )
490 wxSize ext = aDC.GetTextExtent( aLabel );
492 drawLabel( aLabel, wxPoint( aAt.x - ext.x / 2, aAt.y + 3 ), ext );
496 auto drawRimLabel = [&](
const wxString& aLabel,
const wxPoint& aAt,
double aRe,
double aIm )
498 wxSize ext = aDC.GetTextExtent( aLabel );
503 pos.x -=
KiROUND( ext.x * ( 1.0 - aRe ) / 2.0 );
504 pos.y -=
KiROUND( ext.y * ( 1.0 + aIm ) / 2.0 );
506 drawLabel( aLabel, pos, ext );
512 if( anchorPos( -1.0, 0.0, at ) )
513 drawRimLabel( wxS(
"0" ), at, -1.0, 0.0 );
514 else if( edgePos( 0.0, 0.0, 1.0, at ) )
515 drawEdgeLabel( wxS(
"0" ), at );
517 for(
double r : gridVals )
519 if( anchorPos( ( r - 1.0 ) / ( r + 1.0 ), 0.0, at ) || edgePos( r / ( 1.0 + r ), 0.0, 1.0 / ( 1.0 + r ), at ) )
521 drawEdgeLabel( formatValue( r * labelScale ), at );
525 for(
double x : gridVals )
527 double d = x * x + 1.0;
528 double re = ( x * x - 1.0 ) / d;
529 double im = 2.0 * x / d;
530 wxString posLabel = wxS(
"j" ) + formatValue( x * labelScale );
531 wxString negLabel = wxS(
"-j" ) + formatValue( x * labelScale );
533 if( anchorPos( re, im, at ) )
534 drawRimLabel( posLabel, at, re, im );
535 else if( edgePos( 1.0, 1.0 / x, 1.0 / x, at ) )
536 drawEdgeLabel( posLabel, at );
538 if( anchorPos( re, -im, at ) )
539 drawRimLabel( negLabel, at, re, -im );
540 else if( edgePos( 1.0, -1.0 / x, 1.0 / x, at ) )
541 drawEdgeLabel( negLabel, at );
548 note = wxString::Format( wxS(
"Z0 = %s Ω" ), formatValue(
m_z0 ) );
550 note =
_(
"Normalized Z/Z0 (ports differ)" );
552 note =
_(
"Normalized Z/Z0" );
554 wxSize ext = aDC.GetTextExtent( note );
556 aDC.DrawText( note, mL + 4, mT + plotH - ext.y - 4 );
558 aDC.DestroyClippingRegion();
572 const std::vector<double>& xs =
GetDataX();
573 const std::vector<double>& ys =
GetDataY();
574 size_t count = std::min( xs.size(), ys.size() );
580 aDC.SetClippingRegion( view.
plotRect );
584 if(
GetSweepCount() <= 1 || chunk == std::numeric_limits<size_t>::max() || chunk == 0 )
587 std::vector<wxPoint> pts;
594 aDC.DrawLines( (
int) pts.size(), pts.data() );
596 else if( pts.size() == 1 )
598 aDC.SetBrush( wxBrush(
m_pen.GetColour() ) );
599 aDC.DrawCircle( pts[0], 2 );
605 for(
size_t start = 0; start < count; start += chunk )
607 size_t end = std::min( count, start + chunk );
609 for(
size_t ii = start; ii <
end; ii++ )
612 if( !std::isfinite( xs[ii] ) || !std::isfinite( ys[ii] ) )
618 pts.emplace_back( view.
ToScreen( xs[ii], ys[ii] ) );
624 aDC.DestroyClippingRegion();
630 const std::vector<double>& re =
m_trace->GetDataX();
631 const std::vector<double>& im =
m_trace->GetDataY();
632 const std::vector<double>& freqs =
static_cast<SMITH_TRACE*
>(
m_trace )->GetFrequencies();
634 size_t count = std::min( re.size(), im.size() );
639 m_index = std::clamp( aIndex, 0, (
int) count - 1 );
651 const std::vector<double>& freqs =
static_cast<SMITH_TRACE*
>(
m_trace )->GetFrequencies();
652 const std::vector<double>& re =
m_trace->GetDataX();
653 const std::vector<double>& im =
m_trace->GetDataY();
658 size_t end = freqs.size();
659 size_t chunk =
m_trace->GetSweepSize();
661 if(
m_trace->GetSweepCount() > 1
663 && chunk != std::numeric_limits<size_t>::max()
665 && (
size_t)
m_index < freqs.size() )
667 begin = ( (size_t)
m_index / chunk ) * chunk;
668 end = std::min( freqs.size(), begin + chunk );
672 double bestDist = std::numeric_limits<double>::max();
674 for(
size_t ii = begin; ii <
end; ii++ )
676 if( !std::isfinite( freqs[ii] ) )
679 if( ii < re.size() && ii < im.size() && ( !std::isfinite( re[ii] ) || !std::isfinite( im[ii] ) ) )
682 double dist = std::fabs( freqs[ii] - aFreq );
684 if( dist < bestDist )
769 const std::vector<double>& re =
m_trace->GetDataX();
770 const std::vector<double>& im =
m_trace->GetDataY();
771 size_t count = std::min( re.size(), im.size() );
790 double bestDist = std::numeric_limits<double>::max();
792 for(
size_t ii = 0; ii < count; ii++ )
794 if( !std::isfinite( re[ii] ) || !std::isfinite( im[ii] ) )
797 wxPoint p = view.
ToScreen( re[ii], im[ii] );
798 double dx = (double) p.x -
m_dim.x;
799 double dy = (double) p.y -
m_dim.y;
800 double dist = dx * dx + dy * dy;
802 if( dist < bestDist )
827 wxQueueEvent( aWindow.GetParent(),
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
842 m_dim.SetX( marker.x );
843 m_dim.SetY( marker.y );
852 wxColour fg = aWindow.GetForegroundColour();
855 pen.SetColour( cursorColor.
ToColour() );
856 pen.SetStyle( wxPENSTYLE_SOLID );
858 aDC.SetBrush( *wxTRANSPARENT_BRUSH );
860 aDC.DrawCircle( marker, 4 );
861 aDC.DrawLine( marker.x - 8, marker.y, marker.x - 4, marker.y );
862 aDC.DrawLine( marker.x + 4, marker.y, marker.x + 8, marker.y );
863 aDC.DrawLine( marker.x, marker.y - 8, marker.x, marker.y - 4 );
864 aDC.DrawLine( marker.x, marker.y + 4, marker.x, marker.y + 8 );
871 auto formatSI = [](
double aValue,
const wxString& aUnit ) -> wxString
873 if( std::isnan( aValue ) )
881 double sf = pow( 10.0, power );
883 return formatFloat( aValue / sf, 3 ) + wxS(
" " ) + suffix;
886 std::vector<wxString> lines;
888 lines.push_back(
getID() + wxS(
": f = " ) + formatSI( freq, wxS(
"Hz" ) ) );
891 bool absolute = z0 > 0.0;
895 lines.push_back( wxS(
"Z = inf" ) );
899 lines.push_back( wxString::Format( wxS(
"Z = %s %s j%s" ),
900 formatSI( zr, wxS(
"Ω" ) ),
901 zi < 0 ? wxS(
"-" ) : wxS(
"+" ),
902 formatSI( std::fabs( zi ), wxS(
"Ω" ) ) ) );
905 if( std::isfinite( freq ) && freq > 0.0 && zi != 0.0 )
915 lines.push_back( wxString::Format( wxS(
"z = %s %s j%s" ),
917 zi < 0 ? wxS(
"-" ) : wxS(
"+" ),
924 if( std::isfinite( rl ) )
925 lines.push_back( wxString::Format( wxS(
"RL = %s dB" ),
formatFloat( rl, 1 ) ) );
927 lines.push_back( wxS(
"RL = inf" ) );
929 if( std::isfinite( vswr ) )
930 lines.push_back( wxString::Format( wxS(
"VSWR = %s" ),
formatFloat( vswr, 2 ) ) );
932 lines.push_back( wxS(
"VSWR = inf" ) );
938 int lineH = aDC.GetTextExtent( wxS(
"M" ) ).y;
940 for(
const wxString& line : lines )
941 boxW = std::max( boxW, aDC.GetTextExtent( line ).x );
944 boxH = (int) lines.size() * lineH + 6;
946 wxPoint boxPos( marker.x + ( marker.x < aWindow.
GetScrX() / 2 ? 12 : -12 - boxW ),
947 marker.y + ( marker.y < aWindow.
GetScrY() / 2 ? 12 : -12 - boxH ) );
949 boxPos.x = std::clamp( boxPos.x, 0, std::max( 0, aWindow.
GetScrX() - boxW ) );
950 boxPos.y = std::clamp( boxPos.y, 0, std::max( 0, aWindow.
GetScrY() - boxH ) );
952 wxBrush labelBrush( aWindow.GetBackgroundColour() );
954 aDC.SetBrush( labelBrush );
955 aDC.DrawRectangle( wxRect( boxPos, wxSize( boxW, boxH ) ) );
956 aDC.SetTextForeground( cursorColor.
ToColour() );
958 for(
size_t ii = 0; ii < lines.size(); ii++ )
959 aDC.DrawText( lines[ii], boxPos.x + 4, boxPos.y + 3 + (
int) ii * lineH );
987 &&
m_trace->GetSweepCount() > 1
988 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
992 double plotY =
m_window->p2y( newY );
1009 int sweepCount =
m_trace->GetSweepCount();
1010 size_t sweepSize =
m_trace->GetSweepSize();
1012 if( sweepCount <= 1 )
1015 if( sweepSize == std::numeric_limits<size_t>::max() || sweepSize == 0 )
1036 const std::vector<double>& dataX =
m_trace->GetDataX();
1037 const std::vector<double>& dataY =
m_trace->GetDataY();
1039 if( dataX.size() <= 1 )
1046 size_t startIdx = 0;
1047 size_t endIdx = dataX.size();
1048 int sweepCount =
m_trace->GetSweepCount();
1049 size_t sweepSize =
m_trace->GetSweepSize();
1051 if( snapToNearest &&
m_trace->IsMultiRun()
1053 && sweepSize != std::numeric_limits<size_t>::max()
1055 && std::isfinite( snapTargetY ) )
1057 double bestDistance = std::numeric_limits<double>::infinity();
1061 for(
int sweepIdx = 0; sweepIdx < sweepCount; ++sweepIdx )
1063 size_t candidateStart =
static_cast<size_t>( sweepIdx ) * sweepSize;
1064 size_t candidateEnd = std::min( dataX.size(), candidateStart + sweepSize );
1066 if( candidateStart >= candidateEnd )
1069 auto candidateBegin = dataX.begin() + candidateStart;
1070 auto candidateEndIt = dataX.begin() + candidateEnd;
1071 auto candidateMaxIt = std::upper_bound( candidateBegin, candidateEndIt,
m_coords.x );
1072 int candidateMaxIdx = candidateMaxIt - dataX.begin();
1073 int candidateMinIdx = candidateMaxIdx - 1;
1075 if( candidateMinIdx < (
int) candidateStart
1076 || candidateMaxIdx >= (
int) candidateEnd
1077 || candidateMaxIdx >= (
int) dataX.size() )
1082 double leftX = dataX[candidateMinIdx];
1083 double rightX = dataX[candidateMaxIdx];
1085 if( leftX == rightX )
1088 double leftY = dataY[candidateMinIdx];
1089 double rightY = dataY[candidateMaxIdx];
1090 double value = leftY + ( rightY - leftY ) / ( rightX - leftX ) * (
m_coords.x - leftX );
1091 double distance = std::fabs( value - snapTargetY );
1096 bestSweep = sweepIdx;
1107 && sweepSize != std::numeric_limits<size_t>::max()
1110 size_t available =
static_cast<size_t>( sweepCount ) * sweepSize;
1112 if( available <= dataX.size() )
1117 startIdx =
static_cast<size_t>(
m_sweepIndex ) * sweepSize;
1118 endIdx = std::min( dataX.size(), startIdx + sweepSize );
1130 if( startIdx >= endIdx )
1136 auto beginIt = dataX.begin() + startIdx;
1137 auto endIt = dataX.begin() + endIdx;
1140 auto maxXIt = std::upper_bound( beginIt, endIt,
m_coords.x );
1141 int maxIdx = maxXIt - dataX.begin();
1142 int minIdx = maxIdx - 1;
1145 if( minIdx < (
int) startIdx || maxIdx >= (
int) endIdx || maxIdx >= (
int) dataX.size() )
1154 const double leftX = dataX[minIdx];
1155 const double rightX = dataX[maxIdx];
1156 const double leftY = dataY[minIdx];
1157 const double rightY = dataY[maxIdx];
1160 m_coords.y = leftY + ( rightY - leftY ) / ( rightX - leftX ) * (
m_coords.x - leftX );
1169 return wxString::Format(
_(
"%d" ),
id );
1172 return wxEmptyString;
1190 wxQueueEvent( aWindow.GetParent(),
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1208 const bool hasY = std::isfinite(
m_coords.y );
1220 wxColour fg = aWindow.GetForegroundColour();
1224 if( cursorColor.
Distance( textColor ) < 0.66 )
1227 pen.SetColour( cursorColor.
ToColour() );
1228 pen.SetStyle(
m_continuous ? wxPENSTYLE_SOLID : wxPENSTYLE_LONG_DASH );
1231 if( hasY && topPx < cursorPos.y && cursorPos.y < bottomPx )
1232 aDC.DrawLine( leftPx, cursorPos.y, rightPx, cursorPos.y );
1234 if( leftPx < cursorPos.x && cursorPos.x < rightPx )
1236 aDC.DrawLine( cursorPos.x, topPx, cursorPos.x, bottomPx );
1238 wxString
id =
getID();
1239 wxSize size = aDC.GetTextExtent( wxS(
"M" ) );
1240 wxRect textRect( wxPoint( cursorPos.x + 1 - size.x / 2, topPx - 4 - size.y ), size );
1251 size.y = ( size.y / 2 ) * 2;
1252 poly[0] = { cursorPos.x - 1 - size.y / 2, topPx - size.y };
1253 poly[1] = { cursorPos.x + 1 + size.y / 2, topPx - size.y };
1254 poly[2] = { cursorPos.x, topPx };
1256 brush.SetStyle( wxBRUSHSTYLE_SOLID );
1257 brush.SetColour(
m_trace->GetTraceColour() );
1258 aDC.SetBrush( brush );
1259 aDC.DrawPolygon( 3, poly );
1261 aDC.SetTextForeground( textColor.
ToColour() );
1262 aDC.DrawLabel(
id, textRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
1265 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
1268 const std::vector<wxString>& labels =
m_trace->GetMultiRunLabels();
1276 runLabel = wxString::Format(
_(
"Run %d" ),
m_sweepIndex + 1 );
1279 wxSize runSize = aDC.GetTextExtent( runLabel );
1280 int runX = textRect.GetRight() + 6;
1281 wxRect runRect( wxPoint( runX, textRect.y ), runSize );
1283 runRect.Inflate( 3, 1 );
1285 wxBrush labelBrush( aWindow.GetBackgroundColour() );
1286 wxPen labelPen( cursorColor.
ToColour() );
1288 aDC.SetPen( labelPen );
1289 aDC.SetBrush( labelBrush );
1290 aDC.DrawRectangle( runRect );
1291 aDC.SetTextForeground( cursorColor.
ToColour() );
1292 aDC.DrawLabel( runLabel, runRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
1304 bool nearX = std::isfinite(
m_coords.x )
1306 bool nearY = std::isfinite(
m_coords.y )
1309 return nearX || nearY;
1369 m_axis_y1->SetAxisMinMax( aLock, aMin, aMax );
1376 m_axis_y2->SetAxisMinMax( aLock, aMin, aMax );
1383 m_axis_y3->SetAxisMinMax( aLock, aMin, aMax );
1397 return wxEmptyString;
1408 return wxEmptyString;
1419 return wxEmptyString;
1430 return wxEmptyString;
1558 m_axis_y1->SetName(
_(
"Noise (V/√Hz)" ) );
1561 m_axis_y2->SetName(
_(
"Noise (A/√Hz)" ) );
1654 wxString sim_cmd =
m_plotTab->GetSimCommand().Lower();
1657 if( sim_cmd.StartsWith(
".dc", &rem ) )
1665 ch = rem.GetChar( 0 );
1684 m_axis_x->SetName(
_(
"Voltage (swept)" ) );
1695 m_axis_x->SetName(
_(
"Current (swept)" ) );
1706 m_axis_x->SetName(
_(
"Resistance (swept)" ) );
1717 m_axis_x->SetName(
_(
"Temperature (swept)" ) );
1735 m_axis_y1->SetName(
_(
"Voltage (measured)" ) );
1771 bool hasY1Traces =
false;
1772 bool hasY2Traces =
false;
1773 bool hasY3Traces =
false;
1779 if( !trace || trace->GetView() !=
this )
1798 bool visibilityChanged =
false;
1803 visibilityChanged =
true;
1809 visibilityChanged =
true;
1815 visibilityChanged =
true;
1821 visibilityChanged =
true;
1824 if( visibilityChanged )
1837 wxStringTokenizer tokenizer(
m_plotTab->GetSimCommand(),
" \t\r\n", wxTOKEN_STRTOK );
1838 wxString cmd = tokenizer.GetNextToken().Lower();
1840 wxASSERT( cmd == wxS(
".tran" ) );
1846 if( tokenizer.HasMoreTokens() )
1849 if( tokenizer.HasMoreTokens() )
1852 if( tokenizer.HasMoreTokens() )
1870 if( trace->GetView() ==
this )
1871 trace->UpdateScales();
1915 bool unresolved =
false;
1921 if( !smithTrace || smithTrace->
GetView() !=
this )
1926 if( traceZ0 <= 0.0 )
1928 else if( z0 == 0.0 )
1930 else if( traceZ0 != z0 )
1935 m_smithGrid->SetReferenceImpedance( mixed || unresolved ? 0.0 : z0 );
1960 double newZoom = std::clamp(
m_smithZoom * aFactor, 1.0, 50.0 );
1962 if( newZoom <= 1.0 )
2000 if( aEvent.GetWheelAxis() != wxMOUSE_WHEEL_VERTICAL || aEvent.GetWheelRotation() == 0 )
2004 SmithZoomAt( aEvent.GetPosition(), aEvent.GetWheelRotation() > 0 ? 1.2 : 1.0 / 1.2 );
2016 double factor = aEvent.GetMagnification() + 1.0;
2037 wxPoint pos = aEvent.GetPosition();
2063 wxPoint pos = aEvent.GetPosition();
2073 if( aEvent.MiddleIsDown() )
2101 wxPoint pos = aEvent.GetPosition();
2138 PopupMenu( menu, aEvent.GetPosition() );
2150 switch( aEvent.GetId() )
2173 default: aEvent.Skip();
2179 SIM_TAB( aSimCommand, parent ),
2186 wxBoxSizer* outerSizer =
new wxBoxSizer( wxVERTICAL );
2191 wxBoxSizer* viewButtonsSizer =
new wxBoxSizer( wxHORIZONTAL );
2195 addViewButton->SetToolTip(
_(
"Add a new signal view" ) );
2199 removeViewButton->SetToolTip(
_(
"Remove the last signal view" ) );
2201 viewButtonsSizer->Add( addViewButton, 0, wxLEFT, 5 );
2202 viewButtonsSizer->Add( removeViewButton, 0, wxLEFT, 30 );
2204 addViewButton->Bind( wxEVT_BUTTON,
2205 [
this]( wxCommandEvent& aEvent )
2210 removeViewButton->Bind( wxEVT_BUTTON,
2211 [
this]( wxCommandEvent& aEvent )
2213 const std::vector<SIM_VIEW*>& views =
GetViews();
2215 if( !views.empty() )
2219 outerSizer->Add( viewButtonsSizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 3 );
2221 SetSizer( outerSizer );
2273 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_VIEWS_CHANGED ) );
2293 std::vector<TRACE*> plottedHere;
2297 trace->ClearYScaleViewIf( aView );
2299 if( trace->GetView() == aView )
2300 plottedHere.push_back( trace );
2304 for(
TRACE* trace : plottedHere )
2320 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_VIEWS_CHANGED ) );
2328 for(
size_t i = 0; i <
m_views.size(); ++i )
2346 double pos = aSource->
GetPosX();
2352 if( view == aSource )
2355 view->SetScaleX( scaleX );
2356 view->SetXRange( pos, desiredMax, desiredMin );
2367 return view ? view->
GetLabelX() : wxString( wxS(
"" ) );
2374 return view ? view->
GetUnitsX() : wxString( wxS(
"" ) );
2388 view->ShowGrid( aEnable );
2402 view->ShowLegend( aEnable );
2423 view->SetLegendPosition( aPosition );
2432 return view ? view->
GetLabelY1() : wxString( wxS(
"" ) );
2439 return view ? view->
GetLabelY2() : wxString( wxS(
"" ) );
2446 return view ? view->
GetLabelY3() : wxString( wxS(
"" ) );
2453 return view ? view->
GetUnitsY1() : wxString( wxS(
"" ) );
2460 return view ? view->
GetUnitsY2() : wxString( wxS(
"" ) );
2467 return view ? view->
GetUnitsY3() : wxString( wxS(
"" ) );
2474 return view && view->
GetY1Scale( aMin, aMax );
2481 return view && view->
GetY2Scale( aMin, aMax );
2488 return view && view->
GetY3Scale( aMin, aMax );
2495 view->SetY1Scale( aLock, aMin, aMax );
2502 view->SetY2Scale( aLock, aMin, aMax );
2509 view->SetY3Scale( aLock, aMin, aMax );
2516 view->EnsureThirdYAxisExists();
2528 view->UpdateSmithGridColor();
2546 view->SetSmithChart( aEnable );
2553 view->UpdateSmithReferenceImpedance();
2567 return view ? view->
GetSmithPan() : wxRealPoint( 0.0, 0.0 );
2575 view->ResetSmithView();
2584 view->SetSmithView( aZoom, aPanX, aPanY );
2591 view->SmithZoomAt( aPos, aFactor );
2598 view->SmithPanBy( aDelta );
2615 wxPenStyle penStyle;
2618 penStyle = wxPENSTYLE_SOLID;
2620 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
2622 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
2624 penStyle = wxPENSTYLE_SOLID;
2635 if( !trace && aView )
2641 bool hasVoltageTraces =
false;
2643 for(
const auto& [
id, candidate] :
m_traces )
2645 if( candidate->GetView() == aView && ( candidate->GetType() &
SPT_VOLTAGE ) )
2647 hasVoltageTraces =
true;
2652 if( !hasVoltageTraces )
2684 size_t aSweepSize,
bool aIsMultiRun,
const std::vector<wxString>& aMultiRunLabels )
2696 if( aX.size() > 0 && aX[0] == 0 )
2698 aX.erase( aX.begin() );
2699 aY.erase( aY.begin() );
2707 for(
double& pt : aY )
2708 pt = pt * 180.0 /
M_PI;
2712 for(
double& pt : aY )
2727 trace->
SetScale(
nullptr,
nullptr );
2746 cursor->UpdateForNewData();
2757 if( trace == aTrace )
2769 view->DelLayer(
cursor,
true );
2772 view->DelLayer( aTrace,
true,
true );
2773 view->UpdateAxisVisibility();
2774 view->UpdateSmithReferenceImpedance();
2810 if( !freqs.empty() )
2811 cursor->SetCoordX( freqs[freqs.size() * ( aCursorId == 1 ? 2 : 3 ) / 5] );
2823 cursor->SetName( aSignalName );
2829 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
2837 aTrace->
SetCursor( aCursorId,
nullptr );
2840 view->DelLayer(
cursor,
true );
2843 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
2851 view->ResetScales( aIncludeX );
2856 std::map<std::pair<SIM_VIEW*, int>, std::vector<TRACE*>> groups;
2860 if(
SIM_VIEW* view = trace->GetView() )
2861 groups[{ trace->GetYScaleView(), view->GetAxisSlot( trace ) }].push_back( trace );
2864 for(
const auto& [key, traces] : groups )
2866 std::vector<SIM_VIEW*> actualViews;
2868 for(
TRACE* trace : traces )
2872 if( std::find( actualViews.begin(), actualViews.end(), view ) == actualViews.end() )
2873 actualViews.push_back( view );
2876 if( actualViews.size() <= 1 )
2879 bool haveRange =
false;
2883 for(
TRACE* trace : traces )
2885 double lo = trace->GetMinY();
2886 double hi = trace->GetMaxY();
2888 if( !std::isfinite( lo ) || !std::isfinite( hi ) )
2899 minV = std::min( minV, lo );
2900 maxV = std::max( maxV, hi );
2907 for(
SIM_VIEW* view : actualViews )
2909 if(
mpScaleY* axis = view->GetAxisBySlot( key.second ) )
2910 axis->SetDataRange( minV, maxV );
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
The SIMULATOR_FRAME holds the main user-interface for running simulations.
void Move(wxPoint aDelta) override
Moves the layer rectangle of given pixel deltas.
static constexpr int DRAG_MARGIN
bool Inside(const wxPoint &aPoint) const override
Checks whether a point is inside the info box rectangle.
void doSetCoordX(double aValue)
bool OnDoubleClick(const wxPoint &aPoint, mpWindow &aWindow) override
virtual void SetCoordX(double aValue)
void UpdateReference() override
Updates the rectangle reference point.
void Plot(wxDC &aDC, mpWindow &aWindow) override
Plot method.
A color representation with 4 components: red, green, blue, alpha.
COLOR4D & Invert()
Makes the color inverted, alpha remains the same.
wxColour ToColour() const
double Distance(const COLOR4D &other) const
Returns the distance (in RGB space) between two colors.
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
void formatLabels() override
wxString GetUnits() const
LIN_SCALE(const wxString &name, const wxString &unit, int flags)
wxString m_base_axis_label
wxString GetUnits() const
LOG_SCALE(const wxString &name, const wxString &unit, int flags)
void formatLabels() override
bool DeleteTrace(const wxString &aVectorName, int aTraceType)
double GetSmithZoom() const
void UpdateSmithReferenceImpedance()
The saved/restored pan and zoom track the default view.
wxString GetLabelY1() const
mpWindow * GetPlotWin() const
Get (or create, on aView) the TRACE for a signal.
void SetSmithView(double aZoom, double aPanX, double aPanY)
void ShowGrid(bool aEnable)
void SetTraceData(TRACE *aTrace, std::vector< double > &aX, std::vector< double > &aY, int aSweepCount, size_t aSweepSize, bool aIsMultiRun=false, const std::vector< wxString > &aMultiRunLabels={})
void SyncXView(SIM_VIEW *aSource)
const std::vector< SIM_VIEW * > & GetViews() const
wxString GetUnitsY2() const
void SetY2Scale(bool aLock, double aMin, double aMax)
void SmithZoomAt(const wxPoint &aPos, double aFactor)
TRACE * GetTrace(const wxString &aVecName, int aType) const
void SetSmithMode(bool aEnable)
wxString GetLabelX() const
wxRealPoint GetSmithPan() const
wxString GetLabelY3() const
void SetY1Scale(bool aLock, double aMin, double aMax)
wxPoint GetLegendPosition() const
void SetY3Scale(bool aLock, double aMin, double aMax)
std::map< wxString, TRACE * > m_traces
bool IsLegendShown() const
SIM_VIEW * GetDefaultView() const
Add a new (empty) view, stacked below the existing ones.
SIM_VIEW * AddView()
Remove a view.
wxString GetUnitsForTrace(TRACE *aTrace) const
void UpdateTraceStyle(TRACE *trace)
Update plot colors.
void SetLegendPosition(const wxPoint &aPosition)
TRACE * GetOrAddTrace(const wxString &aVectorName, int aType, SIM_VIEW *aView)
void ResetScales(bool aIncludeX)
Update trace line style.
void ShowLegend(bool aEnable)
bool GetY3Scale(double *aMin, double *aMax) const
SIM_PLOT_TAB(const wxString &aSimCommand, wxWindow *parent)
wxString GetLabelY2() const
void EnableCursor(TRACE *aTrace, int aCursorId, const wxString &aSignalName)
int GetViewIndex(SIM_VIEW *aView) const
wxString GetUnitsX() const
Get the display units (e.g. "V", "A", "dB") for a given trace's own view.
void OnLanguageChanged() override
Getter for the default view's math plot window (back-compat for zoom undo/redo, export).
void EnsureThirdYAxisExists()
wxString getTraceId(const wxString &aVectorName, int aType) const
void ApplyPreferences(const SIM_PREFERENCES &aPrefs) override
wxBoxSizer * m_viewsSizer
mpWindow::MouseWheelActionSet m_mouseWheelActions
bool RemoveView(SIM_VIEW *aView)
Mirror aSource's current X range onto every other view of this tab.
void SmithPanBy(const wxPoint &aDelta)
Traces and cursors set aside while in Smith mode, restored when leaving it.
std::vector< SIM_VIEW * > m_views
wxPoint m_LastLegendPosition
wxString GetUnitsY1() const
std::map< wxString, wxColour > m_sessionTraceColors
static mpWindow::MouseWheelActionSet convertMouseWheelActions(const SIM_MOUSE_WHEEL_ACTION_SET &s)
void DisableCursor(TRACE *aTrace, int aCursorId)
Reset scale ranges to fit the current traces, on every view.
bool GetY2Scale(double *aMin, double *aMax) const
void ResetSmithView()
Restore a saved view, values are validated and clamped.
bool GetY1Scale(double *aMin, double *aMax) const
wxString GetUnitsY3() const
SIM_TYPE GetSimType() const
A single stacked plot area within a SIM_PLOT_TAB.
wxString GetLabelY2() const
void OnXViewChanged() override
Directly set this view's X range (used to mirror another view's zoom/pan onto this one).
void updateAxes(int aNewTraceType=SIM_TRACE_TYPE::SPT_UNKNOWN)
void SmithZoomAt(const wxPoint &aPos, double aFactor)
bool GetY3Scale(double *aMin, double *aMax) const
void onSmithMenuCommand(wxCommandEvent &aEvent)
wxString GetUnitsForTrace(TRACE *aTrace) const
Get the Y-axis slot (1, 2 or 3) a trace of this type is plotted on.
void UpdateSmithGridColor()
void SetY2Scale(bool aLock, double aMin, double aMax)
void EnsureThirdYAxisExists()
void SmithPanBy(const wxPoint &aDelta)
void onSmithDClick(wxMouseEvent &aEvent)
wxString GetLabelX() const
wxPoint m_lastLegendPosition
wxString GetLabelY3() const
wxString GetUnitsY3() const
Get the display units (e.g. "V", "A", "dB") for a given trace plotted on this view.
wxPoint GetLegendPosition() const
void onSmithMagnify(wxMouseEvent &aEvent)
void SetY1Scale(bool aLock, double aMin, double aMax)
const wxRealPoint & GetSmithPan() const
void SetY3Scale(bool aLock, double aMin, double aMax)
void onSmithMiddleDown(wxMouseEvent &aEvent)
void onSmithRightDown(wxMouseEvent &aEvent)
SIM_VIEW(SIM_PLOT_TAB *aPlotTab, wxWindow *aParent)
Mirrors this view's X range onto every other view of the same tab.
bool IsLegendShown() const
mpScaleY * GetAxisBySlot(int aSlot) const
double GetSmithZoom() const
bool getSmithView(SMITH_VIEW &aView) const
bool GetY2Scale(double *aMin, double *aMax) const
bool GetY1Scale(double *aMin, double *aMax) const
void onSmithLeftUp(wxMouseEvent &aEvent)
wxString GetLabelY1() const
void UpdateSmithReferenceImpedance()
Re-read the Smith grid pen from the tab's color theme.
void onSmithLeftDown(wxMouseEvent &aEvent)
void ResetScales(bool aIncludeX)
Create/Ensure axes are available for plotting.
int GetAxisSlot(TRACE *aTrace) const
Get the Y-axis scale object for a given slot (1, 2 or 3), or nullptr if not created yet.
wxString GetUnitsX() const
void onSmithRightUp(wxMouseEvent &aEvent)
void SetSmithChart(bool aEnable)
< Show/hide this view's Smith chart overlay.
void UpdateAxisVisibility()
void onSmithMotion(wxMouseEvent &aEvent)
wxString GetUnitsY2() const
wxString GetUnitsY1() const
void onSmithMouseWheel(wxMouseEvent &aEvent)
void prepareDCAxes(int aNewTraceType)
<
Trace hidden while the tab is in Smith mode, kept so leaving the mode restores it.
void UpdateReference() override
Updates the rectangle reference point.
void Plot(wxDC &aDC, mpWindow &aWindow) override
Plot method.
bool Inside(const wxPoint &aPoint) const override
Checks whether a point is inside the info box rectangle.
void SetCoordX(double aValue) override
void snapToIndex(int aIndex)
void snapToFrequency(double aFreq)
void UpdateForNewData() override
void Move(wxPoint aDelta) override
Moves the layer rectangle of given pixel deltas.
Reflection coefficient locus, Re in X and Im in Y, drawn on the Smith chart.
static bool GetChartView(mpWindow &aWindow, double aZoom, const wxRealPoint &aPan, SMITH_VIEW &aView)
void Plot(wxDC &aDC, mpWindow &aWindow) override
Plot given view of layer to the given device context.
Cursor that snaps along a Smith chart locus, keyed by frequency.
void Plot(wxDC &aDC, mpWindow &aWindow) override
Layer plot handler.
const std::vector< double > & GetFrequencies() const
Zero until the response port resolves one, which leaves only normalized values readable.
double GetReferenceImpedance() const
Helper class to recognize Spice formatted values.
void ResetDataRange() override
void ExtendDataRange(double minV, double maxV) override
void SetStartAndEnd(double aStartTime, double aEndTime)
TIME_SCALE(const wxString &name, const wxString &unit, int flags)
Overlay layer drawing the Smith chart grid (constant resistance and reactance circles)
void SetIsMultiRun(bool aIsMultiRun)
void SetTraceColour(const wxColour &aColour)
void SetMultiRunLabels(const std::vector< wxString > &aLabels)
std::map< int, CURSOR * > & GetCursors()
SIM_VIEW * GetView() const
void SetView(SIM_VIEW *aView)
The view whose Y-axis scale this trace's axis should match (defaults to its own view).
SIM_TRACE_TYPE GetType() const
void SetData(const std::vector< double > &aX, const std::vector< double > &aY) override
Assigns new data set for the trace.
void SetCursor(int aCursorId, CURSOR *aCursor)
const std::vector< double > & GetDataY() const
wxColour GetTraceColour() const
const std::vector< double > & GetDataX() const
CURSOR * GetCursor(int aCursorId)
void SetSweepSize(size_t aSweepSize)
size_t GetSweepSize() const
void SetSweepCount(int aSweepCount)
int GetSweepCount() const override
virtual void SetScale(mpScaleBase *scaleX, mpScaleBase *scaleY)
wxPoint GetPosition() const
Returns the position of the upper left corner of the box (in pixels)
virtual void UpdateReference()
Updates the rectangle reference point.
virtual void Move(wxPoint delta)
Moves the layer rectangle of given pixel deltas.
Implements the legend to be added to the plot This layer allows you to add a legend to describe the p...
const wxString & GetName() const
Get layer name.
const wxPen & GetPen() const
Get pen set for this layer.
void SetVisible(bool show)
Sets layer visibility.
const wxFont & GetPlotFont() const
Get the font to draw this layer with, which is the default one when none was set.
void SetPen(const wxPen &pen)
Set layer pen.
Plot layer implementing a y-scale ruler.
void SetMasterScale(mpScaleY *masterScale)
Canvas for plotting mpLayer implementations.
int GetMarginLeft() const
void SetMargins(int top, int right, int bottom, int left)
Set window margins, creating a blank area where some kinds of layers cannot draw.
int GetScrX() const
Get current view's X dimension in device context units.
int GetScrY() const
Get current view's Y dimension in device context units.
void SetMouseWheelActions(const MouseWheelActionSet &s)
Set the pan/zoom actions corresponding to mousewheel/trackpad events.
double p2x(wxCoord pixelCoordX)
Converts mpWindow (screen) pixel coordinates into graph (floating point) coordinates,...
void LimitView(bool aEnable)
Enable limiting of zooming & panning to the area used by the plots.
wxCoord x2p(double x)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
mpInfoLayer * IsInsideInfoLayer(wxPoint &point)
Check if a given point is inside the area of a mpInfoLayer and eventually returns its pointer.
void UpdateAll()
Refresh display.
wxCoord y2p(double y)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
double GetDesiredXmax() const
Returns the right-border layer coordinate that the user wants the mpWindow to show (it may be not exa...
wxMenu * GetPopupMenu()
Get reference to context menu of the plot canvas.
double GetDesiredXmin() const
Returns the left-border layer coordinate that the user wants the mpWindow to show (it may be not exac...
int GetMarginRight() const
int GetMarginBottom() const
void EnableDoubleBuffer(bool enabled)
Enable/disable the double-buffering of the window, eliminating the flicker (default=disabled).
double GetScaleX() const
Get current view's X scale.
bool AddLayer(mpLayer *layer, bool refreshDisplay=true)
Add a plot layer to the canvas.
double GetPosX() const
Get current view's X position.
static bool empty(const wxTextEntryBase *aCtrl)
#define mpALIGN_BORDER_RIGHT
Aligns Y axis to right border.
#define mpALIGN_RIGHT
Aligns label to the right.
#define mpALIGN_LEFT
Aligns label to the left.
#define mpALIGN_BOTTOM
Aligns label to the bottom.
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
double SeriesCapacitance(double aReactance, double aFreq)
Pan that keeps the gamma point under aPos fixed when the view zooms to aNewZoom.
double SeriesInductance(double aReactance, double aFreq)
double VSWR(double aGammaMag)
wxRealPoint ZoomAboutPoint(const SMITH_VIEW &aView, const wxPoint &aPos, double aNewZoom)
S-parameter vectors are named S_<responsePort>_<drivePort>.
bool GammaToImpedance(double aRe, double aIm, double aZ0, double &aResistance, double &aReactance)
< Impedance of a reflection coefficient, z = z0 ( 1 + gamma ) / ( 1 - gamma ), false at the gamma = 1...
double ReturnLoss(double aGammaMag)
Series equivalent element of a reactance at one frequency, henries for aReactance > 0,...
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
Class is responsible for providing colors for traces on simulation plot.
wxDEFINE_EVENT(EVT_SIM_CURSOR_UPDATE, wxCommandEvent)
static void getSISuffix(double x, const wxString &unit, int &power, wxString &suffix)
static int countDecimalDigits(double x, int maxDigits)
static bool smithView(mpWindow &aWindow, SMITH_VIEW &aView)
static wxString formatFloat(double x, int nDigits)
double MagnitudeToDb(double aMagnitude)
Convert a linear magnitude to decibels.
static SIM_MOUSE_WHEEL_ACTION_SET GetMouseDefaults()
Contains preferences pertaining to the simulator.
SIM_MOUSE_WHEEL_ACTION_SET mouse_wheel_actions
< Smith chart placement plus pan/zoom, maps a gamma point to a screen pixel and back.
wxRealPoint ToGamma(const wxPoint &aPt) const
wxPoint ToScreen(double aRe, double aIm) const