Updated graphs to use new math system
This commit is contained in:
parent
a7ff3d60fb
commit
49f9b5442d
@ -23,7 +23,7 @@ TraceMath::DataType TDRLowpass::outputType(TraceMath::DataType inputType)
|
|||||||
|
|
||||||
QString TDRLowpass::description()
|
QString TDRLowpass::description()
|
||||||
{
|
{
|
||||||
return "TDR (bandpass mode)";
|
return "TDR (lowpass mode)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void TDRLowpass::edit()
|
void TDRLowpass::edit()
|
||||||
@ -85,11 +85,13 @@ void TDRLowpass::inputSamplesChanged(unsigned int begin, unsigned int end)
|
|||||||
data[i].x = fs * i;
|
data[i].x = fs * i;
|
||||||
data[i].y = frequencyDomain[i] / (double) fft_bins;
|
data[i].y = frequencyDomain[i] / (double) fft_bins;
|
||||||
}
|
}
|
||||||
|
updateStepResponse(true);
|
||||||
emit outputSamplesChanged(0, data.size());
|
emit outputSamplesChanged(0, data.size());
|
||||||
success();
|
success();
|
||||||
} else {
|
} else {
|
||||||
// not enough input data
|
// not enough input data
|
||||||
data.clear();
|
data.clear();
|
||||||
|
updateStepResponse(false);
|
||||||
emit outputSamplesChanged(0, 0);
|
emit outputSamplesChanged(0, 0);
|
||||||
warning("Not enough input samples");
|
warning("Not enough input samples");
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,15 @@ TraceMath::Data TraceMath::getSample(unsigned int index)
|
|||||||
return data.at(index);
|
return data.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double TraceMath::getStepResponse(unsigned int index)
|
||||||
|
{
|
||||||
|
if(stepResponse.size() > index) {
|
||||||
|
return stepResponse[index];
|
||||||
|
} else {
|
||||||
|
return std::numeric_limits<double>::quiet_NaN();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TraceMath::Data TraceMath::getInterpolatedSample(double x)
|
TraceMath::Data TraceMath::getInterpolatedSample(double x)
|
||||||
{
|
{
|
||||||
Data ret;
|
Data ret;
|
||||||
@ -120,6 +129,7 @@ void TraceMath::inputTypeChanged(TraceMath::DataType type)
|
|||||||
emit outputTypeChanged(dataType);
|
emit outputTypeChanged(dataType);
|
||||||
if(dataType == DataType::Invalid) {
|
if(dataType == DataType::Invalid) {
|
||||||
error("Invalid input data");
|
error("Invalid input data");
|
||||||
|
updateStepResponse(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,6 +156,20 @@ void TraceMath::success()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TraceMath::updateStepResponse(bool valid)
|
||||||
|
{
|
||||||
|
if(valid) {
|
||||||
|
stepResponse.resize(data.size());
|
||||||
|
double accumulate = 0.0;
|
||||||
|
for(unsigned int i=0;i<data.size();i++) {
|
||||||
|
accumulate += data[i].y.real();
|
||||||
|
stepResponse[i] = accumulate;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stepResponse.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString TraceMath::getStatusDescription() const
|
QString TraceMath::getStatusDescription() const
|
||||||
{
|
{
|
||||||
return statusString;
|
return statusString;
|
||||||
|
@ -81,6 +81,7 @@ public:
|
|||||||
static TypeInfo getInfo(Type type);
|
static TypeInfo getInfo(Type type);
|
||||||
|
|
||||||
Data getSample(unsigned int index);
|
Data getSample(unsigned int index);
|
||||||
|
double getStepResponse(unsigned int index);
|
||||||
Data getInterpolatedSample(double x);
|
Data getInterpolatedSample(double x);
|
||||||
unsigned int numSamples();
|
unsigned int numSamples();
|
||||||
|
|
||||||
@ -115,6 +116,11 @@ protected:
|
|||||||
void error(QString err);
|
void error(QString err);
|
||||||
void success();
|
void success();
|
||||||
std::vector<Data> data;
|
std::vector<Data> data;
|
||||||
|
// buffer for time domain step response data. This makes it possible to access an arbitrary sample of the step response without having to
|
||||||
|
// integrate the impulse response every time. Call updateStepResponse in your derived class, if step response data is valid after updating
|
||||||
|
// data.
|
||||||
|
std::vector<double> stepResponse;
|
||||||
|
void updateStepResponse(bool valid);
|
||||||
TraceMath *input;
|
TraceMath *input;
|
||||||
DataType dataType;
|
DataType dataType;
|
||||||
|
|
||||||
|
@ -5,8 +5,7 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Trace::Trace(QString name, QColor color, LiveParameter live)
|
Trace::Trace(QString name, QColor color, LiveParameter live)
|
||||||
: tdr_users(0),
|
: _name(name),
|
||||||
_name(name),
|
|
||||||
_color(color),
|
_color(color),
|
||||||
_liveType(LivedataType::Overwrite),
|
_liveType(LivedataType::Overwrite),
|
||||||
_liveParam(live),
|
_liveParam(live),
|
||||||
@ -75,12 +74,6 @@ void Trace::addData(const Trace::Data& d) {
|
|||||||
}
|
}
|
||||||
success();
|
success();
|
||||||
emit outputSamplesChanged(lower - data.begin(), lower - data.begin() + 1);
|
emit outputSamplesChanged(lower - data.begin(), lower - data.begin() + 1);
|
||||||
if(lower == data.begin()) {
|
|
||||||
// received the first point, which means the last sweep just finished
|
|
||||||
if(tdr_users) {
|
|
||||||
updateTimeDomainData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trace::addData(const Trace::Data &d, const Protocol::SweepSettings &s)
|
void Trace::addData(const Trace::Data &d, const Protocol::SweepSettings &s)
|
||||||
@ -168,78 +161,6 @@ void Trace::removeMarker(TraceMarker *m)
|
|||||||
markers.erase(m);
|
markers.erase(m);
|
||||||
emit markerRemoved(m);
|
emit markerRemoved(m);
|
||||||
}
|
}
|
||||||
//#include <iostream>
|
|
||||||
//#include <chrono>
|
|
||||||
|
|
||||||
void Trace::updateTimeDomainData()
|
|
||||||
{
|
|
||||||
if(data.size() < 2) {
|
|
||||||
// can't compute anything
|
|
||||||
timeDomain.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// using namespace std::chrono;
|
|
||||||
// auto starttime = duration_cast< milliseconds >(
|
|
||||||
// system_clock::now().time_since_epoch()
|
|
||||||
// ).count();
|
|
||||||
auto steps = size();
|
|
||||||
auto firstStep = minFreq();
|
|
||||||
if(firstStep == 0) {
|
|
||||||
// zero as first step would result in infinite number of points, skip and start with second
|
|
||||||
firstStep = lastMath->rData()[1].x;
|
|
||||||
steps--;
|
|
||||||
}
|
|
||||||
if(firstStep * steps != maxFreq()) {
|
|
||||||
// data is not available with correct frequency spacing, calculate required steps
|
|
||||||
steps = maxFreq() / firstStep;
|
|
||||||
}
|
|
||||||
const double PI = 3.141592653589793238463;
|
|
||||||
// reserve vector for negative frequenies and DC as well
|
|
||||||
vector<complex<double>> frequencyDomain(2*steps + 1);
|
|
||||||
// copy frequencies, use the flipped conjugate for negative part
|
|
||||||
for(unsigned int i = 1;i<=steps;i++) {
|
|
||||||
auto S = getData(firstStep * i);
|
|
||||||
constexpr double alpha0 = 0.54;
|
|
||||||
auto hamming = alpha0 - (1.0 - alpha0) * -cos(PI * i / steps);
|
|
||||||
S *= hamming;
|
|
||||||
frequencyDomain[2 * steps - i + 1] = conj(S);
|
|
||||||
frequencyDomain[i] = S;
|
|
||||||
}
|
|
||||||
// use simple extrapolation from lowest two points to extract DC value
|
|
||||||
auto abs_DC = 2.0 * abs(frequencyDomain[1]) - abs(frequencyDomain[2]);
|
|
||||||
auto phase_DC = 2.0 * arg(frequencyDomain[1]) - arg(frequencyDomain[2]);
|
|
||||||
frequencyDomain[0] = polar(abs_DC, phase_DC);
|
|
||||||
|
|
||||||
auto fft_bins = frequencyDomain.size();
|
|
||||||
timeDomain.clear();
|
|
||||||
timeDomain.resize(fft_bins);
|
|
||||||
const double fs = 1.0 / (firstStep * fft_bins);
|
|
||||||
double last_step = 0.0;
|
|
||||||
|
|
||||||
Fft::transform(frequencyDomain, true);
|
|
||||||
constexpr double c = 299792458;
|
|
||||||
for(unsigned int i = 0;i<fft_bins;i++) {
|
|
||||||
TimedomainData t;
|
|
||||||
t.time = fs * i;
|
|
||||||
t.distance = t.time * c * 0.66; // TODO user settable velocity factor
|
|
||||||
if(isReflection()) {
|
|
||||||
t.distance /= 2;
|
|
||||||
}
|
|
||||||
t.impulseResponse = real(frequencyDomain[i]) / fft_bins;
|
|
||||||
t.stepResponse = last_step;
|
|
||||||
if(abs(t.stepResponse) < 1.0) {
|
|
||||||
t.impedance = 50.0 * (1+t.stepResponse) / (1-t.stepResponse);
|
|
||||||
} else {
|
|
||||||
t.impedance = numeric_limits<double>::quiet_NaN();
|
|
||||||
}
|
|
||||||
last_step += t.impulseResponse;
|
|
||||||
timeDomain[i] = t;
|
|
||||||
}
|
|
||||||
// auto duration = duration_cast< milliseconds >(
|
|
||||||
// system_clock::now().time_since_epoch()
|
|
||||||
// ).count() - starttime;
|
|
||||||
// cout << "TDR: " << this << " (took " << duration << "ms)" <<endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Trace::MathInfo>& Trace::getMathOperations() const
|
const std::vector<Trace::MathInfo>& Trace::getMathOperations() const
|
||||||
{
|
{
|
||||||
@ -263,6 +184,7 @@ void Trace::updateLastMath(vector<MathInfo>::reverse_iterator start)
|
|||||||
lastMath = newLast;
|
lastMath = newLast;
|
||||||
// relay signals of end of math chain
|
// relay signals of end of math chain
|
||||||
connect(lastMath, &TraceMath::outputSamplesChanged, this, &Trace::dataChanged);
|
connect(lastMath, &TraceMath::outputSamplesChanged, this, &Trace::dataChanged);
|
||||||
|
emit typeChanged(this);
|
||||||
emit outputSamplesChanged(0, data.size());
|
emit outputSamplesChanged(0, data.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,77 +194,6 @@ void Trace::setReflection(bool value)
|
|||||||
reflection = value;
|
reflection = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trace::addTDRinterest()
|
|
||||||
{
|
|
||||||
if(tdr_users == 0) {
|
|
||||||
// no recent time domain data available, calculate now
|
|
||||||
updateTimeDomainData();
|
|
||||||
}
|
|
||||||
tdr_users++;
|
|
||||||
if(tdr_users == 1) {
|
|
||||||
emit changedTDRstate(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Trace::removeTDRinterest()
|
|
||||||
{
|
|
||||||
if(tdr_users > 0) {
|
|
||||||
tdr_users--;
|
|
||||||
if(tdr_users == 0) {
|
|
||||||
emit changedTDRstate(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Trace::TimedomainData Trace::getTDR(double position)
|
|
||||||
{
|
|
||||||
TimedomainData ret = {};
|
|
||||||
if(!TDRactive() || position < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
int index = 0;
|
|
||||||
bool exact = false;
|
|
||||||
double alpha = 0.0;
|
|
||||||
if(position <= timeDomain.back().time) {
|
|
||||||
auto lower = lower_bound(timeDomain.begin(), timeDomain.end(), position, [](const TimedomainData &lhs, const double pos) -> bool {
|
|
||||||
return lhs.time < pos;
|
|
||||||
});
|
|
||||||
index = lower - timeDomain.begin();
|
|
||||||
if(timeDomain.at(index).time == position) {
|
|
||||||
exact = true;
|
|
||||||
} else {
|
|
||||||
alpha = (position - timeDomain.at(index-1).time) / (timeDomain.at(index).time - timeDomain.at(index-1).time);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(position > timeDomain.back().distance) {
|
|
||||||
// too high, invalid position
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
auto lower = lower_bound(timeDomain.begin(), timeDomain.end(), position, [](const TimedomainData &lhs, const double pos) -> bool {
|
|
||||||
return lhs.distance < pos;
|
|
||||||
});
|
|
||||||
index = lower - timeDomain.begin();
|
|
||||||
if(timeDomain.at(index).distance == position) {
|
|
||||||
exact = true;
|
|
||||||
} else {
|
|
||||||
alpha = (position - timeDomain.at(index-1).distance) / (timeDomain.at(index).distance - timeDomain.at(index-1).distance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(exact) {
|
|
||||||
return timeDomain.at(index);
|
|
||||||
} else {
|
|
||||||
// need to interpolate
|
|
||||||
auto low = timeDomain.at(index-1);
|
|
||||||
auto high = timeDomain.at(index);
|
|
||||||
ret.time = low.time * (1.0 - alpha) + high.time * alpha;
|
|
||||||
ret.distance = low.distance * (1.0 - alpha) + high.distance * alpha;
|
|
||||||
ret.stepResponse = low.stepResponse * (1.0 - alpha) + high.stepResponse * alpha;
|
|
||||||
ret.impulseResponse = low.impulseResponse * (1.0 - alpha) + high.impulseResponse * alpha;
|
|
||||||
ret.impedance = low.impedance * (1.0 - alpha) + high.impedance * alpha;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Trace::description()
|
QString Trace::description()
|
||||||
{
|
{
|
||||||
return name() + ": measured data";
|
return name() + ": measured data";
|
||||||
@ -502,26 +353,30 @@ unsigned int Trace::size()
|
|||||||
return lastMath->numSamples();
|
return lastMath->numSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
double Trace::minFreq()
|
double Trace::minX()
|
||||||
{
|
{
|
||||||
if(size() > 0) {
|
if(lastMath->numSamples() > 0) {
|
||||||
return data.front().x;
|
return lastMath->rData().front().x;
|
||||||
} else {
|
} else {
|
||||||
return 0.0;
|
return numeric_limits<double>::quiet_NaN();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double Trace::maxFreq()
|
double Trace::maxX()
|
||||||
{
|
{
|
||||||
if(size() > 0) {
|
if(lastMath->numSamples() > 0) {
|
||||||
return data.back().x;
|
return lastMath->rData().back().x;
|
||||||
} else {
|
} else {
|
||||||
return 0.0;
|
return numeric_limits<double>::quiet_NaN();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double Trace::findExtremumFreq(bool max)
|
double Trace::findExtremumFreq(bool max)
|
||||||
{
|
{
|
||||||
|
if(lastMath->getDataType() != DataType::Frequency) {
|
||||||
|
// not in frequency domain
|
||||||
|
return numeric_limits<double>::quiet_NaN();
|
||||||
|
}
|
||||||
double compare = max ? numeric_limits<double>::min() : numeric_limits<double>::max();
|
double compare = max ? numeric_limits<double>::min() : numeric_limits<double>::max();
|
||||||
double freq = 0.0;
|
double freq = 0.0;
|
||||||
for(auto sample : lastMath->rData()) {
|
for(auto sample : lastMath->rData()) {
|
||||||
@ -537,6 +392,10 @@ double Trace::findExtremumFreq(bool max)
|
|||||||
|
|
||||||
std::vector<double> Trace::findPeakFrequencies(unsigned int maxPeaks, double minLevel, double minValley)
|
std::vector<double> Trace::findPeakFrequencies(unsigned int maxPeaks, double minLevel, double minValley)
|
||||||
{
|
{
|
||||||
|
if(lastMath->getDataType() != DataType::Frequency) {
|
||||||
|
// not in frequency domain
|
||||||
|
return vector<double>();
|
||||||
|
}
|
||||||
using peakInfo = struct peakinfo {
|
using peakInfo = struct peakinfo {
|
||||||
double frequency;
|
double frequency;
|
||||||
double level_dbm;
|
double level_dbm;
|
||||||
@ -587,9 +446,14 @@ std::vector<double> Trace::findPeakFrequencies(unsigned int maxPeaks, double min
|
|||||||
return frequencies;
|
return frequencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
Trace::Data Trace::sample(unsigned int index)
|
Trace::Data Trace::sample(unsigned int index, SampleType type)
|
||||||
{
|
{
|
||||||
return lastMath->getSample(index);
|
auto data = lastMath->getSample(index);
|
||||||
|
if(type == SampleType::TimeStep) {
|
||||||
|
// exchange impulse data with step data
|
||||||
|
data.y = lastMath->getStepResponse(index);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Trace::getTouchstoneFilename() const
|
QString Trace::getTouchstoneFilename() const
|
||||||
@ -607,41 +471,23 @@ unsigned int Trace::getTouchstoneParameter() const
|
|||||||
return touchstoneParameter;
|
return touchstoneParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::complex<double> Trace::getData(double frequency)
|
|
||||||
{
|
|
||||||
if(lastMath->numSamples() == 0 || frequency < minFreq() || frequency > maxFreq()) {
|
|
||||||
return std::numeric_limits<std::complex<double>>::quiet_NaN();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto i = index(frequency);
|
|
||||||
if(lastMath->getSample(i).x == frequency) {
|
|
||||||
return lastMath->getSample(i).y;
|
|
||||||
} else {
|
|
||||||
// no exact frequency match, needs to interpolate
|
|
||||||
auto high = lastMath->getSample(i);
|
|
||||||
auto low = lastMath->getSample(i - 1);
|
|
||||||
double alpha = (frequency - low.x) / (high.x - low.x);
|
|
||||||
return low.y * (1 - alpha) + high.y * alpha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double Trace::getNoise(double frequency)
|
double Trace::getNoise(double frequency)
|
||||||
{
|
{
|
||||||
if(!isLive() || !settings.valid || (_liveParam != LiveParameter::Port1 && _liveParam != LiveParameter::Port2)) {
|
if(!isLive() || !settings.valid || (_liveParam != LiveParameter::Port1 && _liveParam != LiveParameter::Port2) || lastMath->getDataType() != DataType::Frequency) {
|
||||||
// data not suitable for noise calculation
|
// data not suitable for noise calculation
|
||||||
return std::numeric_limits<double>::quiet_NaN();
|
return std::numeric_limits<double>::quiet_NaN();
|
||||||
}
|
}
|
||||||
// convert to dbm
|
// convert to dbm
|
||||||
auto dbm = 20*log10(abs(getData(frequency)));
|
auto dbm = 20*log10(abs(lastMath->getInterpolatedSample(frequency).y));
|
||||||
// convert to 1Hz bandwidth
|
// convert to 1Hz bandwidth
|
||||||
dbm -= 10*log10(settings.SA.RBW);
|
dbm -= 10*log10(settings.SA.RBW);
|
||||||
return dbm;
|
return dbm;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Trace::index(double frequency)
|
int Trace::index(double x)
|
||||||
{
|
{
|
||||||
auto lower = lower_bound(lastMath->rData().begin(), lastMath->rData().end(), frequency, [](const Data &lhs, const double freq) -> bool {
|
auto lower = lower_bound(lastMath->rData().begin(), lastMath->rData().end(), x, [](const Data &lhs, const double x) -> bool {
|
||||||
return lhs.x < freq;
|
return lhs.x < x;
|
||||||
});
|
});
|
||||||
return lower - lastMath->rData().begin();
|
return lower - lastMath->rData().begin();
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,6 @@ public:
|
|||||||
|
|
||||||
using Data = TraceMath::Data;
|
using Data = TraceMath::Data;
|
||||||
|
|
||||||
class TimedomainData {
|
|
||||||
public:
|
|
||||||
double time;
|
|
||||||
double distance;
|
|
||||||
double impulseResponse;
|
|
||||||
double stepResponse;
|
|
||||||
double impedance;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class LiveParameter {
|
enum class LiveParameter {
|
||||||
S11,
|
S11,
|
||||||
S12,
|
S12,
|
||||||
@ -66,9 +57,10 @@ public:
|
|||||||
bool isReflection();
|
bool isReflection();
|
||||||
LiveParameter liveParameter() { return _liveParam; }
|
LiveParameter liveParameter() { return _liveParam; }
|
||||||
LivedataType liveType() { return _liveType; }
|
LivedataType liveType() { return _liveType; }
|
||||||
|
TraceMath::DataType outputType() { return lastMath->getDataType(); };
|
||||||
unsigned int size();
|
unsigned int size();
|
||||||
double minFreq();
|
double minX();
|
||||||
double maxFreq();
|
double maxX();
|
||||||
double findExtremumFreq(bool max);
|
double findExtremumFreq(bool max);
|
||||||
/* Searches for peaks in the trace data and returns the peak frequencies in ascending order.
|
/* Searches for peaks in the trace data and returns the peak frequencies in ascending order.
|
||||||
* Up to maxPeaks will be returned, with higher level peaks taking priority over lower level peaks.
|
* Up to maxPeaks will be returned, with higher level peaks taking priority over lower level peaks.
|
||||||
@ -76,30 +68,22 @@ public:
|
|||||||
* To detect the next peak, the signal first has to drop at least minValley below the peak level.
|
* To detect the next peak, the signal first has to drop at least minValley below the peak level.
|
||||||
*/
|
*/
|
||||||
std::vector<double> findPeakFrequencies(unsigned int maxPeaks = 100, double minLevel = -100.0, double minValley = 3.0);
|
std::vector<double> findPeakFrequencies(unsigned int maxPeaks = 100, double minLevel = -100.0, double minValley = 3.0);
|
||||||
Data sample(unsigned int index);
|
enum class SampleType {
|
||||||
|
Frequency,
|
||||||
|
TimeImpulse,
|
||||||
|
TimeStep,
|
||||||
|
};
|
||||||
|
|
||||||
|
Data sample(unsigned int index, SampleType type = SampleType::Frequency);
|
||||||
QString getTouchstoneFilename() const;
|
QString getTouchstoneFilename() const;
|
||||||
unsigned int getTouchstoneParameter() const;
|
unsigned int getTouchstoneParameter() const;
|
||||||
std::complex<double> getData(double frequency);
|
|
||||||
/* Returns the noise in dbm/Hz for spectrum analyzer measurements. May return NaN if calculation not possible */
|
/* Returns the noise in dbm/Hz for spectrum analyzer measurements. May return NaN if calculation not possible */
|
||||||
double getNoise(double frequency);
|
double getNoise(double frequency);
|
||||||
int index(double frequency);
|
int index(double x);
|
||||||
std::set<TraceMarker *> getMarkers() const;
|
std::set<TraceMarker *> getMarkers() const;
|
||||||
void setCalibration(bool value);
|
void setCalibration(bool value);
|
||||||
void setReflection(bool value);
|
void setReflection(bool value);
|
||||||
|
|
||||||
// TDR calculation can be ressource intensive, only perform when some other module is interested.
|
|
||||||
// Each interested module should call addTDRinterest(), read the data with getTDR() and finally
|
|
||||||
// call removeTDRinterest() once TDR updates are no longer required.
|
|
||||||
// The data is only updated at the end of a sweep and upon the first addTDRinterest() call.
|
|
||||||
void addTDRinterest();
|
|
||||||
void removeTDRinterest();
|
|
||||||
bool TDRactive() { return tdr_users > 0;};
|
|
||||||
const std::vector<TimedomainData>& getTDR() { return timeDomain;}
|
|
||||||
// interpolates the TDR data
|
|
||||||
// position is assumed to be the delay if it is smaller than the maximum sampled delay, otherwise it is assumed to be the distance.
|
|
||||||
// Since the usual delay values are way smaller than the distance values this should work
|
|
||||||
TimedomainData getTDR(double position);
|
|
||||||
|
|
||||||
DataType outputType(DataType inputType) override { Q_UNUSED(inputType) return DataType::Frequency;};
|
DataType outputType(DataType inputType) override { Q_UNUSED(inputType) return DataType::Frequency;};
|
||||||
QString description() override;
|
QString description() override;
|
||||||
|
|
||||||
@ -128,7 +112,6 @@ public slots:
|
|||||||
void addMarker(TraceMarker *m);
|
void addMarker(TraceMarker *m);
|
||||||
void removeMarker(TraceMarker *m);
|
void removeMarker(TraceMarker *m);
|
||||||
|
|
||||||
private:
|
|
||||||
signals:
|
signals:
|
||||||
void cleared(Trace *t);
|
void cleared(Trace *t);
|
||||||
void typeChanged(Trace *t);
|
void typeChanged(Trace *t);
|
||||||
@ -139,14 +122,8 @@ signals:
|
|||||||
void colorChanged(Trace *t);
|
void colorChanged(Trace *t);
|
||||||
void markerAdded(TraceMarker *m);
|
void markerAdded(TraceMarker *m);
|
||||||
void markerRemoved(TraceMarker *m);
|
void markerRemoved(TraceMarker *m);
|
||||||
void changedTDRstate(bool enabled);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateTimeDomainData();
|
|
||||||
void printTimeDomain();
|
|
||||||
// std::vector<Data> _data;
|
|
||||||
std::vector<TimedomainData> timeDomain;
|
|
||||||
unsigned int tdr_users;
|
|
||||||
QString _name;
|
QString _name;
|
||||||
QColor _color;
|
QColor _color;
|
||||||
LivedataType _liveType;
|
LivedataType _liveType;
|
||||||
|
@ -145,8 +145,8 @@ void TraceExportDialog::selectionChanged(QComboBox *w)
|
|||||||
points = t->size();
|
points = t->size();
|
||||||
ui->points->setText(QString::number(points));
|
ui->points->setText(QString::number(points));
|
||||||
if(points > 0) {
|
if(points > 0) {
|
||||||
lowerFreq = t->minFreq();
|
lowerFreq = t->minX();
|
||||||
upperFreq = t->maxFreq();
|
upperFreq = t->maxX();
|
||||||
ui->lowerFreq->setText(QString::number(lowerFreq));
|
ui->lowerFreq->setText(QString::number(lowerFreq));
|
||||||
ui->upperFreq->setText(QString::number(upperFreq));
|
ui->upperFreq->setText(QString::number(upperFreq));
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ void TraceExportDialog::selectionChanged(QComboBox *w)
|
|||||||
for(auto c : v1) {
|
for(auto c : v1) {
|
||||||
for(int i=1;i<c->count();i++) {
|
for(int i=1;i<c->count();i++) {
|
||||||
Trace *t = qvariant_cast<Trace*>(c->itemData(i));
|
Trace *t = qvariant_cast<Trace*>(c->itemData(i));
|
||||||
if(t->size() != points || (points > 0 && (t->minFreq() != lowerFreq || t->maxFreq() != upperFreq))) {
|
if(t->size() != points || (points > 0 && (t->minX() != lowerFreq || t->maxX() != upperFreq))) {
|
||||||
// this trace is not available anymore
|
// this trace is not available anymore
|
||||||
c->removeItem(i);
|
c->removeItem(i);
|
||||||
// decrement to check the next index in the next loop iteration
|
// decrement to check the next index in the next loop iteration
|
||||||
|
@ -35,35 +35,6 @@ TraceMarker::~TraceMarker()
|
|||||||
emit deleted(this);
|
emit deleted(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TraceMarker::setTimeDomain(bool timeDomain)
|
|
||||||
{
|
|
||||||
if(timeDomain != this->timeDomain) {
|
|
||||||
// setting changed, check if actually available
|
|
||||||
if(timeDomain && !parentTrace->TDRactive()) {
|
|
||||||
qWarning() << "Attempted to enable TDR marker on trace without active TDR";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->timeDomain = timeDomain;
|
|
||||||
|
|
||||||
if(timeDomain) {
|
|
||||||
// need to delete this marker if the TDR data of the trace is no longer available
|
|
||||||
connect(parentTrace, &Trace::changedTDRstate, [=](bool tdr_available){
|
|
||||||
if(!tdr_available) {
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// check if current type still supported
|
|
||||||
if(!getSupportedTypes().count(type)) {
|
|
||||||
// unsupported type, change to manual
|
|
||||||
setType(Type::Manual);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
disconnect(parentTrace, &Trace::changedTDRstate, nullptr, nullptr);
|
|
||||||
}
|
|
||||||
emit timeDomainChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TraceMarker::assignTrace(Trace *t)
|
void TraceMarker::assignTrace(Trace *t)
|
||||||
{
|
{
|
||||||
if(parentTrace) {
|
if(parentTrace) {
|
||||||
@ -73,7 +44,6 @@ void TraceMarker::assignTrace(Trace *t)
|
|||||||
disconnect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged);
|
disconnect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged);
|
||||||
disconnect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol);
|
disconnect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol);
|
||||||
}
|
}
|
||||||
setTimeDomain(false);
|
|
||||||
parentTrace = t;
|
parentTrace = t;
|
||||||
if(!getSupportedTypes().count(type)) {
|
if(!getSupportedTypes().count(type)) {
|
||||||
// new trace does not support the current type
|
// new trace does not support the current type
|
||||||
@ -104,44 +74,45 @@ QString TraceMarker::readableData()
|
|||||||
case Type::Manual:
|
case Type::Manual:
|
||||||
case Type::Maximum:
|
case Type::Maximum:
|
||||||
case Type::Minimum:
|
case Type::Minimum:
|
||||||
if(isTimeDomain()) {
|
// if(isTimeDomain()) {
|
||||||
QString ret;
|
// QString ret;
|
||||||
ret += "Impulse:"+Unit::ToString(timeData.impulseResponse, "", "m ", 3)+" Step:"+Unit::ToString(timeData.stepResponse, "", "m ", 3)+" Impedance:";
|
// ret += "Impulse:"+Unit::ToString(timeData.impulseResponse, "", "m ", 3)+" Step:"+Unit::ToString(timeData.stepResponse, "", "m ", 3)+" Impedance:";
|
||||||
if(isnan(timeData.impedance)) {
|
// if(isnan(timeData.impedance)) {
|
||||||
ret += "Invalid";
|
// ret += "Invalid";
|
||||||
} else {
|
// } else {
|
||||||
ret += Unit::ToString(timeData.impedance, "Ω", "m k", 3);
|
// ret += Unit::ToString(timeData.impedance, "Ω", "m k", 3);
|
||||||
}
|
// }
|
||||||
return ret;
|
// return ret;
|
||||||
} else {
|
// } else
|
||||||
|
{
|
||||||
auto phase = arg(data);
|
auto phase = arg(data);
|
||||||
return QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
|
return QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
|
||||||
}
|
}
|
||||||
case Type::Delta:
|
case Type::Delta:
|
||||||
if(!delta || delta->isTimeDomain() != isTimeDomain()) {
|
if(!delta /*|| delta->isTimeDomain() != isTimeDomain()*/) {
|
||||||
return "Invalid delta marker";
|
return "Invalid delta marker";
|
||||||
} else {
|
} else {
|
||||||
if(isTimeDomain()) {
|
// if(isTimeDomain()) {
|
||||||
// calculate difference between markers
|
// // calculate difference between markers
|
||||||
auto impulse = timeData.impulseResponse - delta->timeData.impulseResponse;
|
// auto impulse = timeData.impulseResponse - delta->timeData.impulseResponse;
|
||||||
auto step = timeData.stepResponse - delta->timeData.stepResponse;
|
// auto step = timeData.stepResponse - delta->timeData.stepResponse;
|
||||||
auto impedance = timeData.impedance - delta->timeData.impedance;
|
// auto impedance = timeData.impedance - delta->timeData.impedance;
|
||||||
QString ret;
|
// QString ret;
|
||||||
ret += "ΔImpulse:"+Unit::ToString(impulse, "", "m ", 3)+" ΔStep:"+Unit::ToString(step, "", "m ", 3)+" ΔImpedance:";
|
// ret += "ΔImpulse:"+Unit::ToString(impulse, "", "m ", 3)+" ΔStep:"+Unit::ToString(step, "", "m ", 3)+" ΔImpedance:";
|
||||||
if(isnan(timeData.impedance)) {
|
// if(isnan(timeData.impedance)) {
|
||||||
ret += "Invalid";
|
// ret += "Invalid";
|
||||||
} else {
|
// } else {
|
||||||
ret += Unit::ToString(impedance, "Ω", "m k", 3);
|
// ret += Unit::ToString(impedance, "Ω", "m k", 3);
|
||||||
}
|
// }
|
||||||
return ret;
|
// return ret;
|
||||||
} else {
|
// } else {
|
||||||
// calculate difference between markers
|
// calculate difference between markers
|
||||||
auto freqDiff = position - delta->position;
|
auto freqDiff = position - delta->position;
|
||||||
auto valueDiff = data / delta->data;
|
auto valueDiff = data / delta->data;
|
||||||
auto phase = arg(valueDiff);
|
auto phase = arg(valueDiff);
|
||||||
auto db = 20*log10(abs(valueDiff));
|
auto db = 20*log10(abs(valueDiff));
|
||||||
return Unit::ToString(freqDiff, "Hz", " kMG") + " / " + QString::number(db, 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
|
return Unit::ToString(freqDiff, "Hz", " kMG") + " / " + QString::number(db, 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::Noise:
|
case Type::Noise:
|
||||||
@ -205,22 +176,22 @@ QString TraceMarker::readableData()
|
|||||||
|
|
||||||
QString TraceMarker::readableSettings()
|
QString TraceMarker::readableSettings()
|
||||||
{
|
{
|
||||||
if(timeDomain) {
|
// if(timeDomain) {
|
||||||
switch(type) {
|
// switch(type) {
|
||||||
case Type::Manual:
|
// case Type::Manual:
|
||||||
case Type::Delta: {
|
// case Type::Delta: {
|
||||||
QString unit;
|
// QString unit;
|
||||||
if(position <= parentTrace->getTDR().back().time) {
|
// if(position <= parentTrace->getTDR().back().time) {
|
||||||
unit = "s";
|
// unit = "s";
|
||||||
} else {
|
// } else {
|
||||||
unit = "m";
|
// unit = "m";
|
||||||
}
|
// }
|
||||||
return Unit::ToString(position, unit, "fpnum k", 4);
|
// return Unit::ToString(position, unit, "fpnum k", 4);
|
||||||
}
|
// }
|
||||||
default:
|
// default:
|
||||||
return "Unhandled case";
|
// return "Unhandled case";
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case Type::Manual:
|
case Type::Manual:
|
||||||
case Type::Maximum:
|
case Type::Maximum:
|
||||||
@ -241,7 +212,7 @@ QString TraceMarker::readableSettings()
|
|||||||
default:
|
default:
|
||||||
return "Unhandled case";
|
return "Unhandled case";
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TraceMarker::readableType()
|
QString TraceMarker::readableType()
|
||||||
@ -270,12 +241,12 @@ void TraceMarker::traceDataChanged()
|
|||||||
{
|
{
|
||||||
// some data of the parent trace changed, check if marker data also changed
|
// some data of the parent trace changed, check if marker data also changed
|
||||||
complex<double> newdata;
|
complex<double> newdata;
|
||||||
if (timeDomain) {
|
// if (timeDomain) {
|
||||||
timeData = parentTrace->getTDR(position);
|
// timeData = parentTrace->getTDR(position);
|
||||||
newdata = complex<double>(timeData.stepResponse, timeData.impulseResponse);
|
// newdata = complex<double>(timeData.stepResponse, timeData.impulseResponse);
|
||||||
} else {
|
// } else {
|
||||||
newdata = parentTrace->getData(position);
|
newdata = parentTrace->sample(parentTrace->index(position)).y;
|
||||||
}
|
// }
|
||||||
if (newdata != data) {
|
if (newdata != data) {
|
||||||
data = newdata;
|
data = newdata;
|
||||||
update();
|
update();
|
||||||
@ -309,11 +280,11 @@ std::set<TraceMarker::Type> TraceMarker::getSupportedTypes()
|
|||||||
{
|
{
|
||||||
set<TraceMarker::Type> supported;
|
set<TraceMarker::Type> supported;
|
||||||
if(parentTrace) {
|
if(parentTrace) {
|
||||||
if(timeDomain) {
|
// if(timeDomain) {
|
||||||
// only basic markers in time domain
|
// // only basic markers in time domain
|
||||||
supported.insert(Type::Manual);
|
// supported.insert(Type::Manual);
|
||||||
supported.insert(Type::Delta);
|
// supported.insert(Type::Delta);
|
||||||
} else {
|
// } else {
|
||||||
// all traces support some basic markers
|
// all traces support some basic markers
|
||||||
supported.insert(Type::Manual);
|
supported.insert(Type::Manual);
|
||||||
supported.insert(Type::Maximum);
|
supported.insert(Type::Maximum);
|
||||||
@ -340,7 +311,7 @@ std::set<TraceMarker::Type> TraceMarker::getSupportedTypes()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
return supported;
|
return supported;
|
||||||
}
|
}
|
||||||
@ -348,21 +319,21 @@ std::set<TraceMarker::Type> TraceMarker::getSupportedTypes()
|
|||||||
void TraceMarker::constrainPosition()
|
void TraceMarker::constrainPosition()
|
||||||
{
|
{
|
||||||
if(parentTrace) {
|
if(parentTrace) {
|
||||||
if(timeDomain) {
|
// if(timeDomain) {
|
||||||
if(position < 0) {
|
// if(position < 0) {
|
||||||
position = 0;
|
// position = 0;
|
||||||
} else if(position > parentTrace->getTDR().back().distance) {
|
// } else if(position > parentTrace->getTDR().back().distance) {
|
||||||
position = parentTrace->getTDR().back().distance;
|
// position = parentTrace->getTDR().back().distance;
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
if(parentTrace->size() > 0) {
|
if(parentTrace->size() > 0) {
|
||||||
if(position > parentTrace->maxFreq()) {
|
if(position > parentTrace->maxX()) {
|
||||||
position = parentTrace->maxFreq();
|
position = parentTrace->maxX();
|
||||||
} else if(position < parentTrace->minFreq()) {
|
} else if(position < parentTrace->minX()) {
|
||||||
position = parentTrace->minFreq();
|
position = parentTrace->minX();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
traceDataChanged();
|
traceDataChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -410,10 +381,10 @@ void TraceMarker::setType(TraceMarker::Type t)
|
|||||||
// invalid delta marker assigned, attempt to find a matching marker
|
// invalid delta marker assigned, attempt to find a matching marker
|
||||||
for(int pass = 0;pass < 3;pass++) {
|
for(int pass = 0;pass < 3;pass++) {
|
||||||
for(auto m : model->getMarkers()) {
|
for(auto m : model->getMarkers()) {
|
||||||
if(m->isTimeDomain() != isTimeDomain()) {
|
// if(m->isTimeDomain() != isTimeDomain()) {
|
||||||
// markers are not on the same domain
|
// // markers are not on the same domain
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
if(pass == 0 && m->parentTrace != parentTrace) {
|
if(pass == 0 && m->parentTrace != parentTrace) {
|
||||||
// ignore markers on different traces in first pass
|
// ignore markers on different traces in first pass
|
||||||
continue;
|
continue;
|
||||||
@ -484,11 +455,6 @@ QString TraceMarker::getSuffix() const
|
|||||||
return suffix;
|
return suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TraceMarker::isTimeDomain() const
|
|
||||||
{
|
|
||||||
return timeDomain;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<TraceMarker *> &TraceMarker::getHelperMarkers() const
|
const std::vector<TraceMarker *> &TraceMarker::getHelperMarkers() const
|
||||||
{
|
{
|
||||||
return helperMarkers;
|
return helperMarkers;
|
||||||
@ -594,15 +560,15 @@ void TraceMarker::updateTypeFromEditor(QWidget *w)
|
|||||||
|
|
||||||
SIUnitEdit *TraceMarker::getSettingsEditor()
|
SIUnitEdit *TraceMarker::getSettingsEditor()
|
||||||
{
|
{
|
||||||
if(timeDomain) {
|
// if(timeDomain) {
|
||||||
switch(type) {
|
// switch(type) {
|
||||||
case Type::Manual:
|
// case Type::Manual:
|
||||||
case Type::Delta:
|
// case Type::Delta:
|
||||||
return new SIUnitEdit("", "fpnum k", 6);
|
// return new SIUnitEdit("", "fpnum k", 6);
|
||||||
default:
|
// default:
|
||||||
return nullptr;
|
// return nullptr;
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case Type::Manual:
|
case Type::Manual:
|
||||||
case Type::Maximum:
|
case Type::Maximum:
|
||||||
@ -619,7 +585,7 @@ SIUnitEdit *TraceMarker::getSettingsEditor()
|
|||||||
case Type::TOI:
|
case Type::TOI:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void TraceMarker::adjustSettings(double value)
|
void TraceMarker::adjustSettings(double value)
|
||||||
@ -798,15 +764,6 @@ std::complex<double> TraceMarker::getData() const
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
Trace::TimedomainData TraceMarker::getTimeData() const
|
|
||||||
{
|
|
||||||
Trace::TimedomainData ret = {};
|
|
||||||
if(timeDomain) {
|
|
||||||
ret = timeData;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TraceMarker::isMovable()
|
bool TraceMarker::isMovable()
|
||||||
{
|
{
|
||||||
if(parent) {
|
if(parent) {
|
||||||
|
@ -15,7 +15,6 @@ class TraceMarker : public QObject
|
|||||||
public:
|
public:
|
||||||
TraceMarker(TraceMarkerModel *model, int number = 1, TraceMarker *parent = nullptr, QString descr = QString());
|
TraceMarker(TraceMarkerModel *model, int number = 1, TraceMarker *parent = nullptr, QString descr = QString());
|
||||||
~TraceMarker();
|
~TraceMarker();
|
||||||
void setTimeDomain(bool timeDomain);
|
|
||||||
void assignTrace(Trace *t);
|
void assignTrace(Trace *t);
|
||||||
Trace* trace();
|
Trace* trace();
|
||||||
QString readableData();
|
QString readableData();
|
||||||
@ -24,7 +23,6 @@ public:
|
|||||||
|
|
||||||
double getPosition() const;
|
double getPosition() const;
|
||||||
std::complex<double> getData() const;
|
std::complex<double> getData() const;
|
||||||
Trace::TimedomainData getTimeData() const;
|
|
||||||
bool isMovable();
|
bool isMovable();
|
||||||
|
|
||||||
QPixmap& getSymbol();
|
QPixmap& getSymbol();
|
||||||
@ -48,8 +46,6 @@ public:
|
|||||||
TraceMarker *helperMarker(unsigned int i);
|
TraceMarker *helperMarker(unsigned int i);
|
||||||
QString getSuffix() const;
|
QString getSuffix() const;
|
||||||
|
|
||||||
bool isTimeDomain() const;
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setPosition(double freq);
|
void setPosition(double freq);
|
||||||
signals:
|
signals:
|
||||||
@ -114,7 +110,6 @@ private:
|
|||||||
// Frequency domain: S parameter
|
// Frequency domain: S parameter
|
||||||
// Time domain: imag part is impulse response, real part is step response
|
// Time domain: imag part is impulse response, real part is step response
|
||||||
std::complex<double> data;
|
std::complex<double> data;
|
||||||
Trace::TimedomainData timeData;
|
|
||||||
QPixmap symbol;
|
QPixmap symbol;
|
||||||
Type type;
|
Type type;
|
||||||
QString suffix;
|
QString suffix;
|
||||||
@ -128,8 +123,6 @@ private:
|
|||||||
double peakThreshold;
|
double peakThreshold;
|
||||||
double offset;
|
double offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool timeDomain;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TRACEMARKER_H
|
#endif // TRACEMARKER_H
|
||||||
|
@ -200,10 +200,7 @@ bool TraceMarkerModel::setData(const QModelIndex &index, const QVariant &value,
|
|||||||
break;
|
break;
|
||||||
case ColIndexTrace: {
|
case ColIndexTrace: {
|
||||||
auto info = qvariant_cast<MarkerWidgetTraceInfo>(value);
|
auto info = qvariant_cast<MarkerWidgetTraceInfo>(value);
|
||||||
// always disable timedomain before switching trace
|
|
||||||
m->setTimeDomain(false);
|
|
||||||
m->assignTrace(info.trace);
|
m->assignTrace(info.trace);
|
||||||
m->setTimeDomain(info.isTimeDomain);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ColIndexSettings: {
|
case ColIndexSettings: {
|
||||||
@ -288,12 +285,7 @@ QWidget *MarkerTraceDelegate::createEditor(QWidget *parent, const QStyleOptionVi
|
|||||||
for(auto t : traces) {
|
for(auto t : traces) {
|
||||||
MarkerWidgetTraceInfo info;
|
MarkerWidgetTraceInfo info;
|
||||||
info.trace = t;
|
info.trace = t;
|
||||||
info.isTimeDomain = false;
|
|
||||||
c->addItem(t->name(), QVariant::fromValue(info));
|
c->addItem(t->name(), QVariant::fromValue(info));
|
||||||
if(t->TDRactive()) {
|
|
||||||
info.isTimeDomain = true;
|
|
||||||
c->addItem(t->name() + " Time Domain", QVariant::fromValue(info));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@ -304,7 +296,6 @@ void MarkerTraceDelegate::setEditorData(QWidget *editor, const QModelIndex &inde
|
|||||||
auto c = (QComboBox*) editor;
|
auto c = (QComboBox*) editor;
|
||||||
MarkerWidgetTraceInfo markerInfo;
|
MarkerWidgetTraceInfo markerInfo;
|
||||||
markerInfo.trace = marker->trace();
|
markerInfo.trace = marker->trace();
|
||||||
markerInfo.isTimeDomain = marker->isTimeDomain();
|
|
||||||
for(int i=0;i<c->count();i++) {
|
for(int i=0;i<c->count();i++) {
|
||||||
auto info = qvariant_cast<MarkerWidgetTraceInfo>(c->itemData(i));
|
auto info = qvariant_cast<MarkerWidgetTraceInfo>(c->itemData(i));
|
||||||
if(info == markerInfo) {
|
if(info == markerInfo) {
|
||||||
|
@ -19,13 +19,11 @@ class MarkerTraceDelegate : public QStyledItemDelegate
|
|||||||
class MarkerWidgetTraceInfo {
|
class MarkerWidgetTraceInfo {
|
||||||
public:
|
public:
|
||||||
Trace *trace;
|
Trace *trace;
|
||||||
bool isTimeDomain;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(const MarkerWidgetTraceInfo& lhs, const MarkerWidgetTraceInfo& rhs)
|
inline bool operator==(const MarkerWidgetTraceInfo& lhs, const MarkerWidgetTraceInfo& rhs)
|
||||||
{
|
{
|
||||||
return lhs.trace == rhs.trace && lhs.isTimeDomain == rhs.isTimeDomain;
|
return lhs.trace == rhs.trace;
|
||||||
}
|
}
|
||||||
Q_DECLARE_METATYPE(MarkerWidgetTraceInfo)
|
Q_DECLARE_METATYPE(MarkerWidgetTraceInfo)
|
||||||
|
|
||||||
|
@ -23,9 +23,6 @@ void TraceModel::addTrace(Trace *t)
|
|||||||
connect(t, &Trace::nameChanged, [=]() {
|
connect(t, &Trace::nameChanged, [=]() {
|
||||||
emit traceNameChanged(t);
|
emit traceNameChanged(t);
|
||||||
});
|
});
|
||||||
connect(t, &Trace::changedTDRstate, [=](bool enabled) {
|
|
||||||
emit traceTDRstateChanged(t, enabled);
|
|
||||||
});
|
|
||||||
traces.push_back(t);
|
traces.push_back(t);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
emit traceAdded(t);
|
emit traceAdded(t);
|
||||||
|
@ -53,12 +53,14 @@ void TracePlot::enableTrace(Trace *t, bool enabled)
|
|||||||
connect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot);
|
connect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot);
|
||||||
connect(t, &Trace::markerAdded, this, &TracePlot::markerAdded);
|
connect(t, &Trace::markerAdded, this, &TracePlot::markerAdded);
|
||||||
connect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved);
|
connect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved);
|
||||||
|
connect(t, &Trace::typeChanged, this, &TracePlot::checkIfStillSupported);
|
||||||
} else {
|
} else {
|
||||||
// disconnect from notifications
|
// disconnect from notifications
|
||||||
disconnect(t, &Trace::dataChanged, this, &TracePlot::triggerReplot);
|
disconnect(t, &Trace::dataChanged, this, &TracePlot::triggerReplot);
|
||||||
disconnect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot);
|
disconnect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot);
|
||||||
disconnect(t, &Trace::markerAdded, this, &TracePlot::markerAdded);
|
disconnect(t, &Trace::markerAdded, this, &TracePlot::markerAdded);
|
||||||
disconnect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved);
|
disconnect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved);
|
||||||
|
disconnect(t, &Trace::typeChanged, this, &TracePlot::checkIfStillSupported);
|
||||||
}
|
}
|
||||||
updateContextMenu();
|
updateContextMenu();
|
||||||
replot();
|
replot();
|
||||||
@ -271,6 +273,14 @@ void TracePlot::triggerReplot()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TracePlot::checkIfStillSupported(Trace *t)
|
||||||
|
{
|
||||||
|
if(!supported(t)) {
|
||||||
|
// something with this trace changed and it can no longer be displayed on this graph
|
||||||
|
enableTrace(t, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TracePlot::markerAdded(TraceMarker *m)
|
void TracePlot::markerAdded(TraceMarker *m)
|
||||||
{
|
{
|
||||||
connect(m, &TraceMarker::dataChanged, this, &TracePlot::triggerReplot);
|
connect(m, &TraceMarker::dataChanged, this, &TracePlot::triggerReplot);
|
||||||
|
@ -59,6 +59,7 @@ protected slots:
|
|||||||
void newTraceAvailable(Trace *t);
|
void newTraceAvailable(Trace *t);
|
||||||
void traceDeleted(Trace *t);
|
void traceDeleted(Trace *t);
|
||||||
void triggerReplot();
|
void triggerReplot();
|
||||||
|
void checkIfStillSupported(Trace *t);
|
||||||
virtual void markerAdded(TraceMarker *m);
|
virtual void markerAdded(TraceMarker *m);
|
||||||
virtual void markerRemoved(TraceMarker *m);
|
virtual void markerRemoved(TraceMarker *m);
|
||||||
protected:
|
protected:
|
||||||
|
@ -56,12 +56,12 @@ std::complex<double> TraceSmithChart::pixelToData(QPoint p)
|
|||||||
QPoint TraceSmithChart::markerToPixel(TraceMarker *m)
|
QPoint TraceSmithChart::markerToPixel(TraceMarker *m)
|
||||||
{
|
{
|
||||||
QPoint ret = QPoint();
|
QPoint ret = QPoint();
|
||||||
if(!m->isTimeDomain()) {
|
// if(!m->isTimeDomain()) {
|
||||||
if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) {
|
if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) {
|
||||||
auto d = m->getData();
|
auto d = m->getData();
|
||||||
ret = transform.map(QPoint(d.real() * smithCoordMax, -d.imag() * smithCoordMax));
|
ret = transform.map(QPoint(d.real() * smithCoordMax, -d.imag() * smithCoordMax));
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,9 +160,9 @@ void TraceSmithChart::draw(QPainter &p) {
|
|||||||
// only draw markers if the trace has at least one point
|
// only draw markers if the trace has at least one point
|
||||||
auto markers = t.first->getMarkers();
|
auto markers = t.first->getMarkers();
|
||||||
for(auto m : markers) {
|
for(auto m : markers) {
|
||||||
if (m->isTimeDomain()) {
|
// if (m->isTimeDomain()) {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
if (limitToSpan && (m->getPosition() < sweep_fmin || m->getPosition() > sweep_fmax)) {
|
if (limitToSpan && (m->getPosition() < sweep_fmin || m->getPosition() > sweep_fmax)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -268,7 +268,7 @@ void TraceSmithChart::updateContextMenu()
|
|||||||
|
|
||||||
bool TraceSmithChart::supported(Trace *t)
|
bool TraceSmithChart::supported(Trace *t)
|
||||||
{
|
{
|
||||||
if(t->isReflection()) {
|
if(t->outputType() == Trace::DataType::Frequency && t->isReflection()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -67,27 +67,14 @@ void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while(erased);
|
} while(erased);
|
||||||
|
|
||||||
auto oldType = YAxis[axis].type;
|
|
||||||
if(isTDRtype(YAxis[axis].type) && !isTDRtype(type)) {
|
|
||||||
// not TDR axis anymore
|
|
||||||
for(auto t : tracesAxis[axis]) {
|
|
||||||
t->removeTDRinterest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
YAxis[axis].type = type;
|
YAxis[axis].type = type;
|
||||||
if(isTDRtype(type) && !isTDRtype(oldType)) {
|
|
||||||
// now a TDR axis
|
|
||||||
for(auto t : tracesAxis[axis]) {
|
|
||||||
t->addTDRinterest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
YAxis[axis].log = log;
|
YAxis[axis].log = log;
|
||||||
YAxis[axis].autorange = autorange;
|
YAxis[axis].autorange = autorange;
|
||||||
YAxis[axis].rangeMin = min;
|
YAxis[axis].rangeMin = min;
|
||||||
YAxis[axis].rangeMax = max;
|
YAxis[axis].rangeMax = max;
|
||||||
YAxis[axis].rangeDiv = div;
|
YAxis[axis].rangeDiv = div;
|
||||||
|
removeUnsupportedTraces();
|
||||||
updateAxisTicks();
|
updateAxisTicks();
|
||||||
updateContextMenu();
|
updateContextMenu();
|
||||||
replot();
|
replot();
|
||||||
@ -100,15 +87,14 @@ void TraceXYPlot::setXAxis(XAxisType type, XAxisMode mode, double min, double ma
|
|||||||
XAxis.rangeMin = min;
|
XAxis.rangeMin = min;
|
||||||
XAxis.rangeMax = max;
|
XAxis.rangeMax = max;
|
||||||
XAxis.rangeDiv = div;
|
XAxis.rangeDiv = div;
|
||||||
|
removeUnsupportedTraces();
|
||||||
updateAxisTicks();
|
updateAxisTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TraceXYPlot::enableTrace(Trace *t, bool enabled)
|
void TraceXYPlot::enableTrace(Trace *t, bool enabled)
|
||||||
{
|
{
|
||||||
for(int axis = 0;axis < 2;axis++) {
|
for(int axis = 0;axis < 2;axis++) {
|
||||||
if(supported(t, YAxis[axis].type)) {
|
enableTraceAxis(t, axis, enabled && supported(t, YAxis[axis].type));
|
||||||
enableTraceAxis(t, axis, enabled);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,10 +170,10 @@ void TraceXYPlot::updateContextMenu()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TraceXYPlot::supported(Trace *)
|
bool TraceXYPlot::supported(Trace *t)
|
||||||
{
|
{
|
||||||
// potentially possible to add every kind of trace (depends on axis)
|
// potentially possible to add every kind of trace (depends on axis)
|
||||||
if(YAxis[0].type != YAxisType::Disabled || YAxis[1].type != YAxisType::Disabled) {
|
if(supported(t, YAxis[0].type) || supported(t, YAxis[1].type)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// no axis
|
// no axis
|
||||||
@ -353,7 +339,7 @@ void TraceXYPlot::draw(QPainter &p)
|
|||||||
pen.setStyle(Qt::SolidLine);
|
pen.setStyle(Qt::SolidLine);
|
||||||
}
|
}
|
||||||
p.setPen(pen);
|
p.setPen(pen);
|
||||||
auto nPoints = numTraceSamples(t);
|
auto nPoints = t->size();
|
||||||
for(unsigned int j=1;j<nPoints;j++) {
|
for(unsigned int j=1;j<nPoints;j++) {
|
||||||
auto last = traceToCoordinate(t, j-1, YAxis[i].type);
|
auto last = traceToCoordinate(t, j-1, YAxis[i].type);
|
||||||
auto now = traceToCoordinate(t, j, YAxis[i].type);
|
auto now = traceToCoordinate(t, j, YAxis[i].type);
|
||||||
@ -376,24 +362,25 @@ void TraceXYPlot::draw(QPainter &p)
|
|||||||
// only draw markers on primary YAxis and if the trace has at least one point
|
// only draw markers on primary YAxis and if the trace has at least one point
|
||||||
auto markers = t->getMarkers();
|
auto markers = t->getMarkers();
|
||||||
for(auto m : markers) {
|
for(auto m : markers) {
|
||||||
if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
|
// if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
|
||||||
// wrong domain, skip this marker
|
// // wrong domain, skip this marker
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
double xPosition;
|
double xPosition;
|
||||||
if(m->isTimeDomain()) {
|
// if(m->isTimeDomain()) {
|
||||||
if(XAxis.type == XAxisType::Distance) {
|
// if(XAxis.type == XAxisType::Distance) {
|
||||||
xPosition = m->getTimeData().distance;
|
// xPosition = m->getTimeData().distance;
|
||||||
} else {
|
// } else {
|
||||||
xPosition = m->getTimeData().time;
|
// xPosition = m->getTimeData().time;
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
xPosition = m->getPosition();
|
xPosition = m->getPosition();
|
||||||
}
|
// }
|
||||||
if (xPosition < XAxis.rangeMin || xPosition > XAxis.rangeMax) {
|
if (xPosition < XAxis.rangeMin || xPosition > XAxis.rangeMax) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QPointF markerPoint = QPointF(xPosition, traceToCoordinate(m->getData(), YAxis[i].type));
|
auto t = m->getTrace();
|
||||||
|
QPointF markerPoint = traceToCoordinate(t, t->index(xPosition), YAxis[i].type);
|
||||||
auto point = plotValueToPixel(markerPoint, i);
|
auto point = plotValueToPixel(markerPoint, i);
|
||||||
if(!plotRect.contains(point)) {
|
if(!plotRect.contains(point)) {
|
||||||
// out of screen
|
// out of screen
|
||||||
@ -412,7 +399,8 @@ void TraceXYPlot::draw(QPainter &p)
|
|||||||
p.setOpacity(0.5);
|
p.setOpacity(0.5);
|
||||||
p.setBrush(Qt::white);
|
p.setBrush(Qt::white);
|
||||||
p.setPen(Qt::white);
|
p.setPen(Qt::white);
|
||||||
if(YAxis[0].type == YAxisType::Disabled || YAxis[1].type == YAxisType::Disabled) {
|
if((YAxis[0].type == YAxisType::Disabled || !supported(dropTrace, YAxis[0].type))
|
||||||
|
|| (YAxis[1].type == YAxisType::Disabled || !supported(dropTrace, YAxis[1].type))) {
|
||||||
// only one axis enabled, show drop area over whole plot
|
// only one axis enabled, show drop area over whole plot
|
||||||
p.drawRect(plotRect);
|
p.drawRect(plotRect);
|
||||||
auto font = p.font();
|
auto font = p.font();
|
||||||
@ -500,27 +488,13 @@ void TraceXYPlot::updateAxisTicks()
|
|||||||
|| tracesAxis[1].find(t.first) != tracesAxis[1].end());
|
|| tracesAxis[1].find(t.first) != tracesAxis[1].end());
|
||||||
auto trace = t.first;
|
auto trace = t.first;
|
||||||
if(enabled && trace->isVisible()) {
|
if(enabled && trace->isVisible()) {
|
||||||
if(!numTraceSamples(trace)) {
|
if(!trace->size()) {
|
||||||
// empty trace, do not use for automatic axis calculation
|
// empty trace, do not use for automatic axis calculation
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// this trace is currently displayed
|
// this trace is currently displayed
|
||||||
double trace_min = std::numeric_limits<double>::max();
|
double trace_min = trace->minX();
|
||||||
double trace_max = std::numeric_limits<double>::lowest();
|
double trace_max = trace->maxX();
|
||||||
switch(XAxis.type) {
|
|
||||||
case XAxisType::Frequency:
|
|
||||||
trace_min = trace->minFreq();
|
|
||||||
trace_max = trace->maxFreq();
|
|
||||||
break;
|
|
||||||
case XAxisType::Time:
|
|
||||||
trace_min = trace->getTDR().front().time;
|
|
||||||
trace_max = trace->getTDR().back().time;
|
|
||||||
break;
|
|
||||||
case XAxisType::Distance:
|
|
||||||
trace_min = trace->getTDR().front().distance;
|
|
||||||
trace_max = trace->getTDR().back().distance;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(trace_min < min) {
|
if(trace_min < min) {
|
||||||
min = trace_min;
|
min = trace_min;
|
||||||
}
|
}
|
||||||
@ -546,7 +520,7 @@ void TraceXYPlot::updateAxisTicks()
|
|||||||
double max = std::numeric_limits<double>::lowest();
|
double max = std::numeric_limits<double>::lowest();
|
||||||
double min = std::numeric_limits<double>::max();
|
double min = std::numeric_limits<double>::max();
|
||||||
for(auto t : tracesAxis[i]) {
|
for(auto t : tracesAxis[i]) {
|
||||||
unsigned int samples = numTraceSamples(t);
|
unsigned int samples = t->size();
|
||||||
for(unsigned int j=0;j<samples;j++) {
|
for(unsigned int j=0;j<samples;j++) {
|
||||||
auto point = traceToCoordinate(t, j, YAxis[i].type);
|
auto point = traceToCoordinate(t, j, YAxis[i].type);
|
||||||
|
|
||||||
@ -557,7 +531,8 @@ void TraceXYPlot::updateAxisTicks()
|
|||||||
|
|
||||||
if(point.y() > max) {
|
if(point.y() > max) {
|
||||||
max = point.y();
|
max = point.y();
|
||||||
} else if(point.y() < min) {
|
}
|
||||||
|
if(point.y() < min) {
|
||||||
min = point.y();
|
min = point.y();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -608,14 +583,7 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled)
|
|||||||
if(alreadyEnabled != enabled) {
|
if(alreadyEnabled != enabled) {
|
||||||
if(enabled) {
|
if(enabled) {
|
||||||
tracesAxis[axis].insert(t);
|
tracesAxis[axis].insert(t);
|
||||||
// connect signals
|
|
||||||
if(isTDRtype(YAxis[axis].type)) {
|
|
||||||
t->addTDRinterest();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if(isTDRtype(YAxis[axis].type)) {
|
|
||||||
t->removeTDRinterest();
|
|
||||||
}
|
|
||||||
tracesAxis[axis].erase(t);
|
tracesAxis[axis].erase(t);
|
||||||
if(axis == 0) {
|
if(axis == 0) {
|
||||||
disconnect(t, &Trace::markerAdded, this, &TraceXYPlot::markerAdded);
|
disconnect(t, &Trace::markerAdded, this, &TraceXYPlot::markerAdded);
|
||||||
@ -634,6 +602,22 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled)
|
|||||||
|
|
||||||
bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
|
bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
|
||||||
{
|
{
|
||||||
|
switch(XAxis.type) {
|
||||||
|
case XAxisType::Frequency:
|
||||||
|
if(t->outputType() != Trace::DataType::Frequency) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XAxisType::Distance:
|
||||||
|
case XAxisType::Time:
|
||||||
|
if(t->outputType() != Trace::DataType::Time) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case YAxisType::Disabled:
|
case YAxisType::Disabled:
|
||||||
return false;
|
return false;
|
||||||
@ -648,66 +632,45 @@ bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
double TraceXYPlot::traceToCoordinate(std::complex<double> data, TraceXYPlot::YAxisType type)
|
void TraceXYPlot::removeUnsupportedTraces()
|
||||||
{
|
{
|
||||||
switch(type) {
|
for(unsigned int i=0;i<2;i++) {
|
||||||
case YAxisType::Magnitude:
|
auto set_copy = tracesAxis[i];
|
||||||
return 20*log10(abs(data));
|
for(auto t : set_copy) {
|
||||||
case YAxisType::Phase:
|
if(!supported(t, YAxis[i].type)) {
|
||||||
return arg(data) * 180.0 / M_PI;
|
enableTraceAxis(t, i, false);
|
||||||
case YAxisType::VSWR:
|
|
||||||
if(abs(data) < 1.0) {
|
|
||||||
return (1+abs(data)) / (1-abs(data));
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case YAxisType::Step:
|
|
||||||
return data.real();
|
|
||||||
case YAxisType::Impulse:
|
|
||||||
return data.imag();
|
|
||||||
case YAxisType::Impedance:
|
|
||||||
if(abs(data.real()) < 1.0) {
|
|
||||||
return 50 * (1+data.real()) / (1-data.real());
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return numeric_limits<double>::quiet_NaN();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlot::YAxisType type)
|
QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlot::YAxisType type)
|
||||||
{
|
{
|
||||||
QPointF ret = QPointF(numeric_limits<double>::quiet_NaN(), numeric_limits<double>::quiet_NaN());
|
QPointF ret = QPointF(numeric_limits<double>::quiet_NaN(), numeric_limits<double>::quiet_NaN());
|
||||||
|
auto data = t->sample(sample);
|
||||||
|
ret.setX(data.x);
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case YAxisType::Magnitude:
|
case YAxisType::Magnitude:
|
||||||
|
ret.setY(20*log10(abs(data.y)));
|
||||||
|
break;
|
||||||
case YAxisType::Phase:
|
case YAxisType::Phase:
|
||||||
case YAxisType::VSWR: {
|
ret.setY(arg(data.y) * 180.0 / M_PI);
|
||||||
auto d = t->sample(sample);
|
break;
|
||||||
ret.setY(traceToCoordinate(d.y, type));
|
case YAxisType::VSWR:
|
||||||
ret.setX(d.x);
|
if(abs(data.y) < 1.0) {
|
||||||
|
ret.setY((1+abs(data.y)) / (1-abs(data.y)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case YAxisType::Impulse:
|
case YAxisType::Impulse:
|
||||||
ret.setY(t->getTDR()[sample].impulseResponse);
|
ret.setY(real(data.y));
|
||||||
if(XAxis.type == XAxisType::Distance) {
|
|
||||||
ret.setX(t->getTDR()[sample].distance);
|
|
||||||
} else {
|
|
||||||
ret.setX(t->getTDR()[sample].time);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case YAxisType::Step:
|
case YAxisType::Step:
|
||||||
ret.setY(t->getTDR()[sample].stepResponse);
|
ret.setY(t->sample(sample, Trace::SampleType::TimeStep).y.real());
|
||||||
if(XAxis.type == XAxisType::Distance) {
|
|
||||||
ret.setX(t->getTDR()[sample].distance);
|
|
||||||
} else {
|
|
||||||
ret.setX(t->getTDR()[sample].time);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case YAxisType::Impedance: {
|
case YAxisType::Impedance: {
|
||||||
ret.setY(t->getTDR()[sample].impedance);
|
double step = t->sample(sample, Trace::SampleType::TimeStep).y.real();
|
||||||
if(XAxis.type == XAxisType::Distance) {
|
if(abs(step) < 1.0) {
|
||||||
ret.setX(t->getTDR()[sample].distance);
|
ret.setY(50 * (1.0+step) / (1.0-step));
|
||||||
} else {
|
|
||||||
ret.setX(t->getTDR()[sample].time);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -719,26 +682,17 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int TraceXYPlot::numTraceSamples(Trace *t)
|
//QPoint TraceXYPlot::dataToPixel(Trace::Data d)
|
||||||
{
|
//{
|
||||||
if(XAxis.type == XAxisType::Frequency) {
|
// if(d.x < XAxis.rangeMin || d.x > XAxis.rangeMax) {
|
||||||
return t->size();
|
// return QPoint();
|
||||||
} else {
|
// }
|
||||||
return t->getTDR().size();
|
// auto y = traceToCoordinate(d.y, YAxis[0].type);
|
||||||
}
|
// QPoint p;
|
||||||
}
|
// p.setX(Util::Scale<double>(d.x, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth));
|
||||||
|
// p.setY(Util::Scale<double>(y, YAxis[0].rangeMin, YAxis[0].rangeMax, plotAreaBottom, 0));
|
||||||
QPoint TraceXYPlot::dataToPixel(Trace::Data d)
|
// return p;
|
||||||
{
|
//}
|
||||||
if(d.x < XAxis.rangeMin || d.x > XAxis.rangeMax) {
|
|
||||||
return QPoint();
|
|
||||||
}
|
|
||||||
auto y = traceToCoordinate(d.y, YAxis[0].type);
|
|
||||||
QPoint p;
|
|
||||||
p.setX(Util::Scale<double>(d.x, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth));
|
|
||||||
p.setY(Util::Scale<double>(y, YAxis[0].rangeMin, YAxis[0].rangeMax, plotAreaBottom, 0));
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPoint TraceXYPlot::plotValueToPixel(QPointF plotValue, int Yaxis)
|
QPoint TraceXYPlot::plotValueToPixel(QPointF plotValue, int Yaxis)
|
||||||
{
|
{
|
||||||
@ -759,22 +713,12 @@ QPointF TraceXYPlot::pixelToPlotValue(QPoint pixel, int Yaxis)
|
|||||||
QPoint TraceXYPlot::markerToPixel(TraceMarker *m)
|
QPoint TraceXYPlot::markerToPixel(TraceMarker *m)
|
||||||
{
|
{
|
||||||
QPoint ret = QPoint();
|
QPoint ret = QPoint();
|
||||||
if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
|
// if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
|
||||||
// invalid domain
|
// // invalid domain
|
||||||
return ret;
|
// return ret;
|
||||||
}
|
// }
|
||||||
QPointF plotPoint;
|
auto t = m->getTrace();
|
||||||
plotPoint.setY(traceToCoordinate(m->getData(), YAxis[0].type));
|
QPointF plotPoint = traceToCoordinate(t, t->index(m->getPosition()), YAxis[0].type);
|
||||||
if(m->isTimeDomain()) {
|
|
||||||
auto timedata = m->getTimeData();
|
|
||||||
if(XAxis.type == XAxisType::Distance) {
|
|
||||||
plotPoint.setX(timedata.distance);
|
|
||||||
} else {
|
|
||||||
plotPoint.setX(timedata.time);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
plotPoint.setX(m->getPosition());
|
|
||||||
}
|
|
||||||
return plotValueToPixel(plotPoint, 0);
|
return plotValueToPixel(plotPoint, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -786,7 +730,7 @@ double TraceXYPlot::nearestTracePoint(Trace *t, QPoint pixel)
|
|||||||
}
|
}
|
||||||
double closestDistance = numeric_limits<double>::max();
|
double closestDistance = numeric_limits<double>::max();
|
||||||
double closestXpos = 0;
|
double closestXpos = 0;
|
||||||
auto samples = numTraceSamples(t);
|
auto samples = t->size();
|
||||||
for(unsigned int i=0;i<samples;i++) {
|
for(unsigned int i=0;i<samples;i++) {
|
||||||
auto point = traceToCoordinate(t, i, YAxis[0].type);
|
auto point = traceToCoordinate(t, i, YAxis[0].type);
|
||||||
if(isnan(point.x()) || isnan(point.y())) {
|
if(isnan(point.x()) || isnan(point.y())) {
|
||||||
|
@ -58,10 +58,9 @@ private:
|
|||||||
QString AxisTypeToName(YAxisType type);
|
QString AxisTypeToName(YAxisType type);
|
||||||
void enableTraceAxis(Trace *t, int axis, bool enabled);
|
void enableTraceAxis(Trace *t, int axis, bool enabled);
|
||||||
bool supported(Trace *t, YAxisType type);
|
bool supported(Trace *t, YAxisType type);
|
||||||
double traceToCoordinate(std::complex<double> data, YAxisType type);
|
void removeUnsupportedTraces();
|
||||||
QPointF traceToCoordinate(Trace *t, unsigned int sample, YAxisType type);
|
QPointF traceToCoordinate(Trace *t, unsigned int sample, YAxisType type);
|
||||||
unsigned int numTraceSamples(Trace *t);
|
// QPoint dataToPixel(Trace::Data d);
|
||||||
QPoint dataToPixel(Trace::Data d);
|
|
||||||
QPoint plotValueToPixel(QPointF plotValue, int Yaxis);
|
QPoint plotValueToPixel(QPointF plotValue, int Yaxis);
|
||||||
QPointF pixelToPlotValue(QPoint pixel, int YAxis);
|
QPointF pixelToPlotValue(QPoint pixel, int YAxis);
|
||||||
QPoint markerToPixel(TraceMarker *m) override;
|
QPoint markerToPixel(TraceMarker *m) override;
|
||||||
|
Loading…
Reference in New Issue
Block a user