// file: ImpulseResponse.java
//

// include the necessary system files
//
//import java.awt.*;
import java.lang.*;
import javax.swing.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.*;
// include the necessary local files
//
//import Constants;
import graph.*;

// class: ImpulseResponse
//
// this class implements a canvas which holds the impulse
// response of a filter described by a set of poles and
// zeros.  
//
public class ImpulseResponse extends G2Dint 
    implements ActionListener, Constants {
    // constant to define the sleep time of the thread
    // measured in milliseconds
    //
    
    public final static int SLEEP_TIME = 30;
    Timer timer;
    // define a constant for the srfft routine
    //
    public final static double ISIP_INV_SQRT2 = 0.707106781188;
    
    // define the number of sample points (N) to use for the discrete
    // approximation. note that we will be using an fft so a power of 2
    // is necessary
    //
    private final static int NUM_SAMPLES = 512;
    
    // define the size of the fft to be used.  it is twice the size of 
    // NUM_SAMPLES because we need redundant information for the top
    // half of the fft
    //
    private final static int FFT_SIZE = 1024;
    private final static int FFT_POWER = 10;
    
    // set class variables
    //
    private double max_frequency = Constants.DEFAULT_SAMPLE_FREQ;
    
    // set class variables
    //
    private double dc_gain = 1;
    
    // set up a thread to take care of updating the display
    //
    
    // set up a flag to determine if the display needs to be
    // updated and initialize it to false
    //
    boolean update_flag = false;
    
    // array to hold the poles and zeros necessary to evaluate the filter
    //
    private PoleZero pz_array[];
    
    // data sets for the x and y-axes
    //
    DataSet x_data, y_data;
    
    // declare array to hold the split-radix twiddle factors
    //
    double sr_wr_d[] = new double[FFT_SIZE];
    double sr_wi_d[] = new double[FFT_SIZE];
    
    // declare the axes
    //
    Axis x_axis, y_axis;
    
    // variable for the type of response: analog/digital
    //
    private int design_type = Constants.DEFAULT_DESIGN;
    
    // components
    //
    JPanel panel = new JPanel();
    
    // define clear_flag 
    //
    public boolean clear_flag = true;
    
    //double values for xmax,xmin,xtic
    //
    double xmax = 1.0;
    double xmin = 0.0;
    double xtic = 0.25;
    double ymin = -1.0;
    double ymax = 1.0;
    double ytic = 0.25;
    
    
    //*****************************************************************
    //
    // constructor methods
    //
    //*****************************************************************
    
    // method: ImpulseResponse
    //
    // parameters: (none)
    // 
    // this method constructs an ImpulseResponse canvas
    //
    public ImpulseResponse(PoleZero pz_array_a[], int type) {
	
	// instantiate the super-class
	//
	super();

	// set up a pointer to the array of poles and zeros
	//
	pz_array = pz_array_a;
	design_type = type;
	
	// remove all old datasets
	//
	this.detachDataSets();
	
	// initialize the parameters of this class
	//
	this.drawzero = false;
	this.drawgrid = true;
	this.borderTop = 20;
	this.borderLeft = 10;
	this.borderBottom = 3;
	this.borderRight = 40;
	this.framecolor = Color.black;
	this.gridcolor = Color.lightGray;
	
	// initialize twiddle factors for fft
	//
	sr_init(sr_wr_d, sr_wi_d);
	
	// initiate the thread for this object
	//
	
	timer = new Timer(SLEEP_TIME, this);
	this.start();
    }
    public void actionPerformed(ActionEvent evt) {
	if (update_flag == true) {
	    // find the size of the display area in pixels
	    //
	    Dimension size = this.getSize();
	    
	    // get the width of the map
	    //
	    int width = size.width;
	    
	    // draw the frequency response due to the array of poles and zeros
	    //
	    if (width > 0) {
		detachDataSets();
		detachAxes();
		if (design_type == Constants.ANALOG) {
		    draw_analog_response();
		}
		else {
		    draw_digital_response();
		}
		
		repaint();
		
	    }
	    
	    // reset the update flag
	    //
	    update_flag = false;
	}
    }
    
    
    
    
    //****************************************************************
    //
    // private methods
    //
    //****************************************************************
    
    
    // method: sr_init
    //
    // parameters
    //     double sr_wr_d: real portion of twiddle factors
    //     double sr_wi_d: imaginary portion of twiddle factors
    //
    // creates lookup tables for sin and cosine terms. 
    //
    // returns: boolean
    //
    private boolean sr_init(double sr_wr_d[], double sr_wi_d[]) {
	
	// define local variables
	//
	double  q = Constants.ISIP_TWOPI / FFT_SIZE;
	double t;
	int  i;
	
	// compute the lookup table entries
	//
	for (i = 0; i < FFT_SIZE; ++i) {
	    t = q*i;
	    sr_wr_d[i] = Math.cos(t);
	    sr_wi_d[i] = Math.sin(t);
	}
	
	// exit gracefully
	//
	return true;     
    }
    
    
    // method: sr_complex_cc
    //
    // this code is based on the code by G.A.Sitton of the Rice University.
    // the theory behind the implementation closely follows the description of the
    // split-radix algorithm in:
    //
    // J.G.Proakis, D.G.Manolakis, "Digital Signal Processing - Principles,
    // Algorithms, and Applications" 2nd Edition, Macmillan Publishing Company,
    // New York, pp. 727-730, 1992.
    // 
    // arguments:
    // float_8* output_a :(output) output data array 
    //                      length of output array will always be 2*N
    //                      memory is allocated internally, not by the calling
    //                      program.
    // float_8* input_a : (input) input data array
    //                      length of input array is 2*N
    //                      memory is allocated by the calling
    //                      program.
    //
    // return : logical_1 indicating status of computation
    //
    // this method computes the fourier transform using the split-radix
    // algorithm 
    //
    private boolean sr_complex(double output_a[], double input_a[]) {
	
	// declare local variables
	//
	int	i, j, k, m, q, ii, i0, i1, i2, i3, is, id, temp;
	double cc1, ss1, cc3, ss3, r1, r2, r3, s1, s2, t;
	int n1, n2, n4;
	
	// initialize temporary memory
	//
	double sr_temp_d[] = new double[FFT_SIZE];
	
	// copy input real data to temporary memory and input imaginary data
	// into output so that the input data is retained
	// after calculations.
	//
	for (k = 0; k < FFT_SIZE; ++k) {
	    sr_temp_d[k] = input_a[k*2];
	    output_a[k] = input_a[k*2+1];
	}
	
	// bit reversal routine
	//
	n1 = FFT_SIZE - 1;
	n2 = FFT_SIZE >> 1;
	for (i = j = 0; i < n1; ++i, j += k) {
	    if (i < j) {
		t = sr_temp_d[j];
		sr_temp_d[j] = sr_temp_d[i];
		sr_temp_d[i] = t;
		t = output_a[j];
		output_a[j] = output_a[i];
		output_a[i] = t;
	    }
	    k = n2;
	    while (k <= j) {
		j -= k;
		k = k >> 1;
	    } 
	}
	
	// Length two butterflies	
	//
	is = 0;
	id = 1;
	n1 = FFT_SIZE - 1;
	
	do {
	    id = id << 2;      
	    for (i0 = is; i0 < FFT_SIZE; i0 += id) {
		i1 = i0 + 1;
		
		r1 = sr_temp_d[i0];
		sr_temp_d[i0] = r1 + sr_temp_d[i1];
		sr_temp_d[i1] = r1 - sr_temp_d[i1];
		
		r1 = output_a[i0];
		output_a[i0] = r1 + output_a[i1];
		output_a[i1] = r1 - output_a[i1]; 
	    }
	    temp = id - 1;
	    is = temp << 1;
	} while (is < n1);
	
	// L shaped butterflies	
	//
	n2 = 2;
	for (k = 2; k <= FFT_POWER; ++k) {
	    temp = n2;
	    n4 = temp >> 1;
	    n2 = n2 << 1;
	    is = 0;
	    temp = n2;
	    id = temp << 1;
	    
	    // Unity twiddle factor loops	
	    //
	    do {
		for (i0 = is; i0 < n1; i0 += id) {
		    i1 = i0 + n4;
		    i2 = i1 + n4;
		    i3 = i2 + n4;	
		    r3 = sr_temp_d[i2] + sr_temp_d[i3];
		    r2 = sr_temp_d[i2] - sr_temp_d[i3];
		    r1 = output_a[i2] + output_a[i3];
		    s2 = output_a[i2] - output_a[i3];
		    
		    sr_temp_d[i2] = sr_temp_d[i0] - r3;
		    sr_temp_d[i0] = sr_temp_d[i0] + r3;
		    sr_temp_d[i3] = sr_temp_d[i1] - s2;
		    sr_temp_d[i1] = s2 + sr_temp_d[i1];
		    
		    output_a[i2] = output_a[i0] - r1;
		    output_a[i0] = output_a[i0] + r1;
		    output_a[i3] = output_a[i1] + r2;
		    output_a[i1] = output_a[i1] - r2;
		    
		} 
		is = (id << 1) - n2;
		id = id << 2; 
		
	    } while (is < n1);
	    
	    q = ii = (int)(FFT_SIZE / (double)n2);
	    
	    // Non-trivial twiddle factor loops	
	    //
	    for (j = 1; j < n4; ++j, q += ii) {
		cc1 = sr_wr_d[q];
		ss1 = sr_wi_d[q];
		i3 = q * 3;
		cc3 = sr_wr_d[i3];
		ss3 = sr_wi_d[i3];
		
		is = j;
		id = n2 << 1;
		
		do {
		    for (i0 = is; i0 < n1; i0 += id) {
			i1 = i0 + n4;
			i2 = i1 + n4;
			i3 = i2 + n4;
			
			r1 = (sr_temp_d[i2] * cc1) + (output_a[i2] * ss1);
			s1 = (output_a[i2] * cc1) - (sr_temp_d[i2] * ss1);
			r2 = (sr_temp_d[i3] * cc3) + (output_a[i3] * ss3);
			s2 = (output_a[i3] * cc3) - (sr_temp_d[i3] * ss3);
			
			r3 = r1 + r2;
			r2 = r1 - r2;
			r1 = s1 + s2;
			s2 = s1 - s2;
			
			sr_temp_d[i2] = sr_temp_d[i0] - r3;
			sr_temp_d[i0] = r3 + sr_temp_d[i0];
			sr_temp_d[i3] = sr_temp_d[i1] - s2;
			sr_temp_d[i1] = s2 + sr_temp_d[i1];
			
			output_a[i2] = output_a[i0] - r1;
			output_a[i0] = r1 + output_a[i0];
			output_a[i3] = output_a[i1] + r2;
			output_a[i1] = output_a[i1] - r2;
		    }
		    is = (id << 1) - n2 + j;
		    id = id << 2;
		} while (is < n1);
	    }
	}
	
	// interlace the output and store in the output_a array
	//
	for (k = FFT_SIZE - 1; k >= 0; --k) {
	    output_a[k*2+1] = output_a[k];
	    output_a[k*2] = sr_temp_d[k];    
	}

	// exit gracefully
	//
	return true;
    }
    
    
    // method: draw_analog_response
    //
    // parameters: none
    // 
    // this method draws the impulse response of the analog poles and zeros on
    // the screen
    //
    // returns: boolean
    //
    private boolean draw_analog_response() {
	
	// declare local variables
	//
	double temp_mag;
	double temp_angle;
	double freq_loc = 1.0E-10;
	double freq_increment;
	double max_freq;
	
	// declare an array twice the size of the fft length (for real and imag)
	// and a time array twice the size of the array
	//
	double fft_array[] = new double[FFT_SIZE << 1];
	double time_array[] = new double[FFT_SIZE << 1];
	
	// fft routine
	//
	freq_increment = max_frequency / NUM_SAMPLES;    
	
	// fill in the fft array
	//
	for (int i = 0; i < (2*FFT_SIZE); i += 2) {
	    temp_mag = 0.0;
	    temp_angle = 0.0; 
	    
	    // calculate the magnitude and angle at this frequency point
	    //
	    // set the magnitude value to the first calculated value
	    //
	    if (pz_array[0] != null) {
		temp_mag = (pz_array[0]).mag_response(freq_loc);
		temp_angle = (pz_array[0]).phase_response(freq_loc);
		if (Double.isInfinite(temp_mag)) {
		    if (temp_mag == Double.POSITIVE_INFINITY) {
			temp_mag = Double.MAX_VALUE;
		    }
		    else {
			temp_mag = -Double.MAX_VALUE;
		    }
		}
	    }
	    else {
		temp_mag = 1.0;
		temp_angle = 0.0;
	    }
	    
	    // calculate the mag response due to each pole or zero
	    // and multiply it with the previous response
	    //
	    for (int j = 1; pz_array[j] != null; j++) {
		temp_mag *= (pz_array[j]).mag_response(freq_loc);
		temp_angle += (pz_array[j]).phase_response(freq_loc);
		
		// check for infinite values
		//
		if (Double.isInfinite(temp_mag)) {
		    if (temp_mag == Double.POSITIVE_INFINITY) {
			temp_mag = Double.MAX_VALUE;
		    }
		    else {
			temp_mag = -Double.MAX_VALUE;
		    }
		}
	    }
	    
	    // multiply by the dc gain of the system
	    //
	    temp_mag *= dc_gain;
	    
	    // determine the real and imaginary portions of the frequency response
	    // at this point
	    //
	    fft_array[i] = temp_mag * Math.cos(temp_angle);
	    fft_array[i+1] = temp_mag * Math.sin(temp_angle);
	    
	    // increment the frequency to the next value
	    //
	    freq_loc += freq_increment;
	    
	}
	
	// mirror the fft data across the half_sample point
	//
	int length_2 = FFT_SIZE * 2;
	/*for (int k = 1; k <= NUM_SAMPLES; k++) {
	    fft_array[length_2 - 2*k] = fft_array[2*k];
	    fft_array[length_2 -2*k + 1] = -fft_array[2*k + 1];
	    }*/
	
	// manually set values to special points in the fft data
	// zero imaginary part of dc, and N/2 point
	//
	fft_array[1] = (double)0.0;
	fft_array[FFT_SIZE+1] = (double)0.0;
	
	// calculate the inverse fft of the frequency data
	//
	double temp_time_array[] = new double[length_2];
	sr_complex(temp_time_array, fft_array);
	
	// circularly shift the data and scale
	//
	double scale_factor = 1.0 / (double)FFT_SIZE;
	time_array[0] = scale_factor * temp_time_array[0];
	for (int n = 1; n <  FFT_SIZE; n++) {
	    time_array[n] = scale_factor * temp_time_array[2*(FFT_SIZE-n)];
	}
	
	// place the data into an array with time markings
	//
	for (int i = 0; i < FFT_SIZE; i++) {
	    temp_time_array[2*i] = (double)i / max_frequency;
	    temp_time_array[2*i+1] = time_array[i];
	}
	
	// attach the y_data to the graph
	//
	this.y_data = this.loadDataSet(temp_time_array, FFT_SIZE);
	
	// setup properties of the y_data
	//
	y_data.linestyle = 1;
	y_data.linecolor = Color.blue;
	
	if(clear_flag) {
	    // place the data into an array with time markings
	    //
	    for (int i = 0; i < FFT_SIZE; i++) {
		temp_time_array[2*i] = 0.0;
		temp_time_array[2*i+1] = 0.0;
	    }
	    
	    // attach the y_data to the graph
	    //
	    this.y_data = this.loadDataSet(temp_time_array, FFT_SIZE);
	    clear_flag = false;
	}
	
	x_axis = this.createXAxis();
        x_axis.setTitleText("Time (Sec)");
        x_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN,8));
        x_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	
	x_axis.setXMin( xmin);
        x_axis.setXMax( xmax);
        x_axis.setXTic( xtic);
	
	x_axis.attachDataSet(y_data);
	x_axis.resetXRange();
	
        // create the y-axis
        //
        y_axis = this.createYAxis();
        y_axis.setTitleText("Re(h(t)) V");
        y_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN, 8));
        y_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	
	// set the default y-axis ranges and ticks
	//
        y_axis.setYMin(ymin);
        y_axis.setYMax(ymax);
        y_axis.setYTic(ytic);
	
	y_axis.attachDataSet(y_data);
	y_axis.resetYRange();
	
	// exit gracefully
	//
	return true;
    }
    
    
    // method: draw_digital_response
    //
    // parameters: none
    // 
    // this method draws the impulse response of the digital poles and zeros on
    // the screen
    //
    // returns: boolean
    //
    private boolean draw_digital_response() {
	
	// declare local variables
	//
	double temp_mag;
	double temp_angle;
	double freq_loc = 1.0E-10;
	double freq_increment;
	
	// declare an array twice the size of the fft length (for real and imag)
	// and a time array twice the size of the array
	//
	double fft_array[] = new double[FFT_SIZE << 1];
	double time_array[] = new double[FFT_SIZE << 1];
	
	
	// determine the frequency increment so that no aliasing occurs in the 
	// fft routine
	//
	freq_increment = (max_frequency / 2 / NUM_SAMPLES);    
	
	
	// fill in the fft array
	//
	for (int i = 0; i < (2*FFT_SIZE); i += 2) {
	    temp_mag = 0.0;
	    temp_angle = 0.0; 
	    
	    // calculate the magnitude and angle at this frequency point
	    //
	    // set the magnitude value to the first calculated value
	    //
	    if (pz_array[0] != null) {
		temp_mag = (pz_array[0]).mag_response(freq_loc, max_frequency);
		temp_angle = (pz_array[0]).phase_response(freq_loc,
							  max_frequency);
		if (Double.isInfinite(temp_mag)) {
		    if (temp_mag == Double.POSITIVE_INFINITY) {
			temp_mag = Double.MAX_VALUE;
		    }
		    else {
			temp_mag = -Double.MAX_VALUE;
		    }
		}
	    }
	    else {
		temp_mag = 1.0;
		temp_angle = 0.0;
	    }
	    
	    // calculate the mag response due to each pole or zero
	    // and multiply it with the previous response
	    //
	    for (int j = 1; pz_array[j] != null; j++) {
		temp_mag *= (pz_array[j]).mag_response(freq_loc, max_frequency);
		temp_angle += (pz_array[j]).phase_response(freq_loc,
							   max_frequency);
		
		// check for infinite values
		//
		if (Double.isInfinite(temp_mag)) {
		    if (temp_mag == Double.POSITIVE_INFINITY) {
			temp_mag = Double.MAX_VALUE;
		    }
		    else {
			temp_mag = -Double.MAX_VALUE;
		    }
		}
	    }
	    
	    // multiply by the dc gain of the system
	    //
	    temp_mag *= dc_gain;
	    
	    // check for infinite values
	    //
	    if (Double.isInfinite(temp_mag)) {
		if (temp_mag == Double.POSITIVE_INFINITY) {
		    temp_mag = Double.MAX_VALUE;
		}
		else {
		    temp_mag = -Double.MAX_VALUE;
		}
	    }
	    
	    // determine the real and imaginary portions of the frequency response
	    // at this point
	    //
	    fft_array[i] = temp_mag * Math.cos(temp_angle);
	    fft_array[i+1] = temp_mag * Math.sin(temp_angle);
	    
	    // increment the frequency to the next value
	    //
	    freq_loc += freq_increment;
	    
	}
	
	int length_2 = FFT_SIZE * 2;
	
	// manually set values to special points in the fft data
	// zero imaginary part of dc, and N/2 point
	//
	fft_array[1] = (double)0.0;
	fft_array[FFT_SIZE+1] = (double)0.0;
	
	// calculate the inverse fft of the frequency data
	//
	double temp_time_array[] = new double[length_2];
	sr_complex(temp_time_array, fft_array);
	
	for (int i = 0; i < length_2; i++) {
	    
	    // multiply by the dc gain of the system
	    //
	    temp_mag = temp_time_array[i];
	    
	    // check for infinite values
	    //
	    if (Double.isInfinite(temp_mag)) {
		if (temp_mag == Double.POSITIVE_INFINITY) {
		    temp_mag = Double.MAX_VALUE;
		    temp_time_array[i] = temp_mag;
		}
		else {
		    temp_mag = -Double.MAX_VALUE;
		    temp_time_array[i] = temp_mag;
		}
	    }
	}      
	
	// circularly shift the data and scale
	//
	double scale_factor = 1.0 / (double)FFT_SIZE;
	time_array[0] = scale_factor * temp_time_array[0];
	for (int n = 1; n < FFT_SIZE; n++) {
	    time_array[n] = scale_factor * temp_time_array[2*(FFT_SIZE-n)];
	}
	
	// place the data into an array with time markings
	//
	for (int i = 0; i < FFT_SIZE; i++) {
	    temp_time_array[2*i] = (double)i / max_frequency;
	    temp_time_array[2*i+1] = time_array[i];
	}
	
	// attach the y_data to the graph
	//
	this.y_data = this.loadDataSet(temp_time_array, FFT_SIZE);
	
	// setup properties of the y_data
	//
	y_data.linestyle = 1;
	y_data.linecolor = Color.blue;
	
	if(clear_flag) {
	    // place the data into an array with time markings
	    //
	    for (int i = 0; i < FFT_SIZE; i++) {
		temp_time_array[2*i] = 0.0;
		temp_time_array[2*i+1] = 0.0;
	    }
	    
	    
	    // attach the y_data to the graph
	    //
	    this.y_data = this.loadDataSet(temp_time_array, FFT_SIZE);
	    clear_flag = false;
	}
	
	x_axis = this.createXAxis();
        x_axis.setTitleText("Time (Sec)");
        x_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN,8));
        x_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	
	x_axis.setXMin( xmin);
        x_axis.setXMax( xmax);
        x_axis.setXTic( xtic);
	
	x_axis.attachDataSet(y_data);
	x_axis.resetXRange();
	
        // create the y-axis
        //
        y_axis = this.createYAxis();
        y_axis.setTitleText("Re(h(t)) V");
        y_axis.setTitleFont(new Font("TimesRoman", Font.PLAIN, 8));
        y_axis.setLabelFont(new Font("Helvetica", Font.PLAIN, 8));
	
	// set the default y-axis ranges and ticks
	//
        y_axis.setYMin(ymin);
        y_axis.setYMax(ymax);
        y_axis.setYTic(ytic);
	
	y_axis.attachDataSet(y_data);
	y_axis.resetYRange();
	
	
	return true;
    }
    
    
    //****************************************************************
    //
    // public methods
    //
    //****************************************************************
    
    
    // method: set_design_type 
    //
    // parameters:
    //     integer new_type: (input) design type to change to
    //
    // this is a method to set the design type.  the design type will
    // be set to the value passed in to new_type
    //
    // returns: boolean
    //
    public boolean set_design_type(int new_type) {
	
	// set the type
	//
	design_type = new_type;
	
	// exit gracefully
	//
	return true;
    }
    
    
    // method: set_pz_array
    //
    // parameters:
    //     PoleZero pz_array_a[]: (input) array of poles and zeros
    // 
    // this method sets the pz_array to point to the input variable
    //
    // returns: boolean
    //
    public boolean set_pz_array(PoleZero pz_array_a[]) {
	
	// set the pointer
	//
	pz_array = pz_array_a;
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: set_dc_gain
    //
    // parameters:
    //     double new_dc_gain: (input) dc gain
    // 
    // this method sets the dc gain of the filter
    //
    // returns: boolean
    //
    public boolean set_dc_gain(double new_dc_gain) {
	
	// set the frequency in all of the sub-components
	//
	dc_gain = new_dc_gain;
	
	// update the screen
	//
	this.update_screen();
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: set_frequency
    //
    // parameters:
    //     double frequency: (input) frequency input
    // 
    // this method sets the sample frequency
    //
    // returns: boolean
    //
    public boolean set_frequency(double frequency) {
	
	// set the frequency
	//
	max_frequency = frequency;
	
	// cause the screen to update
	//
	this.update_screen();
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: update_screen
    //
    // parameters: (none)
    // 
    // this method sets the update_flag causing a repaint of the
    // canvas
    //
    // returns: boolean
    //
    public boolean update_screen() {
	
	// set the update_flag
	//
	update_flag = true;
	
	// exit gracefully
	//
	return true;
	
    }
    
    // method: start
    //
    // parameters: none
    //
    // this method starts or restarts the threaded object
    //
    // returns: none
    //
    public void start () {
	
	// update the canvas
	//
	update_flag = true;
	if(! timer.isRunning()) {
	    timer.start();
	}
    }
    
    
    // method: stop
    //
    // parameters: none
    //
    // this method stops the threaded object
    //
    // returns: none
    //
    public void stop () {
	if(timer.isRunning()) {
	    timer.stop();
	}
    }
    
    // method: setDecimal
    //
    // arguments:
    //   double doubleNumber: input digit to be rounded off
    //   int decimalPlaces: number of significant to round the input digit 
    //
    // return :
    //    double result: the rounded off input digit
    //
    // method takes in a input digit (double) and rounds the input digit
    // to the number of speified significant places specified
    //
    public double SetDecimal(double doubleNumber, int decimalPlaces) {
	
        int wholeNumber = (int)doubleNumber;
        double pastDecimal = ((doubleNumber - wholeNumber) * 
			      Math.pow(10,decimalPlaces));
        int roundedDecimal = (int)Math.round(pastDecimal);
        int combine = (int)(wholeNumber * 
			    Math.pow(10,decimalPlaces)+roundedDecimal);
        double result = combine / Math.pow(10,decimalPlaces);
        return result;
    }
    
    // method   : get_range
    // arguments:
    //   double x_max: get maximum x-axis value for the plot
    //   double x_min: get minimum x-axis value for the plot
    //   double x_tic: x-axis tick values
    //   double y_max: get maximum y-axis value for the plot
    //   double y_min: get minimum y-axis value for the plot
    //   double y_tic: y-axis tick values
    // returns  : none
    // this method gets the x-axis and y-axis ranges for the plot
    //
    public void get_range(double x_max , double x_min, double x_tic,
			  double y_max , double y_min, double y_tic) {
	xmin = x_min;
	xmax = x_max;
	xtic = x_tic;
	ymin = y_min;
	ymax = y_max;
	ytic = y_tic;
	
    }
} // end class ImpulseResponse
