SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, and iOS Chart & Android Chart Components

0 votes
324 views

Hello,

I’m trying to get a scatter series chart to work but it doesn’t render. For Bar, Impulse, and Line I can use the same code only switching the series type, but when I attempt to do the same with Scatter series (both XyScatter and ExtremeScatter) the points don’t render.

Here is my scatter series code:

// ... 
        for (int i = 0; i < dimensions; i++)
        {
            var scatterSeries = new ExtremeScatterRenderableSeries()
            {
                PointMarker = new EllipsePointMarker() { Width = 7, Height = 7}
            };

            scatterSeries.PaletteProvider = new ExtremeScatterPaletteProvider(colors);

            Binding DataSeriesBinding = new Binding("Data");
            DataSeriesBinding.Source = (_cvm as ChartViewModelXY).ndData.ElementAt(i);
            scatterSeries.SetBinding(BaseRenderableSeries.DataSeriesProperty, DataSeriesBinding);

            Chart.RenderableSeries.Add(scatterSeries);

            permanentLineSeries++;
        }
// ...
public class ExtremeScatterPaletteProvider : IExtremePointMarkerPaletteProvider
{
    private readonly List<Color> _dataPointColors;
    private readonly Values<Color> _colors = new Values<Color>();
    public ExtremeScatterPaletteProvider(List<Color> dataPointColors)
    {
        _dataPointColors = dataPointColors;
    }
    // Fill Colors for each data-point index
    public Values<Color> Colors { get { return _colors; } }
    public void OnBeginSeriesDraw(IRenderableSeries rSeries)
    {
        var indexes = rSeries.CurrentRenderPassData.PointSeries.Indexes;
        var count = indexes.Count;
        _colors.Count = count;
        // copy required colors from list using data point indices
        for (int i = 0; i < count; i++)
        {
            var dataPointIndex = indexes[i];
            _colors[i] = _dataPointColors[dataPointIndex];
        }
    }
}

The same code works with 3 other XY charts, so I’m fairly certain the issue is with how I am creating the ExtremeScatter. Any help or insight would be greatly appreciated!

Edit: For anyone curious / googling a fully instantiated XyScatterRenderableSeries in code looks like this:

   // Render a scatter series
    var scatterSeries = new XyScatterRenderableSeries()
     {
            PointMarker = new EllipsePointMarker() { Width = 7, Height = 7, Stroke = colors[i], Fill = colors[i]}
     };  

That will render the series with the same color for both the border and interior.

1 vote
2k views

Hello,

I have been working with an application that plots real-time serial data using a FIFO buffer. I have started programming around the ECG-Monitor example as this is exactly what I am creating.

I have a device that broadcasts real-time ECG data via Bluetooth (HC-05), to be exact. I have paired the device and opened a SerialPort in my program to receive the data. My sampling rate is 256 Hz.

When I used a text file to simulate ECG data, it works perfectly well. However, when I use real-time data, there is a delay in charting that increases as the time increases. An easier way to understand this is, the chart continues plotting for a significant period of time even after I have switched off my hardware device.

I have then come to the conclusion that my data is being received at the rate that I want it to, but the plotting gets delayed at an increasing rate as the time increases.

I am currently using the Direct-X rendering type as this gives me a very smooth plot. I receive the data via SerialPort, write to an array and then to the FIFO buffer.

I’m attaching my code for the same.

namespace SciChart.Examples.Examples.SeeFeaturedApplication.ECGMonitor
{
    public class ECGMonitorViewModel : BaseViewModel
    {
    private Timer _timer;
    private IXyDataSeries<double, double> _series0;
    public static double[] _sourceData = new double[50000];
    private int _currentIndex = 0;
    private int _totalIndex = 0;
    private DoubleRange _yVisibleRange;
    private bool _isBeat;
    private int _heartRate;
    private bool _lastBeat;
    private DateTime _lastBeatTime;
    private ICommand _startCommand;
    private ICommand _stopCommand;
    private const double WindowSize = 5.0;
    private const int TimerInterval = 40;
    public static int counter = 0;
    public static SerialPort mySerialPort=new SerialPort("COM3",9600);

