LightningChart .NETSignal Persistent Intensity Chart

TutorialLearn how to create a WPF Signal Persistent Intensity Chart application with LightningChart .NET

WPF Signal Persistent Intensity Chart

Hello! Today we’re deepening into how to create an intensity series application commonly used in Digital Signal Processing. So, the Signal Persistent Intensity chart shows a time-varying representation of the intensity (magnitude) of signal data. You can expect to use this type of chart in audio processing, image processing, and video analysis.

A Signal Persistent Intensity chart involves analyzing signal data over time. The intensity of the signal at each data point in time is captured and represented visually, e.g., as a heatmap. A Signal Persistent Intensity chart is useful in different contexts, including detecting changes in signal characteristics, identifying patterns within signal data, or visualizing the behavior of the signal over time.

Project Overview

This type of chart is also known as Persistent Spectrum, and we’ll use it to represent the frequency present in a specific signal over a period of time. In further analysis, you can plot your data and identify hidden patterns within the signal.

For this, you can try to identify those patterns that look brighter and longer persistent than the rest of the data. So, let’s now create our own Signal Persistent Intensity chart using LightningChart .NET.

Signal-Persistent-Intensity-Chart

zip icon
Download the project to follow the tutorial

Local Setup

For this polar chart project, we need to take in count the following requirements to compile the project.

  • OS: 32-bit or 64-bit Windows Vista or later, Windows Server 2008 R2 or later.

  • DirectX: 9.0c (Shader model 3 and higher) or 11.0 compatible graphics adapter.

  • Visual Studio: 2010-2019 for development, not required for deployment.

  • Platform .NET Framework: installed version 4.0 or newer.

Now go to the next URL and click the download button: http://lightningchart.com/net-charts/

Download-LightningChart-.NET-SDK

You will be redirected to a sign in form, from then on, the process is very simple to follow. So, after confirming your email, you will have access to your own LightningChart account.

Example-LightningChart-Account

After you sign into your account, you will be able to download the SDK. This SDK will be a "free trial" version, but you will be able to use many important features.

If you download the SDK, you will have an .exe like this:

LightningChart-.NET-SDK-Setup-Downloader

The installation will be a typical Windows process, so please continue with it until it is finished. After the installation, you will see the following programs:

LightningChart-.NET-Installed-Programs
  • License Manager: In this application, you will see the purchase options. All the projects that you will create with this trial SDK, will be available for future developments with all features enabled.
Purchase-Options-LightningChart-.NET
  • LightningChart .NET Interactive Examples: now you can see 100+ interactive visualizations available for WPF, WinForms, and/or UWP though today we’re working with Smith Charts.
LightningChart-.NET-Interactive-Examples

Visual Studio Project

Now let’s work with visual studio. The main difference between using the LightningChart .NET visualizer and Visual Studio, is that we will be able to analyze and experiment with many features in the source code. In the LC visualizer, select the Signal Persistent Intensity Chart and run the example:

IE-Persistent-Signal

In the top-right zone of the windows, you will see the following options:

Available options for Visual Studio projects in LightningChart .NET

For trial SDK, we will be able to use the WPF and WinForms frameworks. If you are fully related to windows forms, this option will be more comfortable. In this case I will use the Windows Presentation Foundation framework.

After clicked the framework to use, we will need to specify a folder where the project will be created:

Signal-Persistent-Chart-File-Tree

Finally, the project will be created, and the Visual Studio will be opened and ready for execution.

Smiths-charts-project-ready 

Code Review

The main code will be wrapped inside MainWindow.xaml.cs. Here we will find the code for UI controls.

charting application UI controls

Inside the code we will check two methods that will create the properties that we need to draw correctly the chart. The interactive example is built with various user controls, to manipulate and change the visual properties of the chart. These controls are not required to generate this graph, so we will focus on the code responsible for generating the object.

CreateSignalGenerator()

In order for our chart to work, we need to create our signal. For this, we will use the SignalGenerator tool and create an instance of the SignalGenerator class and add the following properties:
//Generator A
            _generator = new SignalGenerator
            {
                Name = "generatorA",
                SamplingFrequency = (int)SamplingFrequency,
                OutputInterval = 2,
                ThreadType = ThreadType.Thread,
                ThreadInvoking = false
            };
  • Name: defines the name of the object.
  • SamplingFrequency: Gets or sets the sample rate, that is, the number of output points generated per second.
  • OutputInterval: Gets or defines the output interval of the signal generator. This is measured in milliseconds.
  • ThreadType: Gets or sets the way how the data are generated. Generates data by using a thread or a synchronized timer from the main UI thread.
  • ThreadInvoking: Built-in call to the UI thread when using ThreadType = Thread.

Now, we need to set the collection of sine waveform components:

_generator.WaveformSines.Add(new SineComponent() { Amplitude = 50, Frequency = 150 });
            _generator.WaveformSines.Add(new SineComponent() { Amplitude = 11, Frequency = 75 });
            _generator.WaveformSines.Add(new SineComponent() { Amplitude = 7, Frequency = 50 });
            _generator.WaveformSines.Add(new SineComponent() { Amplitude = 6, Frequency = 0.5 });

Now we need to configure a collection of random noise for the waveform components:

_generator.WaveformRandomNoises.Add(new RandomNoiseComponent() { Amplitude = 1 });

            _generator.DataGenerated += _generator_DataGenerated;
            _generator.Started += _generator_Started;
            _generator.Stopped += _generator_Stopped;
