A common task in scientific programming is to evaluate the raw data that comes in from a device that is used to measure some physical quantity. It can be anything from a car's speedometer, to a digital thermometer, to an electron microscope. In each case the device will send data to a computer in a very simple format which then needs to be interpreted and processed before anything useful can be done with the data. Oftentimes the device will produce measurements very rapidly, possibly even thousands per second.
In this assignment you will get some practice at performing some very simple raw data processing, by reading sensor data from the internet. This sensor data is collected by oceanic buoy stations administered by Scripps Institution of Oceanography at UCSD. We will be using data from two local buoys, one in Oceanside and another in Torrey Pines. Whenever a new set of data is collected, it is dumped to a particular webpage in real time. You will be reading the data for each buoy station from each station's webpage and processing it according to a very simple scheme we have devised.
Buoy stations
It updates every hour or so. Here is an example of what the sensor data looks like:
As you can see, the data is divided into several columns, where each row represents a single measurement from a single buoy station. The data columns use white space characters ('\s') to separate columns. More on the format of the data is found below.
The starting point of this program will be the main method of the DataProcessor class. Your program is supposed to perform the following actions: (this is repeated in more detail in the description of the main method below)
This homework requires skills with file I/O, ArrayLists, and some string parsing. You will need to write the following classes:
You must put a comment at the top of your code that specifies your:
real name, login name, email address, and PID. You must also include a
two-to-four sentence description of the program. Use a multi-line
comment (/* */). YOU MUST DO THIS FOR EVERY JAVA
FILE!
Comments Comments in the style detailed in the book ARE required. Your code must be properly commented to receive full credit.
Each line of the webpage will contain 15 items that we want to record:
The SensorData class is designed to hold these items. One SensorData instance will hold one line's worth of data, which will have all of these 15 items.
The SensorData class should include the following fields and methods:
The SensorReader class will be responsible for reading the webpage and parsing out a list of SensorData objects. The SensorReader object will be constructed and given a specific URL to read from. Then when the user calls the getDataList method, it will open the webpage for reading, read the lines, parse each line into a SensorData object, add each SensorData object to an ArrayList, and finally return the list of SensorData objects. The SensorReader class should have the following fields and methods:
private BufferedReader openURL() {The BufferedReader that this methods returns will be open and all ready to read from. You will still need to close it when you are done reading from it. Because of the wacky network code that we had to use, you will need to include an additional import statement at the top of your SensorReader file:
BufferedReader result = null;
try {
URL urlObject = new URL(this.url);
URLConnection connection = urlObject.openConnection();
InputStream in = connection.getInputStream();
result = new BufferedReader(new InputStreamReader(in));
} catch (MalformedURLException murle) {
System.out.println("URL string is invalid");
System.exit(0);
} catch (IOException ioe) {
System.out.println("Error opening URL connection");
System.exit(0);
}
return result;
}
import java.net.*;
The data in each line is white-space-delimited. This means that there is at least one space character ('\s') in between each data item.
The best way to parse the individual items out of the line is to use the split method of the String class. Since the data items are delimited by one or more space characters, we will use "\\s+" as the delimiter we pass to split. This will return an array of strings, which is the result of chopping up the line at every place where there were one or more space characters in a row.
"a\sb\sc\sd".split("\\s+") == {"a", "b", "c", "d"}
"a\s\s\sb\s\s\sc\s\s\sd".split("\\s+") == {"a", "b", "c", "d"}
Some of the fields of SensorData are doubles or ints. Two useful questions to ask yourself are:
One of the fields (measurementDate) is a Calendar. The java.util.Calendar class is used to store and convert date information. You can read more information about this class in the Java API documenation for Calendar.
To use the Calendar class, you will need to include an additional import statement at the top of your SensorData file:import java.util.*;
To get a new Calendar object, you should call this method (instead of calling "new" since Calendar does not provide a public constructor):
static Calendar getInstance()To set the time of the Calendar object, you should call this method (the date parameter is the day of the month):
void set(int year, int month, int date, int hour, int minute, int second)NOTE:
NOTE:
The "seconds" are not given in the input data, so you can set the seconds to 0 in measurementDate.
Here is an example of creating a Calendar object and setting the time:
Calendar calendar = Calendar.getInstance();
calendar.set(2009, 4, 23, 11, 59, 0);
NOTE: You may assume that the line you are given will always have the 15 space-separated columns, and that we will not run your code on any webpage that does not have the right format.
Since this method will only read from a BufferedReader you will need to catch a general Exception, and if one occurs you should print "Error reading from webpage" and then exit the program.
This class will contain the main method -- this is a set of instructions to read and do some calculations on our two ocean bouys -- theoretically based on something our boss, the scientist might ask us to calculate from the data. This class will read in the sensor data (by calling methods of the SensorReader class), then perform a few calculations. You will perform these calculations twice: first for Oceanside, second for Torrey Pines.
The DataProcessor class should have the following methods:
wind wave height >= overall wave height
However, there is one more complication -- sometimes the sensor readings from the buoys are faulty (this can happen when an animal swims into them, for instance). Any entry with a swell wave height of 0.5 or higher should be filtered from (not included in) the ordered list. Additionally, this method should print out a message before it returns the ordered list to say how many elements of the original ArrayList were removed from the ordered list. The message that should be printed is (where x should be replaced by the number of elements discarded:
Number of sensor readings discarded due to overly high swell wave height: x
In summary, sortInOrder should take an ArrayList input parameter (the unsorted, unfiltered list) and return an ArrayList (the sorted and filtered list).
The way you will make an arrayList in sorted order is by simply inserting the new element into the list in such a way that the new list is still sorted afterwards. Here is an example (just showing how this would work with a list of ints:
Starting List: 15, 10, 13, 12 New List: New List: 15 New List: 10, 15 New List: 10, 13, 15 New List: 10, 12, 13, 15
The trick to making this easy is to decompose the problem into 2 parts. In this method (sortInOrder) do the first part -- loop over all the elements in the original list and for each element (that meets our swell wave height requirements) call a method insertInOrder sending two parameters -- the new list to insert into and the element of the starting list that you want to insert. Below see the method description of insertInOrder.
On <measurement date>, the overall wave height was <overall wave height>, the wind wave height was <wind wave height>, the swell wave height was <swell wave height>, and the steepness was <steepness>Where the <measurement date> string is the result of calling E.getMeasurementDate().getTime(), and the <overall wave height> string is the result of calling E.getOverallWaveHeight(), and the <wind wave height> string is the result of calling E.getWindWaveHeight(), and the <swell wave height> string is the result of calling E.getSwellWaveHeight(), and the <steepness> string is the result of calling E.getSteepness(), The SensorData objects should be printed in the same order they appear in the list.
USAGE: java DataProcessor (urlOceanside) (urlTorreyPines)If there are more than 2 command-line parameters given, the extras will be ignored and the program continues like normal.
After verifying the command-line parameters, the program should do the following steps for each buoy station (Oceanside and then Torrey Pines):
Create a new SensorReader object (let's call it reader), giving it the URL string (command-line parameter) for the buoy station's sensor data. Then call reader.getDataList() to get the list of SensorData objects (let's call our list dataList).
You will then call findGoodSailingTimes on dataList to produce a list of good sailing times (let's call this list goodDataList).
Next you will call sortInOrder on goodDataList to produce a sorted and filtered list of good sailing times (let's call this list sortedGoodDataList).
You will then compute the average overall wave height for the sorted list of good sailing times by calling computeAverage (let's call this value average).
After all this work is done, you will simply print some output. You will begin by printing the following string:
Average overall wave height in <location>: <average>where the <location> string is the location ("Oceanside" or "Torrey Pines"), and the <average> string is the value of average.
Next, you will print the line:
Good sailing times in <location>:
and then call printDataList on the list goodDataList.
Finally, you will print the line:
Sorted and filtered good sailing times in <location>:and then call printDataList on the list sortedGoodDataList. (Remember that inside sortInOrder you will print the number of sensor readings that were discarded.)
To run your code on sample data, type the following on the command line:
> java DataProcessor http://cs.ucsd.edu/classes/sp09/cse8b/psas/PSA2/oceanside.txt http://cs.ucsd.edu/classes/sp09/cse8b/psas/PSA2/torrey_pines.txt
An example of what the final output of main could look like is here.
You can test your program by running it on the live sensor data on the buoy station webpages. THESE WEBPAGES CONTINUALLY CHANGE and so you might have trouble reproducing your results from one run to the next.
Since the data webpage is constantly updating, it is hard to get consistent results when testing your program. That is why we have made a copy of one particular version of the sensor data for each buoy and posted them so you can test your code on data that doesn't change:
http://cs.ucsd.edu/classes/sp09/cse8b/psas/PSA2/oceanside.txt
http://cs.ucsd.edu/classes/sp09/cse8b/psas/PSA2/torrey_pines.txt
We will once again have a sample solution that you can run on ieng6 to test to see if your code performs the correct way. You can run our sample solution by running:
runsamplePSA2 <urlOceanside> <urlTorreyPines>at the command line.
We will be using a Bundle script to do automated turnin for this assignment.
On ieng6 somewhere under your home directory you need to make a folder called PSA2. All of your files for this assignment must go in this folder. The files you need to turn in are:
Here are several examples of the expected input/output behavior that your code should have. For each of these examples, your code should print EXACTLY the same output.
> java DataProcessor
USAGE: java DataProcessor (urlOceanside) (urlTorreyPines)
> java DataProcessor http://cs.ucsd.edu/classes/sp09/cse8b/psas/PSA2/oceanside.txt USAGE: java DataProcessor (urlOceanside) (urlTorreyPines)
> java DataProcessor http://not-existent-webpage.com http://not-existent-webpage.com
Error opening URL connection
> java DataProcessor not-even-valid-url-format not-even-valid-url-format
URL string is invalid
> java DataProcessor http://cs.ucsd.edu/classes/sp09/cse8b/psas/PSA2/oceanside.txt http://cs.ucsd.edu/classes/sp09/cse8b/psas/PSA2/torrey_pines.txt
Sample output is here.