    public ECGMonitorViewModel()
    {
        mySerialPort.Open();
        ECGMonitorViewModel.mySerialPort.WriteLine("A");
        ECGMonitorViewModel.mySerialPort.WriteLine("F");

        _series0 = new XyDataSeries<double, double>() { FifoCapacity = 2460, AcceptsUnsortedData = true };

        YVisibleRange = new DoubleRange(-20, 500);
        _startCommand = new ActionCommand(OnExampleEnter);
        _stopCommand = new ActionCommand(OnExampleExit);
    }

    public ICommand StartCommand
    {
        get
        {
            return _startCommand;
        }
    }

    public ICommand StopCommand
    {
        get
        {
            return _stopCommand;
        }
    }

    public IXyDataSeries<double, double> EcgDataSeries
    {
        get
        {
            return _series0;
        }
        set
        {
            _series0 = value;
            OnPropertyChanged("EcgDataSeries");
        }
    }

    public DoubleRange YVisibleRange
    {
        get
        {
            return _yVisibleRange;
        }
        set
        {
            _yVisibleRange = value;
            OnPropertyChanged("YVisibleRange");
        }
    }

    public bool IsBeat
    {
        get
        {
            return _isBeat;
        }
        set
        {
            if (_isBeat != value)
            {
                _isBeat = value;
                OnPropertyChanged("IsBeat");
            }
        }
    }

    public int HeartRate
    {
        get { return _heartRate; }
        set
        {
            _heartRate = value;
            OnPropertyChanged("HeartRate");
        }
    }

    public void OnExampleExit()
    {
        if (_timer != null)
        {
            _timer.Stop();
            _timer.Elapsed -= TimerElapsed;
            _timer = null;
        }
    }

    public void OnExampleEnter()
    {
        _timer = new Timer(TimerInterval) { AutoReset = true };
        _timer.Elapsed += TimerElapsed;
        _timer.Start();
    }

    private void TimerElapsed(object sender, EventArgs e)
    {
        lock (this)
        {
            for (int i = 0; i < 10; i++)
            {
                AppendPoint(250);
            }

            if ((DateTime.Now - _lastBeatTime).TotalMilliseconds < 120) return;

            IsBeat = _series0.YValues[_series0.Count - 3] > 120 ||
                     _series0.YValues[_series0.Count - 5] > 120 ||
                     _series0.YValues[_series0.Count - 8] > 120;

            if (IsBeat && !_lastBeat)
            {
                HeartRate = (int)(60.0 / (DateTime.Now - _lastBeatTime).TotalSeconds);
                _lastBeatTime = DateTime.Now;
            }
        }
    }

    private void AppendPoint(double sampleRate)
    {
        if (_currentIndex >= _sourceData.Length)
        {
            _currentIndex = 0;
        }

        double voltage = _sourceData[_currentIndex];
        double time = _totalIndex / sampleRate %10;

        if(time==0.00)
        {
            voltage = double.NaN;
        }

        _series0.Append(time, voltage);

        _lastBeat = IsBeat;
        _currentIndex++;
        _totalIndex++;
    }

    public static void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        while (mySerialPort.BytesToRead > 0)
        {
            int b;
            b = mySerialPort.ReadByte();
            _sourceData[counter] = b;
            counter++;
        }
    }
}
}

namespace SciChart.Examples.Examples.SeeFeaturedApplication.ECGMonitor
{
    public partial class ECGMonitorView : UserControl
    {
        public ECGMonitorView()
        {
            InitializeComponent();
            ECGMonitorViewModel.mySerialPort.DataReceived += new SerialDataReceivedEventHandler(ECGMonitorViewModel.mySerialPort_DataReceived);
        }
    }
}

The xaml code is as follows,

<UserControl
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:local="clr-namespace:SciChart.Examples.Examples.SeeFeaturedApplication.ECGMonitor"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:s="http://schemas.abtsoftware.co.uk/scichart"
         xmlns:s3D="http://schemas.abtsoftware.co.uk/scichart3D" xmlns:XamlRasterizer="clr-namespace:SciChart.Drawing.XamlRasterizer;assembly=SciChart.Drawing" x:Class="SciChart.Examples.Examples.SeeFeaturedApplication.ECGMonitor.ECGMonitorView"
         d:DesignHeight="400"
         d:DesignWidth="600"
         mc:Ignorable="d">

