2020-11-22 21:38:52 +08:00
# include " tracesmithchart.h "
2021-10-21 19:00:34 +08:00
2021-06-19 21:33:43 +08:00
# include "Marker/marker.h"
2020-10-23 03:12:33 +08:00
# include "preferences.h"
2020-11-06 20:05:09 +08:00
# include "ui_smithchartdialog.h"
2020-11-23 05:24:49 +08:00
# include "unit.h"
2021-04-13 02:15:38 +08:00
# include "QFileDialog"
2022-01-07 19:37:47 +08:00
# include "Util/util.h"
2022-03-03 19:28:59 +08:00
# include "appwindow.h"
2022-03-18 01:24:18 +08:00
# include "CustomWidgets/informationbox.h"
2020-08-31 04:03:41 +08:00
2021-10-21 19:00:34 +08:00
# include <QPainter>
# include <array>
# include <math.h>
# include <QDebug>
2022-01-08 07:46:17 +08:00
# include <QColorDialog>
2021-10-21 19:00:34 +08:00
2020-08-31 04:03:41 +08:00
using namespace std ;
TraceSmithChart : : TraceSmithChart ( TraceModel & model , QWidget * parent )
2020-11-06 20:05:09 +08:00
: TracePlot ( model , parent )
2020-08-31 04:03:41 +08:00
{
2020-11-06 20:05:09 +08:00
limitToSpan = true ;
2022-03-17 19:53:13 +08:00
limitToEdge = true ;
2022-01-02 03:04:43 +08:00
edgeReflection = 1.0 ;
2022-03-18 01:24:18 +08:00
Z0 = 50.0 ;
2020-11-06 20:05:09 +08:00
initializeTraceInfo ( ) ;
}
2020-12-05 06:49:52 +08:00
nlohmann : : json TraceSmithChart : : toJSON ( )
{
nlohmann : : json j ;
j [ " limit_to_span " ] = limitToSpan ;
2022-01-02 03:04:43 +08:00
j [ " limit_to_edge " ] = limitToEdge ;
j [ " edge_reflection " ] = edgeReflection ;
2022-03-18 01:24:18 +08:00
j [ " Z0 " ] = Z0 ;
2020-12-05 06:49:52 +08:00
nlohmann : : json jtraces ;
for ( auto t : traces ) {
if ( t . second ) {
jtraces . push_back ( t . first - > toHash ( ) ) ;
}
}
j [ " traces " ] = jtraces ;
2022-01-08 07:46:17 +08:00
nlohmann : : json jlines ;
for ( auto line : constantLines ) {
jlines . push_back ( line . toJSON ( ) ) ;
}
j [ " constantLines " ] = jlines ;
2020-12-05 06:49:52 +08:00
return j ;
}
void TraceSmithChart : : fromJSON ( nlohmann : : json j )
{
limitToSpan = j . value ( " limit_to_span " , true ) ;
2022-01-02 03:04:43 +08:00
limitToEdge = j . value ( " limit_to_edge " , false ) ;
edgeReflection = j . value ( " edge_reflection " , 1.0 ) ;
2022-03-18 01:24:18 +08:00
Z0 = j . value ( " Z0 " , 50.0 ) ;
2020-12-05 06:49:52 +08:00
for ( unsigned int hash : j [ " traces " ] ) {
// attempt to find the traces with this hash
bool found = false ;
for ( auto t : model . getTraces ( ) ) {
if ( t - > toHash ( ) = = hash ) {
enableTrace ( t , true ) ;
found = true ;
break ;
}
}
if ( ! found ) {
qWarning ( ) < < " Unable to find trace with hash " < < hash ;
}
}
2022-01-08 07:46:17 +08:00
if ( j . contains ( " constantLines " ) ) {
for ( auto jline : j [ " constantLines " ] ) {
SmithChartConstantLine line ;
line . fromJSON ( jline ) ;
constantLines . push_back ( line ) ;
}
}
2020-12-05 06:49:52 +08:00
}
2022-01-02 03:04:43 +08:00
void TraceSmithChart : : wheelEvent ( QWheelEvent * event )
{
// most mousewheel have 15 degree increments, the reported delta is in 1/8th degree -> 120
auto increment = event - > angleDelta ( ) . y ( ) / 120.0 ;
// round toward bigger step in case of special higher resolution mousewheel
int steps = increment > 0 ? ceil ( increment ) : floor ( increment ) ;
constexpr double zoomfactor = 1.1 ;
auto zoom = pow ( zoomfactor , steps ) ;
edgeReflection / = zoom ;
triggerReplot ( ) ;
}
2020-11-06 20:05:09 +08:00
void TraceSmithChart : : axisSetupDialog ( )
{
auto dialog = new QDialog ( ) ;
auto ui = new Ui : : SmithChartDialog ( ) ;
ui - > setupUi ( dialog ) ;
if ( limitToSpan ) {
2022-01-02 03:04:43 +08:00
ui - > displayModeFreq - > setCurrentIndex ( 1 ) ;
} else {
ui - > displayModeFreq - > setCurrentIndex ( 0 ) ;
}
if ( limitToEdge ) {
ui - > displayModeImp - > setCurrentIndex ( 1 ) ;
2020-11-06 20:05:09 +08:00
} else {
2022-01-02 03:04:43 +08:00
ui - > displayModeImp - > setCurrentIndex ( 0 ) ;
2020-11-06 20:05:09 +08:00
}
2022-01-02 03:04:43 +08:00
ui - > zoomReflection - > setPrecision ( 3 ) ;
ui - > zoomFactor - > setPrecision ( 3 ) ;
ui - > zoomReflection - > setValue ( edgeReflection ) ;
ui - > zoomFactor - > setValue ( 1.0 / edgeReflection ) ;
2022-01-08 07:46:17 +08:00
2022-03-18 01:24:18 +08:00
ui - > impedance - > setUnit ( " Ω " ) ;
ui - > impedance - > setPrecision ( 3 ) ;
ui - > impedance - > setValue ( Z0 ) ;
2022-01-08 07:46:17 +08:00
auto model = new SmithChartContantLineModel ( * this ) ;
ui - > lineTable - > setModel ( model ) ;
ui - > lineTable - > setItemDelegateForColumn ( SmithChartContantLineModel : : ColIndexType , new SmithChartTypeDelegate ) ;
ui - > lineTable - > setItemDelegateForColumn ( SmithChartContantLineModel : : ColIndexParam , new SmithChartParamDelegate ) ;
2020-11-06 20:05:09 +08:00
connect ( ui - > buttonBox , & QDialogButtonBox : : accepted , [ = ] ( ) {
2022-01-02 03:04:43 +08:00
limitToSpan = ui - > displayModeFreq - > currentIndex ( ) = = 1 ;
limitToEdge = ui - > displayModeImp - > currentIndex ( ) = = 1 ;
2020-11-06 20:05:09 +08:00
triggerReplot ( ) ;
} ) ;
2022-01-02 03:04:43 +08:00
connect ( ui - > zoomFactor , & SIUnitEdit : : valueChanged , [ = ] ( ) {
edgeReflection = 1.0 / ui - > zoomFactor - > value ( ) ;
ui - > zoomReflection - > setValueQuiet ( edgeReflection ) ;
} ) ;
connect ( ui - > zoomReflection , & SIUnitEdit : : valueChanged , [ = ] ( ) {
edgeReflection = ui - > zoomReflection - > value ( ) ;
ui - > zoomFactor - > setValueQuiet ( 1.0 / edgeReflection ) ;
} ) ;
2022-03-18 01:24:18 +08:00
connect ( ui - > impedance , & SIUnitEdit : : valueChanged , [ = ] ( ) {
Z0 = ui - > impedance - > value ( ) ;
for ( auto t : traces ) {
if ( t . second ) {
checkIfStillSupported ( t . first ) ;
}
}
ui - > impedance - > setValueQuiet ( Z0 ) ;
} ) ;
2022-01-08 07:46:17 +08:00
connect ( ui - > lineTable , & QTableView : : clicked , [ = ] ( const QModelIndex & index ) {
if ( index . column ( ) = = SmithChartContantLineModel : : ColIndexColor ) {
auto line = & constantLines [ index . row ( ) ] ;
auto newColor = QColorDialog : : getColor ( line - > getColor ( ) , parentWidget ( ) , " Select color " , QColorDialog : : DontUseNativeDialog ) ;
if ( newColor . isValid ( ) ) {
line - > setColor ( newColor ) ;
emit model - > dataChanged ( index , index ) ;
triggerReplot ( ) ;
}
}
} ) ;
auto updatePersistentEditors = [ = ] ( ) {
for ( unsigned int i = 0 ; i < constantLines . size ( ) ; i + + ) {
ui - > lineTable - > openPersistentEditor ( model - > index ( i , SmithChartContantLineModel : : ColIndexType ) ) ;
}
} ;
connect ( ui - > addLine , & QPushButton : : clicked , [ = ] ( ) {
model - > beginResetModel ( ) ;
constantLines . push_back ( SmithChartConstantLine ( ) ) ;
model - > endResetModel ( ) ;
updatePersistentEditors ( ) ;
} ) ;
connect ( ui - > removeLine , & QPushButton : : clicked , [ = ] ( ) {
auto selected = ui - > lineTable - > selectionModel ( ) - > selectedRows ( ) ;
// get indices of lines to delete
std : : vector < int > toDelete ;
for ( auto s : selected ) {
toDelete . push_back ( s . row ( ) ) ;
}
// delete starting with highest index (this makes sure that indices are not messed up after deleting an element
std : : sort ( toDelete . begin ( ) , toDelete . end ( ) ) ;
model - > beginResetModel ( ) ;
for ( auto i = toDelete . rbegin ( ) ; i ! = toDelete . rend ( ) ; i + + ) {
constantLines . erase ( constantLines . begin ( ) + * i ) ;
}
model - > endResetModel ( ) ;
updatePersistentEditors ( ) ;
} ) ;
updatePersistentEditors ( ) ;
2022-03-03 19:28:59 +08:00
if ( AppWindow : : showGUI ( ) ) {
dialog - > show ( ) ;
}
2020-08-31 04:03:41 +08:00
}
2022-01-02 03:04:43 +08:00
QPoint TraceSmithChart : : dataToPixel ( std : : complex < double > d )
{
return transform . map ( QPoint ( d . real ( ) * smithCoordMax * ( 1.0 / edgeReflection ) , - d . imag ( ) * smithCoordMax * ( 1.0 / edgeReflection ) ) ) ;
}
2020-11-22 21:38:52 +08:00
QPoint TraceSmithChart : : dataToPixel ( Trace : : Data d )
2020-08-31 04:03:41 +08:00
{
2020-11-25 23:47:29 +08:00
if ( d . x < sweep_fmin | | d . x > sweep_fmax ) {
2020-11-22 21:38:52 +08:00
return QPoint ( ) ;
2020-08-31 04:03:41 +08:00
}
2022-03-18 01:24:18 +08:00
return dataToPixel ( d . y ) ; }
2020-08-31 04:03:41 +08:00
2020-11-23 05:24:49 +08:00
std : : complex < double > TraceSmithChart : : pixelToData ( QPoint p )
{
auto data = transform . inverted ( ) . map ( QPointF ( p ) ) ;
2022-01-02 03:04:43 +08:00
return complex < double > ( data . x ( ) / smithCoordMax * edgeReflection , - data . y ( ) / smithCoordMax * edgeReflection ) ;
2020-11-23 05:24:49 +08:00
}
2021-06-19 21:33:43 +08:00
QPoint TraceSmithChart : : markerToPixel ( Marker * m )
2020-11-23 04:25:41 +08:00
{
QPoint ret = QPoint ( ) ;
2020-11-29 05:34:40 +08:00
// if(!m->isTimeDomain()) {
2020-11-23 04:25:41 +08:00
if ( m - > getPosition ( ) > = sweep_fmin & & m - > getPosition ( ) < = sweep_fmax ) {
auto d = m - > getData ( ) ;
2022-01-02 03:04:43 +08:00
ret = dataToPixel ( d ) ;
2020-11-23 04:25:41 +08:00
}
2020-11-29 05:34:40 +08:00
// }
2020-11-23 04:25:41 +08:00
return ret ;
}
2021-05-15 02:34:23 +08:00
double TraceSmithChart : : nearestTracePoint ( Trace * t , QPoint pixel , double * distance )
2020-11-23 04:25:41 +08:00
{
double closestDistance = numeric_limits < double > : : max ( ) ;
2021-10-16 21:42:31 +08:00
double closestXpos = 0 ;
2022-01-07 19:37:47 +08:00
unsigned int closestIndex = 0 ;
2021-10-16 21:42:31 +08:00
auto samples = t - > size ( ) ;
for ( unsigned int i = 0 ; i < samples ; i + + ) {
2020-11-23 04:25:41 +08:00
auto data = t - > sample ( i ) ;
auto plotPoint = dataToPixel ( data ) ;
if ( plotPoint . isNull ( ) ) {
// destination point outside of currently displayed range
continue ;
}
auto diff = plotPoint - pixel ;
unsigned int distance = diff . x ( ) * diff . x ( ) + diff . y ( ) * diff . y ( ) ;
if ( distance < closestDistance ) {
closestDistance = distance ;
2021-10-16 21:42:31 +08:00
closestXpos = t - > sample ( i ) . x ;
2022-01-07 19:37:47 +08:00
closestIndex = i ;
}
}
closestDistance = sqrt ( closestDistance ) ;
if ( closestIndex > 0 ) {
auto l1 = dataToPixel ( t - > sample ( closestIndex - 1 ) ) ;
auto l2 = dataToPixel ( t - > sample ( closestIndex ) ) ;
double ratio ;
auto distance = Util : : distanceToLine ( pixel , l1 , l2 , nullptr , & ratio ) ;
if ( distance < closestDistance ) {
closestDistance = distance ;
closestXpos = t - > sample ( closestIndex - 1 ) . x + ( t - > sample ( closestIndex ) . x - t - > sample ( closestIndex - 1 ) . x ) * ratio ;
}
}
if ( closestIndex < t - > size ( ) - 1 ) {
auto l1 = dataToPixel ( t - > sample ( closestIndex ) ) ;
auto l2 = dataToPixel ( t - > sample ( closestIndex + 1 ) ) ;
double ratio ;
auto distance = Util : : distanceToLine ( pixel , l1 , l2 , nullptr , & ratio ) ;
if ( distance < closestDistance ) {
closestDistance = distance ;
closestXpos = t - > sample ( closestIndex ) . x + ( t - > sample ( closestIndex + 1 ) . x - t - > sample ( closestIndex ) . x ) * ratio ;
2020-11-23 04:25:41 +08:00
}
}
2021-05-15 02:34:23 +08:00
if ( distance ) {
* distance = closestDistance ;
}
2021-10-16 21:42:31 +08:00
return closestXpos ;
2020-11-23 04:25:41 +08:00
}
2022-03-16 21:45:59 +08:00
bool TraceSmithChart : : markerVisible ( double x )
2021-05-14 22:18:43 +08:00
{
if ( limitToSpan ) {
if ( x > = sweep_fmin & & x < = sweep_fmax ) {
return true ;
} else {
return false ;
}
} else {
// complete traces visible
return true ;
}
}
2022-03-18 01:24:18 +08:00
bool TraceSmithChart : : configureForTrace ( Trace * t )
{
if ( dropSupported ( t ) ) {
Z0 = t - > getReferenceImpedance ( ) ;
for ( auto t : traces ) {
if ( t . second & & t . first - > getReferenceImpedance ( ) ! = Z0 ) {
enableTrace ( t . first , false ) ;
}
}
return true ;
}
return false ;
}
2020-11-22 07:41:42 +08:00
void TraceSmithChart : : draw ( QPainter & p ) {
auto pref = Preferences : : getInstance ( ) ;
2020-08-31 04:03:41 +08:00
2022-01-02 03:04:43 +08:00
// translate coordinate system so that the smith chart sits in the origin and has a size of 1
2020-11-22 07:41:42 +08:00
auto w = p . window ( ) ;
2020-11-22 21:38:52 +08:00
p . save ( ) ;
2020-11-22 07:41:42 +08:00
p . translate ( w . width ( ) / 2 , w . height ( ) / 2 ) ;
auto scale = qMin ( w . height ( ) , w . width ( ) ) / ( 2.0 * smithCoordMax ) ;
p . scale ( scale , scale ) ;
2020-08-31 04:03:41 +08:00
2020-11-22 07:41:42 +08:00
transform = p . transform ( ) ;
2022-01-02 03:04:43 +08:00
p . restore ( ) ;
2022-01-08 07:46:17 +08:00
auto drawArc = [ & ] ( SmithChartArc a ) {
2022-01-02 03:04:43 +08:00
a . constrainToCircle ( QPointF ( 0 , 0 ) , edgeReflection ) ;
auto topleft = dataToPixel ( complex < double > ( a . center . x ( ) - a . radius , a . center . y ( ) - a . radius ) ) ;
auto bottomright = dataToPixel ( complex < double > ( a . center . x ( ) + a . radius , a . center . y ( ) + a . radius ) ) ;
a . startAngle * = 5760 / ( 2 * M_PI ) ;
a . spanAngle * = 5760 / ( 2 * M_PI ) ;
p . drawArc ( QRect ( topleft , bottomright ) , a . startAngle , a . spanAngle ) ;
} ;
2020-10-23 03:12:33 +08:00
2020-08-31 04:03:41 +08:00
// Outer circle
2021-07-10 19:12:30 +08:00
auto pen = QPen ( pref . Graphs . Color . axis ) ;
2020-11-22 07:41:42 +08:00
pen . setCosmetic ( true ) ;
p . setPen ( pen ) ;
2022-01-08 07:46:17 +08:00
drawArc ( SmithChartArc ( QPointF ( 0 , 0 ) , edgeReflection , 0 , 2 * M_PI ) ) ;
2020-08-31 04:03:41 +08:00
constexpr int Circles = 6 ;
2021-10-18 06:37:40 +08:00
pen = QPen ( pref . Graphs . Color . Ticks . divisions , 0.5 , Qt : : DashLine ) ;
2020-11-22 07:41:42 +08:00
pen . setCosmetic ( true ) ;
p . setPen ( pen ) ;
2022-01-02 03:04:43 +08:00
for ( int i = 1 ; i < Circles * 2 ; i + + ) {
auto radius = ( double ) i / Circles ;
2022-01-08 07:46:17 +08:00
drawArc ( SmithChartArc ( QPointF ( 1.0 - radius , 0.0 ) , radius , 0 , 2 * M_PI ) ) ;
drawArc ( SmithChartArc ( QPointF ( 1.0 + radius , 0.0 ) , radius , 0 , 2 * M_PI ) ) ;
2020-08-31 04:03:41 +08:00
}
2022-01-02 03:04:43 +08:00
p . drawLine ( dataToPixel ( complex < double > ( edgeReflection , 0 ) ) , dataToPixel ( complex < double > ( - edgeReflection , 0 ) ) ) ;
2020-08-31 04:03:41 +08:00
constexpr std : : array < double , 5 > impedanceLines = { 10 , 25 , 50 , 100 , 250 } ;
for ( auto z : impedanceLines ) {
2022-03-18 01:24:18 +08:00
z / = Z0 ;
2022-01-02 03:04:43 +08:00
auto radius = 1.0 / z ;
2022-01-08 07:46:17 +08:00
drawArc ( SmithChartArc ( QPointF ( 1.0 , radius ) , radius , 0 , 2 * M_PI ) ) ;
drawArc ( SmithChartArc ( QPointF ( 1.0 , - radius ) , radius , 0 , 2 * M_PI ) ) ;
}
// draw custom constant parameter lines
for ( auto line : constantLines ) {
pen = QPen ( line . getColor ( ) , pref . Graphs . lineWidth ) ;
pen . setCosmetic ( true ) ;
p . setPen ( pen ) ;
2022-03-18 01:24:18 +08:00
for ( auto arc : line . getArcs ( Z0 ) ) {
2022-01-08 07:46:17 +08:00
drawArc ( arc ) ;
}
2020-08-31 04:03:41 +08:00
}
for ( auto t : traces ) {
if ( ! t . second ) {
// trace not enabled in plot
continue ;
}
auto trace = t . first ;
if ( ! trace - > isVisible ( ) ) {
// trace marked invisible
continue ;
}
2021-12-06 01:26:32 +08:00
pen = QPen ( trace - > color ( ) , pref . Graphs . lineWidth ) ;
2020-11-22 07:41:42 +08:00
pen . setCosmetic ( true ) ;
p . setPen ( pen ) ;
2020-08-31 04:03:41 +08:00
int nPoints = trace - > size ( ) ;
for ( int i = 1 ; i < nPoints ; i + + ) {
2020-11-06 20:05:09 +08:00
auto last = trace - > sample ( i - 1 ) ;
auto now = trace - > sample ( i ) ;
2021-07-10 04:26:44 +08:00
if ( limitToSpan & & ( trace - > getDataType ( ) = = Trace : : DataType : : Frequency ) & & ( last . x < sweep_fmin | | now . x > sweep_fmax ) ) {
2020-11-06 20:05:09 +08:00
continue ;
}
2020-11-25 23:47:29 +08:00
if ( isnan ( now . y . real ( ) ) ) {
2020-08-31 04:03:41 +08:00
break ;
}
2022-01-02 03:04:43 +08:00
if ( limitToEdge & & ( abs ( last . y ) > edgeReflection | | abs ( now . y ) > edgeReflection ) ) {
// outside of visible area
continue ;
}
2020-08-31 04:03:41 +08:00
// scale to size of smith diagram
2022-01-02 03:04:43 +08:00
auto p1 = dataToPixel ( last ) ;
auto p2 = dataToPixel ( now ) ;
2020-08-31 04:03:41 +08:00
// draw line
2022-01-02 03:04:43 +08:00
p . drawLine ( p1 , p2 ) ;
2020-08-31 04:03:41 +08:00
}
2020-09-12 05:07:15 +08:00
if ( trace - > size ( ) > 0 ) {
// only draw markers if the trace has at least one point
auto markers = t . first - > getMarkers ( ) ;
for ( auto m : markers ) {
2020-11-29 05:34:40 +08:00
// if (m->isTimeDomain()) {
// continue;
// }
2020-11-23 04:25:41 +08:00
if ( limitToSpan & & ( m - > getPosition ( ) < sweep_fmin | | m - > getPosition ( ) > sweep_fmax ) ) {
2020-11-06 20:05:09 +08:00
continue ;
}
2020-12-05 19:59:23 +08:00
if ( m - > getPosition ( ) < trace - > minX ( ) | | m - > getPosition ( ) > trace - > maxX ( ) ) {
// marker not in trace range
continue ;
}
2020-09-12 05:07:15 +08:00
auto coords = m - > getData ( ) ;
2022-01-02 03:04:43 +08:00
if ( limitToEdge & & abs ( coords ) > edgeReflection ) {
// outside of visible area
continue ;
}
auto point = dataToPixel ( coords ) ;
2020-09-12 05:07:15 +08:00
auto symbol = m - > getSymbol ( ) ;
2022-01-02 03:04:43 +08:00
p . drawPixmap ( point . x ( ) - symbol . width ( ) / 2 , point . y ( ) - symbol . height ( ) , symbol ) ;
2020-09-12 05:07:15 +08:00
}
2020-08-31 04:03:41 +08:00
}
}
2020-11-22 21:38:52 +08:00
if ( dropPending ) {
2022-01-02 03:04:43 +08:00
// TODO adjust coords due to shifted restore
2020-11-22 21:38:52 +08:00
p . setOpacity ( 0.5 ) ;
p . setBrush ( Qt : : white ) ;
p . setPen ( Qt : : white ) ;
p . drawEllipse ( - smithCoordMax , - smithCoordMax , 2 * smithCoordMax , 2 * smithCoordMax ) ;
auto font = p . font ( ) ;
font . setPixelSize ( 20 ) ;
p . setFont ( font ) ;
p . setOpacity ( 1.0 ) ;
p . setPen ( Qt : : white ) ;
auto text = " Drop here to add \n " + dropTrace - > name ( ) + " \n to Smith chart " ;
p . drawText ( p . window ( ) , Qt : : AlignCenter , text ) ;
} else {
}
}
2022-03-18 01:24:18 +08:00
void TraceSmithChart : : traceDropped ( Trace * t , QPoint position )
{
if ( ! supported ( t ) & & dropSupported ( t ) ) {
// needs to switch to a different domain for the graph
if ( ! InformationBox : : AskQuestion ( " Reference impedance change " , " You dropped a trace that is not supported with the currently selected reference impedance. "
" Do you want to remove all traces and change the graph to the correct reference imppedance? " , true , " ReferenceImpedanceChangeRequest " ) ) {
// user declined to change domain, to not add change impedance
return ;
}
// attempt to configure for this trace
configureForTrace ( t ) ;
}
TracePlot : : traceDropped ( t , position ) ;
}
bool TraceSmithChart : : dropSupported ( Trace * t )
{
if ( ! t - > isReflection ( ) ) {
return false ;
}
switch ( t - > outputType ( ) ) {
case Trace : : DataType : : Frequency :
case Trace : : DataType : : Power :
return true ;
default :
return false ;
}
}
2020-11-23 05:24:49 +08:00
QString TraceSmithChart : : mouseText ( QPoint pos )
{
auto data = pixelToData ( pos ) ;
2022-01-02 03:04:43 +08:00
if ( abs ( data ) < = edgeReflection ) {
2022-03-18 01:24:18 +08:00
data = Z0 * ( 1.0 + data ) / ( 1.0 - data ) ;
2020-11-23 05:24:49 +08:00
auto ret = Unit : : ToString ( data . real ( ) , " " , " " , 3 ) ;
if ( data . imag ( ) > = 0 ) {
ret + = " + " ;
}
ret + = Unit : : ToString ( data . imag ( ) , " j " , " " , 3 ) ;
return ret ;
} else {
return QString ( ) ;
}
}
2020-11-06 20:05:09 +08:00
void TraceSmithChart : : updateContextMenu ( )
{
contextmenu - > clear ( ) ;
auto setup = new QAction ( " Setup... " , contextmenu ) ;
connect ( setup , & QAction : : triggered , this , & TraceSmithChart : : axisSetupDialog ) ;
contextmenu - > addAction ( setup ) ;
2021-05-15 02:34:23 +08:00
2020-11-06 20:05:09 +08:00
contextmenu - > addSeparator ( ) ;
2021-04-13 02:15:38 +08:00
auto image = new QAction ( " Save image... " , contextmenu ) ;
contextmenu - > addAction ( image ) ;
connect ( image , & QAction : : triggered , [ = ] ( ) {
auto filename = QFileDialog : : getSaveFileName ( nullptr , " Save plot image " , " " , " PNG image files (*.png) " , nullptr , QFileDialog : : DontUseNativeDialog ) ;
if ( filename . isEmpty ( ) ) {
// aborted selection
return ;
}
if ( filename . endsWith ( " .png " ) ) {
filename . chop ( 4 ) ;
}
filename + = " .png " ;
grab ( ) . save ( filename ) ;
} ) ;
2021-05-15 02:34:23 +08:00
auto createMarker = contextmenu - > addAction ( " Add marker here " ) ;
bool activeTraces = false ;
for ( auto t : traces ) {
if ( t . second ) {
activeTraces = true ;
break ;
}
}
if ( ! activeTraces ) {
createMarker - > setEnabled ( false ) ;
}
connect ( createMarker , & QAction : : triggered , [ = ] ( ) {
createMarkerAtPosition ( contextmenuClickpoint ) ;
} ) ;
contextmenu - > addSection ( " Traces " ) ;
// Populate context menu
for ( auto t : traces ) {
if ( ! supported ( t . first ) ) {
continue ;
}
auto action = new QAction ( t . first - > name ( ) , contextmenu ) ;
action - > setCheckable ( true ) ;
if ( t . second ) {
action - > setChecked ( true ) ;
}
connect ( action , & QAction : : toggled , [ = ] ( bool active ) {
enableTrace ( t . first , active ) ;
} ) ;
contextmenu - > addAction ( action ) ;
}
2021-04-13 02:15:38 +08:00
contextmenu - > addSeparator ( ) ;
2020-11-06 20:05:09 +08:00
auto close = new QAction ( " Close " , contextmenu ) ;
contextmenu - > addAction ( close ) ;
connect ( close , & QAction : : triggered , [ = ] ( ) {
markedForDeletion = true ;
} ) ;
}
2020-08-31 04:03:41 +08:00
bool TraceSmithChart : : supported ( Trace * t )
{
2022-03-18 01:24:18 +08:00
if ( t - > getReferenceImpedance ( ) ! = Z0 ) {
2020-08-31 04:03:41 +08:00
return false ;
}
2022-03-18 01:24:18 +08:00
return dropSupported ( t ) ;
2020-08-31 04:03:41 +08:00
}
2022-01-02 03:04:43 +08:00
2022-01-08 07:46:17 +08:00
SmithChartArc : : SmithChartArc ( QPointF center , double radius , double startAngle , double spanAngle )
2022-01-02 03:04:43 +08:00
: center ( center ) ,
radius ( radius ) ,
startAngle ( startAngle ) ,
spanAngle ( spanAngle )
{
}
2022-01-08 07:46:17 +08:00
void SmithChartArc : : constrainToCircle ( QPointF center , double radius )
2022-01-02 03:04:43 +08:00
{
// check if arc/circle intersect
auto centerDiff = this - > center - center ;
auto centerDistSquared = centerDiff . x ( ) * centerDiff . x ( ) + centerDiff . y ( ) * centerDiff . y ( ) ;
if ( centerDistSquared > = ( radius + this - > radius ) * ( radius + this - > radius ) ) {
// arc completely outside of constraining circle
spanAngle = 0.0 ;
return ;
} else if ( centerDistSquared < = ( radius - this - > radius ) * ( radius - this - > radius ) ) {
if ( radius > = this - > radius ) {
// arc completely in constraining circle, do nothing
return ;
} else {
// arc completely outside of circle
spanAngle = 0.0 ;
return ;
}
} else {
// there are intersections between the arc and the circle. Calculate points according to https://stackoverflow.com/questions/3349125/circle-circle-intersection-points
auto distance = sqrt ( centerDistSquared ) ;
auto a = ( this - > radius * this - > radius - radius * radius + distance * distance ) / ( 2 * distance ) ;
auto h = sqrt ( this - > radius * this - > radius - a * a ) ;
auto intersectMiddle = this - > center + a * ( center - this - > center ) / distance ;
auto rotatedCenterDiff = center - this - > center ;
swap ( rotatedCenterDiff . rx ( ) , rotatedCenterDiff . ry ( ) ) ;
rotatedCenterDiff . setY ( - rotatedCenterDiff . y ( ) ) ;
auto intersect1 = intersectMiddle + h * rotatedCenterDiff / distance ;
auto intersect2 = intersectMiddle - h * rotatedCenterDiff / distance ;
// got intersection points, convert into angles from arc center
auto wrapAngle = [ ] ( double angle ) - > double {
double ret = fmod ( angle , 2 * M_PI ) ;
if ( ret < 0 ) {
ret + = 2 * M_PI ;
}
return ret ;
} ;
auto angle1 = wrapAngle ( atan2 ( ( intersect1 - this - > center ) . y ( ) , ( intersect1 - this - > center ) . x ( ) ) ) ;
auto angle2 = wrapAngle ( atan2 ( ( intersect2 - this - > center ) . y ( ) , ( intersect2 - this - > center ) . x ( ) ) ) ;
auto angleDiff = wrapAngle ( angle2 - angle1 ) ;
if ( ( angleDiff > = M_PI ) ^ ( a > 0.0 ) ) {
// allowed angles go from intersect1 to intersect2
if ( startAngle < angle1 ) {
startAngle = angle1 ;
}
auto maxSpan = wrapAngle ( angle2 - startAngle ) ;
if ( spanAngle > maxSpan ) {
spanAngle = maxSpan ;
}
} else {
// allowed angles go from intersect2 to intersect1
if ( startAngle < angle2 ) {
startAngle = angle2 ;
}
auto maxSpan = wrapAngle ( angle1 - startAngle ) ;
if ( spanAngle > maxSpan ) {
spanAngle = maxSpan ;
}
}
}
}
2022-01-08 07:46:17 +08:00
SmithChartConstantLine : : SmithChartConstantLine ( )
{
type = Type : : VSWR ;
param = 10.0 ;
color = Qt : : darkRed ;
}
2022-03-18 01:24:18 +08:00
std : : vector < SmithChartArc > SmithChartConstantLine : : getArcs ( double Z0 )
2022-01-08 07:46:17 +08:00
{
std : : vector < SmithChartArc > arcs ;
switch ( type ) {
case Type : : VSWR :
arcs . push_back ( SmithChartArc ( QPointF ( 0.0 , 0.0 ) , ( param - 1.0 ) / ( param + 1.0 ) ) ) ;
break ;
case Type : : Resistance : {
2022-03-15 22:26:02 +08:00
auto circleLeft = ( param / Z0 - 1.0 ) / ( param / Z0 + 1.0 ) ;
2022-01-08 07:46:17 +08:00
arcs . push_back ( SmithChartArc ( QPointF ( ( circleLeft + 1.0 ) / 2 , 0.0 ) , ( 1.0 - circleLeft ) / 2.0 ) ) ;
}
break ;
case Type : : Reactance : {
2022-03-15 22:26:02 +08:00
auto radius = 1.0 / ( param / Z0 ) ;
2022-01-08 07:46:17 +08:00
if ( radius > 0 ) {
arcs . push_back ( SmithChartArc ( QPointF ( 1.0 , radius ) , radius ) ) ;
} else {
arcs . push_back ( SmithChartArc ( QPointF ( 1.0 , radius ) , - radius ) ) ;
}
}
break ;
case Type : : Q : {
auto center = 1.0 / param ;
auto radius = sqrt ( center * center + 1.0 ) ;
arcs . push_back ( SmithChartArc ( QPointF ( 0.0 , center ) , radius ) ) ;
arcs . push_back ( SmithChartArc ( QPointF ( 0.0 , - center ) , radius ) ) ;
}
break ;
case Type : : Last :
break ;
}
return arcs ;
}
QColor SmithChartConstantLine : : getColor ( ) const
{
return color ;
}
void SmithChartConstantLine : : fromJSON ( nlohmann : : json j )
{
type = TypeFromString ( QString : : fromStdString ( j . value ( " type " , " VSWR " ) ) ) ;
if ( type = = Type : : Last ) {
type = Type : : VSWR ;
}
param = j . value ( " param " , 1.0 ) ;
color = QColor ( QString : : fromStdString ( j . value ( " color " , " red " ) ) ) ;
}
nlohmann : : json SmithChartConstantLine : : toJSON ( )
{
nlohmann : : json j ;
j [ " type " ] = TypeToString ( type ) . toStdString ( ) ;
j [ " param " ] = param ;
j [ " color " ] = color . name ( ) . toStdString ( ) ;
return j ;
}
QString SmithChartConstantLine : : getParamUnit ( )
{
switch ( type ) {
case Type : : VSWR : return " " ;
case Type : : Resistance : return " Ω " ;
case Type : : Reactance : return " Ωj " ;
case Type : : Q : return " " ;
case Type : : Last : break ;
}
return " " ;
}
QString SmithChartConstantLine : : TypeToString ( SmithChartConstantLine : : Type type )
{
switch ( type ) {
case Type : : VSWR : return " VSWR " ;
case Type : : Resistance : return " Resistance " ;
case Type : : Reactance : return " Reactance " ;
case Type : : Q : return " Q " ;
case Type : : Last : break ;
}
// should never get here
return " Invalid " ;
}
SmithChartConstantLine : : Type SmithChartConstantLine : : TypeFromString ( QString s )
{
for ( unsigned int i = 0 ; i < ( unsigned int ) Type : : Last ; i + + ) {
if ( TypeToString ( ( Type ) i ) = = s ) {
return ( Type ) i ;
}
}
return Type : : Last ;
}
void SmithChartConstantLine : : setColor ( const QColor & value )
{
color = value ;
}
double SmithChartConstantLine : : getParam ( ) const
{
return param ;
}
void SmithChartConstantLine : : setParam ( double value )
{
param = value ;
}
void SmithChartConstantLine : : setType ( const Type & value )
{
type = value ;
}
SmithChartConstantLine : : Type SmithChartConstantLine : : getType ( ) const
{
return type ;
}
static constexpr int rowHeight = 21 ;
QSize SmithChartParamDelegate : : sizeHint ( const QStyleOptionViewItem & , const QModelIndex & ) const
{
return QSize ( 0 , rowHeight ) ;
}
QWidget * SmithChartParamDelegate : : createEditor ( QWidget * parent , const QStyleOptionViewItem & , const QModelIndex & index ) const
{
auto line = static_cast < const SmithChartContantLineModel * > ( index . model ( ) ) - > lineFromIndex ( index ) ;
auto editor = new SIUnitEdit ( line - > getParamUnit ( ) , " pnum kMG " , 6 ) ;
editor - > setValue ( line - > getParam ( ) ) ;
editor - > setMaximumHeight ( rowHeight ) ;
editor - > setParent ( parent ) ;
connect ( editor , & SIUnitEdit : : valueUpdated , this , & SmithChartParamDelegate : : commitData ) ;
return editor ;
}
void SmithChartParamDelegate : : setModelData ( QWidget * editor , QAbstractItemModel * model , const QModelIndex & index ) const
{
auto line = ( ( SmithChartContantLineModel * ) model ) - > lineFromIndex ( index ) ;
auto si = ( SIUnitEdit * ) editor ;
line - > setParam ( si - > value ( ) ) ;
}
QSize SmithChartTypeDelegate : : sizeHint ( const QStyleOptionViewItem & , const QModelIndex & ) const
{
return QSize ( 0 , rowHeight ) ;
}
QWidget * SmithChartTypeDelegate : : createEditor ( QWidget * parent , const QStyleOptionViewItem & , const QModelIndex & index ) const
{
auto line = static_cast < const SmithChartContantLineModel * > ( index . model ( ) ) - > lineFromIndex ( index ) ;
auto editor = new QComboBox ( ) ;
for ( unsigned int i = 0 ; i < ( unsigned int ) SmithChartConstantLine : : Type : : Last ; i + + ) {
editor - > addItem ( SmithChartConstantLine : : TypeToString ( ( SmithChartConstantLine : : Type ) i ) ) ;
}
editor - > setCurrentIndex ( ( int ) line - > getType ( ) ) ;
connect ( editor , qOverload < int > ( & QComboBox : : currentIndexChanged ) , [ = ] ( int ) {
emit const_cast < SmithChartTypeDelegate * > ( this ) - > commitData ( editor ) ;
} ) ;
editor - > setMaximumHeight ( rowHeight ) ;
editor - > setParent ( parent ) ;
return editor ;
}
void SmithChartTypeDelegate : : setModelData ( QWidget * editor , QAbstractItemModel * model , const QModelIndex & index ) const
{
auto line = ( ( SmithChartContantLineModel * ) model ) - > lineFromIndex ( index ) ;
auto * cb = ( QComboBox * ) editor ;
line - > setType ( ( SmithChartConstantLine : : Type ) cb - > currentIndex ( ) ) ;
// parameter unit may have changed, update model
auto paramIndex = model - > index ( index . row ( ) , SmithChartContantLineModel : : ColIndexParam ) ;
emit model - > dataChanged ( paramIndex , paramIndex ) ;
}
SmithChartContantLineModel : : SmithChartContantLineModel ( TraceSmithChart & chart , QObject * parent )
: chart ( chart )
{
Q_UNUSED ( parent ) ;
}
int SmithChartContantLineModel : : rowCount ( const QModelIndex & parent ) const
{
Q_UNUSED ( parent ) ;
return chart . constantLines . size ( ) ;
}
int SmithChartContantLineModel : : columnCount ( const QModelIndex & parent ) const
{
Q_UNUSED ( parent ) ;
return ColIndexLast ;
}
QVariant SmithChartContantLineModel : : data ( const QModelIndex & index , int role ) const
{
if ( ! index . isValid ( ) )
return QVariant ( ) ;
if ( ( unsigned int ) index . row ( ) > = chart . constantLines . size ( ) )
return QVariant ( ) ;
auto line = chart . constantLines [ index . row ( ) ] ;
switch ( index . column ( ) ) {
case ColIndexColor :
if ( role = = Qt : : BackgroundColorRole ) {
return line . getColor ( ) ;
}
break ;
case ColIndexType :
if ( role = = Qt : : DisplayRole ) {
return SmithChartConstantLine : : TypeToString ( line . getType ( ) ) ;
}
break ;
case ColIndexParam :
if ( role = = Qt : : DisplayRole ) {
return Unit : : ToString ( line . getParam ( ) , line . getParamUnit ( ) , " pnum kMG " , 6 ) ;
}
break ;
default :
break ;
}
return QVariant ( ) ;
}
QVariant SmithChartContantLineModel : : headerData ( int section , Qt : : Orientation orientation , int role ) const
{
Q_UNUSED ( orientation ) ;
if ( role = = Qt : : DisplayRole ) {
switch ( section ) {
case ColIndexColor : return " Color " ;
case ColIndexType : return " Type " ;
case ColIndexParam : return " Parameter " ;
}
}
return QVariant ( ) ;
}
Qt : : ItemFlags SmithChartContantLineModel : : flags ( const QModelIndex & index ) const
{
Qt : : ItemFlags flags = Qt : : ItemIsSelectable | Qt : : ItemIsEnabled ;
switch ( index . column ( ) ) {
case ColIndexType :
case ColIndexParam :
flags | = Qt : : ItemIsEditable ;
break ;
}
return flags ;
}
SmithChartConstantLine * SmithChartContantLineModel : : lineFromIndex ( const QModelIndex & index ) const
{
if ( index . isValid ( ) & & index . row ( ) < ( int ) chart . constantLines . size ( ) ) {
return & chart . constantLines [ index . row ( ) ] ;
} else {
return nullptr ;
}
}