Home : Course Map : Chapter 9 : Java : Tech :
Filtering Data in a Stream to a Histogram
JavaTech
Course Map
Chapter 9

Introduction
Overview
Streams
Wrappers,Buffers
Console I/O
  Text Output 
     Demo 1

  Formatter/printf()
     Demo 2

  Tex 2t Input
     Demo 3

  Scanner
     
Demo 4
File Class
  File I/O
  File Output-Text
     Demo 5

  Formatter to File
     Demo 6

  File Input - Text
    Demo 7

  Scanner - Files
     Demo 8

  File I/O - Binary
     Demo 9
   Demo 10
File Chooser Dialog
  Demo 11

Character Codes
  Demo 12
   Demo13
Object I/O
Types to Bytes
Stream Filters
Other I/O Topics
Exercises

    Supplements
Character I/O
  Demo 1   Demo 2
Random Access
  Demo 3
ZIP/GZIP Streams
  Demo 4
Piped Streams
  Demo 5
NIO Framework
More NIO
  Demo 6

     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

In the previous section, we created classes that allowed histograms to become the source and destination for streams. Here we look at classes that filter data streams on their way to histograms. (See Chapter 9: Java : Sources, Destinations, and Filters for a discussion of stream filtering.)

The class HistFilterOutputStream extends FilterOutputStream and overrides the method write(int datum). (See the source code below.) Via the constructor it obtains an instance of HistogramOutputStream obtained from StreamedHistPanel. The write method carries out the calibration operation on the datum value before writing it to the HistogramOutputStream.

We create the program HistFilterStreamApplet.java that adds an instance of a FilterOutputStream subclass to the stream going to the histogram. The job of this filter is to "calibrate" the data as it streams through the filter. The demonstration program below follows closely to that of the previous Histogram Stream demo except that the filter acts on the simulated sensor data to remove the effects of pedestal and response slopes that differ from channel to channel.

The applet below shows three StreamedHistPanel displays with the histograms at the top using uncalibrated data as in the previous demo. The three histograms below these show data streamed through instances of HistFilterOutputStream that use the calibration constants for the corresponding sensor channel.


HistFilterStreamApplet.java
- This applet/application resembles HistStreamApplet but adds 3 extra StreamedHistPanels whose input streams are each wrapped by a HistFilterOutputStream. These panels (shown in the bottom row) display data for the same 3 sensors (shown in the top row) but calibrated by the HistFilterOutputStream.

+ New classes:
HistFilterOutputStream.java - Wraps an OutputStream and overrides the write(int datum) method. The write method makes a slope and pedestal correction to the data value and then writes it to the OutputStream.

+ Previous classes:
Chapter 9:Tech: MakeSensorData.java, StreamedHistPanel.java
Chapter 6:Tech: Histogram.java, HistPanel.java
Chapter 6:Tech: PlotPanel.java, PlotFormat.java


/**
  *  This filter class wraps an OutputStream, assumed to be
  *  an instance of StreamedHistPanel, and "calibrates" the
  *  data by removing the effects of the hardware slope and
  *  pedestal variations.
  *  For this demonstration, only the write (int b) method is
  *  overridden. Also, the full 4 bytes of the integer argument
  *  are used rather than only the lower byte as usual.
 **/
import java.io.*;

public class HistFilterOutputStream extends FilterOutputStream
{
  double fSlope = 0.0, fPedestal = 0.0;

  /**
    * @param out the wrapped stream.
    * @param slope calibration constant for the sensor response.
    * @param pedestal calibration offset for the sensor.
   **/
  public HistFilterOutputStream (OutputStream out,
                                 double slope,
                                 double pedestal) {
    super (out);
    fSlope    = slope;
    fPedestal = pedestal;
  } // ctor

  /**
    * Override the write (int b) method but use the full integer value
    * rather than only the lowest byte as usual with this method.
    * Carryout the calibration and then write the resulting value as
    * an integer to the output stream.
   **/
  public void write (int b) {
    double val = ((double)b)/100.0;
    int ival = (int) (100.0 * ((val - fPedestal)/fSlope));
    try {
      out.write (ival);
    } catch (IOException ioe) {}
  } // write

  // Not overrriding the other write methods in FilterOutputStream.

} // class HistFilterOutputStream
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;

