Author |
Topic |
wygonski
USA
18 Posts |
Posted - 17 Sep 2016 : 13:13:21
|
I am having trouble solving a problem with a RT scrolling graph that is a single SimpleLinePlot. I'm using your QCRTGraph library for Android Studio. Apologies if I'm posting in the wrong forum, but there doesn't seem to be an Android-specific forum.
Here's the issue:
The scrolling plot needs to display one of three signals under user control. My data rate is 200 points/sec, and I am updating the display every 200ms. I have set setScrollScaleModeY to RT_AUTOSCALE_Y_MINMAX, and this works well for two of the three signals whose value is constrained to a range of amplitude values (e.g. periodic sine wave). However, for a signal that is "noisy", the scroll graph lags significantly to the point of being unusable. (What I mean by "noisy" is that the data is roughly constrained to a range of 50, but the center point of the range can vary. I hope that adequately describes the problematic data).
My hypothesis is that the autoscaling of the y-axis is slowing down the updates. Is this hypothesis correct? If so, is there any other mode that I can set to handle this?
In trying to solve my problem, I found this post on your forum that seems to address the same issue. https://www.quinn-curtis.com/qxy/topic.asp?TOPIC_ID=388&SearchTerms=RT_AUTOSCALE_Y_MINMAX
But the solution proposed by one of the members seems to reference a class that's not in the library that I have (ChartObjScale.ScaleStopY)? Do you have any suggestions along the lines of that solution?
|
|
quinncurtis
1586 Posts |
Posted - 17 Sep 2016 : 16:48:21
|
Scale the y-axis initially to your range of data. Use the RT_NO_AUTOSCALE_Y mode instead of RT_AUTOSCALE_Y_MINMAX. That should turn off auto-scaling of the y-axis. See what the results are. What is the extent of your x-axis in seconds. That will tell us how many points you are trying to plot? In any reply, show the complete setup of the RTScrollFrame object. |
|
|
wygonski
USA
18 Posts |
Posted - 17 Sep 2016 : 19:17:11
|
Thanks for the quick reply. I'm displaying 3s of data which is acquired at a 200 points/sec rate. I am updating the RTProcessVar in blocks of 32 points, and the display is updated every 200ms.
Here's my initialization code:
Constructor:
public RTScrollPlotSingle(Context context) {
super(context);
setFocusable(true);
InitializeGraph();
//startTimer(runPeriodMilliseconds);
updateDisplay(); // JJW added to see if it helps with initial plot region rendering issue
}
public void InitializeGraph()
{
this.setPreferredSize(new ChartDimension(1100,300));
inputChannel1 = new RTProcessVar("Mag #1", new ChartAttribute(ChartColor.LIGHTGREEN, 1.0, ChartConstants.LS_SOLID, ChartColor.LIGHTGREEN));
// override lib defaults in order to allow plotting of negative numbers (default is to clamp to 0)
inputChannel1.setMinimumValue(-Double.MAX_VALUE);
inputChannel1.setMaximumValue(Double.MAX_VALUE);
inputChannel1.setDefaultMinimumDisplayValue(-2000);
inputChannel1.setDefaultMaximumDisplayValue(2000);
inputChannel1.setCurrentValue( inputChannelValue1);
// Important, enables historical data collection for scroll graphs
inputChannel1.setDatasetEnableUpdate(true);
inputChannel1.setCurrentValue(0.0,0.0);
InitializeGraph1();
}
private void InitializeGraph1()
{
ChartView chartVu = this;
ChartAttribute attrib1 = new ChartAttribute (ChartColor.YELLOW, 3,ChartConstants.LS_SOLID);
ChartAttribute attrib2 = new ChartAttribute (ChartColor.RED, 3,ChartConstants.LS_SOLID);
pTransform1 = new CartesianCoordinates(0.0, -10.0, 3.0, 10.0 );
pTransform1.setGraphBorderDiagonal(0.1, 0.1, 0.92, 0.9);
Background graphbackground = new Background( pTransform1, ChartConstants.GRAPH_BACKGROUND, ChartColor.WHITE);
chartVu.addChartObject(graphbackground);
Background plotbackground = new Background( pTransform1, ChartConstants.PLOT_BACKGROUND, ChartColor.BLACK);
chartVu.addChartObject(plotbackground);
xaxis1 = new LinearAxis(pTransform1, ChartConstants.X_AXIS);
xaxis1.setColor(ChartColor.BLACK);
chartVu.addChartObject(xaxis1);
yaxis1 = new LinearAxis(pTransform1, ChartConstants.Y_AXIS);
yaxis1.setColor(ChartColor.BLACK);
chartVu.addChartObject(yaxis1);
AxisTitle yaxistitle = new AxisTitle(yaxis1, font16Bold, "Magnetic Field (pT)");
chartVu.addChartObject(yaxistitle);
Grid yAxisGrid = new Grid(xaxis1, yaxis1, ChartConstants.Y_AXIS, ChartConstants.GRID_MAJOR);
yAxisGrid.setColor(ChartColor.DARKGRAY);
yAxisGrid.setLineWidth(1);
yAxisGrid.setLineStyle(ChartConstants.LS_SOLID);
chartVu.addChartObject(yAxisGrid);
Grid xAxisGrid = new Grid(xaxis1, yaxis1, ChartConstants.X_AXIS, ChartConstants.GRID_ALL);
xAxisGrid.setColor(ChartColor.DARKGRAY);
xAxisGrid.setLineWidth(1);
xAxisGrid.setLineStyle(ChartConstants.LS_SOLID);
chartVu.addChartObject(xAxisGrid);
NumericAxisLabels xAxisLab = new NumericAxisLabels(xaxis1);
xAxisLab.setTextFont(font14);
xAxisLab.setColor(ChartColor.BLACK);
chartVu.addChartObject(xAxisLab);
NumericAxisLabels yAxisLab = new NumericAxisLabels(yaxis1);
yAxisLab.setTextFont(font14);
yAxisLab.setColor(ChartColor.BLACK);
chartVu.addChartObject(yAxisLab);
SimpleLinePlot lineplot1 = new SimpleLinePlot(pTransform1, null, attrib1);
lineplot1.setFastClipMode( ChartConstants.FASTCLIP_X);
rtPlot1 = new RTSimpleSingleValuePlot(pTransform1,lineplot1, inputChannel1);
chartVu.addChartObject(rtPlot1);
scrollFrame = new RTScrollFrame(this, inputChannel1, pTransform1, ChartConstants.RT_FIXEDEXTENT_MOVINGSTART_AUTOSCROLL);
scrollFrame.setTimeStampMode(ChartConstants.RT_MONOTONIC_X_MODE);
scrollFrame.setScrollScaleModeY( ChartConstants.RT_AUTOSCALE_Y_MINMAX);
scrollFrame.setScrollRescaleMargin( 0.05 );
// Allow 10 samples to accumlate before autoscaling y-axis. This prevents rapid
// changes of the y-scale for the first few samples
scrollFrame.setMinSamplesForAutoScale ( 10 );
chartVu.addChartObject(scrollFrame);
}
|
|
|
quinncurtis
1586 Posts |
Posted - 18 Sep 2016 : 12:21:28
|
So what was the result if you turned the y-axis auto-scaling off as described in our previous post? |
|
|
wygonski
USA
18 Posts |
Posted - 18 Sep 2016 : 14:58:12
|
So when I turn off auto scale and manually set the axis range like so:
scrollFrame.setScrollScaleModeY( ChartConstants.RT_NO_AUTOSCALE_Y);
scrollFrame.getChartObjScale().setScaleStartY( 300.0 );
scrollFrame.getChartObjScale().setScaleStopY( 400.0 );
// was scrollFrame.setScrollScaleModeY( ChartConstants.RT_AUTOSCALE_Y_MINMAX);
The scrolling plot behavior is acceptable, no lag is observed. However, I still need some scaling of the axis because the data range is not fixed.
So that led me to think that I could update the Y axis scaling just before the display is updated by obtaining a min and max value from the RTProcessVar history. Is that a good approach? Other suggestions?
|
|
|
wygonski
USA
18 Posts |
Posted - 18 Sep 2016 : 15:29:38
|
This is what I tried next, with the idea that I would scale the axis only when updating the display:
private void updateDisplay()
{
double minH, maxH;
minH = inputChannel1.getMinHistoryValue(40); // scale from most recent n samples
maxH = inputChannel1.getMaxHistoryValue(40);
scrollFrame.getChartObjScale().setScaleStartY( minH );
scrollFrame.getChartObjScale().setScaleStopY( maxH );
this.updateDraw();
}
But the plot and UI still lag. |
|
|
quinncurtis
1586 Posts |
Posted - 19 Sep 2016 : 09:16:16
|
As an experiment, instead of using getMinHistoryValue/getMaxHistoryValue to find the min and max value, add some code to your data collection routine, and do a running test for the min and max value, and use those two values to rescale the scroll frame.
// In your data collection if (newvalue > maxH) maxH= newValue;
if (newvalue < minH) minH= newValue; . . .
// In your update routine scrollFrame.getChartObjScale().setScaleStartY( minH ); scrollFrame.getChartObjScale().setScaleStopY( maxH );
What are the results?
|
|
|
wygonski
USA
18 Posts |
Posted - 19 Sep 2016 : 12:06:10
|
Initial testing of your suggestion shows that the plot updates are now smooth and responsive. So the conclusion is that autoscaling with RT_AUTOSCALE_Y_MINMAX or using getMinHistoryValue() and getMaxHistoryValue() are too resource-intensive for my update rate?
I'm asking because the axis autoscaling needs to be able to reduce the displayed range, too. So should I handle this also outside of your API? |
|
|
quinncurtis
1586 Posts |
Posted - 19 Sep 2016 : 13:06:27
|
I would not expect the simple getMinHistoryValue(40) call slow things down like you describe. It is just running through 40 values of the history, looking for the minimum value (same for the getMinHistoryValue method).
Maybe it has something to do with multi-threading, which the software is not designed to do. Are you calling your updateData method asynchronous to your updateDisplay method (using two different timer events)? In that case there may be some sort of deadlock. Change your code so that you only use one timer, and you update the display from within the updateData method, once every Nth data update. In your example, it would be around once every 40th update, assuming a data update of 200 Hz. This would prevent simulatenous entry of the RTProcessVar variable from two different threads. If that seems to work, re-introduce the scroll frame auto-scaling. |
|
|
wygonski
USA
18 Posts |
Posted - 20 Sep 2016 : 03:03:37
|
I'm receiving data from a separate thread that handles Bluetooth comms. The data is passed back to my Main Activity which then saves the data to a file and adds the data to the RTProcessVar. Both of these are implemented as Runnables. The display update is implemented by a Timer callback which calls updateDisplay, as in your examples.
So the problem as you point out is that I must update the RTProcessVar and also call updateDisplay() from the UI thread, not separate threads, correct? I'll make the changes you suggested and let you know the outcome. |
|
|
wygonski
USA
18 Posts |
Posted - 20 Sep 2016 : 03:19:35
|
I just wanted to add that the reason why I implemented the data update on a separate thread was that I was getting the following message when running it on the UI thread: Choreographer message: "Skipped 365 frames! The application may be doing too much work on its main thread." |
|
|
wygonski
USA
18 Posts |
Posted - 20 Sep 2016 : 03:59:25
|
I take back what I said. This logcat output (I think) shows that updating the RTProcessVar and updating the display are done on the same thread:
09-20 00:41:01.972 24513-24513/com.quspin.quspinmaghost V/RTScrollPlotSingle: Updating the RTProcessVar
09-20 00:41:01.992 24513-24513/com.quspin.quspinmaghost V/RTScrollPlotSingle: Updating the display
09-20 00:41:02.132 24513-24513/com.quspin.quspinmaghost V/RTScrollPlotSingle: Updating the RTProcessVar
09-20 00:41:02.192 24513-24513/com.quspin.quspinmaghost V/RTScrollPlotSingle: Updating the display
09-20 00:41:02.312 24513-24513/com.quspin.quspinmaghost V/RTScrollPlotSingle: Updating the RTProcessVar
09-20 00:41:02.392 24513-24513/com.quspin.quspinmaghost V/RTScrollPlotSingle: Updating the display
09-20 00:41:02.492 24513-24513/com.quspin.quspinmaghost V/RTScrollPlotSingle: Updating the RTProcessVar
09-20 00:41:02.592 24513-24513/com.quspin.quspinmaghost V/RTScrollPlotSingle: Updating the display
09-20 00:41:02.712 24513-24513/com.quspin.quspinmaghost V/RTScrollPlotSingle: Updating the RTProcessVar
|
|
|
quinncurtis
1586 Posts |
Posted - 20 Sep 2016 : 09:09:38
|
The way you describe your program, collecting data in a separate thread, and then passing the buffered data back to the main thread, where it is added to the RTProcessVar, and where the update display takes place, sounds correct. I would question whether using a file to transfer the data is a good idea. Why not use some sort of array, collection or queue to do that, eliminating the file I/O open and close overhead.
As an experiment, simulate your data in the main thread and use that to update the RTProcessVar. If that enables fast scrolling, then you know that the problem is somehow related to your data acquisition task or file buffering scheme. Do you have any sort of measure of what % of the CPU cycles are being used just by the data acquisition routine? |
|
|
wygonski
USA
18 Posts |
Posted - 20 Sep 2016 : 23:49:35
|
I meant to say that the data saving to file is a required feature that is implemented separate from the plotting. The data buffer is time-stamped and saved to a file as one Runnable, and the input data is also used to update the RTProcessVar in another Runnable. The input data is processed in blocks of 32 points with the Runnable, so that implements sort of a FIFO queue.
I will disable the file save to see if it has any impact on performance.
|
|
|
wygonski
USA
18 Posts |
Posted - 21 Sep 2016 : 15:52:03
|
I'm reporting some additional observations of the problem with the lagging graph. For these results, the only axis range scaling that is done is when switching from one dataset to another. I totally removed the feature of saving data to a file, with no impact on the problem -- graph is still lagging and the UI is slow to respond to button presses, etc. When actively plotting, user (non-kernal) CPU time goes from near 0% to 50% no matter what dataset being plotted.
For data that is a square wave toggling between -1000 and 1000, and for data that is steadily increasing, there are no problems with UI performance. For data that is "random" but limited to the range 325 to 345, the plot lags and the UI is slow to respond, when the Y axis scale is set to 320 to 350 (fixed). Interestingly, if the Y axis scale is set to -1000 to 1000 (fixed) such that the data now appears as a horizontal line, the problem goes away--scrolling plot is smooth, no lag, and UI is responsive.
If I drop the time interval that I'm plotting from 3s to 1s (rate is 200 points/sec) the problem goes away, plot does not lag and UI is responsive.
Any thoughts?
|
|
|
wygonski
USA
18 Posts |
Posted - 21 Sep 2016 : 16:01:17
|
I forgot to add that if I disable the timer that causes the display to update, user CPU time is under 7%. In this case, data streaming from Bluetooth and updating RTProcessVar is still occurring. |
|
|
Topic |
|
|
|