When the chart runs, it will start generating data to feed our signal. If new data were generated during the signal creation process, it will be added using the _generator_DataGenerated and putSamplestoChart functions:
private void _generator_DataGenerated(DataGeneratedEventArgs args)
        {
            Dispatcher.Invoke(_putSamplesToChart, args.Samples, args.FirstSampleTimeStamp);
        }

        private void PutSamplesToChart(ref double[][] samples, double firstSampleTimeStamp)
        {
            if (_chart == null)
            {
                return;
            }

When the data generation has started, the process of creating data points for the signal persistent intensity chart will begin. This will be calculated based on the value of the sampling frequency specified in the SamplingFrequency variable.

private void _generator_Started(StartedEventArgs args)
        {
            Dispatcher.Invoke(new System.Action(HandleGeneratorStart));
        }

        private void HandleGeneratorStart()
        {
            _oldSamples = null;

            _chart.BeginUpdate();

            //Calculate points / trace count 
            _pointsPerTrace = (int)(Math.Round((_chart.ViewXY.XAxes[0].Maximum - _chart.ViewXY.XAxes[0].Minimum)
                * _generator.SamplingFrequency)) + 1;

            _chart.ViewXY.SampleDataSeries[0].SamplingFrequency = _generator.SamplingFrequency;

            ClearPersistentLayer();

            _chart.Title.Text = "Persistent intensity signal monitor, " + _generator.SamplingFrequency.ToString() + " samples/sec";

If the generator is stopped by the user, the chart and all its properties will be disposed of:

private void PerformDisposing()
        {
            if (_historicDataLayer != null)
            {
                _historicDataLayer.Dispose();
                _historicDataLayer = null;
            }

            // Don't forget to clear chart grid child list.
            gridChart.Children.Clear();

            if (_chart != null)
            {
                _chart.Dispose();
                _chart = null;
            }

CreateChart()

This main method is responsible for creating the Signal Persistent Intensity chart object. We begin by creating a new instance of LightningChart and giving the chart a name.

Naming the chart object

_chart = new LightningChart
            {
                ChartName = "Persistent signal chart"
            };

BeginUpdate()

This property disables control repaints when a property is changed. It’s recommended to use it for updating the status of several properties as well as for updating series points.

_chart.BeginUpdate();

LegendBox

Adds and configures legend boxes within the chart object. Notice that the legend will be automatically populated by the values created on the chart. So, you’ll only need to specify its orientation and XY position.

_chart.ViewXY.LegendBoxes[0].ShowCheckboxes = false;
            _chart.ViewXY.LegendBoxes[0].Layout = LegendBoxLayout.Vertical;
            _chart.ViewXY.LegendBoxes[0].Offset = new PointIntXY(-15, -70);

Y-axis Configuration

The range numbers will set the Y limits for the signal persistent intensity chart. In this case, minus one hundred (-100) would be the lowest position allowed. The text will be the label displayed on the Y axis, just like this:

y-axis-config
_chart.ViewXY.YAxes[0].SetRange(-100, 100);
            _chart.ViewXY.YAxes[0].Title.Text = "Amplitude, V";

X-axis Configuration

_chart.ViewXY.XAxes[0].SetRange(0, 0.01);
            _chart.ViewXY.XAxes[0].ValueType = AxisValueType.Number;
            _chart.ViewXY.XAxes[0].ScrollMode = XAxisScrollMode.None;
            _chart.ViewXY.XAxes[0].FormatValueLabel += ExamplePersistentIntensitySignal_FormatValueLabel;
            _chart.ViewXY.XAxes[0].Title.Text = "Trace time, ms";
            _chart.ViewXY.XAxes[0].AutoDivSpacing = false;
            _chart.ViewXY.XAxes[0].MajorDiv = 0.001;

First, we need to set the range limits for the X-axis. The axis will be a number type, but we can choose between other types:

X-axis-configuration

For scrolling, we can select one of the available behaviors:

scrolling-behaviors

Title.Text:

title

Adding a sample data series

To create a series, we need to specify the Y and X axes of our chart. The stroke should not be displayed but it should be only used for rendering on the persistent intensity layer.

SampleDataSeries trace = new SampleDataSeries(_chart.ViewXY, _chart.ViewXY.XAxes[0], _chart.ViewXY.YAxes[0])
            {
                Visible = false
            };

Adding UI properties

The sampling Frequency by default is set to 10000.0 (this value was declared at the beginning of the project). To add this series, we need to add the collection to the SampleDataSeries list. We can add many collections to the list.

trace.Title.Text = "Historic";
            trace.ShowInLegendBox = false;
            trace.LineStyle.Width = 5;
            trace.LineVisible = true;
            trace.SamplingFrequency = SamplingFrequency;
            trace.FirstSampleTimeStamp = 0;
            trace.PointsVisible = false;
            _chart.ViewXY.SampleDataSeries.Add(trace);

EndUpdate()

Once we configure all the properties for our signal, we need to finalize the update. The EndUpdate function enables control repainting and refreshes the control.

_chart.EndUpdate();

Adding chart object

Finally, we need to add the chart object to the gridChart XAML element.

gridChart.Children.Add(_chart);

Final Application

Here’s the final WPF Surface Mesh chart:

As a developer unfamiliar with the study of sine wave signals, I must admit that before I began studying this signal persistent intensity chart, I thought it would be difficult to explain the logic of the code. It was a big surprise to realize that lightning charts managed to get quite a friendly implementation.

We need to make use of an XY chart, create an instance of SampleDataSeries, assign properties, and assign the collection to our chart. Doing this with real data in a dynamic process would increase the complexity of the data mapping, but in the end, the chart creation would be the same.

Working on this article, I got many ideas on how to implement this type of chart, applying data obtained by signal sensors. I guess the biggest concern would be getting the values from a sensor because LightningChart does the other half of the job.

Omar Urbano Software Engineer

Omar Urbano

Software Engineer

LinkedIn icon
divider-light

Continue learning with LightningChart