/**
  * This applet/application closely resembles HistStream_JApplet11 but
  * 3 extra StreamedHistPanels are added. These will hold data for the
  * same 3 sensors but after the data is calibrated by instances of
  * HistFilterOutputStream, which wrap StreamedHistPanel streams.
**/
public class HistFilterStreamApplet extends JApplet
             implements ActionListener, Updateable
{
  final static int NUM_HISTS = 3;

  // Histograms for the panels.
  Histogram [] fHistogram = new Histogram[2*NUM_HISTS];

  // The HistPanel subclass that accept data streams.
  StreamedHistPanel [] fHistPanel = new StreamedHistPanel[2*NUM_HISTS];

  // Create a panel to hold the histogram sub-panels
  JPanel fHistsPanel = null;

  // Array of treams to the panels
  OutputStream [] fDataOut = new OutputStream[2*NUM_HISTS];

  // Constants for three channels of sensor data plus
  // the copies of the channels for calibration.
  double [] fSlope    = {0.6, 1.2, 1.1, 0.6, 1.2, 1.1};
  double [] fPedestal = {2.4, 1.3, 3.4, 2.4, 1.3, 3.4};

  int fNumDataPoints = 100;

  boolean fMakingHist      = false;
  boolean fUpdateDisplay   = false;
  MakeSensorData fMakeData = null;

  // Use the java.util Timer and TimerTask combo
  // for timing events.
  java.util.Timer fTimer = null;

  // A text field for input strings
  JTextField fTextField = null;

  // Flag for whether the applet is in a browser
  // or running via the main () below.
  boolean fInBrowser=true;

  //Buttons
  JButton fGoButton    = null;
  JButton fClearButton = null;
  JButton fExitButton  = null;

  public void init ()  {
    Container content_pane = getContentPane ();
    JPanel panel = new JPanel (new BorderLayout ());

    // Use a textfield for an input parameter.
    fTextField =
      new JTextField (Integer.toString (fNumDataPoints), 10);

    // If return hit after entering text, the
    // actionPerformed will be invoked.
    fTextField.addActionListener (this);

    fGoButton = new JButton ("Go");
    fGoButton.addActionListener (this);

    fClearButton = new JButton ("Clear");
    fClearButton.addActionListener (this);

    fExitButton = new JButton ("Exit");
    fExitButton.addActionListener (this);

    JPanel control_panel = new JPanel ();

    control_panel.add (fTextField);
    control_panel.add (fGoButton);
    control_panel.add (fClearButton);
    control_panel.add (fExitButton);

    // Create a panel to hold the histogram sub-panels
    fHistsPanel = new JPanel (new GridLayout (2,3));

    // Create the HistPanels and their histograms.
    for (int i=0; i < NUM_HISTS; i++)  {
        fHistogram[i] = new Histogram ("Sensor "+i,
                                "Data", 25,0.0,10.0);
        fHistPanel[i] = new StreamedHistPanel (fHistogram[i]);

        // Add the histogram panels to the container panel
        fHistsPanel.add (fHistPanel[i]);

        // Get the output streams for each panel.
        fDataOut[i] = fHistPanel[i].getOutputStream ();

    }

    // Create the HistPanels for the calibrated sensor histograms.
    for (int i=0; i < NUM_HISTS; i++) {
        fHistogram[i+NUM_HISTS] = new Histogram (" Calibrated Sensor "+i,
                                "Data", 25,0.0,10.0);
        fHistPanel[i+NUM_HISTS] =
          new StreamedHistPanel (fHistogram[i+NUM_HISTS]);

        // Add the histogram panels to the container panel
        fHistsPanel.add (fHistPanel[i+NUM_HISTS]);

        // Get the output streams for each panel and wrap them in
        // a filter that will calibrate the stream data.
        fDataOut[i+NUM_HISTS] =
          new HistFilterOutputStream (
                fHistPanel[i+NUM_HISTS].getOutputStream (),
                fSlope[i],fPedestal[i]);

    }
    makeHist ();
    fGoButton.setText ("Stop");
    fClearButton.setEnabled (false);
    if (fInBrowser) fExitButton.setEnabled (false);

    panel.add (fHistsPanel,BorderLayout.CENTER);
    panel.add (control_panel,BorderLayout.SOUTH);

    // Add text area with scrolling to the contentPane.
    content_pane.add (panel);
  } // init

  /** Stop the filling if the browser page unloaded. **/
  public void stop ()
  {
    fGoButton.setText ("Go");
    fClearButton.setEnabled (true);
    fMakingHist = false;
  } // stop

  public void actionPerformed (ActionEvent e) {
    Object source = e.getSource ();
    if (source == fGoButton || source == fTextField)  {
      String strNumDataPoints = fTextField.getText ();
      try {
        fNumDataPoints = Integer.parseInt (strNumDataPoints);
      }
      catch (NumberFormatException ex) {
        // Could open an error dialog here but just
        // display a message on the browser status line.
        showStatus ("Bad input value");
        return;
      }
      if (!fMakingHist) {
        makeHist ();
        fGoButton.setText ("Stop");
        fClearButton.setEnabled (false);
      }  else {
        // Stop button has been pushed
        stop ();
      }
    } else if (source == fClearButton)  {
        for (int i=0; i < 2*NUM_HISTS; i++)
        {   fHistogram[i].clear ();}
        repaint ();
    } else if (!fInBrowser)
        System.exit (0);
  } // actionPerformed

  /** Create the histogram and the timers. **/
  void makeHist () {
    // only allow for one data maker at a time.
    if (fMakingHist) return;
    fMakingHist = true;

    // Create the runnable object to fill the histograms
    // The data will simualte sensors that return data with
    // slightly different slopes and pedestals.
    // The maximum delay between data readings will be 500msecs.
    fMakeData =
      new MakeSensorData (this, fDataOut, fNumDataPoints,
                         fSlope, fPedestal, 500);
    Thread data_thread = new Thread (fMakeData);

    // Before starting the filling, create the timer task
    // that will cause the histogram display to update
    // during the filling.
    // Create a timer. TimerTask created in MakeHist ()
    fTimer = new java.util.Timer ();

    // Start the timer after 100ms and then repeat calls
    // to run in PaintHistTask object every 250ms.
    fTimer.schedule (new PaintHistTask (), 100, 250);

    // Now start the data filling.
    data_thread.start ();

    return;

  } // makeHist

  /**
    * Use the inner class technique to define the
    * TimerTask subclass for signalling that the display
    * should be updated.
   **/
  class PaintHistTask extends java.util.TimerTask {
    public void run () {
       fUpdateDisplay = true;
    }
  } // class PaintHistTask

  /**
    *  Repaint the histogram display and turn off the
    *  update display flag. Return the makingHist flag
    *  to indicate if updating should continue.
   **/
  public boolean update (Object obj) {
    // Don't update the display until the timer
    // turns on the updateDisplay flag.
    if ( fUpdateDisplay) {
      // Possible this method called before outputPanel
      // created in the init (). So check if it exists
      // before attempting to repaint.
      if (fHistsPanel != null) fHistsPanel.repaint ();
      fUpdateDisplay = false;
    }
    return fMakingHist;
  } // update

  /** Called when the histogram filling is finished. **/
  public void done () {
    fMakingHist = false;

    // Stop the histogram display updates.
    fTimer.cancel ();

    // Update one last time
    fHistsPanel.repaint ();

    // Reset the buttons.
    fGoButton.setText ("Go");
    fClearButton.setEnabled (true);

  } // done

  public static void main (String[] args) {
    int frame_width=500;
    int frame_height=600;

    HistFilterStreamApplet applet = new HistFilterStreamApplet ();
    applet.fInBrowser = false;
    applet.init ();

    // Following anonymous class used to close window & exit program
    JFrame f = new JFrame ("Demo");
    f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

    // Add applet to the frame
    f.getContentPane ().add ( applet);
    f.setSize (new Dimension (frame_width,frame_height));
    f.setVisible (true);
  } // main

} // class HistFilterStreamApplet

 

References & Web Resources

Latest update: Nov. 14, 2004

              Tech
Histogram I/O
Hist I/O - Get/Set
  Demo 1
Hist I/O - Objects
  Demo 2
HistogramStream
  Demo 3
Filtering Data
  Demo 4
Exercises

           Physics
Physics Model
Simulation Design
Physics Simulator
  Demo 1
Experiment Design
Experiment Sim.
  Demo 2
Analysis
Expt. + Analysis
  Demo 3
Exercises

  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.