From 04:00 PM CDT – 08:00 PM CDT (09:00 PM UTC – 01:00 AM UTC) Tuesday, April 16, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

VerticalIntensityChartCollection freeze and unreponsive

Solved!
Go to solution

Hi,

We are using VerticalIntensityChartCollection described in M20207.

We want to build a high resolution intensity graph and set VerticalIntensityChartCollection capacity as 400  to be able to increase vertical resolution and GenerateData method creates randon data array with size of graph's width.

 

Timer thicks every 200 milliseconds and when that interval decrese graph start to freeze and application become unreponsive.

I have attached complete example and in case of failure added code below.

 

Best Regard,

Hakan,

<Window x:Class="VerticalIntensityGraphExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ni="http://schemas.ni.com/controls/2009/xaml/presentation"
        Title="MainWindow" Height="480" Width="640" WindowState="Maximized" >
    <Grid>
        <ni:IntensityGraph  x:Name="graph">
            <ni:IntensityGraph.ColorScale>
                <ni:ColorScale x:Name="colorScale1">
                    <ni:ColorScaleMarker Color="Red" Value="0"/>
                    <ni:ColorScaleMarker Color="Orange" Value="-10"/>
                    <ni:ColorScaleMarker Color="Yellow" Value="-40"/>
                    <ni:ColorScaleMarker Color="Green" Value="-65"/>
                    <ni:ColorScaleMarker Color="Blue" Value="-100"/>
                    <ni:ColorScaleMarker Color="DarkBlue" Value="-120" />
                </ni:ColorScale>
            </ni:IntensityGraph.ColorScale>
            <ni:IntensityGraph.VerticalAxis>
                <ni:AxisDouble x:Name="verticalAxis1" Adjuster="ContinuousChart" Orientation="Vertical"/>
            </ni:IntensityGraph.VerticalAxis>
            <ni:IntensityGraph.HorizontalAxis>
                <ni:AxisDouble x:Name="xAxis" Adjuster="FitExactly" Orientation="Horizontal" Range="750, 1250, System.Double" />
            </ni:IntensityGraph.HorizontalAxis>
        </ni:IntensityGraph>
    </Grid>
</Window>

 

 

namespace VerticalIntensityGraphExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private const double YIncrement = 10/400.00;
        private readonly DispatcherTimer timer;
        private readonly VerticalIntensityChartCollection<double, double> chart;
        public MainWindow()
        {
            InitializeComponent();

            chart = new VerticalIntensityChartCollection<double, double>(capacity: 400);
            // Initialize chart with data.
            chart.XStart = 750.0;
            //chart.XIncrement = 100.0;
            chart.Append(0, new double[0]);
            // Tell graph about data, and intervals.
            xAxis.MajorDivisions = new RangeLabeledDivisions { Mode = RangeDivisionsMode.CreateIntervalMode(chart.XStart, chart.XIncrement) };
            
            graph.DefaultVerticalInterval = YIncrement;
            graph.DataSource = chart;
            // Use a timer to update chart data.
            timer = new DispatcherTimer(TimeSpan.FromMilliseconds(200), DispatcherPriority.Normal, OnTimerTick, Dispatcher);
        }
        private void OnTimerTick(object sender, EventArgs e)
        {
            var graphPlotArea = graph.GetPlotAreaSize();
            int length = (int)graphPlotArea.Width;
            double xIncrement = (double)(500.00 / length);
            chart.XIncrement = xIncrement;
            chart.XStart = 750.0;
            graph.DefaultHorizontalInterval = chart.XIncrement;
            // Add a new set of data after the previous point.
            var previous = chart[chart.Count - 1];
            double y = previous.Index + YIncrement;
            double[] z = GenerateData(length); //Enumerable.Repeat(y * 2.0, length).Select((v, i) => v + i).ToArray();
            chart.Append(y,z);
        }

        Random rnd = new Random();
        private double[] GenerateData(int length)
        {
            int newLength = length;

            double[] data = new double[newLength];

            for (int i = 0; i < newLength; i++)
            {
                data[i] = rnd.Next(-125, 0);                     
            }
            return data;
        }
    }
}

 

0 Kudos
Message 1 of 9
(3,201 Views)

First, one simple change would be to use a timer priority below input (e.g. DispatcherPriority.Input - 1), so that mouse and keyboard input events will still take precedence over generating additional random data.

 

A more general problem is that you are potentially changing the data interval on each append. If the plot area size changes (due to the window resizing, or axis label updates), then old appends will be mis-interpreted using a new interval and display incorrectly. (To quote the caveat in the original answer: “Note that this simplified implementation does require every appended array to use the same length”.)

 

Also note that the VerticalIntensityChartCollection is built from a 1D collection containing a sequence of 1D arrays, and was not actually optimized for true 2D data. For larger loads, creating a specialized collection would be more appropriate.

 

In fact, if what you want is a one-to-one mapping of color values to on-screen pixels, you might consider just using an image and writing directly to the pixel array yourself using a WPF WriteableBitmap. (I admit I do not understand what your ultimate use case is — just guessing this test showing the distribution of random numbers is not the ultimate application :))

~ Paul H
0 Kudos
Message 2 of 9
(3,171 Views)

