Problem updating Dynamic Chart because of high Sample Rate

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
Ethernut
Posts: 3
Joined: Wed Jun 24, 2015 10:55 am
antibot: No, of course not.

Problem updating Dynamic Chart because of high Sample Rate

Post by Ethernut » Mon Jun 29, 2015 12:35 pm

Hello everybody,

I am trying to create a dynamic chart from the JFreeChart library in order to visualize raw rs232 data. The program works fine at lower sample rates. But unfortunately I am sampling with a very high sampling frequency of 9 KHz. Printing the received rs232 data alone is no problem. After I insert the chart and adding the values to the chart, the program does not work anymore correctly. Sometimes the values are incorrect, upset and without a sense. I have added the code below. I would appreciate any kind of help.

Regards,
Ethernut

Code: Select all

import gnu.io.*;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
//import java.io.OutputStream;
import java.util.Enumeration;
import java.util.TooManyListenersException;

import org.jfree.ui.RefineryUtilities;

//import OeffnenUndSenden.serialPortEventListener;

/**
 * Download RXTX from: http://jlog.org/rxtx-win.html
 * @author Fadi
 *
 */

public class ReadFromRS232 implements Runnable
{
	static DynamicDataDemoRPI demo;

	/**
	 * @param args
	 * @throws InterruptedException 
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException,
			InterruptedException
	{
		//TODO Diagram
		System.out.println("Creating RS232 Terminal");
		demo = new DynamicDataDemoRPI("RS232");
		demo.pack();
		RefineryUtilities.centerFrameOnScreen(demo);
		demo.setVisible(true);

		demo.AddNewData(0.0);//Initialize diagram
		Runnable runnable = new ReadFromRS232();
		Thread.sleep(300);
		new Thread(runnable).start();
		System.out.println("main finished");
	}

	/**
	 * 
	 */

	CommPortIdentifier serialPortId;
	Enumeration enumComm;
	SerialPort serialPort;
	//OutputStream outputStream;
	InputStream inputStream;
	Boolean serialPortGeoeffnet = false;

	ArrayList<String> stockList = new ArrayList<String>();
	String cat = "\0";

//	int baudrate = 9600;
	int baudrate = 38400;
	int dataBits = SerialPort.DATABITS_8;
	int stopBits = SerialPort.STOPBITS_1;
	int parity = SerialPort.PARITY_NONE;
	String portName = "COM3";						//COM Port

	int secondsRuntime = 60;

	public ReadFromRS232()
	{
		System.out.println("Konstruktor: EinfachSenden");
	}

	public void run()
	{
		Integer secondsRemaining = secondsRuntime;
		if (oeffneSerialPort(portName) != true)
			return;

		while (secondsRemaining > 0)
		{
			//			System.out.println("Sekunden verbleiben: " + secondsRemaining.toString());
			secondsRemaining--;
			try
			{
				Thread.sleep(1000);
			}
			catch (InterruptedException e)
			{
			}
		}
		schliesseSerialPort();

	}

	boolean oeffneSerialPort(String portName)
	{
		Boolean foundPort = false;
		if (serialPortGeoeffnet != false)
		{
			System.out.println("Serialport bereits geöffnet");
			return false;
		}
		System.out.println("Öffne Serialport");
		enumComm = CommPortIdentifier.getPortIdentifiers();
		while (enumComm.hasMoreElements())
		{
			serialPortId = (CommPortIdentifier) enumComm.nextElement();
			if (portName.contentEquals(serialPortId.getName()))
			{
				foundPort = true;
				break;
			}
		}
		if (foundPort != true)
		{
			System.out.println("Serialport nicht gefunden: " + portName);
			return false;
		}
		try
		{
			serialPort = (SerialPort) serialPortId.open("Öffnen und Senden",
					500);
		}
		catch (PortInUseException e)
		{
			System.out.println("Port belegt");
		}
		/*
				try {
					outputStream = serialPort.getOutputStream();
				} catch (IOException e) {
					System.out.println("Keinen Zugriff auf OutputStream");
				}
		*/
		try
		{
			inputStream = serialPort.getInputStream();
		}
		catch (IOException e)
		{
			System.out.println("Keinen Zugriff auf InputStream");
		}
		try
		{
			serialPort.addEventListener(new serialPortEventListener());
		}
		catch (TooManyListenersException e)
		{
			System.out.println("TooManyListenersException für Serialport");
		}
		serialPort.notifyOnDataAvailable(true);
		try
		{
			serialPort
					.setSerialPortParams(baudrate, dataBits, stopBits, parity);
		}
		catch (UnsupportedCommOperationException e)
		{
			System.out.println("Konnte Schnittstellen-Paramter nicht setzen");
		}

		serialPortGeoeffnet = true;
		return true;
	}

