QWT axis force major tick at zero - c++

For a better unserstanding what I've done and what I've want to achive:
I want to set up a QwtPlot with a time Axis to display a signal (Voltage over time). I wrote a own implementation for the QwtScaleDraw so i can add an offset (lets say 1000µs) and change the "zero" value of the time axis.
No my system measures a event after 123µs plus this offset. So i want to display only this part of the plot with setInterval(1000, 1123) . But for better unserstanding of the plot I want to add some time before and after this event. Lets say 10% of the event length
10% of 123µs is 12.3µs => setInterval(987.7, 1135.3)
The axis show now a timespan from -12.3µs to 135.3µs.
NOW THE QUESTION: How could I force the axis to show a major tick at 0µs?
Do I have to use the QwtScaleEngine to calculate my ticks or is it a job for the QwtScaleDraw or is there already a function inside the Axis I've only missed?
EDIT:
I think the problem is my Offset. I substract this value direct from the original value inside the Function QwtText label(qreal value) const of my own implementation of QwtScaleDraw. So the zero to display is in this example the value 1000. Maybe its better so set the offset a other way?

I found a solution:
Create a own class inherit QwtLinearScaleEngine. Add a new member variable offset and add getter/setter. Overwrite the function divideScale and test if the given interval contains your offset. If not or when offset is null, use normal function of QwtLinearScaleEngine to calculate the divide scale.
When the interval contains your offset, go from that offset back (substract) with the given step size to the lower bound. Now you can go from this value up to the upper bound and add every value as a tick.
QwtScaleDiv QwtLinearScaleEngineEx::divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize) const
{
QwtScaleDiv div = QwtLinearScaleEngine::divideScale(x1, x2, maxMajorSteps, maxMinorSteps, stepSize);
QVector<QwtScaleDiv::TickType> tickTypes = QVector<QwtScaleDiv::TickType>() << QwtScaleDiv::MajorTick << QwtScaleDiv::MediumTick << QwtScaleDiv::MinorTick;
QList<double> ticksByType[QwtScaleDiv::NTickTypes];
if(offset() == 0 || !div.contains(offset())){
for(auto type : tickTypes){
ticksByType[type] = div.ticks(type);
}
}else{
for(auto type : tickTypes){
auto ticks = div.ticks(type);
if(ticks.length() > 1){
stepSize = qAbs(ticks[1]-ticks[0]);
for( double i=offset()-(stepSize*floor((offset()-div.lowerBound())/stepSize));
i<div.upperBound();
i+=stepSize )
{
ticksByType[type].append(i);
}
}
}
}
return QwtScaleDiv(QwtInterval(x1, x2), ticksByType);
}

Related

How can I properly determine the value of a variable based on where the slider is

So I am trying to make a UI system and I am currently working on a slider. The sliders are meant to help me change a variable's value quickly when my application is running. It consists of 2 rectangles. tRect is the horizontal on a slider and tSliderRect is the vertical rectangle that the user can move. I am setting the initial position of tSliderRect based on the value of the variable. The following code only runs in the first frame. My problem here is that it does not work if min has a negative value.
//min and max are for the variable's value
void SliderInit(const float variable, const float min, const float max) noexcept
{
//these positions are also adjusted depending on where the window is
//but that is done later and not important for this question.
tSliderRect.x = (variable / max) * tRect.width;
tSliderRect.width = 0.08f * tRect.width;
tSliderRect.y = tRect.height - (0.25f * tRect.height);
tSliderRect.height = 1.5f * tRect.height;
}
Same problem later when I try to calculate the variable's value depending on the position of tSliderRect. This is what I have. I don't know how I would adjust for min. What if min is negative?
void SetVariableValue(float& variable, float min, float max) const noexcept
{
variable = (tSliderRect.x / tRect.width) * max;
}
Essentially, I want to map the values from tSliderRect.x to tRect.width into min to max. Thanks.
Here is a lousy drawing to help my explanation.

Detecting peak value on realtime data stream

