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()
|
||||
{
|
||||
return "TDR (bandpass mode)";
|
||||
return "TDR (lowpass mode)";
|
||||
}
|
||||
|
||||
void TDRLowpass::edit()
|
||||
@ -85,11 +85,13 @@ void TDRLowpass::inputSamplesChanged(unsigned int begin, unsigned int end)
|
||||
data[i].x = fs * i;
|
||||
data[i].y = frequencyDomain[i] / (double) fft_bins;
|
||||
}
|
||||
updateStepResponse(true);
|
||||
emit outputSamplesChanged(0, data.size());
|
||||
success();
|
||||
} else {
|
||||
// not enough input data
|
||||
data.clear();
|
||||
updateStepResponse(false);
|
||||
emit outputSamplesChanged(0, 0);
|
||||
warning("Not enough input samples");
|
||||
}
|
||||
|
@ -52,6 +52,15 @@ TraceMath::Data TraceMath::getSample(unsigned int 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)
|
||||
{
|
||||
Data ret;
|
||||
@ -120,6 +129,7 @@ void TraceMath::inputTypeChanged(TraceMath::DataType type)
|
||||
emit outputTypeChanged(dataType);
|
||||
if(dataType == DataType::Invalid) {
|
||||
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
|
||||
{
|
||||
return statusString;
|
||||
|
@ -81,6 +81,7 @@ public:
|
||||
static TypeInfo getInfo(Type type);
|
||||
|
||||
Data getSample(unsigned int index);
|
||||
double getStepResponse(unsigned int index);
|
||||
Data getInterpolatedSample(double x);
|
||||
unsigned int numSamples();
|
||||
|
||||
@ -115,6 +116,11 @@ protected:
|
||||
void error(QString err);
|
||||
void success();
|
||||
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;
|
||||
DataType dataType;
|
||||
|
||||
|
@ -5,8 +5,7 @@
|
||||
using namespace std;
|
||||
|
||||
Trace::Trace(QString name, QColor color, LiveParameter live)
|
||||
: tdr_users(0),
|
||||
_name(name),
|
||||
: _name(name),
|
||||
_color(color),
|
||||
_liveType(LivedataType::Overwrite),
|
||||
_liveParam(live),
|
||||
@ -75,12 +74,6 @@ void Trace::addData(const Trace::Data& d) {
|
||||
}
|
||||
success();
|
||||
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)
|
||||
@ -168,78 +161,6 @@ void Trace::removeMarker(TraceMarker *m)
|
||||
markers.erase(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
|
||||
{
|
||||
@ -263,6 +184,7 @@ void Trace::updateLastMath(vector<MathInfo>::reverse_iterator start)
|
||||
lastMath = newLast;
|
||||
// relay signals of end of math chain
|
||||
connect(lastMath, &TraceMath::outputSamplesChanged, this, &Trace::dataChanged);
|
||||
emit typeChanged(this);
|
||||
emit outputSamplesChanged(0, data.size());
|
||||
}
|
||||
}
|
||||
@ -272,77 +194,6 @@ void Trace::setReflection(bool 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()
|
||||
{
|
||||
return name() + ": measured data";
|
||||
@ -502,26 +353,30 @@ unsigned int Trace::size()
|
||||
return lastMath->numSamples();
|
||||
}
|
||||
|
||||
double Trace::minFreq()
|
||||
double Trace::minX()
|
||||
{
|
||||
if(size() > 0) {
|
||||
return data.front().x;
|
||||
if(lastMath->numSamples() > 0) {
|
||||
return lastMath->rData().front().x;
|
||||
} else {
|
||||
return 0.0;
|
||||
return numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
|
||||
double Trace::maxFreq()
|
||||
double Trace::maxX()
|
||||
{
|
||||
if(size() > 0) {
|
||||
return data.back().x;
|
||||
if(lastMath->numSamples() > 0) {
|
||||
return lastMath->rData().back().x;
|
||||
} else {
|
||||
return 0.0;
|
||||
return numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
|
||||
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 freq = 0.0;
|
||||
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)
|
||||
{
|
||||
if(lastMath->getDataType() != DataType::Frequency) {
|
||||
// not in frequency domain
|
||||
return vector<double>();
|
||||
}
|
||||
using peakInfo = struct peakinfo {
|
||||
double frequency;
|
||||
double level_dbm;
|
||||
@ -587,9 +446,14 @@ std::vector<double> Trace::findPeakFrequencies(unsigned int maxPeaks, double min
|
||||
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
|
||||
@ -607,41 +471,23 @@ unsigned int Trace::getTouchstoneParameter() const
|
||||
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)
|
||||
{
|
||||
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
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
// convert to dbm
|
||||
auto dbm = 20*log10(abs(getData(frequency)));
|
||||
auto dbm = 20*log10(abs(lastMath->getInterpolatedSample(frequency).y));
|
||||
// convert to 1Hz bandwidth
|
||||
dbm -= 10*log10(settings.SA.RBW);
|
||||
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 {
|
||||
return lhs.x < freq;
|
||||
auto lower = lower_bound(lastMath->rData().begin(), lastMath->rData().end(), x, [](const Data &lhs, const double x) -> bool {
|
||||
return lhs.x < x;
|
||||
});
|
||||
return lower - lastMath->rData().begin();
|
||||
}
|
||||
|
@ -19,15 +19,6 @@ public:
|
||||
|
||||
using Data = TraceMath::Data;
|
||||
|
||||
class TimedomainData {
|
||||
public:
|
||||
double time;
|
||||
double distance;
|
||||
double impulseResponse;
|
||||
double stepResponse;
|
||||
double impedance;
|
||||
};
|
||||
|
||||
enum class LiveParameter {
|
||||
S11,
|
||||
S12,
|
||||
@ -66,9 +57,10 @@ public:
|
||||
bool isReflection();
|
||||
LiveParameter liveParameter() { return _liveParam; }
|
||||
LivedataType liveType() { return _liveType; }
|
||||
TraceMath::DataType outputType() { return lastMath->getDataType(); };
|
||||
unsigned int size();
|
||||
double minFreq();
|
||||
double maxFreq();
|
||||
double minX();
|
||||
double maxX();
|
||||
double findExtremumFreq(bool max);
|
||||
/* 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.
|
||||
@ -76,30 +68,22 @@ public:
|
||||
* 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);
|
||||
Data sample(unsigned int index);
|
||||
enum class SampleType {
|
||||
Frequency,
|
||||
TimeImpulse,
|
||||
TimeStep,
|
||||
};
|
||||
|
||||
Data sample(unsigned int index, SampleType type = SampleType::Frequency);
|
||||
QString getTouchstoneFilename() 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 */
|
||||
double getNoise(double frequency);
|
||||
int index(double frequency);
|
||||
int index(double x);
|
||||
std::set<TraceMarker *> getMarkers() const;
|
||||
void setCalibration(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;};
|
||||
QString description() override;
|
||||
|
||||
@ -128,7 +112,6 @@ public slots:
|
||||
void addMarker(TraceMarker *m);
|
||||
void removeMarker(TraceMarker *m);
|
||||
|
||||
private:
|
||||
signals:
|
||||
void cleared(Trace *t);
|
||||
void typeChanged(Trace *t);
|
||||
@ -139,14 +122,8 @@ signals:
|
||||
void colorChanged(Trace *t);
|
||||
void markerAdded(TraceMarker *m);
|
||||
void markerRemoved(TraceMarker *m);
|
||||
void changedTDRstate(bool enabled);
|
||||
|
||||
private:
|
||||
void updateTimeDomainData();
|
||||
void printTimeDomain();
|
||||
// std::vector<Data> _data;
|
||||
std::vector<TimedomainData> timeDomain;
|
||||
unsigned int tdr_users;
|
||||
QString _name;
|
||||
QColor _color;
|
||||
LivedataType _liveType;
|
||||
|
@ -145,8 +145,8 @@ void TraceExportDialog::selectionChanged(QComboBox *w)
|
||||
points = t->size();
|
||||
ui->points->setText(QString::number(points));
|
||||
if(points > 0) {
|
||||
lowerFreq = t->minFreq();
|
||||
upperFreq = t->maxFreq();
|
||||
lowerFreq = t->minX();
|
||||
upperFreq = t->maxX();
|
||||
ui->lowerFreq->setText(QString::number(lowerFreq));
|
||||
ui->upperFreq->setText(QString::number(upperFreq));
|
||||
}
|
||||
@ -157,7 +157,7 @@ void TraceExportDialog::selectionChanged(QComboBox *w)
|
||||
for(auto c : v1) {
|
||||
for(int i=1;i<c->count();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
|
||||
c->removeItem(i);
|
||||
// decrement to check the next index in the next loop iteration
|
||||
|
@ -35,35 +35,6 @@ TraceMarker::~TraceMarker()
|
||||
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)
|
||||
{
|
||||
if(parentTrace) {
|
||||
@ -73,7 +44,6 @@ void TraceMarker::assignTrace(Trace *t)
|
||||
disconnect(parentTrace, &Trace::dataChanged, this, &TraceMarker::traceDataChanged);
|
||||
disconnect(parentTrace, &Trace::colorChanged, this, &TraceMarker::updateSymbol);
|
||||
}
|
||||
setTimeDomain(false);
|
||||
parentTrace = t;
|
||||
if(!getSupportedTypes().count(type)) {
|
||||
// new trace does not support the current type
|
||||
@ -104,44 +74,45 @@ QString TraceMarker::readableData()
|
||||
case Type::Manual:
|
||||
case Type::Maximum:
|
||||
case Type::Minimum:
|
||||
if(isTimeDomain()) {
|
||||
QString ret;
|
||||
ret += "Impulse:"+Unit::ToString(timeData.impulseResponse, "", "m ", 3)+" Step:"+Unit::ToString(timeData.stepResponse, "", "m ", 3)+" Impedance:";
|
||||
if(isnan(timeData.impedance)) {
|
||||
ret += "Invalid";
|
||||
} else {
|
||||
ret += Unit::ToString(timeData.impedance, "Ω", "m k", 3);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
// if(isTimeDomain()) {
|
||||
// QString ret;
|
||||
// ret += "Impulse:"+Unit::ToString(timeData.impulseResponse, "", "m ", 3)+" Step:"+Unit::ToString(timeData.stepResponse, "", "m ", 3)+" Impedance:";
|
||||
// if(isnan(timeData.impedance)) {
|
||||
// ret += "Invalid";
|
||||
// } else {
|
||||
// ret += Unit::ToString(timeData.impedance, "Ω", "m k", 3);
|
||||
// }
|
||||
// return ret;
|
||||
// } else
|
||||
{
|
||||
auto phase = arg(data);
|
||||
return QString::number(toDecibel(), 'g', 4) + "db@" + QString::number(phase*180/M_PI, 'g', 4);
|
||||
}
|
||||
case Type::Delta:
|
||||
if(!delta || delta->isTimeDomain() != isTimeDomain()) {
|
||||
if(!delta /*|| delta->isTimeDomain() != isTimeDomain()*/) {
|
||||
return "Invalid delta marker";
|
||||
} else {
|
||||
if(isTimeDomain()) {
|
||||
// calculate difference between markers
|
||||
auto impulse = timeData.impulseResponse - delta->timeData.impulseResponse;
|
||||
auto step = timeData.stepResponse - delta->timeData.stepResponse;
|
||||
auto impedance = timeData.impedance - delta->timeData.impedance;
|
||||
QString ret;
|
||||
ret += "ΔImpulse:"+Unit::ToString(impulse, "", "m ", 3)+" ΔStep:"+Unit::ToString(step, "", "m ", 3)+" ΔImpedance:";
|
||||
if(isnan(timeData.impedance)) {
|
||||
ret += "Invalid";
|
||||
} else {
|
||||
ret += Unit::ToString(impedance, "Ω", "m k", 3);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
// if(isTimeDomain()) {
|
||||
// // calculate difference between markers
|
||||
// auto impulse = timeData.impulseResponse - delta->timeData.impulseResponse;
|
||||
// auto step = timeData.stepResponse - delta->timeData.stepResponse;
|
||||
// auto impedance = timeData.impedance - delta->timeData.impedance;
|
||||
// QString ret;
|
||||
// ret += "ΔImpulse:"+Unit::ToString(impulse, "", "m ", 3)+" ΔStep:"+Unit::ToString(step, "", "m ", 3)+" ΔImpedance:";
|
||||
// if(isnan(timeData.impedance)) {
|
||||
// ret += "Invalid";
|
||||
// } else {
|
||||
// ret += Unit::ToString(impedance, "Ω", "m k", 3);
|
||||
// }
|
||||
// return ret;
|
||||
// } else {
|
||||
// calculate difference between markers
|
||||
auto freqDiff = position - delta->position;
|
||||
auto valueDiff = data / delta->data;
|
||||
auto phase = arg(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);
|
||||
}
|
||||
// }
|
||||
}
|
||||
break;
|
||||
case Type::Noise:
|
||||
@ -205,22 +176,22 @@ QString TraceMarker::readableData()
|
||||
|
||||
QString TraceMarker::readableSettings()
|
||||
{
|
||||
if(timeDomain) {
|
||||
switch(type) {
|
||||
case Type::Manual:
|
||||
case Type::Delta: {
|
||||
QString unit;
|
||||
if(position <= parentTrace->getTDR().back().time) {
|
||||
unit = "s";
|
||||
} else {
|
||||
unit = "m";
|
||||
}
|
||||
return Unit::ToString(position, unit, "fpnum k", 4);
|
||||
}
|
||||
default:
|
||||
return "Unhandled case";
|
||||
}
|
||||
} else {
|
||||
// if(timeDomain) {
|
||||
// switch(type) {
|
||||
// case Type::Manual:
|
||||
// case Type::Delta: {
|
||||
// QString unit;
|
||||
// if(position <= parentTrace->getTDR().back().time) {
|
||||
// unit = "s";
|
||||
// } else {
|
||||
// unit = "m";
|
||||
// }
|
||||
// return Unit::ToString(position, unit, "fpnum k", 4);
|
||||
// }
|
||||
// default:
|
||||
// return "Unhandled case";
|
||||
// }
|
||||
// } else {
|
||||
switch(type) {
|
||||
case Type::Manual:
|
||||
case Type::Maximum:
|
||||
@ -241,7 +212,7 @@ QString TraceMarker::readableSettings()
|
||||
default:
|
||||
return "Unhandled case";
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
QString TraceMarker::readableType()
|
||||
@ -270,12 +241,12 @@ void TraceMarker::traceDataChanged()
|
||||
{
|
||||
// some data of the parent trace changed, check if marker data also changed
|
||||
complex<double> newdata;
|
||||
if (timeDomain) {
|
||||
timeData = parentTrace->getTDR(position);
|
||||
newdata = complex<double>(timeData.stepResponse, timeData.impulseResponse);
|
||||
} else {
|
||||
newdata = parentTrace->getData(position);
|
||||
}
|
||||
// if (timeDomain) {
|
||||
// timeData = parentTrace->getTDR(position);
|
||||
// newdata = complex<double>(timeData.stepResponse, timeData.impulseResponse);
|
||||
// } else {
|
||||
newdata = parentTrace->sample(parentTrace->index(position)).y;
|
||||
// }
|
||||
if (newdata != data) {
|
||||
data = newdata;
|
||||
update();
|
||||
@ -309,11 +280,11 @@ std::set<TraceMarker::Type> TraceMarker::getSupportedTypes()
|
||||
{
|
||||
set<TraceMarker::Type> supported;
|
||||
if(parentTrace) {
|
||||
if(timeDomain) {
|
||||
// only basic markers in time domain
|
||||
supported.insert(Type::Manual);
|
||||
supported.insert(Type::Delta);
|
||||
} else {
|
||||
// if(timeDomain) {
|
||||
// // only basic markers in time domain
|
||||
// supported.insert(Type::Manual);
|
||||
// supported.insert(Type::Delta);
|
||||
// } else {
|
||||
// all traces support some basic markers
|
||||
supported.insert(Type::Manual);
|
||||
supported.insert(Type::Maximum);
|
||||
@ -340,7 +311,7 @@ std::set<TraceMarker::Type> TraceMarker::getSupportedTypes()
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
@ -348,21 +319,21 @@ std::set<TraceMarker::Type> TraceMarker::getSupportedTypes()
|
||||
void TraceMarker::constrainPosition()
|
||||
{
|
||||
if(parentTrace) {
|
||||
if(timeDomain) {
|
||||
if(position < 0) {
|
||||
position = 0;
|
||||
} else if(position > parentTrace->getTDR().back().distance) {
|
||||
position = parentTrace->getTDR().back().distance;
|
||||
}
|
||||
} else {
|
||||
// if(timeDomain) {
|
||||
// if(position < 0) {
|
||||
// position = 0;
|
||||
// } else if(position > parentTrace->getTDR().back().distance) {
|
||||
// position = parentTrace->getTDR().back().distance;
|
||||
// }
|
||||
// } else {
|
||||
if(parentTrace->size() > 0) {
|
||||
if(position > parentTrace->maxFreq()) {
|
||||
position = parentTrace->maxFreq();
|
||||
} else if(position < parentTrace->minFreq()) {
|
||||
position = parentTrace->minFreq();
|
||||
if(position > parentTrace->maxX()) {
|
||||
position = parentTrace->maxX();
|
||||
} else if(position < parentTrace->minX()) {
|
||||
position = parentTrace->minX();
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
traceDataChanged();
|
||||
}
|
||||
}
|
||||
@ -410,10 +381,10 @@ void TraceMarker::setType(TraceMarker::Type t)
|
||||
// invalid delta marker assigned, attempt to find a matching marker
|
||||
for(int pass = 0;pass < 3;pass++) {
|
||||
for(auto m : model->getMarkers()) {
|
||||
if(m->isTimeDomain() != isTimeDomain()) {
|
||||
// markers are not on the same domain
|
||||
continue;
|
||||
}
|
||||
// if(m->isTimeDomain() != isTimeDomain()) {
|
||||
// // markers are not on the same domain
|
||||
// continue;
|
||||
// }
|
||||
if(pass == 0 && m->parentTrace != parentTrace) {
|
||||
// ignore markers on different traces in first pass
|
||||
continue;
|
||||
@ -484,11 +455,6 @@ QString TraceMarker::getSuffix() const
|
||||
return suffix;
|
||||
}
|
||||
|
||||
bool TraceMarker::isTimeDomain() const
|
||||
{
|
||||
return timeDomain;
|
||||
}
|
||||
|
||||
const std::vector<TraceMarker *> &TraceMarker::getHelperMarkers() const
|
||||
{
|
||||
return helperMarkers;
|
||||
@ -594,15 +560,15 @@ void TraceMarker::updateTypeFromEditor(QWidget *w)
|
||||
|
||||
SIUnitEdit *TraceMarker::getSettingsEditor()
|
||||
{
|
||||
if(timeDomain) {
|
||||
switch(type) {
|
||||
case Type::Manual:
|
||||
case Type::Delta:
|
||||
return new SIUnitEdit("", "fpnum k", 6);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
// if(timeDomain) {
|
||||
// switch(type) {
|
||||
// case Type::Manual:
|
||||
// case Type::Delta:
|
||||
// return new SIUnitEdit("", "fpnum k", 6);
|
||||
// default:
|
||||
// return nullptr;
|
||||
// }
|
||||
// } else {
|
||||
switch(type) {
|
||||
case Type::Manual:
|
||||
case Type::Maximum:
|
||||
@ -619,7 +585,7 @@ SIUnitEdit *TraceMarker::getSettingsEditor()
|
||||
case Type::TOI:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
void TraceMarker::adjustSettings(double value)
|
||||
@ -798,15 +764,6 @@ std::complex<double> TraceMarker::getData() const
|
||||
return data;
|
||||
}
|
||||
|
||||
Trace::TimedomainData TraceMarker::getTimeData() const
|
||||
{
|
||||
Trace::TimedomainData ret = {};
|
||||
if(timeDomain) {
|
||||
ret = timeData;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool TraceMarker::isMovable()
|
||||
{
|
||||
if(parent) {
|
||||
|
@ -15,7 +15,6 @@ class TraceMarker : public QObject
|
||||
public:
|
||||
TraceMarker(TraceMarkerModel *model, int number = 1, TraceMarker *parent = nullptr, QString descr = QString());
|
||||
~TraceMarker();
|
||||
void setTimeDomain(bool timeDomain);
|
||||
void assignTrace(Trace *t);
|
||||
Trace* trace();
|
||||
QString readableData();
|
||||
@ -24,7 +23,6 @@ public:
|
||||
|
||||
double getPosition() const;
|
||||
std::complex<double> getData() const;
|
||||
Trace::TimedomainData getTimeData() const;
|
||||
bool isMovable();
|
||||
|
||||
QPixmap& getSymbol();
|
||||
@ -48,8 +46,6 @@ public:
|
||||
TraceMarker *helperMarker(unsigned int i);
|
||||
QString getSuffix() const;
|
||||
|
||||
bool isTimeDomain() const;
|
||||
|
||||
public slots:
|
||||
void setPosition(double freq);
|
||||
signals:
|
||||
@ -114,7 +110,6 @@ private:
|
||||
// Frequency domain: S parameter
|
||||
// Time domain: imag part is impulse response, real part is step response
|
||||
std::complex<double> data;
|
||||
Trace::TimedomainData timeData;
|
||||
QPixmap symbol;
|
||||
Type type;
|
||||
QString suffix;
|
||||
@ -128,8 +123,6 @@ private:
|
||||
double peakThreshold;
|
||||
double offset;
|
||||
};
|
||||
|
||||
bool timeDomain;
|
||||
};
|
||||
|
||||
#endif // TRACEMARKER_H
|
||||
|
@ -200,10 +200,7 @@ bool TraceMarkerModel::setData(const QModelIndex &index, const QVariant &value,
|
||||
break;
|
||||
case ColIndexTrace: {
|
||||
auto info = qvariant_cast<MarkerWidgetTraceInfo>(value);
|
||||
// always disable timedomain before switching trace
|
||||
m->setTimeDomain(false);
|
||||
m->assignTrace(info.trace);
|
||||
m->setTimeDomain(info.isTimeDomain);
|
||||
}
|
||||
break;
|
||||
case ColIndexSettings: {
|
||||
@ -288,12 +285,7 @@ QWidget *MarkerTraceDelegate::createEditor(QWidget *parent, const QStyleOptionVi
|
||||
for(auto t : traces) {
|
||||
MarkerWidgetTraceInfo info;
|
||||
info.trace = t;
|
||||
info.isTimeDomain = false;
|
||||
c->addItem(t->name(), QVariant::fromValue(info));
|
||||
if(t->TDRactive()) {
|
||||
info.isTimeDomain = true;
|
||||
c->addItem(t->name() + " Time Domain", QVariant::fromValue(info));
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@ -304,7 +296,6 @@ void MarkerTraceDelegate::setEditorData(QWidget *editor, const QModelIndex &inde
|
||||
auto c = (QComboBox*) editor;
|
||||
MarkerWidgetTraceInfo markerInfo;
|
||||
markerInfo.trace = marker->trace();
|
||||
markerInfo.isTimeDomain = marker->isTimeDomain();
|
||||
for(int i=0;i<c->count();i++) {
|
||||
auto info = qvariant_cast<MarkerWidgetTraceInfo>(c->itemData(i));
|
||||
if(info == markerInfo) {
|
||||
|
@ -19,13 +19,11 @@ class MarkerTraceDelegate : public QStyledItemDelegate
|
||||
class MarkerWidgetTraceInfo {
|
||||
public:
|
||||
Trace *trace;
|
||||
bool isTimeDomain;
|
||||
|
||||
};
|
||||
|
||||
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)
|
||||
|
||||
|
@ -23,9 +23,6 @@ void TraceModel::addTrace(Trace *t)
|
||||
connect(t, &Trace::nameChanged, [=]() {
|
||||
emit traceNameChanged(t);
|
||||
});
|
||||
connect(t, &Trace::changedTDRstate, [=](bool enabled) {
|
||||
emit traceTDRstateChanged(t, enabled);
|
||||
});
|
||||
traces.push_back(t);
|
||||
endInsertRows();
|
||||
emit traceAdded(t);
|
||||
|
@ -53,12 +53,14 @@ void TracePlot::enableTrace(Trace *t, bool enabled)
|
||||
connect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot);
|
||||
connect(t, &Trace::markerAdded, this, &TracePlot::markerAdded);
|
||||
connect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved);
|
||||
connect(t, &Trace::typeChanged, this, &TracePlot::checkIfStillSupported);
|
||||
} else {
|
||||
// disconnect from notifications
|
||||
disconnect(t, &Trace::dataChanged, this, &TracePlot::triggerReplot);
|
||||
disconnect(t, &Trace::visibilityChanged, this, &TracePlot::triggerReplot);
|
||||
disconnect(t, &Trace::markerAdded, this, &TracePlot::markerAdded);
|
||||
disconnect(t, &Trace::markerRemoved, this, &TracePlot::markerRemoved);
|
||||
disconnect(t, &Trace::typeChanged, this, &TracePlot::checkIfStillSupported);
|
||||
}
|
||||
updateContextMenu();
|
||||
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)
|
||||
{
|
||||
connect(m, &TraceMarker::dataChanged, this, &TracePlot::triggerReplot);
|
||||
|
@ -59,6 +59,7 @@ protected slots:
|
||||
void newTraceAvailable(Trace *t);
|
||||
void traceDeleted(Trace *t);
|
||||
void triggerReplot();
|
||||
void checkIfStillSupported(Trace *t);
|
||||
virtual void markerAdded(TraceMarker *m);
|
||||
virtual void markerRemoved(TraceMarker *m);
|
||||
protected:
|
||||
|
@ -56,12 +56,12 @@ std::complex<double> TraceSmithChart::pixelToData(QPoint p)
|
||||
QPoint TraceSmithChart::markerToPixel(TraceMarker *m)
|
||||
{
|
||||
QPoint ret = QPoint();
|
||||
if(!m->isTimeDomain()) {
|
||||
// if(!m->isTimeDomain()) {
|
||||
if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) {
|
||||
auto d = m->getData();
|
||||
ret = transform.map(QPoint(d.real() * smithCoordMax, -d.imag() * smithCoordMax));
|
||||
}
|
||||
}
|
||||
// }
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -160,9 +160,9 @@ void TraceSmithChart::draw(QPainter &p) {
|
||||
// only draw markers if the trace has at least one point
|
||||
auto markers = t.first->getMarkers();
|
||||
for(auto m : markers) {
|
||||
if (m->isTimeDomain()) {
|
||||
continue;
|
||||
}
|
||||
// if (m->isTimeDomain()) {
|
||||
// continue;
|
||||
// }
|
||||
if (limitToSpan && (m->getPosition() < sweep_fmin || m->getPosition() > sweep_fmax)) {
|
||||
continue;
|
||||
}
|
||||
@ -268,7 +268,7 @@ void TraceSmithChart::updateContextMenu()
|
||||
|
||||
bool TraceSmithChart::supported(Trace *t)
|
||||
{
|
||||
if(t->isReflection()) {
|
||||
if(t->outputType() == Trace::DataType::Frequency && t->isReflection()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -67,27 +67,14 @@ void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
if(isTDRtype(type) && !isTDRtype(oldType)) {
|
||||
// now a TDR axis
|
||||
for(auto t : tracesAxis[axis]) {
|
||||
t->addTDRinterest();
|
||||
}
|
||||
}
|
||||
}
|
||||
YAxis[axis].log = log;
|
||||
YAxis[axis].autorange = autorange;
|
||||
YAxis[axis].rangeMin = min;
|
||||
YAxis[axis].rangeMax = max;
|
||||
YAxis[axis].rangeDiv = div;
|
||||
removeUnsupportedTraces();
|
||||
updateAxisTicks();
|
||||
updateContextMenu();
|
||||
replot();
|
||||
@ -100,15 +87,14 @@ void TraceXYPlot::setXAxis(XAxisType type, XAxisMode mode, double min, double ma
|
||||
XAxis.rangeMin = min;
|
||||
XAxis.rangeMax = max;
|
||||
XAxis.rangeDiv = div;
|
||||
removeUnsupportedTraces();
|
||||
updateAxisTicks();
|
||||
}
|
||||
|
||||
void TraceXYPlot::enableTrace(Trace *t, bool enabled)
|
||||
{
|
||||
for(int axis = 0;axis < 2;axis++) {
|
||||
if(supported(t, YAxis[axis].type)) {
|
||||
enableTraceAxis(t, axis, enabled);
|
||||
}
|
||||
enableTraceAxis(t, axis, enabled && supported(t, YAxis[axis].type));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
if(YAxis[0].type != YAxisType::Disabled || YAxis[1].type != YAxisType::Disabled) {
|
||||
if(supported(t, YAxis[0].type) || supported(t, YAxis[1].type)) {
|
||||
return true;
|
||||
} else {
|
||||
// no axis
|
||||
@ -353,7 +339,7 @@ void TraceXYPlot::draw(QPainter &p)
|
||||
pen.setStyle(Qt::SolidLine);
|
||||
}
|
||||
p.setPen(pen);
|
||||
auto nPoints = numTraceSamples(t);
|
||||
auto nPoints = t->size();
|
||||
for(unsigned int j=1;j<nPoints;j++) {
|
||||
auto last = traceToCoordinate(t, j-1, 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
|
||||
auto markers = t->getMarkers();
|
||||
for(auto m : markers) {
|
||||
if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
|
||||
// wrong domain, skip this marker
|
||||
continue;
|
||||
}
|
||||
// if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
|
||||
// // wrong domain, skip this marker
|
||||
// continue;
|
||||
// }
|
||||
double xPosition;
|
||||
if(m->isTimeDomain()) {
|
||||
if(XAxis.type == XAxisType::Distance) {
|
||||
xPosition = m->getTimeData().distance;
|
||||
} else {
|
||||
xPosition = m->getTimeData().time;
|
||||
}
|
||||
} else {
|
||||
// if(m->isTimeDomain()) {
|
||||
// if(XAxis.type == XAxisType::Distance) {
|
||||
// xPosition = m->getTimeData().distance;
|
||||
// } else {
|
||||
// xPosition = m->getTimeData().time;
|
||||
// }
|
||||
// } else {
|
||||
xPosition = m->getPosition();
|
||||
}
|
||||
// }
|
||||
if (xPosition < XAxis.rangeMin || xPosition > XAxis.rangeMax) {
|
||||
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);
|
||||
if(!plotRect.contains(point)) {
|
||||
// out of screen
|
||||
@ -412,7 +399,8 @@ void TraceXYPlot::draw(QPainter &p)
|
||||
p.setOpacity(0.5);
|
||||
p.setBrush(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
|
||||
p.drawRect(plotRect);
|
||||
auto font = p.font();
|
||||
@ -500,27 +488,13 @@ void TraceXYPlot::updateAxisTicks()
|
||||
|| tracesAxis[1].find(t.first) != tracesAxis[1].end());
|
||||
auto trace = t.first;
|
||||
if(enabled && trace->isVisible()) {
|
||||
if(!numTraceSamples(trace)) {
|
||||
if(!trace->size()) {
|
||||
// empty trace, do not use for automatic axis calculation
|
||||
continue;
|
||||
}
|
||||
// this trace is currently displayed
|
||||
double trace_min = std::numeric_limits<double>::max();
|
||||
double trace_max = std::numeric_limits<double>::lowest();
|
||||
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;
|
||||
}
|
||||
double trace_min = trace->minX();
|
||||
double trace_max = trace->maxX();
|
||||
if(trace_min < min) {
|
||||
min = trace_min;
|
||||
}
|
||||
@ -546,7 +520,7 @@ void TraceXYPlot::updateAxisTicks()
|
||||
double max = std::numeric_limits<double>::lowest();
|
||||
double min = std::numeric_limits<double>::max();
|
||||
for(auto t : tracesAxis[i]) {
|
||||
unsigned int samples = numTraceSamples(t);
|
||||
unsigned int samples = t->size();
|
||||
for(unsigned int j=0;j<samples;j++) {
|
||||
auto point = traceToCoordinate(t, j, YAxis[i].type);
|
||||
|
||||
@ -557,7 +531,8 @@ void TraceXYPlot::updateAxisTicks()
|
||||
|
||||
if(point.y() > max) {
|
||||
max = point.y();
|
||||
} else if(point.y() < min) {
|
||||
}
|
||||
if(point.y() < min) {
|
||||
min = point.y();
|
||||
}
|
||||
}
|
||||
@ -608,14 +583,7 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled)
|
||||
if(alreadyEnabled != enabled) {
|
||||
if(enabled) {
|
||||
tracesAxis[axis].insert(t);
|
||||
// connect signals
|
||||
if(isTDRtype(YAxis[axis].type)) {
|
||||
t->addTDRinterest();
|
||||
}
|
||||
} else {
|
||||
if(isTDRtype(YAxis[axis].type)) {
|
||||
t->removeTDRinterest();
|
||||
}
|
||||
tracesAxis[axis].erase(t);
|
||||
if(axis == 0) {
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
case YAxisType::Disabled:
|
||||
return false;
|
||||
@ -648,66 +632,45 @@ bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
|
||||
return true;
|
||||
}
|
||||
|
||||
double TraceXYPlot::traceToCoordinate(std::complex<double> data, TraceXYPlot::YAxisType type)
|
||||
void TraceXYPlot::removeUnsupportedTraces()
|
||||
{
|
||||
switch(type) {
|
||||
case YAxisType::Magnitude:
|
||||
return 20*log10(abs(data));
|
||||
case YAxisType::Phase:
|
||||
return arg(data) * 180.0 / M_PI;
|
||||
case YAxisType::VSWR:
|
||||
if(abs(data) < 1.0) {
|
||||
return (1+abs(data)) / (1-abs(data));
|
||||
for(unsigned int i=0;i<2;i++) {
|
||||
auto set_copy = tracesAxis[i];
|
||||
for(auto t : set_copy) {
|
||||
if(!supported(t, YAxis[i].type)) {
|
||||
enableTraceAxis(t, i, false);
|
||||
}
|
||||
}
|
||||
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 ret = QPointF(numeric_limits<double>::quiet_NaN(), numeric_limits<double>::quiet_NaN());
|
||||
auto data = t->sample(sample);
|
||||
ret.setX(data.x);
|
||||
switch(type) {
|
||||
case YAxisType::Magnitude:
|
||||
ret.setY(20*log10(abs(data.y)));
|
||||
break;
|
||||
case YAxisType::Phase:
|
||||
case YAxisType::VSWR: {
|
||||
auto d = t->sample(sample);
|
||||
ret.setY(traceToCoordinate(d.y, type));
|
||||
ret.setX(d.x);
|
||||
}
|
||||
ret.setY(arg(data.y) * 180.0 / M_PI);
|
||||
break;
|
||||
case YAxisType::VSWR:
|
||||
if(abs(data.y) < 1.0) {
|
||||
ret.setY((1+abs(data.y)) / (1-abs(data.y)));
|
||||
}
|
||||
break;
|
||||
case YAxisType::Impulse:
|
||||
ret.setY(t->getTDR()[sample].impulseResponse);
|
||||
if(XAxis.type == XAxisType::Distance) {
|
||||
ret.setX(t->getTDR()[sample].distance);
|
||||
} else {
|
||||
ret.setX(t->getTDR()[sample].time);
|
||||
}
|
||||
ret.setY(real(data.y));
|
||||
break;
|
||||
case YAxisType::Step:
|
||||
ret.setY(t->getTDR()[sample].stepResponse);
|
||||
if(XAxis.type == XAxisType::Distance) {
|
||||
ret.setX(t->getTDR()[sample].distance);
|
||||
} else {
|
||||
ret.setX(t->getTDR()[sample].time);
|
||||
}
|
||||
ret.setY(t->sample(sample, Trace::SampleType::TimeStep).y.real());
|
||||
break;
|
||||
case YAxisType::Impedance: {
|
||||
ret.setY(t->getTDR()[sample].impedance);
|
||||
if(XAxis.type == XAxisType::Distance) {
|
||||
ret.setX(t->getTDR()[sample].distance);
|
||||
} else {
|
||||
ret.setX(t->getTDR()[sample].time);
|
||||
double step = t->sample(sample, Trace::SampleType::TimeStep).y.real();
|
||||
if(abs(step) < 1.0) {
|
||||
ret.setY(50 * (1.0+step) / (1.0-step));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -719,26 +682,17 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int TraceXYPlot::numTraceSamples(Trace *t)
|
||||
{
|
||||
if(XAxis.type == XAxisType::Frequency) {
|
||||
return t->size();
|
||||
} else {
|
||||
return t->getTDR().size();
|
||||
}
|
||||
}
|
||||
|
||||
QPoint TraceXYPlot::dataToPixel(Trace::Data d)
|
||||
{
|
||||
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::dataToPixel(Trace::Data d)
|
||||
//{
|
||||
// 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)
|
||||
{
|
||||
@ -759,22 +713,12 @@ QPointF TraceXYPlot::pixelToPlotValue(QPoint pixel, int Yaxis)
|
||||
QPoint TraceXYPlot::markerToPixel(TraceMarker *m)
|
||||
{
|
||||
QPoint ret = QPoint();
|
||||
if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
|
||||
// invalid domain
|
||||
return ret;
|
||||
}
|
||||
QPointF plotPoint;
|
||||
plotPoint.setY(traceToCoordinate(m->getData(), 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());
|
||||
}
|
||||
// if(m->isTimeDomain() != (XAxis.type != XAxisType::Frequency)) {
|
||||
// // invalid domain
|
||||
// return ret;
|
||||
// }
|
||||
auto t = m->getTrace();
|
||||
QPointF plotPoint = traceToCoordinate(t, t->index(m->getPosition()), YAxis[0].type);
|
||||
return plotValueToPixel(plotPoint, 0);
|
||||
}
|
||||
|
||||
@ -786,7 +730,7 @@ double TraceXYPlot::nearestTracePoint(Trace *t, QPoint pixel)
|
||||
}
|
||||
double closestDistance = numeric_limits<double>::max();
|
||||
double closestXpos = 0;
|
||||
auto samples = numTraceSamples(t);
|
||||
auto samples = t->size();
|
||||
for(unsigned int i=0;i<samples;i++) {
|
||||
auto point = traceToCoordinate(t, i, YAxis[0].type);
|
||||
if(isnan(point.x()) || isnan(point.y())) {
|
||||
|
@ -58,10 +58,9 @@ private:
|
||||
QString AxisTypeToName(YAxisType type);
|
||||
void enableTraceAxis(Trace *t, int axis, bool enabled);
|
||||
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);
|
||||
unsigned int numTraceSamples(Trace *t);
|
||||
QPoint dataToPixel(Trace::Data d);
|
||||
// QPoint dataToPixel(Trace::Data d);
|
||||
QPoint plotValueToPixel(QPointF plotValue, int Yaxis);
|
||||
QPointF pixelToPlotValue(QPoint pixel, int YAxis);
|
||||
QPoint markerToPixel(TraceMarker *m) override;
|
||||
|
Loading…
Reference in New Issue
Block a user