	void schliesseSerialPort()
	{
		if (serialPortGeoeffnet == true)
		{
			System.out.println("Schließe Serialport");
			serialPort.close();
			serialPortGeoeffnet = false;
		}
		else
		{
			System.out.println("Serialport bereits geschlossen");
		}
	}

	//TODO Read Data over RS232

	void serialPortDatenVerfuegbar() throws NumberFormatException,
			InterruptedException
	{
		try
		{
			byte[] data = new byte[150];
			int num;
			while (inputStream.available() > 0)
			{
				num = inputStream.read(data, 0, data.length);
				//System.out.println("Array: " + stockList.get(0));
				String string = new String(data, 0, num);
				stockList.add(string);
				//				System.out.println("Empfange: " + string);
				if (string.indexOf("\n") != -1)
				{
					//System.out.println("Enter angekommen");
					for (String string2 : stockList)
					{
						cat = cat + string2;
					}
					stockList.clear();
					stockList.remove(stockList);
					System.out.println("empfangen: " + cat + ", length: " + cat.length());
					if (cat.length() < 15 && cat.length() > 6)
					{
						demo.AddNewData(Double.parseDouble(cat));
					}
					cat = "\0";
				}
			}
			//			String[] stockArr = new String[stockList.size()];
			//			stockArr = stockList.toArray(stockArr);
			//			
			//			cat=stockArr[0] += stockArr[1] += stockArr[2];
			//			System.out.println(cat);
		}
		catch (IOException e)
		{
			System.out.println("Fehler beim Lesen empfangener Daten");
		}
	}

	class serialPortEventListener implements SerialPortEventListener
	{
		public void serialEvent(SerialPortEvent event)
		{
			//			System.out.println("serialPortEventlistener");
			switch (event.getEventType())
			{
			case SerialPortEvent.DATA_AVAILABLE:
				try
				{
					serialPortDatenVerfuegbar();
				}
				catch (NumberFormatException e)
				{
					System.out.println("Fehler bei NumberFormatException");
					System.out.println("cat = " + cat + "Ende");
					e.printStackTrace();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				break;
			case SerialPortEvent.BI:
			case SerialPortEvent.CD:
			case SerialPortEvent.CTS:
			case SerialPortEvent.DSR:
			case SerialPortEvent.FE:
			case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
			case SerialPortEvent.PE:
			case SerialPortEvent.RI:
			default:
			}
		}
	}
}

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Problem updating Dynamic Chart because of high Sample Ra

Post by paradoxoff » Mon Jun 29, 2015 2:28 pm

What kind of advice do you expect to get? RefineryUtilities.centerFrameOnScreen(demo) is the only line in your code that actually uses a JFreeChart class.

John Matthews
Posts: 513
Joined: Wed Sep 12, 2007 3:18 pm

Re: Problem updating Dynamic Chart because of high Sample Ra

Post by John Matthews » Tue Jun 30, 2015 11:07 am

Query the serial port in the background of a SwingWorker and update your chart's dataset as shown here. Your implementation of process() will be called periodically with a List<V> of interim results as discussed here.

Ethernut
Posts: 3
Joined: Wed Jun 24, 2015 10:55 am
antibot: No, of course not.

Re: Problem updating Dynamic Chart because of high Sample Ra

Post by Ethernut » Wed Jul 01, 2015 1:48 pm

Hi paradoxoff,
paradoxoff wrote:What kind of advice do you expect to get? RefineryUtilities.centerFrameOnScreen(demo) is the only line in your code that actually uses a JFreeChart class.
Yey I am using only one line. But I dont need any more actually. I just want wo print the data in the chart. Following line works perfectly if I comment out several lines for adding data:

Code: Select all

