Data selector for whistle classifier

Added standard data selection options to whistle classifier output, and
also made it a 'Detector' type for down stream data connections
This commit is contained in:
Douglas Gillespie 2022-09-15 17:01:45 +01:00
parent 368fcbfc61
commit 425dd21d9f
13 changed files with 395 additions and 10 deletions

View File

@ -2000,6 +2000,10 @@ public abstract class SQLLogging {
}
PamSubtableData subtableData = new PamSubtableData();
long utc = SQLTypes.millisFromTimeStamp(subtableTableDef.getTimeStampItem().getValue());
if (utc % 1000 == 0) {
int millis = subtableTableDef.getTimeStampMillis().getIntegerValue();
utc += millis;
}
subtableData.setChildUTC(utc);
subtableData.setParentID(subtableTableDef.getParentID().getIntegerValue());
subtableData.setParentUID(subtableTableDef.getParentUID().getLongValue());

View File

@ -351,15 +351,13 @@ public class SQLTypes {
* goes to the default, so it needs to be on UTC at the moment data are written to the database, not
* just in this function.
*/
// return timeMillis;
if (timeMillis == null) {
return null;
}
// TimeZone tz = TimeZone.getDefault();
// TimeZone.setDefault(PamCalendar.defaultTimeZone);
// Timestamp ts = new Timestamp(timeMillis - tz.getOffset(timeMillis));
Timestamp ts = new Timestamp(timeMillis);
// TimeZone.setDefault(tz);
// Timestamp newTS = ts.toLocalDateTime();
// return PamCalendar.formatDBDateTime(timeMillis, false);
TimeZone tz = TimeZone.getDefault();
Timestamp ts = new UTCTimestamp(timeMillis - tz.getOffset(timeMillis));
return ts;
}

View File

@ -0,0 +1,30 @@
package generalDatabase;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
* Override standard Timestamp class and stop it making UTC corrections as it writes to database
* @author dg50
*
*/
public class UTCTimestamp extends Timestamp {
private static final long serialVersionUID = 1L;
public UTCTimestamp(long time) {
super(time);
// TODO Auto-generated constructor stub
}
@Override
public long getTime() {
return super.getTime();
}
@Override
public LocalDateTime toLocalDateTime() {
return super.toLocalDateTime();
}
}

View File