Thanks Paul,

Actually we have a Frequency-Domain Graph and its data source is DataCollection<double,dobule> .

We want to create Waterfall (Intensity, Spectrogram) graph which shows historical Frequency (XAxis)-Domain(Color) data changed with time(Y-Axis).

We want to high resolution intensity graph that’s why printing a color on each pixel as much as possible.

In real application we are actually changing the data interval only when graph's size changed. When graph size changed we refresh the data and clear the graph data.

I added a real application screenshot to show what our application and ultimate use case.

Graph’s data refreshes every 200 milliseconds. Intensity graph starts to fill one pixel on each height (Y-axis) and slows down in time as graph matrix fully loaded. When we increase the refresh rate as 800 millisecond it works a litter better but still it has a freezing problem.

How could we use a specialized collection(maybe 2D Collection) for larger loads? Coul you prive us an example?

Thanks,

Hakan

0 Kudos
Message 3 of 9
(3,162 Views)

Thank you for detailing your actual scenario — that makes much more sense!

 

(I am still not sure what purpose is served by letting the resolution of the screen determine the sampling history for your data. I suppose if you had a very high-resolution source, then sampling at the screen pixel level would be under-sampling and thus save on memory? Or perhaps you can get better interpolation by manually generating intermediate values? Either way, I will trust you know your application better than I do!)

 

By the way, is the DataCollection<double,double> you refer to a modified version of the collection from this answer?


Attached is an example of a more specialized chart collection for your specific intensity data (and sorry for mis-speaking earlier, as I neglected the time dimension before: a history of 2D samples results in a total of 3 dimensions). This collection follows the same general pattern as the other custom graph collections, but uses a custom data store to avoid copying. I used this implementation of IndexedQueue to store the appended arrays. Using a flat circular array to store all of the index data might further improve performance, but was beyond the scope of what I had time to implement.

~ Paul H
0 Kudos
Message 4 of 9
(3,148 Views)

Hi Paul,

 

Thank you for quick and efficient answer but we do not understand how could we implement FixedRateChartCollection3D data type and bind to intensity graph?

Could you please provide us an modified  version of  WPF example we sent before in this ticket.

 

Thanks,

0 Kudos
Message 5 of 9
(3,123 Views)

The new data collection type is a replacement for the vertical intensity chart data collection. Here is the updated main window code I used to test it:

 

public partial class MainWindow : Window {
    private const double YIncrement = 10 / 400.00;
    private readonly DispatcherTimer timer;
    private FixedRateChartCollection3D<double> chart;

    public MainWindow( ) {
        InitializeComponent( );

        chart = new FixedRateChartCollection3D<double>( capacity: 400 ) { XStart = 750.0, YIncrement = YIncrement };
        graph.DefaultVerticalInterval = YIncrement;
        graph.DataSource = chart;

        timer = new DispatcherTimer( TimeSpan.FromMilliseconds( 50 ), DispatcherPriority.Input - 1, OnTimerTick, Dispatcher );
    }

    private void OnTimerTick( object sender, EventArgs e ) {
        // Reset chart collection if horizontal resolution changes.
        var graphPlotArea = graph.GetPlotAreaSize( );
        int length = (int)graphPlotArea.Width;
        double xIncrement = (double)(500.00 / length);
        if( chart.AppendCount == 0 )
            chart.XIncrement = xIncrement;
        else if( chart.XIncrement != xIncrement )
            graph.DataSource = chart = new FixedRateChartCollection3D<double>( chart.Capacity ) {
                XStart = chart.XStart,
                XIncrement = xIncrement,
                YIncrement = YIncrement
            };
        graph.DefaultHorizontalInterval = chart.XIncrement;

        // Add a new set of data after the previous point.
        double[] z = GenerateData( length );
        chart.Append( z );
    }

    Random rnd = new Random( );
    private double[] GenerateData( int length ) {
        int newLength = length;

        double[] data = new double[newLength];
        for( int i = 0; i < newLength; i++ )
            data[i] = rnd.Next( -125, 0 );

        return data;
    }
}

 

~ Paul H
0 Kudos
Message 6 of 9
(3,116 Views)

Hi Paul,

Thank you for sample code.

I tested code and It works pretty well except when chart capacity reaches _data.Count, IndexedQueu cleared and despite Intensity graph’s vertical adjuster ContinousChart it  behaves as PagedChart.

How do we fit that behavior?

 

Best Regards,

Hakan

0 Kudos
Message 7 of 9
(3,112 Views)
Solution
Accepted by topic author yrnhkn

I expect that is due to the “// Reset chart collection if horizontal resolution changes.” section of the code (which I added to emulate the “When graph size changed we refresh the data and clear the graph data.” behavior you mentioned) — once the collection reached capacity, the vertical axis would adjust, generating new/larger labels, and decreasing the plot area size. I neglected to mention that I also used PlotAreaMargin="50,Auto,Auto,Auto" on the graph in my XAML to prevent this resizing.

~ Paul H
0 Kudos
Message 8 of 9
(3,107 Views)

Thanks Paul,

 

It is working pretty well.

 

Best Regard,

 

HAKAN

0 Kudos
Message 9 of 9
(3,095 Views)