24#include <wx/tokenzr.h>
44 fmt.Printf(
"%%.0%df", nDigits );
54static void getSISuffix(
double x,
const wxString& unit,
int& power, wxString& suffix )
56 const int n_powers = 11;
84 for(
int i = 0; i < n_powers - 1; i++ )
86 double r_cur = pow( 10, powers[i].exponent );
88 if( fabs( x ) >= r_cur && fabs( x ) < r_cur * 1000.0 )
90 power = powers[i].exponent;
92 if( powers[i].suffix )
93 suffix = wxString( powers[i].suffix ) + unit;
105 if( std::isnan( x ) )
108 auto countSignificantDigits =
111 while( k && ( k % 10LL ) == 0LL )
125 int64_t k = (int)( ( x - floor( x ) ) * pow( 10.0, (
double) maxDigits ) );
126 int n = countSignificantDigits( k );
129 n = std::min( n, countSignificantDigits( k + 1 ) );
135template <
typename T_PARENT>
140 T_PARENT(
name, flags, false ),
149 double maxVis = T_PARENT::AbsVisibleMaxValue();
154 int constexpr MAX_DIGITS = 3;
155 int constexpr MAX_DISAMBIGUATION_DIGITS = 6;
156 bool duplicateLabels =
false;
160 double sf = pow( 10.0, power );
167 for(
size_t ii = 0; ii < T_PARENT::m_tickLabels.size(); ++ii )
174 if( ii > 0 && l.
label == T_PARENT::m_tickLabels[ii-1].label )
175 duplicateLabels =
true;
178 while( duplicateLabels && ++digits <= MAX_DISAMBIGUATION_DIGITS );
203 LIN_SCALE::ExtendDataRange( minV, maxV );
233template <
typename T_PARENT>
238 T_PARENT(
name, flags, false ),
249 int constexpr MAX_DIGITS = 3;
254 double sf = pow( 10.0, power );
272 int radius = std::min( plotW, plotH ) / 2 - 15;
277 aView.
center = wxPoint( mL + plotW / 2, mT + plotH / 2 );
281 aView.
plotRect = wxRect( mL, mT, plotW, plotH );
292 wxRealPoint pan = view ? view->
GetSmithPan() : wxRealPoint( 0.0, 0.0 );
308 const wxRect& plotRect = view.
plotRect;
309 int mL = plotRect.x, mT = plotRect.y, plotW = plotRect.width, plotH = plotRect.height;
311 aDC.SetClippingRegion( plotRect );
315 [&](
double aR ) ->
int
321 auto drawClippedCircle =
322 [&](
double aCx,
double aCy,
double aR )
324 constexpr double LIMIT = 1.0002;
326 std::vector<wxPoint> run;
327 int segments = segmentsFor( aR );
329 for(
int ii = 0; ii <= segments; ii++ )
331 double angle = 2.0 *
M_PI * ii / segments;
332 double re = aCx + aR * cos( angle );
333 double im = aCy + aR * sin( angle );
335 if( re * re + im * im <= LIMIT )
337 run.push_back( view.
ToScreen( re, im ) );
342 aDC.DrawLines( (
int) run.size(), run.data() );
349 aDC.DrawLines( (
int) run.size(), run.data() );
353 [](
double aValue ) -> wxString
355 return wxString::Format( wxS(
"%g" ), aValue );
359 double labelScale =
m_z0 > 0.0 ?
m_z0 : 1.0;
361 static const std::vector<double> baseVals = { 0.2, 0.5, 1.0, 2.0, 5.0 };
362 std::vector<double> gridVals = baseVals;
364 constexpr double FINEST_SCALE = 2.0 / ( 1.0 + 20.0 );
366 wxRealPoint lowGamma = view.
ToGamma( wxPoint( mL, mT + plotH ) );
367 wxRealPoint highGamma = view.
ToGamma( wxPoint( mL + plotW, mT ) );
372 return std::hypot( std::max( { lowGamma.x - aRe, aRe - highGamma.x, 0.0 } ),
373 std::max( { lowGamma.y, -highGamma.y, 0.0 } ) );
376 bool zoomedIn = ( plotW / 2.0 ) / view.
radius < 1.0;
377 bool converged = zoomedIn && std::min( poleDistance( 1.0 ), poleDistance( -1.0 ) ) < FINEST_SCALE;
379 if( view.
zoom >= 2.0 && !converged )
380 gridVals.insert( gridVals.end(), { 0.1, 0.3, 0.4, 0.7, 1.5, 3.0, 10.0 } );
382 if( view.
zoom >= 5.0 && !converged )
383 gridVals.insert( gridVals.end(), { 0.05, 0.15, 0.6, 0.8, 1.2, 1.7, 2.5, 4.0, 7.0, 20.0 } );
385 wxPen gridPen =
m_pen;
386 gridPen.SetStyle( wxPENSTYLE_DOT );
388 aDC.SetBrush( *wxTRANSPARENT_BRUSH );
390 aDC.SetTextForeground(
m_pen.GetColour() );
391 aDC.SetPen( gridPen );
394 for(
double r : gridVals )
395 drawClippedCircle( r / ( 1.0 + r ), 0.0, 1.0 / ( 1.0 + r ) );
398 for(
double x : gridVals )
400 drawClippedCircle( 1.0, 1.0 / x, 1.0 / x );
401 drawClippedCircle( 1.0, -1.0 / x, 1.0 / x );
410 constexpr double LABEL_LIMIT = 1.0002;
413 [&](
double aRe,
double aIm, wxPoint& aOut ) ->
bool
415 if( aRe * aRe + aIm * aIm > LABEL_LIMIT )
418 wxPoint p = view.
ToScreen( aRe, aIm );
420 if( !plotRect.Contains( p ) )
429 [&](
double aCx,
double aCy,
double aR, wxPoint& aOut ) ->
bool
431 int bestMargin = std::numeric_limits<int>::max();
433 int segments = segmentsFor( aR );
435 for(
int ii = 0; ii <= segments; ii++ )
437 double angle = 2.0 *
M_PI * ii / segments;
438 double re = aCx + aR * cos( angle );
439 double im = aCy + aR * sin( angle );
441 if( re * re + im * im > LABEL_LIMIT )
444 wxPoint p = view.
ToScreen( re, im );
446 if( !plotRect.Contains( p ) )
449 int margin = std::min( { p.x - mL, mL + plotW - p.x, p.y - mT, mT + plotH - p.y } );
451 if( margin < bestMargin )
463 [&](
const wxPoint& aPos,
const wxSize& aExt ) -> wxPoint
465 return wxPoint( std::clamp( aPos.x, mL + 1, mL + plotW - aExt.x - 1 ),
466 std::clamp( aPos.y, mT + 1, mT + plotH - aExt.y - 1 ) );
469 std::vector<wxRect> placed;
472 [&](
const wxString& aLabel,
const wxPoint& aPos,
const wxSize& aExt )
474 wxPoint pos = clampToPlot( aPos, aExt );
475 wxRect box( pos, aExt );
479 for(
const wxRect& seen : placed )
481 if( seen.Intersects( box ) )
485 placed.push_back( box );
486 aDC.DrawText( aLabel, pos );
490 [&](
const wxString& aLabel,
const wxPoint& aAt )
492 wxSize ext = aDC.GetTextExtent( aLabel );
494 drawLabel( aLabel, wxPoint( aAt.x - ext.x / 2, aAt.y + 3 ), ext );
498 auto drawRimLabel = [&](
const wxString& aLabel,
const wxPoint& aAt,
double aRe,
double aIm )
500 wxSize ext = aDC.GetTextExtent( aLabel );
505 pos.x -=
KiROUND( ext.x * ( 1.0 - aRe ) / 2.0 );
506 pos.y -=
KiROUND( ext.y * ( 1.0 + aIm ) / 2.0 );
508 drawLabel( aLabel, pos, ext );
514 if( anchorPos( -1.0, 0.0, at ) )
515 drawRimLabel( wxS(
"0" ), at, -1.0, 0.0 );
516 else if( edgePos( 0.0, 0.0, 1.0, at ) )
517 drawEdgeLabel( wxS(
"0" ), at );
519 for(
double r : gridVals )
521 if( anchorPos( ( r - 1.0 ) / ( r + 1.0 ), 0.0, at ) || edgePos( r / ( 1.0 + r ), 0.0, 1.0 / ( 1.0 + r ), at ) )
523 drawEdgeLabel( formatValue( r * labelScale ), at );
527 for(
double x : gridVals )
529 double d = x * x + 1.0;
530 double re = ( x * x - 1.0 ) / d;
531 double im = 2.0 * x / d;
532 wxString posLabel = wxS(
"j" ) + formatValue( x * labelScale );
533 wxString negLabel = wxS(
"-j" ) + formatValue( x * labelScale );
535 if( anchorPos( re, im, at ) )
536 drawRimLabel( posLabel, at, re, im );
537 else if( edgePos( 1.0, 1.0 / x, 1.0 / x, at ) )
538 drawEdgeLabel( posLabel, at );
540 if( anchorPos( re, -im, at ) )
541 drawRimLabel( negLabel, at, re, -im );
542 else if( edgePos( 1.0, -1.0 / x, 1.0 / x, at ) )
543 drawEdgeLabel( negLabel, at );
550 note = wxString::Format( wxS(
"Z0 = %s Ω" ), formatValue(
m_z0 ) );
552 note =
_(
"Normalized Z/Z0 (ports differ)" );
554 note =
_(
"Normalized Z/Z0" );
556 wxSize ext = aDC.GetTextExtent( note );
558 aDC.DrawText( note, mL + 4, mT + plotH - ext.y - 4 );
560 aDC.DestroyClippingRegion();
574 const std::vector<double>& xs =
GetDataX();
575 const std::vector<double>& ys =
GetDataY();
576 size_t count = std::min( xs.size(), ys.size() );
582 aDC.SetClippingRegion( view.
plotRect );
586 if(
GetSweepCount() <= 1 || chunk == std::numeric_limits<size_t>::max() || chunk == 0 )
589 std::vector<wxPoint> pts;
596 aDC.DrawLines( (
int) pts.size(), pts.data() );
598 else if( pts.size() == 1 )
600 aDC.SetBrush( wxBrush(
m_pen.GetColour() ) );
601 aDC.DrawCircle( pts[0], 2 );
607 for(
size_t start = 0; start < count; start += chunk )
609 size_t end = std::min( count, start + chunk );
611 for(
size_t ii = start; ii <
end; ii++ )
614 if( !std::isfinite( xs[ii] ) || !std::isfinite( ys[ii] ) )
620 pts.emplace_back( view.
ToScreen( xs[ii], ys[ii] ) );
626 aDC.DestroyClippingRegion();
632 const std::vector<double>& re =
m_trace->GetDataX();
633 const std::vector<double>& im =
m_trace->GetDataY();
634 const std::vector<double>& freqs =
static_cast<SMITH_TRACE*
>(
m_trace )->GetFrequencies();
636 size_t count = std::min( re.size(), im.size() );
641 m_index = std::clamp( aIndex, 0, (
int) count - 1 );
653 const std::vector<double>& freqs =
static_cast<SMITH_TRACE*
>(
m_trace )->GetFrequencies();
654 const std::vector<double>& re =
m_trace->GetDataX();
655 const std::vector<double>& im =
m_trace->GetDataY();
660 size_t end = freqs.size();
661 size_t chunk =
m_trace->GetSweepSize();
663 if(
m_trace->GetSweepCount() > 1
665 && chunk != std::numeric_limits<size_t>::max()
667 && (
size_t)
m_index < freqs.size() )
669 begin = ( (size_t)
m_index / chunk ) * chunk;
670 end = std::min( freqs.size(), begin + chunk );
674 double bestDist = std::numeric_limits<double>::max();
676 for(
size_t ii = begin; ii <
end; ii++ )
678 if( !std::isfinite( freqs[ii] ) )
681 if( ii < re.size() && ii < im.size() && ( !std::isfinite( re[ii] ) || !std::isfinite( im[ii] ) ) )
684 double dist = std::fabs( freqs[ii] - aFreq );
686 if( dist < bestDist )
771 const std::vector<double>& re =
m_trace->GetDataX();
772 const std::vector<double>& im =
m_trace->GetDataY();
773 size_t count = std::min( re.size(), im.size() );
792 double bestDist = std::numeric_limits<double>::max();
794 for(
size_t ii = 0; ii < count; ii++ )
796 if( !std::isfinite( re[ii] ) || !std::isfinite( im[ii] ) )
799 wxPoint p = view.
ToScreen( re[ii], im[ii] );
800 double dx = (double) p.x -
m_dim.x;
801 double dy = (double) p.y -
m_dim.y;
802 double dist = dx * dx + dy * dy;
804 if( dist < bestDist )
829 wxQueueEvent( aWindow.GetParent(),
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
844 m_dim.SetX( marker.x );
845 m_dim.SetY( marker.y );
854 wxColour fg = aWindow.GetForegroundColour();
857 pen.SetColour( cursorColor.
ToColour() );
858 pen.SetStyle( wxPENSTYLE_SOLID );
860 aDC.SetBrush( *wxTRANSPARENT_BRUSH );
862 aDC.DrawCircle( marker, 4 );
863 aDC.DrawLine( marker.x - 8, marker.y, marker.x - 4, marker.y );
864 aDC.DrawLine( marker.x + 4, marker.y, marker.x + 8, marker.y );
865 aDC.DrawLine( marker.x, marker.y - 8, marker.x, marker.y - 4 );
866 aDC.DrawLine( marker.x, marker.y + 4, marker.x, marker.y + 8 );
873 auto formatSI = [](
double aValue,
const wxString& aUnit ) -> wxString
875 if( std::isnan( aValue ) )
883 double sf = pow( 10.0, power );
885 return formatFloat( aValue / sf, 3 ) + wxS(
" " ) + suffix;
888 std::vector<wxString> lines;
890 lines.push_back(
getID() + wxS(
": f = " ) + formatSI( freq, wxS(
"Hz" ) ) );
893 bool absolute = z0 > 0.0;
897 lines.push_back( wxS(
"Z = inf" ) );
901 lines.push_back( wxString::Format( wxS(
"Z = %s %s j%s" ),
902 formatSI( zr, wxS(
"Ω" ) ),
903 zi < 0 ? wxS(
"-" ) : wxS(
"+" ),
904 formatSI( std::fabs( zi ), wxS(
"Ω" ) ) ) );
907 if( std::isfinite( freq ) && freq > 0.0 && zi != 0.0 )
917 lines.push_back( wxString::Format( wxS(
"z = %s %s j%s" ),
919 zi < 0 ? wxS(
"-" ) : wxS(
"+" ),
926 if( std::isfinite( rl ) )
927 lines.push_back( wxString::Format( wxS(
"RL = %s dB" ),
formatFloat( rl, 1 ) ) );
929 lines.push_back( wxS(
"RL = inf" ) );
931 if( std::isfinite( vswr ) )
932 lines.push_back( wxString::Format( wxS(
"VSWR = %s" ),
formatFloat( vswr, 2 ) ) );
934 lines.push_back( wxS(
"VSWR = inf" ) );
940 int lineH = aDC.GetTextExtent( wxS(
"M" ) ).y;
942 for(
const wxString& line : lines )
943 boxW = std::max( boxW, aDC.GetTextExtent( line ).x );
946 boxH = (int) lines.size() * lineH + 6;
948 wxPoint boxPos( marker.x + ( marker.x < aWindow.
GetScrX() / 2 ? 12 : -12 - boxW ),
949 marker.y + ( marker.y < aWindow.
GetScrY() / 2 ? 12 : -12 - boxH ) );
951 boxPos.x = std::clamp( boxPos.x, 0, std::max( 0, aWindow.
GetScrX() - boxW ) );
952 boxPos.y = std::clamp( boxPos.y, 0, std::max( 0, aWindow.
GetScrY() - boxH ) );
954 wxBrush labelBrush( aWindow.GetBackgroundColour() );
956 aDC.SetBrush( labelBrush );
957 aDC.DrawRectangle( wxRect( boxPos, wxSize( boxW, boxH ) ) );
958 aDC.SetTextForeground( cursorColor.
ToColour() );
960 for(
size_t ii = 0; ii < lines.size(); ii++ )
961 aDC.DrawText( lines[ii], boxPos.x + 4, boxPos.y + 3 + (
int) ii * lineH );
989 &&
m_trace->GetSweepCount() > 1
990 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
994 double plotY =
m_window->p2y( newY );
1011 int sweepCount =
m_trace->GetSweepCount();
1012 size_t sweepSize =
m_trace->GetSweepSize();
1014 if( sweepCount <= 1 )
1017 if( sweepSize == std::numeric_limits<size_t>::max() || sweepSize == 0 )
1038 const std::vector<double>& dataX =
m_trace->GetDataX();
1039 const std::vector<double>& dataY =
m_trace->GetDataY();
1041 if( dataX.size() <= 1 )
1048 size_t startIdx = 0;
1049 size_t endIdx = dataX.size();
1050 int sweepCount =
m_trace->GetSweepCount();
1051 size_t sweepSize =
m_trace->GetSweepSize();
1053 if( snapToNearest &&
m_trace->IsMultiRun()
1055 && sweepSize != std::numeric_limits<size_t>::max()
1057 && std::isfinite( snapTargetY ) )
1059 double bestDistance = std::numeric_limits<double>::infinity();
1063 for(
int sweepIdx = 0; sweepIdx < sweepCount; ++sweepIdx )
1065 size_t candidateStart =
static_cast<size_t>( sweepIdx ) * sweepSize;
1066 size_t candidateEnd = std::min( dataX.size(), candidateStart + sweepSize );
1068 if( candidateStart >= candidateEnd )
1071 auto candidateBegin = dataX.begin() + candidateStart;
1072 auto candidateEndIt = dataX.begin() + candidateEnd;
1073 auto candidateMaxIt = std::upper_bound( candidateBegin, candidateEndIt,
m_coords.x );
1074 int candidateMaxIdx = candidateMaxIt - dataX.begin();
1075 int candidateMinIdx = candidateMaxIdx - 1;
1077 if( candidateMinIdx < (
int) candidateStart
1078 || candidateMaxIdx >= (
int) candidateEnd
1079 || candidateMaxIdx >= (
int) dataX.size() )
1084 double leftX = dataX[candidateMinIdx];
1085 double rightX = dataX[candidateMaxIdx];
1087 if( leftX == rightX )
1090 double leftY = dataY[candidateMinIdx];
1091 double rightY = dataY[candidateMaxIdx];
1092 double value = leftY + ( rightY - leftY ) / ( rightX - leftX ) * (
m_coords.x - leftX );
1093 double distance = std::fabs( value - snapTargetY );
1098 bestSweep = sweepIdx;
1109 && sweepSize != std::numeric_limits<size_t>::max()
1112 size_t available =
static_cast<size_t>( sweepCount ) * sweepSize;
1114 if( available <= dataX.size() )
1119 startIdx =
static_cast<size_t>(
m_sweepIndex ) * sweepSize;
1120 endIdx = std::min( dataX.size(), startIdx + sweepSize );
1132 if( startIdx >= endIdx )
1138 auto beginIt = dataX.begin() + startIdx;
1139 auto endIt = dataX.begin() + endIdx;
1142 auto maxXIt = std::upper_bound( beginIt, endIt,
m_coords.x );
1143 int maxIdx = maxXIt - dataX.begin();
1144 int minIdx = maxIdx - 1;
1147 if( minIdx < (
int) startIdx || maxIdx >= (
int) endIdx || maxIdx >= (
int) dataX.size() )
1156 const double leftX = dataX[minIdx];
1157 const double rightX = dataX[maxIdx];
1158 const double leftY = dataY[minIdx];
1159 const double rightY = dataY[maxIdx];
1162 m_coords.y = leftY + ( rightY - leftY ) / ( rightX - leftX ) * (
m_coords.x - leftX );
1171 return wxString::Format(
_(
"%d" ),
id );
1174 return wxEmptyString;
1192 wxQueueEvent( aWindow.GetParent(),
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1210 const bool hasY = std::isfinite(
m_coords.y );
1222 wxColour fg = aWindow.GetForegroundColour();
1226 if( cursorColor.
Distance( textColor ) < 0.66 )
1229 pen.SetColour( cursorColor.
ToColour() );
1230 pen.SetStyle(
m_continuous ? wxPENSTYLE_SOLID : wxPENSTYLE_LONG_DASH );
1233 if( hasY && topPx < cursorPos.y && cursorPos.y < bottomPx )
1234 aDC.DrawLine( leftPx, cursorPos.y, rightPx, cursorPos.y );
1236 if( leftPx < cursorPos.x && cursorPos.x < rightPx )
1238 aDC.DrawLine( cursorPos.x, topPx, cursorPos.x, bottomPx );
1240 wxString
id =
getID();
1241 wxSize size = aDC.GetTextExtent( wxS(
"M" ) );
1242 wxRect textRect( wxPoint( cursorPos.x + 1 - size.x / 2, topPx - 4 - size.y ), size );
1253 size.y = ( size.y / 2 ) * 2;
1254 poly[0] = { cursorPos.x - 1 - size.y / 2, topPx - size.y };
1255 poly[1] = { cursorPos.x + 1 + size.y / 2, topPx - size.y };
1256 poly[2] = { cursorPos.x, topPx };
1258 brush.SetStyle( wxBRUSHSTYLE_SOLID );
1259 brush.SetColour(
m_trace->GetTraceColour() );
1260 aDC.SetBrush( brush );
1261 aDC.DrawPolygon( 3, poly );
1263 aDC.SetTextForeground( textColor.
ToColour() );
1264 aDC.DrawLabel(
id, textRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
1267 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
1270 const std::vector<wxString>& labels =
m_trace->GetMultiRunLabels();
1278 runLabel = wxString::Format(
_(
"Run %d" ),
m_sweepIndex + 1 );
1281 wxSize runSize = aDC.GetTextExtent( runLabel );
1282 int runX = textRect.GetRight() + 6;
1283 wxRect runRect( wxPoint( runX, textRect.y ), runSize );
1285 runRect.Inflate( 3, 1 );
1287 wxBrush labelBrush( aWindow.GetBackgroundColour() );
1288 wxPen labelPen( cursorColor.
ToColour() );
1290 aDC.SetPen( labelPen );
1291 aDC.SetBrush( labelBrush );
1292 aDC.DrawRectangle( runRect );
1293 aDC.SetTextForeground( cursorColor.
ToColour() );
1294 aDC.DrawLabel( runLabel, runRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
1306 bool nearX = std::isfinite(
m_coords.x )
1308 bool nearY = std::isfinite(
m_coords.y )
1311 return nearX || nearY;
1371 m_axis_y1->SetAxisMinMax( aLock, aMin, aMax );
1378 m_axis_y2->SetAxisMinMax( aLock, aMin, aMax );
1385 m_axis_y3->SetAxisMinMax( aLock, aMin, aMax );
1399 return wxEmptyString;
1410 return wxEmptyString;
1421 return wxEmptyString;
1432 return wxEmptyString;
1560 m_axis_y1->SetName(
_(
"Noise (V/√Hz)" ) );
1563 m_axis_y2->SetName(
_(
"Noise (A/√Hz)" ) );
1656 wxString sim_cmd =
m_plotTab->GetSimCommand().Lower();
1659 if( sim_cmd.StartsWith(
".dc", &rem ) )
1667 ch = rem.GetChar( 0 );
1686 m_axis_x->SetName(
_(
"Voltage (swept)" ) );
1697 m_axis_x->SetName(
_(
"Current (swept)" ) );
1708 m_axis_x->SetName(
_(
"Resistance (swept)" ) );
1719 m_axis_x->SetName(
_(
"Temperature (swept)" ) );
1737 m_axis_y1->SetName(
_(
"Voltage (measured)" ) );
1773 bool hasY1Traces =
false;
1774 bool hasY2Traces =
false;
1775 bool hasY3Traces =
false;
1781 if( !trace || trace->GetView() !=
this )
1800 bool visibilityChanged =
false;
1805 visibilityChanged =
true;
1811 visibilityChanged =
true;
1817 visibilityChanged =
true;
1823 visibilityChanged =
true;
1826 if( visibilityChanged )
1839 wxStringTokenizer tokenizer(
m_plotTab->GetSimCommand(),
" \t\r\n", wxTOKEN_STRTOK );
1840 wxString cmd = tokenizer.GetNextToken().Lower();
1842 wxASSERT( cmd == wxS(
".tran" ) );
1848 if( tokenizer.HasMoreTokens() )
1851 if( tokenizer.HasMoreTokens() )
1854 if( tokenizer.HasMoreTokens() )
1872 if( trace->GetView() ==
this )
1873 trace->UpdateScales();
1917 bool unresolved =
false;
1923 if( !smithTrace || smithTrace->
GetView() !=
this )
1928 if( traceZ0 <= 0.0 )
1930 else if( z0 == 0.0 )
1932 else if( traceZ0 != z0 )
1937 m_smithGrid->SetReferenceImpedance( mixed || unresolved ? 0.0 : z0 );
1962 double newZoom = std::clamp(
m_smithZoom * aFactor, 1.0, 50.0 );
1964 if( newZoom <= 1.0 )
2002 if( aEvent.GetWheelAxis() != wxMOUSE_WHEEL_VERTICAL || aEvent.GetWheelRotation() == 0 )
2006 SmithZoomAt( aEvent.GetPosition(), aEvent.GetWheelRotation() > 0 ? 1.2 : 1.0 / 1.2 );
2018 double factor = aEvent.GetMagnification() + 1.0;
2039 wxPoint pos = aEvent.GetPosition();
2065 wxPoint pos = aEvent.GetPosition();
2075 if( aEvent.MiddleIsDown() )
2103 wxPoint pos = aEvent.GetPosition();
2140 PopupMenu( menu, aEvent.GetPosition() );
2152 switch( aEvent.GetId() )
2175 default: aEvent.Skip();
2181 SIM_TAB( aSimCommand, parent ),
2188 wxBoxSizer* outerSizer =
new wxBoxSizer( wxVERTICAL );
2193 wxBoxSizer* viewButtonsSizer =
new wxBoxSizer( wxHORIZONTAL );
2197 addViewButton->SetToolTip(
_(
"Add a new signal view" ) );
2201 removeViewButton->SetToolTip(
_(
"Remove the last signal view" ) );
2203 viewButtonsSizer->Add( addViewButton, 0, wxLEFT, 5 );
2204 viewButtonsSizer->Add( removeViewButton, 0, wxLEFT, 30 );
2206 addViewButton->Bind( wxEVT_BUTTON,
2207 [
this]( wxCommandEvent& aEvent )
2212 removeViewButton->Bind( wxEVT_BUTTON,
2213 [
this]( wxCommandEvent& aEvent )
2215 const std::vector<SIM_VIEW*>& views =
GetViews();
2217 if( !views.empty() )
2221 outerSizer->Add( viewButtonsSizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 3 );
2223 SetSizer( outerSizer );
2275 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_VIEWS_CHANGED ) );
2295 std::vector<TRACE*> plottedHere;
2299 trace->ClearYScaleViewIf( aView );
2301 if( trace->GetView() == aView )
2302 plottedHere.push_back( trace );
2306 for(
TRACE* trace : plottedHere )
2322 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_VIEWS_CHANGED ) );
2330 for(
size_t i = 0; i <
m_views.size(); ++i )
2348 double pos = aSource->
GetPosX();
2354 if( view == aSource )
2357 view->SetScaleX( scaleX );
2358 view->SetXRange( pos, desiredMax, desiredMin );
2369 return view ? view->
GetLabelX() : wxString( wxS(
"" ) );
2376 return view ? view->
GetUnitsX() : wxString( wxS(
"" ) );
2390 view->ShowGrid( aEnable );
2404 view->ShowLegend( aEnable );
2425 view->SetLegendPosition( aPosition );
2434 return view ? view->
GetLabelY1() : wxString( wxS(
"" ) );
2441 return view ? view->
GetLabelY2() : wxString( wxS(
"" ) );
2448 return view ? view->
GetLabelY3() : wxString( wxS(
"" ) );
2455 return view ? view->
GetUnitsY1() : wxString( wxS(
"" ) );
2462 return view ? view->
GetUnitsY2() : wxString( wxS(
"" ) );
2469 return view ? view->
GetUnitsY3() : wxString( wxS(
"" ) );
2476 return view && view->
GetY1Scale( aMin, aMax );
2483 return view && view->
GetY2Scale( aMin, aMax );
2490 return view && view->
GetY3Scale( aMin, aMax );
2497 view->SetY1Scale( aLock, aMin, aMax );
2504 view->SetY2Scale( aLock, aMin, aMax );
2511 view->SetY3Scale( aLock, aMin, aMax );
2518 view->EnsureThirdYAxisExists();
2530 view->UpdateSmithGridColor();
2548 view->SetSmithChart( aEnable );
2555 view->UpdateSmithReferenceImpedance();
2569 return view ? view->
GetSmithPan() : wxRealPoint( 0.0, 0.0 );
2577 view->ResetSmithView();
2586 view->SetSmithView( aZoom, aPanX, aPanY );
2593 view->SmithZoomAt( aPos, aFactor );
2600 view->SmithPanBy( aDelta );
2617 wxPenStyle penStyle;
2620 penStyle = wxPENSTYLE_SOLID;
2622 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
2624 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
2626 penStyle = wxPENSTYLE_SOLID;
2637 if( !trace && aView )
2643 bool hasVoltageTraces =
false;
2645 for(
const auto& [
id, candidate] :
m_traces )
2647 if( candidate->GetView() == aView && ( candidate->GetType() &
SPT_VOLTAGE ) )
2649 hasVoltageTraces =
true;
2654 if( !hasVoltageTraces )
2686 size_t aSweepSize,
bool aIsMultiRun,
const std::vector<wxString>& aMultiRunLabels )
2698 if( aX.size() > 0 && aX[0] == 0 )
2700 aX.erase( aX.begin() );
2701 aY.erase( aY.begin() );
2709 for(
double& pt : aY )
2710 pt = pt * 180.0 /
M_PI;
2714 for(
double& pt : aY )
2729 trace->
SetScale(
nullptr,
nullptr );
2748 cursor->UpdateForNewData();
2759 if( trace == aTrace )
2771 view->DelLayer(
cursor,
true );
2774 view->DelLayer( aTrace,
true,
true );
2775 view->UpdateAxisVisibility();
2776 view->UpdateSmithReferenceImpedance();
2812 if( !freqs.empty() )
2813 cursor->SetCoordX( freqs[freqs.size() * ( aCursorId == 1 ? 2 : 3 ) / 5] );
2825 cursor->SetName( aSignalName );
2831 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
2839 aTrace->
SetCursor( aCursorId,
nullptr );
2842 view->DelLayer(
cursor,
true );
2845 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
2853 view->ResetScales( aIncludeX );
2858 std::map<std::pair<SIM_VIEW*, int>, std::vector<TRACE*>> groups;
2862 if(
SIM_VIEW* view = trace->GetView() )
2863 groups[{ trace->GetYScaleView(), view->GetAxisSlot( trace ) }].push_back( trace );
2866 for(
const auto& [key, traces] : groups )
2868 std::vector<SIM_VIEW*> actualViews;
2870 for(
TRACE* trace : traces )
2874 if( std::find( actualViews.begin(), actualViews.end(), view ) == actualViews.end() )
2875 actualViews.push_back( view );
2878 if( actualViews.size() <= 1 )
2881 bool haveRange =
false;
2885 for(
TRACE* trace : traces )
2887 double lo = trace->GetMinY();
2888 double hi = trace->GetMaxY();
2890 if( !std::isfinite( lo ) || !std::isfinite( hi ) )
2901 minV = std::min( minV, lo );
2902 maxV = std::max( maxV, hi );
2909 for(
SIM_VIEW* view : actualViews )
2911 if(
mpScaleY* axis = view->GetAxisBySlot( key.second ) )
2912 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
The saved/restored pan and zoom track the default view.
void UpdateSmithReferenceImpedance()
Refresh the grid z0 from the shown Smith traces, on every view.
wxString GetLabelY1() const
mpWindow * GetPlotWin() const
Getter for the default view's math plot window (back-compat for zoom undo/redo, export).
void SetSmithView(double aZoom, double aPanX, double aPanY)
Restore a saved view, values are validated and clamped.
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)
Mirror aSource's current X range onto every other view of this tab.
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)
Smith mode is tab-wide: every view of the tab shows a Smith chart while it is on.
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
The view new traces default to, and legacy (single-view) settings apply to.
SIM_VIEW * AddView()
Add a new (empty) view, stacked below the existing ones.
wxString GetUnitsForTrace(TRACE *aTrace) const
Get the display units (e.g. "V", "A", "dB") for a given trace's own view.
void UpdateTraceStyle(TRACE *trace)
Update trace line style.
void SetLegendPosition(const wxPoint &aPosition)
TRACE * GetOrAddTrace(const wxString &aVectorName, int aType, SIM_VIEW *aView)
Get (or create, on aView) the TRACE for a signal.
void ResetScales(bool aIncludeX)
Reset scale ranges to fit the current traces, on every view.
void UpdatePlotColors()
Update plot colors.
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)
Turn on/off the cursor for a particular trace.
int GetViewIndex(SIM_VIEW *aView) const
wxString GetUnitsX() const
void OnLanguageChanged() override
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)
Remove a view.
void SmithPanBy(const wxPoint &aDelta)
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)
bool GetY2Scale(double *aMin, double *aMax) const
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
Mirrors this view's X range onto every other view of the same tab.
void updateAxes(int aNewTraceType=SIM_TRACE_TYPE::SPT_UNKNOWN)
Create/Ensure axes are available for plotting.
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 display units (e.g. "V", "A", "dB") for a given trace plotted on this view.
void UpdateSmithGridColor()
Re-read the Smith grid pen from the tab's color theme.
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
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)
bool IsLegendShown() const
mpScaleY * GetAxisBySlot(int aSlot) const
Get the Y-axis scale object for a given slot (1, 2 or 3), or nullptr if not created yet.
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()
Refresh the grid z0 from the Smith traces shown on this view.
void onSmithLeftDown(wxMouseEvent &aEvent)
void ResetScales(bool aIncludeX)
Reset the Y (and, if requested, X) scale ranges to fit this view's own traces.
int GetAxisSlot(TRACE *aTrace) const
Get the Y-axis slot (1, 2 or 3) a trace of this type is plotted on.
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)
Construct the plot axes for DC simulation plot.
Cursor that snaps along a Smith chart locus, keyed by frequency.
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.
Overlay layer drawing the Smith chart grid (constant resistance and reactance circles)
static bool GetChartView(mpWindow &aWindow, double aZoom, const wxRealPoint &aPan, SMITH_VIEW &aView)
Chart placement including pan/zoom.
void Plot(wxDC &aDC, mpWindow &aWindow) override
Plot given view of layer to the given device context.
Reflection coefficient locus, Re in X and Im in Y, drawn on the Smith chart.
void Plot(wxDC &aDC, mpWindow &aWindow) override
Layer plot handler.
const std::vector< double > & GetFrequencies() const
double GetReferenceImpedance() const
Helper class to handle Spice way of expressing values (e.g. 10.5 Meg)
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)
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
The SIM_VIEW this trace is currently plotted on (nullptr if not plotted anywhere).
void SetView(SIM_VIEW *aView)
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.
bool m_continuous
Specify if the layer will be plotted as a continuous line or a set of points.
bool m_visible
Toggles layer visibility.
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)
double SeriesInductance(double aReactance, double aFreq)
Series equivalent element of a reactance at one frequency, henries for aReactance > 0,...
double VSWR(double aGammaMag)
wxRealPoint ZoomAboutPoint(const SMITH_VIEW &aView, const wxPoint &aPos, double aNewZoom)
Pan that keeps the gamma point under aPos fixed when the view zooms to aNewZoom.
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 s...
double ReturnLoss(double aGammaMag)
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)
SIM_TRACE_TYPE
Possible trace types.
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