Click train, CPOD, Time Base Display, Ishmael updates and Google deep learning integration (#28)

* Updates to click train detector

New GUI for click train classification - more intuitive and allows users to build more powerful classifiers.
CPOD data can now build average waveforms.
CPOD click trains can be viewed in TDisplayFX pop up menu displays
Fixed Peak Frequency symbol chooser so it saves the colour box settings

* Create click_train_help.md

* Update click_train_help.md

* Screenshots

* Update click_train_help.md

* Update click_train_help.md

* Update click_train_help.md

* More screenshots

* Add screenshots

* Update click_train_help.md

* Add classifier screenshot

* Update click_train_help.md

* Update click_train_help.md

* Update click_train_help.md

* Updates and bug fixes to click train detector and CPOD importer

* Update click_train_help.md

* Update POM with latest jdl4pam

* Add screenshots for click train detector help

* Screenshots

* Update click_train_help.md

* Defult option for CPOD and porpoise to click train detector.

Also a minor

* Update pom.xml

* Bug fix for raw spectrogram in TDDisplayFX

* Update KetosClassifier.java

* Updates to click train detector

New GUI for click train classification - more intuitive and allows users to build more powerful classifiers.
CPOD data can now build average waveforms.
CPOD click trains can be viewed in TDisplayFX pop up menu displays
Fixed Peak Frequency symbol chooser so it saves the colour box settings

* Create click_train_help.md

* Update click_train_help.md

* Screenshots

* Update click_train_help.md

* More screenshots

* Update click_train_help.md

* Update click_train_help.md

* Add screenshots

* Add classifier screenshot

* Update click_train_help.md

* Updates and bug fixes to click train detector and CPOD importer

* Update click_train_help.md

* Update click_train_help.md

* Update click_train_help.md

* Update click_train_help.md

* Add screenshots for click train detector help

* Screenshots

* Update click_train_help.md

* Defult option for CPOD and porpoise to click train detector.

Also a minor

* Bug fix for raw spectrogram in TDDisplayFX

* Update KetosClassifier.java

* Fix standard classifier JSON logging

* Update click_train_help.md

* Fixed bug in sweep classifier when using SoundTrap click detections

* Added some colour averaging in the TFDisplayFX spectrgoram

* Bug fix for rainbow click bearings

Bug fix when rainbow clicks are imported mean bearings cannot be calculated - was an array size issue in DelayGroup

* Google humpback whale deep learning classifier

Google's humpback whale deep learning classifier can now be imported.
Updated TDisplayFX to make the data selection panes cleaner and clearer.
Updated the TDisplayFX so that predicitons from deep learning models now have some display options e.g. changing colour.

* Updated the prediction plots on time display

Deep learning prediciton plots on the time display have now been updated to have some colour options and also act as a legend.

* Updates to TDisplayFX

yFIshmael now owrks with TDisplayFX
TDisplayFX UI changes to make simpler.
Some abstraction for drawing lines on TDisplayFX

* Updates to click train detector

New GUI for click train classification - more intuitive and allows users to build more powerful classifiers.
CPOD data can now build average waveforms.
CPOD click trains can be viewed in TDisplayFX pop up menu displays
Fixed Peak Frequency symbol chooser so it saves the colour box settings

* Create click_train_help.md

* Update click_train_help.md

* Screenshots

* Update click_train_help.md

* More screenshots

* Update click_train_help.md

* Update click_train_help.md

* Add screenshots

* Add classifier screenshot

* Update click_train_help.md

* Updates and bug fixes to click train detector and CPOD importer

* Update click_train_help.md

* Update click_train_help.md

* Update click_train_help.md

* Update click_train_help.md

* Add screenshots for click train detector help

* Screenshots

* Update click_train_help.md

* Defult option for CPOD and porpoise to click train detector.

Also a minor

* Updates to click train detector

New GUI for click train classification - more intuitive and allows users to build more powerful classifiers.
CPOD data can now build average waveforms.
CPOD click trains can be viewed in TDisplayFX pop up menu displays
Fixed Peak Frequency symbol chooser so it saves the colour box settings

* Create click_train_help.md

* Update click_train_help.md

* Update click_train_help.md

* Updates and bug fixes to click train detector and CPOD importer

* Bug fix for raw spectrogram in TDDisplayFX

* Update KetosClassifier.java

* Fix standard classifier JSON logging

* Fixed bug in sweep classifier when using SoundTrap click detections

* Added some colour averaging in the TFDisplayFX spectrgoram

* Bug fix for rainbow click bearings

Bug fix when rainbow clicks are imported mean bearings cannot be calculated - was an array size issue in DelayGroup

* Google humpback whale deep learning classifier

Google's humpback whale deep learning classifier can now be imported.
Updated TDisplayFX to make the data selection panes cleaner and clearer.
Updated the TDisplayFX so that predicitons from deep learning models now have some display options e.g. changing colour.

* Updated the prediction plots on time display

Deep learning prediciton plots on the time display have now been updated to have some colour options and also act as a legend.

* Updates to TDisplayFX

yFIshmael now owrks with TDisplayFX
TDisplayFX UI changes to make simpler.
Some abstraction for drawing lines on TDisplayFX

* Merge fixes to click train detector

* Bug fix to UI
This commit is contained in:
Jamie Mac 2022-04-20 12:27:21 +01:00 committed by GitHub
parent 07752f3dd3
commit da387005a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
145 changed files with 4396 additions and 916 deletions

View File

@ -6,7 +6,7 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk-16.0.2">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/Java 17">
<attributes>
<attribute name="module" value="true"/>
<attribute name="maven.pomderived" value="true"/>

1
.gitignore vendored
View File

@ -35,3 +35,4 @@ bin/
target/WMM.COF
settings.xml
.classpath

View File

@ -1,8 +1,11 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=16
org.eclipse.jdt.core.compiler.compliance=16
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=11
org.eclipse.jdt.core.compiler.source=16

View File

@ -329,7 +329,7 @@
<dependency>
<groupId>io.github.macster110</groupId>
<artifactId>jdl4pam</artifactId>
<version>0.0.93</version>
<version>0.0.94</version>
</dependency>
<!-- https://mvnrepository.com/artifact/gov.nist.math/jama -->

View File

@ -26,11 +26,11 @@ import Acquisition.filedate.FileDateDialogStrip;
import Acquisition.layoutFX.AcquisitionPaneFX;
import Acquisition.layoutFX.DAQSettingsPane;
import Acquisition.layoutFX.FolderInputPane;
import javafx.application.Platform;
import Acquisition.pamAudio.PamAudioSystem;
import PamController.PamControlledUnitSettings;
import PamController.PamController;
import PamController.PamSettings;
import PamModel.SMRUEnable;
import PamUtils.PamAudioFileFilter;
import PamUtils.PamCalendar;
import PamUtils.PamFileChooser;
@ -503,7 +503,9 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings{
/****FX GUI stuff****/
if (folderInputPane!=null) {
Platform.runLater(()->{
folderInputPane.newFileList(fileListData);
});
}
}

View File

@ -4,20 +4,28 @@ import java.util.ArrayList;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxGlyphs.PamGlyphDude;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamButton;
import pamViewFX.fxNodes.PamComboBox;
import pamViewFX.fxNodes.PamGridPane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamTextField;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxNodes.flipPane.FlipPane;
import Acquisition.AcquisitionControl;
import Acquisition.AcquisitionParameters;
import Acquisition.ChannelListPanel;
@ -108,6 +116,22 @@ public class AcquisitionPaneFX extends SettingsPane<AcquisitionParameters>{
*/
private PamBorderPane mainPane;
/**
*
*/
private FlipPane flipPane;
/**
* Pane which can be used for advanced settings.
*/
private PamBorderPane advancedSettingPane;
/**
* Title label for the advanced pane.
*/
private Label advLabel;
/**
* Default spacing for VBox
*/
@ -124,6 +148,12 @@ public class AcquisitionPaneFX extends SettingsPane<AcquisitionParameters>{
this.acquisitionControl=aquisitionControl;
this.acquisitionParameters=acquisitionControl.getAcquisitionParameters();
//create the flip pane.
flipPane=new FlipPane();
flipPane.setFlipDirection(Orientation.HORIZONTAL);
flipPane.setFlipTime(250); //default is 700ms- way too high
if (aquisitionControl.isViewer()){
this.mainPane.setCenter(createViewerModePane());
@ -131,10 +161,50 @@ public class AcquisitionPaneFX extends SettingsPane<AcquisitionParameters>{
else {
this.mainPane.setCenter(createRealTimePane());
}
flipPane.getFront().getChildren().add(mainPane);
//create the advanced flip pane.
advancedSettingPane = createAdvSettingsPane();
flipPane.getBack().getChildren().add(advancedSettingPane);
System.out.println("MAKE PANE: " + acquisitionParameters.getDaqSystemType());
}
/**
* Create the advanced settings pane which can be accessed by DAQ panes if needed.
*/
private PamBorderPane createAdvSettingsPane() {
PamButton back = new PamButton();
back.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-left", Color.WHITE, PamGuiManagerFX.iconSize));
back.setOnAction((action)->{
flipPane.flipToFront();
});
PamBorderPane advPane = new PamBorderPane();
advPane.setPadding(new Insets(5,5,5,5));
PamHBox buttonHolder = new PamHBox();
buttonHolder.setBackground(null);
//buttonHolder.setStyle("-fx-background-color: red;");
buttonHolder.setAlignment(Pos.CENTER_LEFT);
buttonHolder.getChildren().addAll(back, advLabel = new Label("Adv. Settings"));
advLabel.setAlignment(Pos.CENTER);
advLabel.setMaxWidth(Double.MAX_VALUE); //nneed ot make sure label is in center.
PamGuiManagerFX.titleFont2style(advLabel);
advLabel.setAlignment(Pos.CENTER);
HBox.setHgrow(advLabel, Priority.ALWAYS);
advPane.setTop(buttonHolder);
return advPane;
}
/**
* Create the Sound Aquisition pane for real time monitoring.
@ -506,7 +576,7 @@ public class AcquisitionPaneFX extends SettingsPane<AcquisitionParameters>{
@Override
public Node getContentNode() {
return mainPane;
return flipPane;
}
@Override
@ -528,4 +598,20 @@ public class AcquisitionPaneFX extends SettingsPane<AcquisitionParameters>{
return getParams();
}
/**
* Get the flip pane.
* @return the flip pane.
*/
public FlipPane getFlipPane() {
return this.flipPane;
}
public PamBorderPane getAdvancedPane() {
return this.advancedSettingPane;
}
public Label getAdvancedLabel() {
return this.advLabel;
}
}

View File

@ -220,6 +220,10 @@ public class FileDataDialogStripFX extends PamBorderPane {
advDatePane.setParams();
}
public FileDatePane<?> getAdvDatePane() {
return advDatePane;
}
/**
* Enable the controls.
*/

View File

@ -0,0 +1,19 @@
package Acquisition.layoutFX;
import Acquisition.FolderInputSystem;
import javafx.scene.control.Label;
import pamViewFX.fxNodes.PamBorderPane;
/**
* Pane with controls to fix wave file headers.
* @author Jamie Macaulay
*
*/
public class FixWavPane extends PamBorderPane {
public FixWavPane(FolderInputSystem folderInputSystem) {
this.setCenter(new Label("Hello fix wav pane"));
}
}

View File

@ -24,11 +24,14 @@ import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.Tooltip;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.layout.HBox;
@ -38,12 +41,14 @@ import javafx.scene.paint.Color;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxGlyphs.PamGlyphDude;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamButton;
import pamViewFX.fxNodes.PamComboBox;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamProgressBar;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch;
/**
* Pane for the folder input of the sound acquisition.
@ -53,6 +58,8 @@ import pamViewFX.fxNodes.PamVBox;
*/
public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
private static final double UTIL_BUTTON_WIDTH = 120;
/**
* Shows current and previous files.
*/
@ -133,6 +140,16 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
*/
private AcquisitionPaneFX acquisitionPaneFX;
/**
* Pane to fix the headers of wave files.
*/
private FixWavPane fixWavPane;
/**
* Toggle button for merging contigious files
*/
private ToggleButton mergeContigious;
// /**
// * The folder input system.
// * @param folderInputSystem - the folder system.
@ -225,9 +242,15 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
fileDateStrip=new FileDataDialogStripFX(folderInputSystem.getAquisitionControl().getFileDate());
fileDateStrip.setMaxWidth(Double.MAX_VALUE);
fileDateStrip.prefWidthProperty().bind(pamVBox.widthProperty());
fixWavPane = new FixWavPane(folderInputSystem);
Label utilsLabel=new Label("Sound file utilities");
PamGuiManagerFX.titleFont2style(utilsLabel);
pamVBox.getChildren().addAll(fileSelectBox, subFolderPane, progressBar, createTablePane(),
fileDateText=new Label(), fileDateStrip);
fileDateText=new Label(), utilsLabel, createUtilsPane());
//allow users to check file headers in viewer mode.
// if (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW) {
@ -237,7 +260,7 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
});
//pamVBox.getChildren().addAll(checkFiles);
mergeFiles=new CheckBox("Merge contiguous files");
//mergeFiles=new CheckBox("Merge contiguous files");
// pamVBox.getChildren().addAll(subFolders, mergeFiles);
//
// if (PamguardVersionInfo.getReleaseType() == PamguardVersionInfo.ReleaseType.BETA) {
@ -246,6 +269,51 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
return pamVBox;
}
/**
* Create
* @return
*/
private Pane createUtilsPane() {
PamHBox hBox = new PamHBox();
hBox.setSpacing(5);
hBox.setAlignment(Pos.CENTER_LEFT);
//Time stamp pane
PamButton time = new PamButton("Time stamps");
time.setPrefWidth(UTIL_BUTTON_WIDTH);
time.setGraphic(PamGlyphDude.createPamIcon("mdi2a-av-timer", PamGuiManagerFX.iconSize));
time.setOnAction((action)->{
acquisitionPaneFX.getAdvancedPane().setCenter(this.fileDateStrip.getAdvDatePane().getContentNode());
acquisitionPaneFX.getAdvancedLabel().setText("File Time Stamps");
acquisitionPaneFX.getFlipPane().flipToBack();
});
acquisitionPaneFX.getFlipPane().flipFrontProperty().addListener((obsVal, oldVal, newVal)->{
this.fileDateStrip.getAdvDatePane().getParams();
});
PamButton wavFix = new PamButton("Fix wav");
wavFix.setPrefWidth(UTIL_BUTTON_WIDTH);
wavFix.setGraphic(PamGlyphDude.createPamIcon("mdi2f-file-settings", PamGuiManagerFX.iconSize));
wavFix.setOnAction((action)->{
acquisitionPaneFX.getAdvancedLabel().setText("Fix Wave Files");
acquisitionPaneFX.getAdvancedPane().setCenter(this.fixWavPane);
acquisitionPaneFX.getFlipPane().flipToBack();
});
mergeContigious = new ToggleButton("Merge files");
mergeContigious.setPrefWidth(UTIL_BUTTON_WIDTH);
mergeContigious.setGraphic(PamGlyphDude.createPamIcon("mdi2s-set-merge", PamGuiManagerFX.iconSize));
hBox.getChildren().addAll(time, wavFix, mergeContigious);
return hBox;
}
/**
* Create the table pane.
@ -437,6 +505,8 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
// }
// }
folderInputParameters.mergeFiles = mergeContigious.isSelected();
folderInputParameters.subFolders = this.subFolders.isSelected();
return folderInputParameters;
@ -457,6 +527,8 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
fileDateStrip.setFileList(table.getItems());
mergeContigious.setSelected(currParams.mergeFiles);
}

View File

@ -80,6 +80,7 @@ public class StandardFileDatePane extends FileDatePane<StandardFileDateSettings>
mainPane.setCenter(createDatePane());
this.setParams();
mainPane.setPrefWidth(350);
mainPane.setPadding(new Insets(5,5,5,5));
}

View File

@ -15,12 +15,14 @@ import java.io.Serializable;
import javax.swing.JMenuItem;
import IshmaelDetector.dataPlotFX.IshmaelDetPlotProvider;
import IshmaelDetector.layoutFX.IshEnergyPaneFX;
import PamController.PamControlledUnitSettings;
import PamController.PamController;
import PamController.PamSettings;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamRawDataBlock;
import dataPlotsFX.data.TDDataProviderRegisterFX;
import pamViewFX.fxNodes.pamDialogFX.PamDialogFX2AWT;
@SuppressWarnings("rawtypes")
@ -39,6 +41,7 @@ public class EnergySumControl extends IshDetControl implements PamSettings {
public EnergySumControl(String unitName) {
super("Energy Sum Detector", unitName, new EnergySumParams());
}
@Override

View File

@ -26,7 +26,6 @@ public class EnergySumProcess extends IshDetFnProcess {
private int loBinRatio, hiBinRatio; //the hi and lo ratio bin
/**
* Keeps a track of the adaptive noise floor
*/
@ -161,7 +160,6 @@ public class EnergySumProcess extends IshDetFnProcess {
if (getEnergySumParams().adaptiveThreshold) {
//System.out.println("Result dB: " + result);
if (noisefloor==-Double.MIN_VALUE) noisefloor = result;
else noisefloor = getEnergySumParams().longFilter*result + (1-getEnergySumParams().longFilter)*noisefloor;
@ -171,11 +169,11 @@ public class EnergySumProcess extends IshDetFnProcess {
//Set up structure for depositing the result -- a vector of length 2 to also include noise floor data.
//and append the new data to the end of the data stream.
outputUnit.detData = new double[3][1];
outputUnit.detData[1][0]= noisefloor;
outputUnit.detData[2][0]= result;
outputUnit.detData[1][0]= noisefloor; //the smoothed average of detection data (add static threshold for absolute threshold)
outputUnit.detData[2][0]= result; //the raw result
//now the result is just the result minus the noise floor <- keeps all the Ishmael calculations in line with current code structure.
result = result - noisefloor;
result = result - noisefloor; //the result minus noise floor i..e the difference - this allows Ishmael downstream modules to use the same code structure.
}
else {
//Set up structure for depositing the result -- a vector of length 1 --

View File

@ -0,0 +1,160 @@
package IshmaelDetector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import PamguardMVC.DataUnitBaseData;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import binaryFileStorage.BinaryDataSource;
import binaryFileStorage.BinaryHeader;
import binaryFileStorage.BinaryObjectData;
import binaryFileStorage.ModuleFooter;
import binaryFileStorage.ModuleHeader;
/**
* Beginning of a binary data source for the Ishameal detector to save the
* Ishmael data so new peaks can be picked out.
* TODO
* @author Jamie Macaulay
*
*/
public class IshBinaryDataSource extends BinaryDataSource {
/**
* Stream name
*/
private String streamName;
/**
* The Ishmael detector control.
*/
private IshPeakProcess ishDetControl;
private static final int currentVersion = 1;
public IshBinaryDataSource(IshPeakProcess ishDetFnProcess, PamDataBlock sisterDataBlock, String streamName) {
super(sisterDataBlock);
this.ishDetControl=ishDetFnProcess;
this.streamName = streamName;
}
@Override
public String getStreamName() {
return streamName;
}
@Override
public int getStreamVersion() {
return 1;
}
@Override
public int getModuleVersion() {
return currentVersion;
}
@Override
public byte[] getModuleHeaderData() {
// TODO Auto-generated method stub
return null;
}
private ByteArrayOutputStream bos;
private DataOutputStream dos;
@Override
public BinaryObjectData getPackedData(PamDataUnit pamDataUnit) {
IshDetection ishDet = (IshDetection) pamDataUnit;
//System.out.println("DLDetecitonBinarySource: packed: " + pamDataUnit.getBasicData().getMeasuredAmplitudeType());
// make a byte array output stream and write the data to that,
// then dump that down to the main storage stream
if (dos == null || bos == null) {
dos = new DataOutputStream(bos = new ByteArrayOutputStream());
}
else {
bos.reset();
}
try {
dos.writeDouble(ishDet.getPeakHeight());
dos.writeDouble(ishDet.getPeakTimeSam());
}
catch (IOException e) {
e.printStackTrace();
return null;
}
// getBinaryStorageStream().storeData(1, cd.getTimeMilliseconds(), bos.toByteArray());
return new BinaryObjectData(1, bos.toByteArray());
}
@Override
public PamDataUnit sinkData(BinaryObjectData binaryObjectData,
BinaryHeader bh, int moduleVersion) {
ByteArrayInputStream bis = new ByteArrayInputStream(binaryObjectData.getData(),
0, binaryObjectData.getDataLength());
DataInputStream dis = new DataInputStream(bis);
DataUnitBaseData baseData = binaryObjectData.getDataUnitBaseData();
//This is not stored in the base data. Probably sensible because it's the same number for every data unit.
//baseData.setMeasuredAmplitudeType(DataUnitBaseData.AMPLITUDE_SCALE_LINREFSD); <- now in constructor.
//System.out.println("DLDetecitonBinarySource: sink: " + baseData.getMeasuredAmplitudeType());
//model results are loaded as annotations.
try {
double peak = dis.readDouble();
long peakSam = dis.readLong();
if (baseData.getChannelBitmap()==0) {
baseData.setChannelBitmap(1);
}
IshDetection newUnit = new IshDetection(baseData, peakSam, peak);
bis.close();
return newUnit;
} catch (IOException e) {
// e.printStackTrace();
System.err.println(e.getMessage());
return null;
}
}
@Override
public ModuleFooter sinkModuleFooter(BinaryObjectData binaryObjectData,
BinaryHeader bh, ModuleHeader moduleHeader) {
// TODO Auto-generated method stub
return null;
}
@Override
public ModuleHeader sinkModuleHeader(BinaryObjectData binaryObjectData,
BinaryHeader bh) {
// TODO Auto-generated method stub
return null;
}
@Override
public void newFileOpened(File outputFile) {
// TODO Auto-generated method stub
}
}

View File

@ -9,6 +9,8 @@ import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import IshmaelDetector.dataPlotFX.IshmaelDetPlotProvider;
import IshmaelDetector.dataPlotFX.IshmaelFnPlotProvider;
import PamController.PamControlledUnit;
import PamController.PamControlledUnitSettings;
import PamController.PamController;
@ -19,6 +21,7 @@ import PamUtils.PamUtils;
import PamView.dialog.GroupedSourcePanel;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamRawDataBlock;
import dataPlotsFX.data.TDDataProviderRegisterFX;
/**
@ -65,11 +68,28 @@ public abstract class IshDetControl extends PamControlledUnit implements PamSett
//Display.
ishDetGraphics = new IshDetGraphics(this, getOutputDataBlock());
//FX display data providers
IshmaelDetPlotProvider ishDetPlotProviderFX = new IshmaelDetPlotProvider(this);
IshmaelFnPlotProvider ishFnPlotProviderFX = new IshmaelFnPlotProvider(this);
TDDataProviderRegisterFX.getInstance().registerDataInfo(ishDetPlotProviderFX);
TDDataProviderRegisterFX.getInstance().registerDataInfo(ishFnPlotProviderFX);
//Saver.
ishDetSave = new IshDetSave(this);
}
/**
* Get the Ishmael Fn process - this creates the detector output but
* does not perform the binary classification.
* @returnc the Ishmael Fn process
*/
public IshDetFnProcess getIshDetFnProcess() {
return ishDetFnProcess;
}
/** Return any old data block of the right type so that the detection
* process's input can get hooked up to something from the get-go. The
* input is typically re-hooked when the settings file is read.
@ -234,5 +254,14 @@ public abstract class IshDetControl extends PamControlledUnit implements PamSett
}
return false;
}
/**
* Get the Ishmael peak process. The peak process selects handles
* binary classification of the detector output.
* @return the Ishamel peak process.
*/
public IshPeakProcess getIshPeakProcess() {
return this.ishPeakProcess;
}
}

View File

@ -1,16 +1,24 @@
package IshmaelDetector;
import PamguardMVC.AcousticDataUnit;
import PamguardMVC.DataUnitBaseData;
import PamguardMVC.PamDataUnit;
import PamguardMVC.superdet.SuperDetection;
/**
* Standard data unit for Ishmael detector output.
* @author David Mellinger
* <p>
* Ishmael detectors essentially always results in a ID data series of detector output and noise floor. A peak
* can then be selected on either the detector output or the ratio between the detector ouput and noise.
* <p>
* IshDetFnDataUnit stores a chunk of the raw output from the Ishmael detectors.
*
* @author David Mellinger and Jamie Macaulay
*
*/
@SuppressWarnings("rawtypes")
public class IshDetFnDataUnit extends PamDataUnit<PamDataUnit,SuperDetection> implements AcousticDataUnit {
double[][] detData; //a sequence of detection function points
public IshDetFnDataUnit(long timeMilliseconds, int channelBitmap, long startSample,
@ -19,6 +27,13 @@ public class IshDetFnDataUnit extends PamDataUnit<PamDataUnit,SuperDetection> im
this.detData = new double[][]{detData};
}
public IshDetFnDataUnit(DataUnitBaseData baseData, double[][] detData2) {
super(baseData);
this.detData = detData2;
}
/**
* Get Ishmael detector data. The first 1D array (detData[0]) is the detector output
* The second 1D array (deData[0]) is the noise floor of the detector. Null if the detector

View File

@ -17,7 +17,7 @@ import PamguardMVC.PamProcess;
* course of doing making detections. Currently this means it is the superclass
* of EnergySumProcess, SgramCorrProcess, and MatchFiltProcess.
*
* @author Dave Mellinger
* @author Dave Mellinger and Jamie Macaulay
*/
@SuppressWarnings("rawtypes")
public abstract class IshDetFnProcess extends PamProcess
@ -36,7 +36,7 @@ public abstract class IshDetFnProcess extends PamProcess
/**
* The binary data source. Records the raw detector output.
*/
private IshmaelBinaryDataSource ishmealBinaryDataSource;
private IshFnBinarySource ishmealBinaryDataSource;
/** Initialiser.
* <p>IMPORTANT: The subclass initializer should construct the ishDetParams
@ -59,7 +59,7 @@ public abstract class IshDetFnProcess extends PamProcess
IshDetFnDataUnit.class, getLongName(), this, 1 << channel);
ishmealBinaryDataSource = new IshmaelBinaryDataSource(this, outputDataBlock, "Ishmael_det_raw");
ishmealBinaryDataSource = new IshFnBinarySource(this, outputDataBlock, "Ishmael Fn");
outputDataBlock.setBinaryDataSource(ishmealBinaryDataSource);
// outputDataBlock = new RecyclingDataBlock<IshDetFnDataUnit>(
@ -232,4 +232,12 @@ public abstract class IshDetFnProcess extends PamProcess
* the upper bound of the detection kernel
*/
public abstract float getHiFreq();
/**
* Get the output data block.
* @return the output data block.
*/
public PamDataBlock getOutputDataBlock() {
return outputDataBlock;
}
}

View File

@ -1,13 +1,21 @@
package IshmaelDetector;
import PamDetection.PamDetection;
import PamguardMVC.DataUnitBaseData;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import PamguardMVC.superdet.SuperDetection;
/** Basic detection on a single channel for the Ishmael det/loc.
/**
* Basic detection on a single channel for the Ishmael det/loc.
* <p>
* Note that the Ishmael detection does not contain raw wav data - this is
* intentional as the clip generator can be used to generate clips if required.
* As the Ishmael detector is a high false positive rate intial detection stage
* then it may very well be beneficial not to store clips but simply pass the raw
* data to downstream processes.
*
* @author Doug Gillespie and Dave Mellinger
* @author Doug Gillespie, Jamie Macaulay and Dave Mellinger
*
*/
public class IshDetection extends PamDataUnit<PamDataUnit, SuperDetection> implements PamDetection {
@ -27,6 +35,17 @@ public class IshDetection extends PamDataUnit<PamDataUnit, SuperDetection> imple
peakDelaySec = (peakTimeSam - startSam)/parentDataBlock.getSampleRate();
setInfo(startMsec, channelBitmap, startSam, durationSam, lowFreq, highFreq, peakTimeSam, peakHeight);
}
public IshDetection(DataUnitBaseData baseData, long peakTimeSample, double peakHeight) {
super(baseData);
setInfo(baseData.getTimeMilliseconds(), baseData.getChannelBitmap(),
baseData.getStartSample(), baseData.getSampleDuration(), (float) baseData.getFrequency()[0],
(float) baseData.getFrequency()[1], peakTimeSample, peakHeight);
}
public String getCallType() { return callType; }
public void setCallType(String callType) { this.callType = callType; }
public double getPeakDelaySec() { return peakDelaySec; }

View File

@ -0,0 +1,162 @@
package IshmaelDetector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import PamguardMVC.DataUnitBaseData;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import binaryFileStorage.BinaryDataSource;
import binaryFileStorage.BinaryHeader;
import binaryFileStorage.BinaryObjectData;
import binaryFileStorage.ModuleFooter;
import binaryFileStorage.ModuleHeader;
/**
* Binary data for peak binary data source.
*
* @author Jamie Macaulay
*
*/
public class IshFnBinarySource extends BinaryDataSource {
/**
* Stream name
*/
private String streamName;
/**
* Reference to the peak process.
*/
private IshDetFnProcess ishDetFnProcess;
private static final int currentVersion = 1;
public IshFnBinarySource(IshDetFnProcess ishDetFnProcess, PamDataBlock sisterDataBlock, String streamName) {
super(sisterDataBlock);
this.streamName=streamName;
this.ishDetFnProcess = ishDetFnProcess;
}
@Override
public String getStreamName() {
return "Ishmael Peak Data";
}
@Override
public int getStreamVersion() {
return 1;
}
@Override
public int getModuleVersion() {
return currentVersion;
}
@Override
public byte[] getModuleHeaderData() {
// TODO Auto-generated method stub
return null;
}
@Override
public PamDataUnit sinkData(BinaryObjectData binaryObjectData,
BinaryHeader bh, int moduleVersion) {
ByteArrayInputStream bis = new ByteArrayInputStream(binaryObjectData.getData(),
0, binaryObjectData.getDataLength());
DataInputStream dis = new DataInputStream(bis);
DataUnitBaseData baseData = binaryObjectData.getDataUnitBaseData();
//This is not stored in the base data. Probably sensible because it's the same number for every data unit.
//baseData.setMeasuredAmplitudeType(DataUnitBaseData.AMPLITUDE_SCALE_LINREFSD); <- now in constructor.
//System.out.println("DLDetecitonBinarySource: sink: " + baseData.getMeasuredAmplitudeType());
//model results are loaded as annotations.
try {
int nDet = dis.readInt();
double[][] detData = new double[nDet][];
for (int i=0; i<nDet; i++) {
//the first double is peak, the second is noise.
detData[i] = new double[] {dis.readDouble(), dis.readDouble()};
}
if (baseData.getChannelBitmap()==0) {
baseData.setChannelBitmap(1);
}
IshDetFnDataUnit newUnit = new IshDetFnDataUnit(baseData, detData);
bis.close();
return newUnit;
} catch (IOException e) {
// e.printStackTrace();
System.err.println(e.getMessage());
return null;
}
}
@Override
public ModuleHeader sinkModuleHeader(BinaryObjectData binaryObjectData, BinaryHeader bh) {
// TODO Auto-generated method stub
return null;
}
@Override
public ModuleFooter sinkModuleFooter(BinaryObjectData binaryObjectData, BinaryHeader bh,
ModuleHeader moduleHeader) {
// TODO Auto-generated method stub
return null;
}
private ByteArrayOutputStream bos;
private DataOutputStream dos;
@Override
public BinaryObjectData getPackedData(PamDataUnit pamDataUnit) {
IshDetFnDataUnit ishDet = (IshDetFnDataUnit) pamDataUnit;
//System.out.println("DLDetecitonBinarySource: packed: " + pamDataUnit.getBasicData().getMeasuredAmplitudeType());
// make a byte array output stream and write the data to that,
// then dump that down to the main storage stream
if (dos == null || bos == null) {
dos = new DataOutputStream(bos = new ByteArrayOutputStream());
}
else {
bos.reset();
}
try {
dos.writeInt(ishDet.getDetData().length);
for (int i=0; i<ishDet.getDetData().length; i++) {
dos.writeDouble(ishDet.getDetData()[i][0]);
dos.writeDouble(ishDet.getDetData()[i][1]);
}
}
catch (IOException e) {
e.printStackTrace();
return null;
}
// getBinaryStorageStream().storeData(1, cd.getTimeMilliseconds(), bos.toByteArray());
return new BinaryObjectData(1, bos.toByteArray());
}
@Override
public void newFileOpened(File outputFile) {
// TODO Auto-generated method stub
}
}

View File

@ -71,6 +71,8 @@ public class IshLogger extends PamDetectionLogging {
long durationSam = getDuration().getIntegerValue();
double pHeight = peakHeight.getDoubleValue();
long pTimeSam = peakSample.getIntegerValue();
//create the data unit - note that no raw waveform data is stored in the database.
IshDetection id = new IshDetection(timeMilliseconds, endMillis, (float)getLowFreq().getDoubleValue(),
(float)getHighFreq().getDoubleValue(), pTimeSam, pHeight, getPamDataBlock(), chanMap, startSam, durationSam);
id.setDatabaseIndex(databaseIndex);

View File

@ -26,7 +26,6 @@ import PamguardMVC.PamProcess;
public class IshPeakProcess extends PamProcess
{
public static final SymbolData defaultSymbol = new SymbolData(PamSymbolType.SYMBOL_DIAMOND, 10, 10, false, Color.GREEN, Color.GREEN);
//public Complex[] inputData;
//IshDetCalcIntfc ishDetCalcIntfc;
@ -49,6 +48,11 @@ public class IshPeakProcess extends PamProcess
*/
private int activeChannels;
/**
* The Ishmael binary data source.
*/
private IshBinaryDataSource ishmealBinaryDataSource;
public IshPeakProcess(IshDetControl ishDetControl,
PamDataBlock parentDataBlock) {
@ -61,6 +65,12 @@ public class IshPeakProcess extends PamProcess
ishDetControl.getUnitName() + " events", this, parentDataBlock.getSequenceMap());
outputDataBlock.setCanClipGenerate(true);
addOutputDataBlock(outputDataBlock);
//set the binary data source.
ishmealBinaryDataSource = new IshBinaryDataSource(this, outputDataBlock, "Ishmael Detections");
outputDataBlock.setBinaryDataSource(ishmealBinaryDataSource);
//graphics for the map and spectrogram.
PamDetectionOverlayGraphics overlayGraphics = new PamDetectionOverlayGraphics(outputDataBlock, new PamSymbol(defaultSymbol));
outputDataBlock.setOverlayDraw(overlayGraphics);
StandardSymbolManager symbolManager = new StandardSymbolManager(outputDataBlock, defaultSymbol, true);
@ -129,7 +139,6 @@ public class IshPeakProcess extends PamProcess
else {
maxTimeN = Math.max(0, (long)(dRate * p.maxTime));
}
refactoryTimeSam = Math.max(0, (long)(this.getSampleRate() * p.refractoryTime));
@ -191,7 +200,7 @@ public class IshPeakProcess extends PamProcess
Math.round(1000.0 * (float)durationSam * ishDetControl.ishDetFnProcess.getDetSampleRate());
float lowFreq = ishDetControl.ishDetFnProcess.getLoFreq();
float highFreq = ishDetControl.ishDetFnProcess.getHiFreq();
IshDetection iDet = outputDataBlock.getRecycledUnit();
if (iDet != null) { //refurbished
// iDet.setInfo(startMsec, 1 << chanIx, startSam, durationSam,
@ -205,6 +214,7 @@ public class IshPeakProcess extends PamProcess
//11/03/2020 - major bug fix - start sample of Ishamel detector data unit was wrong - it was at the end of the data unit for some reason.
iDet = new IshDetection(startMsec, endMsec, lowFreq, highFreq, chan.peakTimeSam,
chan.peakHeight, outputDataBlock, arg1.getChannelBitmap(), startSam - durationSam, durationSam);
iDet.setSequenceBitmap(arg1.getSequenceBitmapObject());
iDet.setParentDataBlock(outputDataBlock);
}
@ -238,6 +248,7 @@ public class IshPeakProcess extends PamProcess
}
}
/**
* Checks whether a peak is over threshold...
* @param value - the current value of the detector
@ -270,4 +281,8 @@ public class IshPeakProcess extends PamProcess
//This keeps the compiler happy -- it's abstract in the superclass.
@Override public void pamStop() { }
public PamDataBlock getOutputDataBlock() {
return this.outputDataBlock;
}
}

View File

@ -1,93 +0,0 @@
package IshmaelDetector;
import java.io.File;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import binaryFileStorage.BinaryDataSource;
import binaryFileStorage.BinaryHeader;
import binaryFileStorage.BinaryObjectData;
import binaryFileStorage.ModuleFooter;
import binaryFileStorage.ModuleHeader;
/**
* Beginning of a binary data source for the Ishameal detector to save the
* Ishmael data so new peaks can be picked out.
* TODO
* @author Jamie Macaulay
*
*/
public class IshmaelBinaryDataSource extends BinaryDataSource {
/**
* Stream name
*/
private String streamName;
/**
* The Ishmael detector control.
*/
private IshDetFnProcess ishDetControl;
private static final int currentVersion = 1;
public IshmaelBinaryDataSource(IshDetFnProcess ishDetFnProcess, PamDataBlock sisterDataBlock, String streamName) {
super(sisterDataBlock);
this.ishDetControl=ishDetFnProcess;
this.streamName = streamName;
}
@Override
public String getStreamName() {
return streamName;
}
@Override
public int getStreamVersion() {
return 1;
}
@Override
public int getModuleVersion() {
return currentVersion;
}
@Override
public byte[] getModuleHeaderData() {
// TODO Auto-generated method stub
return null;
}
@Override
public PamDataUnit sinkData(BinaryObjectData binaryObjectData, BinaryHeader bh, int moduleVersion) {
// TODO Auto-generated method stub
return null;
}
@Override
public ModuleHeader sinkModuleHeader(BinaryObjectData binaryObjectData, BinaryHeader bh) {
// TODO Auto-generated method stub
return null;
}
@Override
public ModuleFooter sinkModuleFooter(BinaryObjectData binaryObjectData, BinaryHeader bh,
ModuleHeader moduleHeader) {
// TODO Auto-generated method stub
return null;
}
@Override
public BinaryObjectData getPackedData(PamDataUnit pamDataUnit) {
// TODO Auto-generated method stub
return null;
}
@Override
public void newFileOpened(File outputFile) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,56 @@
package IshmaelDetector.dataPlotFX;
import IshmaelDetector.IshDetControl;
import PamView.GeneralProjector.ParameterType;
import PamView.GeneralProjector.ParameterUnits;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import dataPlotsFX.TDSymbolChooserFX;
import dataPlotsFX.data.TDDataInfoFX;
import dataPlotsFX.data.TDDataProviderFX;
import dataPlotsFX.data.generic.GenericScaleInfo;
import dataPlotsFX.layout.TDGraphFX;
/**
* Plots the raw Ishmael data (i.e the detection values and noise)
*
* @author Jamie Macaulay
*
*/
public class IshamelFnPlotInfo extends TDDataInfoFX {
/**
* Ish det control
*/
private IshDetControl ishControl;
private GenericScaleInfo probabilityScaleInfo;
private GenericScaleInfo frequencyInfo;
public IshamelFnPlotInfo(TDDataProviderFX tdDataProvider, IshDetControl ishControl, TDGraphFX tdGraph) {
super(tdDataProvider, tdGraph, ishControl.getIshDetFnProcess().getOutputDataBlock());
this.ishControl=ishControl;
probabilityScaleInfo = new GenericScaleInfo(-0.1, 1.1, ParameterType.PROBABILITY, ParameterUnits.PROBABILITY);
frequencyInfo = new GenericScaleInfo(0, 1, ParameterType.FREQUENCY, ParameterUnits.HZ);
addScaleInfo(probabilityScaleInfo);
addScaleInfo(frequencyInfo);
}
@Override
public Double getDataValue(PamDataUnit pamDataUnit) {
// TODO Auto-generated method stub
return null;
}
@Override
public TDSymbolChooserFX getSymbolChooser() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,79 @@
package IshmaelDetector.dataPlotFX;
import java.io.FileInputStream;
import IshmaelDetector.IshDetControl;
import PamguardMVC.PamDataUnit;
import dataPlotsFX.data.TDDataProviderFX;
import dataPlotsFX.data.generic.GenericDataPlotInfo;
import dataPlotsFX.data.generic.GenericSettingsPane;
import dataPlotsFX.layout.TDGraphFX;
import dataPlotsFX.layout.TDSettingsPane;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import pamViewFX.fxGlyphs.PamSVGIcon;
/**
* Plots Ishmael detections on the TDdisplayFX
*
* @author Jamie Macaulay
*
*/
public class IshmaelDetPlotInfo extends GenericDataPlotInfo {
/*
* Ish det control
*/
private IshDetControl ishControl;
/**
* Settings pane for Ishamel detections
*/
private GenericSettingsPane genericSymbolpane;
public IshmaelDetPlotInfo(TDDataProviderFX tdDataProvider, IshDetControl ishControl, TDGraphFX tdGraph) {
super(tdDataProvider, tdGraph, ishControl.getIshPeakProcess().getOutputDataBlock());
this.ishControl=ishControl;
}
public TDSettingsPane getGraphSettingsPane() {
if (genericSymbolpane==null) {
genericSymbolpane = new GenericSettingsPane(this);
genericSymbolpane.setIcon(makeIcon());
}
return genericSymbolpane;
}
@Override
public Double getAmplitudeValue(PamDataUnit pamDataUnit) {
System.out.println("Ishamel max val: " + pamDataUnit.getAmplitudeDB());
return pamDataUnit.getAmplitudeDB();
}
private Node makeIcon() {
try {
Image image = new Image(getClass().getResourceAsStream("/Resources/Ishmael_icon.png"));
//Setting the image view
ImageView imageView = new ImageView(image);
//setting the fit height and width of the image view
imageView.setFitHeight(20);
imageView.setFitWidth(20);
//Setting the preserve ratio of the image view
imageView.setPreserveRatio(true);
return imageView;
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,33 @@
package IshmaelDetector.dataPlotFX;
import IshmaelDetector.IshDetControl;
import dataPlotsFX.data.TDDataInfoFX;
import dataPlotsFX.data.TDDataProviderFX;
import dataPlotsFX.layout.TDGraphFX;
/**
* The plot provider for Ishmael detection data.
* @author Jamie Macaulay
*
*/
public class IshmaelDetPlotProvider extends TDDataProviderFX {
private IshDetControl ishControl;
public IshmaelDetPlotProvider(IshDetControl ishControl) {
super(ishControl.getIshPeakProcess().getOutputDataBlock());
this.ishControl = ishControl;
}
@Override
public TDDataInfoFX createDataInfo(TDGraphFX tdGraph) {
return new IshmaelDetPlotInfo(this, ishControl, tdGraph);
}
@Override
public String getName() {
return "Detections, " + ishControl.getUnitName();
}
}

View File

@ -0,0 +1,14 @@
package IshmaelDetector.dataPlotFX;
import rawDeepLearningClassifier.dataPlotFX.DLPredictionPane;
import rawDeepLearningClassifier.dataPlotFX.DLPredictionPlotInfoFX;
public class IshmaelFnPane extends DLPredictionPane {
public IshmaelFnPane(DLPredictionPlotInfoFX dlPredictionPlotInfoFX) {
super(dlPredictionPlotInfoFX);
// TODO Auto-generated constructor stub
}
}

View File

@ -0,0 +1,96 @@
package IshmaelDetector.dataPlotFX;
import IshmaelDetector.IshDetControl;
import IshmaelDetector.IshDetFnDataUnit;
import PamView.GeneralProjector.ParameterType;
import PamView.GeneralProjector.ParameterUnits;
import PamguardMVC.PamDataUnit;
import dataPlotsFX.data.TDDataProviderFX;
import dataPlotsFX.data.generic.GenericLinePlotInfo;
import dataPlotsFX.data.generic.GenericScaleInfo;
import dataPlotsFX.layout.TDGraphFX;
import javafx.scene.paint.Color;
import rawDeepLearningClassifier.dataPlotFX.LineInfo;
/**
* Plots the raw Ishmael detection output.
*
* @author Jamie Macaulay
*
*/
public class IshmaelFnPlotInfo extends GenericLinePlotInfo {
/**
* The frequency information.
*/
private GenericScaleInfo ishmaelPeakInfo;
/**
* The default colour.
*/
//private Color color = Color.DODGERBLUE;
/**
* Reference to the Ishmael detector control.
*/
private IshDetControl ishDetControl;
private LineInfo defaultLineInfo = new LineInfo(true, Color.BLUE);
private LineInfo threshLineInfo = new LineInfo(true, Color.RED);
public IshmaelFnPlotInfo(TDDataProviderFX tdDataProvider, IshDetControl ishDetControl, TDGraphFX tdGraph) {
super(tdDataProvider, tdGraph, ishDetControl.getIshDetFnProcess().getOutputDataBlock());
this.ishDetControl=ishDetControl;
ishmaelPeakInfo = new GenericScaleInfo(0, 100, ParameterType.ISHDET, ParameterUnits.NONE);
addScaleInfo(ishmaelPeakInfo);
}
@Override
public double[][] getDetData(PamDataUnit pamDataUnit) {
//System.out.println("Ch: " + pamDataUnit.getChannelBitmap() + " len: "+ ((IshDetFnDataUnit) pamDataUnit) .getDetData().length);
double[][] data = ((IshDetFnDataUnit) pamDataUnit) .getDetData();
double[][] plotData = new double[2][];
if (data.length==3) {
//here plot the threshold and smoothed data.
plotData[0] = data[2]; //the results
double[] thresh = new double[data[1].length];
for (int i=0; i<thresh.length; i++) {
thresh[i] = data[1][i]+ishDetControl.getIshDetectionParams().thresh;
}
plotData[1]=thresh;
}
else {
//static threshold - add the threshold to the data
double[] thresh = new double[data[0].length];
for (int i=0; i<thresh.length; i++) {
thresh[i] = ishDetControl.getIshDetectionParams().thresh;
}
plotData[0]= data[0];
plotData[1]= thresh;
}
return plotData;
}
@Override
public LineInfo getColor(int i) {
if (i==0) {
return defaultLineInfo;
}
else {
return threshLineInfo;
}
}
}

View File

@ -0,0 +1,33 @@
package IshmaelDetector.dataPlotFX;
import IshmaelDetector.IshDetControl;
import dataPlotsFX.data.TDDataInfoFX;
import dataPlotsFX.data.TDDataProviderFX;
import dataPlotsFX.layout.TDGraphFX;
/**
* The Ishmael raw detection output plot provider.
*
* @author Jamie Macaulay
*
*/
public class IshmaelFnPlotProvider extends TDDataProviderFX {
private IshDetControl ishControl;
public IshmaelFnPlotProvider(IshDetControl ishControl) {
super(ishControl.getIshDetFnProcess().getOutputDataBlock());
this.ishControl = ishControl;
}
@Override
public TDDataInfoFX createDataInfo(TDGraphFX tdGraph) {
return new IshmaelFnPlotInfo(this, ishControl, tdGraph);
}
@Override
public String getName() {
return "Raw Detection Output, " + ishControl.getUnitName();
}
}

View File

@ -4,6 +4,7 @@ import IshmaelDetector.EnergySumControl;
import IshmaelDetector.EnergySumParams;
import IshmaelDetector.IshDetParams;
import PamController.SettingsPane;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
@ -12,6 +13,7 @@ import javafx.scene.layout.Pane;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxNodes.PamSpinner;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch;
import pamViewFX.fxNodes.utilsFX.ControlField;
/**
@ -46,14 +48,14 @@ public class EnergySumPane extends SettingsPane<IshDetParams> {
/**
* Check box for changing using log scale or not.
*/
private CheckBox logScaleCheckBox;
private PamToggleSwitch logScaleCheckBox;
//comparing ratios
/**
* Check box for comparing ratios in the Ishamel detector.
*/
private CheckBox rationCheckBox;
private PamToggleSwitch rationCheckBox;
private ControlField<Double> minRatioFreq;
@ -62,7 +64,7 @@ public class EnergySumPane extends SettingsPane<IshDetParams> {
/**
* Check box for the adaptive noise floor
*/
private CheckBox noiseFloorBox;
private PamToggleSwitch noiseFloorBox;
//adaptive threshold
/**
@ -81,12 +83,14 @@ public class EnergySumPane extends SettingsPane<IshDetParams> {
/**
* Check box for enabling output smoothing.
*/
private CheckBox outPutSmoothing;
private PamToggleSwitch outPutSmoothing;
/**
* Long filter control for adaptive noise floor
*/
private ControlField<Double> shortFilter;
public static final double INSET_RIGHT = 130;
public EnergySumPane(EnergySumControl ishEnergyControl) {
super(null);
@ -107,15 +111,18 @@ public class EnergySumPane extends SettingsPane<IshDetParams> {
PamGuiManagerFX.titleFont2style(titleLabel);
//titleLabel.setFont(PamGuiManagerFX.titleFontSize2);
minFreq = new ControlField<Double>("Lower Frequency Bound ", "Hz", 0, Double.MAX_VALUE, 500);
minFreq = new ControlField<Double>("Lower Frequency Bound", "Hz", 0, Double.MAX_VALUE, 500);
minFreq.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(3));
minFreq.setTooltip(new Tooltip("The lower bound of the frequency band to analyse"));
// minFreq.getLabel1().setPadding(new Insets(0,INSET_RIGHT,0,0));
minFreq.getLabel1().setPrefWidth(INSET_RIGHT);
maxFreq = new ControlField<Double>("Upper Frequency Bound ", "Hz", 0, Double.MAX_VALUE, 500);
maxFreq = new ControlField<Double>("Upper Frequency Bound", "Hz", 0, Double.MAX_VALUE, 500);
maxFreq.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(3));
maxFreq.setTooltip(new Tooltip("The upper bound frequency band to analyse"));
rationCheckBox = new CheckBox("Use Energy Ratio");
maxFreq.getLabel1().setPrefWidth(INSET_RIGHT);
rationCheckBox = new PamToggleSwitch("Use Energy Ratio");
rationCheckBox.setTooltip(new Tooltip(
"Sometimes, pulsed noises (clicks or thumps) can be enough like a call of interest\n" +
"to trigger false detections by energy measurement. In this case, it can work to use\n "+
@ -126,18 +133,20 @@ public class EnergySumPane extends SettingsPane<IshDetParams> {
//adaptive noise floor and ration are not compatible
if (rationCheckBox.isSelected()) noiseFloorBox.setSelected(false);
});
minRatioFreq = new ControlField<Double>("Lower Ratio Bound ", "Hz", 0, Double.MAX_VALUE, 500);
minRatioFreq = new ControlField<Double>("Lower Ratio Bound", "Hz", 0, Double.MAX_VALUE, 500);
minRatioFreq.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(3));
minRatioFreq.setTooltip(new Tooltip("The lower bound frequency band to compare frequency ratios"));
minRatioFreq.getLabel1().setPrefWidth(INSET_RIGHT);
maxRatioFreq = new ControlField<Double>("Upper Ratio Bound ", "Hz", 0, Double.MAX_VALUE, 500);
maxRatioFreq = new ControlField<Double>("Upper Ratio Bound", "Hz", 0, Double.MAX_VALUE, 500);
maxRatioFreq.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(3));
maxRatioFreq.setTooltip(new Tooltip("The upper bound frequency band to compare frequency ratios"));
maxRatioFreq.getLabel1().setPrefWidth(INSET_RIGHT);
noiseFloorBox = new CheckBox("Use Adaptive Threshold");
noiseFloorBox = new PamToggleSwitch("Use Adaptive Threshold");
noiseFloorBox.setTooltip(new Tooltip(
"The default Ishmael detector uses a static threshold. If noise suddenly increases\n"
+ "then the threshold can be below the noise either triggerring lots of detections or\n"
@ -145,11 +154,12 @@ public class EnergySumPane extends SettingsPane<IshDetParams> {
+ "above the noise floor. An adaptive noise floor tracks the noise with a detection \n"
+ "triggered if the energy measurment reaches threshold above the adaptive noise floor."));
longFilter = new ControlField<Double>("Long filter ", "", 0, Double.MAX_VALUE, 0.00001);
longFilter = new ControlField<Double>("Long filter", "", 0, Double.MAX_VALUE, 0.00001);
longFilter.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(9));
longFilter.setTooltip(new Tooltip("The long filter. Lower values mean the adaptive noise floor changes more slowly with changing energy"));
spikeThresh = new ControlField<Double>("Spike Threshold ", "", 1, Double.MAX_VALUE, 10);
longFilter.getLabel1().setPrefWidth(INSET_RIGHT);
spikeThresh = new ControlField<Double>("Spike Threshold", "", 1, Double.MAX_VALUE, 10);
spikeThresh.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(9));
spikeThresh.setTooltip(new Tooltip( "The maximum multiple above the Ishmael FFT detection output the adaptive threshold can be before\n"
+ "an exponential decay is added. Since the adaptive threshold is essentially an averaging filter\n"
@ -157,7 +167,8 @@ public class EnergySumPane extends SettingsPane<IshDetParams> {
+ "adaptive threshold can end up being so high it takes a long time to settle back to tracking the\n"
+ "raw ouput. The spike threshold means that if this occurs the adaptive threshold will fall back to\n"
+ "sensible values much faster."));
spikeThresh.getLabel1().setPrefWidth(INSET_RIGHT);
noiseFloorBox.selectedProperty().addListener((obsval, oldval, newval)->{
enableControls();
//adaptive noise floor and ration are not compatible
@ -165,11 +176,12 @@ public class EnergySumPane extends SettingsPane<IshDetParams> {
rationCheckBox.setSelected(false);
}
});
outPutSmoothing = new CheckBox("Use Detector Smoothing");
outPutSmoothing = new PamToggleSwitch("Use Detector Smoothing");
outPutSmoothing.setTooltip(new Tooltip(
"It can be advantageous to smootrh the out from the detector. This can mean that the detectors\n "
+ "is more robust for detecting some multi modal sounds, e.g. dynamite bombs, which have multiple\n"
"It can be advantageous to smooth the output from the detector. This can mean that the detectors\n "
+ "are more robust for detecting some multi modal sounds, e.g. dynamite bombs, which have multiple\n"
+ "closely spaced peaks do not briefly go below threshold and thus are not detected."));
outPutSmoothing.selectedProperty().addListener((obsval, oldval, newval)->{
enableControls();
@ -178,8 +190,9 @@ public class EnergySumPane extends SettingsPane<IshDetParams> {
shortFilter = new ControlField<Double>("Short filter ", "", 0, Double.MAX_VALUE, 0.1);
shortFilter.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(9));
shortFilter.setTooltip(new Tooltip("The short filter which defines smoothing. Lower values mean the smoothing is greater"));
logScaleCheckBox = new CheckBox("Use log scale");
shortFilter.getLabel1().setPrefWidth(INSET_RIGHT);
logScaleCheckBox = new PamToggleSwitch("Use log scale");
mainPane.getChildren().addAll(titleLabel, minFreq, maxFreq, rationCheckBox, minRatioFreq, maxRatioFreq,
noiseFloorBox, longFilter, spikeThresh, outPutSmoothing, shortFilter, logScaleCheckBox);

View File

@ -43,6 +43,9 @@ public class PeakPickingPane extends SettingsPane<IshDetParams> {
* Spinner for the minimum inter detection interval (IDI) between concurrent detections.
*/
private PamSpinner<Double> minIDI;
public static final double INSET_RIGHT = 130;
public PeakPickingPane() {
super(null);
@ -69,14 +72,19 @@ public class PeakPickingPane extends SettingsPane<IshDetParams> {
int row=0;
gridHolder.add(new Label("Threshold"), 0, 0);
Label threshLabel = new Label("Threshold");
threshLabel.setPrefWidth(INSET_RIGHT);
gridHolder.add(threshLabel, 0, 0);
gridHolder.add(threshold = new PamSpinner<Double>(0, Double.MAX_VALUE, 0.05, 0.01), 1, 0);
threshold.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
threshold.getValueFactory().setConverter(PamSpinner.createStringConverter(8));
threshold.setEditable(true);
row++;
gridHolder.add(new Label("Min time over threshold"), 0, row);
Label minThresh = new Label("Min time over threshold");
minThresh.setPrefWidth(INSET_RIGHT);
gridHolder.add(minThresh, 0, row);
gridHolder.add(minTime = new PamSpinner<Double>(0, Double.MAX_VALUE, 0.4, 0.1), 1, row);
minTime.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
minTime.getValueFactory().setConverter(PamSpinner.createStringConverter(4));
@ -92,7 +100,9 @@ public class PeakPickingPane extends SettingsPane<IshDetParams> {
});
row++;
gridHolder.add(new Label("Max time over threshold"), 0, row);
Label maxThresh = new Label("Max time over threshold");
maxThresh.setPrefWidth(INSET_RIGHT);
gridHolder.add(maxThresh, 0, row);
gridHolder.add(maxTime = new PamSpinner<Double>(0, Double.MAX_VALUE, 0, 0.1), 1, row);
maxTime.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
maxTime.getValueFactory().setConverter(PamSpinner.createStringConverter(4));
@ -107,7 +117,9 @@ public class PeakPickingPane extends SettingsPane<IshDetParams> {
});
row++;
gridHolder.add(new Label("Min IDI"), 0, row);
Label minIDILabel = new Label("Min IDI");
minIDILabel.setPrefWidth(INSET_RIGHT);
gridHolder.add(minIDILabel, 0, row);
gridHolder.add(minIDI = new PamSpinner<Double>(0, Double.MAX_VALUE, 0.4, 0.1), 1, row);
minIDI.getValueFactory().setConverter(PamSpinner.createStringConverter(4));
minIDI.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);

View File

@ -400,7 +400,7 @@ abstract public class IshLocProcess extends PamProcess implements SpectrogramMar
outputUnit.setInfo(startSam, channelMap, startSam, durationSam);
else { //new outputUnit
outputUnit = new IshDetection(startMsec, endMsec, (float)f0, (float)f1,
midSam, 1.0, outputDataBlock, channelMap, startSam, durationSam);
midSam, 1.0, outputDataBlock, channelMap, startSam, durationSam); //TODO - need to add ra data.
}
////////////////////////////////// Do it! ////////////////////////////////////////

View File

@ -84,16 +84,19 @@ public class DelayGroup {
int iOut = 0;
//if max delays is null then it's just the spectrum length
if (maxDelays==null) {
//added maxDelays.length!=nOutputs because imported clicks (e.g. from Rainbow clicks) can have
//a maxDelays length of 0.
if (maxDelays==null || maxDelays.length!=nOutputs) {
maxDelays = new double[waveformInput.length];
for (int i = 0; i < nOutputs; i++) {
maxDelays[i]=specData[0].getFftLength();
}
}
//perform the time delay calculations
for (int i = 0; i < nChan; i++) {
for (int j = i+1; j < nChan; j++, iOut++) {
for (int j = i+1; j < nChan; j++, iOut++) {
TimeDelayData td = correlations.getDelay(specData[i].getFftData(), specData[j].getFftData(),
delayParams, sampleRate*delayParams.getUpSample(), specData[i].getFftLength(), maxDelays[iOut]);
td.scaleDelay(1./ delayParams.getUpSample());

View File

@ -842,5 +842,18 @@ public class PamArrayUtils {
return false;
}
/**
* Convert a float array to a double array.
* @param arrf - the float array
* @return a double array containing the same numbers as arrf.
*/
public static double[] float2Double(float[] arrf) {
double[] arr = new double[arrf.length];
for (int i=0; i<arr.length; i++) {
arr[i] = (double) arrf[i];
}
return arr;
}
}

View File

@ -51,6 +51,7 @@ public class PamUtils {
channels++;
}
}
/*
* Looks like this has been returning 1 instead of -1 for many years.
* May need to revert to this behaviour if modules have come to depend
@ -58,6 +59,7 @@ public class PamUtils {
*/
if (channels > 1)
return -1;
return singleChan;
}

View File

@ -152,6 +152,41 @@ public class AverageWaveform {
count++;
}
/**
* Add to an average spectra when no raw data is present e.g. CPOD. Here the average spectra is an amplitude weighted historgram
* of the bandwidth of the detection.
* @param minFreq - the minimum frequency in Hz
* @param maxFreq - the maximum frequency in Hz
* @param amplitude - the amplitude of the detection in dB
* @param sampleRate2 - the sample rate in samples per second.
* @param defaultFFTLen - the defaultFFT length.
*/
public void addWaveform(double minFreq, double maxFreq, double amplitude, float sampleRate2, int defaultFFTLen) {
this.fftLength = defaultFFTLen;
//System.out.println("Add to averagewaveform: minFreq " + minFreq + " maxFreq: " + maxFreq + " amplitude: " + amplitude + " sR: " + sampleRate2);
if (avrgSpectra==null) {
//the FFT length is the length of the first waveform
avrgSpectra = new double[this.fftLength];
return;
}
double amplitudebin = amplitude/(maxFreq-minFreq);
double minFreqBin;
double maxFreqBin;
for (int i= 0; i <avrgSpectra.length; i++) {
minFreqBin = (i/(double) avrgSpectra.length)*(sampleRate2/2);
maxFreqBin = (i/(double) avrgSpectra.length)*(sampleRate2/2);
if (minFreqBin > minFreq && maxFreqBin<=maxFreq) {
avrgSpectra[i]+=amplitudebin;
}
}
count++;
}
/**
* Get the average waveform. Not normalised.
* @return the average waveform
@ -230,7 +265,7 @@ public class AverageWaveform {
/**
* Get the sample rate for the average spectra.
* @return the smaple rate.
* @return the sample rate in samples per second.
*/
public float getSampleRate() {
return sampleRate;
@ -239,11 +274,14 @@ public class AverageWaveform {
/**
* Set the sample rate for the average spectra.
* @param the smaple rate.
* @param the sample rate in samples per second.
*/
public void setSampleRate(float sampleRate) {
this.sampleRate = sampleRate;
}
}

View File

@ -85,7 +85,7 @@ public abstract class GeneralProjector<T extends PamCoordinate> {
TIME ("Time"), FREQUENCY ("Frequency"), AMPLITUDE ("Amplitude"), LATITUDE ("Latitude"), LONGITUDE ("Longitude") ,
BEARING ("Bearing"), RANGE ("Range"), SLANTANGLE ("Slant angle"), ICI ("Inter-click-interval"),
DEPTH ("Depth"), SLANTBEARING ("Slant bearing"), AMPLITUDE_STEM ("Amplitude (stem)"), AMPLITUDE_LIN ("Linear Amplitude"),
SPEED ("Speed"), PROBABILITY ("Probability"), NCYCLES ("No. cycles"), BANDWIDTH("Bandwidth");
SPEED ("Speed"), PROBABILITY ("Probability"), NCYCLES ("No. cycles"), BANDWIDTH("Bandwidth"), ISHDET("Detector Output");
private String unit;
@ -103,7 +103,8 @@ public abstract class GeneralProjector<T extends PamCoordinate> {
static public enum ParameterUnits {
SECONDS ("s"), HZ ("Hz"), DB ("dB"), RAW ("raw"), DECIMALDEGREES ("\u00B0"), METERS ("m"),
NMILES ("nmi"), DEGREES ("\u00B0"), RADIANS ("rad"), METRESPERSECOND ("m/s"), PROBABILITY ("p"), N("N");
NMILES ("nmi"), DEGREES ("\u00B0"), RADIANS ("rad"), METRESPERSECOND ("m/s"), PROBABILITY ("p"),
N("N"), NONE("");
private String unit;

View File

@ -45,6 +45,8 @@ public class PeakFreqModifier extends SymbolModifier {
*/
private ColourArrayType colourArrayType;
private PeakFreqOptionsPane peakFreqOptions;
public PeakFreqModifier(PamSymbolChooser symbolChooser) {
super(PEAK_FREQ_MODIFIER_NAME, symbolChooser, SymbolModType.FILLCOLOUR | SymbolModType.LINECOLOUR );
checkColourArray();
@ -124,11 +126,17 @@ public class PeakFreqModifier extends SymbolModifier {
@Override
public SymbolModifierPane getOptionsPane() {
return new PeakFreqOptionsPane(this);
//System.out.println("PEAK FREQ COLOUR ARRAY2: " + peakFreqSymbolOptions.freqColourArray);
if (this.peakFreqOptions==null) {
peakFreqOptions = new PeakFreqOptionsPane(this);
peakFreqOptions.setParams();
}
return peakFreqOptions;
}
@Override
public SymbolModifierParams getSymbolModifierParams() {
//System.out.println("PEAK FREQ COLOUR ARRAY3: " + peakFreqSymbolOptions.freqColourArray);
if (peakFreqSymbolOptions==null) peakFreqSymbolOptions = new PeakFreqSymbolOptions();
return peakFreqSymbolOptions;
}
@ -138,9 +146,15 @@ public class PeakFreqModifier extends SymbolModifier {
if (!(symbolModifierParams instanceof PeakFreqSymbolOptions)) {
System.err.println("PeakFreqModifier: warning: the saved parameters were not an instance of PeakFreqSymbolOptions");
peakFreqSymbolOptions = new PeakFreqSymbolOptions();
checkColourArray();
return;
}
else this.peakFreqSymbolOptions = (PeakFreqSymbolOptions) symbolModifierParams;
else {
this.peakFreqSymbolOptions = (PeakFreqSymbolOptions) symbolModifierParams;
//System.out.println("PEAK FREQ COLOUR ARRAY: " + peakFreqSymbolOptions.freqColourArray);
checkColourArray();
}
}

View File

@ -99,35 +99,11 @@ public class RawDataTransforms {
*/
private int shortestFFTLength;
/**
* Object for synchronization. Get thread lock if this isn't the same as
* the object holding the data.
*/
private Object synchObject;
/**
* Raw Data Transforms for a RawDataHolder using the rawDataHolder as the synchronization
* object.
* @param rawDataHolder RawDataHolder object (e.g. a click)
*/
public RawDataTransforms(@SuppressWarnings("rawtypes") PamDataUnit rawDataHolder) {
this(rawDataHolder, rawDataHolder);
}
/**
* Raw Data Transforms for a RawDataHolder.
* @param rawDataHolder RawDataHolder object (e.g. a click)
* @param synchObject synchronization object, which is most likely the RawDataHolder object.
*/
public RawDataTransforms(@SuppressWarnings("rawtypes") PamDataUnit rawDataHolder, Object synchObject) {
this.rawData=(RawDataHolder) rawDataHolder;
this.dataUnit = rawDataHolder;
this.synchObject = synchObject;
if (this.synchObject == null) {
this.synchObject = this;
}
}
@ -167,25 +143,23 @@ public class RawDataTransforms {
* @param fftLength
* @return Power spectrum
*/
public double[] getPowerSpectrum(int channel, int minBin, int maxBin, int fftLength) {
synchronized (synchObject) {
if (minBin==0 && maxBin>=this.getWaveData(0).length-1) {
return getPowerSpectrum(channel, fftLength);
}
if (fftLength == 0) {
fftLength = getCurrentSpectrumLength();
}
double[] waveformTrim = new double[maxBin-minBin];
// System.out.println("minBin: " + minBin + " maxBin: " + maxBin + " raw waveform: " + this.getWaveData(channel).length);
System.arraycopy(this.getWaveData(channel), minBin, waveformTrim, 0, Math.min(this.getWaveData(channel).length-minBin-1, waveformTrim.length));
ComplexArray cData = getComplexSpectrumHann(waveformTrim, fftLength);
return cData.magsq();
public synchronized double[] getPowerSpectrum(int channel, int minBin, int maxBin, int fftLength) {
if (minBin==0 && maxBin>=this.getWaveData(0).length-1) {
return getPowerSpectrum(channel, fftLength);
}
if (fftLength == 0) {
fftLength = getCurrentSpectrumLength();
}
double[] waveformTrim = new double[maxBin-minBin];
//System.out.println("minBin: " + minBin + " maxBin: " + maxBin + " raw waveform: " + this.getWaveData(channel).length);
System.arraycopy(this.getWaveData(channel), minBin, waveformTrim, 0, Math.min(this.getWaveData(channel).length-minBin-1, waveformTrim.length));
ComplexArray cData = getComplexSpectrumHann(waveformTrim, fftLength);
return cData.magsq();
}
/**
@ -196,31 +170,29 @@ public class RawDataTransforms {
* @param fftLength
* @return Power spectrum
*/
public double[] getPowerSpectrum(int channel, int fftLength) {
synchronized (synchObject) {
if (powerSpectra == null) {
powerSpectra = new double[PamUtils.getNumChannels(dataUnit.getChannelBitmap())][];
}
if (fftLength == 0) {
fftLength = getCurrentSpectrumLength();
}
if (powerSpectra[channel] == null
|| powerSpectra[channel].length != fftLength / 2) {
ComplexArray cData = getComplexSpectrumHann(channel, fftLength);
currentSpecLen = fftLength;
powerSpectra[channel] = cData.magsq();
if (powerSpectra==null){
System.err.println("DLDetection: could not calculate power spectra");
return null;
}
if (powerSpectra[channel].length != fftLength/2) {
powerSpectra[channel] = Arrays.copyOf(powerSpectra[channel], fftLength/2);
}
}
return powerSpectra[channel];
public synchronized double[] getPowerSpectrum(int channel, int fftLength) {
if (powerSpectra == null) {
powerSpectra = new double[PamUtils.getNumChannels(dataUnit.getChannelBitmap())][];
}
if (fftLength == 0) {
fftLength = getCurrentSpectrumLength();
}
if (powerSpectra[channel] == null
|| powerSpectra[channel].length != fftLength / 2) {
ComplexArray cData = getComplexSpectrumHann(channel, fftLength);
currentSpecLen = fftLength;
powerSpectra[channel] = cData.magsq();
if (powerSpectra==null){
System.err.println("DLDetection: could not calculate power spectra");
return null;
}
if (powerSpectra[channel].length != fftLength/2) {
powerSpectra[channel] = Arrays.copyOf(powerSpectra[channel], fftLength/2);
}
}
return powerSpectra[channel];
}
@ -230,27 +202,25 @@ public class RawDataTransforms {
* @param fftLength
* @return Sum of power spectra
*/
public double[] getTotalPowerSpectrum(int fftLength) {
synchronized (synchObject) {
if (fftLength == 0) {
fftLength = getCurrentSpectrumLength();
}
if (fftLength == 0) {
fftLength = PamUtils.getMinFftLength(getSampleDuration());
}
double[] ps;
if (totalPowerSpectrum == null
|| totalPowerSpectrum.length != fftLength / 2) {
totalPowerSpectrum = new double[fftLength / 2];
for (int c = 0; c < PamUtils.getNumChannels(this.dataUnit.getChannelBitmap()); c++) {
ps = getPowerSpectrum(c, fftLength);
for (int i = 0; i < fftLength / 2; i++) {
totalPowerSpectrum[i] += ps[i];
}
public synchronized double[] getTotalPowerSpectrum(int fftLength) {
if (fftLength == 0) {
fftLength = getCurrentSpectrumLength();
}
if (fftLength == 0) {
fftLength = PamUtils.getMinFftLength(getSampleDuration());
}
double[] ps;
if (totalPowerSpectrum == null
|| totalPowerSpectrum.length != fftLength / 2) {
totalPowerSpectrum = new double[fftLength / 2];
for (int c = 0; c < PamUtils.getNumChannels(this.dataUnit.getChannelBitmap()); c++) {
ps = getPowerSpectrum(c, fftLength);
for (int i = 0; i < fftLength / 2; i++) {
totalPowerSpectrum[i] += ps[i];
}
}
return totalPowerSpectrum;
}
return totalPowerSpectrum;
}
@ -265,17 +235,15 @@ public class RawDataTransforms {
* @param fftLength - the FFT length to use.
* @return the complex spectrum - the comnplex spectrum of the wave data from the specified channel.
*/
public ComplexArray getComplexSpectrumHann(int channel, int fftLength) {
synchronized (synchObject) {
complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())];
if (complexSpectrum[channel] == null
|| complexSpectrum.length != fftLength / 2) {
public synchronized ComplexArray getComplexSpectrumHann(int channel, int fftLength) {
complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())];
if (complexSpectrum[channel] == null
|| complexSpectrum.length != fftLength / 2) {
complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength);
currentSpecLen = fftLength;
}
return complexSpectrum[channel];
complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength);
currentSpecLen = fftLength;
}
return complexSpectrum[channel];
}
@ -314,12 +282,10 @@ public class RawDataTransforms {
* @return the spectrogram length.
*/
private int getCurrentSpectrumLength() {
synchronized (synchObject) {
if (currentSpecLen<=0) {
currentSpecLen = PamUtils.getMinFftLength(dataUnit.getSampleDuration());
}
return currentSpecLen;
if (currentSpecLen<=0) {
currentSpecLen = PamUtils.getMinFftLength(dataUnit.getSampleDuration());
}
return currentSpecLen;
}
@ -405,31 +371,29 @@ public class RawDataTransforms {
* @param fftLength
* @return the complex spectrum
*/
public ComplexArray getComplexSpectrum(int channel, int fftLength) {
synchronized (synchObject) {
double[] paddedRawData;
double[] rawData;
int i, mn;
public synchronized ComplexArray getComplexSpectrum(int channel, int fftLength) {
double[] paddedRawData;
double[] rawData;
int i, mn;
if (complexSpectrum == null) {
complexSpectrum = new ComplexArray[getNChan()];
}
if (complexSpectrum[channel] == null
|| complexSpectrum.length != fftLength / 2) {
paddedRawData = new double[fftLength];
rawData = getWaveData(channel);
//double[] rotData = getRotationCorrection(channel);
mn = Math.min(fftLength, getSampleDuration().intValue());
for (i = 0; i < mn; i++) {
paddedRawData[i] = rawData[i];//-rotData[i];
}
for (i = mn; i < fftLength; i++) {
paddedRawData[i] = 0;
}
complexSpectrum[channel] = fastFFT.rfft(paddedRawData, fftLength);
}
return complexSpectrum[channel];
if (complexSpectrum == null) {
complexSpectrum = new ComplexArray[getNChan()];
}
if (complexSpectrum[channel] == null
|| complexSpectrum.length != fftLength / 2) {
paddedRawData = new double[fftLength];
rawData = getWaveData(channel);
//double[] rotData = getRotationCorrection(channel);
mn = Math.min(fftLength, getSampleDuration().intValue());
for (i = 0; i < mn; i++) {
paddedRawData[i] = rawData[i];//-rotData[i];
}
for (i = mn; i < fftLength; i++) {
paddedRawData[i] = 0;
}
complexSpectrum[channel] = fastFFT.rfft(paddedRawData, fftLength);
}
return complexSpectrum[channel];
}
@ -438,16 +402,14 @@ public class RawDataTransforms {
* @param iChan channel index
* @return analytic waveform
*/
public double[] getAnalyticWaveform(int iChan) {
synchronized (synchObject) {
if (analyticWaveform == null) {
analyticWaveform = new double[getNChan()][];
}
// if (analyticWaveform[iChan] == null) {
analyticWaveform[iChan] = hilbert.getHilbert(getWaveData(iChan));
// }
return analyticWaveform[iChan];
public synchronized double[] getAnalyticWaveform(int iChan) {
if (analyticWaveform == null) {
analyticWaveform = new double[getNChan()][];
}
// if (analyticWaveform[iChan] == null) {
analyticWaveform[iChan] = hilbert.getHilbert(getWaveData(iChan));
// }
return analyticWaveform[iChan];
}
/**
@ -459,14 +421,12 @@ public class RawDataTransforms {
* @param fftFilterParams fft filter parameters.
* @return analystic waveform.
*/
public double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) {
synchronized (synchObject) {
if (filtered == false || fftFilterParams == null) {
return getAnalyticWaveform(iChan);
}
else {
return getFilteredAnalyticWaveform(fftFilterParams, iChan);
}
public synchronized double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) {
if (filtered == false || fftFilterParams == null) {
return getAnalyticWaveform(iChan);
}
else {
return getFilteredAnalyticWaveform(fftFilterParams, iChan);
}
}
@ -477,8 +437,7 @@ public class RawDataTransforms {
* @param iChan channel number
* @return envelope of the filtered data.
*/
public double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) {
synchronized (synchObject) {
public synchronized double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) {
if (analyticWaveform == null) {
analyticWaveform = new double[getNChan()][];
}
@ -487,7 +446,6 @@ public class RawDataTransforms {
getHilbert(getFilteredWaveData(fftFilterParams, iChan));
// }
return analyticWaveform[iChan];
}
}
/**
@ -497,21 +455,19 @@ public class RawDataTransforms {
* @return analystic waveforms
*/
public double[][] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams) {
synchronized (synchObject) { // new
if (analyticWaveform == null) {
analyticWaveform = new double[getNChan()][];
}
for (int iChan = 0; iChan < getNChan(); iChan++) {
if (fftFilterParams != null) {
analyticWaveform[iChan] = hilbert.
getHilbert(getFilteredWaveData(fftFilterParams, iChan));
}
else {
analyticWaveform[iChan] = getAnalyticWaveform(iChan);
}
}
return analyticWaveform;
if (analyticWaveform == null) {
analyticWaveform = new double[getNChan()][];
}
for (int iChan = 0; iChan < getNChan(); iChan++) {
if (fftFilterParams != null) {
analyticWaveform[iChan] = hilbert.
getHilbert(getFilteredWaveData(fftFilterParams, iChan));
}
else {
analyticWaveform[iChan] = getAnalyticWaveform(iChan);
}
}
return analyticWaveform;
}
@ -522,7 +478,7 @@ public class RawDataTransforms {
* @param channelIndex channel index
* @return filtered waveform data
*/
public double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) {
public synchronized double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) {
filteredWaveData = getFilteredWaveData(filterParams);
return filteredWaveData[channelIndex];
}
@ -533,14 +489,12 @@ public class RawDataTransforms {
* @param filterParams filter parameters
* @return array of filtered data
*/
public double[][] getFilteredWaveData(FFTFilterParams filterParams) {
synchronized (synchObject) {
public synchronized double[][] getFilteredWaveData(FFTFilterParams filterParams) {
//System.out.println("Make filterred wave data!: " + (filterParams != oldFFTFilterParams));
if (filteredWaveData == null || filterParams != oldFFTFilterParams) {
filteredWaveData = makeFilteredWaveData(filterParams);
}
return filteredWaveData;
}
}
@ -584,15 +538,13 @@ public class RawDataTransforms {
* @return FFT filter object.
*/
public FFTFilter getFFTFilter(FFTFilterParams fftFilterParams) {
synchronized (synchObject) {
if (fftFilter == null) {
fftFilter = new FFTFilter(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate());
}
else {
fftFilter.setParams(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate());
}
return fftFilter;
if (fftFilter == null) {
fftFilter = new FFTFilter(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate());
}
else {
fftFilter.setParams(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate());
}
return fftFilter;
}
@ -637,9 +589,7 @@ public class RawDataTransforms {
* @return
*/
private int getNChan() {
synchronized (synchObject) { // new
return this.rawData.getWaveData().length;
}
return this.rawData.getWaveData().length;
}
/**
@ -672,11 +622,9 @@ public class RawDataTransforms {
* Free eup some memory by deleting the filtered wave data, power spectra and analytic waveform.
*/
public void freeMemory() {
synchronized (synchObject) {
filteredWaveData = null;
powerSpectra = null;
analyticWaveform = null;
}
filteredWaveData = null;
powerSpectra = null;
analyticWaveform = null;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -117,9 +117,12 @@ public class BinaryOfflineDataMap extends OfflineDataMap<BinaryOfflineDataMapPoi
Datagram dataGram = mapPoint.getDatagram();
DatagramManager datagramManager = binaryStore.getDatagramManager();
int dataGramSecs = datagramManager.getDatagramSettings().datagramSeconds;
long dataGramMillis = dataGramSecs * 1000L;
int gramLength = dataGramProvider.getNumDataGramPoints();
DatagramDataPoint datagramPoint = null;
if (dataGram == null) {
dataGram = new Datagram(dataGramSecs);
long pStart = mapPoint.getStartTime();
@ -136,6 +139,7 @@ public class BinaryOfflineDataMap extends OfflineDataMap<BinaryOfflineDataMapPoi
* time of this data unit ...
*/
while (pamDataUnit.getTimeMilliseconds() >= datagramPoint.getStartTime()+dataGramMillis) {
datagramPoint.setEndTime(datagramPoint.getStartTime()+dataGramMillis);
long newStart = datagramPoint.getEndTime();
long newEnd = newStart + dataGramMillis;

View File

@ -1530,6 +1530,9 @@ public class SweepClassifierDialog extends PamDialog implements ImportExportUser
if (clickControl!=null) {
int[] chanGroups = clickControl.getClickParameters().getGroupedSourceParameters().getChannelGroups();
multiChan = false;
if (chanGroups==null) return multiChan;
for (int i=0; i<chanGroups.length; i++) {
int chans = clickControl.getClickParameters().getGroupedSourceParameters().getGroupChannels(i);
// Debug.out.println("Check multi-channel: " + chanGroups[i] + " num: " + PamUtils.getNumChannels(chans));

View File

@ -149,6 +149,12 @@ public class CTDataUnit extends CTDetectionGroupDataUnit implements RawDataHolde
// Debug.println("CTDataUnit: Add data to raw data holder: " +
// averageWaveform.getAverageWaveform());
}
else {
//here we simply create a histogram of frequencies and add the freq limits to the histogram.
averageWaveform.addWaveform(dataUnit.getBasicData().getFrequency()[0],dataUnit.getBasicData().getFrequency()[1],
dataUnit.getAmplitudeDB(),
dataUnit.getParentDataBlock().getSampleRate(), defaultFFTLen);
}
}
@Override

View File

@ -86,6 +86,11 @@ public class ClickTrainDataBlock<T extends CTDetectionGroupDataUnit> extends Sup
this.sortData();
}
@Override
public float getSampleRate() {
return super.getSampleRate();
}
// /* (non-Javadoc)
// * @see PamguardMVC.PamDataBlock#removeOldUnitsT(long)

View File

@ -94,6 +94,8 @@ public class ClickTrainProcess extends PamInstantProcess {
getClickTrainParams().dataSourceName);
}
//System.out.println("CPOD sample rate: " + sourceDataBlock.getSampleRate());
// if (sourceDataBlock!=null) System.out.println("Click train process: Source data" + sourceDataBlock.getDataName());
// else System.out.println("The source data is null: " + getClickTrainParams().dataSourceName);
@ -101,6 +103,9 @@ public class ClickTrainProcess extends PamInstantProcess {
setParentDataBlock(sourceDataBlock);
prepareProcess();
//System.out.println("CLICK TRAIN sample rate: " + this.getSampleRate());
}

View File

@ -56,6 +56,13 @@ public interface CTClassifier {
* Set the classifier parameters.
* @param ctClassifierParams - the ct classifier paratmers
*/
public void setParams(CTClassifierParams ctClassifierParams);
public void setParams(CTClassifierParams ctClassifierParams);
/**
* Get the CT classifier params.
* @return the CT classifier params.
*/
public CTClassifierParams getParams();
}

View File

@ -10,6 +10,8 @@ import clickTrainDetector.classification.bearingClassifier.BearingClassification
import clickTrainDetector.classification.bearingClassifier.BearingClassifier;
import clickTrainDetector.classification.simplechi2classifier.Chi2CTClassification;
import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdClassifier;
import clickTrainDetector.classification.standardClassifier.StandardClassification;
import clickTrainDetector.classification.standardClassifier.StandardClassifier;
import clickTrainDetector.classification.templateClassifier.TemplateClassification;
import clickTrainDetector.classification.templateClassifier.CTTemplateClassifier;
@ -43,18 +45,22 @@ public class CTClassifierManager {
this.preClassifier = new Chi2ThresholdClassifier(clickTrainControl, -1);
}
@Deprecated
public String getClassifierName(CTClassifierType classifierType) {
switch (classifierType) {
case CHI2THRESHOLD:
return "X\u00b2 threshold classifier";
case TEMPLATECLASSIFIER:
return "Spectral Template";
case BEARINGCLASSIFIER:
return "Bearing Classifier";
/////****ADD NEW CLASSIFIERS HERE****/////
default:
return "";
}
return classifierType.toString();
// switch (classifierType) {
// case CHI2THRESHOLD:
// return "X\u00b2 threshold classifier";
// case TEMPLATECLASSIFIER:
// return "Spectral Template";
// case BEARINGCLASSIFIER:
// return "Bearing Classifier";
// case STANDARDCLASSIFIER:
// return "Click Train Classifier";
// /////****ADD NEW CLASSIFIERS HERE****/////
// default:
// return "";
// }
}
/**
@ -72,6 +78,7 @@ public class CTClassifierManager {
* @return the CT classifier
*/
public CTClassifier createClassifier(CTClassifierType classifierType) {
if (classifierType==null) return null;
switch (classifierType) {
case CHI2THRESHOLD:
return new Chi2ThresholdClassifier(clickTrainControl, 1);
@ -79,6 +86,8 @@ public class CTClassifierManager {
return new CTTemplateClassifier(clickTrainControl, 1);
case BEARINGCLASSIFIER:
return new BearingClassifier(clickTrainControl, -1);
case STANDARDCLASSIFIER:
return new StandardClassifier(clickTrainControl, -1);
/////****ADD NEW CLASSIFIERS HERE****/////
default:
return new Chi2ThresholdClassifier(clickTrainControl, 1);
@ -113,6 +122,8 @@ public class CTClassifierManager {
return new TemplateClassification(jsonstring);
case BEARINGCLASSIFIER:
return new BearingClassification(jsonstring);
case STANDARDCLASSIFIER:
return new StandardClassification(jsonstring);
/////****ADD NEW CLASSIFICATIONTYPES HERE****/////
default:
return null;
@ -147,7 +158,7 @@ public class CTClassifierManager {
//first check the pre-classifier
Chi2CTClassification classification = this.preClassifier.classifyClickTrain(ctDataUnit);
Debug.out.println("Pre classifier: " + PamCalendar.formatDateTime(ctDataUnit.getTimeMilliseconds()) + " N. " + ctDataUnit.getSubDetectionsCount() + "UID first: " + ctDataUnit.getSubDetection(0).getUID() );
System.out.println("Pre classifier: " + PamCalendar.formatDateTime(ctDataUnit.getTimeMilliseconds()) + " N. " + ctDataUnit.getSubDetectionsCount() + "UID first: " + ctDataUnit.getSubDetection(0).getUID() );
if (classification.getSpeciesID()==CTClassifier.NOSPECIES) {
System.out.println("No SPECIES: chi^2" + ctDataUnit.getCTChi2());
@ -178,12 +189,18 @@ public class CTClassifierManager {
ctDataUnit.clearClassifiers();
ctDataUnit.setClassificationIndex(-1);
System.out.println("Classify species: Num classifier " + this.cTClassifiers.size());
for (int i=0; i<clickTrainControl.getCurrentClassifiers().size(); i++) {
for (int i=0; i<this.cTClassifiers.size(); i++) {
System.out.println("Classifier: " + i);
//the first classifier
ctclassification = clickTrainControl.getCurrentClassifiers().get(i).classifyClickTrain(ctDataUnit);
ctclassification = this.cTClassifiers.get(i).classifyClickTrain(ctDataUnit);
System.out.println("Classifier complete: SPECIES: " + ctclassification.getSpeciesID());
// Debug.out.println(i + " ClassifierManager: Classify a click train data unit: " + ctDataUnit + " parent data block: " +
// " " + ctDataUnit.getnSubDetections());
@ -191,6 +208,7 @@ public class CTClassifierManager {
//set the species flag but only if this is the first time the ct data unit has been classified.
if (ctclassification.getSpeciesID()>CTClassifier.NOSPECIES && !hasBeenClssfd) {
System.out.println("Set classiifcation index: " + i);
ctDataUnit.setClassificationIndex(i); //set the classification index.
hasBeenClssfd = true;
}
@ -216,6 +234,10 @@ public class CTClassifierManager {
CTClassifier aClassifier;
for (int i=0; i<ctParams.length; i++) {
aClassifier = createClassifier(ctParams[i].type);
if (aClassifier==null) {
System.err.println("CTCLassifier manager: the classifier is null");
continue;
}
aClassifier.setParams(ctParams[i]);
cTClassifiers.add(aClassifier);
}

View File

@ -32,7 +32,7 @@ public class CTClassifierParams implements Cloneable, Serializable, ManagedParam
public int speciesFlag = 1;
/**
* Easy way to know which classifier the parameter class belong to rather than big instancof statement
* Easy way to know which classifier the parameter class belong to rather than big instance of statement
*/
public CTClassifierType type;

View File

@ -9,9 +9,51 @@ import java.io.Serializable;
*/
public enum CTClassifierType implements Serializable {
CHI2THRESHOLD, //very simple chi^2 threshold classifier.
TEMPLATECLASSIFIER,//template based classifier
BEARINGCLASSIFIER, // bearing based classification
MATCHEDCLICK //external matched click classifier which uses average waveforms of click trains.
CHI2THRESHOLD("X\\u00b2 threshold classifier", false), //very simple chi^2 threshold classifier.
TEMPLATECLASSIFIER("Template correlation", false),//template based classifier
BEARINGCLASSIFIER("Bearing classifier",false), // bearing based classification
IDICLASSIFIER("IDI classifier", false), //IDI.
STANDARDCLASSIFIER("Standard Classifier", true), //COMBINES THE IDI, BEARINGS, TEMPLATE AND CHI2THRESHOLD into one classifier
MATCHEDCLICK("Matched click classifier", false); //external matched click classifier which uses average waveforms of click trains.
/**
* True to enable the classifier.
*/
private boolean enable;
/**
* The name to use for the classifier.
*/
private String name;
/**
* The name of the classifier type.
* @return the name.
*/
public String getName() {
return name;
}
/**
* Check whether the classifier should be used or not.
* @return true of the classifier is enabled.
*/
public boolean isEnable() {
return enable;
}
CTClassifierType(String name, boolean enable){
this.name = name;
this.enable = enable;
}
// @Override
// public String toString() {
// return name;
// }
}

View File

@ -11,7 +11,6 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import PamguardMVC.debug.Debug;
import clickTrainDetector.classification.simplechi2classifier.Chi2CTClassification;
public class SimpleClassifierJSONLogging extends ClassifierJSONLogging {
@ -27,7 +26,7 @@ public class SimpleClassifierJSONLogging extends ClassifierJSONLogging {
JsonFactory jf = new JsonFactory();
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
JsonGenerator jg = jf.createJsonGenerator(os, JsonEncoding.UTF8);
JsonGenerator jg = jf.createGenerator(os, JsonEncoding.UTF8);
jg.writeStartObject();
jg.writeStringField(TYPEFIELD, ctClassification.getClassifierType().toString());
jg.writeNumberField(SPECIESFIELD, ctClassification.getSpeciesID());

View File

@ -0,0 +1,118 @@
package clickTrainDetector.classification.idiClassifier;
import clickTrainDetector.ClickTrainControl;
import clickTrainDetector.classification.CTClassification;
import clickTrainDetector.classification.CTClassifierType;
import clickTrainDetector.classification.ClassifierJSONLogging;
/**
* Classification result from the bearing classifier.
*
* @author Jamie Macaulay
*
*/
public class IDIClassification implements CTClassification {
/**
* Mean bearing derivative.
*/
private double medianIDI;
/**
* Median bearing derivative.
*/
private double meanIDI;
/**
* Standard bearing derivative.
*/
private double stdIDI;
/**
* The current species ID.
*/
private int speciesID;
/**
* Bearing classifier JSON logging.
*/
private IDIClassifierJSON idiClassifierJSONLogging;
/**
* Constructor for the bearing classifier.
* @param speciesID - the speciesID flag.
* @param medianIDI - the median IDI in seconds.
* @param meanIDI - the mean IDI in seconds.
* @param stdIDI - the standard deviation in IDI.
*/
public IDIClassification(int speciesID, double medianIDI, double meanIDI, double stdIDI) {
this.medianIDI = medianIDI;
this.meanIDI = meanIDI;
this.stdIDI = stdIDI;
this.speciesID = speciesID;
idiClassifierJSONLogging = new IDIClassifierJSON();
}
/**
* Bearing classification from a JSON string.
* @param jsonstring - JSON string containing the bearing data.
*/
public IDIClassification(String jsonstring) {
idiClassifierJSONLogging = new IDIClassifierJSON();
CTClassification classification = idiClassifierJSONLogging.createClassification(jsonstring);
this.speciesID =classification.getSpeciesID();
this.medianIDI =((IDIClassification) classification).getMeanIDI();
this.meanIDI =((IDIClassification) classification).getMedianIDI();
this.stdIDI =((IDIClassification) classification).getStdIDI();
}
@Override
public CTClassifierType getClassifierType() {
return CTClassifierType.IDICLASSIFIER;
}
@Override
public int getSpeciesID() {
return speciesID;
}
@Override
public String getSummaryString() {
return String.format("IDI Classifier: Mean IDI %.5fs Median IDI %.5fs Std IDI%.5fs",
meanIDI, medianIDI, stdIDI);
}
@Override
public ClassifierJSONLogging getJSONLogging() {
return idiClassifierJSONLogging;
}
/**
* Get the median of bearing delta values in radians per second.
* @return the median bearing delta in rad/s.
*/
public double getMedianIDI() {
return this.medianIDI;
}
/**
* Get the mean of bearing delta values in radians per second.
* @return the mean bearing delta in rad/s
*
*/
public double getMeanIDI() {
return this.meanIDI;
}
/**
* Get the standard deviation in bearing delta values in radians per second.
* @return the standard deviation in bearing delta in rad/s
*/
public double getStdIDI() {
return this.stdIDI;
}
}

View File

@ -0,0 +1,103 @@
package clickTrainDetector.classification.idiClassifier;
import clickTrainDetector.CTDataUnit;
import clickTrainDetector.ClickTrainControl;
import clickTrainDetector.IDIInfo;
import clickTrainDetector.classification.CTClassification;
import clickTrainDetector.classification.CTClassifier;
import clickTrainDetector.classification.CTClassifierParams;
import clickTrainDetector.layout.classification.CTClassifierGraphics;
import clickTrainDetector.layout.classification.idiClassifier.IDIClassifierGraphics;
/**
* An inter-detection interval based classifier for click trains.
*
* @author Jamie Macaulay
*/
public class IDIClassifier implements CTClassifier {
/**
* The IDI parameters.
*/
private IDIClassifierParams idiParams;
/**
* The bearing classifier graphics
*/
private IDIClassifierGraphics idiClassifierGraphics;
public IDIClassifier(ClickTrainControl clickTrainControl, int defaultSpeciesID) {
idiParams = new IDIClassifierParams();
idiParams.speciesFlag=defaultSpeciesID;
this.idiClassifierGraphics = new IDIClassifierGraphics(clickTrainControl, this);
}
@Override
public CTClassification classifyClickTrain(CTDataUnit clickTrain) {
//check IDI classification is passed
boolean passesIDI = checkIDIMeasurements(clickTrain);
int speciesID = CTClassifier.NOSPECIES;
if (passesIDI) {
speciesID = idiParams.speciesFlag;
}
return new IDIClassification( speciesID, clickTrain.getIDIInfo().medianIDI, clickTrain.getIDIInfo().meanIDI, clickTrain.getIDIInfo().stdIDI);
}
@Override
public String getName() {
return "IDI classifier";
}
@Override
public int getSpeciesID() {
return idiParams.speciesFlag;
}
@Override
public CTClassifierGraphics getCTClassifierGraphics() {
return idiClassifierGraphics;
}
@Override
public void setParams(CTClassifierParams ctClassifierParams) {
this.idiParams=(IDIClassifierParams) ctClassifierParams;
}
/**
* Check IDI measurements for a click train
* @return true if all measurements are passed.
*/
private boolean checkIDIMeasurements(CTDataUnit clickTrain) {
IDIInfo idiInfo = clickTrain.getIDIInfo();
if (idiParams.useMedianIDI &&
(idiInfo.medianIDI<idiParams.minMedianIDI || idiInfo.medianIDI>idiParams.maxMedianIDI)) {
return false;
}
if (idiParams.useMeanIDI &&
(idiInfo.meanIDI<idiParams.minMeanIDI || idiInfo.meanIDI>idiParams.maxMeanIDI)) {
return false;
}
if (idiParams.useStdIDI &&
(idiInfo.stdIDI<idiParams.minStdIDI || idiInfo.stdIDI>idiParams.maxStdIDI)) {
return false;
}
return true;
}
public IDIClassifierParams getParams() {
return idiParams;
}
}

View File

@ -0,0 +1,89 @@
package clickTrainDetector.classification.idiClassifier;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import clickTrainDetector.classification.CTClassification;
import clickTrainDetector.classification.SimpleClassifierJSONLogging;
public class IDIClassifierJSON extends SimpleClassifierJSONLogging {
public static final String MEDIAN_IDI_STRING = "MED_IDI_DELTA";
public static final String MEAN_IDI_STRING = "MEAN_IDI_DELTA";
public static final String STD_IDI_STRING = "STD_IDI_DELTA";
/**
* Write JSON data after default TYPE and SPECIESID values have been added.
* @param the JSON data generator.
* @throws IOException
* @throws JsonGenerationException
*/
public void writeJSONData(JsonGenerator jg, CTClassification ctClassification) {
try {
jg.writeNumberField(MEDIAN_IDI_STRING, ((IDIClassification) ctClassification).getMedianIDI());
jg.writeNumberField(MEAN_IDI_STRING, ((IDIClassification) ctClassification).getMeanIDI());
jg.writeNumberField(STD_IDI_STRING, ((IDIClassification) ctClassification).getStdIDI());
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Get classifier data after default TYPE and SPECIESID values has been
* @param the JSON node which contains data.
*/
public CTClassification createClassification(JsonNode jTree) {
JsonNode na = jTree.findValue("SPECIES");
int speciesID;
double medianIDIDelta;
double meanIDIDelta;
double stdIDIDelta;
if (na != null ) {
speciesID = na.asInt();
}
else {
System.err.println("Cannot load IDI classifier");
return null;
}
na = jTree.findValue(MEDIAN_IDI_STRING);
if (na != null ) {
medianIDIDelta = na.asDouble();
}
else {
System.err.println("Cannot load IDI classifier: no median IDI");
return null;
}
na = jTree.findValue(MEAN_IDI_STRING);
if (na != null ) {
meanIDIDelta = na.asDouble();
}
else {
System.err.println("Cannot load IDI classifier: no mean IDI");
return null;
}
na = jTree.findValue(STD_IDI_STRING);
if (na != null ) {
stdIDIDelta = na.asDouble();
}
else {
System.err.println("Cannot load IDI classifier: no std IDI");
return null;
}
IDIClassification idiClassification =
new IDIClassification(speciesID, meanIDIDelta, medianIDIDelta, stdIDIDelta);
return idiClassification;
}
}

View File

@ -0,0 +1,62 @@
package clickTrainDetector.classification.idiClassifier;
import java.io.Serializable;
import PamModel.parametermanager.ManagedParameters;
import clickTrainDetector.classification.CTClassifierParams;
public class IDIClassifierParams extends CTClassifierParams implements Serializable, Cloneable, ManagedParameters {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Use the median ICI measurements
*/
public boolean useMedianIDI = true;
/**
* The minimim median ICI.
*/
public Double minMedianIDI = 0.; // seconds
/**
* The maximum median ICI
*/
public Double maxMedianIDI = 2.; // seconds
/**
* Use the mean ICI measurements.
*/
public boolean useMeanIDI = false;
/**
* The minimum median ICI.
*/
public Double minMeanIDI = 0.; // seconds
/**
* The maximum median ICI
*/
public Double maxMeanIDI = 2.; // seconds
/**
* Use the mean ICI measurements
*/
public boolean useStdIDI = false;
/**
* The minimum standard deviation in ICI.
*/
public Double minStdIDI = 0.; // seconds
/**
* The maximum standard deviation in ICI
*/
public Double maxStdIDI = 100.; // seconds
}

View File

@ -50,7 +50,7 @@ public class Chi2CTClassification implements CTClassification {
@Override
public String getSummaryString() {
return "Simple X\u00b2 Clssfr: Species ID: " + this.getSpeciesID();
return "X\u00b2 Clssfr: Species ID: " + this.getSpeciesID();
}
@Override

View File

@ -0,0 +1,83 @@
package clickTrainDetector.classification.standardClassifier;
import clickTrainDetector.classification.CTClassification;
import clickTrainDetector.classification.CTClassifierType;
import clickTrainDetector.classification.ClassifierJSONLogging;
import clickTrainDetector.classification.bearingClassifier.BearingClassifierJSON;
/**
* A classification object for a standard classification
*
* @author Jamie Macaulay
*
*/
public class StandardClassification implements CTClassification {
/**
* The current species ID.
*/
private int speciesID;
/**
* All the classifications.
*/
private CTClassification[] ctClassifications;
public CTClassification[] getCtClassifications() {
return ctClassifications;
}
/**
* Standard classifier JSON logging.
*/
private StandardClassificationJSON standardClassifierJSONLogging;
public StandardClassification(CTClassification[] ctClassifications, int speciesID) {
this.ctClassifications=ctClassifications;
standardClassifierJSONLogging = new StandardClassificationJSON(ctClassifications);
this.speciesID=speciesID;
}
/**
* Create a classification unit from a JSON string.
* @param jsonstring
*/
public StandardClassification(String jsonstring) {
standardClassifierJSONLogging = new StandardClassificationJSON();
CTClassification classification = standardClassifierJSONLogging.createClassification(jsonstring);
this.ctClassifications = ((StandardClassification) classification).getCtClassifications();
this.speciesID =classification.getSpeciesID();
}
@Override
public CTClassifierType getClassifierType() {
return CTClassifierType.STANDARDCLASSIFIER;
}
@Override
public int getSpeciesID() {
return speciesID;
}
@Override
public String getSummaryString() {
String summaryString ="";
for (CTClassification ctClassification : ctClassifications) {
summaryString+= ctClassification.getSummaryString() + "\n";
}
return summaryString;
}
@Override
public ClassifierJSONLogging getJSONLogging() {
return standardClassifierJSONLogging;
}
}

View File

@ -0,0 +1,113 @@
package clickTrainDetector.classification.standardClassifier;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import clickTrainDetector.classification.CTClassification;
import clickTrainDetector.classification.SimpleClassifierJSONLogging;
import clickTrainDetector.classification.bearingClassifier.BearingClassification;
import clickTrainDetector.classification.bearingClassifier.BearingClassifier;
import clickTrainDetector.classification.idiClassifier.IDIClassification;
import clickTrainDetector.classification.idiClassifier.IDIClassifier;
import clickTrainDetector.classification.simplechi2classifier.Chi2CTClassification;
import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdClassifier;
import clickTrainDetector.classification.templateClassifier.CTTemplateClassifier;
import clickTrainDetector.classification.templateClassifier.TemplateClassification;
/**
* Standard classification JSON logging
*
* @author Jamie Macaulay
*
*/
public class StandardClassificationJSON extends SimpleClassifierJSONLogging {
private CTClassification[] standardClassifier;
/**
* Create the JSON logging for standard classifier.
* @param ctClassifications - the standard classifier.
*/
public StandardClassificationJSON(CTClassification[] ctClassifications) {
this.standardClassifier=ctClassifications;
}
public StandardClassificationJSON() {
}
@Override
public void writeJSONData(JsonGenerator jg, CTClassification ctClassification) {
for (int i=0; i<standardClassifier.length; i++) {
((SimpleClassifierJSONLogging) standardClassifier[i].getJSONLogging()).writeJSONData(jg, standardClassifier[i]);
}
}
@Override
public CTClassification createClassification(String jsonString) {
try {
ObjectMapper om = new ObjectMapper();
JsonNode jTree = om.readTree(new ByteArrayInputStream(jsonString.getBytes()));
// JsonNode nv = jTree.findValue("NAME");
JsonNode na = jTree.findValue("SPECIES");
int speciesID;
if (na != null ) {
speciesID = na.asInt();
}
else {
System.err.println("Cannot load standard classifier");
return null;
}
//System.out.println("Hello load classification: " + standardClassifier.length);
CTClassification[] ctClassification = new CTClassification[StandardClassifier.CLASSIFIER_TYPES.length];
for (int i=0; i<ctClassification.length; i++) {
CTClassification classification = null;
switch (StandardClassifier.CLASSIFIER_TYPES[i]) {
case CHI2THRESHOLD:
classification = new Chi2CTClassification(jsonString);
break;
case IDICLASSIFIER:
classification = new IDIClassification(jsonString);
break;
case TEMPLATECLASSIFIER:
classification = new TemplateClassification(jsonString);
break;
case BEARINGCLASSIFIER:
classification = new BearingClassification(jsonString);
break;
default:
System.err.println("StandardClassification: classifier JSON not found");
break;
}
ctClassification[i] = classification;
}
StandardClassification stClassification =
new StandardClassification(ctClassification, speciesID);
return stClassification;
} catch (IOException e) {
System.err.println("Classification interpreting " + jsonString);
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,192 @@
package clickTrainDetector.classification.standardClassifier;
import java.util.ArrayList;
import clickTrainDetector.CTDataUnit;
import clickTrainDetector.ClickTrainControl;
import clickTrainDetector.classification.CTClassification;
import clickTrainDetector.classification.CTClassifier;
import clickTrainDetector.classification.CTClassifierParams;
import clickTrainDetector.classification.CTClassifierType;
import clickTrainDetector.classification.bearingClassifier.BearingClassifier;
import clickTrainDetector.classification.bearingClassifier.BearingClassifierParams;
import clickTrainDetector.classification.idiClassifier.IDIClassification;
import clickTrainDetector.classification.idiClassifier.IDIClassifier;
import clickTrainDetector.classification.simplechi2classifier.Chi2CTClassification;
import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdClassifier;
import clickTrainDetector.classification.templateClassifier.CTTemplateClassifier;
import clickTrainDetector.layout.classification.CTClassifierGraphics;
import clickTrainDetector.layout.classification.standardClassifier.StandardClassifierGraphics;
/**
* Combines the IDI, CHI2, BEARING and TEMPLATE classifier into one.
* @author Jamie Macaulay
*
*/
public class StandardClassifier implements CTClassifier {
/**
* It's quite complicated to keep track of the species ID for all sub classifier but just set them
* to 1 to check whether the classifier has passed or not,
*/
public int SUB_CLASSIFIER_SPECIESID = 1;
/**
* The bearing classifier parameters.
*/
private StandardClassifierParams standardClssfrParams = new StandardClassifierParams();
/**
* The graphics for changing settings.
*/
private StandardClassifierGraphics standardClassifierGraphics;
/**
* List of the classifiers used.
*/
private ArrayList<CTClassifier> classifiers;
/**
* Click train control.
*/
private ClickTrainControl clickTrainControl;
/**
* The classifier types used in the standard classifier.
* ****New types MUST BE ADDED HERE****
*/
public static CTClassifierType[] CLASSIFIER_TYPES = {CTClassifierType.CHI2THRESHOLD, CTClassifierType.IDICLASSIFIER, CTClassifierType.TEMPLATECLASSIFIER, CTClassifierType.BEARINGCLASSIFIER};
public StandardClassifier(ClickTrainControl clickTrainControl, int speciesID) {
this.clickTrainControl = clickTrainControl;
standardClassifierGraphics = new StandardClassifierGraphics(clickTrainControl, this);
standardClssfrParams.speciesFlag=speciesID;
//load the settings
createClassifiers();
}
/**
* Create the classifier. Each sub classifier (if enabled) must be passed for a positive classification.
*/
private void createClassifiers() {
classifiers = new ArrayList<CTClassifier>();
//do this so that is CLASSIFIER_TYPES changes order things still work.
for (int i=0; i<CLASSIFIER_TYPES.length; i++) {
switch (CLASSIFIER_TYPES[i]) {
default:
break;
case CHI2THRESHOLD:
classifiers.add(new Chi2ThresholdClassifier(clickTrainControl, SUB_CLASSIFIER_SPECIESID));
break;
case IDICLASSIFIER:
classifiers.add(new IDIClassifier(clickTrainControl,SUB_CLASSIFIER_SPECIESID));
break;
case TEMPLATECLASSIFIER:
classifiers.add(new CTTemplateClassifier(clickTrainControl, SUB_CLASSIFIER_SPECIESID));
break;
case BEARINGCLASSIFIER:
classifiers.add(new BearingClassifier(clickTrainControl, SUB_CLASSIFIER_SPECIESID));
break;
}
}
setClassifierParams();
}
/**
* Set the parameters for the individual classifiers
*/
private void setClassifierParams() {
if (standardClssfrParams.ctClassifierParams ==null || standardClssfrParams.ctClassifierParams.length != classifiers.size()) {
standardClssfrParams.ctClassifierParams = new CTClassifierParams[classifiers.size()];
standardClssfrParams.enable = new boolean[classifiers.size()];
//enable all classifiers by default.
for (int i=0; i<classifiers.size(); i++) {
standardClssfrParams.enable[i] = true; //default is false
}
}
for (int i=0; i<classifiers.size(); i++) {
if (standardClssfrParams.ctClassifierParams[i]==null) {
//set default settings
standardClssfrParams.ctClassifierParams[i]= classifiers.get(i).getParams();
}
else {
//the standard classifier should have settings set.
classifiers.get(i).setParams(standardClssfrParams.ctClassifierParams[i]);
}
}
}
@Override
public CTClassification classifyClickTrain(CTDataUnit clickTrain) {
int speciesID = standardClssfrParams.speciesFlag;
System.out.println("Standard Classificiation: " );
//all classifiers have to pass.
CTClassification[] ctClassification = new CTClassification[classifiers.size()];
for (int i=0; i<classifiers.size(); i++) {
ctClassification[i] = classifiers.get(i).classifyClickTrain(clickTrain);
System.out.println("Standard Classificiation: " + i + " speciesID: " + ctClassification[i].getSpeciesID()
+ " sub species: "+ classifiers.get(i).getParams().speciesFlag + " standard species: " +speciesID + " use? : " + standardClssfrParams.enable[i]);
if (standardClssfrParams.enable[i]) {
if (ctClassification[i].getSpeciesID() != SUB_CLASSIFIER_SPECIESID){
speciesID = CTClassifier.NOSPECIES;
}
}
}
System.out.println("SPECIES ID: " + speciesID);
//create the classification.
StandardClassification classification = new StandardClassification(ctClassification, speciesID);
return classification;
}
@Override
public String getName() {
return "Standard Classifier";
}
@Override
public int getSpeciesID() {
return standardClssfrParams.speciesFlag;
}
@Override
public CTClassifierGraphics getCTClassifierGraphics() {
return standardClassifierGraphics;
}
@Override
public void setParams(CTClassifierParams ctClassifierParams) {
this.standardClssfrParams=(StandardClassifierParams) ctClassifierParams;
setClassifierParams();
}
public StandardClassifierParams getParams() {
return standardClssfrParams;
}
/**
* Get the classifiers the standard classifier uses. .
* @return the classifiers.
*/
public ArrayList<CTClassifier> getClassifiers() {
return classifiers;
}
}

View File

@ -0,0 +1,80 @@
package clickTrainDetector.classification.standardClassifier;
import clickTrainDetector.classification.CTClassifierParams;
import clickTrainDetector.classification.CTClassifierType;
/**
* Standard classifier parameters.
*/
public class StandardClassifierParams extends CTClassifierParams {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* List of the classifier parameters used.
*/
public CTClassifierParams[] ctClassifierParams;
/**
* List of which classifiers are enabled.
*/
public boolean[] enable;
public StandardClassifierParams(){
///very important to set this or else the clasifier manager does not
//know which classifier to create.
type = CTClassifierType.STANDARDCLASSIFIER;
}
//
// /**
// * Standard classifier parameters.
// */
// public Chi2ThresholdParams standClassifierParams = new Chi2ThresholdParams();
//
//
// /**
// * The parameters for bearing classification.
// */
// public BearingClassifierParams bearingClassifierParams = new BearingClassifierParams();
//
//
// /**
// * The IDI parameters.
// */
// public IDIClassifierParams idClassifierParams = new IDIClassifierParams();
//
//
// /**
// * Template classifier parameters.
// */
// public TemplateClassifierParams templateClassifierParams = new TemplateClassifierParams();
public CTClassifierParams clone() {
StandardClassifierParams clonedParams =(StandardClassifierParams) super.clone();
//make sure to hard clone the settings.
for (int i=0; i<clonedParams.ctClassifierParams.length; i++) {
clonedParams.ctClassifierParams[i] = clonedParams.ctClassifierParams[i].clone();
}
return clonedParams;
}
}

View File

@ -47,7 +47,7 @@ public class CTTemplateClassifier implements CTClassifier {
/**
* THe Chi2 threshold classifier.
*/
public Chi2ThresholdClassifier chi2ThresholdClassifier;
//public Chi2ThresholdClassifier chi2ThresholdClassifier;
/**
* The spectrum template which has been interpolated for current sample rate and
@ -57,15 +57,15 @@ public class CTTemplateClassifier implements CTClassifier {
public CTTemplateClassifier(ClickTrainControl clickTrainControl, int defaultSpeciesID) {
this.clickTrainControl = clickTrainControl;
chi2ThresholdClassifier=new Chi2ThresholdClassifier(clickTrainControl, defaultSpeciesID);
//chi2ThresholdClassifier=new Chi2ThresholdClassifier(clickTrainControl, defaultSpeciesID);
templateClassifierParams.speciesFlag=defaultSpeciesID;
templateClassifierParams.chi2ThresholdParams.speciesFlag=defaultSpeciesID; //must make this the same
//templateClassifierParams.chi2ThresholdParams.speciesFlag=defaultSpeciesID; //must make this the same
}
public CTTemplateClassifier(int defaultSpeciesID) {
chi2ThresholdClassifier=new Chi2ThresholdClassifier(defaultSpeciesID);
//chi2ThresholdClassifier=new Chi2ThresholdClassifier(defaultSpeciesID);
templateClassifierParams.speciesFlag=defaultSpeciesID;
templateClassifierParams.chi2ThresholdParams.speciesFlag=defaultSpeciesID; //must make this the same
//templateClassifierParams.chi2ThresholdParams.speciesFlag=defaultSpeciesID; //must make this the same
}
@Override
@ -74,9 +74,9 @@ public class CTTemplateClassifier implements CTClassifier {
//Debug.out.println("Click train: " + clickTrain + " sub count: " + clickTrain.getSubDetectionsCount() + " UID: " + clickTrain.getUID());
//little HACK here to ensure species falgs are the same.
templateClassifierParams.chi2ThresholdParams.speciesFlag=templateClassifierParams.speciesFlag;
//templateClassifierParams.chi2ThresholdParams.speciesFlag=templateClassifierParams.speciesFlag;
chi2ThresholdClassifier.setParams(templateClassifierParams.chi2ThresholdParams);
//chi2ThresholdClassifier.setParams(templateClassifierParams.chi2ThresholdParams);
if (clickTrain.getAverageSpectra()==null) {
System.err.println("TemplateClassifier:There is no average waveform for template classification: " + clickTrain.averageWaveform);
@ -89,25 +89,17 @@ public class CTTemplateClassifier implements CTClassifier {
//need to work out correlation threshold whatever
//first check the chi2 value.
Chi2CTClassification chi2Classification = chi2ThresholdClassifier.classifyClickTrain(clickTrain);
//Chi2CTClassification chi2Classification = chi2ThresholdClassifier.classifyClickTrain(clickTrain);
//check chi^2 classification is passed
if (chi2Classification.getSpeciesID()!=templateClassifierParams.speciesFlag) {
//no classification
// Debug.out.println(templateClassifierParams.classifierName + " Classifier: Failed chi2 classifier: " + chi2Classification.getSpeciesID() +
// " " +chi2ThresholdClassifier.getParams().speciesFlag + " " + templateClassifierParams.speciesFlag + " min chi2: " + templateClassifierParams.chi2ThresholdParams.chi2Threshold);
return new TemplateClassification(CTClassifier.PRECLASSIFIERFLAG, corrValue);
}
// //check chi^2 classification is passed
// if (chi2Classification.getSpeciesID()!=templateClassifierParams.speciesFlag) {
// //no classification
//// Debug.out.println(templateClassifierParams.classifierName + " Classifier: Failed chi2 classifier: " + chi2Classification.getSpeciesID() +
//// " " +chi2ThresholdClassifier.getParams().speciesFlag + " " + templateClassifierParams.speciesFlag + " min chi2: " + templateClassifierParams.chi2ThresholdParams.chi2Threshold);
// return new TemplateClassification(CTClassifier.PRECLASSIFIERFLAG, corrValue);
// }
//check IDI classification is passed
boolean passesIDI = checkIDIMeasurements(clickTrain);
//check chi^2 classification is passed
if (!passesIDI) {
//no classification
// Debug.out.println(templateClassifierParams.classifierName +" Classifier: Failed IDI classifier: ");
return new TemplateClassification(CTClassifier.PRECLASSIFIERFLAG, corrValue);
}
// check template correlation.
if (Double.isNaN(corrValue) || corrValue<templateClassifierParams.corrThreshold) {
@ -219,30 +211,6 @@ public class CTTemplateClassifier implements CTClassifier {
return corr;
}
/**
* Check IDI measurments for a click trian
* @return true if all measurments are passed.
*/
private boolean checkIDIMeasurements(CTDataUnit clickTrain) {
IDIInfo idiInfo = clickTrain.getIDIInfo();
if (templateClassifierParams.useMedianIDI &&
(idiInfo.medianIDI<templateClassifierParams.minMedianIDI || idiInfo.medianIDI>templateClassifierParams.maxMedianIDI)) {
return false;
}
if (templateClassifierParams.useMeanIDI &&
(idiInfo.meanIDI<templateClassifierParams.minMeanIDI || idiInfo.meanIDI>templateClassifierParams.maxMeanIDI)) {
return false;
}
if (templateClassifierParams.useStdIDI &&
(idiInfo.stdIDI<templateClassifierParams.minStdIDI || idiInfo.stdIDI>templateClassifierParams.maxStdIDI)) {
return false;
}
return true;
}
@Override
public String getName() {
@ -270,14 +238,14 @@ public class CTTemplateClassifier implements CTClassifier {
this.templateClassifierParams=(TemplateClassifierParams) ctClassifierParams;
}
/**
* Get the simple chi^2 classifier which forms one of the classification tests for
* the template classifier.
* @return the chi2 classifier.
*/
public Chi2ThresholdClassifier getSimpleCTClassifier() {
return this.chi2ThresholdClassifier;
}
// /**
// * Get the simple chi^2 classifier which forms one of the classification tests for
// * the template classifier.
// * @return the chi2 classifier.
// */
// public Chi2ThresholdClassifier getSimpleCTClassifier() {
// return this.chi2ThresholdClassifier;
// }
@Override
public int getSpeciesID() {

View File

@ -16,7 +16,7 @@ public class SpectrumTemplateDataUnit extends PamDataUnit {
public SpectrumTemplateDataUnit(MatchTemplate spectrumTemplate) {
super(0L);
this.spectrumTemplate=spectrumTemplate;
this.setSampleDuration(1000L); //needed for some plots sometimes...
}
}

View File

@ -4,8 +4,6 @@ import PamModel.parametermanager.ManagedParameters;
import PamModel.parametermanager.PamParameterSet;
import clickTrainDetector.classification.CTClassifierParams;
import clickTrainDetector.classification.CTClassifierType;
import clickTrainDetector.classification.bearingClassifier.BearingClassifierParams;
import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdParams;
import clickTrainDetector.classification.templateClassifier.DefualtSpectrumTemplates.SpectrumTemplateType;
import matchedTemplateClassifer.MatchTemplate;
@ -34,7 +32,7 @@ public class TemplateClassifierParams extends CTClassifierParams implements Mana
/**
* Basic chi2 threshold params.
*/
public Chi2ThresholdParams chi2ThresholdParams = new Chi2ThresholdParams();
//public Chi2ThresholdParams chi2ThresholdParams = new Chi2ThresholdParams();
/**
* Bearing parameters
@ -45,53 +43,6 @@ public class TemplateClassifierParams extends CTClassifierParams implements Mana
* Template correlation paramters.
*/
/**
* Use the median ICI measurements
*/
public boolean useMedianIDI = true;
/**
* The minimim median ICI.
*/
public Double minMedianIDI = 0.; // seconds
/**
* The maximum median ICI
*/
public Double maxMedianIDI = 2.; // seconds
/**
* Use the mean ICI measurements.
*/
public boolean useMeanIDI = false;
/**
* The minimum median ICI.
*/
public Double minMeanIDI = 0.; // seconds
/**
* The maximum median ICI
*/
public Double maxMeanIDI = 2.; // seconds
/**
* Use the mean ICI measurements
*/
public boolean useStdIDI = false;
/**
* The minimum standard deviation in ICI.
*/
public Double minStdIDI = 0.; // seconds
/**
* The maximum standard deviation in ICI
*/
public Double maxStdIDI = 100.; // seconds
/**
* Match FFT template.
@ -107,7 +58,7 @@ public class TemplateClassifierParams extends CTClassifierParams implements Mana
public TemplateClassifierParams clone() {
TemplateClassifierParams clonedParams =(TemplateClassifierParams) super.clone();
clonedParams.spectrumTemplate=spectrumTemplate.clone();
clonedParams.chi2ThresholdParams=chi2ThresholdParams.clone();
//clonedParams.chi2ThresholdParams=chi2ThresholdParams.clone();
return clonedParams;
}

View File

@ -1,10 +1,12 @@
package clickTrainDetector.clickTrainAlgorithms.mht;
import PamguardMVC.PamDataBlock;
import clickTrainDetector.clickTrainAlgorithms.mht.mhtvar.BearingChi2VarParams;
import clickTrainDetector.clickTrainAlgorithms.mht.mhtvar.BearingChi2VarParams.BearingJumpDrctn;
import clickTrainDetector.clickTrainAlgorithms.mht.mhtvar.CorrelationChi2Params;
import clickTrainDetector.clickTrainAlgorithms.mht.mhtvar.IDIChi2Params;
import clickTrainDetector.clickTrainAlgorithms.mht.mhtvar.SimpleChi2VarParams;
import cpod.CPODClickDataBlock;
/**
* Default parameters for different species for the MHT click train detector.
@ -74,10 +76,11 @@ public class DefaultMHTParams {
/**
* Get default MHT parameters for a particular default species.
* @param pamDataBlock
*
* @return the default MHT parameters for giver DefaultMHTSpecies.
*/
public static MHTParams getDefaultMHTParams(DefaultMHTSpecies defaultSpecies) {
public static MHTParams getDefaultMHTParams(DefaultMHTSpecies defaultSpecies, PamDataBlock<?> pamDataBlock ) {
MHTParams mhtParams = new MHTParams();
@ -146,28 +149,70 @@ public class DefaultMHTParams {
break;
case PORPOISE:
if (pamDataBlock!=null && pamDataBlock.getClass().isAssignableFrom(CPODClickDataBlock.class)) {
chi2Settings[0]= new IDIChi2Params("ICI", "s", 0.2, 0.0002, SimpleChi2VarParams.SCALE_FACTOR_ICI);
((IDIChi2Params) chi2Settings[0]).minIDI = 0.001;
chi2Settings[1]= new SimpleChi2VarParams("Amplitude", "dB", 100, 0.6, SimpleChi2VarParams.SCALE_FACTOR_AMPLITUDE);
chi2Settings[2]= new BearingChi2VarParams("Bearing Delta", "\u00b0", Math.toRadians(0.2), Math.toRadians(0.01),
SimpleChi2VarParams.SCALE_FACTOR_BEARING); //RADIANS
chi2Settings[3]= new CorrelationChi2Params("Correlation");
chi2Settings[0]= new IDIChi2Params("ICI", "s", 0.2, 0.0002, SimpleChi2VarParams.SCALE_FACTOR_ICI);
chi2Settings[1]= new SimpleChi2VarParams("Amplitude", "dB", 30, 1, SimpleChi2VarParams.SCALE_FACTOR_AMPLITUDE);
chi2Settings[2]= new BearingChi2VarParams("Bearing Delta", "\u00b0", Math.toRadians(0.2), Math.toRadians(0.01),
SimpleChi2VarParams.SCALE_FACTOR_BEARING); //RADIANS
chi2Settings[3]= new CorrelationChi2Params("Correlation");
simpleChi2Params.chi2Settings = chi2Settings;
simpleChi2Params.chi2Settings = chi2Settings;
simpleChi2Params.enable = new boolean[] {true, true, false, false, false, false};
simpleChi2Params.maxICI = 0.25;
simpleChi2Params.coastPenalty = 10;
simpleChi2Params.newTrackPenalty = 50;
simpleChi2Params.longTrackExponent = 0.01;
simpleChi2Params.lowICIExponent = 2;
simpleChi2Params.newTrackN = 3;
simpleChi2Params.enable = new boolean[] {true, true, true, false, false, false};
//the MHT kernel params
mhtKernelParams.maxCoast = 2;
mhtKernelParams.nHold = 100;
mhtKernelParams.nPruneBackStart = 15;
mhtKernelParams.nPruneback = 8;
mhtKernelParams.nPruneback = 8;
//set the two main parameter classes.
mhtParams.chi2Params = simpleChi2Params;
mhtParams.mhtKernal = mhtKernelParams;
}
else {
//the MHT kernel params
mhtKernelParams.maxCoast = 7;
mhtKernelParams.nHold = 40;
mhtKernelParams.nPruneBackStart = 15;
mhtKernelParams.nPruneback = 7;
//set the two main parameter classes.
mhtParams.chi2Params = simpleChi2Params;
mhtParams.mhtKernal = mhtKernelParams;
chi2Settings[0]= new IDIChi2Params("ICI", "s", 0.2, 0.0002, SimpleChi2VarParams.SCALE_FACTOR_ICI);
chi2Settings[1]= new SimpleChi2VarParams("Amplitude", "dB", 30, 1, SimpleChi2VarParams.SCALE_FACTOR_AMPLITUDE);
chi2Settings[2]= new BearingChi2VarParams("Bearing Delta", "\u00b0", Math.toRadians(0.2), Math.toRadians(0.01),
SimpleChi2VarParams.SCALE_FACTOR_BEARING); //RADIANS
chi2Settings[3]= new CorrelationChi2Params("Correlation");
simpleChi2Params.chi2Settings = chi2Settings;
simpleChi2Params.enable = new boolean[] {true, true, true, false, false, false};
//the MHT kernel params
mhtKernelParams.maxCoast = 7;
mhtKernelParams.nHold = 40;
mhtKernelParams.nPruneBackStart = 15;
mhtKernelParams.nPruneback = 7;
//set the two main parameter classes.
mhtParams.chi2Params = simpleChi2Params;
mhtParams.mhtKernal = mhtKernelParams;
}
break;

View File

@ -66,10 +66,9 @@ public class MHTClassifierMAT {
classification = cTClassifiers.get(i).classifyClickTrain(dataUnit);
dataUnit.addCtClassification(classification);
// System.out.println("Classification " + classification.getSpeciesID() + " speciesID " + cTClassifiers.get(i).getSpeciesID());
System.out.println("Classification " + classification.getSpeciesID() + " speciesID " + cTClassifiers.get(i).getSpeciesID());
if (classification.getSpeciesID()>CTClassifier.NOSPECIES && !hasBeenClssfd) {
// System.out.println("Set classiifcation index: " + i);
dataUnit.setClassificationIndex(i); //set the classification index.
hasBeenClssfd = true;
}

View File

@ -0,0 +1,196 @@
# Click Train Detector
## Overview
When a toothed whale, bat or other echolocator uses echolocation for hunting or sensing their surroundings they usually produce regular clicks/calls which vary slowly in inter-click/call-interval, amplitude, bearing etc. Individual click detections can be difficult to classify from other random transients because recieved waveforms and spectra are distorted by number of factors, such as narrow beam profiles, frequency dependent absorption, propogation effects and animal behaviour. The broadband clicks of many dolphins psecies are especially difficult to distinguish because they are very similar to many other sources of transient noise, such as cavitations from ship propellors. However, the echolocation clicks used by toothed whales (and bats) are not produced in isolation - animals tend to rapidly produce clicks with a slowly varying inter-click-interval (ICI); there are very few non-biological sources which produce regular repetitive sound and so this provides an additional contextual dimension for click classification. An automated algorithm which is based on identifying repeating patterns of sounds therefore has the potential to be significantly more accurate than an algorithm based on identifying individual calls.
The PAMGuard click train detector module is used to detect and then classify repeating patterns of clicks. It is designed to work with multiple types of acoustic data, from CPOD detections to single channel and multi-channel hydrophone recordings.
## How it works
PAMGuards click train detector utilises both a detection and classification stage to extract click trains from recordings.
The detection stage is currently based on a multi hypothesis tracking (MHT) algorithm. This algorithm considers all possible combinations of transient detections creating a large hypothesis matrix which holds potential click trains. As more clicks are added to the hypothesis matrix it grows exponentially and so, to prevent a computer running out memory, it is regularly “pruned” to keep only the most likely click trains over time. The assigned likelihood of a click train is based on number of properties which can be defined in by the user. For example, a user might select, ICI, Amplitude and Correlation as variables to score click trains; this would mean that combinations of clicks with slowly changing ICI, amplitude and waveforms would be favoured by the algorithm and stay in the hypothesis matrix. Other properties such as bearing, click length and peak frequency can also be selected. A graphical explanation of the click train detection algorithm is shown in Figure 1 and a more detailed explanation of the be found in Macaulay (2019).
<p align="center">
<img width="930" height="900" src = "resources/mht_diagram.png">
</p>
_Diagram demonstrating how the click train algorithm works. Black dots are a set of 14 detected clicks at times t1 to t14. The click train algorithm begins at click 1 and creates two possible clicks trains, one that includes the first click (filled circle) and the other in which the click is not part of the click train (non-filled circle). The algorithm then moves to the next click and adds it to the hypothesis matrix. As the number of clicks increases, the hypothesis matrix exponentially expands in size and must be pruned. After a minimum of Npmin clicks (in this case 4) each track hypothesis (possible click train) is assigned a χ<sup>2</sup> score. The track hypothesis with lowest score (defined by larger coloured circles) has its branch traced back Np (in this case 3) clicks. Any track hypothesis which do not include the click Np steps back are pruned (defined by the double lines). Clicks which share no click associations with the first track hypothesis are then pruned and the process repeats until all clicks are part of a track or a maximum number of tracks have been considered (in this example there are two tracks). The algorithm then moves to the next click, adds it to the hypothesis matrix, assigns χ<sup>2</sup> scores and traces the lowest χ<sup>2</sup> branch Np steps back, pruning the hypothesis matrix again; the process repeats until the last click. Note that there is always a track hypothesis with no associated clicks (i.e. the bottom-most branch where no clicks belong to a click train). If a track hypothesis is confirmed and thus removed from the hypothesis matrix, then this track can be used to start another click train_
The advantage of this MHT approach is that the click train detection module is quite general and can cope with a large variety of complex situations and multiple overlapping click trains. The disadvantage is that there are a large number of potential variables which can be set that affect the performance of the detector which can make it complex to initially set up.
The subsequent classification stage attempts to classify detected click trains to species. Classification is currently based on a series of relatively simple binary classification steps but there is scope for machine learning approaches in future versions. The binary classification is based on parameters such as number of detected clicks, the mean and standard deviation in ICI and bearing and the correlation of the average spectrum of the click train with a predefined spectral template.
A click train which has been both detected and classified is saved to PAMGuards database and can be reclassified in PAMGuards viewer mode.
## Configuring the click train detector
The primary settings to configure can be split into MHT Kernel and χ^2 settings these are all set in the primary click train detector dialog as shown in Figure 2.
<p align="center">
<img width="850" height="700" src = "resources/detection_pane.png">
</p>
_The settings pane of the click train detector._
### MHT Kernel Settings
The MHT Kernel is the part of the detection algorithm which creates and then prunes the large hypothesis matrix which keeps a copy of all possible click trains. MHT Kernel settings are therefore important because they influence speed (a larger number of possible click trains in memory is more processor intensive) and the quality of the detections (the larger the number of possibilities the more likely that “true” click trains are contained in the hypothesis matrix). The specific settings are;
***Prune-back***: The hypothesis matrix needs pruned so that it does not grow exponentially and cause memory issues. The matrix is pruned at Np (see Figure 1) previous detections i.e. if Np is 5 then then then the algorithm selects the most likely click train, moves back five detections back and discards other hypothesis that do not contain the combination of clicks in that branch. Thus, increasing the prune-back means that more hypothesis are kept at any one time but decreasing will lead to faster processing times as less combination are kept in memory.
***Prune-start***: The initial number of detections before the pruning process starts. This cannot be less than Prune-back and should generally should be set no more than 15 for 8GB of memory.
***Max no. coasts***: A click train is saved and removed the hypothesis mix once it has passed a number of tests. It must be over three clicks long, survive the pruning process and have missed the max no. coasts. A coast is when a click has been missed from a click train based on ICI. i.e. if the ICI is 2s and a click train goes for 6s without a detected click then there have been 3 coasts. Increasing the maximum number of coasts means that click trains are less fragmented but can come at the cost of keeping click trains in the hypothesis matrix for longer which have ended.
***Max no. trains***: This is a maximum allowed number of trains in the hypothesis mix. Note this refers to the number of trains which can survive pruning the actual number of potential click trains in the hypothesis mix will be much larger. Generally, just via pruning, the hypothesis matrix will keep itself below the max no. trains, however, in certain situations it can grow too large and requires a limit. The max no. trains therefore usually have little effect on results but should generally be set to less than 50 to ensure smooth processing
### χ<sup>2</sup> Settings
χ<sup>2</sup> is a measure of the likelihood that a click train is from a (usually) biological source. The higher the χ<sup>2</sup> value the lower the quality of a click train.
The χ<sup>2</sup> model used in the click train detector considers both the slowly varying properties of click trains, as well as bonus and penalty factors to discourage fragmentation and aliasing (selecting a multiple of the true ICI) of detected click trains.
The initial basis of the model is:
<p align="center">
<img width="550" height="100" src = "resources/mht_equation.png">
</p>
where _m_ is the number of selected descriptors, e.g. ICI, amplitude, bearing etc., and _y(i,k)_ is the measurement of descriptor _i_ for click _k_ in a click train with n associated clicks. _t(k+1)_ is the measured time of a click _k_. Each descriptor is divided by q<sub>i</sub> which is a user tuneable parameter that alters the importance each descriptor has on the total χ<sup>2</sup>. Ideally it should correspond to a prediction of the likely variance of the descriptor.
The descriptors can be enabled and the variance set in the χ<sup>2</sup> Settings pane. The toggle button next to each descriptor sets whether a descriptor is used to score a click train and the variance is then set using the slider or by inputting manually by clicking the settings cog. Increasing the variance means that the descriptor has less of an influence on the calculation of χ<sup>2</sup> and decreasing means that the descriptor has a larger influence on χ<sup>2</sup>. In some cases, clicks can be so close together that the variance is tiny and thus χ<sup>2</sup> becomes huge e.g. during buzzes. A minimum variance value (_qt<sub>i</sub>_) prevents the variance _(max(q<sub>i</sub> (t<sub>(k+1)</sub>-t<sub>k</sub> ),qt<sub>i</sub> )<sup>2</sup>)_ from falling below very low values.
Ideally the variance for each parameter would be calculated from a test dataset of manually annotated click trains e.g. by calculating the variance of ICI of all marked click trains.
<p align="center">
<img width="900" height="120" src = "resources/varience_pane.png">
</p>
_Each descriptor has a variance setting which can be changed by moving the slider or manually inputting data by clicking the settings button. Variance is multiplied by the ICI for each click detection because clicks closer together in time the descriptor values will change less. In some cases, clicks can be so close together that the variance is tiny and thus χ<sup>2</sup> in Eq. 1 becomes huge e.g. during buzzes. A Min. Error prevents the variance from falling below very low values._
The available descriptors parameters can be set in the click detector settings pane (Figure 3) and works as follows;
***IDI:*** the inter-detection-interval in milliseconds. The algorithm looks for slowly changes in the interval between detections.
***Amplitude:*** the amplitude in dB re 1/muPa pp. The algorithm looks for slowly changing amplitude values. Note that the algorithm is comparing the change in change in amplitude so that the click train algorithm is not biased against large but consistent changes in amplitude (e.g. due to a narrow beam profile sweeping across a hydrophone).
***Bearing:*** the bearing of multi-channel clicks in degrees. Slowly changing bearings will increase the likelihood that click trains are detected. Note that in a similar way to Amplitude, the change in change in bearing is considered so that the algorithm is not biased against large but consistent changes in bearings. The bearing parameter has some additional settings which apply a large penalty to clicks trains if there is a large (user-defined) jump in bearing.
***Correlation:*** the algorithm calculates the peak of the cross-correlation value of subsequent clicks and looks for slowly changing values in the cross-correlation value. This tells the click train algorithm to search for clicks with consistent/slowly changing spectra. The correlation descriptor also has some additional settings which allow the user to pre-filter waveforms before cross-correlation. This is especially useful in removing noise from higher frequency detections.
***Time Delays:*** the time delay between multi-channel clicks in milliseconds. The algorithm looks for slowly changing values in the time delays between multichannel clicks. This is useful for arrays with more than two hydrophones where an error in a single time delay measurement may cause a substantial error in bearing. Like amplitude and bearing, the time delay values are the change in change in time delays between subsequent clicks to ensure that click trains are not biased against faster changes in bearing.
***Click Length:*** the length of the saved waveform of a click in milliseconds. This is a crude measure of the length of a click; however, it can be useful in helping the algorithm distinguish between species with long multi-modal clicks such as sperm whales, and much shorter broadband clicks such as dolphins.
***Peak Frequency:*** the peak frequency in Hz. The peak frequency between subsequent clicks is used score click trains. This is useful for click trains with very stable peak frequencies such as echosounders, narrow band high frequency species and perhaps some beaked whale species.
### Advanced χ<sup>2</sup> Settings
The descriptors used in Eq. 1 on their own do not provide a good score for click train detections. This is because Eq.1 can achieve the same score by either skipping clicks e.g. every second click in a click train, or by splitting click trains into smaller fragments.
<p align="center">
<img width="500" height="350" src = "resources/advanced_pane.png">
</p>
_The advanced settings for calculating χ^2. These parameters are primarily used to prevent click train aliasing and fragmentation.
The advanced settings (see Figure 4) are a series of additional factors that prevent aliasing and fragmentation and work as flows._
***Low ICI Bonus:*** if the median ICI of the possible click train is above a specified maximum value, a large penalty is added which effectively makes it one of the least likely click trains in the hypothesis matrix. If the median ICI is below the maximum value then χ<sup>2</sup> = (χ<sup>2</sup> (I/(max<sub>k</sub>*I<sub>k</sub>))<sup>LI</sup> where I is the median ICI, max<sub>k</sub>*I<sub>k</sub> is the maximum ICI in the possible click train and LI is the low ICI Bonus constant term. This bonus term favours lower ICI values, preventing aliased click trains.
***Long track bonus:*** add a bonus factor for longer click trains to prevent fragmentation. This is the total length of the click train in seconds divided by the total hypothesis matrix time in seconds L which is then multiplied so that χ<sup>2</sup> = (χ<sup>2</sup>*L)<sup>LT</sup> where LT is the long track bonus.
***Coast penalty:*** add a penalty for “coasting” i.e. when an expected click, based on ICI, is not present in the click train. This penalty is multiplied by the number of coasts i.e. the likely number of missed clicks based on ICI
***New Track Penalty:*** if a track hypothesis is newly added in the hypothesis matrix, then add a minor penalty factor. This is added until the number of click trains exceeds No. New Track Clicks
## Classification
The classification process attempts to assign a species identification to each detected click trains. Currently there is only one implemented classifier, a simple binary classifier which tests user defined parameters (e.g. IDI, bearing, spectral correlation and classifies each click). Classification parameters are accessed via the classification tab in the settings dialog.
There is currently a basic spectral correlation/IDI/bearing classifier; more complex classifiers can be implemented in the future.
<p align="center">
<img width="510" height="800" src = "resources/classifier_pane.png">
</p>
_The classifier settings. Users can add multiple classifiers using the + button next to the classifier tabs. Each classifier allows the user to choose a number of different approaches to classification based on the goodness of fit, inter-click interval, average spectra and bearings of the click trains. Users can use just one or all of these options and set specific parameters for each._
Users can add multiple classifiers by selecting the + button next to the classification tabs. Each classifier allows the user to choose a number of different methods for click train classification based on the goodness of fit, inter-click interval, average spectra and/or bearings of the click trains; for a click train to be classified it must pass all enabled methods (use toggle switches to enable and disable different types of classification). The different classification methods.
### χ<sup>2</sup> threshold classifier
The click train is classified if its overall χ<sup>2</sup> value is lower than the set χ<sup>2</sup> Threshold and it has more than Min. Clicks and the time between the first and last click is greater than Min. Time
### IDI Classifier
The click train is classified if the median/mean and standard deviation in the inter detection interval (IDI) between subsequent clicks are within user defined limits.
### Spectrum Template Classifier
The click train is classified if the average spectra of the click train has a correlation value above Spectrum Correlation Threshold with a user defined spectral template. The template can be set using the button on the top right of the spectrum plot a default spectrum can be loaded or a spectrum can be loaded from a .mat or .csv file. A csv file should have the first row as the spectrum and first column of the second row the sample rate. A .mat file should be a single saved structure with sR (sample rate) and spectrum (array of spectrum values) fields.
### Bearing Classifier
The click train is classified if minimum and maximum bearing (Bearing Limits) the average change in bearing ( Bearing Mean), the median change in bearing ( Bearing Median) and/or the average standard deviation in bearing change ( Bearing Std) are within user defined limits.
## Parametrising the classifier
Each classifier has a set of metadata that are added to click trains. This can be accessed through the tooltip or right click menus in various displays. For example, in the Time Base Display FX hover the mouse over a click train or bring the pop menu with a right click. Parameters such as the spectral correlation value, IDI and bearing information etc are displayed which allows users to get an idea of which values to set for the classifier. Currently this requires (like most PAMGuard classifiers) a trial and error approach. It is hoped that future update will allow manually validated data to be used to parametrise both the detection and classification stage of the click train detector.
<p align="center">
<img width="700" height="500" src = "resources/rightclickmenu.png">
</p>
_The metadata associated with each classifier is stored with every click train and be accessed through right clicking on or hovering the mouse over a click train detection._
## Localisation
The click train detector can be used to localise the position of animals detected by the click train detector using target motion analysis. This generally means that the localisation capabilities are generally restricted to data which has been collected using towed hydrophone arrays.
<p align="center">
<img width="242" height="430" src = "resources/localisation1.png">
</p>
_Screenshot of the click train localisation settings. Currently, only target motion is supported._
Localisation is enabled by ticking Localise click trains. The type of localisation algorithm which is used is selected in the Localisation algorithms (See the localisation section in PAMGuard help for more info on localisation algorithms). Localisation using 3D simplex and MCMC can be processor intensive, especially when there are a large number of clicks in a train and so the Algorithm Limits pane can be used to set a maximum number of input clicks for a localisation. If the maximum is exceeded then clicks are sub sampled from the click train evenly in time.
Generally, target motion localisation only works well when there are a large number of clicks over a long time period. The Filters tab allows users to select which click trains are localised and also to remove spurious results from unsuccessful localisations. The Pre Localisation Filter allows users to select a minimum number of detections before localisations are attempted and a minimum bearing change in the click train (Min Angle range). Click trains with larger angle ranges will generally result in higher quality localisations.
<p align="center">
<img width="242" height="430" src = "resources/localisation2.png">
</p>
_The filter tab allows users to pre-filter which click train are localised._
The Results Filter allows for spurious localisation results to be deleted: any results from target motion localisation (which can have more than one possible localisation) which are further away than Maximum Range, shallower than Minimum Depth or deeper than Maximum Depth are discarded.
Running
The click train detector can be run in real time or post processing. In real time add the module and it will automatically detected click trains once PAMGuard started.
<p align="center">
<img width="200" height="300" src = "resources/offlineprocessing.png">
</p>
_The filter tab allows users to pre-filter which click train are localised._
In viewer mode, add the module and then go to Settings>Click Train Detector > Reanalyse click trains….This will bring up PAMGuards generic data reprocessing dialog with two settings, Click Train Detector or Click Train Classifier. The Click Train Detector option will run the detection and classification algorithm again. The Click Train Classifier will only run the classification algorithm on existing detected click trains (much faster). Note that users can select how much data to reprocess in the Data dropdown menu All Data means the entire dataset will be reprocessed, Loaded Data means just the current data loaded in the display (all scrollable data), Select Data allows the user to define two time limits between which all data is reprocessed.
## Visualising Results
The results from the click train detector can be visualised in a variety of displays in PAMGuard.
### Click bearing time display
By default, clicks trains will be shown in the Click Detector Modules in built bearing time display. Different click trains are represented as different colours. Note that you must right click on the display and select Colour by Click Trains
<p align="center">
<img width="940" height="500" src = "resources/clicktrain_BT.png">
</p>
_The results of the click train detector displayed on the bearing time display. Different colours correspond to different click trains._
### Time Display FX
The Time Display FX is a more modern display which allows any time-based data to be plotted together on a large variety of y-axis (e.g., frequency, bearing, amplitude etc.). Click trains will be plotted on the time-based display by adding Click detections to the display and then using the right
<p align="center">
<img width="940" height="500" src = "resources/clicktrain_TDFX.png">
</p>
_Click train data displayed in the time display FX. Users can right click on click trains to view average spectra and waterfall spectrograms (shown here in top right)._

View File

@ -22,6 +22,7 @@ import clickTrainDetector.layout.classification.CTClassifiersPane;
import cpod.CPODClick;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
@ -50,7 +51,7 @@ public class ClickTrainAlgorithmPaneFX extends SettingsPane<ClickTrainParams> {
//there is something a little weird about template spectrum classifier which in Swing means
//it needs to be 850 in height or the template covers some of the controls and they
//cannot be clicked on??
private static final double prefHeight = 800;
private static final double prefHeight = 750;
private static final double prefWidth = 450;
@ -194,12 +195,16 @@ public class ClickTrainAlgorithmPaneFX extends SettingsPane<ClickTrainParams> {
Label label = new Label("Click Train Detector Algorithm");
// label.setFont(PamGuiManagerFX.titleFontSize2);
PamGuiManagerFX.titleFont2style(label);
dataSelectorHolder = new PamBorderPane();
PamVBox ctDetectorHolder = new PamVBox();
ctDetectorHolder.setSpacing(5);
ctDetectorHolder.getChildren().addAll(label, clickTrainAlgorithmBox, ctSettingsHolder);
ctDetectorHolder.getChildren().addAll(sourcePane, groupHolder, dataSelectorHolder, label, clickTrainAlgorithmBox, ctSettingsHolder);
ctDetectorHolder.setPadding(new Insets(5,5,5,5));
//the data selector pane.
dataSelectorPane = createDataSelectorPane();
@ -210,6 +215,9 @@ public class ClickTrainAlgorithmPaneFX extends SettingsPane<ClickTrainParams> {
ctClassifierHolder= new CTClassifiersPane(clickTrainControl);
ctClassifierHolder.setPadding(new Insets(5,0,0,0));
//the tab pane to hold classifier and the detector pane.
tabPane = new TabPane();
Tab tab1 = new Tab("Detector", ctDetectorHolder);
@ -221,10 +229,11 @@ public class ClickTrainAlgorithmPaneFX extends SettingsPane<ClickTrainParams> {
tabPane.getTabs().addAll(tab1, tab3, tab2);
tabPane.setPrefHeight(prefHeight);
dataSelectorHolder = new PamBorderPane();
tabPane.setSide(Side.TOP);
holder.getChildren().addAll(sourcePane, groupHolder, dataSelectorHolder, tabPane);
holder.setPadding(new Insets(5,5,5,5));
holder.getChildren().addAll(tabPane);
//holder.setPadding(new Insets(5,5,5,5));
return holder;
}

View File

@ -3,6 +3,7 @@ package clickTrainDetector.layout;
import clickTrainDetector.ClickTrainControl;
import clickTrainDetector.ClickTrainParams;
import clickTrainDetector.layout.classification.simplechi2classifier.SimpleCTClassifierPane;
import javafx.geometry.Insets;
import javafx.scene.layout.Pane;
import pamViewFX.fxNodes.PamBorderPane;
@ -27,7 +28,8 @@ public class PreClassifierPane extends PamBorderPane {
this.clickTrainControl = clickTrainControl;
this.setCenter(createClassifierPane());
this.setCenter(createClassifierPane());
this.setPadding(new Insets(5,5,5,5));
}

View File

@ -18,13 +18,13 @@ public interface CTClassifierGraphics {
public Pane getCTClassifierPane();
/**
* Called to save the params from the settings pane.
* Called to save the parameters from the settings pane.
*/
public CTClassifierParams getParams();
/**
* Set the parameters
* @param params - the paramters for a classifier
* Set the parameters.
* @param params - the parameters for a classifier.
*/
public void setParams(CTClassifierParams params);

View File

@ -5,6 +5,7 @@ import clickTrainDetector.ClickTrainControl;
import clickTrainDetector.classification.CTClassifier;
import clickTrainDetector.classification.CTClassifierParams;
import clickTrainDetector.classification.CTClassifierType;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
@ -28,7 +29,7 @@ public class CTClassifierPane extends PamBorderPane {
/**
* The classifier type combo box.
*/
private ComboBox<String> classifierListBox;
private ComboBox<CTClassifierType> classifierListBox;
/**
* Reference to the click train control.
@ -45,10 +46,19 @@ public class CTClassifierPane extends PamBorderPane {
*/
private CTClassifier currentClassifier;
/**
* A list of the type of classifiers.
*/
private CTClassifierType[] ctClassifierTypes;
/**
* The name field.
*/
private TextField nameField;
/**
* Selects the species ID for the classifier.
*/
private PamSpinner<Integer> speciesIDSpinner;
/**
@ -56,7 +66,6 @@ public class CTClassifierPane extends PamBorderPane {
*/
private int defaultSpeciesID = 1;
/**
* Constructor for the classifier pane.
* @param i - the default species ID
@ -102,12 +111,16 @@ public class CTClassifierPane extends PamBorderPane {
ctClassifierTypes = CTClassifierType.values();
classifierListBox = new ComboBox<String>();
int nClassifiers = 0;
classifierListBox = new ComboBox<CTClassifierType>();
for (int i=0; i<ctClassifierTypes.length; i++) {
classifierListBox.getItems().add(clickTrainControl.getClassifierManager().getClassifierName(ctClassifierTypes[i]));
if (ctClassifierTypes[i].isEnable()) {
classifierListBox.getItems().add(ctClassifierTypes[i]);
nClassifiers++;
}
}
classifierListBox.setOnAction((action)->{
setClassifierPane(classifierListBox.getSelectionModel().getSelectedIndex());
setClassifierPane(classifierListBox.getSelectionModel().getSelectedItem());
});
classifierListBox.getSelectionModel().select(0);
@ -116,9 +129,15 @@ public class CTClassifierPane extends PamBorderPane {
settingsHolder = new PamBorderPane();
//set the current classifier pane.
setClassifierPane(0);
setClassifierPane(classifierListBox.getItems().get(0));
//only add the classifiers type box if there is more than one type of classifier.
mainHolder.getChildren().addAll(label1, holder);
if (nClassifiers>1) mainHolder.getChildren().addAll(label2, classifierListBox);
mainHolder.getChildren().addAll(settingsHolder);
mainHolder.setPadding(new Insets(5,5,5,5)); //otherwise looks messy against edges of tab pane.
mainHolder.getChildren().addAll(label1, holder, label2, classifierListBox, settingsHolder);
return mainHolder;
}
@ -144,15 +163,15 @@ public class CTClassifierPane extends PamBorderPane {
*/
public void setParams(CTClassifierParams params) {
//probably a more elegent way to do this.
int index =-1;
for (int i=0; i<ctClassifierTypes.length; i++) {
if (ctClassifierTypes[i]==params.type) {
index=i;
break;
}
}
classifierListBox.getSelectionModel().select(index);
setClassifierPane(index);
// int index =-1;
// for (int i=0; i<ctClassifierTypes.length; i++) {
// if (ctClassifierTypes[i]==params.type) {
// index=i;
// break;
// }
// }
classifierListBox.getSelectionModel().select(params.type);
setClassifierPane(params.type);
//species Id
setBasicParams(params);
@ -174,10 +193,11 @@ public class CTClassifierPane extends PamBorderPane {
/**
* Set the classifier pane.
* @param clssfrIndex - set the index.
* @param ctClassifierType - set the index.
*/
private void setClassifierPane(int clssfrIndex) {
currentClassifier = clickTrainControl.getClassifierManager().createClassifier(clssfrIndex);
private void setClassifierPane(CTClassifierType ctClassifierType) {
if (ctClassifierType==null) return;
currentClassifier = clickTrainControl.getClassifierManager().createClassifier(ctClassifierType);
//Classifier pane- this also sets parameters for the classifier specific pane
if (currentClassifier.getCTClassifierGraphics()!=null) {
settingsHolder.setCenter(currentClassifier.getCTClassifierGraphics().getCTClassifierPane());

View File

@ -32,11 +32,13 @@ public class CTClassifiersPane extends PamBorderPane {
*/
private CheckBox enableClassifierBox;
/**
* Reference to the click train control.
*/
private ClickTrainControl clickTrainControl;
/**
* Tab pane where each tab is a different classifier.
*/
@ -55,22 +57,18 @@ public class CTClassifiersPane extends PamBorderPane {
*/
private Pane createClassifierPane() {
// enable the classifier.
enableClassifierBox = new CheckBox("Enable Click Train Classification");
enableClassifierBox.setOnAction(action ->{
enableClassifierPane(enableClassifierBox.isSelected());
});
// enable the classifier.
enableClassifierBox = new CheckBox("Enable Click Train Classification");
enableClassifierBox.setOnAction(action ->{
enableClassifierPane(enableClassifierBox.isSelected());
});
// enable the classifier.
enableClassifierBox = new CheckBox("Enable Click Train Classification");
enableClassifierBox.setOnAction(action ->{
enableClassifierPane(enableClassifierBox.isSelected());
});
//with just one classifier.
pamTabPane = new PamTabPane();
pamTabPane.setAddTabButton(true);
@ -78,10 +76,7 @@ public class CTClassifiersPane extends PamBorderPane {
pamTabPane.getAddTabButton().setGraphic(PamGlyphDude.createPamIcon("mdi2p-plus", PamGuiManagerFX.iconSize));
pamTabPane.setTabClosingPolicy(TabClosingPolicy.SELECTED_TAB);
pamTabPane.getAddTabButton().setTooltip(new Tooltip(
"Add a new template. The classifier will check all templates \n"
+ "and classify the click train if any template passes it's designated \n"
+ "value. All template match, reject and threshold values are \n"
+ "stored as annotations in binary files."));
"Add a new classifier."));
pamTabPane.getAddTabButton().setOnAction((action)->{
CTClassifierPane clssfrPane = new CTClassifierPane(clickTrainControl);
@ -113,9 +108,7 @@ public class CTClassifiersPane extends PamBorderPane {
enableClassifierPane(enableClassifierBox.isSelected());
enableClassifierPane(enableClassifierBox.isSelected());
return holder;
}
@ -153,7 +146,7 @@ public class CTClassifiersPane extends PamBorderPane {
super("Tab");
this.ctClassifierPane = tmpltClassifierPane;
this.setContent(tmpltClassifierPane);
tmpltClassifierPane.getNameField().setText("Template " + (listPos+1));
tmpltClassifierPane.getNameField().setText("Classifer " + (listPos+1));
this.textProperty().bind(tmpltClassifierPane.getNameField().textProperty());
}
@ -187,8 +180,6 @@ public class CTClassifiersPane extends PamBorderPane {
ctClassifierParams.add(aClassifierParams);
}
if (!PamArrayUtils.unique(speciesCodeList) && speciesCodeList.length>1) {
System.err.println("CTClassifiersPane: The species codes are not unique");
PamArrayUtils.printArray(speciesCodeList);

View File

@ -45,17 +45,17 @@ public class BearingClassifierPane extends SettingsPane<BearingClassifierParams
*/
private void createBearingClassifierPane() {
Label label = new Label("Bearing Limits");
// Label label = new Label("Bearing Limits");
// label.setFont(PamGuiManagerFX.titleFontSize2);
PamGuiManagerFX.titleFont2style(label);
// PamGuiManagerFX.titleFont2style(label);
bearingLims = new MinMaxPane<Double>("Bearing", "\u00B0", -180., 180., 2.);
bearingLims = new MinMaxPane<Double>("Bearing Limits", "\u00B0", -180., 180., 2.);
Label label2 = new Label("\u0394 Bearing");
// Label label2 = new Label("\u0394 Bearing");
// label2.setFont(PamGuiManagerFX.titleFontSize2);
PamGuiManagerFX.titleFont2style(label2);
// PamGuiManagerFX.titleFont2style(label2);
bearingDMean = new MinMaxPane<Double>("\u0394 Bearing Mean ", "\u00B0/s", -180., 180., 0.001);
@ -70,7 +70,7 @@ public class BearingClassifierPane extends SettingsPane<BearingClassifierParams
this.mainPane = new PamVBox();
mainPane.setSpacing(5);
mainPane.getChildren().addAll(label, bearingLims, label2, bearingDMean, bearingDMedian, bearingDStd);
mainPane.getChildren().addAll(bearingLims, bearingDMean, bearingDMedian, bearingDStd);
}
@Override

View File

@ -0,0 +1,59 @@
package clickTrainDetector.layout.classification.idiClassifier;
import clickTrainDetector.ClickTrainControl;
import clickTrainDetector.classification.CTClassifierParams;
import clickTrainDetector.classification.idiClassifier.IDIClassifier;
import clickTrainDetector.classification.idiClassifier.IDIClassifierParams;
import clickTrainDetector.layout.classification.CTClassifierGraphics;
import javafx.scene.layout.Pane;
/**
* The IDI classifier graphics. Has controls for the IDI classifier.
*
* @author Jamie Macaulay
*
*/
public class IDIClassifierGraphics implements CTClassifierGraphics {
private IDIPane idiPane;
/**
* Reference to the IDI classifier
*/
private IDIClassifier idiClassifier;
public IDIClassifierGraphics(ClickTrainControl clickTrainControl, IDIClassifier idiClassifier) {
this.idiClassifier = idiClassifier;
}
@Override
public Pane getCTClassifierPane() {
if (idiPane == null) {
idiPane = new IDIPane();
}
return idiPane;
}
@Override
public CTClassifierParams getParams() {
IDIClassifierParams clssfrParams = idiPane.getParams(idiClassifier.getParams());
if (clssfrParams==null) {
System.err.print("Bearing classifier returned null params");
return null;
}
// else {
// simpleChi2Classifier.setParams(clssfrParams);
// return clssfrParams;
// }
return clssfrParams;
}
@Override
public void setParams(CTClassifierParams params) {
idiPane.setParams((IDIClassifierParams) params);
}
}

View File

@ -1,5 +1,6 @@
package clickTrainDetector.layout.classification.templateClassifier;
package clickTrainDetector.layout.classification.idiClassifier;
import clickTrainDetector.classification.idiClassifier.IDIClassifierParams;
import clickTrainDetector.classification.templateClassifier.TemplateClassifierParams;
import javafx.scene.layout.Pane;
@ -63,45 +64,45 @@ public class IDIPane extends PamBorderPane {
/**
* Set IDI parameters.
* @param templateClassifierParams - parameters to set.
* @param idiClassifierParams - parameters to set.
*/
public void setParams(TemplateClassifierParams templateClassifierParams) {
public void setParams(IDIClassifierParams idiClassifierParams) {
medianMinMax.setParams(templateClassifierParams.minMedianIDI, templateClassifierParams.maxMedianIDI, templateClassifierParams.useMedianIDI);
medianMinMax.setParams(idiClassifierParams.minMedianIDI, idiClassifierParams.maxMedianIDI, idiClassifierParams.useMedianIDI);
meanMinMax.setParams(templateClassifierParams.minMeanIDI, templateClassifierParams.maxMeanIDI, templateClassifierParams.useMeanIDI);
meanMinMax.setParams(idiClassifierParams.minMeanIDI, idiClassifierParams.maxMeanIDI, idiClassifierParams.useMeanIDI);
stdMinMax.setParams(templateClassifierParams.minStdIDI, templateClassifierParams.maxStdIDI, templateClassifierParams.useStdIDI);
stdMinMax.setParams(idiClassifierParams.minStdIDI, idiClassifierParams.maxStdIDI, idiClassifierParams.useStdIDI);
}
/**
* Get IDI parameters.
* @param templateClassifierParams - parameter class to add IDI parameters settings to./
* @param idiClassifierParams - parameter class to add IDI parameters settings to./
* @return altered parameters class.
*/
public TemplateClassifierParams getParams(TemplateClassifierParams templateClassifierParams) {
public IDIClassifierParams getParams(IDIClassifierParams idiClassifierParams) {
@SuppressWarnings("rawtypes")
MinMaxParams minMaxParams = medianMinMax.getParams();
templateClassifierParams.minMedianIDI = (Double) minMaxParams.min;
templateClassifierParams.maxMedianIDI = (Double) minMaxParams.max;
templateClassifierParams.useMedianIDI = minMaxParams.enabled;
idiClassifierParams.minMedianIDI = (Double) minMaxParams.min;
idiClassifierParams.maxMedianIDI = (Double) minMaxParams.max;
idiClassifierParams.useMedianIDI = minMaxParams.enabled;
minMaxParams = meanMinMax.getParams();
templateClassifierParams.minMeanIDI = (Double) minMaxParams.min;
templateClassifierParams.maxMeanIDI = (Double) minMaxParams.max;
templateClassifierParams.useMeanIDI = minMaxParams.enabled;
idiClassifierParams.minMeanIDI = (Double) minMaxParams.min;
idiClassifierParams.maxMeanIDI = (Double) minMaxParams.max;
idiClassifierParams.useMeanIDI = minMaxParams.enabled;
minMaxParams = stdMinMax.getParams();
templateClassifierParams.minStdIDI = (Double) minMaxParams.min;
templateClassifierParams.maxStdIDI = (Double) minMaxParams.max;
templateClassifierParams.useStdIDI = minMaxParams.enabled;
idiClassifierParams.minStdIDI = (Double) minMaxParams.min;
idiClassifierParams.maxStdIDI = (Double) minMaxParams.max;
idiClassifierParams.useStdIDI = minMaxParams.enabled;
return templateClassifierParams;
return idiClassifierParams;
}

View File

@ -4,8 +4,13 @@ import PamController.SettingsPane;
import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdClassifier;
import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdParams;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Spinner;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Pane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamSpinner;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxNodes.utilsFX.ControlField;
@ -19,10 +24,12 @@ import pamViewFX.fxNodes.utilsFX.ControlField;
public class SimpleCTClassifierPane extends SettingsPane<Chi2ThresholdParams> {
private static final double LABEL_WIDTH = 115;
/**
* The main pane.
*/
private PamVBox mainPane;
private Pane mainPane;
/**
* The chi^2 threshold.
@ -47,23 +54,42 @@ public class SimpleCTClassifierPane extends SettingsPane<Chi2ThresholdParams> {
public SimpleCTClassifierPane(Chi2ThresholdClassifier simpleChi2Classifier) {
super(null);
this.simpleChi2Classifier=simpleChi2Classifier;
mainPane = createSimpleClassifierPane();
mainPane = createSimpleClassifierPane(Orientation.VERTICAL);
}
public SimpleCTClassifierPane() {
super(null);
mainPane = createSimpleClassifierPane();
mainPane = createSimpleClassifierPane(Orientation.VERTICAL);
}
public SimpleCTClassifierPane(Chi2ThresholdClassifier simpleChi2Classifier, Orientation orientation) {
super(null);
this.simpleChi2Classifier=simpleChi2Classifier;
mainPane = createSimpleClassifierPane(orientation);
}
/**
* Create simple classifier pane.
*/
private PamVBox createSimpleClassifierPane() {
private Pane createSimpleClassifierPane(Orientation orientation) {
Pane vBox;
if (orientation == Orientation.VERTICAL) {
vBox = new PamVBox();
((PamVBox) vBox).setSpacing(5);
}
else {
vBox = new PamHBox();
((PamHBox) vBox).setSpacing(5);
}
PamVBox vBox = new PamVBox();
vBox.setPadding(new Insets(5,0,0,0));
vBox.setSpacing(5);
PamHBox pamHBox = new PamHBox();
pamHBox.setSpacing(5);
// create spinner.
chi2Threshold = new ControlField<Double>("X\u00b2 Threshold ", "", 0.0, Double.MAX_VALUE, 25.0);
@ -72,17 +98,20 @@ public class SimpleCTClassifierPane extends SettingsPane<Chi2ThresholdParams> {
"A click train has a X\u00b2 value which is based on the consistancy of inter detection interval \n"
+ "amplitude and other factors. The calculation of X\\\\u00b2 changes depending on the click train \n"
+ "detector is used."));
chi2Threshold.getLabel1().setPrefWidth(LABEL_WIDTH);
minClicks = new ControlField<Double>("Minimum Clicks ", "", 0, Integer.MAX_VALUE, 5);
minClicks = new ControlField<Double>("Min. Clicks ", "", 0, Integer.MAX_VALUE, 5);
minClicks.setTooltip(new Tooltip(
"The minimum number of detections."));
minClicks.getSpinner().setEditable(true);
minClicks.getLabel1().setPrefWidth(LABEL_WIDTH);
minTime = new ControlField<Double>("Minimum Time ", "s", 0.0, Double.MAX_VALUE, 1.0);
minTime = new ControlField<Double>("Min. Time ", "s", 0.0, Double.MAX_VALUE, 1.0);
minTime.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(2));
minTime.setTooltip(new Tooltip(
"The minimum time for a click train."));
minTime.getSpinner().setEditable(true);
minTime.getLabel1().setPrefWidth(LABEL_WIDTH);
chi2Threshold.getSpinner().setEditable(true);
@ -125,7 +154,7 @@ public class SimpleCTClassifierPane extends SettingsPane<Chi2ThresholdParams> {
* Get the main pane.
* @return the main pane.
*/
public PamVBox getMainPane() {
public Pane getMainPane() {
return mainPane;
}
@ -134,6 +163,7 @@ public class SimpleCTClassifierPane extends SettingsPane<Chi2ThresholdParams> {
// TODO Auto-generated method stub
}

View File

@ -0,0 +1,70 @@
package clickTrainDetector.layout.classification.standardClassifier;
import clickTrainDetector.ClickTrainControl;
import clickTrainDetector.classification.CTClassifierParams;
import clickTrainDetector.classification.bearingClassifier.BearingClassifier;
import clickTrainDetector.classification.bearingClassifier.BearingClassifierParams;
import clickTrainDetector.classification.standardClassifier.StandardClassifier;
import clickTrainDetector.classification.standardClassifier.StandardClassifierParams;
import clickTrainDetector.layout.classification.CTClassifierGraphics;
import javafx.scene.layout.Pane;
/**
* Handles the GUI for the standard click train classifier.
* @author Jamie Macaulay
*
*/
public class StandardClassifierGraphics implements CTClassifierGraphics {
/**
* Reference to the pane which hold controls for standard classifier params.
*/
private StandardClassifierPane standardClassifierPane;
/**
* Reference to the click train control.
*/
private ClickTrainControl clickTrainControl;
/**
* Reference to the standard classifier
*/
private StandardClassifier standardClassifier;
public StandardClassifierGraphics(ClickTrainControl clickTrainControl, StandardClassifier standardClassifier) {
this.clickTrainControl=clickTrainControl;
this.standardClassifier= standardClassifier;
}
@Override
public Pane getCTClassifierPane() {
if (standardClassifierPane ==null) {
standardClassifierPane = new StandardClassifierPane(standardClassifier);
}
return (Pane) standardClassifierPane.getContentNode();
}
@Override
public CTClassifierParams getParams() {
StandardClassifierParams clssfrParams = standardClassifierPane.getParams(standardClassifier.getParams());
if (clssfrParams==null) {
System.err.print("Bearing classifier returned null params");
return null;
}
// else {
// simpleChi2Classifier.setParams(clssfrParams);
// return clssfrParams;
// }
return clssfrParams;
}
@Override
public void setParams(CTClassifierParams params) {
standardClassifierPane.setParams((StandardClassifierParams) params);
}
}

View File

@ -0,0 +1,109 @@
package clickTrainDetector.layout.classification.standardClassifier;
import PamController.SettingsPane;
import clickTrainDetector.classification.standardClassifier.StandardClassifier;
import clickTrainDetector.classification.standardClassifier.StandardClassifierParams;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch;
/**
* Pane for the standard classifier. Essentially holds all the panes of the other classifiers.
* @author Jamie Macaulay
*
*/
public class StandardClassifierPane extends SettingsPane<StandardClassifierParams> {
private PamBorderPane mainPane;
private StandardClassifier standardClassifier;
private PamToggleSwitch[] enableSwitch;
private Label[] labels;
public StandardClassifierPane(StandardClassifier standardClassifier) {
super(null);
this.standardClassifier = standardClassifier;
mainPane = new PamBorderPane();
mainPane.setCenter(createPane());
}
private Pane createPane() {
PamVBox vBox = new PamVBox();
vBox.setSpacing(5);
labels = new Label[standardClassifier.getClassifiers().size()];
enableSwitch = new PamToggleSwitch[standardClassifier.getClassifiers().size()];
for (int i=0; i<standardClassifier.getClassifiers().size(); i++) {
enableSwitch[i] = new PamToggleSwitch("");
labels[i] = new Label(standardClassifier.getClassifiers().get(i).getName());
PamGuiManagerFX.titleFont2style(labels[i]);
final int ii = i;
enableSwitch[i].selectedProperty().addListener((obsVal, oldVal, newVal)->{
disableClassifierPane(ii);
});
disableClassifierPane(ii); //need to call here or else when the pane is first created stuff is not disabled.
PamHBox hBox = new PamHBox();
hBox.setSpacing(5);
hBox.getChildren().addAll(enableSwitch[i], labels[i]);
vBox.getChildren().addAll(hBox, standardClassifier.getClassifiers().get(i).getCTClassifierGraphics().getCTClassifierPane());
}
return vBox;
}
private void disableClassifierPane(int ii) {
standardClassifier.getClassifiers().get(ii).getCTClassifierGraphics().getCTClassifierPane().setDisable(!enableSwitch[ii].isSelected());
labels[ii].setDisable(!enableSwitch[ii].isSelected());
}
@Override
public StandardClassifierParams getParams(StandardClassifierParams currParams) {
for (int i=0; i<standardClassifier.getClassifiers().size(); i++) {
currParams.ctClassifierParams[i] = standardClassifier.getClassifiers().get(i).getCTClassifierGraphics().getParams();
currParams.enable[i] = enableSwitch[i].isSelected();
}
return currParams;
}
@Override
public void setParams(StandardClassifierParams input) {
for (int i=0; i<standardClassifier.getClassifiers().size(); i++) {
standardClassifier.getClassifiers().get(i).getCTClassifierGraphics().setParams(input.ctClassifierParams[i]);
enableSwitch[i].setSelected(input.enable[i]);
disableClassifierPane(i);
}
}
@Override
public String getName() {
return "Standard Classifier";
}
@Override
public Node getContentNode() {
return mainPane;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
}

View File

@ -6,6 +6,7 @@ import clickTrainDetector.classification.templateClassifier.CTTemplateClassifier
import clickTrainDetector.classification.templateClassifier.DefualtSpectrumTemplates;
import clickTrainDetector.classification.templateClassifier.DefualtSpectrumTemplates.SpectrumTemplateType;
import clickTrainDetector.classification.templateClassifier.TemplateClassifierParams;
import clickTrainDetector.layout.classification.idiClassifier.IDIPane;
import clickTrainDetector.layout.classification.simplechi2classifier.SimpleCTClassifierPane;
import javafx.geometry.Insets;
import javafx.scene.Node;
@ -35,7 +36,7 @@ public class TemplateClassifierPane extends SettingsPane<TemplateClassifierParam
/**
* Simple classifier pane
*/
private SimpleCTClassifierPane simpleCTClassifierPane;
//private SimpleCTClassifierPane simpleCTClassifierPane;
/**
* Reference to the CT classifer.
@ -45,7 +46,7 @@ public class TemplateClassifierPane extends SettingsPane<TemplateClassifierParam
/**
* The inter-detection settings pane.
*/
private IDIPane idiPane;
//private IDIPane idiPane;
private TemplateSpectrumPane spectrumTemplatePane;
@ -73,24 +74,24 @@ public class TemplateClassifierPane extends SettingsPane<TemplateClassifierParam
private Node createTemplatePane() {
//create the simple classifier pane.
Label label1 = new Label("X\u00b2 Threshold");
// label1.setFont(PamGuiManagerFX.titleFontSize2);
PamGuiManagerFX.titleFont2style(label1);
simpleCTClassifierPane = new SimpleCTClassifierPane(cTTemplateClassifier.getSimpleCTClassifier());
//the IDI settings
Label label2 = new Label("IDI Settings");
// label2.setFont(PamGuiManagerFX.titleFontSize2);
PamGuiManagerFX.titleFont2style(label2);
idiPane = new IDIPane();
// //create the simple classifier pane.
// Label label1 = new Label("X\u00b2 Threshold");
//// label1.setFont(PamGuiManagerFX.titleFontSize2);
// PamGuiManagerFX.titleFont2style(label1);
//
// simpleCTClassifierPane = new SimpleCTClassifierPane(cTTemplateClassifier.getSimpleCTClassifier());
//
// //the IDI settings
// Label label2 = new Label("IDI Settings");
//// label2.setFont(PamGuiManagerFX.titleFontSize2);
// PamGuiManagerFX.titleFont2style(label2);
//
// idiPane = new IDIPane();
//the spectrum template pane.
Label label3 = new Label("Spectrum Template");
//Label label3 = new Label("Spectrum Template");
// label3.setFont(PamGuiManagerFX.titleFontSize2);
PamGuiManagerFX.titleFont2style(label3);
//PamGuiManagerFX.titleFont2style(label3);
// create spinner.
spectrumthreshold = new ControlField<Double>("Spectrum Correlation Threshold", "", 0.0, Double.MAX_VALUE, 0.2);
@ -100,18 +101,21 @@ public class TemplateClassifierPane extends SettingsPane<TemplateClassifierParam
spectrumTemplatePane = new TemplateSpectrumPane();
spectrumTemplatePane.setPadding(new Insets(5,5,5,5));
spectrumTemplatePane.setMaxWidth(Double.POSITIVE_INFINITY);
spectrumTemplatePane.setMinWidth(50);
// spectrumTemplatePane.setMouseTransparent(true);
//make general and spectrum correlation pane.
PamVBox generalPane = new PamVBox();
generalPane.setSpacing(5);
generalPane.getChildren().addAll(label1, simpleCTClassifierPane.getContentNode(), label2,
idiPane);
// generalPane.getChildren().addAll(label1, simpleCTClassifierPane.getContentNode(), label2,
// idiPane);
generalPane.setPadding(new Insets(5,5,5,5));
PamVBox spectrumPane = new PamVBox();
spectrumPane.setSpacing(5);
spectrumPane.getChildren().addAll(label3, spectrumthreshold, spectrumTemplatePane);
spectrumPane.getChildren().addAll(spectrumthreshold, spectrumTemplatePane);
spectrumPane.setPadding(new Insets(5,5,5,5));
@ -139,9 +143,9 @@ public class TemplateClassifierPane extends SettingsPane<TemplateClassifierParam
@Override
public TemplateClassifierParams getParams(TemplateClassifierParams currParams) {
currParams.chi2ThresholdParams = simpleCTClassifierPane.getParams(currParams.chi2ThresholdParams);
currParams = idiPane.getParams(currParams);
// currParams.chi2ThresholdParams = simpleCTClassifierPane.getParams(currParams.chi2ThresholdParams);
//
// currParams = idiPane.getParams(currParams);
currParams.spectrumTemplate = spectrumTemplatePane.getSpectrum();
@ -153,11 +157,11 @@ public class TemplateClassifierPane extends SettingsPane<TemplateClassifierParam
@Override
public void setParams(TemplateClassifierParams input) {
//TEMP- testing settings classes.
if (input.chi2ThresholdParams==null) input.chi2ThresholdParams=new Chi2ThresholdParams();
//if (input.chi2ThresholdParams==null) input.chi2ThresholdParams=new Chi2ThresholdParams();
if (input.spectrumTemplate==null) input.spectrumTemplate=DefualtSpectrumTemplates.getTemplate(SpectrumTemplateType.DOLPHIN);
simpleCTClassifierPane.setParams(input.chi2ThresholdParams);
idiPane.setParams(input);
// simpleCTClassifierPane.setParams(input.chi2ThresholdParams);
// idiPane.setParams(input);
spectrumTemplatePane.setSpectrum(input.spectrumTemplate);
spectrumthreshold.setValue(input.corrThreshold);
}

View File

@ -85,6 +85,7 @@ public class TemplateSpectrumPane extends PamBorderPane {
templateDisplay.setDataInfo(templateSpectrumInfo=new TemplateDDDataInfo(templateDisplay, 192000));
templateDisplay.setPrefHeight(prefHeight);
templateDisplay.setMaxHeight(prefHeight);
//templateDisplay.setPrefWidth(500); //TODO - need to make this span the whole display...
templateDisplay.getDataTypePane().notifyDataChange(); //need this to initialise options pane for different plots.
@ -111,17 +112,28 @@ public class TemplateSpectrumPane extends PamBorderPane {
splitMenuButtonReject.setTranslateX(-20);
detectionPlot.setMouseTransparent(true);
detectionPlot.setMaxWidth(Double.MAX_VALUE);
StackPane stackPane = new StackPane();
stackPane.getChildren().add(detectionPlot);
stackPane.getChildren().add(splitMenuButtonReject);
stackPane.setPrefHeight(prefHeight);
stackPane.setMaxHeight(prefHeight);
stackPane.setMaxWidth(Double.MAX_VALUE);
stackPane.setPrefWidth(500); //need this for some reason to make the plot resize.
//stackPane.setStyle("-fx-background-color: red;");
detectionPlot.prefWidthProperty().bind(stackPane.widthProperty());
PamVBox holder = new PamVBox();
holder.setSpacing(5);
holder.getChildren().addAll(stackPane);
holder.setMaxWidth(Double.MAX_VALUE);
//holder.setStyle("-fx-background-color: green;");
holder.setPadding(new Insets(10,5,5,5));

View File

@ -5,7 +5,7 @@ import clickTrainDetector.clickTrainAlgorithms.mht.mhtvar.SimpleChi2VarParams;
/**
* The IDI Chi2 Pane.
* @author au671271
* @author Jamie Macaulay
*
*/
public class IDIChi2Pane extends SimpleMHTVarPane {

View File

@ -222,7 +222,7 @@ public class MHTSettingsPane extends SettingsPane<MHTParams> {
for (DefaultMHTSpecies speciesTypes: DefaultMHTSpecies.values()) {
menuItem = new MenuItem(DefaultMHTParams.getDefaultSpeciesName(speciesTypes));
menuItem.setOnAction((action)->{
setParams(DefaultMHTParams.getDefaultMHTParams(speciesTypes));
setParams(DefaultMHTParams.getDefaultMHTParams(speciesTypes, mhtClickTrainAlgorithm.getClickTrainControl().getParentDataBlock()));
});
speciesChoiceBox.getItems().add(menuItem);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -20,5 +20,11 @@ public class CPODProcess extends PamProcess {
// TODO Auto-generated method stub
}
@Override
public float getSampleRate() {
return CPODClickDataBlock.CPOD_SR;
}
}

View File

@ -46,7 +46,7 @@ public class CPODTDSettingsPane implements TDSettingsPane {
public CPODTDSettingsPane(CPODPlotInfoFX cpodPlotInfoFX) {
this.cpodPlotInfoFX=cpodPlotInfoFX;
mainPane = createMainPane();
mainPane.setPrefWidth(300);
mainPane.setPrefWidth(330);
}
/**

View File

@ -402,10 +402,15 @@ public class CPODSettingsPane extends SettingsPane<CPODParams> {
//have a look through the files and check that there are some files to import.
for (int i=0; i<CPODFileType.values().length; i++) {
try {
List<File> cp1 = (List<File>) FileUtils.listFiles(folder,
new String[]{CPODFileType.values()[i].getText()}, this.subFolder.isSelected());
System.out.println("Files out: " + cp1);
files.addAll(cp1);
}
catch (Exception e) {
System.err.println("Current directory does not exist: " + folder );
}
}
}

View File

@ -69,6 +69,7 @@ public class Datagram implements Serializable, ManagedParameters {
* @param datagramPoint
*/
public synchronized void addDataPoint(DatagramDataPoint datagramPoint) {
//System.out.println("Datagram points: " + dataPoints.size() + " intervalSeconds: " + intervalSeconds);
dataPoints.add(datagramPoint);
}

View File

@ -439,8 +439,16 @@ public class DatagramManager {
* @param dmp
*/
private void processDataMapPoint(PamDataBlock dataBlock, OfflineDataMapPoint dmp) {
long startTime = dmp.getStartTime();
long endTime = dmp.getEndTime();
// if (endTime>=5951575134760222265L) {
// System.out.println("Darn: "+ dmp.toString());
// };
//sometimes can get corrupt end times.
DatagramProvider datagramProvider = dataBlock.getDatagramProvider();
int nPoints = datagramProvider.getNumDataGramPoints();
float[] tempData = new float[nPoints]; // temp holder - gets converted to float later on
@ -499,6 +507,7 @@ public class DatagramManager {
}
datagramPoint.setData(gramData, usedDataUnits);
}
//System.out.println("Process Point: " + currentStart + " " + endTime);
datagram.addDataPoint(datagramPoint);
currentStart = currentEnd;
currentEnd = currentStart+datagramMillis;

View File

@ -90,6 +90,8 @@ public class ClickFFTPlotManager2 extends FFTPlotManager {
double[][] spectrum=clickSpectrogram.getSpectrogram(chanClick);
double clipLevel = ArrayManager.getArrayManager().getCurrentArray().getClipLevel(PamUtils.getChannelArray(pamDataUnit.getChannelBitmap())[chanClick]);
//System.out.println("clipLevel: " + clipLevel);
drawClipSpectrogram(spectrum, pamDataUnit.getTimeMilliseconds(),
writableImage.getScrollingPLot2DSegmenter().getMaxY(), clickPlotInfoFX.getClickDisplayParams().fftHop,
@ -123,8 +125,6 @@ public class ClickFFTPlotManager2 extends FFTPlotManager {
double[] minMax=PamUtils.getMinAndMax(spectrum);
double cutOff=clickPlotInfoFX.getClickDisplayParams().fftCutOf*minMax[1];
double binSize = maxFreq/(double) spectrum.length;
// System.out.println("cutOff: "+cutOff+ " fftCutOf: " + clickDisplayParams.fftCutOf);
int y1, y2;
@ -142,8 +142,6 @@ public class ClickFFTPlotManager2 extends FFTPlotManager {
y1=writableImage.getScrollingPLot2DSegmenter().getImageYPixels(0, writableImage);
y2=writableImage.getScrollingPLot2DSegmenter().getImageYPixels(maxFreq, writableImage);
strokeLine(writableImage, tc, y1, y2, ffColor);
}

View File

@ -417,6 +417,8 @@ public abstract class ScrollingImageSegmenter {
* @param tm - the repaint time in millis.
*/
public void paintImages(GraphicsContext g, TDProjectorFX tdProjector, double scrollStart, long tm) {
//System.out.println("ScrollingImageSegmenter: paintImages: " + tm);
if (tm==0) {
paintImages(g, tdProjector, scrollStart);
return;
@ -452,6 +454,7 @@ public abstract class ScrollingImageSegmenter {
double y1;
double y2;
double tcMillis;
for (int i=0; i<this.imageSegments.size(); i++) {
//Parameters:
// img - the image to be drawn or null.
@ -482,8 +485,8 @@ public abstract class ScrollingImageSegmenter {
y2=tdProjector.getYPix(maxYVal);
// System.out.println("Paint the image: tC: "
// + "" + tC + " tCEnd: " + tCEnd + " tcMillis " + tcMillis + " y1: " + y1 + " y2 " + y2 );
// System.out.println("Paint the image: tC: "
// + "" + tC + " tCEnd: " + tCEnd + " tcMillis " + tcMillis + " y1: " + y1 + " y2 " + y2 );
g.drawImage(imageSegments.get(i),
0,

View File

@ -85,13 +85,25 @@ public abstract class FFTPlotManager {
}
public void lastUnitDrawn(GraphicsContext g, double scrollStart, TDProjectorFX tdProjector,int plotnumber) {
public void lastUnitDrawn(GraphicsContext g, double scrollStart, TDProjectorFX tdProjector, int plotnumber) {
//System.out.println("FFTPlotManager: Last drawn unit1");
if (rawClipInfo.getScaleInfo()==null) return;
int plot = PamUtils.getSingleChannel(rawClipInfo.getScaleInfo().getPlotChannels()[plotnumber]); //needs to be the plot channels because the waveSegmenter is organised by channel
if (rawClipInfo.getScaleInfo()==null){
System.err.println("FFTPlotManager: the rawClipInfo.getScaleInfo() is null");
return;
}
//needs to be the plot channels because the waveSegmenter is organised by channel
int plot = PamUtils.getSingleChannel(rawClipInfo.getScaleInfo().getPlotChannels()[plotnumber]);
if (rawClipInfo.getScaleInfo().getPlotChannels()[plotnumber]==0) plot=0;
// System.out.println("FFTPlotManager: Last drawn unit2: " + " " + rawClipInfo.getScaleInfo().getPlotChannels()[plotnumber] + " " + plotnumber);
//
// System.out.println("FFTPlotManager: Last drawn unit2: " + plot + " rawClipInfo.getScaleInfoIndex(): "
// + rawClipInfo.getScaleInfoIndex() + " " +rawClipInfo.getScaleInfos().indexOf(getFrequencyScaleInfo()));
//only draw the writable images once we have the last data unit.
if (plot>=0 && fftImageSegmenter[plot]!=null && rawClipInfo.getScaleInfoIndex()==rawClipInfo.getScaleInfos().indexOf(getFrequencyScaleInfo())) {
fftImageSegmenter[plot].paintImages(g, tdProjector, scrollStart, 0);
@ -310,7 +322,7 @@ public abstract class FFTPlotManager {
double timeMillisFFT;
int newtc;
//if zero just draw one line to be efficient
// System.out.println("SpectrogramLength: " + spectrogram.length);
//System.out.println("SpectrogramLength: " + spectrogram.length);
//maybe compress image?
@ -331,13 +343,17 @@ public abstract class FFTPlotManager {
tc=newtc;
//double[] minmax = PamUtils.getMinAndMax(spectrogram);
//System.out.println("Plot spectrogram: tc " + tc + " minmax: " + 20*Math.log10(minmax[0])+clipLevel + " " + 20*Math.log10(minmax[1])+ " Clip level: " + clipLevel);
// double[] minmax = PamUtils.getMinAndMax(spectrogram);
// System.out.println("Plot spectrogram: tc " + tc + " minmax: " + 20*Math.log10(minmax[0])+clipLevel + " " + 20*Math.log10(minmax[1])+ " Clip level: " + clipLevel);
//how many lines in the image does the FFT take up?
Color prevCol;
Color col = null;
int argb;
for (int j=0; j<writableImage.getHeight(); j++) {
writableImage.getPixelWriter().setColor(0, j, Color.BLACK);
//what is the spectrum value for the height?
int spec = (int) ((j/(double) writableImage.getHeight())*spectrogram[i].length);
@ -347,7 +363,19 @@ public abstract class FFTPlotManager {
//find the correct colour for the spectrogram value and draw
for (int k=0; k<nslices; k++) {
if ((tc+k)>=writableImage.getWidth()) continue;
writableImage.getPixelWriter().setColor(tc+k, j, getSpectrumColour(spectrogram[i][spec], clipLevel, rawClipInfo.getDataBlock().getSampleRate(), fftLength));
//what do we do if a colour is already there? Take an avaerge with the other colours...
argb = writableImage.getPixelReader().getArgb(tc+k, j);
col = getSpectrumColour(spectrogram[i][spec],clipLevel, rawClipInfo.getDataBlock().getSampleRate(), fftLength);
if (argb>0) {
prevCol = Color.rgb((argb >> 16) & 0xFF, (argb >> 8) & 0xFF, (argb) & 0xFF);
col = Color.rgb((int) (col.getRed()+prevCol.getRed())/2, (int) (col.getGreen()+prevCol.getGreen())/2,
(int) (col.getBlue()+prevCol.getBlue())/2);
}
writableImage.getPixelWriter().setColor(tc+k, j, col);
}
}
// }
@ -437,20 +465,20 @@ public abstract class FFTPlotManager {
return dB;
}
// /**
// * Stroke a vertical line in the writable image.
// * @param writableImage - the writable image
// * @param x1 - x start of the line to stroke.
// * @param y1 - y start of the line to stroke.
// * @param x2 - x end of the line to stroke.
// * @param y2 -
// */
// private void strokeLine(WritableImageSegment writableImage, int x1, int y1, int y2, Color color) {
// //weird. y==y2 does not work but i<y2 does?
// for (int i=y1; i<y2; i++) {
// writableImage.getPixelWriter().setColor((int) Math.min(x1, writableImage.getWidth()-1), i, color);
// }
// }
// /**
// * Stroke a vertical line in the writable image.
// * @param writableImage - the writable image
// * @param x1 - x start of the line to stroke.
// * @param y1 - y start of the line to stroke.
// * @param x2 - x end of the line to stroke.
// * @param y2 -
// */
// private void strokeLine(WritableImageSegment writableImage, int x1, int y1, int y2, Color color) {
// //weird. y==y2 does not work but i<y2 does?
// for (int i=y1; i<y2; i++) {
// writableImage.getPixelWriter().setColor((int) Math.min(x1, writableImage.getWidth()-1), i, color);
// }
// }
/**

View File

@ -75,6 +75,7 @@ public abstract class RawWavePlotManager {
int plot = PamUtils.getSingleChannel(rawClipInfo.getScaleInfo().getPlotChannels()[plotnumber]); //needs to be the plot channels because the waveSegmenter is organised by channel
if (rawClipInfo.getScaleInfo().getPlotChannels()[plotnumber]==0) plot=0;
//System.out.println("Plot plot: " + plot + " " + plotnumber);

View File

@ -385,13 +385,13 @@ public abstract class TDDataInfoFX {
synchronized (pamDataBlock.getSynchLock()) {
//FIXME - shouldn;t have to clear every time but seems like we do?
//FIXME - shouldn't have to clear every time but seems like we do?
pamDataBlock.clearChannelIterators();
// scrollStart = PamCalendar.getTimeInMillis();
//work out start and stop times
long loopEnd = (long) (scrollStart + (tdProjector.getVisibleTime() * 1.05));
long loopStart = (long) (scrollStart - (tdProjector.getVisibleTime() * .05));
long loopEnd = (long) (scrollStart + (tdProjector.getVisibleTime() * 1.5));
long loopStart = (long) (scrollStart - (tdProjector.getVisibleTime() * .5));
//find a number close to the index start,
ListIterator<PamDataUnit> it = getUnitIterator( loopStart, plotNumber);

Some files were not shown because too many files have changed in this diff Show More