The chapter discusses Strip Charts, RealTime Charts with Gigasoft's charting components.
Stripcharts and real-time charts can be implemented in a variety of ways. Also, all ProEssentials graph controls can be used in real-time high speed updating scenarios. Real-time is the repeated process of changing the graph control's data and/or properties and refreshing the image.
Graph Control for Strip Charts
The Graph component will likely be the most common component used in a real-time scenario. It is also designed to be the easiest to implement. The properties AppendYData and AppendPointLabelData can be used to quickly and easily implement a strip chart. A strip chart is one that as you feed data, each new feed shifts the chart to the left or right. The direction of the shift is controlled with the property AppendToEnd.
Below is some example code which will produce a strip chart. The first section is used to initialize the component, preparing for real-time operation. The second section is used in a timer or other repeated event. WinForm, VCL, and C/C++ code will be similar in structure. The example code included with ProEssentials also has examples of real-time implementations. Within the demo and example projects see examples 017, 020, scientific examples 115, 116, 117, 117, and 119 for working stripchart and realtime charts.
Initialization of Strip Chart
Pego1.PrepareImages = True
Pego1.Subsets = 2
Pego1.Points = 200
Pego1.PointsToGraph = 20
Pego1.PointsToGraphInit = PEPTGI_LASTPOINTS 'Show Last Points Initially
'** Manually configure scales **'
Pego1.ManualScaleControlY = PEMSC_MINMAX
Pego1.ManualMaxY = 100
Pego1.ManualMinY = 1
Pego1.ManualMaxDataString = "000.000"
Pego1.ManualMaxPointLabel = "00:00:00xx"
'** Set various properties commonly used in real-time **'
Pego1.NoStackedData = True
Pego1.NoRandomPointsToGraph = True
Pego1.AllowHistogram = False
Pego1.FocalRect = False
'** Needed to allocate point labels so append logic works **'
'** Set last point label, 199 = Points - 1 **'
Pego1.PointLabels(199) = ""
'** Clear out first four data points **'
Pego1.YData(0, 0) = 0
Pego1.YData(0, 1) = 0
Pego1.YData(0, 2) = 0
Pego1.YData(0, 3) = 0
'** Start Timer **'
Timer1.Enabled = True
Strip Chart Timer Section
Dim CurrentTime As String
'** new point label **'
CurrentTime = Time$ ' Time$ is a function to return the time as a string
'** new YData, we need to prepare two pieces of data **'
ReDim NewData(2) As Single
NewData(0) = (Rnd * 20) + 2
NewData(1) = (Rnd * 40) + 60
'** transfer new point label **'
Call PEvset(Pego1.hObject, PEP_szaAPPENDPOINTLABELDATA, ByVal CurrentTime, 1)
'** transfer new YData **'
'** this will also update and view new image **'
Call PEvset(Pego1.hObject, PEP_faAPPENDYDATA, NewData(0), 1)
Strip Chart Example Code Explanation
Setting PrepareImages to TRUE is important and gives you flicker-free output.
Next, Subsets and Points define how much data the component will hold, PointsToGraph is set to 20 and controls how much data the user will see. There will be a horizontal scrollbar to pan left and right through data. PointsToGraphInit controls whether the first 20 or last 20 points are initially displayed.
Next, the graph's y axis is manually configured. This is because during the component's real-time operation, it will not automatically adjust the scales. However, if you do want the component's scale to adjust during real-time, this is possible and is discussed later. ManualMaxDataString and ManualMaxPointLabel are used to set a string which would represent the largest string in either the table or PointLabels respectively. This is used to reserve space during the component's image construction process to prevent overlapping text.
Next, a few properties are set which are recommended for real-time operation.
Next, the last PointLabel element is set to a null string. This allocates memory for all PointLabels and this is necessary for the append logic to work correctly. If memory is not pre-allocated, there will be nothing to shift.
Next, the first four default data points are set to zero. The default NullDataValue is zero so these lines of code essentially empty the graph of all valid data. Thus starting with an empty chart.
Finally, the component is initialized with PEreinitialize and PEresetimage, and the timer mechanism is started.
Within the timer event, we prepare new PointLabels and YData to append to the chart.
CurrentTime is a string which will show below the new data value. You use PEvset and AppendPointLabelData to pass the address of a null-terminated string to append to the PointLabels property array. If more than one string is going to be appended, they need to all exist in the one null-terminated string, but be delimited with the tab character. Usually you will just pass one string at a time. Note that ByVal is the mechanism in Visual Basic used to de-reference a string handle and provide the actual pointer to the string data. In Delphi you would use the @ operator to provide the address of the string, and in C/C++ the & operator.
New YData is also prepared and also passed by reference. NewData(0) is Visual Basic's method of passing the data by reference. Since this graph component has 2 subsets, and we will be updating one sample's worth of data, 2 pieces of data are prepared for appending to the YData array. Note that the last argument in the PEvset call is 1. This stands for 1 new sample. Since the control knows how many subsets are included in the graph component (2), it will automatically look for 2 pieces of data. This is different in how PEvset is used with the non-appending type properties such as YData. If more than one sample's worth of data is being appended, you still format the data in a single dimension array. In this case you will prepare all of the first subset's data, followed by the second subset's data and so on. Using PEvset with AppendYData also has a special function of resetting and updating the image. Because of this, you will always use AppendYData after AppendPointLabelData.
If you need the graph to update the y axis while in a real-time operation, you have to call PEactions = 0, or PEreinitialize, PEresetimage, and InvalidateRect at the end of the timer event.
All other components for Real-Time Strip Charts
ProEssentials includes appending type properties for all the data type arrays. For example, AppendXData is the property you would use to append data to the XData property array. There are also double precision versions of these properties like AppendXDataII. Plus AppendPointColors can append PointColors if per point coloring is being implemented.
Scientific Graph controls are implemented in a real-time operation similarly to the Graph control. You will just use slightly different properties to pass data and you will have to call PEreinitialize, PEresetimage, and InvalidateRect (Refresh) at the end of the timer event.
The example code included with ProEssentials includes several Scientific Graph Control strip chart and real-time examples. Refer to these for more information on how to implement strip charts with the Scientific Graph. Examples 115, 116, 117, 118, and 119.
We've added example code showing another variation of real-time charting. Refer to examples 020 within the demo and example projects to see this realtime first hand.
This function is designed to partially update an image, in other words, not having to update all data points. It contains two arguments which define the first data point, and quantity of data points to update. The developer will adjust/add a varying amount of data to the YData and possibly XData property arrays. After changing this data, the developer calls PEpartialresetimage with the starting point index, and the number of data-point indices adjusted. All subsets need to redraw the same number of points. This function will then invoke code to draw only those point indices selected to a cached memory device context (DC). So, when using PEpartialresetimage, CacheBmp must be set to TRUE in the control's initial initialization.
Two common real-time strip chart scenarios will use this function. The first is when you have a large data set, (thousands of data points), and you want to quickly add a data point at the end of existing data. This means that Points is being set to a larger size than actually being currently shown in the image. The developer is thus padding the unused data indices with the NullDataValue or leaving them zero if the default null data value is not changed. This type of real-time update is great for a quick addition, however, the grid is not updated. Occasionally, a full call to PEresetimage is needed to update all data points along with the grid and other non-data attributes. See example 020 for an actual realtime implementation.
Another scenario is if you want to continuously plot to the grid area, and preserve everything drawn. This will essentially let you produce a chart with millions of plotted data values, but actually, only plotting 1 or more during any one update. For example, the developer can set the chart up to contain only two data points. Upon a timed event, the developer updates the two data points and calls PEpartialresetimage to update the chart's current image. Note that when attempting to update a currently drawn line segment, you will have to include the last point of the last update as the first point of the current update. This will continue the line from the last point drawn. Also note that when the control gets a WM_PAINT event caused by the operating system, your image may be reset and start over from an empty graph. Based on the intended use, this may or may-not be a problem. If it is, you can increase the Points property and redraw this larger amount of points, then revert back to just updating one line segement per update.
3D Scientific Graph Control and 3D strip charts and realtime charting.
The PEreconstruct3Dpolygons function to aid in 3D real-time strip charts. See example 410, 411, and 412 within the demo. These examples are similar to 017 or 115 discussed above.