	void serialPortDatenVerfuegbar() throws NumberFormatException, InterruptedException
	{
		byte[] buffer = new byte[1024];
		int len = -1;
		{
			while ((len = inputStream.read(buffer)) > -1)
			{
				string=new String(buffer, 0, len);
				System.out.print(string);
			}
		}
		catch (IOException e)
		{
			System.out.println("Fehler beim Lesen empfangener Daten");
		}
	}
I am getting several right values from the voltage my AVR is sending:

1.662329
1.662329
1.662329
1.662329
1.662329
1.662329
1.662329
1.662329
1.662329
1.662329
1.662329
1.662329

But since I am getting date through UART. Every character is printed for its own. So i tokend these together to be able to parse it to a double variable. When I do so, the whole thing goes down the water and I am getting errors.

Code: Select all

void serialPortDatenVerfuegbar() throws NumberFormatException, InterruptedException
	{
		try
		{
			byte[] data = new byte[150];
			int num;
			while (inputStream.available() > 0)
			{
				num = inputStream.read(data, 0, data.length);
				//System.out.println("Array: " + stockList.get(0));
				String string = new String(data, 0, num);
				stockList.add(string);
				if (string.indexOf("\n") != -1)
				{
					//System.out.println("Enter angekommen");
					for (String string2 : stockList)
					{
						cat = cat + string2;
					}
					stockList.clear();
					stockList.remove(stockList);
					System.out.println("empfangen: " + cat + ", length: " + cat.length());
					if (cat.length() < 15 && cat.length() > 6)
					{
						if(Double.parseDouble(cat)<=5.0)
						{
							demo.AddNewData(Double.parseDouble(cat));
						}
					}
					cat = "\0";
				}
			}
			//			String[] stockArr = new String[stockList.size()];
			//			stockArr = stockList.toArray(stockArr);
			//			
			//			cat=stockArr[0] += stockArr[1] += stockArr[2];
			//			System.out.println(cat);
		}
		catch (IOException e)
		{
			System.out.println("Fehler beim Lesen empfangener Daten");
		}
	}
I suppose that the JFreeChart does not suport high rates of adding data into it. Becaus if I use a much lower sample rate than it works pretty fine...

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Problem updating Dynamic Chart because of high Sample Ra

Post by paradoxoff » Wed Jul 01, 2015 3:46 pm

You are certainly using more JFreeChart classes than the RefineryUtilities, most importantly the JFreeChart class. From your description, I guess that you are also using an XYPlot, and that implies that you are using an implementation of XYDataset and XYItemRenderer. The performance of this combination will heavily depend on which exact implementation you use, one of the existing ones (XYSeriesCollection or DefaultXYDataset in combination with an XYLineAndShapeRenderer) or a custom one. Since you still refuse to show any JFreeChart related code, I have no idea how it would be possible to increase the performance. I guess that some of the logic is contained in the method AddNewData of your DynamicDataDemoRPI class, but since this class is not part of the JFreeChart package, I have no idea how it looks like.

A tentative proposal to get maximum performance under your conditions is the following:
Reduce the update frequency of your dataset to a much lower value. 10-20 Hz should be fast enough. Anything faster will most likely not make a difference to the viewer but will dramatically increase the number of generated change events. If the data are coming in with a frequency of 1 kHz, this means that you will have to wait until you have collected 50-100 data points, then add this bunch to the dataset in a single method call, and let the dataset fire a DatasetChangeEvent. You will probably have to write a custom XYDataset implementation for that purpose. Something similar to a DefaultXYDataset should do the job. The exact implementation will depent on other requirements, such as whether you need to store all data (which will lead to 3.6 M data points if the frequency is 1 kHz and the run time is 1 h) or whether you just need to store the data from lets say the last 10 min.

Ethernut
Posts: 3
Joined: Wed Jun 24, 2015 10:55 am
antibot: No, of course not.

Re: Problem updating Dynamic Chart because of high Sample Ra

Post by Ethernut » Thu Jul 02, 2015 7:34 am

Hi paradoxoff,

I am very sorry. You are right, I simply forgot to add my chart class. Here my DynamicChart class:

Code: Select all

import java.awt.Color;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.ApplicationFrame;

/**
 * An example of a time series chart. For the most part, default settings are
 * used, except that the renderer is modified to show filled shapes (as well as
 * lines) at each data point.
 * 
 */
@SuppressWarnings("serial")
public class DynamicDataDemoRPI extends ApplicationFrame
{

	public static TimeSeries s1;
	public static DynamicDataDemoRPI demo;

	/**
	 * A demonstration application showing how to create a simple time series
	 * chart. This example uses monthly data.
	 * 
	 * @param title
	 *            the frame title.
	 */
	public DynamicDataDemoRPI(final String title) {

		super(title);
		final XYDataset dataset = createDataset();
		final JFreeChart chart = createChart(dataset);
		final ChartPanel chartPanel = new ChartPanel(chart);
		chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
		chartPanel.setMouseZoomable(true, false);
		setContentPane(chartPanel);
	}