@ -44,6 +44,31 @@ public class SqliteSQLTypes extends SQLTypes {
return super.systemSqlType(sqlType);
}
@Override
public Object getTimeStamp(Long timeMillis) {
/**
* This has just got nasty WRT time zones.
* When the TimeStamp is written to database it uses the default time zone correction, which of course
* I don't want since I only do UTC. I was therefore subtracting this off before creating the ts
* so that it all worked fine when it was added back on again.
* This was fine for many years until someone processed data from exactly when the clocks went
* forward in the spring. Because the data were just after the clocks going forward, it took off
* an hour, then failed to add it back on again since the time was now before daylight saving.
* Amazed this has never happened before. Well done G and E ! I can fix it by setting the
* default time zone to UTC when PAMGuard starts, but note that all future references to local time
* will then be UTC. If I try to change it temporarily it doesn't help since the Timestamp always
* goes to the default, so it needs to be on UTC at the moment data are written to the database, not
* just in this function.
*
* Seems that for SQLite we can get away with a string, for MySQL we need a TimeStamp object still
*/
// return timeMillis;
if (timeMillis == null) {
return null;
}
return PamCalendar.formatDBDateTime(timeMillis, true);
}
@Override
public String formatDBDateTimeQueryString(long timeMilliseconds) {
switch (dateClass) {

View File

@ -135,7 +135,7 @@ public class Pamguard {
Thread folderSizeThread = new Thread(folderSizeMon);
folderSizeThread.start();
TimeZone.setDefault(PamCalendar.defaultTimeZone);
// TimeZone.setDefault(PamCalendar.defaultTimeZone);
System.out.println("**********************************************************");
try {

View File

@ -2,12 +2,25 @@ package whistleClassifier;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamProcess;
import PamguardMVC.dataSelector.DataSelectorCreator;
import whistleClassifier.dataselect.WslClsDataSelectCreator;
public class WhistleClasificationDataBlock extends PamDataBlock<WhistleClassificationDataUnit> {
public WhistleClasificationDataBlock(PamProcess parentProcess, int channelMap) {
super(WhistleClassificationDataUnit.class, "Whistle Classification", parentProcess, channelMap);
private WslClsDataSelectCreator dataSelectCreator;
private WhistleClassifierControl wslClassifierControl;
public WhistleClasificationDataBlock(WhistleClassifierControl wslClassifierControl, PamProcess parentProcess, int channelMap) {
super(WhistleClassificationDataUnit.class, "Whistle Classification", parentProcess, channelMap);
this.wslClassifierControl = wslClassifierControl;
}
@Override
public DataSelectorCreator getDataSelectCreator() {
if (dataSelectCreator == null) {
dataSelectCreator = new WslClsDataSelectCreator(wslClassifierControl, this);
}
return dataSelectCreator;
}
}

View File

@ -1,9 +1,10 @@
package whistleClassifier;
import PamDetection.PamDetection;
import PamguardMVC.AcousticDataUnit;
import PamguardMVC.PamDataUnit;
public class WhistleClassificationDataUnit extends PamDataUnit<PamDataUnit,PamDataUnit> implements AcousticDataUnit {
public class WhistleClassificationDataUnit extends PamDataUnit<PamDataUnit,PamDataUnit> implements AcousticDataUnit, PamDetection {
private double[] speciesLogLikelihoods;

View File

@ -65,7 +65,7 @@ public class WhistleClassifierProcess extends PamProcess {
this.whistleClassifierControl = whistleClassifierControl;
whistleClasificationDataBlock = new WhistleClasificationDataBlock(this, 3);
whistleClasificationDataBlock = new WhistleClasificationDataBlock(whistleClassifierControl, this, 3);
addOutputDataBlock(whistleClasificationDataBlock);

View File

@ -0,0 +1,35 @@
package whistleClassifier.dataselect;
import java.io.Serializable;
/**
* Data selector params for a single species.
* @author dg50
*
*/
public class SppClsSelectParams implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
public String name;
public boolean selected;
public double minScore;
public SppClsSelectParams(String name, boolean selected, double minScore) {
super();
this.name = name;
this.selected = selected;
this.minScore = minScore;
}
@Override
protected SppClsSelectParams clone() {
try {
return (SppClsSelectParams) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,31 @@
package whistleClassifier.dataselect;
import PamguardMVC.PamDataBlock;
import PamguardMVC.dataSelector.DataSelectParams;
import PamguardMVC.dataSelector.DataSelector;
import PamguardMVC.dataSelector.DataSelectorCreator;
import whistleClassifier.WhistleClasificationDataBlock;
import whistleClassifier.WhistleClassifierControl;
public class WslClsDataSelectCreator extends DataSelectorCreator {
private WhistleClassifierControl wslClassifierControl;
private WhistleClasificationDataBlock wslClassifierDataBlock;
public WslClsDataSelectCreator(WhistleClassifierControl wslClassifierControl, WhistleClasificationDataBlock pamDataBlock) {
super(pamDataBlock);
this.wslClassifierControl = wslClassifierControl;
this.wslClassifierDataBlock = pamDataBlock;
}
@Override
public DataSelector createDataSelector(String selectorName, boolean allowScores, String selectorType) {
return new WslClsDataSelector(wslClassifierControl, wslClassifierDataBlock, selectorName, allowScores);
}
@Override
public DataSelectParams createNewParams(String name) {
return new WslClsSelectorParams();
}
}

View File

@ -0,0 +1,70 @@
package whistleClassifier.dataselect;
import PamView.dialog.PamDialogPanel;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import PamguardMVC.dataSelector.DataSelectParams;
import PamguardMVC.dataSelector.DataSelector;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
import whistleClassifier.WhistleClassificationDataUnit;
import whistleClassifier.WhistleClassifierControl;
/**
* Species selector for whistle classifier. Currently only does yes / no, will
* maybe one day be extended to allow scores as well.
* @author dg50
*
*/
public class WslClsDataSelector extends DataSelector {
private WhistleClassifierControl wslClassifierControl;
private WslClsSelectorParams wcsParams = new WslClsSelectorParams();
public WslClsDataSelector(WhistleClassifierControl wslClassifierControl, PamDataBlock pamDataBlock, String selectorName, boolean allowScores) {
super(pamDataBlock, selectorName, allowScores);
this.wslClassifierControl = wslClassifierControl;
}
@Override
public void setParams(DataSelectParams dataSelectParams) {
if (dataSelectParams instanceof WslClsSelectorParams) {
wcsParams = (WslClsSelectorParams) dataSelectParams;
}
}
@Override
public WslClsSelectorParams getParams() {
return wcsParams;
}
@Override
public PamDialogPanel getDialogPanel() {
return new WslClsDialogPanel(wslClassifierControl, this);
}
@Override
public DynamicSettingsPane<Boolean> getDialogPaneFX() {
// TODO Auto-generated method stub
return null;
}
@Override
public double scoreData(PamDataUnit pamDataUnit) {
WhistleClassificationDataUnit wcdu = (WhistleClassificationDataUnit) pamDataUnit;
String species = wcdu.getSpecies();
// score = wcdu.get
SppClsSelectParams sppParams = wcsParams.getSppParams(species);
// if ()
// if (sppParams.selected == false) {
// return 0;
// }
// if (isAllowScores()) {
// return sppP
// }
// wslClassifierControl.getWhistleClassificationParameters().
return sppParams.selected ? 1 : 0;
}
}

View File

@ -0,0 +1,132 @@
package whistleClassifier.dataselect;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import PamView.dialog.PamDialog;
import PamView.dialog.PamDialogPanel;
import PamView.dialog.PamGridBagContraints;
import PamguardMVC.dataSelector.DataSelectParams;
import whistleClassifier.FragmentClassifierParams;
import whistleClassifier.WhistleClassificationParameters;
import whistleClassifier.WhistleClassifierControl;
/**
* dialog for whistle classifier data selector
* @author dg50
*
*/
public class WslClsDialogPanel implements PamDialogPanel {
private WhistleClassifierControl wslClassifierControl;
private WslClsDataSelector wslClsDataSelector;
private JPanel mainPanel;
private JPanel sppPanel;
private JCheckBox[] speciesBoxes;
private JTextField[] speciesScores;
public WslClsDialogPanel(WhistleClassifierControl wslClassifierControl, WslClsDataSelector wslClsDataSelector) {
this.wslClassifierControl = wslClassifierControl;
this.wslClsDataSelector = wslClsDataSelector;
this.mainPanel = new JPanel(new BorderLayout());
mainPanel.setBorder(new TitledBorder("Select species"));
sppPanel = new JPanel(new GridBagLayout());
mainPanel.add(BorderLayout.CENTER, sppPanel);
}
@Override
public JComponent getDialogComponent() {
return mainPanel;
}
@Override
public void setParams() {
WhistleClassificationParameters wslParams = wslClassifierControl.getWhistleClassificationParameters();
WslClsSelectorParams selParams = wslClsDataSelector.getParams();
if (wslParams == null) {
return;
}
FragmentClassifierParams fragParams = wslParams.fragmentClassifierParams;
if (fragParams == null) {
return;
}
fillSppPanel(wslParams, selParams);
}
private void fillSppPanel(WhistleClassificationParameters wslParams, WslClsSelectorParams selParams) {
boolean allowScores = wslClsDataSelector.isAllowScores();
sppPanel.removeAll();
String[] sppList = wslParams.fragmentClassifierParams.getSpeciesList();
if (sppList == null) {
return;
}
int nSpp = sppList.length;
speciesBoxes = new JCheckBox[nSpp];
speciesScores = new JTextField[nSpp];
GridBagConstraints c = new PamGridBagContraints();
sppPanel.add(new JLabel("Species", JLabel.CENTER), c);
if (allowScores) {
c.gridx++;
JLabel lab = new JLabel(" Min score ", JLabel.CENTER);
lab.setToolTipText("Minimum classification score (between 0 and 1)");
sppPanel.add(lab , c);
}
for (int i = 0; i < nSpp; i++) {
speciesBoxes[i] = new JCheckBox(sppList[i]);
speciesScores[i] = new JTextField(3);
c.gridx = 0;
c.gridy++;
sppPanel.add(speciesBoxes[i], c);
if (allowScores) {
c.gridx++;
sppPanel.add(speciesScores[i], c);
}
SppClsSelectParams sppSel = selParams.getSppParams(sppList[i]);
speciesBoxes[i].setSelected(sppSel.selected);
speciesScores[i].setText(String.format("%3.2f", sppSel.minScore));
}
}
@Override
public boolean getParams() {
WslClsSelectorParams selParams = wslClsDataSelector.getParams();
boolean allowScores = wslClsDataSelector.isAllowScores();
if (speciesBoxes == null) {
return false;
}
int nSpp = speciesBoxes.length;
for (int i = 0; i < nSpp; i++) {
String name = speciesBoxes[i].getText();
boolean sel = speciesBoxes[i].isSelected();
double score = 0;
if (allowScores) {
try {
score = Double.valueOf(speciesScores[i].getText());
}
catch (NumberFormatException e) {
score = -1;
}
}
if (score < 0 || score > 1) {
return PamDialog.showWarning(null, "Invalid score value for " + name, "Score values must be betwween 0 and 1");
}
selParams.setSppParams(name, new SppClsSelectParams(name, sel, score));
}
return true;
}
}

View File

@ -0,0 +1,46 @@
package whistleClassifier.dataselect;
import java.io.Serializable;
import java.util.HashMap;
import PamguardMVC.dataSelector.DataSelectParams;
public class WslClsSelectorParams extends DataSelectParams implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private HashMap<String, SppClsSelectParams> sppParamsTable = new HashMap<>();
public WslClsSelectorParams() {
// TODO Auto-generated constructor stub
}
@Override
public WslClsSelectorParams clone() {
try {
return (WslClsSelectorParams) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public void setSppParams(String sppName, SppClsSelectParams params) {
if (sppParamsTable == null) {
sppParamsTable = new HashMap<String, SppClsSelectParams>();
}
sppParamsTable.put(sppName, params);
}
public SppClsSelectParams getSppParams(String sppName) {
if (sppParamsTable == null) {
sppParamsTable = new HashMap<String, SppClsSelectParams>();
}
SppClsSelectParams sppParams = sppParamsTable.get(sppName);
if (sppParams == null) {
sppParams = new SppClsSelectParams(sppName, false, 0);
}
return sppParams;
}
}