| 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 |