	/**
	 * Creates a chart.
	 * 
	 * @param dataset
	 *            a dataset.
	 * 
	 * @return A chart.
	 */
	@SuppressWarnings("deprecation")
	private JFreeChart createChart(final XYDataset dataset)
	{

		final JFreeChart chart = ChartFactory.createTimeSeriesChart(
				"Duration Time Diagram", "Time [s]", "PIR State [bool]",
				dataset, true, true, false);

		chart.setBackgroundPaint(Color.white);

		// final StandardLegend sl = (StandardLegend) chart.getLegend();
		// sl.setDisplaySeriesShapes(true);

		final XYPlot plot = chart.getXYPlot();
		plot.setBackgroundPaint(Color.lightGray);
		plot.setDomainGridlinePaint(Color.white);
		plot.setRangeGridlinePaint(Color.white);
		// plot.setAxisOffset(new Spacer(Spacer.ABSOLUTE, 5.0, 5.0, 5.0, 5.0));
		plot.setDomainCrosshairVisible(true);
		plot.setRangeCrosshairVisible(true);

		final XYItemRenderer renderer = plot.getRenderer();
		if (renderer instanceof StandardXYItemRenderer)
		{
			final StandardXYItemRenderer rr = (StandardXYItemRenderer) renderer;
			// rr.setPaint(true);
			rr.setShapesFilled(true);
			rr.setItemLabelsVisible(true);
		}

		final DateAxis axis = (DateAxis) plot.getDomainAxis();
		axis.setDateFormatOverride(new SimpleDateFormat("ss"));
		axis.setVerticalTickLabels(true);

		return chart;

	}

	// ****************************************************************************
	// * JFREECHART DEVELOPER GUIDE *
	// * The JFreeChart Developer Guide, written by David Gilbert, is available
	// *
	// * to purchase from Object Refinery Limited: *
	// * *
	// * http://www.object-refinery.com/jfreechart/guide.html *
	// * *
	// * Sales are used to provide funding for the JFreeChart project - please *
	// * support us so that we can continue developing free software. *
	// ****************************************************************************

	/**
	 * Creates a dataset, consisting of two series of monthly data.
	 * 
	 * @return the dataset.
	 */
	@SuppressWarnings("deprecation")
	private XYDataset createDataset()
	{
		DynamicDataDemoRPI.s1 = new TimeSeries("RS232 Data", Second.class);
		// s1.add(new Second(1,12,12,31,12,2014), 1.8);
		// s1.add(new Second(5,5,15,12,11,2014), 167.3);
		final TimeSeriesCollection dataset = new TimeSeriesCollection();
		dataset.addSeries(s1);
//		dataset.addSeries(s2);

		dataset.setDomainIsPointsInTime(true);

		return dataset;
	}

	public void AddNewData(double data1) throws IOException,
			InterruptedException
	{
//		GSMModul modul = new GSMModul();
//		modul.GetTimeFromRTC();

		s1.add(new Second(), data1);
		
		// Adding both data to logfile
		
//		FileWriter fw = new FileWriter("diagram_data.txt", true); // the true will append the new data
//		fw.append(GSMModul.hour + ":" + GSMModul.minute + ":" + GSMModul.second
//				+ "-" + GSMModul.day + "-" + GSMModul.month + "-"
//				+ GSMModul.year + "\t" + (int)data1 + "\t" + (int)data2 + "\n"); //data1 -> upper, data2 -> lower
//		fw.close();
	}

	// public static void main(final String[] args) {
	// Log.getInstance().addTarget(new PrintStreamLogTarget());
	// demo = new TimeSeriesDemo("Time Series Demo 1");
	// demo.pack();
	// RefineryUtilities.centerFrameOnScreen(demo);
	// demo.setVisible(true);
	// }
}
thank you very much for your input.

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: Problem updating Dynamic Chart because of high Sample Ra

Post by paradoxoff » Thu Jul 02, 2015 10:53 am

I see two major issues with your code.
1. You are using the add method of the TimeSeries class. The TimeSeries performs for every addition step a binary search in its internal data structure to check whether a data item with the same time period already exists. This takes time. If you call the add method several times per second, you will get a significant overhead, as the time to search for a data item will increase with increasing length of the internal data structure.
2. You are using the Second class. If the binary search finds a data item with the same Second value, the add method will throw a SeriesException, and the data item will not be added. With an data addition frequency of 1 kHz, you might easily get 999 exceptions per second. Do you have any error log that you could check?

Locked