<UserControl.Resources>

    <Style TargetType="{x:Type s:RenderSurfaceBase}">
        <Setter Property="Effect">
            <Setter.Value>
                <DropShadowEffect BlurRadius="5"
                                  ShadowDepth="0"
                                  Color="#FFB3E8F6" />
            </Setter.Value>
        </Setter>
    </Style>

    <local:BeatToScaleConverter x:Key="BeatToScaleConverter" />
</UserControl.Resources>

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding StartCommand}" />
    </i:EventTrigger>
    <i:EventTrigger EventName="Unoaded">
        <i:InvokeCommandAction Command="{Binding StopCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

<Grid>

    <s:SciChartSurface RenderPriority="Low" MaxFrameRate="25" AutoRangeOnStartup="False">

        <s:SciChartSurface.RenderableSeries>
            <s:FastLineRenderableSeries DataSeries="{Binding EcgDataSeries}"
                Stroke="#FFB3E8F6"                                      
                StrokeThickness="2" />
        </s:SciChartSurface.RenderableSeries>

        <s:SciChartSurface.YAxis>
            <s:NumericAxis AxisTitle="Voltage (mV)"
                DrawMinorGridLines="True"
                MaxAutoTicks="5"
                VisibleRange="{Binding YVisibleRange, Mode=TwoWay}" />
        </s:SciChartSurface.YAxis>

        <s:SciChartSurface.XAxis>
            <s:NumericAxis AxisTitle="Seconds (s)" TextFormatting="0.000s" VisibleRange="0, 10" AutoRange="Never"/>
        </s:SciChartSurface.XAxis>

        <s:SciChartSurface.RenderSurface>
            <s3D:Direct3D10RenderSurface/>
        </s:SciChartSurface.RenderSurface>

    </s:SciChartSurface>

    <StackPanel Margin="30,30" Orientation="Horizontal">
        <StackPanel.Effect>
            <DropShadowEffect BlurRadius="5"
                ShadowDepth="0"
                Color="#FFB3E8F6" />
        </StackPanel.Effect>

        <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
            <Canvas x:Name="layer1"
                Width="20"
                Height="20"
                Margin="12,34,10,0">
                <Canvas.RenderTransform>
                    <ScaleTransform CenterX="-6"
                        CenterY="-6"
                        ScaleX="{Binding IsBeat, Converter={StaticResource BeatToScaleConverter}}"
                        ScaleY="{Binding IsBeat, Converter={StaticResource BeatToScaleConverter}}" />
                </Canvas.RenderTransform>
                <Path Data="m 0 0 c -4 -4 -8.866933 -10.79431 -10 -15 0 0 0 -5 5 -5 5 0 5 5 5 5 0 0 0 -5 5 -5 5 0 5.242535 4.02986 5 5 -1 4 -6 11 -10 15 z" Fill="#FFB0E6F4" />
            </Canvas>
        </Grid>

        <TextBlock HorizontalAlignment="Left"
            VerticalAlignment="Top"
            FontFamily="ArialBlack"
            FontSize="36"
            FontWeight="Bold"
            Foreground="#FFB0E6F4"
            Text="{Binding HeartRate}" />
        <TextBlock HorizontalAlignment="Left"
            VerticalAlignment="Top"
            FontFamily="ArialBlack"
            FontSize="36"
            FontWeight="Bold"
            Foreground="#FFB0E6F4"
            Text="BPM" />

    </StackPanel>

    <TextBlock Margin="5"
        HorizontalAlignment="Left"
        VerticalAlignment="Bottom"
        FontSize="9"
        FontStyle="Italic"
        Foreground="#FFB0E6F4"/>

</Grid>

I’ve attached pictures of what my program currently does. I have also attached a file with sample data in case anybody wants to test the program. I have also tried building in release mode and that doesn’t help, either.

The only issue is the delay in plotting of real-time data. Otherwise, the graphing and rendering is really smooth. As I am a complete beginner to this, can somebody help with me with what I might have done wrong?

Thanks a ton,

Jaivignesh Jayakumar

Showing 2 results
This template supports the sidebar's widgets. Add one or use Full Width layout.