I'm using QCustomPlot to read and display realtime value from an IMU. This is how I set the realtimeDataSlot:
void Settings::realtimeDataSlot(double x_acceleration_g, double y_acceleration_g, double z_acceleration_g, double z_acceleration_gnew)
{
static QTime time(QTime::currentTime());
// calculate two new data points:
double key = time.elapsed()/1000.0; // time elapsed since start of demo, in seconds
static double lastPointKey = 0;
if (key-lastPointKey > 0.02) // at most add point every 20 ms
{
// add data to lines:
ui->customPlot->graph(0)->addData(key, x_acceleration_g); // X axis
ui->customPlot->graph(1)->addData(key, y_acceleration_g); // Y axis
ui->customPlot->graph(2)->addData(key, z_acceleration_g); // Z axis
ui->customPlot->graph(3)->addData(key, z_acceleration_gnew);
lastPointKey = key;
}
// make key axis range scroll with the data (at a constant range size of 8):
ui->customPlot->xAxis->setRange(key, 8, Qt::AlignRight);
ui->customPlot->replot();
// calculate frames per second:
static double lastFpsKey;
static int frameCount;
++frameCount;
if (key-lastFpsKey >2) // average fps over 2 seconds
{
ui->statusbar->showMessage(
QString("%1 FPS, Total Data points: %2")
.arg(frameCount/(key-lastFpsKey), 0, 'f', 0)
.arg(ui->customPlot->graph(0)->data()->size()+ui->customPlot->graph(1)->data()->size())
, 0);
lastFpsKey = key;
frameCount = 0;
}
}
which shows me as follows:
As a next step, I need to detect the peaks in any axis, say for example in the above figure in the Y axis there are peak values which I need to detect and count. Can somebody show me a way to do this?
Thanks in advance
EDIT
I marked in the peaks following figure:
I define peak as the figure that value (positive values) more than 0.25 g at high rate.
How to do it for the y-axis:
you define a window size on the x-axis, say 5 x-values of 100
let that window move from start to end of the x-axis, which means: for the first measure, look at the x-values number 0,1,2,3,4 and for the second measure, look at the x-values number 1,2,3,4,5 and so on
for each window measure: determine the maximum y-value in that window, and increase a score counter for the appropriate x-value.
after the complete move of the window from start to end you need to find the x-values with the highest score counters.
The size of your window gives you the number of peaks.
Also do the same for the minimum values to find the negative peaks.
Take care at the start and end of the graph.

How can I smooth the rate of change of a number data?

I'm a collage student and I'm new here. I've been having a problem with my assignment which I have to calculate the speed of a car. I've done with the right algorithm (using a 1/60s timer) and formula except that I have a problem with displaying the speed number. The output data changes very fast in 1 seconds (Yes, it will change very frequently in 1s since I use a 1/60s timer). Is there any way to smooth the rate of change within that output?
I've tried to round the number but the rate of change still very quick.
//For example Car1 object is moving along the x axis
//My method to calculate the speed with a 1/60s timer
//every 1/60s timeout:
if(distanceToggler == true ){
vDistance[0] = car->getCarPos().x();
}
else {
vDistance[1] = car->getCarPos().x();
}
//if Ture assign to vDistance[0] else assign to vDistance[1]
distanceToggler = !distanceToggler;
if ( (vDistance[1] - vDistance[0]) >= 0 ){
defaultSetting.editCurrentCarSpeed( (vDistance[1]-vDistance[0]) / (0.6f) );
}
currentCarSpeed = (vDistance[0]-vDistance[1]) / (0.6f);
A simple way to smooth noisy values arriving frequently is to keep a kind of running average and only adjust it by a percentage of each new value:
const float smooth_factor = 0.05f;
// Assume the first sample is correct (alternatively you could initialize to 0)
float smooth_v;
std::cin >> smooth_v;
// Read samples and output filtered samples
for(float v; std::cin >> v; )
{
smooth_v = (1.0f - smooth_factor) * smooth_v + smooth_factor * v;
std::cout << smooth_v << std::endl;
}
The smaller you make smooth_factor, the slower the "smooth" value will change in response to new data. You can tweak this value to something suitable to your application.
This is a fast alternative to taking an unweighted windowed average (although such averages can be computed in constant time), although it's slightly different in that every historical value has some effect (which reduces with time).

Create a plot graphs with c++ visual studio

