Squashed commit of the following:

commit c3b58ab3e2
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Apr 2 09:34:45 2025 +0100

    Update D3Control.java

    don't actually enable the FX for d3 since it doesn't work

commit 7665c1257f
Merge: 56375bd4 2dda6791
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Apr 2 09:29:43 2025 +0100

    Merge branch 'main' of https://github.com/PAMGuard/PAMGuard

commit 56375bd4c0
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Apr 2 09:29:38 2025 +0100

    null traps in TD display

commit 2dda67918c
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Apr 2 09:05:06 2025 +0100

    Updates to Raven data plotting

    Also reading HARP header
    Also adding a second to data keep in Click Detector
This commit is contained in:
Jamie Mac 2025-04-04 13:20:42 +01:00
parent 66e452e53d
commit a3b0fb473b
20 changed files with 571 additions and 18 deletions

View File

@ -467,7 +467,7 @@ final public class PamModel implements PamSettings {
mi.setToolTipText("Record observer monitoring effort");
mi.setModulesMenuGroup(utilitiesGroup);
// mi.setHidden(SMRUEnable.isEnable() == false);
mi.setToolTipText("Enables an observer to enter their name and infomation about which displays are being monitored");
mi.setToolTipText("Enables an observer to enter their name and information about which displays are being monitored");
mi.setMaxNumber(1);
mi = PamModuleInfo.registerControlledUnit(BackupManager.class.getName(), BackupManager.defaultName);
@ -708,7 +708,7 @@ final public class PamModel implements PamSettings {
mi = PamModuleInfo.registerControlledUnit("envelopeTracer.EnvelopeControl", "Envelope Tracing");
mi.addDependency(new PamDependency(RawDataUnit.class, "Acquisition.AcquisitionControl"));
mi.setToolTipText("");
mi.setToolTipText("Traces the envelope of audio data and outputs it as a new waveform");
mi.setModulesMenuGroup(processingGroup);
mi.setModulesMenuGroup(processingGroup);
mi.setHelpPoint("sound_processing/EnvelopeTrace/Docs/EnvelopeOverview.html");

View File

@ -0,0 +1,167 @@
package PamUtils;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
/**
* Copied from https://www.peterfranza.com/2008/09/26/little-endian-input-stream/
* @author dg50
*
*/
public class LittleEndianDataInputStream extends InputStream implements DataInput {
public LittleEndianDataInputStream(InputStream in) {
this.in = in;
this.d = new DataInputStream(in);
w = new byte[8];
}
public int available() throws IOException {
return d.available();
}
public final short readShort() throws IOException
{
d.readFully(w, 0, 2);
return (short)(
(w[1]&0xff) << 8 |
(w[0]&0xff));
}
/**
* Note, returns int even though it reads a short.
*/
public final int readUnsignedShort() throws IOException
{
d.readFully(w, 0, 2);
return (
(w[1]&0xff) << 8 |
(w[0]&0xff));
}
/**
* like DataInputStream.readChar except little endian.
*/
public final char readChar() throws IOException
{
d.readFully(w, 0, 2);
return (char) (
(w[1]&0xff) << 8 |
(w[0]&0xff));
}
/**
* like DataInputStream.readInt except little endian.
*/
public final int readInt() throws IOException
{
d.readFully(w, 0, 4);
return
(w[3]) << 24 |
(w[2]&0xff) << 16 |
(w[1]&0xff) << 8 |
(w[0]&0xff);
}
/**
* like DataInputStream.readUnsignedInt except little endian.
*/
public final long readUnsignedInt() throws IOException
{
int v = readInt();
return Integer.toUnsignedLong(v);
// ByteArray.to
// return
// (w[3]) << 24 |
// (w[2]&0xff) << 16 |
// (w[1]&0xff) << 8 |
// (w[0]&0xff);
}
/**
* like DataInputStream.readLong except little endian.
*/
public final long readLong() throws IOException
{
d.readFully(w, 0, 8);
return
(long)(w[7]) << 56 |
(long)(w[6]&0xff) << 48 |
(long)(w[5]&0xff) << 40 |
(long)(w[4]&0xff) << 32 |
(long)(w[3]&0xff) << 24 |
(long)(w[2]&0xff) << 16 |
(long)(w[1]&0xff) << 8 |
(long)(w[0]&0xff);
}
public final float readFloat() throws IOException {
// still need to byteswap
return Float.intBitsToFloat(readInt());
}
public final double readDouble() throws IOException {
// still need to byteswap
return Double.longBitsToDouble(readLong());
}
public final int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
public final void readFully(byte b[]) throws IOException {
d.readFully(b, 0, b.length);
}
public final void readFully(byte b[], int off, int len) throws IOException {
d.readFully(b, off, len);
}
public final int skipBytes(int n) throws IOException {
return d.skipBytes(n);
}
public final long skip(long n) throws IOException {
return d.skipBytes((int) n);
}
public final boolean readBoolean() throws IOException {
return d.readBoolean();
}
public final byte readByte() throws IOException {
return d.readByte();
}
public int read() throws IOException {
return in.read();
}
public final int readUnsignedByte() throws IOException {
return d.readUnsignedByte();
}
@Deprecated
public final String readLine() throws IOException {
return d.readLine();
}
public final String readUTF() throws IOException {
return d.readUTF();
}
public final void close() throws IOException {
d.close();
}
private DataInputStream d; // to get at high level readFully methods of
// DataInputStream
private InputStream in; // to get at the low-level read methods of
// InputStream
private byte w[]; // work array for buffering input
}

View File

@ -641,15 +641,18 @@ public class PamGui extends PamView implements WindowListener, PamSettings {
menuItem = new JMenuItem("Module Ordering ...");
menuItem.addActionListener(new menuModuleOrder());
menuItem.setToolTipText("Change the order of modules in the PAMGuard configuration");
orderModulesEnabler.addMenuItem(menuItem);
fileMenu.add(menuItem);
menuItem = new JMenuItem("Show Object List ...");
menuItem.addActionListener(new menuShowObjectList());
menuItem.setToolTipText("Show a list of data and detections currnetly held in memory for each module");
fileMenu.add(menuItem);
menuItem = new JMenuItem("Show Data Model ...");
menuItem.addActionListener(new menuShowObjectDiagram());
menuItem.setToolTipText("Show a graphical representation of modules and their interconnections");
fileMenu.add(menuItem);
if (!isViewer) {

View File

@ -620,6 +620,13 @@ abstract public class PamDialog extends JDialog {
return showWarning(warningTitle, warningText);
}
/**
* Display a warning text.
* @param owner
* @param warningTitle
* @param warningText
* @return
*/
public static boolean showWarning(Window owner, String warningTitle, String warningText) {
JOptionPane.showMessageDialog(owner, warningText, warningTitle, JOptionPane.ERROR_MESSAGE);
return false;

View File

@ -1,7 +1,6 @@
package PamView.dialog;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Window;
@ -16,20 +15,14 @@ import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.TitledBorder;
import PamController.PamController;
import PamDetection.LocalisationInfo;
import PamUtils.PamUtils;
import PamguardMVC.PamConstants;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import PamguardMVC.PamProcess;
/**
* Standard panel for dialogs that shows a list of

View File

@ -143,7 +143,7 @@ public class ClickDetector extends PamProcess {
private PamDataBlock<TriggerLevelDataUnit> triggerDataBlock;
private PamRawDataBlock doubleFilteredData;
// private PamRawDataBlock doubleFilteredData;
// protected PamDataBlock<ClickDetection> trackedClicks;
private PamDataBlock trackedClicks;
@ -730,12 +730,12 @@ public class ClickDetector extends PamProcess {
// }
if ((newRawData.getChannelBitmap() & clickControl.clickParameters.getChannelBitmap()) == 0)
return;
return; // not a channel we're interested in
//
// if (obs == filteredDataBlock || obs == doubleFilteredData)
// return;
clickControl.newRawData(obs, newData);
clickControl.newRawData(obs, newData); // does nothing
// see if it's time to start a new file
// only do this here if it's not multithread
@ -1496,7 +1496,9 @@ public class ClickDetector extends PamProcess {
int keepMillis = (int) (relSamplesToMilliseconds(requiredKeepSamples) * 2);
// int keepSeconds = Math.max(1, (int)
// relSamplesToMilliseconds(requiredKeepSamples)/1000);
keepMillis = Math.max(1000, keepMillis);
// add an extra second on 2025-04-01 to try to avoid null clicks.
keepMillis = Math.max(1000, keepMillis) + 1000;
// filteredDataBlock.setNaturalLifetime(keepSeconds);
finalDataSource = filteredDataBlock;
finalDataSource.setNaturalLifetimeMillis(keepMillis);
@ -1642,7 +1644,7 @@ public class ClickDetector extends PamProcess {
/*
* Waveform data ends up pointing either to the raw data, or the output of the
* fist filter if there is one. new wavefformData is created every time (or
* recycled from the data block) since we may beed to go back a while to find
* recycled from the data block) since we may need to go back a while to find
* data from a previous block
*/
double[][] waveformData = new double[nChannels][];

View File

@ -16,12 +16,15 @@ import d3.calibration.CalFileReader;
import d3.calibration.CalibrationInfo;
import d3.calibration.CalibrationSet;
import d3.plots.D3DataPlotProvider;
import d3.plots.D3DataProviderFX;
import dataPlots.data.TDDataProviderRegister;
import dataPlotsFX.data.TDDataProviderRegisterFX;
import fileOfflineData.OfflineFileControl;
import fileOfflineData.OfflineFileMapPoint;
import fileOfflineData.OfflineFileProcess;
//import au.com.bytecode.opencsv.CSVReader;
import pamScrollSystem.ViewLoadObserver;
import userDisplay.UserDisplayControl;
import wavFiles.WavFileReader;
import wavFiles.WavHeader;
@ -35,13 +38,14 @@ public class D3Control extends OfflineFileControl {
private D3DataPlotProvider d3DataPlotProvider;
private float[] oldAccell = new float[3];
private D3DataUnit previousJerkUnit;
private D3DataProviderFX d3PlotProvider;
public D3Control(String unitName) {
super(unitType, unitName);
TDDataProviderRegister.getInstance().registerDataInfo(d3DataPlotProvider = new D3DataPlotProvider(this, getD3DataBlock()));
// UserDisplayControl.addUserDisplayProvider(d3PlotProvider = new D3PlotProvider(this));
// TDDataProviderRegisterFX.getInstance().registerDataInfo(d3PlotProvider = new D3DataProviderFX(this, getD3DataBlock()));
}
@Override
@ -321,6 +325,9 @@ public class D3Control extends OfflineFileControl {
float[] calulateDepth(D3DataUnit dataUnit) {
// find the pressure calibration and make a depth line.
if (calibrations == null) {
return null;
}
CalibrationInfo depthCal = calibrations.findCalibrationInfo("press");
CalibrationInfo tempCal = calibrations.findCalibrationInfo("press:bridge");
int pressInd = findSensorIndex("press");

View File

@ -0,0 +1,43 @@
package d3.plots;
import java.util.ArrayList;
import PamView.GeneralProjector.ParameterType;
import PamView.GeneralProjector.ParameterUnits;
import d3.D3Control;
import d3.D3DataBlock;
import d3.D3SensorInfo;
import dataPlotsFX.data.TDDataInfoFX;
import dataPlotsFX.data.TDDataProviderFX;
import dataPlotsFX.data.TDScaleInfo;
import dataPlotsFX.layout.TDGraphFX;
public class D3DataProviderFX extends TDDataProviderFX {
private D3DataBlock d3DataBlock;
private D3Control d3Control;
public D3DataProviderFX(D3Control d3Control, D3DataBlock d3DataBlock) {
super(d3DataBlock);
this.d3Control = d3Control;
this.d3DataBlock = d3DataBlock;
}
@Override
public TDDataInfoFX createDataInfo(TDGraphFX tdGraph) {
D3PlotInfoFX dataInfo = new D3PlotInfoFX(d3Control, this, tdGraph, d3DataBlock);
createDataChannels(dataInfo);
return dataInfo;
}
public int createDataChannels(D3PlotInfoFX dataInfo) {
int ind = 0;
ArrayList<D3SensorInfo> sensorInfos = d3Control.getD3SensorInfos();
for (D3SensorInfo sensInfo:sensorInfos) {
// need min max type units.
dataInfo.addScaleInfo(new TDScaleInfo(-1, 1, ParameterType.AMPLITUDE, ParameterUnits.NONE));
}
return ind;
}
}

View File

@ -0,0 +1,60 @@
package d3.plots;
import PamController.PamControllerInterface;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import d3.D3Control;
import d3.D3DataBlock;
import dataPlotsFX.TDSymbolChooserFX;
import dataPlotsFX.data.TDDataInfoFX;
import dataPlotsFX.data.TDDataProviderFX;
import dataPlotsFX.layout.TDGraphFX;
import dataPlotsFX.projector.TDProjectorFX;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.shape.Polygon;
public class D3PlotInfoFX extends TDDataInfoFX {
private D3Control d3Control;
private D3DataBlock d3DataBlock;
private int dataChannels;
private D3DataProviderFX d3DataProvider;
public D3PlotInfoFX(D3Control d3Control, D3DataProviderFX d3DataProvider, TDGraphFX tdGraph, D3DataBlock pamDataBlock) {
super(d3DataProvider, tdGraph, pamDataBlock);
this.d3Control = d3Control;
this.d3DataProvider = d3DataProvider;
this.d3DataBlock = pamDataBlock;
}
@Override
public Polygon drawDataUnit(int plotNumber, PamDataUnit pamDataUnit, GraphicsContext g, double scrollStart,
TDProjectorFX tdProjector, int type) {
// need to draw a line for this.
return null;
}
@Override
public Double getDataValue(PamDataUnit pamDataUnit) {
// TODO Auto-generated method stub
return 0.2;
}
@Override
public TDSymbolChooserFX getSymbolChooser() {
// TODO Auto-generated method stub
return null;
}
@Override
public void notifyChange(int changeType) {
if (changeType == PamControllerInterface.OFFLINE_DATA_LOADED && dataChannels == 0) {
dataChannels = d3DataProvider.createDataChannels(this);
if (dataChannels > 0) {
// getTdGraph().listAvailableAxisNames();
}
}
}
}

View File

@ -143,6 +143,11 @@ public abstract class TDDataInfoFX {
scaleInfoIndex = -1;
for (int i = 0; i < scaleInfos.size(); i++) {
TDScaleInfo scaleInfo = scaleInfos.get(i);
ParameterType scaleDataType = scaleInfo.getDataType();
if (scaleDataType == null) {
continue;
}
if (scaleInfos.get(i).getDataType().equals(dataType) && scaleInfos.get(i).getDataUnit().equals(dataUnits)) {
scaleInfoIndex = i;
return true;
@ -167,7 +172,15 @@ public abstract class TDDataInfoFX {
*/
public boolean hasAxisName(ParameterType dataType, ParameterUnits dataUnits) {
for (int i = 0; i < scaleInfos.size(); i++) {
if (scaleInfos.get(i).getDataType().equals(dataType)
TDScaleInfo scaleInfo = scaleInfos.get(i);
if (scaleInfo == null) {
return false;
}
ParameterType scaleDataType = scaleInfo.getDataType();
if (scaleDataType == null) {
return false;
}
if (scaleInfo.getDataType().equals(dataType)
&& scaleInfos.get(i).getDataUnit().equals(dataUnits)) {
return true;
}
@ -291,6 +304,9 @@ public abstract class TDDataInfoFX {
*/
public TDScaleInfo getScaleInfo() {
if (scaleInfoIndex==-1 && scaleInfos.size()>0) scaleInfoIndex=0;
if (scaleInfoIndex < 0 || scaleInfoIndex >= scaleInfos.size()) {
return null;
}
return scaleInfos.get(this.scaleInfoIndex);
}

View File

@ -257,6 +257,9 @@ public class TDProjectorFX extends TimeProjectorFX {
* @return the axis type string.
*/
public static String getUnitName(ParameterUnits axisType){
if (axisType == null) {
return null;
}
return axisType.toString();
// String axisUnits="";
@ -301,6 +304,9 @@ public class TDProjectorFX extends TimeProjectorFX {
* @return the axis type string.
*/
public static String getAxisName(ParameterType axisType){
if (axisType == null) {
return null;
}
return axisType.toString();
// String axisName="";

View File

@ -131,4 +131,11 @@ public class RavenControl extends PamControlledUnit implements PamSettings {
return true;
}
/**
* @return the ravenParameters
*/
public RavenParameters getRavenParameters() {
return ravenParameters;
}
}

View File

@ -9,6 +9,8 @@ public class RavenParameters implements Serializable, Cloneable {
public String importFile;
public double timeOffsetSeconds = 0;
private ArrayList<RavenColumnInfo> extraColumns;
public ArrayList<RavenColumnInfo> getExtraColumns() {

View File

@ -12,10 +12,12 @@ import PamView.symbol.StandardSymbolManager;
import PamView.symbol.SymbolData;
import PamguardMVC.PamProcess;
import dataMap.OfflineDataMap;
import dataPlots.data.TDDataProviderRegister;
import dataPlotsFX.data.TDDataProviderRegisterFX;
import generalDatabase.DBControlUnit;
import ravendata.fx.RavenPlotProviderFX;
import ravendata.swing.RavenGraphics;
import ravendata.swing.RavenPlotProvider;
public class RavenProcess extends PamProcess {
@ -38,6 +40,7 @@ public class RavenProcess extends PamProcess {
ravenDataBlock.setPamSymbolManager(new StandardSymbolManager(ravenDataBlock, standardSymbol));
TDDataProviderRegisterFX.getInstance().registerDataInfo(new RavenPlotProviderFX(ravenDataBlock));
// TDDataProviderRegister.getInstance().registerDataInfo(new RavenPlotProvider(ravenDataBlock));
}
@Override
@ -92,7 +95,7 @@ public class RavenProcess extends PamProcess {
* this as an option in future releases.
* Offset of 2843100 needed for mn23_055a tag data.
*/
long offsetMillis = 0;//2843100;
long offsetMillis = (long) (ravenControl.getRavenParameters().timeOffsetSeconds * 1000.);
RavenDataRow prevRow = null;
for (RavenDataRow ravenRow : ravenData) {

View File

@ -0,0 +1,27 @@
package ravendata.swing;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import dataPlots.data.TDDataInfo;
import dataPlots.data.TDDataProvider;
import dataPlots.data.TDSymbolChooser;
import dataPlots.layout.TDGraph;
public class RavenDataInfo extends TDDataInfo {
public RavenDataInfo(TDDataProvider tdDataProvider, TDGraph tdGraph, PamDataBlock dataBlock) {
super(tdDataProvider, tdGraph, dataBlock);
}
@Override
public Double getDataValue(PamDataUnit pamDataUnit) {
// TODO Auto-generated method stub
return null;
}
@Override
public TDSymbolChooser getSymbolChooser() {
return null;//getDataBlock().getPamSymbolManager().getSymbolChooser("TDPlot" + getTdGraph().getGraphNumber(), null);
}
}

View File

@ -10,6 +10,7 @@ import java.io.File;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
@ -30,6 +31,7 @@ public class RavenImportDialog extends PamDialog {
private JButton chooseButton;
private JTextField timeOffset;
private RavenImportDialog(Window parentFrame) {
@ -40,10 +42,30 @@ public class RavenImportDialog extends PamDialog {
ravenFile = new JTextField(80);
ravenFile.setEditable(false);
chooseButton = new JButton("Select ...");
c.gridwidth = 2;
c.gridx = c.gridy = 0;
mainPanel.add(ravenFile, c);
JPanel p2 = new JPanel(new GridBagLayout());
GridBagConstraints c2 = new PamGridBagContraints();
c2.gridx = 1;
c2.gridwidth = 1;
p2.add(chooseButton, c2);
c2.gridx = 0;
c2.gridy++;
c2.gridwidth = 1;
p2.add(new JLabel("Time offset (s) ", JLabel.RIGHT), c2);
c2.gridx++;
p2.add(timeOffset = new JTextField(7), c2);
String tip = "Added to data as it's read from file";
timeOffset.setToolTipText(tip);
c.gridwidth = 2;
c.gridy++;
mainPanel.add(new PamAlignmentPanel(chooseButton, BorderLayout.EAST), c);
mainPanel.add(new PamAlignmentPanel(p2, BorderLayout.EAST), c);
chooseButton.addActionListener(new ActionListener() {
@Override
@ -80,6 +102,7 @@ public class RavenImportDialog extends PamDialog {
private void setParams(RavenParameters ravenParameters) {
this.ravenParameters = ravenParameters;
ravenFile.setText(ravenParameters.importFile);
timeOffset.setText(String.format("%5.3f", ravenParameters.timeOffsetSeconds));
}
@Override
@ -94,6 +117,12 @@ public class RavenImportDialog extends PamDialog {
return showWarning(str);
}
ravenParameters.importFile = fn;
try {
ravenParameters.timeOffsetSeconds = Double.valueOf(timeOffset.getText());
}
catch (NumberFormatException e) {
return showWarning("Invalid time offset value. Must be a number");
}
return true;
}

View File

@ -0,0 +1,21 @@
package ravendata.swing;
import PamguardMVC.PamDataBlock;
import dataPlots.data.TDDataInfo;
import dataPlots.data.TDDataProvider;
import dataPlots.layout.TDGraph;
public class RavenPlotProvider extends TDDataProvider {
public RavenPlotProvider(PamDataBlock parentDataBlock) {
super(parentDataBlock);
// TODO Auto-generated constructor stub
}
@Override
public TDDataInfo createDataInfo(TDGraph tdGraph) {
// TODO Auto-generated method stub
return new RavenDataInfo(this, tdGraph, getDataBlock());
}
}

View File

@ -6,6 +6,8 @@ import java.util.ArrayList;
import javax.sound.sampled.AudioFormat;
import clickDetector.WindowsFile;
import wavFiles.xwav.HarpHeader;
import wavFiles.xwav.XWavException;
public class WavHeader {
@ -110,6 +112,18 @@ public class WavHeader {
windowsWavFile.seek(fmtEnd);
// break;
}
else if (testString.equals("harp")) {
chunkSize = windowsWavFile.readWinInt();
headChunk = new byte[chunkSize];
windowsWavFile.read(headChunk);
try {
HarpHeader.readHarpHeader(headChunk);
} catch (XWavException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// wavHeadChunks.add(new WavHeadChunk(testString, headChunk));
}
else {
/*
* As an example, SCRIPPS HARP .x.wav files have a chunk

View File

@ -0,0 +1,112 @@
package wavFiles.xwav;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Calendar;
import PamUtils.LittleEndianDataInputStream;
import PamUtils.PamCalendar;
public class HarpHeader {
private HarpHeader() {
// TODO Auto-generated constructor stub
}
/**
* Unpack harp data junk from a xwav file.
* @param chunkData
* @return
*/
public static HarpHeader readHarpHeader(byte[] chunkData) throws XWavException {
/*
* Based on matlab code found at https://github.com/MarineBioAcousticsRC/Wav2XWav/blob/main/wrxwavhdX.m
*/
LittleEndianDataInputStream dis = new LittleEndianDataInputStream(new ByteArrayInputStream(chunkData));
// new LittleEnd
try {
int harpSize = chunkData.length;
int xhdVersion = dis.readUnsignedByte();
String firmwareVersion = readString(dis, 10);
String insId = readString(dis, 4);
String site = readString(dis, 4);
String experiment = readString(dis, 8);// could be 8 in example
int diskSequenceNumber = dis.readUnsignedByte();
String diskSerialNumber = readString(dis, 8);
int numRF = dis.readUnsignedShort();
int longitude = dis.readInt(); // defo written as integers. guessing float*1e5.
int latitude = dis.readInt();
// float longitude = dis.readFloat();
// float latitude = dis.readFloat();
int depth = dis.readShort();
// skip 8.
dis.skip(8);
/*
* then read numRF chunks, each of which is 32 bytes. In this example, we
* have harpSize = 29752, so expecting about (29752-50)/32
*/
long lastT = 0;
for (int iRF = 0; iRF < numRF; iRF++) {
// time is from datevec, so it's year, month ... second in the first six
int[] dateVec = new int[7];
for (int i = 0; i < 6; i++) {
dateVec[i] = dis.readUnsignedByte();
}
dateVec[6] = dis.readUnsignedShort(); // number of millis.
long byteLoc = dis.readUnsignedInt();
long byteLength = dis.readUnsignedInt();
long writeLength = dis.readUnsignedInt();
long sampleRate = dis.readUnsignedInt();
int gain = dis.readUnsignedByte();
dis.skip(7);
long tMillis = dateVec2Millis(dateVec);
// if (lastT != 0) {
// System.out.printf("%s length %d = %3.3fs, step = %dms\n", PamCalendar.formatDBDateTime(tMillis, true), byteLength,
// (double) byteLength / (double) sampleRate / 2., tMillis-lastT);
// }
// else {
// System.out.printf("%s length %d = %3.3fs\n", PamCalendar.formatDBDateTime(tMillis, true), byteLength,
// (double) byteLength / (double) sampleRate / 2.);
// }
lastT = tMillis;
}
} catch (IOException e) {
throw new XWavException(e.getMessage());
}
return null;
}
/**
* Convert datevec read from file to Java millis.
* @param dateVec
*/
private static long dateVec2Millis(int[] dateVec) {
// format is yy, mm, dd, hh, mm, ss, ms as int values.
Calendar c = Calendar.getInstance();
c.setTimeZone(PamCalendar.defaultTimeZone);
c.clear();
int yy = dateVec[0];
if (yy < 90) {
yy += 2000;
}
c.set(yy, dateVec[1]-1, dateVec[2], dateVec[3], dateVec[4], dateVec[5]);
long millis = c.getTimeInMillis() + dateVec[6];
return millis;
}
private static String readString(LittleEndianDataInputStream dis, int bytes) throws XWavException {
byte[] data;
try {
data = dis.readNBytes(bytes);
String str = new String(data);
return str;
} catch (IOException e) {
throw new XWavException(e.getMessage());
}
}
}

View File

@ -0,0 +1,34 @@
package wavFiles.xwav;
public class XWavException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public XWavException() {
// TODO Auto-generated constructor stub
}
public XWavException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public XWavException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
public XWavException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public XWavException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
}