The Y axis will be scaled automatically, depending on the values coming fallowing function.
void mouseHandleCordinate(double val){
// include graph function.
}
So I want to create the plot chart against the time. X axis represent time and Y represent the value coming above function. How I create above graph function.
Always pass the data into void mouseHandleCordinate(double val) function.
As example:
val >>> 2.1,3,1,6,7,5.5,0,9,5,6,7,3.6,2,5,6,7,8,1,2,3,4 >> represent double val
Time>>> 21,20,19,18,17,......., 4,3,2,1 second
not sure I got what you asking but looks like you want to create continuous function following table of sampled points like:
const int N=21; // number of samples
const double t0=21.0,t1=1.0; // start,end times
const double val[N]={ 2.1,3,1,6,7,5.5,0,9,5,6,7,3.6,2,5,6,7,8,1,2,3,4 };
double f(double t) // nearest
{
t = (t-t0)/(t1-t0); // time scaled to <0,1>
t*= (N-1); // time scaled to <0,N) .. index in table
int ix=t; // convert to closest index in val[]
if ((ix<0)||(ix>=N)) return 0.0; // handle undefined times
return val[ix]; // return closest point in val[]
}
This will give you the nearest neighbor style function value. If you need something better use linear or cubic or better interpolation for example:
double f(double t) // linear
{
t = (t-t0)/(t1-t0); // time scaled to <0,1>
t*= (N-1); // time scaled to <0,N) .. index in table
int ix=t; // convert to closest index in val[]
if ((ix<0)||(ix>=N)) return 0.0; // handle undefined times
if (ix==N-1) return val[ix]; // return closest point in val[] if on edge
// linear interpolation
t = t-floor(t); // distance of time between ix and ix+1 points scaled to <0,1>
return val[ix]+(val[ix+1]-val[ix])*t; // return linear interpolated value
}
Now to your problem:
void mouseHandleCordinate(double mx) // mouse x coordinate in [pixels] I assume
{
double t,x,y,x0,y0
// plot
x=0;
y=f(view_start_time)
for (t=view_start_time;t<=view_end_time;t+=(view_end_time-view_start_time)/view_size_in_pixels,x0=x,x++)
{
y0=y; y=f(t);
// render line x0,y0,x,y
}
// mouse highlight
t = view_start_time+((view_end_time-view_start_time)*mx/view_size_in_pixels);
x = mx;
y = f(t);
// render point x,y ... for example with circle r = 16 pixels
}
where:
view_start_time is time of the left most pixel in your plot view
view_end_time is time of the right most pixel in your plot view
view_size_in_pixels is the x resolution in [pixels] of your plot view
[Notes]
I code the stuff directly in SO editor so there may be typos ...
Hoping you are calling the mouseHandleCordinate in some Paint event instead of on mouse movement which should only schedule repaint order... Also you should add y view scale in similar manner to x scale I used ...
For more info see:
How can i produce multi point linear interpolation?

Linear size increase ends up increasing by 1 more sometimes

I have a flow layout. Inside it I have about 900 tables. Each table is stacked one on top of the other. I have a slider which resizes them and thus causes the flow layout to resize too.
The problem is, the tables should be linearly resizing. Their base size is 200x200. So when scale = 1.0, the w and h of the tables is 200.
Here is an example of the problem:
My issue is when delta is 8 instead of 9. What could I do to make sure my increases are always linear?
void LobbyTableManagaer::changeTableScale( double scale )
{
setTableScale(scale);
}
void LobbyTableManager::setTableScale( double scale )
{
scale += 0.3;
scale *= 2.0;
float scrollRel = m_vScroll->getRelativeValue();
setScale(scale);
rescaleTables();
resizeFlow();
...
double LobbyTableManager::getTableScale() const
{
return (getInnerWidth() / 700.0) * getScale();
}
void LobbyFilterManager::valueChanged( agui::Slider* source,int val )
{
if(source == m_magnifySlider)
{
DISPATCH_LOBBY_EVENT
{
(*it)->changeTableScale((double)val / source->getRange());
}
}
}
In short, I would like to ensure that the tables always increase by a linear amount. I cant understand why every few times delta is 8 rather than 9.
Thanks
Look at your "200 X Table Scale" values, they are going up by about 8.8. So when it is rounded to an integer, it will be 9 more than the previous value about 80% of the time and 8 more the other 20% of the time.
If you really need the increases to be the same size every time, you have to do everything with integers. Otherwise, you have to adjust your scale changes so the result is closer to 9.0.