From 7fefb386209e92c18dbf17d5c6857923e1bb7946 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 29 Mar 2022 11:39:26 +0100 Subject: [PATCH 01/37] Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. --- src/PamguardMVC/RawDataTransforms.java | 286 +++++++++++++++---------- 1 file changed, 169 insertions(+), 117 deletions(-) diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index bab6ccd0..28ff9364 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -99,11 +99,35 @@ 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; + } } @@ -143,23 +167,25 @@ public class RawDataTransforms { * @param fftLength * @return Power spectrum */ - 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(); + 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(); } - - 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(); } /** @@ -170,29 +196,31 @@ public class RawDataTransforms { * @param fftLength * @return Power spectrum */ - 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; - + public double[] getPowerSpectrum(int channel, int fftLength) { + synchronized (synchObject) { + if (powerSpectra == null) { + powerSpectra = new double[PamUtils.getNumChannels(dataUnit.getChannelBitmap())][]; } - if (powerSpectra[channel].length != fftLength/2) { - powerSpectra[channel] = Arrays.copyOf(powerSpectra[channel], fftLength/2); + 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]; } - return powerSpectra[channel]; } @@ -202,25 +230,27 @@ public class RawDataTransforms { * @param fftLength * @return Sum of power spectra */ - 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]; + 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]; + } } } + return totalPowerSpectrum; } - return totalPowerSpectrum; } @@ -235,15 +265,17 @@ 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 synchronized ComplexArray getComplexSpectrumHann(int channel, int fftLength) { - complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())]; - if (complexSpectrum[channel] == null - || complexSpectrum.length != fftLength / 2) { + public ComplexArray getComplexSpectrumHann(int channel, int fftLength) { + synchronized (synchObject) { + complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())]; + if (complexSpectrum[channel] == null + || complexSpectrum.length != fftLength / 2) { - complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength); - currentSpecLen = fftLength; + complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength); + currentSpecLen = fftLength; + } + return complexSpectrum[channel]; } - return complexSpectrum[channel]; } @@ -282,10 +314,12 @@ public class RawDataTransforms { * @return the spectrogram length. */ private int getCurrentSpectrumLength() { - if (currentSpecLen<=0) { - currentSpecLen = PamUtils.getMinFftLength(dataUnit.getSampleDuration()); + synchronized (synchObject) { + if (currentSpecLen<=0) { + currentSpecLen = PamUtils.getMinFftLength(dataUnit.getSampleDuration()); + } + return currentSpecLen; } - return currentSpecLen; } @@ -371,29 +405,31 @@ public class RawDataTransforms { * @param fftLength * @return the complex spectrum */ - public synchronized ComplexArray getComplexSpectrum(int channel, int fftLength) { - double[] paddedRawData; - double[] rawData; - int i, mn; + public ComplexArray getComplexSpectrum(int channel, int fftLength) { + synchronized (synchObject) { + 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]; + if (complexSpectrum == null) { + complexSpectrum = new ComplexArray[getNChan()]; } - for (i = mn; i < fftLength; i++) { - paddedRawData[i] = 0; + 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); } - complexSpectrum[channel] = fastFFT.rfft(paddedRawData, fftLength); + return complexSpectrum[channel]; } - return complexSpectrum[channel]; } @@ -402,14 +438,16 @@ public class RawDataTransforms { * @param iChan channel index * @return analytic waveform */ - public synchronized double[] getAnalyticWaveform(int iChan) { - if (analyticWaveform == null) { - analyticWaveform = new double[getNChan()][]; + 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]; } - // if (analyticWaveform[iChan] == null) { - analyticWaveform[iChan] = hilbert.getHilbert(getWaveData(iChan)); - // } - return analyticWaveform[iChan]; } /** @@ -421,12 +459,14 @@ public class RawDataTransforms { * @param fftFilterParams fft filter parameters. * @return analystic waveform. */ - public synchronized double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) { - if (filtered == false || fftFilterParams == null) { - return getAnalyticWaveform(iChan); - } - else { - return getFilteredAnalyticWaveform(fftFilterParams, iChan); + public double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) { + synchronized (synchObject) { + if (filtered == false || fftFilterParams == null) { + return getAnalyticWaveform(iChan); + } + else { + return getFilteredAnalyticWaveform(fftFilterParams, iChan); + } } } @@ -437,7 +477,8 @@ public class RawDataTransforms { * @param iChan channel number * @return envelope of the filtered data. */ - public synchronized double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) { + public double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) { + synchronized (synchObject) { if (analyticWaveform == null) { analyticWaveform = new double[getNChan()][]; } @@ -446,6 +487,7 @@ public class RawDataTransforms { getHilbert(getFilteredWaveData(fftFilterParams, iChan)); // } return analyticWaveform[iChan]; + } } /** @@ -455,19 +497,21 @@ public class RawDataTransforms { * @return analystic waveforms */ public double[][] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams) { - if (analyticWaveform == null) { - analyticWaveform = new double[getNChan()][]; - } - for (int iChan = 0; iChan < getNChan(); iChan++) { - if (fftFilterParams != null) { - analyticWaveform[iChan] = hilbert. - getHilbert(getFilteredWaveData(fftFilterParams, iChan)); + synchronized (synchObject) { // new + if (analyticWaveform == null) { + analyticWaveform = new double[getNChan()][]; } - else { - analyticWaveform[iChan] = getAnalyticWaveform(iChan); + for (int iChan = 0; iChan < getNChan(); iChan++) { + if (fftFilterParams != null) { + analyticWaveform[iChan] = hilbert. + getHilbert(getFilteredWaveData(fftFilterParams, iChan)); + } + else { + analyticWaveform[iChan] = getAnalyticWaveform(iChan); + } } + return analyticWaveform; } - return analyticWaveform; } @@ -478,7 +522,7 @@ public class RawDataTransforms { * @param channelIndex channel index * @return filtered waveform data */ - public synchronized double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) { + public double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) { filteredWaveData = getFilteredWaveData(filterParams); return filteredWaveData[channelIndex]; } @@ -489,12 +533,14 @@ public class RawDataTransforms { * @param filterParams filter parameters * @return array of filtered data */ - public synchronized double[][] getFilteredWaveData(FFTFilterParams filterParams) { + public double[][] getFilteredWaveData(FFTFilterParams filterParams) { + synchronized (synchObject) { //System.out.println("Make filterred wave data!: " + (filterParams != oldFFTFilterParams)); if (filteredWaveData == null || filterParams != oldFFTFilterParams) { filteredWaveData = makeFilteredWaveData(filterParams); } return filteredWaveData; + } } @@ -538,13 +584,15 @@ public class RawDataTransforms { * @return FFT filter object. */ public FFTFilter getFFTFilter(FFTFilterParams fftFilterParams) { - if (fftFilter == null) { - fftFilter = new FFTFilter(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); + synchronized (synchObject) { + if (fftFilter == null) { + fftFilter = new FFTFilter(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); + } + else { + fftFilter.setParams(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); + } + return fftFilter; } - else { - fftFilter.setParams(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); - } - return fftFilter; } @@ -589,7 +637,9 @@ public class RawDataTransforms { * @return */ private int getNChan() { - return this.rawData.getWaveData().length; + synchronized (synchObject) { // new + return this.rawData.getWaveData().length; + } } /** @@ -622,9 +672,11 @@ public class RawDataTransforms { * Free eup some memory by deleting the filtered wave data, power spectra and analytic waveform. */ public void freeMemory() { - filteredWaveData = null; - powerSpectra = null; - analyticWaveform = null; + synchronized (synchObject) { + filteredWaveData = null; + powerSpectra = null; + analyticWaveform = null; + } } From db32b280d16b8aaeb20af74d4d1dd226c6048e53 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 29 Mar 2022 14:25:31 +0100 Subject: [PATCH 02/37] fix problem in SummaryComand --- src/PamController/command/SummaryCommand.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/PamController/command/SummaryCommand.java b/src/PamController/command/SummaryCommand.java index c9b0055e..91185b8b 100644 --- a/src/PamController/command/SummaryCommand.java +++ b/src/PamController/command/SummaryCommand.java @@ -35,10 +35,6 @@ public class SummaryCommand extends ExtCommand { } public String getModulesSummary(boolean clear) { - return getReturnString(); - } - - public String getReturnString() { PamController pamController = PamController.getInstance(); int nMod = pamController.getNumControlledUnits(); PamControlledUnit aModule; From 81fd9f5481e069d75560d379627ad18244a00349 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 13 Apr 2022 17:03:44 +0100 Subject: [PATCH 03/37] Update command line options --- .classpath | 2 +- src/PamView/dialog/GenericSwingDialog.java | 1 + src/binaryFileStorage/BinaryStore.java | 47 ++++++++++++++++++++++ src/clickDetector/ClickDetector.java | 2 +- src/pamguard/GlobalArguments.java | 33 +++++++++++++++ src/pamguard/Pamguard.java | 9 +++++ 6 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/pamguard/GlobalArguments.java diff --git a/.classpath b/.classpath index a8555a1f..ba469602 100644 --- a/.classpath +++ b/.classpath @@ -6,7 +6,7 @@ - + diff --git a/src/PamView/dialog/GenericSwingDialog.java b/src/PamView/dialog/GenericSwingDialog.java index 0539ca61..8d0177a9 100644 --- a/src/PamView/dialog/GenericSwingDialog.java +++ b/src/PamView/dialog/GenericSwingDialog.java @@ -33,6 +33,7 @@ public class GenericSwingDialog extends PamDialog { GenericSwingDialog swingDialog = new GenericSwingDialog(parentFrame, title, dialogPanels); swingDialog.setParams(); + swingDialog.pack(); swingDialog.setVisible(true); return swingDialog.allOk; } diff --git a/src/binaryFileStorage/BinaryStore.java b/src/binaryFileStorage/BinaryStore.java index efc1f23c..c2007cb2 100644 --- a/src/binaryFileStorage/BinaryStore.java +++ b/src/binaryFileStorage/BinaryStore.java @@ -27,6 +27,7 @@ import org.w3c.dom.Element; import pamScrollSystem.ViewLoadObserver; import pamViewFX.pamTask.PamTaskUpdate; +import pamguard.GlobalArguments; //import com.mysql.jdbc.NdbLoadBalanceExceptionChecker; @@ -137,6 +138,9 @@ PamSettingsSource, OfflineDataStore { //TODO- temp; for debug; private int nUnits=0; + // arg name for global change to store name. + public static final String GlobalFolderArg = "-binaryfolder"; + /** * The FX GUI for binary store */ @@ -402,9 +406,52 @@ PamSettingsSource, OfflineDataStore { public boolean restoreSettings( PamControlledUnitSettings pamControlledUnitSettings) { binaryStoreSettings = ((BinaryStoreSettings) pamControlledUnitSettings.getSettings()).clone(); + /* + * Then check to see if there is a command line override of the currently stored folder name. + */ + String globFolder = GlobalArguments.getParam(GlobalFolderArg); + if (globFolder != null) { + boolean ok = checkGlobFolder(globFolder); + if (ok) { + binaryStoreSettings.setStoreLocation(globFolder); // remember it. + } + else { + System.err.println("Unable to set binary storage folder " + globFolder); + } + } return true; } + /** + * Set and create if necessary the global folder. + * @param globFolder + */ + private boolean checkGlobFolder(String globFolder) { + File outFold = new File(globFolder); + if (outFold.exists()) { + if (outFold.isDirectory()) { + return true; // all OK + } + else { + return false; // it must be a file - that's bad ! + } + } + // try to create it. + try { + if (outFold.mkdirs()) { + FileFunctions.setNonIndexingBit(outFold); + return true; + } + else { + return false; // unable to make the folder. + } + } + catch (Exception e) { + System.err.println("Can't set binary store folder: " + e.getLocalizedMessage()); + return false; + } + + } @Override public JMenuItem createFileMenu(JFrame parentFrame) { JMenuItem m; diff --git a/src/clickDetector/ClickDetector.java b/src/clickDetector/ClickDetector.java index eaca372c..074138d7 100644 --- a/src/clickDetector/ClickDetector.java +++ b/src/clickDetector/ClickDetector.java @@ -374,7 +374,7 @@ public class ClickDetector extends PamProcess { * @see PamguardMVC.PamProcess#SetupProcess() */ @Override - public void setupProcess() { + public synchronized void setupProcess() { pauseDetection = true; diff --git a/src/pamguard/GlobalArguments.java b/src/pamguard/GlobalArguments.java new file mode 100644 index 00000000..c8c6599a --- /dev/null +++ b/src/pamguard/GlobalArguments.java @@ -0,0 +1,33 @@ +package pamguard; + +import java.util.HashMap; + +/** + * Global parameter pairs set at startup time.
+ * These are the arguments passed to the command line.
Basically all going into a static hash map + * @author dg50 + * + */ +public class GlobalArguments { + + static HashMap globalFlags = new HashMap<>(); + + /** + * Set a global parameter value + * @param name value name + * @param value parameter value + */ + public static void setParam(String name, String value) { + globalFlags.put(name, value); + } + + /** + * Get a global parameter value + * @param name value name + * @return value + */ + public static String getParam(String name) { + return globalFlags.get(name); + } + +} diff --git a/src/pamguard/Pamguard.java b/src/pamguard/Pamguard.java index ebc980a0..3c63090e 100644 --- a/src/pamguard/Pamguard.java +++ b/src/pamguard/Pamguard.java @@ -39,6 +39,7 @@ import PamView.FullScreen; import PamView.ScreenSize; import PamView.dialog.warn.WarnOnce; import PamguardMVC.debug.Debug; +import binaryFileStorage.BinaryStore; import dataPlotsFX.JamieDev; import java.io.BufferedReader; import java.io.File; @@ -213,6 +214,14 @@ public class Pamguard { System.out.println("Disabling log file from command line switch..."); ProxyPrintStream.disableLogFile(); } + else if (anArg.equalsIgnoreCase(BinaryStore.GlobalFolderArg)) { + // output folder for binary files. + GlobalArguments.setParam(anArg, args[iArg++]); + } + else if (anArg.equalsIgnoreCase("-databasefile")) { + // database file name + GlobalArguments.setParam(anArg, args[iArg++]); + } else if (anArg.equalsIgnoreCase("-help")) { System.out.println("--PamGuard Help"); System.out.println("\n--For standard GUI deployment run without any options.\n"); From db28beb6dabcf9b09e31e1891826bb3d8bd7d706 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 29 Mar 2022 11:39:26 +0100 Subject: [PATCH 04/37] Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. --- src/PamguardMVC/RawDataTransforms.java | 286 +++++++++++++++---------- 1 file changed, 169 insertions(+), 117 deletions(-) diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index e71dd030..28ff9364 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -99,11 +99,35 @@ 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; + } } @@ -143,23 +167,25 @@ public class RawDataTransforms { * @param fftLength * @return Power spectrum */ - 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(); + 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(); } - - 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(); } /** @@ -170,29 +196,31 @@ public class RawDataTransforms { * @param fftLength * @return Power spectrum */ - 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; - + public double[] getPowerSpectrum(int channel, int fftLength) { + synchronized (synchObject) { + if (powerSpectra == null) { + powerSpectra = new double[PamUtils.getNumChannels(dataUnit.getChannelBitmap())][]; } - if (powerSpectra[channel].length != fftLength/2) { - powerSpectra[channel] = Arrays.copyOf(powerSpectra[channel], fftLength/2); + 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]; } - return powerSpectra[channel]; } @@ -202,25 +230,27 @@ public class RawDataTransforms { * @param fftLength * @return Sum of power spectra */ - 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]; + 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]; + } } } + return totalPowerSpectrum; } - return totalPowerSpectrum; } @@ -235,15 +265,17 @@ 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 synchronized ComplexArray getComplexSpectrumHann(int channel, int fftLength) { - complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())]; - if (complexSpectrum[channel] == null - || complexSpectrum.length != fftLength / 2) { + public ComplexArray getComplexSpectrumHann(int channel, int fftLength) { + synchronized (synchObject) { + complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())]; + if (complexSpectrum[channel] == null + || complexSpectrum.length != fftLength / 2) { - complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength); - currentSpecLen = fftLength; + complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength); + currentSpecLen = fftLength; + } + return complexSpectrum[channel]; } - return complexSpectrum[channel]; } @@ -282,10 +314,12 @@ public class RawDataTransforms { * @return the spectrogram length. */ private int getCurrentSpectrumLength() { - if (currentSpecLen<=0) { - currentSpecLen = PamUtils.getMinFftLength(dataUnit.getSampleDuration()); + synchronized (synchObject) { + if (currentSpecLen<=0) { + currentSpecLen = PamUtils.getMinFftLength(dataUnit.getSampleDuration()); + } + return currentSpecLen; } - return currentSpecLen; } @@ -371,29 +405,31 @@ public class RawDataTransforms { * @param fftLength * @return the complex spectrum */ - public synchronized ComplexArray getComplexSpectrum(int channel, int fftLength) { - double[] paddedRawData; - double[] rawData; - int i, mn; + public ComplexArray getComplexSpectrum(int channel, int fftLength) { + synchronized (synchObject) { + 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]; + if (complexSpectrum == null) { + complexSpectrum = new ComplexArray[getNChan()]; } - for (i = mn; i < fftLength; i++) { - paddedRawData[i] = 0; + 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); } - complexSpectrum[channel] = fastFFT.rfft(paddedRawData, fftLength); + return complexSpectrum[channel]; } - return complexSpectrum[channel]; } @@ -402,14 +438,16 @@ public class RawDataTransforms { * @param iChan channel index * @return analytic waveform */ - public synchronized double[] getAnalyticWaveform(int iChan) { - if (analyticWaveform == null) { - analyticWaveform = new double[getNChan()][]; + 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]; } - // if (analyticWaveform[iChan] == null) { - analyticWaveform[iChan] = hilbert.getHilbert(getWaveData(iChan)); - // } - return analyticWaveform[iChan]; } /** @@ -421,12 +459,14 @@ public class RawDataTransforms { * @param fftFilterParams fft filter parameters. * @return analystic waveform. */ - public synchronized double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) { - if (filtered == false || fftFilterParams == null) { - return getAnalyticWaveform(iChan); - } - else { - return getFilteredAnalyticWaveform(fftFilterParams, iChan); + public double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) { + synchronized (synchObject) { + if (filtered == false || fftFilterParams == null) { + return getAnalyticWaveform(iChan); + } + else { + return getFilteredAnalyticWaveform(fftFilterParams, iChan); + } } } @@ -437,7 +477,8 @@ public class RawDataTransforms { * @param iChan channel number * @return envelope of the filtered data. */ - public synchronized double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) { + public double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) { + synchronized (synchObject) { if (analyticWaveform == null) { analyticWaveform = new double[getNChan()][]; } @@ -446,6 +487,7 @@ public class RawDataTransforms { getHilbert(getFilteredWaveData(fftFilterParams, iChan)); // } return analyticWaveform[iChan]; + } } /** @@ -455,19 +497,21 @@ public class RawDataTransforms { * @return analystic waveforms */ public double[][] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams) { - if (analyticWaveform == null) { - analyticWaveform = new double[getNChan()][]; - } - for (int iChan = 0; iChan < getNChan(); iChan++) { - if (fftFilterParams != null) { - analyticWaveform[iChan] = hilbert. - getHilbert(getFilteredWaveData(fftFilterParams, iChan)); + synchronized (synchObject) { // new + if (analyticWaveform == null) { + analyticWaveform = new double[getNChan()][]; } - else { - analyticWaveform[iChan] = getAnalyticWaveform(iChan); + for (int iChan = 0; iChan < getNChan(); iChan++) { + if (fftFilterParams != null) { + analyticWaveform[iChan] = hilbert. + getHilbert(getFilteredWaveData(fftFilterParams, iChan)); + } + else { + analyticWaveform[iChan] = getAnalyticWaveform(iChan); + } } + return analyticWaveform; } - return analyticWaveform; } @@ -478,7 +522,7 @@ public class RawDataTransforms { * @param channelIndex channel index * @return filtered waveform data */ - public synchronized double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) { + public double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) { filteredWaveData = getFilteredWaveData(filterParams); return filteredWaveData[channelIndex]; } @@ -489,12 +533,14 @@ public class RawDataTransforms { * @param filterParams filter parameters * @return array of filtered data */ - public synchronized double[][] getFilteredWaveData(FFTFilterParams filterParams) { + public double[][] getFilteredWaveData(FFTFilterParams filterParams) { + synchronized (synchObject) { //System.out.println("Make filterred wave data!: " + (filterParams != oldFFTFilterParams)); if (filteredWaveData == null || filterParams != oldFFTFilterParams) { filteredWaveData = makeFilteredWaveData(filterParams); } return filteredWaveData; + } } @@ -538,13 +584,15 @@ public class RawDataTransforms { * @return FFT filter object. */ public FFTFilter getFFTFilter(FFTFilterParams fftFilterParams) { - if (fftFilter == null) { - fftFilter = new FFTFilter(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); + synchronized (synchObject) { + if (fftFilter == null) { + fftFilter = new FFTFilter(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); + } + else { + fftFilter.setParams(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); + } + return fftFilter; } - else { - fftFilter.setParams(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); - } - return fftFilter; } @@ -589,7 +637,9 @@ public class RawDataTransforms { * @return */ private int getNChan() { - return this.rawData.getWaveData().length; + synchronized (synchObject) { // new + return this.rawData.getWaveData().length; + } } /** @@ -622,9 +672,11 @@ public class RawDataTransforms { * Free eup some memory by deleting the filtered wave data, power spectra and analytic waveform. */ public void freeMemory() { - filteredWaveData = null; - powerSpectra = null; - analyticWaveform = null; + synchronized (synchObject) { + filteredWaveData = null; + powerSpectra = null; + analyticWaveform = null; + } } From bfd93cb6233b13ece6afeb97839a6cf5a018f363 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 13 Apr 2022 17:03:44 +0100 Subject: [PATCH 05/37] Update command line options --- .classpath | 2 +- src/PamView/dialog/GenericSwingDialog.java | 1 + src/binaryFileStorage/BinaryStore.java | 47 ++++++++++++++++++++++ src/clickDetector/ClickDetector.java | 2 +- src/pamguard/GlobalArguments.java | 33 +++++++++++++++ src/pamguard/Pamguard.java | 9 +++++ 6 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/pamguard/GlobalArguments.java diff --git a/.classpath b/.classpath index a5d40376..ba469602 100644 --- a/.classpath +++ b/.classpath @@ -6,7 +6,7 @@
- + diff --git a/src/PamView/dialog/GenericSwingDialog.java b/src/PamView/dialog/GenericSwingDialog.java index 0539ca61..8d0177a9 100644 --- a/src/PamView/dialog/GenericSwingDialog.java +++ b/src/PamView/dialog/GenericSwingDialog.java @@ -33,6 +33,7 @@ public class GenericSwingDialog extends PamDialog { GenericSwingDialog swingDialog = new GenericSwingDialog(parentFrame, title, dialogPanels); swingDialog.setParams(); + swingDialog.pack(); swingDialog.setVisible(true); return swingDialog.allOk; } diff --git a/src/binaryFileStorage/BinaryStore.java b/src/binaryFileStorage/BinaryStore.java index efc1f23c..c2007cb2 100644 --- a/src/binaryFileStorage/BinaryStore.java +++ b/src/binaryFileStorage/BinaryStore.java @@ -27,6 +27,7 @@ import org.w3c.dom.Element; import pamScrollSystem.ViewLoadObserver; import pamViewFX.pamTask.PamTaskUpdate; +import pamguard.GlobalArguments; //import com.mysql.jdbc.NdbLoadBalanceExceptionChecker; @@ -137,6 +138,9 @@ PamSettingsSource, OfflineDataStore { //TODO- temp; for debug; private int nUnits=0; + // arg name for global change to store name. + public static final String GlobalFolderArg = "-binaryfolder"; + /** * The FX GUI for binary store */ @@ -402,9 +406,52 @@ PamSettingsSource, OfflineDataStore { public boolean restoreSettings( PamControlledUnitSettings pamControlledUnitSettings) { binaryStoreSettings = ((BinaryStoreSettings) pamControlledUnitSettings.getSettings()).clone(); + /* + * Then check to see if there is a command line override of the currently stored folder name. + */ + String globFolder = GlobalArguments.getParam(GlobalFolderArg); + if (globFolder != null) { + boolean ok = checkGlobFolder(globFolder); + if (ok) { + binaryStoreSettings.setStoreLocation(globFolder); // remember it. + } + else { + System.err.println("Unable to set binary storage folder " + globFolder); + } + } return true; } + /** + * Set and create if necessary the global folder. + * @param globFolder + */ + private boolean checkGlobFolder(String globFolder) { + File outFold = new File(globFolder); + if (outFold.exists()) { + if (outFold.isDirectory()) { + return true; // all OK + } + else { + return false; // it must be a file - that's bad ! + } + } + // try to create it. + try { + if (outFold.mkdirs()) { + FileFunctions.setNonIndexingBit(outFold); + return true; + } + else { + return false; // unable to make the folder. + } + } + catch (Exception e) { + System.err.println("Can't set binary store folder: " + e.getLocalizedMessage()); + return false; + } + + } @Override public JMenuItem createFileMenu(JFrame parentFrame) { JMenuItem m; diff --git a/src/clickDetector/ClickDetector.java b/src/clickDetector/ClickDetector.java index eaca372c..074138d7 100644 --- a/src/clickDetector/ClickDetector.java +++ b/src/clickDetector/ClickDetector.java @@ -374,7 +374,7 @@ public class ClickDetector extends PamProcess { * @see PamguardMVC.PamProcess#SetupProcess() */ @Override - public void setupProcess() { + public synchronized void setupProcess() { pauseDetection = true; diff --git a/src/pamguard/GlobalArguments.java b/src/pamguard/GlobalArguments.java new file mode 100644 index 00000000..c8c6599a --- /dev/null +++ b/src/pamguard/GlobalArguments.java @@ -0,0 +1,33 @@ +package pamguard; + +import java.util.HashMap; + +/** + * Global parameter pairs set at startup time.
+ * These are the arguments passed to the command line.
Basically all going into a static hash map + * @author dg50 + * + */ +public class GlobalArguments { + + static HashMap globalFlags = new HashMap<>(); + + /** + * Set a global parameter value + * @param name value name + * @param value parameter value + */ + public static void setParam(String name, String value) { + globalFlags.put(name, value); + } + + /** + * Get a global parameter value + * @param name value name + * @return value + */ + public static String getParam(String name) { + return globalFlags.get(name); + } + +} diff --git a/src/pamguard/Pamguard.java b/src/pamguard/Pamguard.java index ebc980a0..3c63090e 100644 --- a/src/pamguard/Pamguard.java +++ b/src/pamguard/Pamguard.java @@ -39,6 +39,7 @@ import PamView.FullScreen; import PamView.ScreenSize; import PamView.dialog.warn.WarnOnce; import PamguardMVC.debug.Debug; +import binaryFileStorage.BinaryStore; import dataPlotsFX.JamieDev; import java.io.BufferedReader; import java.io.File; @@ -213,6 +214,14 @@ public class Pamguard { System.out.println("Disabling log file from command line switch..."); ProxyPrintStream.disableLogFile(); } + else if (anArg.equalsIgnoreCase(BinaryStore.GlobalFolderArg)) { + // output folder for binary files. + GlobalArguments.setParam(anArg, args[iArg++]); + } + else if (anArg.equalsIgnoreCase("-databasefile")) { + // database file name + GlobalArguments.setParam(anArg, args[iArg++]); + } else if (anArg.equalsIgnoreCase("-help")) { System.out.println("--PamGuard Help"); System.out.println("\n--For standard GUI deployment run without any options.\n"); From bc7e5b189c384a5dfecd2ec32f2f240a7208b464 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 20 Apr 2022 12:34:50 +0100 Subject: [PATCH 06/37] Update DecimatorParams.java --- src/decimator/DecimatorParams.java | 38 +++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/decimator/DecimatorParams.java b/src/decimator/DecimatorParams.java index 93e934e0..8fe72caf 100644 --- a/src/decimator/DecimatorParams.java +++ b/src/decimator/DecimatorParams.java @@ -41,12 +41,44 @@ public class DecimatorParams implements Serializable, Cloneable, ManagedParamete public int interpolation = 0; - DecimatorParams() { - super(); + /** + * Create decimator params with a nominal output sample rate of 2Kz and + * a 6th order low pass filter + */ + public DecimatorParams() { + this(2000); +// filterParams = new FilterParams(); +// filterParams.filterBand = FilterBand.LOWPASS; +// filterParams.lowPassFreq = newSampleRate / 2; +// filterParams.filterOrder = 6; + } + + /** + * Create decimator parameters with the given output frequency and a + * 6th order low pass filter. + * @param newSampleRate + */ + public DecimatorParams(float newSampleRate) { + this(newSampleRate, 6); +// this.newSampleRate = newSampleRate; +// filterParams = new FilterParams(); +// filterParams.filterBand = FilterBand.LOWPASS; +// filterParams.lowPassFreq = newSampleRate / 2; +// filterParams.filterOrder = 6; + } + + /** + * Create decimator parameters with the given output frequency and a + * low pass filter with given order. + * @param newSampleRate + * @param filterOrder filter order + */ + public DecimatorParams(float newSampleRate, int filterOrder) { + this.newSampleRate = newSampleRate; filterParams = new FilterParams(); filterParams.filterBand = FilterBand.LOWPASS; filterParams.lowPassFreq = newSampleRate / 2; - filterParams.filterOrder = 6; + filterParams.filterOrder = filterOrder; } /* (non-Javadoc) From a62cff4f11bd6dbaeb8cd0fc14a8e65f7d911291 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 22 Apr 2022 16:22:55 +0100 Subject: [PATCH 07/37] couple of updates to support new contact collator plugin --- .classpath | 3 +- .settings/org.eclipse.jdt.core.prefs | 6 ++-- src/PamUtils/PamUtils.java | 2 +- src/PamguardMVC/RawDataTransforms.java | 29 +++++++++++++++---- .../clipDisplay/ClipDisplayUnit.java | 3 ++ 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/.classpath b/.classpath index ba469602..188c7291 100644 --- a/.classpath +++ b/.classpath @@ -6,9 +6,8 @@
- + - diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 41b74bc8..e8c450c0 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,11 +1,11 @@ eclipse.preferences.version=1 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.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.compliance=11 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=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=16 +org.eclipse.jdt.core.compiler.source=11 diff --git a/src/PamUtils/PamUtils.java b/src/PamUtils/PamUtils.java index 0b1971dc..2886e527 100644 --- a/src/PamUtils/PamUtils.java +++ b/src/PamUtils/PamUtils.java @@ -79,7 +79,7 @@ public class PamUtils { * the channel pos is the same as the single channel number. However, if there * are gaps in the channelBitmap, then the channel pos will be < than the * channel Number. - * @param singleChannel + * @param singleChannel single channel number (index, not map) * @param channelBitmap * @return the channel position in the channel list or -1 if it isn't available */ diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index 28ff9364..ed371072 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -543,7 +543,6 @@ public class RawDataTransforms { } } - private double[][] makeFilteredWaveData(FFTFilterParams filterParams) { double[][] waveData = this.rawData.getWaveData(); if (waveData == null || waveData[0].length == 0) { @@ -569,10 +568,6 @@ public class RawDataTransforms { return filteredWaveData; } - - - - // private FFTFilter getFFTFilter(FFTFilterParams filterParams) { // // TODO Auto-generated method stub // return null; @@ -619,9 +614,31 @@ public class RawDataTransforms { } - private double[] getWaveData(int channel) { + /** + * Get the wave data for the given channel. + * @param channel channel index + * @return wave data + */ + public double[] getWaveData(int channel) { return this.rawData.getWaveData()[channel]; } + + /** + * Get the wave data for the given channel in int16 format. + * @param channel channel index + * @return int16 data array. + */ + public short[] getShortWaveData(int channel) { + double[] dData = getWaveData(channel); + if (dData == null) { + return null; + } + short[] shortData = new short[dData.length]; + for (int i = 0; i < shortData.length; i++) { + shortData[i] = (short) (dData[i]*32767); + } + return shortData; + } /** diff --git a/src/clipgenerator/clipDisplay/ClipDisplayUnit.java b/src/clipgenerator/clipDisplay/ClipDisplayUnit.java index 97a2d9bf..2d4d0838 100644 --- a/src/clipgenerator/clipDisplay/ClipDisplayUnit.java +++ b/src/clipgenerator/clipDisplay/ClipDisplayUnit.java @@ -181,6 +181,9 @@ public class ClipDisplayUnit extends PamPanel { return mCol > 128 ? Color.BLACK : Color.WHITE; } private PamSymbol getBorderSymbol() { + if (clipDisplayPanel.getSymbolChooser() == null) { + return null; + } PamSymbol symbol = clipDisplayPanel.getSymbolChooser().getPamSymbol(clipDisplayPanel.getClipDataProjector(), getClipDataUnit()); return symbol; } From 2842143613a3600e36a65b278ff91a58d1f65c2f Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 26 Apr 2022 13:29:50 +0100 Subject: [PATCH 08/37] Sorting out sample rate info in clip display to support Contact Collator plugin --- src/clipgenerator/clipDisplay/ClipDisplayUnit.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clipgenerator/clipDisplay/ClipDisplayUnit.java b/src/clipgenerator/clipDisplay/ClipDisplayUnit.java index 2d4d0838..078d6b78 100644 --- a/src/clipgenerator/clipDisplay/ClipDisplayUnit.java +++ b/src/clipgenerator/clipDisplay/ClipDisplayUnit.java @@ -288,8 +288,8 @@ public class ClipDisplayUnit extends PamPanel { String tStr = PamCalendar.formatTime(clipDataUnit.getTimeMilliseconds(), true); g.drawString(tStr, borderSize, fontAscent); - - String lenString = String.format("%3.2fs", (float)clipDataUnit.getSampleDuration() / clipDisplayPanel.getSampleRate()); + float fs = clipDataUnit.getSourceSampleRate(); // was clipDisplayPanel.getSampleRate() + String lenString = String.format("%3.2fs", (float)clipDataUnit.getSampleDuration() / fs); Rectangle2D strSize = fm.getStringBounds(lenString, g); g.drawString(lenString, (int) (getWidth()-strSize.getWidth()), getHeight()-fm.getDescent()); From cba66b4ec941ea9394181fcf40ee316abc4ff6e0 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 27 Apr 2022 09:14:00 +0100 Subject: [PATCH 09/37] FLAC Speed Improve flac speed --- src/Acquisition/FileInputSystem.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Acquisition/FileInputSystem.java b/src/Acquisition/FileInputSystem.java index 3d0152e5..5fbea317 100644 --- a/src/Acquisition/FileInputSystem.java +++ b/src/Acquisition/FileInputSystem.java @@ -846,7 +846,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe lastProgressUpdate = totalSamples; } - while (newDataUnits.getQueueSize() > 2) { + while (newDataUnits.getQueueSize() > 3*nChannels) { if (dontStop == false) break; try { Thread.sleep(2); @@ -972,7 +972,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe * get it's act together on a timer and use this data * unit, then set it's reference to zero. */ - while (newDataUnits.getQueueSize() > 10) { + while (newDataUnits.getQueueSize() > 3*nChannels) { if (dontStop == false) break; try { Thread.sleep(1); From f8cf8a72cb9fa9cff553755e7d454663f6c97bac Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 27 Apr 2022 09:17:00 +0100 Subject: [PATCH 10/37] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 28c40451..76f04525 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ target/WMM.COF settings.xml .classpath +.classpath From 35617c2571e5b7fbe56616812646a0aef76bf271 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 27 Apr 2022 09:18:28 +0100 Subject: [PATCH 11/37] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 76f04525..e875dc34 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ target/WMM.COF settings.xml .classpath .classpath +.classpath From 50e3e86784be0fc2aff71a926e910f4e25abc38a Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 27 Apr 2022 09:27:46 +0100 Subject: [PATCH 12/37] Updates to support new features in Contact Collator --- src/PamguardMVC/RawDataHolder.java | 2 +- src/PamguardMVC/RawDataUtils.java | 71 +++++++++++++++++++++ src/clipgenerator/ClipBinaryDataSource.java | 71 +++++++++++---------- src/clipgenerator/ClipDataUnit.java | 35 ++++------ 4 files changed, 121 insertions(+), 58 deletions(-) create mode 100644 src/PamguardMVC/RawDataUtils.java diff --git a/src/PamguardMVC/RawDataHolder.java b/src/PamguardMVC/RawDataHolder.java index 4ff0c9a4..b07996e1 100644 --- a/src/PamguardMVC/RawDataHolder.java +++ b/src/PamguardMVC/RawDataHolder.java @@ -19,7 +19,7 @@ public interface RawDataHolder { /** * Get the raw data transforms class. This handles standard data transforms * that are often used in raw data units, e.g. calculating the spectrum, filtering - * waveforms etc. + * waveforms, getting data as an int16 (short) array, etc. * * @return the data transforms object. */ diff --git a/src/PamguardMVC/RawDataUtils.java b/src/PamguardMVC/RawDataUtils.java new file mode 100644 index 00000000..b11d55cb --- /dev/null +++ b/src/PamguardMVC/RawDataUtils.java @@ -0,0 +1,71 @@ +package PamguardMVC; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * A few useful functions for handling raw data which are better off in a simple + * class rather than linked to a data unit, such as the functions in RawDataTransforms.
+ * This was copied from the ClipBinaryStorage class so cannot easily be changed without breaking + * backwards compatibility.
+ * If it must be changed (which will happen one day) then I suggest an id flag as the first int16 which + * is always negative. Then if it's >0 we know it's a channel number in the old format, if <0 a new as + * yet to be defined format.
+ * An obvious future development would be to write x3 or even simple zipped packets. + * @author dg50 + * + */ +public class RawDataUtils { + + /** + * Write the wave clip in scaled int8 format into a data output stream. + * @param dos Data output stream + * @param rawData raw data + * @throws IOException + */ + public void writeWaveClipInt8(DataOutputStream dos, double[][] rawData) throws IOException { + int nChan = rawData.length; + int nSamps = rawData[0].length; + double minVal = 0, maxVal = 0; + for (int iC = 0; iC < nChan; iC++) { + double[] chanData = rawData[iC]; + for (int iS = 0; iS < nSamps; iS++) { + minVal = Math.min(minVal, chanData[iS]); + maxVal = Math.max(maxVal, chanData[iS]); + } + } + maxVal = Math.max(maxVal, -minVal); + float scale = (float) (127./maxVal); + dos.writeShort(nChan); + dos.writeInt(nSamps); + dos.writeFloat(scale); + for (int iC = 0; iC < nChan; iC++) { + double[] chanData = rawData[iC]; + for (int iS = 0; iS < nSamps; iS++) { + dos.writeByte((int) (chanData[iS] * scale)); + } + } + } + + /** + * Read a waveform clip in scaled int8 format from a data input stream + * @param dis data input stream + * @return waveform double array + * @throws IOException + */ + public double[][] readWavClipInt8(DataInputStream dis) throws IOException { + int nChan = dis.readShort(); + int nSamps = dis.readInt(); + double scale = 1./dis.readFloat(); + double[][] rawData = new double[nChan][nSamps]; + for (int iC = 0; iC < nChan; iC++) { + double[] chanData = rawData[iC]; + for (int iS = 0; iS < nSamps; iS++) { + chanData[iS] = (double) dis.readByte() * scale; + } + } + return rawData; + } + +} diff --git a/src/clipgenerator/ClipBinaryDataSource.java b/src/clipgenerator/ClipBinaryDataSource.java index d0e9ea30..7f1a05fe 100644 --- a/src/clipgenerator/ClipBinaryDataSource.java +++ b/src/clipgenerator/ClipBinaryDataSource.java @@ -8,6 +8,7 @@ import java.io.File; import java.io.IOException; import PamguardMVC.PamDataUnit; +import PamguardMVC.RawDataUtils; import binaryFileStorage.BinaryDataSource; import binaryFileStorage.BinaryHeader; import binaryFileStorage.BinaryObjectData; @@ -20,6 +21,8 @@ public class ClipBinaryDataSource extends BinaryDataSource { private ClipControl clipControl; private ClipDisplayDataBlock clipDataBlock; + private RawDataUtils rawDataUtils = new RawDataUtils(); + /** *

Module version changes

* Version 2: Moved start sample, channel bitmap, duration to DataUnitBaseData general data structure
@@ -161,42 +164,44 @@ public class ClipBinaryDataSource extends BinaryDataSource { * @param rawData * @throws IOException */ - private void writeWaveClip(DataOutputStream dos2, double[][] rawData) throws IOException { - int nChan = rawData.length; - int nSamps = rawData[0].length; - double minVal = 0, maxVal = 0; - for (int iC = 0; iC < nChan; iC++) { - double[] chanData = rawData[iC]; - for (int iS = 0; iS < nSamps; iS++) { - minVal = Math.min(minVal, chanData[iS]); - maxVal = Math.max(maxVal, chanData[iS]); - } - } - maxVal = Math.max(maxVal, -minVal); - float scale = (float) (127./maxVal); - dos.writeShort(nChan); - dos.writeInt(nSamps); - dos.writeFloat(scale); - for (int iC = 0; iC < nChan; iC++) { - double[] chanData = rawData[iC]; - for (int iS = 0; iS < nSamps; iS++) { - dos.writeByte((int) (chanData[iS] * scale)); - } - } + private void writeWaveClip(DataOutputStream dos, double[][] rawData) throws IOException { + rawDataUtils.writeWaveClipInt8(dos, rawData); +// int nChan = rawData.length; +// int nSamps = rawData[0].length; +// double minVal = 0, maxVal = 0; +// for (int iC = 0; iC < nChan; iC++) { +// double[] chanData = rawData[iC]; +// for (int iS = 0; iS < nSamps; iS++) { +// minVal = Math.min(minVal, chanData[iS]); +// maxVal = Math.max(maxVal, chanData[iS]); +// } +// } +// maxVal = Math.max(maxVal, -minVal); +// float scale = (float) (127./maxVal); +// dos.writeShort(nChan); +// dos.writeInt(nSamps); +// dos.writeFloat(scale); +// for (int iC = 0; iC < nChan; iC++) { +// double[] chanData = rawData[iC]; +// for (int iS = 0; iS < nSamps; iS++) { +// dos.writeByte((int) (chanData[iS] * scale)); +// } +// } } private double[][] readWavClip(DataInputStream dis) throws IOException { - int nChan = dis.readShort(); - int nSamps = dis.readInt(); - double scale = 1./dis.readFloat(); - double[][] rawData = new double[nChan][nSamps]; - for (int iC = 0; iC < nChan; iC++) { - double[] chanData = rawData[iC]; - for (int iS = 0; iS < nSamps; iS++) { - chanData[iS] = (double) dis.readByte() * scale; - } - } - return rawData; +// int nChan = dis.readShort(); +// int nSamps = dis.readInt(); +// double scale = 1./dis.readFloat(); +// double[][] rawData = new double[nChan][nSamps]; +// for (int iC = 0; iC < nChan; iC++) { +// double[] chanData = rawData[iC]; +// for (int iS = 0; iS < nSamps; iS++) { +// chanData[iS] = (double) dis.readByte() * scale; +// } +// } +// return rawData; + return rawDataUtils.readWavClipInt8(dis); } @Override diff --git a/src/clipgenerator/ClipDataUnit.java b/src/clipgenerator/ClipDataUnit.java index ea67a736..5185cd6e 100644 --- a/src/clipgenerator/ClipDataUnit.java +++ b/src/clipgenerator/ClipDataUnit.java @@ -51,30 +51,6 @@ public class ClipDataUnit extends PamDataUnit imple */ private float sourceSampleRate; -// /** -// * Sample rate data are to be FFT's at for display purposes. -// */ -// private float displaySampleRate; - -// /** -// * Constructor to use when data have gone into a wav file. -// * @param timeMilliseconds -// * @param triggerMilliseconds -// * @param startSample -// * @param durationSamples -// * @param channelMap -// * @param fileName -// * @param triggerName -// */ -// public ClipDataUnit(long timeMilliseconds, long triggerMilliseconds, -// long startSample, int durationSamples, int channelMap, String fileName, -// String triggerName) { -// super(timeMilliseconds, channelMap, startSample, durationSamples); -// this.triggerMilliseconds = triggerMilliseconds; -// this.fileName = fileName; -// this.triggerName = triggerName; -// } - /** * Constructor to use if storing data into the binary system. * @param timeMilliseconds @@ -102,6 +78,17 @@ public class ClipDataUnit extends PamDataUnit imple } private BufferedImage[] clipImages = new BufferedImage[PamConstants.MAX_CHANNELS]; + + /** + * Get an image of the clip + * @param channel + * @param fftLength + * @param fftHop + * @param scaleMin + * @param scaleMax + * @param colorTable + * @return clip image (Swing buffered image) + */ public BufferedImage getClipImage(int channel, int fftLength, int fftHop, double scaleMin, double scaleMax, Color[] colorTable) { double[][] specData = getSpectrogramData(channel, fftLength, fftHop); From a0900a667ee7864989fe6757dfe2900920491645 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 2 May 2022 19:38:14 +0100 Subject: [PATCH 13/37] Small update to RawDatautils to handle null data --- src/PamUtils/PamUtils.java | 2 +- src/PamguardMVC/RawDataUtils.java | 29 ++++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/PamUtils/PamUtils.java b/src/PamUtils/PamUtils.java index 2886e527..85ce33ba 100644 --- a/src/PamUtils/PamUtils.java +++ b/src/PamUtils/PamUtils.java @@ -397,7 +397,7 @@ public class PamUtils { /** * Force an angle to sit within some range. * @param angle input angle (radians) - * @param maxAngle maximum angle in degrees + * @param maxAngle maximum angle in radians * @return output angle (radians) */ static public double constrainedAngleR(double angle, double maxAngle) { diff --git a/src/PamguardMVC/RawDataUtils.java b/src/PamguardMVC/RawDataUtils.java index b11d55cb..3bdf954d 100644 --- a/src/PamguardMVC/RawDataUtils.java +++ b/src/PamguardMVC/RawDataUtils.java @@ -19,14 +19,23 @@ import java.io.IOException; public class RawDataUtils { /** - * Write the wave clip in scaled int8 format into a data output stream. + * Write the wave clip in scaled int8 format into a data output stream. If rawData is null + * or empty, it will still write the header consisting of nChan, nSamp and a scale, but no + * data. * @param dos Data output stream * @param rawData raw data * @throws IOException */ public void writeWaveClipInt8(DataOutputStream dos, double[][] rawData) throws IOException { - int nChan = rawData.length; - int nSamps = rawData[0].length; + int nChan, nSamps; + if (rawData == null || rawData.length == 0 || rawData[0] == null) { + nChan = 0; + nSamps = 0; + } + else { + nChan = rawData.length; + nSamps = rawData[0].length; + } double minVal = 0, maxVal = 0; for (int iC = 0; iC < nChan; iC++) { double[] chanData = rawData[iC]; @@ -36,7 +45,13 @@ public class RawDataUtils { } } maxVal = Math.max(maxVal, -minVal); - float scale = (float) (127./maxVal); + float scale; + if (maxVal == 0) { + scale = 1.f; + } + else { + scale = (float) (127./maxVal); + } dos.writeShort(nChan); dos.writeInt(nSamps); dos.writeFloat(scale); @@ -51,13 +66,17 @@ public class RawDataUtils { /** * Read a waveform clip in scaled int8 format from a data input stream * @param dis data input stream - * @return waveform double array + * @return waveform double array or null if nChan or nSamps was zero (implying an empty array + * was written in the fist place) * @throws IOException */ public double[][] readWavClipInt8(DataInputStream dis) throws IOException { int nChan = dis.readShort(); int nSamps = dis.readInt(); double scale = 1./dis.readFloat(); + if (nChan == 0 || nSamps == 0) { + return null; + } double[][] rawData = new double[nChan][nSamps]; for (int iC = 0; iC < nChan; iC++) { double[] chanData = rawData[iC]; From 8faa33138e0349c648df6cab9fef47fd63f48216 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 22 Apr 2022 16:22:55 +0100 Subject: [PATCH 14/37] couple of updates to support new contact collator plugin --- .classpath | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.classpath b/.classpath index ba469602..188c7291 100644 --- a/.classpath +++ b/.classpath @@ -6,9 +6,8 @@
- + - From 91a8e5fec007d12ba56952b16594b7e9af9994d9 Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Fri, 27 May 2022 06:39:20 -0600 Subject: [PATCH 15/37] Added data selectors to Click Train Detector (#34) * 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 * Updates to FX GUI * 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 * Screenshots * Updates and bug fixes to click train detector and CPOD importer * 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 * Updates and bug fixes to click train detector and CPOD importer * Fix standard classifier JSON logging * Merge fixes to click train detector * 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 * Screenshots * Updates and bug fixes to click train detector and CPOD importer * 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 * Screenshots * Updates and bug fixes to click train detector and CPOD importer * Fix standard classifier JSON logging * 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. * Bug fix to UI * Bug fixes to FX GUI * Updates to click train detector * Squashed commit of the following: commit 9f998165ee95dbb16acaf420876f3aeab75c2158 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon May 2 19:40:24 2022 +0100 Updates to support ContactCollator plugin (#33) * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * fix problem in SummaryComand * Update command line options * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * Update command line options * Update DecimatorParams.java * couple of updates to support new contact collator plugin * Sorting out sample rate info in clip display to support Contact Collator plugin * FLAC Speed Improve flac speed * Update .gitignore * Update .gitignore * Updates to support new features in Contact Collator * Small update to RawDatautils to handle null data * Updates to click train detector * Squashed commit of the following: commit 62b020b3204aa56189b1c2da88bcbb9f49140936 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sat May 14 06:52:20 2022 +0100 Add a new offlinefileslist function commit 3a9a5311aa529b66340f6ae322f88905b911947a Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed Apr 27 09:43:31 2022 +0100 Update .gitignore commit 9f998165ee95dbb16acaf420876f3aeab75c2158 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon May 2 19:40:24 2022 +0100 Updates to support ContactCollator plugin (#33) * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * fix problem in SummaryComand * Update command line options * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * Update command line options * Update DecimatorParams.java * couple of updates to support new contact collator plugin * Sorting out sample rate info in clip display to support Contact Collator plugin * FLAC Speed Improve flac speed * Update .gitignore * Update .gitignore * Updates to support new features in Contact Collator * Small update to RawDatautils to handle null data * Updated data selector for click train detector * Click train detector updates and bug fixes Fixed very annoying bug which meant templates did not show properly when the dialog was first opened in swing. Added feature to simple classifier were % of clicks of one click classification (or other data selector paramter) can be used to classify. Co-authored-by: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> --- .gitignore | 1 - .../layoutFX/CheckWavHeadersPane.java | 32 +- src/Acquisition/layoutFX/FolderInputPane.java | 1 + src/PamguardMVC/RawDataTransforms.java | 298 ++++++++---------- src/Resources/css/pamSettingsCSS.css | 36 +-- .../dataSelector/ClickSelectPaneFX.java | 5 +- .../classification/CTClassifierManager.java | 29 +- .../classification/CTClassifierParams.java | 14 +- .../BearingClassifierParams.java | 1 + .../idiClassifier/IDIClassifierParams.java | 6 + .../Chi2CTClassification.java | 9 +- .../Chi2ThresholdClassifier.java | 70 +++- .../Chi2ThresholdParams.java | 16 +- .../StandardClassifier.java | 8 +- .../StandardClassifierParams.java | 3 + .../TemplateClassifierParams.java | 1 + .../mht/mhtvar/AmplitudeChi2.java | 5 +- .../dataselector/CTDataSelector.java | 35 ++ .../dataselector/CTSelectParams.java | 12 + src/clickTrainDetector/layout/CTSwingGUI.java | 2 +- .../layout/ClickTrainAlgorithmPaneFX.java | 8 +- .../layout/PreClassifierPane.java | 2 +- .../SimpleCTClassifierGraphics.java | 10 +- .../SimpleCTClassifierPane.java | 123 +++++++- .../TemplateClassifierPane.java | 6 + .../TemplateSpectrumPane.java | 9 +- .../dataselector/CTDataSelectPanel.java | 149 ++++++++- .../layout/mht/MHTSettingsPane.java | 10 +- .../layout/DetectionPlotDisplay.java | 5 + src/detectionPlotFX/plots/SpectrumPlot.java | 2 +- .../fxNodes/pamAxis/PamAxisPane2.java | 2 +- .../fxNodes/utilityPanes/MinMaxPane.java | 1 - .../layoutFX/SpectrogramNoisePaneFX.java | 15 +- 33 files changed, 667 insertions(+), 259 deletions(-) diff --git a/.gitignore b/.gitignore index c9419840..6df976a9 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,3 @@ settings.xml .classpath .classpath .classpath -.classpath diff --git a/src/Acquisition/layoutFX/CheckWavHeadersPane.java b/src/Acquisition/layoutFX/CheckWavHeadersPane.java index 83607217..60638bae 100644 --- a/src/Acquisition/layoutFX/CheckWavHeadersPane.java +++ b/src/Acquisition/layoutFX/CheckWavHeadersPane.java @@ -7,6 +7,7 @@ import Acquisition.FolderInputSystem; import Acquisition.WavFileFuncs; import PamUtils.PamAudioFileFilter; import javafx.geometry.Insets; +import javafx.application.Platform; import javafx.beans.property.SimpleDoubleProperty; import javafx.concurrent.Task; import javafx.geometry.Pos; @@ -89,6 +90,8 @@ public class CheckWavHeadersPane extends PamBorderPane { */ private SimpleDoubleProperty progressProperty = new SimpleDoubleProperty(0); + private PamButton runButton; + /** * Constructor for the CheckWavHeadersPane * @param folderInputSystem - the folder input system. @@ -102,20 +105,19 @@ public class CheckWavHeadersPane extends PamBorderPane { this.folderInputSystem=folderInputSystem; folderName = new Label(" "); - PamGuiManagerFX.titleFont2style(folderName); + //PamGuiManagerFX.titleFont2style(folderName); textArea = new TextArea(); textArea.setEditable(false); ScrollPane scrollPane = new ScrollPane(textArea); scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER); //scrollPane.setPrefSize(322, 300); - this.setCenter(scrollPane); PamHBox pamHBox = new PamHBox(); pamHBox.setAlignment(Pos.CENTER_LEFT); pamHBox.setSpacing(5); - PamButton runButton = new PamButton(); + runButton = new PamButton(); runButton.setGraphic(PamGlyphDude.createPamIcon("mdi2p-play")); runButton.setOnAction((action)->{ checkFiles(); @@ -128,15 +130,15 @@ public class CheckWavHeadersPane extends PamBorderPane { pamHBox.getChildren().addAll(runButton, progressBar); progressBar.setMaxWidth(Double.MAX_VALUE); - mainPane.getChildren().addAll(folderName, textArea, pamHBox); - - this.setPadding(new Insets(5,0,5,15)); + this.setCenter(mainPane); + + this.setPadding(new Insets(5,0,5,15)); } - private void setParams() { + void setParams() { running = ran = false; subFolders = folderInputSystem.getFolderInputParameters().subFolders; if (subFolders) { @@ -149,7 +151,7 @@ public class CheckWavHeadersPane extends PamBorderPane { textArea.setText(" "); allFiles.clear(); nFiles = countFiles(folder); - progressBar.setProgress(0); + progressProperty.setValue(0); progressBar.progressProperty().bind(progressProperty); //progressBar.setMaximum(Math.max(nFiles, 1)); enableControls(); @@ -213,6 +215,10 @@ public class CheckWavHeadersPane extends PamBorderPane { nErrors = 0; enableControls(); checkFilesWorker = new CheckFiles(); + + Thread th = new Thread(checkFilesWorker); + th.setDaemon(true); + th.start(); } @@ -245,10 +251,16 @@ public class CheckWavHeadersPane extends PamBorderPane { * for each file, report on progress with it's name and * whether or not it had an error */ - int error; File aFile; + System.out.println("Analaysing files: Start: " + allFiles.size() ); for (int i = 0; i < allFiles.size(); i++) { - error = checkFile(aFile = allFiles.get(i)); + System.out.println("Analaysing files: " + i); + final int error = checkFile(aFile = allFiles.get(i)); + final File aFile1 = aFile; + Platform.runLater(()->{ + textArea.appendText(String.format("\n File %s %s" , aFile1.getName() , + error == AudioFileFuncs.FILE_OK ? "OK" : ("Error " + error))); + }); //progressBar.progressProperty().bind(null); progressProperty.setValue(100*i/(double) allFiles.size()); } diff --git a/src/Acquisition/layoutFX/FolderInputPane.java b/src/Acquisition/layoutFX/FolderInputPane.java index 963890a5..b6602e92 100644 --- a/src/Acquisition/layoutFX/FolderInputPane.java +++ b/src/Acquisition/layoutFX/FolderInputPane.java @@ -304,6 +304,7 @@ public class FolderInputPane extends DAQSettingsPane{ wavFix.setOnAction((action)->{ acquisitionPaneFX.getAdvancedLabel().setText("Fix Wave Files"); acquisitionPaneFX.getAdvancedPane().setCenter(this.fixWavPane); + fixWavPane.setParams(); acquisitionPaneFX.getFlipPane().flipToBack(); }); diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index ed371072..c0684730 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -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; - } } private double[][] makeFilteredWaveData(FFTFilterParams filterParams) { @@ -579,15 +533,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; } @@ -654,9 +606,7 @@ public class RawDataTransforms { * @return */ private int getNChan() { - synchronized (synchObject) { // new - return this.rawData.getWaveData().length; - } + return this.rawData.getWaveData().length; } /** @@ -689,11 +639,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; } diff --git a/src/Resources/css/pamSettingsCSS.css b/src/Resources/css/pamSettingsCSS.css index 605da31c..d635ea5b 100644 --- a/src/Resources/css/pamSettingsCSS.css +++ b/src/Resources/css/pamSettingsCSS.css @@ -35,8 +35,8 @@ -fx-font-color: -fx-text; -fx-font-family: "Ubuntu"; - -fx-border-radius: 5 5 5 5; - -fx-background-radius: 5 5 5 5; + -fx-border-radius: 5 5 5 5; + -fx-background-radius: 5 5 5 5; -icons-color: -fx-icon_col; } @@ -615,7 +615,7 @@ -fx-background: -fx-darkbackground; -fx-background-color: -fx-darkbackground; -fx-border-radius: 0 0 0 0; - -fx-padding: 7 0 7 0; + -fx-padding: 5 0 5 0; -fx-border-color: transparent; } @@ -623,9 +623,9 @@ -fx-text-fill: white; -fx-background-color: -fx-darkbackground; -fx-border-color: -fx-border_col; - -fx-background-radius: 6 6 0 0; - -fx-border-radius: 6 6 0 0; - -fx-padding: 7 0 7 0; + -fx-background-radius: 5 5 0 0; + -fx-border-radius: 5 5 0 0; + -fx-padding: 5 0 5 0; } .spinner .increment-arrow { @@ -635,16 +635,16 @@ .spinner .increment-arrow-button:hover { -fx-background-color: -fx-highlight; -fx-border-color: -fx-highlight_border; - -fx-background-radius: 6 6 0 0; - -fx-border-radius: 6 6 0 0; + -fx-background-radius: 5 5 0 0; + -fx-border-radius: 5 5 0 0; } .spinner .decrement-arrow-button { -fx-text-fill: white; -fx-background-color: -fx-darkbackground; -fx-border-color: -fx-border_col; - -fx-background-radius: 0 0 6 6; - -fx-border-radius: 0 0 6 6 + -fx-background-radius: 0 0 5 5; + -fx-border-radius: 0 0 5 5 } .spinner .decrement-arrow { @@ -654,8 +654,8 @@ .spinner .decrement-arrow-button:hover { -fx-background-color: -fx-highlight; -fx-border-color: -fx-highlight_border; - -fx-background-radius: 0 0 6 6; - -fx-border-radius: 0 0 6 6; + -fx-background-radius: 0 0 5 5; + -fx-border-radius: 0 0 5 5; } /*Arrows are horizontal either side of text box*/ @@ -663,7 +663,7 @@ -fx-text-fill: white; -fx-background-color: -fx-darkbackground; -fx-border-color: -fx-border_col; - -fx-border-radius: 0 6 6 0 + -fx-border-radius: 0 5 5 0 } @@ -671,22 +671,22 @@ -fx-text-fill: white; -fx-background-color: -fx-darkbackground; -fx-border-color: -fx-border_col; - -fx-border-radius: 6 0 0 6 + -fx-border-radius: 5 0 0 5 } .spinner.split-arrows-horizontal .increment-arrow-button:hover { -fx-background-color: -fx-highlight; -fx-border-color: -fx-highlight_border; - -fx-background-radius: 0 6 6 0; - -fx-border-radius: 0 6 6 0; + -fx-background-radius: 0 5 5 0; + -fx-border-radius: 0 5 5 0; } .spinner.split-arrows-horizontal .decrement-arrow-button:hover { -fx-background-color: -fx-highlight; -fx-border-color: -fx-highlight_border; - -fx-background-radius: 6 0 0 6; - -fx-border-radius: 6 0 0 6; + -fx-background-radius: 5 0 0 5; + -fx-border-radius: 5 0 0 5; } .spinner.split-arrows-horizontal .text-field { diff --git a/src/clickDetector/dataSelector/ClickSelectPaneFX.java b/src/clickDetector/dataSelector/ClickSelectPaneFX.java index 743fa946..0c1d661d 100644 --- a/src/clickDetector/dataSelector/ClickSelectPaneFX.java +++ b/src/clickDetector/dataSelector/ClickSelectPaneFX.java @@ -217,7 +217,8 @@ public class ClickSelectPaneFX extends DynamicSettingsPane { void setParams() { ClickAlarmParameters clickAlarmParameters = clickDataSelector.getClickAlarmParameters(); speciesSelect.getChildren().clear(); - + + //species pane setup species = null; weights = null; @@ -350,6 +351,7 @@ public class ClickSelectPaneFX extends DynamicSettingsPane { } boolean getParams() { + ClickAlarmParameters clickAlarmParameters = clickDataSelector.getClickAlarmParameters().clone(); clickAlarmParameters.useEchoes = useEchoes.isSelected(); @@ -397,6 +399,7 @@ public class ClickSelectPaneFX extends DynamicSettingsPane { // btDisplayParameters.showANDEvents = andEvents.isSelected(); // btDisplayParameters.showEventsOnly = onlyEvents.isSelected(); + clickDataSelector.setClickAlarmParameters(clickAlarmParameters); return true; } diff --git a/src/clickTrainDetector/classification/CTClassifierManager.java b/src/clickTrainDetector/classification/CTClassifierManager.java index 1c0af211..2389d8ab 100644 --- a/src/clickTrainDetector/classification/CTClassifierManager.java +++ b/src/clickTrainDetector/classification/CTClassifierManager.java @@ -1,9 +1,8 @@ package clickTrainDetector.classification; import java.util.ArrayList; +import java.util.UUID; -import PamUtils.PamCalendar; -import PamguardMVC.debug.Debug; import clickTrainDetector.CTDataUnit; import clickTrainDetector.ClickTrainControl; import clickTrainDetector.classification.bearingClassifier.BearingClassification; @@ -158,10 +157,10 @@ public class CTClassifierManager { //first check the pre-classifier Chi2CTClassification classification = this.preClassifier.classifyClickTrain(ctDataUnit); - System.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()); + //System.out.println("No SPECIES: chi^2" + ctDataUnit.getCTChi2()); ctDataUnit.setJunkTrain(true); //no need to do any more classification- the click train has been flagged for deletion. ctDataUnit.clearClassifiers(); @@ -189,17 +188,17 @@ public class CTClassifierManager { ctDataUnit.clearClassifiers(); ctDataUnit.setClassificationIndex(-1); - System.out.println("Classify species: Num classifier " + this.cTClassifiers.size()); + //System.out.println("Classify species: Num classifier " + this.cTClassifiers.size()); for (int i=0; iCTClassifier.NOSPECIES && !hasBeenClssfd) { - System.out.println("Set classiifcation index: " + i); + //System.out.println("Set classiifcation index: " + i); ctDataUnit.setClassificationIndex(i); //set the classification index. hasBeenClssfd = true; } @@ -238,12 +237,26 @@ public class CTClassifierManager { System.err.println("CTCLassifier manager: the classifier is null"); continue; } + + if (ctParams[i].uniqueID ==null) { + //old versions may not have a unique ID so needs to be added. + ctParams[i].uniqueID = UUID.randomUUID().toString(); + } aClassifier.setParams(ctParams[i]); cTClassifiers.add(aClassifier); } } + /** + * Get the pre-classifer. This is an intial very broad classifier that is used to determine + * whether a click train should be saved or dumped from memory. + * @return the pre-classifier. + */ + public Chi2ThresholdClassifier getPreClassifier() { + return preClassifier; + } + diff --git a/src/clickTrainDetector/classification/CTClassifierParams.java b/src/clickTrainDetector/classification/CTClassifierParams.java index 81af2483..2841b1ab 100644 --- a/src/clickTrainDetector/classification/CTClassifierParams.java +++ b/src/clickTrainDetector/classification/CTClassifierParams.java @@ -1,6 +1,7 @@ package clickTrainDetector.classification; import java.io.Serializable; +import java.util.UUID; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; @@ -20,12 +21,23 @@ public class CTClassifierParams implements Cloneable, Serializable, ManagedParam private static final long serialVersionUID = 1L; + public CTClassifierParams() { + this.uniqueID = UUID.randomUUID().toString(); + } + + /** - * A very simple species flag to indicate what classifier was used. 0 means not classified. + * The name of the classifier. */ public String classifierName = ""; + /** + * A unique ID for the classifier that never changes. This is important for accessing data selectors. + */ + public String uniqueID; + + /** * A very simple species flag to indicate what classifier was used. 0 means not classified. */ diff --git a/src/clickTrainDetector/classification/bearingClassifier/BearingClassifierParams.java b/src/clickTrainDetector/classification/bearingClassifier/BearingClassifierParams.java index 0bff0c6f..08031d6b 100644 --- a/src/clickTrainDetector/classification/bearingClassifier/BearingClassifierParams.java +++ b/src/clickTrainDetector/classification/bearingClassifier/BearingClassifierParams.java @@ -15,6 +15,7 @@ public class BearingClassifierParams extends CTClassifierParams implements Manag public BearingClassifierParams(){ + super(); type = CTClassifierType.BEARINGCLASSIFIER; } diff --git a/src/clickTrainDetector/classification/idiClassifier/IDIClassifierParams.java b/src/clickTrainDetector/classification/idiClassifier/IDIClassifierParams.java index a2bca3cb..4862c3dc 100644 --- a/src/clickTrainDetector/classification/idiClassifier/IDIClassifierParams.java +++ b/src/clickTrainDetector/classification/idiClassifier/IDIClassifierParams.java @@ -4,9 +4,15 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import clickTrainDetector.classification.CTClassifierParams; +import clickTrainDetector.classification.CTClassifierType; public class IDIClassifierParams extends CTClassifierParams implements Serializable, Cloneable, ManagedParameters { + public IDIClassifierParams(){ + super(); + type = CTClassifierType.IDICLASSIFIER; + } + /** * */ diff --git a/src/clickTrainDetector/classification/simplechi2classifier/Chi2CTClassification.java b/src/clickTrainDetector/classification/simplechi2classifier/Chi2CTClassification.java index 22ef818e..c3c667a4 100644 --- a/src/clickTrainDetector/classification/simplechi2classifier/Chi2CTClassification.java +++ b/src/clickTrainDetector/classification/simplechi2classifier/Chi2CTClassification.java @@ -1,5 +1,7 @@ package clickTrainDetector.classification.simplechi2classifier; +import PamguardMVC.PamDataBlock; +import PamguardMVC.dataSelector.DataSelector; import clickTrainDetector.classification.CTClassification; import clickTrainDetector.classification.CTClassifierType; import clickTrainDetector.classification.ClassifierJSONLogging; @@ -22,15 +24,14 @@ public class Chi2CTClassification implements CTClassification { private int speciesCode = -1; - private SimpleClassifierJSONLogging simpleClassifierJSONLogging; + private SimpleClassifierJSONLogging simpleClassifierJSONLogging; - public Chi2CTClassification(int speciesCode) { this.speciesCode = speciesCode; simpleClassifierJSONLogging=new SimpleClassifierJSONLogging(); } - + /** * Create the classification from a JSON string @@ -62,5 +63,7 @@ public class Chi2CTClassification implements CTClassification { public ClassifierJSONLogging getJSONLogging() { return simpleClassifierJSONLogging; } + + } diff --git a/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdClassifier.java b/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdClassifier.java index 9577e5a9..20473840 100644 --- a/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdClassifier.java +++ b/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdClassifier.java @@ -1,6 +1,8 @@ package clickTrainDetector.classification.simplechi2classifier; import PamUtils.PamCalendar; +import PamguardMVC.PamDataBlock; +import PamguardMVC.dataSelector.DataSelector; import PamguardMVC.debug.Debug; import clickTrainDetector.CTDataUnit; import clickTrainDetector.ClickTrainControl; @@ -32,10 +34,17 @@ public class Chi2ThresholdClassifier implements CTClassifier { */ private ClickTrainControl clickTrainControl; + + /** + * Data selector for the chi2 threshold classifier. + */ + private DataSelector dataSelector; + public Chi2ThresholdClassifier(ClickTrainControl clickTrainControl, int defaultSpeciesID) { this.clickTrainControl=clickTrainControl; clssfrParams.speciesFlag=defaultSpeciesID; + createDataSelector(clickTrainControl.getParentDataBlock()); } @@ -65,8 +74,62 @@ public class Chi2ThresholdClassifier implements CTClassifier { return new Chi2CTClassification(CTClassifier.NOSPECIES); //no classification } + if (!isPercClicks(clickTrain)) { +// Debug.out.println("Failed on chi2Threshold"); + return new Chi2CTClassification(CTClassifier.NOSPECIES); //no classification + } + + return new Chi2CTClassification(clssfrParams.speciesFlag); } + + + /** + * Check the percentage of clicks which are correctly classified by the data selector. + * @param clickTrain - the click train + * @return true if the percentage critera is passed + * + */ + private boolean isPercClicks(CTDataUnit clickTrain) { + + //no point iterating through click if we do not need to. + if (clssfrParams.minPercentage==0) return true; + + double count = 0; + for (int i=0; i0) { + count = count+1.; + } + } + if (count/clickTrain.getSubDetectionsCount()>=clssfrParams.minPercentage) { + return true; + } + else { + return false; + } + } + + + /** + * Get the data selector. + * @param source - the source data block + * @return the data selector. + */ + public void createDataSelector(PamDataBlock source) { + if (dataSelector==null || dataSelector.getPamDataBlock()!=source) { + //create the data selector + //System.out.println("Data selector: " + dataSelector); + if (source!=null) { + dataSelector=source.getDataSelectCreator().getDataSelector(clickTrainControl.getUnitName() + " " + clssfrParams.uniqueID + + "_X2_threshold_classifier", false, null); + //System.out.println("Data selector: " + dataSelector); + } + else { + dataSelector=null; + } + } + } + @Override public String getName() { @@ -122,7 +185,6 @@ public class Chi2ThresholdClassifier implements CTClassifier { public void setParams(Chi2ThresholdParams clssfrParams) { //System.out.println("HELLO CLASSIFIER PARAMS: " + clssfrParams.chi2Threahold ); this.clssfrParams=clssfrParams; - } /** @@ -145,4 +207,10 @@ public class Chi2ThresholdClassifier implements CTClassifier { } + public DataSelector getDataSelector() { + createDataSelector(clickTrainControl.getParentDataBlock()); + return dataSelector; + } + + } diff --git a/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdParams.java b/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdParams.java index 83f4d6e5..a174a915 100644 --- a/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdParams.java +++ b/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdParams.java @@ -1,5 +1,8 @@ package clickTrainDetector.classification.simplechi2classifier; +import java.io.Serializable; +import java.util.UUID; + import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import clickTrainDetector.classification.CTClassifierParams; @@ -12,9 +15,10 @@ import clickTrainDetector.classification.CTClassifierType; * @author Jamie Macaulay * */ -public class Chi2ThresholdParams extends CTClassifierParams implements ManagedParameters { +public class Chi2ThresholdParams extends CTClassifierParams implements ManagedParameters, Serializable, Cloneable { public Chi2ThresholdParams(){ + super(); super.type=CTClassifierType.CHI2THRESHOLD; } @@ -25,7 +29,7 @@ public class Chi2ThresholdParams extends CTClassifierParams implements ManagedPa /** * The chi2 threshold to set. This is the chi2 value divided by the number of clicks in the train. - * If zero then the classification always passes...A bit fo a hack for testing + * If zero then the classification always passes...A bit of a hack for testing */ public double chi2Threshold = 1500.; @@ -33,7 +37,13 @@ public class Chi2ThresholdParams extends CTClassifierParams implements ManagedPa * The minimum number of clicks. */ public int minClicks = 5; - + + /** + * The minimum %percentage which must the parent data selector of clicks/ + *. values are 0-1. + */ + public double minPercentage = 0; + /** * The minimum time in seconds. */ diff --git a/src/clickTrainDetector/classification/standardClassifier/StandardClassifier.java b/src/clickTrainDetector/classification/standardClassifier/StandardClassifier.java index f340d9d3..eef29887 100644 --- a/src/clickTrainDetector/classification/standardClassifier/StandardClassifier.java +++ b/src/clickTrainDetector/classification/standardClassifier/StandardClassifier.java @@ -130,7 +130,7 @@ public class StandardClassifier implements CTClassifier { int speciesID = standardClssfrParams.speciesFlag; - System.out.println("Standard Classificiation: " ); + //System.out.println("Standard Classificiation: " ); //all classifiers have to pass. CTClassification[] ctClassification = new CTClassification[classifiers.size()]; @@ -138,8 +138,8 @@ public class StandardClassifier implements CTClassifier { 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]); +// 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){ @@ -148,7 +148,7 @@ public class StandardClassifier implements CTClassifier { } } - System.out.println("SPECIES ID: " + speciesID); + //System.out.println("SPECIES ID: " + speciesID); //create the classification. StandardClassification classification = new StandardClassification(ctClassification, speciesID); diff --git a/src/clickTrainDetector/classification/standardClassifier/StandardClassifierParams.java b/src/clickTrainDetector/classification/standardClassifier/StandardClassifierParams.java index a3d13e0e..25eb5775 100644 --- a/src/clickTrainDetector/classification/standardClassifier/StandardClassifierParams.java +++ b/src/clickTrainDetector/classification/standardClassifier/StandardClassifierParams.java @@ -1,5 +1,7 @@ package clickTrainDetector.classification.standardClassifier; +import java.util.UUID; + import clickTrainDetector.classification.CTClassifierParams; import clickTrainDetector.classification.CTClassifierType; @@ -27,6 +29,7 @@ public class StandardClassifierParams extends CTClassifierParams { public StandardClassifierParams(){ + super(); ///very important to set this or else the clasifier manager does not //know which classifier to create. type = CTClassifierType.STANDARDCLASSIFIER; diff --git a/src/clickTrainDetector/classification/templateClassifier/TemplateClassifierParams.java b/src/clickTrainDetector/classification/templateClassifier/TemplateClassifierParams.java index cd781ca7..e4d15845 100644 --- a/src/clickTrainDetector/classification/templateClassifier/TemplateClassifierParams.java +++ b/src/clickTrainDetector/classification/templateClassifier/TemplateClassifierParams.java @@ -22,6 +22,7 @@ public class TemplateClassifierParams extends CTClassifierParams implements Mana private static final long serialVersionUID = 10L; public TemplateClassifierParams(){ + super(); super.type=CTClassifierType.TEMPLATECLASSIFIER; // chi2ThresholdParams = new Chi2ThresholdParams(); // template = DefualtSpectrumTemplates.getTemplate(SpectrumTemplateType.BEAKED_WHALE); diff --git a/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/AmplitudeChi2.java b/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/AmplitudeChi2.java index 68f45902..cff7d705 100644 --- a/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/AmplitudeChi2.java +++ b/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/AmplitudeChi2.java @@ -39,7 +39,10 @@ public class AmplitudeChi2 extends SimpleChi2VarDelta { @Override public double getDiffValue(PamDataUnit pamDataUnit0, PamDataUnit pamDataUnit1) { //System.out.println("DB: " + pamDataUnit0.getAmplitudeDB()); - return pamDataUnit0.getAmplitudeDB()-pamDataUnit1.getAmplitudeDB(); + //made this abs so it can deal with increasing then decreasing click trains. i.e. + //the click trian is not penalised if it gradually increasing then starts to gradually decrease + //in amplitude. + return Math.abs(pamDataUnit0.getAmplitudeDB()-pamDataUnit1.getAmplitudeDB()); } @Override diff --git a/src/clickTrainDetector/dataselector/CTDataSelector.java b/src/clickTrainDetector/dataselector/CTDataSelector.java index d607c87d..97e14554 100644 --- a/src/clickTrainDetector/dataselector/CTDataSelector.java +++ b/src/clickTrainDetector/dataselector/CTDataSelector.java @@ -1,9 +1,12 @@ package clickTrainDetector.dataselector; +import java.util.Arrays; + import PamDetection.LocContents; import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelectParams; import PamguardMVC.dataSelector.DataSelector; +import clickTrainDetector.CTDataUnit; import clickTrainDetector.CTDetectionGroupDataUnit; import clickTrainDetector.ClickTrainControl; import clickTrainDetector.ClickTrainDataBlock; @@ -43,6 +46,8 @@ public class CTDataSelector extends DataSelector { private boolean allowScores; + private boolean[] useSpeciesList; + public CTDataSelector(ClickTrainControl clickTrainControl, ClickTrainDataBlock clickTrainDataBlock, String selectorName, boolean allowScores) { super(clickTrainDataBlock, selectorName, allowScores); @@ -83,6 +88,7 @@ public class CTDataSelector extends DataSelector { @Override public DataSelectParams getParams() { + getDialogPanel().getParams(ctSelectParams); return ctSelectParams; } @@ -99,6 +105,8 @@ public class CTDataSelector extends DataSelector { CTDetectionGroupDataUnit ctDataUnit = (CTDetectionGroupDataUnit) pamDataUnit; if (ctDataUnit.getSubDetectionsCount()(parentFrame, setPane, false); } - if (classificationTab!=null) settingsPane.setTab(classificationTab ? 2 : 0); //set the tab to the classification tab + if (classificationTab!=null && classificationTab) settingsPane.setTab(2); //set the tab to the classification tab ClickTrainParams newParams = settingsDialog.showDialog(this.clickTrainControl.getClickTrainParams()); diff --git a/src/clickTrainDetector/layout/ClickTrainAlgorithmPaneFX.java b/src/clickTrainDetector/layout/ClickTrainAlgorithmPaneFX.java index f31c03eb..50af611e 100644 --- a/src/clickTrainDetector/layout/ClickTrainAlgorithmPaneFX.java +++ b/src/clickTrainDetector/layout/ClickTrainAlgorithmPaneFX.java @@ -324,7 +324,13 @@ public class ClickTrainAlgorithmPaneFX extends SettingsPane { popOver.showingProperty().addListener((obs, old, newval)->{ if (newval) { - clickTrainControl.getDataSelector().getDialogPaneFX().setParams(true); + //the dialog has opened + clickTrainControl.getDataSelector().getDialogPaneFX().setParams(true); + } + else { + //the dialog has closed + clickTrainControl.getDataSelector().getDialogPaneFX().getParams(true); + } }); diff --git a/src/clickTrainDetector/layout/PreClassifierPane.java b/src/clickTrainDetector/layout/PreClassifierPane.java index 3344ed93..9abca195 100644 --- a/src/clickTrainDetector/layout/PreClassifierPane.java +++ b/src/clickTrainDetector/layout/PreClassifierPane.java @@ -34,7 +34,7 @@ public class PreClassifierPane extends PamBorderPane { private Pane createClassifierPane() { - simpleCTClassifierPane = new SimpleCTClassifierPane(null); + simpleCTClassifierPane = new SimpleCTClassifierPane(clickTrainControl.getClassifierManager().getPreClassifier()); return (Pane) simpleCTClassifierPane.getContentNode(); } diff --git a/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierGraphics.java b/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierGraphics.java index 1e5f9c7e..8a27c478 100644 --- a/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierGraphics.java +++ b/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierGraphics.java @@ -35,14 +35,15 @@ public class SimpleCTClassifierGraphics implements CTClassifierGraphics { if (simpleCTClassiferPane==null) { simpleCTClassiferPane = new SimpleCTClassifierPane(simpleChi2Classifier); } - //params are set here. - simpleCTClassiferPane.setParams(simpleChi2Classifier.getParams()); + //System.out.println("SimpleCTClassifierGraphics getCTClassifierPane: " + simpleChi2Classifier.getParams().chi2Threshold); + //params are set here. <- do not do this because you may wish to store the classifier params somewhere else. + //simpleCTClassiferPane.setParams(simpleChi2Classifier.getParams()); return (Pane) simpleCTClassiferPane.getContentNode(); } @Override public CTClassifierParams getParams() { - Chi2ThresholdParams clssfrParams = simpleCTClassiferPane.getParams(simpleChi2Classifier.getParams()); + Chi2ThresholdParams clssfrParams = simpleCTClassiferPane.getParams(simpleChi2Classifier.getParams()).clone(); if (clssfrParams==null) { System.err.print("Simple Chi2 Classifier returned null params"); return null; @@ -51,13 +52,14 @@ public class SimpleCTClassifierGraphics implements CTClassifierGraphics { // simpleChi2Classifier.setParams(clssfrParams); // return clssfrParams; // } + //System.out.println("SimpleCTClassifierGraphics - getParams: " + clssfrParams.chi2Threshold); return clssfrParams; } @Override public void setParams(CTClassifierParams params) { simpleCTClassiferPane.setParams((Chi2ThresholdParams) params); - + //System.out.println("SimpleCTClassifierGraphics - setParams: " + ((Chi2ThresholdParams) params).chi2Threshold); } } diff --git a/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierPane.java b/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierPane.java index f7999529..d11e4c63 100644 --- a/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierPane.java +++ b/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierPane.java @@ -1,18 +1,32 @@ package clickTrainDetector.layout.classification.simplechi2classifier; +import java.text.DecimalFormat; + +import org.controlsfx.control.PopOver; + import PamController.SettingsPane; +import PamguardMVC.PamDataBlock; import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdClassifier; import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdParams; import javafx.geometry.Insets; import javafx.geometry.Orientation; +import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.CheckBox; +import javafx.scene.control.Control; import javafx.scene.control.Spinner; +import javafx.scene.control.SpinnerValueFactory; import javafx.scene.control.Tooltip; import javafx.scene.layout.Pane; +import javafx.util.StringConverter; +import pamViewFX.PamGuiManagerFX; +import pamViewFX.fxGlyphs.PamGlyphDude; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamSpinner; import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; import pamViewFX.fxNodes.utilsFX.ControlField; /** @@ -51,6 +65,17 @@ public class SimpleCTClassifierPane extends SettingsPane { */ private ControlField minTime; + private PamToggleSwitch dataSelectorCheckBox; + + private PamButton dataSelectorButton; + + private PopOver popOver; + + /** + * The minimum percentage of clicks for a certain class. + */ + private ControlField minPercClicks; + public SimpleCTClassifierPane(Chi2ThresholdClassifier simpleChi2Classifier) { super(null); this.simpleChi2Classifier=simpleChi2Classifier; @@ -96,17 +121,35 @@ public class SimpleCTClassifierPane extends SettingsPane { chi2Threshold.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(0)); chi2Threshold.setTooltip(new Tooltip( "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" + + "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("Min. Clicks ", "", 0, Integer.MAX_VALUE, 5); + minClicks = new ControlField("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); + + + minPercClicks = new ControlField("Min. % clicks ", "", 0, 100., 1.); + minPercClicks.setTooltip(new Tooltip( + "The minimum number of detections.")); + minPercClicks.getSpinner().setEditable(true); + SpinnerValueFactory valueFactory = new SpinnerValueFactory.DoubleSpinnerValueFactory(0, 100., 0., 1.); + valueFactory.setConverter(doubleConverter); + minPercClicks.getSpinner().setValueFactory(valueFactory); + minPercClicks.getSpinner().setEditable(true); + ///HACK to get the percentage sign to show? + minPercClicks.getSpinner().increment(); + minPercClicks.getSpinner().decrement(); - minTime = new ControlField("Min. Time ", "s", 0.0, Double.MAX_VALUE, 1.0); + minPercClicks.getLabel1().setPrefWidth(LABEL_WIDTH); + + minPercClicks.getChildren().add( createDataSelectorPane()); + + + minTime = new ControlField("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.")); @@ -115,29 +158,83 @@ public class SimpleCTClassifierPane extends SettingsPane { chi2Threshold.getSpinner().setEditable(true); - vBox.getChildren().addAll(chi2Threshold, minClicks, minTime); + vBox.getChildren().addAll(chi2Threshold, minClicks, minPercClicks, minTime); return vBox; } + + /** + * Create the data selector. + * @return the data selector. + */ + private Pane createDataSelectorPane() { + PamHBox hbox = new PamHBox(); + hbox.setSpacing(5); + hbox.setAlignment(Pos.CENTER_LEFT); + + dataSelectorButton = new PamButton(); +// dataSelectorButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.SETTINGS, PamGuiManagerFX.iconSize)); + dataSelectorButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconSize)); + dataSelectorButton.setOnAction((action)->{ + showDataSelectorPane(); + }); + hbox.getChildren().addAll(dataSelectorButton); + return hbox; + } + + + /** + * Creates pane allowing the user to change fine scale things such as error limits. + * @return the pop over pane. + */ + public void showDataSelectorPane() { + + if (popOver==null) { + popOver = new PopOver(); + PamBorderPane holder = new PamBorderPane(simpleChi2Classifier.getDataSelector().getDialogPaneFX().getContentNode()); + holder.setPadding(new Insets(5,5,5,5)); + popOver.setContentNode(holder); + } + + popOver.showingProperty().addListener((obs, old, newval)->{ + if (newval) { +// System.out.println("Data Selector: " + simpleChi2Classifier.getDataSelector()); + simpleChi2Classifier.getDataSelector().getDialogPaneFX().setParams(true); + } + else { + simpleChi2Classifier.getDataSelector().getDialogPaneFX().getParams(true); + } + }); + + popOver.show(dataSelectorButton); + } @Override public Chi2ThresholdParams getParams(Chi2ThresholdParams currParams) { + //System.out.println("Get PERC spinner value; " + minPercClicks.getSpinner().getValue()); currParams.chi2Threshold=chi2Threshold.getSpinner().getValue(); //HACK - for some reason Integer spinner is returning a double currParams.minClicks=minClicks.getSpinner().getValue().intValue(); currParams.minTime=minTime.getSpinner().getValue(); + currParams.minPercentage=minPercClicks.getSpinner().getValue()/100; + + if (simpleChi2Classifier!=null && simpleChi2Classifier.getDataSelector()!=null) { + simpleChi2Classifier.getDataSelector().getDialogPaneFX().getParams(true); + } return currParams; } @Override public void setParams(Chi2ThresholdParams input) { + chi2Threshold.getSpinner().getValueFactory().setValue(input.chi2Threshold); //HACK - for some reason Integer spinner is returning a double minClicks.getSpinner().getValueFactory().setValue((double) input.minClicks); minTime.getSpinner().getValueFactory().setValue(input.minTime); + minPercClicks.getSpinner().getValueFactory().setValue(input.minPercentage*100.); } @Override @@ -164,6 +261,24 @@ public class SimpleCTClassifierPane extends SettingsPane { } + + StringConverter doubleConverter = new StringConverter() { + private final DecimalFormat df = new DecimalFormat("###.#"); + @Override + public String toString(Double object) { + if (object == null) {return "";} + return df.format(object)+"%";} + @Override + public Double fromString(String string) { + try { + if (string == null) {return null;} + string = string.trim(); + if (string.length() < 1) {return null;} + return df.parse(string).doubleValue(); + } catch (Exception ex) {throw new RuntimeException(ex);} + } + }; + diff --git a/src/clickTrainDetector/layout/classification/templateClassifier/TemplateClassifierPane.java b/src/clickTrainDetector/layout/classification/templateClassifier/TemplateClassifierPane.java index b7d9f913..77193e4b 100644 --- a/src/clickTrainDetector/layout/classification/templateClassifier/TemplateClassifierPane.java +++ b/src/clickTrainDetector/layout/classification/templateClassifier/TemplateClassifierPane.java @@ -118,6 +118,8 @@ public class TemplateClassifierPane extends SettingsPane{ + setCheckBoxEnable(); + }); + constraints.gridy++; + + classifierCheckBoxes = new JCheckBox[classifcationManager.getCurrentClassifiers().size()]; + for (int i=0; i { pruneStartSpinner.setEditable(true); pruneBackSpinner.setTooltip(new Tooltip("The minimum number of detections before pruning starts.")); gridPane.add(pruneStartSpinner, 1, gridY); - gridY++; + gridY=0; - gridPane.add(new Label("Max no. coasts"), 0, gridY); + gridPane.add(new Label(" Max no. coasts"), 2, gridY); nCoastsSpinner = new PamSpinner(0,Integer.MAX_VALUE,3,1); nCoastsSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); nCoastsSpinner.setPrefWidth(70); nCoastsSpinner.setEditable(true); nCoastsSpinner.setTooltip(new Tooltip("The maximum number of missing detections before a track is closed")); - gridPane.add(nCoastsSpinner, 1, gridY); + gridPane.add(nCoastsSpinner, 3, gridY); gridY++; - gridPane.add(new Label("Max no. trains"), 0, gridY); + gridPane.add(new Label(" Max no. trains"), 2, gridY); nHoldSpinner = new PamSpinner(0,1000,3,1); nHoldSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); nHoldSpinner.setPrefWidth(70); nHoldSpinner.setEditable(true); nHoldSpinner.setTooltip(new Tooltip("The maximum number of unique click trains that can be tracked at the same time.")); - gridPane.add(nHoldSpinner, 1, gridY); + gridPane.add(nHoldSpinner, 3, gridY); pamVBox.getChildren().addAll(label, gridPane); diff --git a/src/detectionPlotFX/layout/DetectionPlotDisplay.java b/src/detectionPlotFX/layout/DetectionPlotDisplay.java index 713274d4..865eecb9 100644 --- a/src/detectionPlotFX/layout/DetectionPlotDisplay.java +++ b/src/detectionPlotFX/layout/DetectionPlotDisplay.java @@ -447,6 +447,11 @@ public class DetectionPlotDisplay extends PamBorderPane implements UserDisplayNo private void drawDataUnit(PamDataUnit newDataUnit) { //Debug.out.println("DetectionPlotDisplay DrawDataUnit: " +newDataUnit); if (currentDataInfo!=null){ + //sometimes the axis just need a little push to make sure the pane and axis object bindings have been updated + for (int i=0; i implements Detection } g2.strokePolyline(scaledDataX, scaledDataY, scaledDataY.length-1); if (fillSpectrum) { - // System.out.println("Last point: " + x0 + " y0 " + r.getHeight()); + //System.out.println("Last point: " + x0 + " y0 " + y0 + " " + scale + " " + clickLineData[iChan][0] + " " + projector.getAxis(Side.LEFT).getMinVal() + " " +projector.getAxis(Side.LEFT).getTotalPixels()); scaledDataX[scaledDataX.length-1]=x0; // the last x position scaledDataY[scaledDataY.length-1]= r.getHeight() ; //return the line to zero for polygon drawing // PamUtils.PamArrayUtils.printArray(scaledDataY); diff --git a/src/pamViewFX/fxNodes/pamAxis/PamAxisPane2.java b/src/pamViewFX/fxNodes/pamAxis/PamAxisPane2.java index 3b80847b..905109bc 100644 --- a/src/pamViewFX/fxNodes/pamAxis/PamAxisPane2.java +++ b/src/pamViewFX/fxNodes/pamAxis/PamAxisPane2.java @@ -93,7 +93,7 @@ public class PamAxisPane2 extends StackPane { this.getChildren().add(mainPane); } - private void layoutAxis(){ + public void layoutAxis(){ mainPane.setCenter(null); diff --git a/src/pamViewFX/fxNodes/utilityPanes/MinMaxPane.java b/src/pamViewFX/fxNodes/utilityPanes/MinMaxPane.java index 82f64c6d..b4942247 100644 --- a/src/pamViewFX/fxNodes/utilityPanes/MinMaxPane.java +++ b/src/pamViewFX/fxNodes/utilityPanes/MinMaxPane.java @@ -1,7 +1,6 @@ package pamViewFX.fxNodes.utilityPanes; import java.io.Serializable; - import javafx.geometry.Pos; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; diff --git a/src/spectrogramNoiseReduction/layoutFX/SpectrogramNoisePaneFX.java b/src/spectrogramNoiseReduction/layoutFX/SpectrogramNoisePaneFX.java index cdc4d836..60f67120 100644 --- a/src/spectrogramNoiseReduction/layoutFX/SpectrogramNoisePaneFX.java +++ b/src/spectrogramNoiseReduction/layoutFX/SpectrogramNoisePaneFX.java @@ -3,12 +3,10 @@ package spectrogramNoiseReduction.layoutFX; import java.util.ArrayList; -import org.controlsfx.control.ToggleSwitch; - import pamViewFX.PamGuiManagerFX; -import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; import pamViewFX.fxNodes.utilityPanes.SourcePaneFX; import spectrogramNoiseReduction.SpecNoiseMethod; import spectrogramNoiseReduction.SpectrogramNoiseProcess; @@ -16,10 +14,8 @@ import spectrogramNoiseReduction.SpectrogramNoiseSettings; 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.Tooltip; -import javafx.scene.layout.Pane; import PamController.SettingsPane; import PamguardMVC.PamDataBlock; @@ -39,7 +35,7 @@ public class SpectrogramNoisePaneFX extends SettingsPane Date: Fri, 3 Jun 2022 08:48:16 +0100 Subject: [PATCH 16/37] Small changes to support Genesis work --- src/PamView/ColourArray.java | 2 +- src/clickDetector/ClickBTDisplay.java | 3 +++ src/clickDetector/ClickBinaryDataSource.java | 4 ++-- src/clickDetector/ClickDetection.java | 2 ++ src/generalDatabase/backup/DatabaseBackupStream.java | 3 +++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/PamView/ColourArray.java b/src/PamView/ColourArray.java index e7d65dd2..edbdb61d 100644 --- a/src/PamView/ColourArray.java +++ b/src/PamView/ColourArray.java @@ -344,7 +344,7 @@ public class ColourArray implements Cloneable, Serializable, ManagedParameters { * @return a contrasting colour. */ private Color createContrastingColour() { - Color[] tryCols = {Color.white, Color.red, Color.blue, Color.green, Color.black}; + Color[] tryCols = {Color.white, Color.red, Color.CYAN, Color.blue, Color.green, Color.black}; if (colours == null) { return tryCols[0]; } diff --git a/src/clickDetector/ClickBTDisplay.java b/src/clickDetector/ClickBTDisplay.java index 1f797971..a8556bdd 100644 --- a/src/clickDetector/ClickBTDisplay.java +++ b/src/clickDetector/ClickBTDisplay.java @@ -1572,6 +1572,9 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett } private double clickAngleToY(ClickDetection click) { AbstractLocalisation loc = click.getLocalisation(); +// if (click.getUID() == 110006089) { +// System.out.println("Click 110006089 angle " + click.getAngle()); +// } if (loc == null) return 0; double angle = 0; GpsData oll; diff --git a/src/clickDetector/ClickBinaryDataSource.java b/src/clickDetector/ClickBinaryDataSource.java index 62e64810..b9294cbd 100644 --- a/src/clickDetector/ClickBinaryDataSource.java +++ b/src/clickDetector/ClickBinaryDataSource.java @@ -210,8 +210,8 @@ public class ClickBinaryDataSource extends BinaryDataSource { // long uid = binaryObjectData.getDataUnitBaseData().getUID(); // System.out.printf("Loading click with UID %d at %s\n", uid, // PamCalendar.formatDateTime(binaryObjectData.getTimeMilliseconds())); -// if (uid == lastUID) { -// System.out.println("Click repeat UID: " + lastUID); +// if (uid == 110006089) { +// System.out.println("Click UID: " + 110006089); // } // else { // lastUID = binaryObjectData.getDataUnitBaseData().getUID(); diff --git a/src/clickDetector/ClickDetection.java b/src/clickDetector/ClickDetection.java index a0528738..330fdca9 100644 --- a/src/clickDetector/ClickDetection.java +++ b/src/clickDetector/ClickDetection.java @@ -1328,6 +1328,8 @@ public class ClickDetection extends PamDataUnit implem /** * Returns the angle in degrees for compatibilty with older version of click detector + * This is really bad to use for anything apart from two element arrays and it would be + * sensible to remove the function entirely. * @return angle of the click detection in degrees */ public double getAngle() { diff --git a/src/generalDatabase/backup/DatabaseBackupStream.java b/src/generalDatabase/backup/DatabaseBackupStream.java index fc6f4d47..d7808a96 100644 --- a/src/generalDatabase/backup/DatabaseBackupStream.java +++ b/src/generalDatabase/backup/DatabaseBackupStream.java @@ -120,6 +120,9 @@ public class DatabaseBackupStream extends FileBackupStream { public FileLocation getSourceLocation() { File dbFile = getDatabaesFile(); + if (dbFile == null) { + return null; + } FileLocation sl = new FileLocation(); sl.path = dbFile.getAbsolutePath(); sl.canEditMask = false; From 8a34362261e0c907816924cb0f6bd95da6bc40bc Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sat, 4 Jun 2022 11:00:30 +0100 Subject: [PATCH 17/37] Squashed commit of the following: commit f2860d8154b7641f85b73a85ceb51e20c57c42bd Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri Jun 3 08:48:16 2022 +0100 Small changes to support Genesis work commit 91a8e5fec007d12ba56952b16594b7e9af9994d9 Author: Jamie Mac Date: Fri May 27 06:39:20 2022 -0600 Added data selectors to Click Train Detector (#34) * 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 * Updates to FX GUI * 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 * Screenshots * Updates and bug fixes to click train detector and CPOD importer * 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 * Updates and bug fixes to click train detector and CPOD importer * Fix standard classifier JSON logging * Merge fixes to click train detector * 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 * Screenshots * Updates and bug fixes to click train detector and CPOD importer * 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 * Screenshots * Updates and bug fixes to click train detector and CPOD importer * Fix standard classifier JSON logging * 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. * Bug fix to UI * Bug fixes to FX GUI * Updates to click train detector * Squashed commit of the following: commit 9f998165ee95dbb16acaf420876f3aeab75c2158 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon May 2 19:40:24 2022 +0100 Updates to support ContactCollator plugin (#33) * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * fix problem in SummaryComand * Update command line options * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * Update command line options * Update DecimatorParams.java * couple of updates to support new contact collator plugin * Sorting out sample rate info in clip display to support Contact Collator plugin * FLAC Speed Improve flac speed * Update .gitignore * Update .gitignore * Updates to support new features in Contact Collator * Small update to RawDatautils to handle null data * Updates to click train detector * Squashed commit of the following: commit 62b020b3204aa56189b1c2da88bcbb9f49140936 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sat May 14 06:52:20 2022 +0100 Add a new offlinefileslist function commit 3a9a5311aa529b66340f6ae322f88905b911947a Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed Apr 27 09:43:31 2022 +0100 Update .gitignore commit 9f998165ee95dbb16acaf420876f3aeab75c2158 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon May 2 19:40:24 2022 +0100 Updates to support ContactCollator plugin (#33) * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * fix problem in SummaryComand * Update command line options * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * Update command line options * Update DecimatorParams.java * couple of updates to support new contact collator plugin * Sorting out sample rate info in clip display to support Contact Collator plugin * FLAC Speed Improve flac speed * Update .gitignore * Update .gitignore * Updates to support new features in Contact Collator * Small update to RawDatautils to handle null data * Updated data selector for click train detector * Click train detector updates and bug fixes Fixed very annoying bug which meant templates did not show properly when the dialog was first opened in swing. Added feature to simple classifier were % of clicks of one click classification (or other data selector paramter) can be used to classify. Co-authored-by: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> commit 7a08005ba895aa801b3bf31ef094173210b4d6a2 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri May 27 13:18:02 2022 +0100 Sort out a couple of issues with scroll play mode timing commit e2b48c8e071d45070b8ab3ff42660832bcb12fe5 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue May 17 21:06:48 2022 +0100 Changes to corner layout commit 62b020b3204aa56189b1c2da88bcbb9f49140936 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sat May 14 06:52:20 2022 +0100 Add a new offlinefileslist function commit 3a9a5311aa529b66340f6ae322f88905b911947a Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed Apr 27 09:43:31 2022 +0100 Update .gitignore commit 9f998165ee95dbb16acaf420876f3aeab75c2158 Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon May 2 19:40:24 2022 +0100 Updates to support ContactCollator plugin (#33) * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * fix problem in SummaryComand * Update command line options * Change synchronization on RawDataTransforms soit uses the owner data holder, not itself for synchronization. Otherwise you get thread locks. * Update command line options * Update DecimatorParams.java * couple of updates to support new contact collator plugin * Sorting out sample rate info in clip display to support Contact Collator plugin * FLAC Speed Improve flac speed * Update .gitignore * Update .gitignore * Updates to support new features in Contact Collator * Small update to RawDatautils to handle null data --- .classpath | 4 +- .gitignore | 1 + buildconfigurations/Build PAMGuard.launch | 21 -- .../layoutFX/CheckWavHeadersPane.java | 32 +- src/Acquisition/layoutFX/FolderInputPane.java | 1 + src/PamView/ColourArray.java | 2 +- src/PamView/ColourComboBox.java | 11 +- src/PamView/GeneralProjector.java | 2 +- src/PamView/panel/CornerLayoutContraint.java | 18 ++ src/PamguardMVC/PamDataBlock.java | 4 +- src/PamguardMVC/RawDataTransforms.java | 298 ++++++++---------- .../dataSelector/DataSelectorSettings.java | 2 +- src/Resources/css/pamSettingsCSS.css | 36 +-- src/clickDetector/ClickBTDisplay.java | 3 + src/clickDetector/ClickBinaryDataSource.java | 4 +- src/clickDetector/ClickDetection.java | 2 + .../dataSelector/ClickSelectPaneFX.java | 5 +- .../classification/CTClassifierManager.java | 29 +- .../classification/CTClassifierParams.java | 14 +- .../BearingClassifierParams.java | 1 + .../idiClassifier/IDIClassifierParams.java | 6 + .../Chi2CTClassification.java | 9 +- .../Chi2ThresholdClassifier.java | 70 +++- .../Chi2ThresholdParams.java | 16 +- .../StandardClassifier.java | 8 +- .../StandardClassifierParams.java | 3 + .../TemplateClassifierParams.java | 1 + .../mht/mhtvar/AmplitudeChi2.java | 5 +- .../dataselector/CTDataSelector.java | 35 ++ .../dataselector/CTSelectParams.java | 12 + src/clickTrainDetector/layout/CTSwingGUI.java | 2 +- .../layout/ClickTrainAlgorithmPaneFX.java | 8 +- .../layout/PreClassifierPane.java | 2 +- .../SimpleCTClassifierGraphics.java | 10 +- .../SimpleCTClassifierPane.java | 123 +++++++- .../TemplateClassifierPane.java | 6 + .../TemplateSpectrumPane.java | 9 +- .../dataselector/CTDataSelectPanel.java | 149 ++++++++- .../layout/mht/MHTSettingsPane.java | 10 +- src/dataMap/SummaryPanel.java | 4 +- .../layout/DetectionPlotDisplay.java | 5 + src/detectionPlotFX/plots/SpectrumPlot.java | 2 +- src/fileOfflineData/OfflineFileList.java | 88 ++++++ .../backup/DatabaseBackupStream.java | 3 + src/pamScrollSystem/AbstractPamScroller.java | 26 +- .../fxNodes/pamAxis/PamAxisPane2.java | 2 +- .../fxNodes/utilityPanes/MinMaxPane.java | 1 - .../layoutFX/SpectrogramNoisePaneFX.java | 15 +- 48 files changed, 826 insertions(+), 294 deletions(-) delete mode 100644 buildconfigurations/Build PAMGuard.launch create mode 100644 src/fileOfflineData/OfflineFileList.java diff --git a/.classpath b/.classpath index 188c7291..e1412773 100644 --- a/.classpath +++ b/.classpath @@ -6,8 +6,10 @@ - + + + diff --git a/.gitignore b/.gitignore index e875dc34..6df976a9 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ *.zip *.tar.gz *.rar +*.dll # eclipse project file .project diff --git a/buildconfigurations/Build PAMGuard.launch b/buildconfigurations/Build PAMGuard.launch deleted file mode 100644 index a004d384..00000000 --- a/buildconfigurations/Build PAMGuard.launch +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Acquisition/layoutFX/CheckWavHeadersPane.java b/src/Acquisition/layoutFX/CheckWavHeadersPane.java index 83607217..60638bae 100644 --- a/src/Acquisition/layoutFX/CheckWavHeadersPane.java +++ b/src/Acquisition/layoutFX/CheckWavHeadersPane.java @@ -7,6 +7,7 @@ import Acquisition.FolderInputSystem; import Acquisition.WavFileFuncs; import PamUtils.PamAudioFileFilter; import javafx.geometry.Insets; +import javafx.application.Platform; import javafx.beans.property.SimpleDoubleProperty; import javafx.concurrent.Task; import javafx.geometry.Pos; @@ -89,6 +90,8 @@ public class CheckWavHeadersPane extends PamBorderPane { */ private SimpleDoubleProperty progressProperty = new SimpleDoubleProperty(0); + private PamButton runButton; + /** * Constructor for the CheckWavHeadersPane * @param folderInputSystem - the folder input system. @@ -102,20 +105,19 @@ public class CheckWavHeadersPane extends PamBorderPane { this.folderInputSystem=folderInputSystem; folderName = new Label(" "); - PamGuiManagerFX.titleFont2style(folderName); + //PamGuiManagerFX.titleFont2style(folderName); textArea = new TextArea(); textArea.setEditable(false); ScrollPane scrollPane = new ScrollPane(textArea); scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER); //scrollPane.setPrefSize(322, 300); - this.setCenter(scrollPane); PamHBox pamHBox = new PamHBox(); pamHBox.setAlignment(Pos.CENTER_LEFT); pamHBox.setSpacing(5); - PamButton runButton = new PamButton(); + runButton = new PamButton(); runButton.setGraphic(PamGlyphDude.createPamIcon("mdi2p-play")); runButton.setOnAction((action)->{ checkFiles(); @@ -128,15 +130,15 @@ public class CheckWavHeadersPane extends PamBorderPane { pamHBox.getChildren().addAll(runButton, progressBar); progressBar.setMaxWidth(Double.MAX_VALUE); - mainPane.getChildren().addAll(folderName, textArea, pamHBox); - - this.setPadding(new Insets(5,0,5,15)); + this.setCenter(mainPane); + + this.setPadding(new Insets(5,0,5,15)); } - private void setParams() { + void setParams() { running = ran = false; subFolders = folderInputSystem.getFolderInputParameters().subFolders; if (subFolders) { @@ -149,7 +151,7 @@ public class CheckWavHeadersPane extends PamBorderPane { textArea.setText(" "); allFiles.clear(); nFiles = countFiles(folder); - progressBar.setProgress(0); + progressProperty.setValue(0); progressBar.progressProperty().bind(progressProperty); //progressBar.setMaximum(Math.max(nFiles, 1)); enableControls(); @@ -213,6 +215,10 @@ public class CheckWavHeadersPane extends PamBorderPane { nErrors = 0; enableControls(); checkFilesWorker = new CheckFiles(); + + Thread th = new Thread(checkFilesWorker); + th.setDaemon(true); + th.start(); } @@ -245,10 +251,16 @@ public class CheckWavHeadersPane extends PamBorderPane { * for each file, report on progress with it's name and * whether or not it had an error */ - int error; File aFile; + System.out.println("Analaysing files: Start: " + allFiles.size() ); for (int i = 0; i < allFiles.size(); i++) { - error = checkFile(aFile = allFiles.get(i)); + System.out.println("Analaysing files: " + i); + final int error = checkFile(aFile = allFiles.get(i)); + final File aFile1 = aFile; + Platform.runLater(()->{ + textArea.appendText(String.format("\n File %s %s" , aFile1.getName() , + error == AudioFileFuncs.FILE_OK ? "OK" : ("Error " + error))); + }); //progressBar.progressProperty().bind(null); progressProperty.setValue(100*i/(double) allFiles.size()); } diff --git a/src/Acquisition/layoutFX/FolderInputPane.java b/src/Acquisition/layoutFX/FolderInputPane.java index 963890a5..b6602e92 100644 --- a/src/Acquisition/layoutFX/FolderInputPane.java +++ b/src/Acquisition/layoutFX/FolderInputPane.java @@ -304,6 +304,7 @@ public class FolderInputPane extends DAQSettingsPane{ wavFix.setOnAction((action)->{ acquisitionPaneFX.getAdvancedLabel().setText("Fix Wave Files"); acquisitionPaneFX.getAdvancedPane().setCenter(this.fixWavPane); + fixWavPane.setParams(); acquisitionPaneFX.getFlipPane().flipToBack(); }); diff --git a/src/PamView/ColourArray.java b/src/PamView/ColourArray.java index e7d65dd2..edbdb61d 100644 --- a/src/PamView/ColourArray.java +++ b/src/PamView/ColourArray.java @@ -344,7 +344,7 @@ public class ColourArray implements Cloneable, Serializable, ManagedParameters { * @return a contrasting colour. */ private Color createContrastingColour() { - Color[] tryCols = {Color.white, Color.red, Color.blue, Color.green, Color.black}; + Color[] tryCols = {Color.white, Color.red, Color.CYAN, Color.blue, Color.green, Color.black}; if (colours == null) { return tryCols[0]; } diff --git a/src/PamView/ColourComboBox.java b/src/PamView/ColourComboBox.java index 9bfca80b..dcd95f67 100644 --- a/src/PamView/ColourComboBox.java +++ b/src/PamView/ColourComboBox.java @@ -81,6 +81,15 @@ public class ColourComboBox extends PamPanel { } +// private void createImages() { +// for (int i = 0; i < ColourArray.ColourArrayType.values().length; i++) { +// intArray[i] = new Integer(i); +// colourStrings[i]=ColourArray.getName(types[i]); +// colourArray=ColourArray.createStandardColourArray(256, ColourArray.ColourArrayType.values()[i]); +// images[i]=new ImageIcon(createColourMapImage(colourArray, height, width)); +// } +// } + public void addActionListener(ActionListener actionListener){ colourBox.addActionListener(actionListener); } @@ -161,7 +170,7 @@ public class ColourComboBox extends PamPanel { for (int i = 0; i < ColourArray.ColourArrayType.values().length; i++) { intArray[i] = new Integer(i); colourStrings[i]=ColourArray.getName(types[i]); - colourArray=ColourArray.createStandardColourArray(256, ColourArray.ColourArrayType.values()[i]); + colourArray=ColourArray.createStandardColourArray(width, ColourArray.ColourArrayType.values()[i]); images[i]=new ImageIcon(createColourMapImage(colourArray, height, width)); } } diff --git a/src/PamView/GeneralProjector.java b/src/PamView/GeneralProjector.java index 8bb6d8d3..b9aa21e4 100644 --- a/src/PamView/GeneralProjector.java +++ b/src/PamView/GeneralProjector.java @@ -85,7 +85,7 @@ public abstract class GeneralProjector { 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"), ISHDET("Detector Output"); + SPEED ("Speed"), PROBABILITY ("Probability"), NCYCLES ("No. cycles"), BANDWIDTH("Bandwidth"), ISHDET("Detector Output"), X("x coordinate"), Y("y coordinate"); private String unit; diff --git a/src/PamView/panel/CornerLayoutContraint.java b/src/PamView/panel/CornerLayoutContraint.java index d371d6ef..20b77500 100644 --- a/src/PamView/panel/CornerLayoutContraint.java +++ b/src/PamView/panel/CornerLayoutContraint.java @@ -23,6 +23,24 @@ public class CornerLayoutContraint implements Serializable, Cloneable { public int anchor = FIRST_LINE_START; + /** + * Construct a corner layout constraint with the given anchor + * @param anchor + */ + public CornerLayoutContraint(int anchor) { + super(); + this.anchor = anchor; + } + + + /** + * construct a corner layout constraint with the default anchor + */ + public CornerLayoutContraint() { + super(); + this.anchor = anchor; + } + @Override protected CornerLayoutContraint clone() { try { diff --git a/src/PamguardMVC/PamDataBlock.java b/src/PamguardMVC/PamDataBlock.java index bc7c188f..4db0263c 100644 --- a/src/PamguardMVC/PamDataBlock.java +++ b/src/PamguardMVC/PamDataBlock.java @@ -986,7 +986,9 @@ public class PamDataBlock extends PamObservable { } /** - * Instruction from the viewer scroll manager to load new data. + * Instruction from the viewer scroll manager to load new data.

This just calls through + * to loadViewerData(OfflineDataLoadInfo ...) so this should not be overridden. Override + * the other function instead. * * @param dataStart data start time in millis * @param dataEnd data end time in millis. diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index ed371072..c0684730 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -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; - } } private double[][] makeFilteredWaveData(FFTFilterParams filterParams) { @@ -579,15 +533,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; } @@ -654,9 +606,7 @@ public class RawDataTransforms { * @return */ private int getNChan() { - synchronized (synchObject) { // new - return this.rawData.getWaveData().length; - } + return this.rawData.getWaveData().length; } /** @@ -689,11 +639,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; } diff --git a/src/PamguardMVC/dataSelector/DataSelectorSettings.java b/src/PamguardMVC/dataSelector/DataSelectorSettings.java index 273812d8..7d593f4d 100644 --- a/src/PamguardMVC/dataSelector/DataSelectorSettings.java +++ b/src/PamguardMVC/dataSelector/DataSelectorSettings.java @@ -32,7 +32,7 @@ public class DataSelectorSettings implements Serializable, ManagedParameters { * @return Params or null if they don't exist. */ public DataSelectParams getParams(String name) { - if (selectorParams == null) { + if (selectorParams == null || name == null) { return null; } return selectorParams.get(name); diff --git a/src/Resources/css/pamSettingsCSS.css b/src/Resources/css/pamSettingsCSS.css index 605da31c..d635ea5b 100644 --- a/src/Resources/css/pamSettingsCSS.css +++ b/src/Resources/css/pamSettingsCSS.css @@ -35,8 +35,8 @@ -fx-font-color: -fx-text; -fx-font-family: "Ubuntu"; - -fx-border-radius: 5 5 5 5; - -fx-background-radius: 5 5 5 5; + -fx-border-radius: 5 5 5 5; + -fx-background-radius: 5 5 5 5; -icons-color: -fx-icon_col; } @@ -615,7 +615,7 @@ -fx-background: -fx-darkbackground; -fx-background-color: -fx-darkbackground; -fx-border-radius: 0 0 0 0; - -fx-padding: 7 0 7 0; + -fx-padding: 5 0 5 0; -fx-border-color: transparent; } @@ -623,9 +623,9 @@ -fx-text-fill: white; -fx-background-color: -fx-darkbackground; -fx-border-color: -fx-border_col; - -fx-background-radius: 6 6 0 0; - -fx-border-radius: 6 6 0 0; - -fx-padding: 7 0 7 0; + -fx-background-radius: 5 5 0 0; + -fx-border-radius: 5 5 0 0; + -fx-padding: 5 0 5 0; } .spinner .increment-arrow { @@ -635,16 +635,16 @@ .spinner .increment-arrow-button:hover { -fx-background-color: -fx-highlight; -fx-border-color: -fx-highlight_border; - -fx-background-radius: 6 6 0 0; - -fx-border-radius: 6 6 0 0; + -fx-background-radius: 5 5 0 0; + -fx-border-radius: 5 5 0 0; } .spinner .decrement-arrow-button { -fx-text-fill: white; -fx-background-color: -fx-darkbackground; -fx-border-color: -fx-border_col; - -fx-background-radius: 0 0 6 6; - -fx-border-radius: 0 0 6 6 + -fx-background-radius: 0 0 5 5; + -fx-border-radius: 0 0 5 5 } .spinner .decrement-arrow { @@ -654,8 +654,8 @@ .spinner .decrement-arrow-button:hover { -fx-background-color: -fx-highlight; -fx-border-color: -fx-highlight_border; - -fx-background-radius: 0 0 6 6; - -fx-border-radius: 0 0 6 6; + -fx-background-radius: 0 0 5 5; + -fx-border-radius: 0 0 5 5; } /*Arrows are horizontal either side of text box*/ @@ -663,7 +663,7 @@ -fx-text-fill: white; -fx-background-color: -fx-darkbackground; -fx-border-color: -fx-border_col; - -fx-border-radius: 0 6 6 0 + -fx-border-radius: 0 5 5 0 } @@ -671,22 +671,22 @@ -fx-text-fill: white; -fx-background-color: -fx-darkbackground; -fx-border-color: -fx-border_col; - -fx-border-radius: 6 0 0 6 + -fx-border-radius: 5 0 0 5 } .spinner.split-arrows-horizontal .increment-arrow-button:hover { -fx-background-color: -fx-highlight; -fx-border-color: -fx-highlight_border; - -fx-background-radius: 0 6 6 0; - -fx-border-radius: 0 6 6 0; + -fx-background-radius: 0 5 5 0; + -fx-border-radius: 0 5 5 0; } .spinner.split-arrows-horizontal .decrement-arrow-button:hover { -fx-background-color: -fx-highlight; -fx-border-color: -fx-highlight_border; - -fx-background-radius: 6 0 0 6; - -fx-border-radius: 6 0 0 6; + -fx-background-radius: 5 0 0 5; + -fx-border-radius: 5 0 0 5; } .spinner.split-arrows-horizontal .text-field { diff --git a/src/clickDetector/ClickBTDisplay.java b/src/clickDetector/ClickBTDisplay.java index 1f797971..a8556bdd 100644 --- a/src/clickDetector/ClickBTDisplay.java +++ b/src/clickDetector/ClickBTDisplay.java @@ -1572,6 +1572,9 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett } private double clickAngleToY(ClickDetection click) { AbstractLocalisation loc = click.getLocalisation(); +// if (click.getUID() == 110006089) { +// System.out.println("Click 110006089 angle " + click.getAngle()); +// } if (loc == null) return 0; double angle = 0; GpsData oll; diff --git a/src/clickDetector/ClickBinaryDataSource.java b/src/clickDetector/ClickBinaryDataSource.java index 62e64810..b9294cbd 100644 --- a/src/clickDetector/ClickBinaryDataSource.java +++ b/src/clickDetector/ClickBinaryDataSource.java @@ -210,8 +210,8 @@ public class ClickBinaryDataSource extends BinaryDataSource { // long uid = binaryObjectData.getDataUnitBaseData().getUID(); // System.out.printf("Loading click with UID %d at %s\n", uid, // PamCalendar.formatDateTime(binaryObjectData.getTimeMilliseconds())); -// if (uid == lastUID) { -// System.out.println("Click repeat UID: " + lastUID); +// if (uid == 110006089) { +// System.out.println("Click UID: " + 110006089); // } // else { // lastUID = binaryObjectData.getDataUnitBaseData().getUID(); diff --git a/src/clickDetector/ClickDetection.java b/src/clickDetector/ClickDetection.java index a0528738..330fdca9 100644 --- a/src/clickDetector/ClickDetection.java +++ b/src/clickDetector/ClickDetection.java @@ -1328,6 +1328,8 @@ public class ClickDetection extends PamDataUnit implem /** * Returns the angle in degrees for compatibilty with older version of click detector + * This is really bad to use for anything apart from two element arrays and it would be + * sensible to remove the function entirely. * @return angle of the click detection in degrees */ public double getAngle() { diff --git a/src/clickDetector/dataSelector/ClickSelectPaneFX.java b/src/clickDetector/dataSelector/ClickSelectPaneFX.java index 743fa946..0c1d661d 100644 --- a/src/clickDetector/dataSelector/ClickSelectPaneFX.java +++ b/src/clickDetector/dataSelector/ClickSelectPaneFX.java @@ -217,7 +217,8 @@ public class ClickSelectPaneFX extends DynamicSettingsPane { void setParams() { ClickAlarmParameters clickAlarmParameters = clickDataSelector.getClickAlarmParameters(); speciesSelect.getChildren().clear(); - + + //species pane setup species = null; weights = null; @@ -350,6 +351,7 @@ public class ClickSelectPaneFX extends DynamicSettingsPane { } boolean getParams() { + ClickAlarmParameters clickAlarmParameters = clickDataSelector.getClickAlarmParameters().clone(); clickAlarmParameters.useEchoes = useEchoes.isSelected(); @@ -397,6 +399,7 @@ public class ClickSelectPaneFX extends DynamicSettingsPane { // btDisplayParameters.showANDEvents = andEvents.isSelected(); // btDisplayParameters.showEventsOnly = onlyEvents.isSelected(); + clickDataSelector.setClickAlarmParameters(clickAlarmParameters); return true; } diff --git a/src/clickTrainDetector/classification/CTClassifierManager.java b/src/clickTrainDetector/classification/CTClassifierManager.java index 1c0af211..2389d8ab 100644 --- a/src/clickTrainDetector/classification/CTClassifierManager.java +++ b/src/clickTrainDetector/classification/CTClassifierManager.java @@ -1,9 +1,8 @@ package clickTrainDetector.classification; import java.util.ArrayList; +import java.util.UUID; -import PamUtils.PamCalendar; -import PamguardMVC.debug.Debug; import clickTrainDetector.CTDataUnit; import clickTrainDetector.ClickTrainControl; import clickTrainDetector.classification.bearingClassifier.BearingClassification; @@ -158,10 +157,10 @@ public class CTClassifierManager { //first check the pre-classifier Chi2CTClassification classification = this.preClassifier.classifyClickTrain(ctDataUnit); - System.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()); + //System.out.println("No SPECIES: chi^2" + ctDataUnit.getCTChi2()); ctDataUnit.setJunkTrain(true); //no need to do any more classification- the click train has been flagged for deletion. ctDataUnit.clearClassifiers(); @@ -189,17 +188,17 @@ public class CTClassifierManager { ctDataUnit.clearClassifiers(); ctDataUnit.setClassificationIndex(-1); - System.out.println("Classify species: Num classifier " + this.cTClassifiers.size()); + //System.out.println("Classify species: Num classifier " + this.cTClassifiers.size()); for (int i=0; iCTClassifier.NOSPECIES && !hasBeenClssfd) { - System.out.println("Set classiifcation index: " + i); + //System.out.println("Set classiifcation index: " + i); ctDataUnit.setClassificationIndex(i); //set the classification index. hasBeenClssfd = true; } @@ -238,12 +237,26 @@ public class CTClassifierManager { System.err.println("CTCLassifier manager: the classifier is null"); continue; } + + if (ctParams[i].uniqueID ==null) { + //old versions may not have a unique ID so needs to be added. + ctParams[i].uniqueID = UUID.randomUUID().toString(); + } aClassifier.setParams(ctParams[i]); cTClassifiers.add(aClassifier); } } + /** + * Get the pre-classifer. This is an intial very broad classifier that is used to determine + * whether a click train should be saved or dumped from memory. + * @return the pre-classifier. + */ + public Chi2ThresholdClassifier getPreClassifier() { + return preClassifier; + } + diff --git a/src/clickTrainDetector/classification/CTClassifierParams.java b/src/clickTrainDetector/classification/CTClassifierParams.java index 81af2483..2841b1ab 100644 --- a/src/clickTrainDetector/classification/CTClassifierParams.java +++ b/src/clickTrainDetector/classification/CTClassifierParams.java @@ -1,6 +1,7 @@ package clickTrainDetector.classification; import java.io.Serializable; +import java.util.UUID; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; @@ -20,12 +21,23 @@ public class CTClassifierParams implements Cloneable, Serializable, ManagedParam private static final long serialVersionUID = 1L; + public CTClassifierParams() { + this.uniqueID = UUID.randomUUID().toString(); + } + + /** - * A very simple species flag to indicate what classifier was used. 0 means not classified. + * The name of the classifier. */ public String classifierName = ""; + /** + * A unique ID for the classifier that never changes. This is important for accessing data selectors. + */ + public String uniqueID; + + /** * A very simple species flag to indicate what classifier was used. 0 means not classified. */ diff --git a/src/clickTrainDetector/classification/bearingClassifier/BearingClassifierParams.java b/src/clickTrainDetector/classification/bearingClassifier/BearingClassifierParams.java index 0bff0c6f..08031d6b 100644 --- a/src/clickTrainDetector/classification/bearingClassifier/BearingClassifierParams.java +++ b/src/clickTrainDetector/classification/bearingClassifier/BearingClassifierParams.java @@ -15,6 +15,7 @@ public class BearingClassifierParams extends CTClassifierParams implements Manag public BearingClassifierParams(){ + super(); type = CTClassifierType.BEARINGCLASSIFIER; } diff --git a/src/clickTrainDetector/classification/idiClassifier/IDIClassifierParams.java b/src/clickTrainDetector/classification/idiClassifier/IDIClassifierParams.java index a2bca3cb..4862c3dc 100644 --- a/src/clickTrainDetector/classification/idiClassifier/IDIClassifierParams.java +++ b/src/clickTrainDetector/classification/idiClassifier/IDIClassifierParams.java @@ -4,9 +4,15 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import clickTrainDetector.classification.CTClassifierParams; +import clickTrainDetector.classification.CTClassifierType; public class IDIClassifierParams extends CTClassifierParams implements Serializable, Cloneable, ManagedParameters { + public IDIClassifierParams(){ + super(); + type = CTClassifierType.IDICLASSIFIER; + } + /** * */ diff --git a/src/clickTrainDetector/classification/simplechi2classifier/Chi2CTClassification.java b/src/clickTrainDetector/classification/simplechi2classifier/Chi2CTClassification.java index 22ef818e..c3c667a4 100644 --- a/src/clickTrainDetector/classification/simplechi2classifier/Chi2CTClassification.java +++ b/src/clickTrainDetector/classification/simplechi2classifier/Chi2CTClassification.java @@ -1,5 +1,7 @@ package clickTrainDetector.classification.simplechi2classifier; +import PamguardMVC.PamDataBlock; +import PamguardMVC.dataSelector.DataSelector; import clickTrainDetector.classification.CTClassification; import clickTrainDetector.classification.CTClassifierType; import clickTrainDetector.classification.ClassifierJSONLogging; @@ -22,15 +24,14 @@ public class Chi2CTClassification implements CTClassification { private int speciesCode = -1; - private SimpleClassifierJSONLogging simpleClassifierJSONLogging; + private SimpleClassifierJSONLogging simpleClassifierJSONLogging; - public Chi2CTClassification(int speciesCode) { this.speciesCode = speciesCode; simpleClassifierJSONLogging=new SimpleClassifierJSONLogging(); } - + /** * Create the classification from a JSON string @@ -62,5 +63,7 @@ public class Chi2CTClassification implements CTClassification { public ClassifierJSONLogging getJSONLogging() { return simpleClassifierJSONLogging; } + + } diff --git a/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdClassifier.java b/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdClassifier.java index 9577e5a9..20473840 100644 --- a/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdClassifier.java +++ b/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdClassifier.java @@ -1,6 +1,8 @@ package clickTrainDetector.classification.simplechi2classifier; import PamUtils.PamCalendar; +import PamguardMVC.PamDataBlock; +import PamguardMVC.dataSelector.DataSelector; import PamguardMVC.debug.Debug; import clickTrainDetector.CTDataUnit; import clickTrainDetector.ClickTrainControl; @@ -32,10 +34,17 @@ public class Chi2ThresholdClassifier implements CTClassifier { */ private ClickTrainControl clickTrainControl; + + /** + * Data selector for the chi2 threshold classifier. + */ + private DataSelector dataSelector; + public Chi2ThresholdClassifier(ClickTrainControl clickTrainControl, int defaultSpeciesID) { this.clickTrainControl=clickTrainControl; clssfrParams.speciesFlag=defaultSpeciesID; + createDataSelector(clickTrainControl.getParentDataBlock()); } @@ -65,8 +74,62 @@ public class Chi2ThresholdClassifier implements CTClassifier { return new Chi2CTClassification(CTClassifier.NOSPECIES); //no classification } + if (!isPercClicks(clickTrain)) { +// Debug.out.println("Failed on chi2Threshold"); + return new Chi2CTClassification(CTClassifier.NOSPECIES); //no classification + } + + return new Chi2CTClassification(clssfrParams.speciesFlag); } + + + /** + * Check the percentage of clicks which are correctly classified by the data selector. + * @param clickTrain - the click train + * @return true if the percentage critera is passed + * + */ + private boolean isPercClicks(CTDataUnit clickTrain) { + + //no point iterating through click if we do not need to. + if (clssfrParams.minPercentage==0) return true; + + double count = 0; + for (int i=0; i0) { + count = count+1.; + } + } + if (count/clickTrain.getSubDetectionsCount()>=clssfrParams.minPercentage) { + return true; + } + else { + return false; + } + } + + + /** + * Get the data selector. + * @param source - the source data block + * @return the data selector. + */ + public void createDataSelector(PamDataBlock source) { + if (dataSelector==null || dataSelector.getPamDataBlock()!=source) { + //create the data selector + //System.out.println("Data selector: " + dataSelector); + if (source!=null) { + dataSelector=source.getDataSelectCreator().getDataSelector(clickTrainControl.getUnitName() + " " + clssfrParams.uniqueID + + "_X2_threshold_classifier", false, null); + //System.out.println("Data selector: " + dataSelector); + } + else { + dataSelector=null; + } + } + } + @Override public String getName() { @@ -122,7 +185,6 @@ public class Chi2ThresholdClassifier implements CTClassifier { public void setParams(Chi2ThresholdParams clssfrParams) { //System.out.println("HELLO CLASSIFIER PARAMS: " + clssfrParams.chi2Threahold ); this.clssfrParams=clssfrParams; - } /** @@ -145,4 +207,10 @@ public class Chi2ThresholdClassifier implements CTClassifier { } + public DataSelector getDataSelector() { + createDataSelector(clickTrainControl.getParentDataBlock()); + return dataSelector; + } + + } diff --git a/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdParams.java b/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdParams.java index 83f4d6e5..a174a915 100644 --- a/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdParams.java +++ b/src/clickTrainDetector/classification/simplechi2classifier/Chi2ThresholdParams.java @@ -1,5 +1,8 @@ package clickTrainDetector.classification.simplechi2classifier; +import java.io.Serializable; +import java.util.UUID; + import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import clickTrainDetector.classification.CTClassifierParams; @@ -12,9 +15,10 @@ import clickTrainDetector.classification.CTClassifierType; * @author Jamie Macaulay * */ -public class Chi2ThresholdParams extends CTClassifierParams implements ManagedParameters { +public class Chi2ThresholdParams extends CTClassifierParams implements ManagedParameters, Serializable, Cloneable { public Chi2ThresholdParams(){ + super(); super.type=CTClassifierType.CHI2THRESHOLD; } @@ -25,7 +29,7 @@ public class Chi2ThresholdParams extends CTClassifierParams implements ManagedPa /** * The chi2 threshold to set. This is the chi2 value divided by the number of clicks in the train. - * If zero then the classification always passes...A bit fo a hack for testing + * If zero then the classification always passes...A bit of a hack for testing */ public double chi2Threshold = 1500.; @@ -33,7 +37,13 @@ public class Chi2ThresholdParams extends CTClassifierParams implements ManagedPa * The minimum number of clicks. */ public int minClicks = 5; - + + /** + * The minimum %percentage which must the parent data selector of clicks/ + *. values are 0-1. + */ + public double minPercentage = 0; + /** * The minimum time in seconds. */ diff --git a/src/clickTrainDetector/classification/standardClassifier/StandardClassifier.java b/src/clickTrainDetector/classification/standardClassifier/StandardClassifier.java index f340d9d3..eef29887 100644 --- a/src/clickTrainDetector/classification/standardClassifier/StandardClassifier.java +++ b/src/clickTrainDetector/classification/standardClassifier/StandardClassifier.java @@ -130,7 +130,7 @@ public class StandardClassifier implements CTClassifier { int speciesID = standardClssfrParams.speciesFlag; - System.out.println("Standard Classificiation: " ); + //System.out.println("Standard Classificiation: " ); //all classifiers have to pass. CTClassification[] ctClassification = new CTClassification[classifiers.size()]; @@ -138,8 +138,8 @@ public class StandardClassifier implements CTClassifier { 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]); +// 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){ @@ -148,7 +148,7 @@ public class StandardClassifier implements CTClassifier { } } - System.out.println("SPECIES ID: " + speciesID); + //System.out.println("SPECIES ID: " + speciesID); //create the classification. StandardClassification classification = new StandardClassification(ctClassification, speciesID); diff --git a/src/clickTrainDetector/classification/standardClassifier/StandardClassifierParams.java b/src/clickTrainDetector/classification/standardClassifier/StandardClassifierParams.java index a3d13e0e..25eb5775 100644 --- a/src/clickTrainDetector/classification/standardClassifier/StandardClassifierParams.java +++ b/src/clickTrainDetector/classification/standardClassifier/StandardClassifierParams.java @@ -1,5 +1,7 @@ package clickTrainDetector.classification.standardClassifier; +import java.util.UUID; + import clickTrainDetector.classification.CTClassifierParams; import clickTrainDetector.classification.CTClassifierType; @@ -27,6 +29,7 @@ public class StandardClassifierParams extends CTClassifierParams { public StandardClassifierParams(){ + super(); ///very important to set this or else the clasifier manager does not //know which classifier to create. type = CTClassifierType.STANDARDCLASSIFIER; diff --git a/src/clickTrainDetector/classification/templateClassifier/TemplateClassifierParams.java b/src/clickTrainDetector/classification/templateClassifier/TemplateClassifierParams.java index cd781ca7..e4d15845 100644 --- a/src/clickTrainDetector/classification/templateClassifier/TemplateClassifierParams.java +++ b/src/clickTrainDetector/classification/templateClassifier/TemplateClassifierParams.java @@ -22,6 +22,7 @@ public class TemplateClassifierParams extends CTClassifierParams implements Mana private static final long serialVersionUID = 10L; public TemplateClassifierParams(){ + super(); super.type=CTClassifierType.TEMPLATECLASSIFIER; // chi2ThresholdParams = new Chi2ThresholdParams(); // template = DefualtSpectrumTemplates.getTemplate(SpectrumTemplateType.BEAKED_WHALE); diff --git a/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/AmplitudeChi2.java b/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/AmplitudeChi2.java index 68f45902..cff7d705 100644 --- a/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/AmplitudeChi2.java +++ b/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/AmplitudeChi2.java @@ -39,7 +39,10 @@ public class AmplitudeChi2 extends SimpleChi2VarDelta { @Override public double getDiffValue(PamDataUnit pamDataUnit0, PamDataUnit pamDataUnit1) { //System.out.println("DB: " + pamDataUnit0.getAmplitudeDB()); - return pamDataUnit0.getAmplitudeDB()-pamDataUnit1.getAmplitudeDB(); + //made this abs so it can deal with increasing then decreasing click trains. i.e. + //the click trian is not penalised if it gradually increasing then starts to gradually decrease + //in amplitude. + return Math.abs(pamDataUnit0.getAmplitudeDB()-pamDataUnit1.getAmplitudeDB()); } @Override diff --git a/src/clickTrainDetector/dataselector/CTDataSelector.java b/src/clickTrainDetector/dataselector/CTDataSelector.java index d607c87d..97e14554 100644 --- a/src/clickTrainDetector/dataselector/CTDataSelector.java +++ b/src/clickTrainDetector/dataselector/CTDataSelector.java @@ -1,9 +1,12 @@ package clickTrainDetector.dataselector; +import java.util.Arrays; + import PamDetection.LocContents; import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelectParams; import PamguardMVC.dataSelector.DataSelector; +import clickTrainDetector.CTDataUnit; import clickTrainDetector.CTDetectionGroupDataUnit; import clickTrainDetector.ClickTrainControl; import clickTrainDetector.ClickTrainDataBlock; @@ -43,6 +46,8 @@ public class CTDataSelector extends DataSelector { private boolean allowScores; + private boolean[] useSpeciesList; + public CTDataSelector(ClickTrainControl clickTrainControl, ClickTrainDataBlock clickTrainDataBlock, String selectorName, boolean allowScores) { super(clickTrainDataBlock, selectorName, allowScores); @@ -83,6 +88,7 @@ public class CTDataSelector extends DataSelector { @Override public DataSelectParams getParams() { + getDialogPanel().getParams(ctSelectParams); return ctSelectParams; } @@ -99,6 +105,8 @@ public class CTDataSelector extends DataSelector { CTDetectionGroupDataUnit ctDataUnit = (CTDetectionGroupDataUnit) pamDataUnit; if (ctDataUnit.getSubDetectionsCount()(parentFrame, setPane, false); } - if (classificationTab!=null) settingsPane.setTab(classificationTab ? 2 : 0); //set the tab to the classification tab + if (classificationTab!=null && classificationTab) settingsPane.setTab(2); //set the tab to the classification tab ClickTrainParams newParams = settingsDialog.showDialog(this.clickTrainControl.getClickTrainParams()); diff --git a/src/clickTrainDetector/layout/ClickTrainAlgorithmPaneFX.java b/src/clickTrainDetector/layout/ClickTrainAlgorithmPaneFX.java index f31c03eb..50af611e 100644 --- a/src/clickTrainDetector/layout/ClickTrainAlgorithmPaneFX.java +++ b/src/clickTrainDetector/layout/ClickTrainAlgorithmPaneFX.java @@ -324,7 +324,13 @@ public class ClickTrainAlgorithmPaneFX extends SettingsPane { popOver.showingProperty().addListener((obs, old, newval)->{ if (newval) { - clickTrainControl.getDataSelector().getDialogPaneFX().setParams(true); + //the dialog has opened + clickTrainControl.getDataSelector().getDialogPaneFX().setParams(true); + } + else { + //the dialog has closed + clickTrainControl.getDataSelector().getDialogPaneFX().getParams(true); + } }); diff --git a/src/clickTrainDetector/layout/PreClassifierPane.java b/src/clickTrainDetector/layout/PreClassifierPane.java index 3344ed93..9abca195 100644 --- a/src/clickTrainDetector/layout/PreClassifierPane.java +++ b/src/clickTrainDetector/layout/PreClassifierPane.java @@ -34,7 +34,7 @@ public class PreClassifierPane extends PamBorderPane { private Pane createClassifierPane() { - simpleCTClassifierPane = new SimpleCTClassifierPane(null); + simpleCTClassifierPane = new SimpleCTClassifierPane(clickTrainControl.getClassifierManager().getPreClassifier()); return (Pane) simpleCTClassifierPane.getContentNode(); } diff --git a/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierGraphics.java b/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierGraphics.java index 1e5f9c7e..8a27c478 100644 --- a/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierGraphics.java +++ b/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierGraphics.java @@ -35,14 +35,15 @@ public class SimpleCTClassifierGraphics implements CTClassifierGraphics { if (simpleCTClassiferPane==null) { simpleCTClassiferPane = new SimpleCTClassifierPane(simpleChi2Classifier); } - //params are set here. - simpleCTClassiferPane.setParams(simpleChi2Classifier.getParams()); + //System.out.println("SimpleCTClassifierGraphics getCTClassifierPane: " + simpleChi2Classifier.getParams().chi2Threshold); + //params are set here. <- do not do this because you may wish to store the classifier params somewhere else. + //simpleCTClassiferPane.setParams(simpleChi2Classifier.getParams()); return (Pane) simpleCTClassiferPane.getContentNode(); } @Override public CTClassifierParams getParams() { - Chi2ThresholdParams clssfrParams = simpleCTClassiferPane.getParams(simpleChi2Classifier.getParams()); + Chi2ThresholdParams clssfrParams = simpleCTClassiferPane.getParams(simpleChi2Classifier.getParams()).clone(); if (clssfrParams==null) { System.err.print("Simple Chi2 Classifier returned null params"); return null; @@ -51,13 +52,14 @@ public class SimpleCTClassifierGraphics implements CTClassifierGraphics { // simpleChi2Classifier.setParams(clssfrParams); // return clssfrParams; // } + //System.out.println("SimpleCTClassifierGraphics - getParams: " + clssfrParams.chi2Threshold); return clssfrParams; } @Override public void setParams(CTClassifierParams params) { simpleCTClassiferPane.setParams((Chi2ThresholdParams) params); - + //System.out.println("SimpleCTClassifierGraphics - setParams: " + ((Chi2ThresholdParams) params).chi2Threshold); } } diff --git a/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierPane.java b/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierPane.java index f7999529..d11e4c63 100644 --- a/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierPane.java +++ b/src/clickTrainDetector/layout/classification/simplechi2classifier/SimpleCTClassifierPane.java @@ -1,18 +1,32 @@ package clickTrainDetector.layout.classification.simplechi2classifier; +import java.text.DecimalFormat; + +import org.controlsfx.control.PopOver; + import PamController.SettingsPane; +import PamguardMVC.PamDataBlock; import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdClassifier; import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdParams; import javafx.geometry.Insets; import javafx.geometry.Orientation; +import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.CheckBox; +import javafx.scene.control.Control; import javafx.scene.control.Spinner; +import javafx.scene.control.SpinnerValueFactory; import javafx.scene.control.Tooltip; import javafx.scene.layout.Pane; +import javafx.util.StringConverter; +import pamViewFX.PamGuiManagerFX; +import pamViewFX.fxGlyphs.PamGlyphDude; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamSpinner; import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; import pamViewFX.fxNodes.utilsFX.ControlField; /** @@ -51,6 +65,17 @@ public class SimpleCTClassifierPane extends SettingsPane { */ private ControlField minTime; + private PamToggleSwitch dataSelectorCheckBox; + + private PamButton dataSelectorButton; + + private PopOver popOver; + + /** + * The minimum percentage of clicks for a certain class. + */ + private ControlField minPercClicks; + public SimpleCTClassifierPane(Chi2ThresholdClassifier simpleChi2Classifier) { super(null); this.simpleChi2Classifier=simpleChi2Classifier; @@ -96,17 +121,35 @@ public class SimpleCTClassifierPane extends SettingsPane { chi2Threshold.getSpinner().getValueFactory().setConverter(PamSpinner.createStringConverter(0)); chi2Threshold.setTooltip(new Tooltip( "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" + + "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("Min. Clicks ", "", 0, Integer.MAX_VALUE, 5); + minClicks = new ControlField("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); + + + minPercClicks = new ControlField("Min. % clicks ", "", 0, 100., 1.); + minPercClicks.setTooltip(new Tooltip( + "The minimum number of detections.")); + minPercClicks.getSpinner().setEditable(true); + SpinnerValueFactory valueFactory = new SpinnerValueFactory.DoubleSpinnerValueFactory(0, 100., 0., 1.); + valueFactory.setConverter(doubleConverter); + minPercClicks.getSpinner().setValueFactory(valueFactory); + minPercClicks.getSpinner().setEditable(true); + ///HACK to get the percentage sign to show? + minPercClicks.getSpinner().increment(); + minPercClicks.getSpinner().decrement(); - minTime = new ControlField("Min. Time ", "s", 0.0, Double.MAX_VALUE, 1.0); + minPercClicks.getLabel1().setPrefWidth(LABEL_WIDTH); + + minPercClicks.getChildren().add( createDataSelectorPane()); + + + minTime = new ControlField("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.")); @@ -115,29 +158,83 @@ public class SimpleCTClassifierPane extends SettingsPane { chi2Threshold.getSpinner().setEditable(true); - vBox.getChildren().addAll(chi2Threshold, minClicks, minTime); + vBox.getChildren().addAll(chi2Threshold, minClicks, minPercClicks, minTime); return vBox; } + + /** + * Create the data selector. + * @return the data selector. + */ + private Pane createDataSelectorPane() { + PamHBox hbox = new PamHBox(); + hbox.setSpacing(5); + hbox.setAlignment(Pos.CENTER_LEFT); + + dataSelectorButton = new PamButton(); +// dataSelectorButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.SETTINGS, PamGuiManagerFX.iconSize)); + dataSelectorButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconSize)); + dataSelectorButton.setOnAction((action)->{ + showDataSelectorPane(); + }); + hbox.getChildren().addAll(dataSelectorButton); + return hbox; + } + + + /** + * Creates pane allowing the user to change fine scale things such as error limits. + * @return the pop over pane. + */ + public void showDataSelectorPane() { + + if (popOver==null) { + popOver = new PopOver(); + PamBorderPane holder = new PamBorderPane(simpleChi2Classifier.getDataSelector().getDialogPaneFX().getContentNode()); + holder.setPadding(new Insets(5,5,5,5)); + popOver.setContentNode(holder); + } + + popOver.showingProperty().addListener((obs, old, newval)->{ + if (newval) { +// System.out.println("Data Selector: " + simpleChi2Classifier.getDataSelector()); + simpleChi2Classifier.getDataSelector().getDialogPaneFX().setParams(true); + } + else { + simpleChi2Classifier.getDataSelector().getDialogPaneFX().getParams(true); + } + }); + + popOver.show(dataSelectorButton); + } @Override public Chi2ThresholdParams getParams(Chi2ThresholdParams currParams) { + //System.out.println("Get PERC spinner value; " + minPercClicks.getSpinner().getValue()); currParams.chi2Threshold=chi2Threshold.getSpinner().getValue(); //HACK - for some reason Integer spinner is returning a double currParams.minClicks=minClicks.getSpinner().getValue().intValue(); currParams.minTime=minTime.getSpinner().getValue(); + currParams.minPercentage=minPercClicks.getSpinner().getValue()/100; + + if (simpleChi2Classifier!=null && simpleChi2Classifier.getDataSelector()!=null) { + simpleChi2Classifier.getDataSelector().getDialogPaneFX().getParams(true); + } return currParams; } @Override public void setParams(Chi2ThresholdParams input) { + chi2Threshold.getSpinner().getValueFactory().setValue(input.chi2Threshold); //HACK - for some reason Integer spinner is returning a double minClicks.getSpinner().getValueFactory().setValue((double) input.minClicks); minTime.getSpinner().getValueFactory().setValue(input.minTime); + minPercClicks.getSpinner().getValueFactory().setValue(input.minPercentage*100.); } @Override @@ -164,6 +261,24 @@ public class SimpleCTClassifierPane extends SettingsPane { } + + StringConverter doubleConverter = new StringConverter() { + private final DecimalFormat df = new DecimalFormat("###.#"); + @Override + public String toString(Double object) { + if (object == null) {return "";} + return df.format(object)+"%";} + @Override + public Double fromString(String string) { + try { + if (string == null) {return null;} + string = string.trim(); + if (string.length() < 1) {return null;} + return df.parse(string).doubleValue(); + } catch (Exception ex) {throw new RuntimeException(ex);} + } + }; + diff --git a/src/clickTrainDetector/layout/classification/templateClassifier/TemplateClassifierPane.java b/src/clickTrainDetector/layout/classification/templateClassifier/TemplateClassifierPane.java index b7d9f913..77193e4b 100644 --- a/src/clickTrainDetector/layout/classification/templateClassifier/TemplateClassifierPane.java +++ b/src/clickTrainDetector/layout/classification/templateClassifier/TemplateClassifierPane.java @@ -118,6 +118,8 @@ public class TemplateClassifierPane extends SettingsPane{ + setCheckBoxEnable(); + }); + constraints.gridy++; + + classifierCheckBoxes = new JCheckBox[classifcationManager.getCurrentClassifiers().size()]; + for (int i=0; i { pruneStartSpinner.setEditable(true); pruneBackSpinner.setTooltip(new Tooltip("The minimum number of detections before pruning starts.")); gridPane.add(pruneStartSpinner, 1, gridY); - gridY++; + gridY=0; - gridPane.add(new Label("Max no. coasts"), 0, gridY); + gridPane.add(new Label(" Max no. coasts"), 2, gridY); nCoastsSpinner = new PamSpinner(0,Integer.MAX_VALUE,3,1); nCoastsSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); nCoastsSpinner.setPrefWidth(70); nCoastsSpinner.setEditable(true); nCoastsSpinner.setTooltip(new Tooltip("The maximum number of missing detections before a track is closed")); - gridPane.add(nCoastsSpinner, 1, gridY); + gridPane.add(nCoastsSpinner, 3, gridY); gridY++; - gridPane.add(new Label("Max no. trains"), 0, gridY); + gridPane.add(new Label(" Max no. trains"), 2, gridY); nHoldSpinner = new PamSpinner(0,1000,3,1); nHoldSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); nHoldSpinner.setPrefWidth(70); nHoldSpinner.setEditable(true); nHoldSpinner.setTooltip(new Tooltip("The maximum number of unique click trains that can be tracked at the same time.")); - gridPane.add(nHoldSpinner, 1, gridY); + gridPane.add(nHoldSpinner, 3, gridY); pamVBox.getChildren().addAll(label, gridPane); diff --git a/src/dataMap/SummaryPanel.java b/src/dataMap/SummaryPanel.java index abc1e993..5786a331 100644 --- a/src/dataMap/SummaryPanel.java +++ b/src/dataMap/SummaryPanel.java @@ -99,8 +99,8 @@ public class SummaryPanel extends HidingDialogComponent { dataEnds[i].setText(" ---No data---"); } else { - dataStarts[i].setText(PamCalendar.formatDateTime2(dataExtent[0], true)); - dataEnds[i].setText(PamCalendar.formatDateTime(dataExtent[1], true)); + dataStarts[i].setText(PamCalendar.formatDateTime2(dataExtent[0], true)); + dataEnds[i].setText(PamCalendar.formatDateTime(dataExtent[1], true)); } } for (int i = offlineDataStores.size(); i < maxDataSources; i++) { diff --git a/src/detectionPlotFX/layout/DetectionPlotDisplay.java b/src/detectionPlotFX/layout/DetectionPlotDisplay.java index 713274d4..865eecb9 100644 --- a/src/detectionPlotFX/layout/DetectionPlotDisplay.java +++ b/src/detectionPlotFX/layout/DetectionPlotDisplay.java @@ -447,6 +447,11 @@ public class DetectionPlotDisplay extends PamBorderPane implements UserDisplayNo private void drawDataUnit(PamDataUnit newDataUnit) { //Debug.out.println("DetectionPlotDisplay DrawDataUnit: " +newDataUnit); if (currentDataInfo!=null){ + //sometimes the axis just need a little push to make sure the pane and axis object bindings have been updated + for (int i=0; i implements Detection } g2.strokePolyline(scaledDataX, scaledDataY, scaledDataY.length-1); if (fillSpectrum) { - // System.out.println("Last point: " + x0 + " y0 " + r.getHeight()); + //System.out.println("Last point: " + x0 + " y0 " + y0 + " " + scale + " " + clickLineData[iChan][0] + " " + projector.getAxis(Side.LEFT).getMinVal() + " " +projector.getAxis(Side.LEFT).getTotalPixels()); scaledDataX[scaledDataX.length-1]=x0; // the last x position scaledDataY[scaledDataY.length-1]= r.getHeight() ; //return the line to zero for polygon drawing // PamUtils.PamArrayUtils.printArray(scaledDataY); diff --git a/src/fileOfflineData/OfflineFileList.java b/src/fileOfflineData/OfflineFileList.java new file mode 100644 index 00000000..5395c230 --- /dev/null +++ b/src/fileOfflineData/OfflineFileList.java @@ -0,0 +1,88 @@ +package fileOfflineData; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; + +/** + * Make a list of files with the given file filter. + * @author dg50 + * + */ +public class OfflineFileList { + + private ArrayList files = new ArrayList<>(); + private String folder; + private FileFilter fileFilter; + private boolean includeSubFolders; + + public OfflineFileList(String folder, FileFilter fileFilter, boolean includeSubFolders) { + this.folder = folder; + this.fileFilter = fileFilter; + this.includeSubFolders = includeSubFolders; + updateCatalog(); + } + + public int updateCatalog() { + files.clear(); + File current = new File(this.folder); + addFiles(current); + return files.size(); + } + + private void addFiles(File current) { + if (current.exists() == false) { + return; + } + if (current.isFile() && checkFilter(current)) { + /* + * This can only really happen if the root passed in is a file, not a + * folder, since files within the folder structure will get + * added from the loop below. + */ + files.add(current); + } + else if (current.isDirectory()) { + File[] filesList = current.listFiles(); + if (filesList == null) { + return; + } + for (int i = 0; i < filesList.length; i++) { + File aFile = filesList[i]; + if (aFile.isFile() && checkFilter(aFile)) { + files.add(aFile); + } + else if (aFile.isDirectory() && includeSubFolders) { + addFiles(aFile); + } + } + } + } + + /** + * Check that if there is a filter, the file is accepted. + * @param aFile + * @return + */ + private boolean checkFilter(File aFile) { + if (fileFilter == null) { + return true; + } + return fileFilter.accept(aFile); + } + + /** + * Get a list of files in the catalog as a simple string list. + * @return files as strings + */ + public String[] asStringList() { + if (files == null) { + return null; + } + String[] str = new String[files.size()]; + for (int i = 0; i < files.size(); i++) { + str[i] = files.get(i).getAbsolutePath(); + } + return str; + } +} diff --git a/src/generalDatabase/backup/DatabaseBackupStream.java b/src/generalDatabase/backup/DatabaseBackupStream.java index fc6f4d47..d7808a96 100644 --- a/src/generalDatabase/backup/DatabaseBackupStream.java +++ b/src/generalDatabase/backup/DatabaseBackupStream.java @@ -120,6 +120,9 @@ public class DatabaseBackupStream extends FileBackupStream { public FileLocation getSourceLocation() { File dbFile = getDatabaesFile(); + if (dbFile == null) { + return null; + } FileLocation sl = new FileLocation(); sl.path = dbFile.getAbsolutePath(); sl.canEditMask = false; diff --git a/src/pamScrollSystem/AbstractPamScroller.java b/src/pamScrollSystem/AbstractPamScroller.java index 95afb5eb..54f441e7 100644 --- a/src/pamScrollSystem/AbstractPamScroller.java +++ b/src/pamScrollSystem/AbstractPamScroller.java @@ -44,6 +44,14 @@ public abstract class AbstractPamScroller implements DataTimeLimits { */ protected double[] playSpeeds = {.1, 0.25, .5, 1.0, 2, 5, 10}; +// private long realTimerStart; +// +// private long timerStartPosition; + + private long timerLastCurrentTime; + +// private long timerTimeMillis; + public AbstractPamScroller(String name, int orientation, int stepSizeMillis, long defaultLoadTime, boolean hasMenu){ @@ -630,6 +638,8 @@ public abstract class AbstractPamScroller implements DataTimeLimits { playTimerAction(); } }); + timerLastCurrentTime = System.currentTimeMillis(); +// timeLastValue = getValueMillis(); playTimer.start(); playbackStarted(); } @@ -671,11 +681,17 @@ public abstract class AbstractPamScroller implements DataTimeLimits { stopPlayback(); return; } - long step = Math.max((long) (timerInterval * scrollerData.getPlaySpeed()), 1); - setValueMillis(pos+step); - if (pos == getValueMillis()) { - playTimer.setDelay(playTimer.getDelay()*2); - } + /** + * Since the action on setValue can take quite some time, the timer can run slow + * if we just increment by what we think this delay is, so increment the value + * based on how much system time has elapsed between the last call and now + */ + long now = System.currentTimeMillis(); + int elapsed = (int) (now - timerLastCurrentTime); + int toAdd = (int) (elapsed*scrollerData.getPlaySpeed()); + timerLastCurrentTime = now; + + setValueMillis(getValueMillis()+toAdd); } } diff --git a/src/pamViewFX/fxNodes/pamAxis/PamAxisPane2.java b/src/pamViewFX/fxNodes/pamAxis/PamAxisPane2.java index 3b80847b..905109bc 100644 --- a/src/pamViewFX/fxNodes/pamAxis/PamAxisPane2.java +++ b/src/pamViewFX/fxNodes/pamAxis/PamAxisPane2.java @@ -93,7 +93,7 @@ public class PamAxisPane2 extends StackPane { this.getChildren().add(mainPane); } - private void layoutAxis(){ + public void layoutAxis(){ mainPane.setCenter(null); diff --git a/src/pamViewFX/fxNodes/utilityPanes/MinMaxPane.java b/src/pamViewFX/fxNodes/utilityPanes/MinMaxPane.java index 82f64c6d..b4942247 100644 --- a/src/pamViewFX/fxNodes/utilityPanes/MinMaxPane.java +++ b/src/pamViewFX/fxNodes/utilityPanes/MinMaxPane.java @@ -1,7 +1,6 @@ package pamViewFX.fxNodes.utilityPanes; import java.io.Serializable; - import javafx.geometry.Pos; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; diff --git a/src/spectrogramNoiseReduction/layoutFX/SpectrogramNoisePaneFX.java b/src/spectrogramNoiseReduction/layoutFX/SpectrogramNoisePaneFX.java index cdc4d836..60f67120 100644 --- a/src/spectrogramNoiseReduction/layoutFX/SpectrogramNoisePaneFX.java +++ b/src/spectrogramNoiseReduction/layoutFX/SpectrogramNoisePaneFX.java @@ -3,12 +3,10 @@ package spectrogramNoiseReduction.layoutFX; import java.util.ArrayList; -import org.controlsfx.control.ToggleSwitch; - import pamViewFX.PamGuiManagerFX; -import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; import pamViewFX.fxNodes.utilityPanes.SourcePaneFX; import spectrogramNoiseReduction.SpecNoiseMethod; import spectrogramNoiseReduction.SpectrogramNoiseProcess; @@ -16,10 +14,8 @@ import spectrogramNoiseReduction.SpectrogramNoiseSettings; 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.Tooltip; -import javafx.scene.layout.Pane; import PamController.SettingsPane; import PamguardMVC.PamDataBlock; @@ -39,7 +35,7 @@ public class SpectrogramNoisePaneFX extends SettingsPane Date: Tue, 7 Jun 2022 18:06:47 +0100 Subject: [PATCH 18/37] sorting sup and super detections (again) --- src/PamguardMVC/superdet/SuperDetDataBlock.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PamguardMVC/superdet/SuperDetDataBlock.java b/src/PamguardMVC/superdet/SuperDetDataBlock.java index 560ade53..581c0b92 100644 --- a/src/PamguardMVC/superdet/SuperDetDataBlock.java +++ b/src/PamguardMVC/superdet/SuperDetDataBlock.java @@ -471,6 +471,7 @@ public class SuperDetDataBlock Date: Wed, 8 Jun 2022 10:06:25 +0100 Subject: [PATCH 19/37] Merge pull request #3 from PAMGuard/main (#35) From cc3889a95f66991ece93791f87ba818e1121b61b Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 9 Jun 2022 16:06:00 +0100 Subject: [PATCH 20/37] Extra option in Generic dialog to specify location on screen. --- src/PamView/dialog/GenericSwingDialog.java | 46 +++++++++++++++++-- .../paneloverlay/OverlayDataManager.java | 1 - 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/PamView/dialog/GenericSwingDialog.java b/src/PamView/dialog/GenericSwingDialog.java index 8d0177a9..208bd386 100644 --- a/src/PamView/dialog/GenericSwingDialog.java +++ b/src/PamView/dialog/GenericSwingDialog.java @@ -1,5 +1,8 @@ package PamView.dialog; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Toolkit; import java.awt.Window; import javax.swing.BoxLayout; @@ -8,9 +11,9 @@ import javax.swing.JPanel; import javax.swing.border.EmptyBorder; public class GenericSwingDialog extends PamDialog { - + private boolean allOk; - + private PamDialogPanel[] dialogPanels; private GenericSwingDialog(Window parentFrame, String title, PamDialogPanel ...dialogPanels) { @@ -25,15 +28,48 @@ public class GenericSwingDialog extends PamDialog { } mainPanel.add(comp); } - + setDialogComponent(mainPanel); } - - public static boolean showDialog(Window parentFrame, String title, PamDialogPanel ...dialogPanels) { + public static boolean showDialog(Window parentFrame, String title, PamDialogPanel ...dialogPanels) { + return showDialog(parentFrame, title, null, dialogPanels); + } + + /** + * Show dialog at a specific location on the screen. + * @param parentFrame + * @param title + * @param screenPoint + * @param dialogPanels + * @return + */ + public static boolean showDialog(Window parentFrame, String title, Point screenPoint, PamDialogPanel ...dialogPanels) { GenericSwingDialog swingDialog = new GenericSwingDialog(parentFrame, title, dialogPanels); swingDialog.setParams(); swingDialog.pack(); + if (screenPoint != null) { + try { + // check we're not going too far off the screen. + Dimension sz = swingDialog.getPreferredSize(); + Dimension screen = null; + if (parentFrame != null) { + screen = parentFrame.getSize(); + } + else { + screen = Toolkit.getDefaultToolkit().getScreenSize(); + } + screenPoint.y = Math.min(screenPoint.y, screen.height-sz.height-10); + screenPoint.y = Math.max(screenPoint.y, 0); + screenPoint.x = Math.min(screenPoint.x, screen.width-sz.width-10); + screenPoint.x = Math.max(screenPoint.x, 0); + + swingDialog.setLocation(screenPoint); + } + catch (Exception e) { + // shouldn't happen, but if it does, it doesn't matter much + } + } swingDialog.setVisible(true); return swingDialog.allOk; } diff --git a/src/PamView/paneloverlay/OverlayDataManager.java b/src/PamView/paneloverlay/OverlayDataManager.java index 75d7a1a4..c633ed3d 100644 --- a/src/PamView/paneloverlay/OverlayDataManager.java +++ b/src/PamView/paneloverlay/OverlayDataManager.java @@ -42,7 +42,6 @@ public abstract class OverlayDataManager Date: Sat, 11 Jun 2022 11:36:36 +0100 Subject: [PATCH 21/37] Fixes to ROCCA memory problem. See https://github.com/PAMGuard/PAMGuard/issues/36 Made a temp build of this for testing. --- .settings/org.eclipse.jdt.core.prefs | 5 +++++ pom.xml | 2 +- src/IshmaelLocator/IshLocProcess.java | 4 ++++ src/PamController/PamguardVersionInfo.java | 4 ++-- src/PamModel/PamDependency.java | 14 +++++++++++--- src/PamModel/PamPluginInterface.java | 2 ++ src/rocca/RoccaControl.java | 19 +++++++++++++++++++ src/rocca/RoccaProcess.java | 8 ++++++++ src/rocca/RoccaSidePanel.java | 3 +++ src/rocca/RoccaWhistleSelect.java | 10 ++++++++++ 10 files changed, 65 insertions(+), 6 deletions(-) diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index e8c450c0..fbf1aacf 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,7 +1,12 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error diff --git a/pom.xml b/pom.xml index 324409a6..cfccc70f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.pamguard Pamguard - 2.02.03 + 2.02.04 Pamguard Java12+ Pamguard for Java 12+, using Maven to control dependcies www.pamguard.org diff --git a/src/IshmaelLocator/IshLocProcess.java b/src/IshmaelLocator/IshLocProcess.java index 144dec7a..cdec7212 100644 --- a/src/IshmaelLocator/IshLocProcess.java +++ b/src/IshmaelLocator/IshLocProcess.java @@ -380,6 +380,10 @@ abstract public class IshLocProcess extends PamProcess implements SpectrogramMar } else { newTime = oldTime*2; } + /* + * Above causes same error as in Rocca and needs fixing + */ + newTime = Math.min(newTime, 600000); // don't let this exceed 10 minutes. // System.out.println("Adjusting raw data natural lifetime from " + oldTime + " ms to " + newTime + " ms"); daqBlock.setNaturalLifetimeMillis(newTime); // increase the lifetime to try and prevent this from happening again return; diff --git a/src/PamController/PamguardVersionInfo.java b/src/PamController/PamguardVersionInfo.java index 1217f1d8..2c91e596 100644 --- a/src/PamController/PamguardVersionInfo.java +++ b/src/PamController/PamguardVersionInfo.java @@ -31,12 +31,12 @@ public class PamguardVersionInfo { * Version number, major version.minorversion.sub-release. * Note: can't go higher than sub-release 'f' */ - static public final String version = "2.02.03"; + static public final String version = "2.02.04"; /** * Release date */ - static public final String date = "8 February 2022"; + static public final String date = "11 June 2022"; // /** // * Release type - Beta or Core diff --git a/src/PamModel/PamDependency.java b/src/PamModel/PamDependency.java index a6818972..072a0b0f 100644 --- a/src/PamModel/PamDependency.java +++ b/src/PamModel/PamDependency.java @@ -20,12 +20,22 @@ public class PamDependency { private String dataBlockName; + /** + * + * @param requiredDataType Class of Data unit + * @param defaultProvider Class name of default provider. + * @param dataBlockName Specific data block name + */ public PamDependency(Class requiredDataType, String defaultProvider, String dataBlockName) { this.requiredDataType = requiredDataType; this.defaultProvider = defaultProvider; this.dataBlockName = dataBlockName; } + /** + * @param requiredDataType Class of Data unit + * @param defaultProvider Class name of default provider. + */ public PamDependency(Class requiredDataType, String defaultProvider) { this.requiredDataType = requiredDataType; this.defaultProvider = defaultProvider; @@ -41,12 +51,10 @@ public class PamDependency { /** * @return Returns the requiredDataType. */ -// public DataType getRequiredDataType() { -// return requiredDataType; -// } public Class getRequiredDataType() { return requiredDataType; } + /** * @return Returns the dataBlockName. */ diff --git a/src/PamModel/PamPluginInterface.java b/src/PamModel/PamPluginInterface.java index 84d76c17..13539613 100644 --- a/src/PamModel/PamPluginInterface.java +++ b/src/PamModel/PamPluginInterface.java @@ -67,6 +67,8 @@ public interface PamPluginInterface extends CommonPluginInterface { * A short description of the plug in module. This text is used in various informational * windows displayed to the user. The value returned here is typically the same as the text returned * from the {@link #getDefaultName() getDefaultName()} method (e.g. Click Detector or FFT (Spectrogram) Engine). + *
This is the text used in the main 'Add Modules' menus and is used as the + * second argument to PamModuleInfo.registerControlledUnit *

* This field cannot be null. * @return String describing the plugin. Cannot be null. diff --git a/src/rocca/RoccaControl.java b/src/rocca/RoccaControl.java index 2d67789b..c3881b17 100644 --- a/src/rocca/RoccaControl.java +++ b/src/rocca/RoccaControl.java @@ -69,6 +69,11 @@ public class RoccaControl extends PamControlledUnit implements PamSettings { */ public static final String unitType = "Rocca"; + /* + * Max max data keep time to avoid memory overflows. Currently at 15 minutes. + */ + private static final int MAXMAXDATAKEEPTIME = 900000; + /** * reference to the ClickControl module when it is loaded in Viewer mode */ @@ -193,6 +198,17 @@ public class RoccaControl extends PamControlledUnit implements PamSettings { return 1; } + /** + * A bit of a fudge to deal with some old code which kept doubling the + * keep time of the raw data, which eventually led to raw data using too + * much memory and bringing down PG. I don't fully understand that code so + * have left as much of it as possible, but put this in as an absolute + * maximum which cannot be exceeded. + * @return max in milliseconds as int, which is what's used in PAMDataBlock. + */ + public int getMaxDataKeepTime() { + return MAXMAXDATAKEEPTIME; + } /** * * @param eventList @@ -242,18 +258,21 @@ public class RoccaControl extends PamControlledUnit implements PamSettings { (rcdb.getContour().get(RoccaContourStats.ParamIndx.SNR) > 35. || rcdb.getContour().get(RoccaContourStats.ParamIndx.DURATION) < 0.005 || rcdb.getContour().get(RoccaContourStats.ParamIndx.DURATION) > 0.6 )) { + rcdb.setNaturalLifetimeMillis(0); return; } if (roccaParameters.roccaClassifierModelFilename.getName().equals("HIClick.model") && (rcdb.getContour().get(RoccaContourStats.ParamIndx.SNR) > 40. || rcdb.getContour().get(RoccaContourStats.ParamIndx.DURATION) < 0.01 || rcdb.getContour().get(RoccaContourStats.ParamIndx.DURATION) > 0.6 )) { + rcdb.setNaturalLifetimeMillis(0); return; } if (roccaParameters.roccaClassifierModelFilename.getName().equals("NWAtlClick.model") && (rcdb.getContour().get(RoccaContourStats.ParamIndx.SNR) > 35. || rcdb.getContour().get(RoccaContourStats.ParamIndx.DURATION) < 0.005 || rcdb.getContour().get(RoccaContourStats.ParamIndx.DURATION) > 0.6 )) { + rcdb.setNaturalLifetimeMillis(0); return; } diff --git a/src/rocca/RoccaProcess.java b/src/rocca/RoccaProcess.java index e5f3d50d..86b5502b 100644 --- a/src/rocca/RoccaProcess.java +++ b/src/rocca/RoccaProcess.java @@ -363,6 +363,9 @@ public class RoccaProcess extends PamProcess { // 2017/12/4 set the natural lifetime to Integer.Max, so that we definitely keep all of the data // units during this code block. Set the lifetime back to 0 at the end of the block + /* + * DG June '22 made sure this is the case when the function returns early ! + */ rcdb.setNaturalLifetimeMillis(Integer.MAX_VALUE); rcdb.calculateStatistics(); @@ -385,6 +388,7 @@ public class RoccaProcess extends PamProcess { rcdb.getContour().get(RoccaContourStats.ParamIndx.DURATION) > 1.5 || rcdb.getContour().get(RoccaContourStats.ParamIndx.FREQABSSLOPEMEAN) < 2000. || rcdb.getContour().get(RoccaContourStats.ParamIndx.FREQABSSLOPEMEAN) > 28000. )) { + rcdb.setNaturalLifetimeMillis(0); return; } if (roccaControl.roccaParameters.roccaClassifierModelFilename.getName().equals("HIWhist.model") && @@ -398,6 +402,7 @@ public class RoccaProcess extends PamProcess { rcdb.getContour().get(RoccaContourStats.ParamIndx.FREQABSSLOPEMEAN) > 60000. || rcdb.getContour().get(RoccaContourStats.ParamIndx.FREQRANGE) < 800. || rcdb.getContour().get(RoccaContourStats.ParamIndx.FREQRANGE) > 14000. )) { + rcdb.setNaturalLifetimeMillis(0); return; } if (roccaControl.roccaParameters.roccaClassifierModelFilename.getName().equals("NWAtlWhist.model") && @@ -407,6 +412,7 @@ public class RoccaProcess extends PamProcess { rcdb.getContour().get(RoccaContourStats.ParamIndx.DURATION) > 2.5 || rcdb.getContour().get(RoccaContourStats.ParamIndx.FREQABSSLOPEMEAN) < 9100. || rcdb.getContour().get(RoccaContourStats.ParamIndx.FREQABSSLOPEMEAN) > 82000. )) { + rcdb.setNaturalLifetimeMillis(0); return; } } @@ -743,6 +749,8 @@ public class RoccaProcess extends PamProcess { } else { newTime = prdb.getNaturalLifetimeMillis()*2; } + // stop it getting silly. + newTime = Math.min(newTime, roccaControl.getMaxDataKeepTime()); prdb.setNaturalLifetimeMillis(newTime); // increase the lifetime to try and prevent this from happening again return null; } diff --git a/src/rocca/RoccaSidePanel.java b/src/rocca/RoccaSidePanel.java index 879c5fee..281724cc 100644 --- a/src/rocca/RoccaSidePanel.java +++ b/src/rocca/RoccaSidePanel.java @@ -114,7 +114,10 @@ public class RoccaSidePanel extends PamObserverAdapter implements PamSidePanel this.rsdb = new RoccaSightingDataBlock (roccaControl.roccaProcess, roccaControl.roccaParameters.getChannelMap()); + + // this one probably OK to never delete ? rsdb.setNaturalLifetimeMillis(Integer.MAX_VALUE); + rdl = new RoccaDetectionLogger(this, rsdb); rsdb.SetLogging(rdl); rsdb.setMixedDirection(PamDataBlock.MIX_INTODATABASE); diff --git a/src/rocca/RoccaWhistleSelect.java b/src/rocca/RoccaWhistleSelect.java index f75438a0..72f1f59e 100644 --- a/src/rocca/RoccaWhistleSelect.java +++ b/src/rocca/RoccaWhistleSelect.java @@ -171,6 +171,8 @@ public class RoccaWhistleSelect extends PamProcess implements SpectrogramMarkObs roccaControl.roccaSidePanel.sidePanel.addASighting(false); // if the user hit cancel, just exit if (dummy == RoccaSightingDataUnit.NONE) { + selectedWhistle.setNaturalLifetimeMillis(0); + selectedWhistleRaw.setNaturalLifetimeMillis(0); return; } } @@ -198,6 +200,14 @@ public class RoccaWhistleSelect extends PamProcess implements SpectrogramMarkObs selectedWhistleRaw, display, channel); + + /** + * DG June '22. + * I hope IT's K to set these back here. It's possible it's also + * done elsewhere. + */ + selectedWhistle.setNaturalLifetimeMillis(0); + selectedWhistleRaw.setNaturalLifetimeMillis(0); } } From d88da616822aa540af216868eba2949b276d6be4 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sat, 11 Jun 2022 19:06:34 +0100 Subject: [PATCH 22/37] Fix but in string annotation updates and a couple of features to allow gradual building of datamap in background while work continues. --- .classpath | 9 ++- dependency-reduced-pom.xml | 2 +- src/PamguardMVC/PamDataUnit.java | 66 +++++++++++++++----- src/annotation/string/StringDialogPanel.java | 17 +++-- src/dataMap/DataMapControl.java | 27 +++++++- src/dataMap/DataMapPanel.java | 7 +++ src/warnings/SingleLineWarningDisplay.java | 3 +- 7 files changed, 106 insertions(+), 25 deletions(-) diff --git a/.classpath b/.classpath index 3558670a..b2a2497b 100644 --- a/.classpath +++ b/.classpath @@ -1,8 +1,7 @@ - + - @@ -11,5 +10,11 @@ + + + + + + diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index 3d87c487..7ee84c51 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ org.pamguard Pamguard Pamguard Java12+ - 2.02.03 + 2.02.04 Pamguard for Java 12+, using Maven to control dependcies www.pamguard.org diff --git a/src/PamguardMVC/PamDataUnit.java b/src/PamguardMVC/PamDataUnit.java index 94514773..2a9994a6 100644 --- a/src/PamguardMVC/PamDataUnit.java +++ b/src/PamguardMVC/PamDataUnit.java @@ -943,23 +943,27 @@ abstract public class PamDataUnit } } - int nAttotations = getNumDataAnnotations(); - for (int i = 0; i < nAttotations; i++) { - DataAnnotation an = getDataAnnotation(i); - DataAnnotationType ant = an.getDataAnnotationType(); - String anName = ant.getAnnotationName(); - String anString = an.toString(); - if (anString == null) { - continue; - } - if (anString.contains("")) { - anString = anString.replace("", ""); - } - if (anString.contains("")) { - anString = anString.replace("", ""); - } - str += anName + ": " + anString + "
"; + String annotString = getAnnotationsSummaryString(); + if (annotString != null) { + str += annotString; } +// int nAttotations = getNumDataAnnotations(); +// for (int i = 0; i < nAttotations; i++) { +// DataAnnotation an = getDataAnnotation(i); +// DataAnnotationType ant = an.getDataAnnotationType(); +// String anName = ant.getAnnotationName(); +// String anString = an.toString(); +// if (anString == null) { +// continue; +// } +// if (anString.contains("")) { +// anString = anString.replace("", ""); +// } +// if (anString.contains("")) { +// anString = anString.replace("", ""); +// } +// str += anName + ": " + anString + "
"; +// } // add frequency and amplitude information @@ -999,6 +1003,36 @@ abstract public class PamDataUnit return str; } + /** + * Get string information for the annotations. Kept separate so + * it can be called in overridden version of getSummaryString() + * @return + */ + public String getAnnotationsSummaryString() { + int nAnnotations = getNumDataAnnotations(); + if (nAnnotations == 0) { + return null; + } + String str = ""; + for (int i = 0; i < nAnnotations; i++) { + DataAnnotation an = getDataAnnotation(i); + DataAnnotationType ant = an.getDataAnnotationType(); + String anName = ant.getAnnotationName(); + String anString = an.toString(); + if (anString == null) { + continue; + } + if (anString.contains("")) { + anString = anString.replace("", ""); + } + if (anString.contains("")) { + anString = anString.replace("", ""); + } + str += anName + ": " + anString + "
"; + } + return str.length() > 0 ? str : null; + } + /** * Some functions to do with data annotations */ diff --git a/src/annotation/string/StringDialogPanel.java b/src/annotation/string/StringDialogPanel.java index ccee5943..6aaafd93 100644 --- a/src/annotation/string/StringDialogPanel.java +++ b/src/annotation/string/StringDialogPanel.java @@ -10,6 +10,7 @@ import java.awt.event.WindowEvent; import javax.swing.JComponent; import javax.swing.SwingUtilities; +import PamUtils.PamCalendar; import annotation.AnnotationDialogPanel; import PamView.DBTextArea; import PamView.dialog.PamDialogPanel; @@ -51,13 +52,21 @@ public class StringDialogPanel implements AnnotationDialogPanel { note = note.trim(); } if (note != null && note.length() > 0) { - StringAnnotation an = (StringAnnotation) pamDataUnit.findDataAnnotation(StringAnnotation.class, - stringAnnotationType.getAnnotationName()); - if (an == null) { + StringAnnotation an; + /* + * always add a new annotation rather than editing the old one since + * the old one will get removed to handle annotation types which really + * do need to make a new one each time. + */ + +// = (StringAnnotation) pamDataUnit.findDataAnnotation(StringAnnotation.class, +// stringAnnotationType.getAnnotationName()); +// if (an == null) { an = new StringAnnotation(stringAnnotationType); pamDataUnit.addDataAnnotation(an); - } +// } an.setString(textArea.getText()); + pamDataUnit.setLastUpdateTime(PamCalendar.getTimeInMillis()); } return true; } diff --git a/src/dataMap/DataMapControl.java b/src/dataMap/DataMapControl.java index 1005e999..af4de85e 100644 --- a/src/dataMap/DataMapControl.java +++ b/src/dataMap/DataMapControl.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import javax.swing.SwingUtilities; import pamScrollSystem.AbstractScrollManager; - import generalDatabase.DBControlUnit; import binaryFileStorage.BinaryStore; import dataMap.layoutFX.DataMapGUIFX; @@ -240,6 +239,32 @@ public class DataMapControl extends PamControlledUnit implements PamSettings { public ArrayList getMappedDataBlocks() { return mappedDataBlocks; } + + /** + * Update a single data map. Useful when bringing in data + * from 'non standard' sources. Check overall time limits and + * redraw, but no total map creation. + * @param singleDataMap + */ + public void updateSingleDataMap(OfflineDataMap singleDataMap) { + long newLastTime = singleDataMap.getLastDataTime(); + long newFirstTime = singleDataMap.getFirstDataTime(); + if (mappedDataBlocks == null) { + return; + } + for (PamDataBlock aBlock : mappedDataBlocks) { + int nMaps = aBlock.getNumOfflineDataMaps(); + for (int iMap = 0; iMap < nMaps; iMap++) { + OfflineDataMap aMap = aBlock.getOfflineDataMap(iMap); + newFirstTime = Math.min(newFirstTime, aMap.getFirstDataTime()); + newLastTime = Math.max(newLastTime, aMap.getLastDataTime()); + } + } + firstTime = newFirstTime; + lastTime = newLastTime; + dataMapPanel.repaintAll(); + dataMapPanel.getSummaryPanel().newDataSources(); + } /** * @return the firstTime for any data in any data block diff --git a/src/dataMap/DataMapPanel.java b/src/dataMap/DataMapPanel.java index f5331804..7074ac9d 100644 --- a/src/dataMap/DataMapPanel.java +++ b/src/dataMap/DataMapPanel.java @@ -167,5 +167,12 @@ public class DataMapPanel extends PamBorderPanel implements PamTabPanel { } summaryPanel.newDataSources(); } + + /** + * @return the summaryPanel + */ + public SummaryPanel getSummaryPanel() { + return summaryPanel; + } } diff --git a/src/warnings/SingleLineWarningDisplay.java b/src/warnings/SingleLineWarningDisplay.java index 69b70b2a..1765f372 100644 --- a/src/warnings/SingleLineWarningDisplay.java +++ b/src/warnings/SingleLineWarningDisplay.java @@ -67,7 +67,8 @@ public class SingleLineWarningDisplay implements WarningDisplay { lastWarning.setToolTipText(null); } else { - String str = String.format("Warning: %s-%s ", w.getWarningSource(), w.getWarningMessage()); + String str = w.getWarnignLevel() > 0 ? "Warning: " : ""; + str += String.format("%s-%s ", w.getWarningSource(), w.getWarningMessage()); lastWarning.setText(str); lastWarning.setWarningLevel(w.getWarnignLevel()); lastWarning.setToolTipText(w.getWarningTip()); From 768840c76e63b5ee9dc241f851ebb5bb54325b71 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 13 Jun 2022 09:25:00 +0100 Subject: [PATCH 23/37] ROCCA bug - I missed one setting of RawDataLifeTime. Have updated --- src/rocca/RoccaProcess.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rocca/RoccaProcess.java b/src/rocca/RoccaProcess.java index 86b5502b..f49ff04d 100644 --- a/src/rocca/RoccaProcess.java +++ b/src/rocca/RoccaProcess.java @@ -742,7 +742,6 @@ public class RoccaProcess extends PamProcess { PamUtils.makeChannelMap(lowestChanList)); int firstIndx = prdb.getUnitIndex(firstRDU); if (firstIndx==-1) { - System.out.println("RoccaProcess: Cannot determine firstIndx, raw data lifetime = " + prdb.getNaturalLifetimeMillis() + " ms"); int newTime; if (prdb.getNaturalLifetimeMillis() > Integer.MAX_VALUE/2) { newTime = Integer.MAX_VALUE; @@ -751,6 +750,7 @@ public class RoccaProcess extends PamProcess { } // stop it getting silly. newTime = Math.min(newTime, roccaControl.getMaxDataKeepTime()); + System.out.println("RoccaProcess: Cannot determine firstIndx, raw data lifetime = " + prdb.getNaturalLifetimeMillis() + " ms"); prdb.setNaturalLifetimeMillis(newTime); // increase the lifetime to try and prevent this from happening again return null; } @@ -777,13 +777,14 @@ public class RoccaProcess extends PamProcess { PamUtils.makeChannelMap(highestChanList)); int lastIndx = prdb.getUnitIndex(lastRDU); if (lastIndx==-1) { - System.out.println("RoccaProcess: Cannot determine lastIndx, raw data lifetime = " + prdb.getNaturalLifetimeMillis() + " ms"); int newTime; if (prdb.getNaturalLifetimeMillis() > Integer.MAX_VALUE/2) { newTime = Integer.MAX_VALUE; } else { newTime = prdb.getNaturalLifetimeMillis()*2; } + newTime = Math.min(newTime, roccaControl.getMaxDataKeepTime()); + System.out.println("RoccaProcess: Cannot determine lastIndx, raw data lifetime = " + prdb.getNaturalLifetimeMillis() + " ms"); prdb.setNaturalLifetimeMillis(newTime); // increase the lifetime to try and prevent this from happening again return null; } From b5eb1b843cada41a83e38a639923a09fd711e0e7 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 14 Jun 2022 17:34:07 +0100 Subject: [PATCH 24/37] Several small bug fixes to support annotating super detectoins of super detections --- .../overlaymark/ExtMouseAdapter.java | 3 ++- .../overlaymark/OverlayMarker.java | 2 -- .../modifier/SuperDetSymbolWrapper.java | 3 ++- src/PamguardMVC/PamDataUnit.java | 26 +++++++++++++++++++ .../handler/AnnotationChoiceHandler.java | 11 ++++++++ src/generalDatabase/PamTableItem.java | 6 ++++- src/offlineProcessing/OfflineTask.java | 12 ++++++++- 7 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/PamView/paneloverlay/overlaymark/ExtMouseAdapter.java b/src/PamView/paneloverlay/overlaymark/ExtMouseAdapter.java index d06d0041..dcac5902 100644 --- a/src/PamView/paneloverlay/overlaymark/ExtMouseAdapter.java +++ b/src/PamView/paneloverlay/overlaymark/ExtMouseAdapter.java @@ -87,7 +87,8 @@ public class ExtMouseAdapter { } final public boolean mouseWheelMoved(MouseWheelEvent e) { - return mouseWheelMoved(fxScroll(e)); +// return mouseWheelMoved(fxScroll(e)); + return false; } // private MouseEvent swingMouse(javafx.scene.input.MouseEvent e) { diff --git a/src/PamView/paneloverlay/overlaymark/OverlayMarker.java b/src/PamView/paneloverlay/overlaymark/OverlayMarker.java index 3dc591f9..c6ad2ad6 100644 --- a/src/PamView/paneloverlay/overlaymark/OverlayMarker.java +++ b/src/PamView/paneloverlay/overlaymark/OverlayMarker.java @@ -610,8 +610,6 @@ abstract public class OverlayMarker extends ExtMouseAdapter implements MarkManag } } -// Debug.out.printf("OverlayMarker: _ 2 In getSelectedMarkedDataUnits with %d available units\n", selectedData.size()); - return selectedData; } diff --git a/src/PamView/symbol/modifier/SuperDetSymbolWrapper.java b/src/PamView/symbol/modifier/SuperDetSymbolWrapper.java index b498bf69..e4bfda42 100644 --- a/src/PamView/symbol/modifier/SuperDetSymbolWrapper.java +++ b/src/PamView/symbol/modifier/SuperDetSymbolWrapper.java @@ -32,12 +32,13 @@ public class SuperDetSymbolWrapper extends SymbolModifier { @Override public SymbolData getSymbolData(GeneralProjector projector, PamDataUnit dataUnit) { - PamDataUnit superDet = dataUnit.getSuperDetection(superDetDataBlock); + PamDataUnit superDet = dataUnit.getSuperDetection(superDetDataBlock, true); if (superDet == null) { return null; } return superDetModifier.getSymbolData(getSymbolChooser().getProjector(), superDet); } + @Override public String getToolTipText() { diff --git a/src/PamguardMVC/PamDataUnit.java b/src/PamguardMVC/PamDataUnit.java index 2a9994a6..829ec1ab 100644 --- a/src/PamguardMVC/PamDataUnit.java +++ b/src/PamguardMVC/PamDataUnit.java @@ -1292,6 +1292,32 @@ abstract public class PamDataUnit return null; } + /** + * find a super detection form the parent data block of the super detection. + * @param superDataBlock data block of super detection + * @param allowSuperSuper Allow iteration through mutilple super detection layers + * @return data unit from that block, or null. + */ + public SuperDetection getSuperDetection(PamDataBlock superDataBlock, boolean allowSuperSuper) { + synchronized (superDetectionSyncronisation) { + if (superDetections == null) return null; + SuperDetection superDet; + for (int i = 0; i < superDetections.size(); i++) { + superDet = superDetections.get(i); + if (superDet.getParentDataBlock() == superDataBlock) { + return superDet; + } + if (allowSuperSuper) { + SuperDetection supersuper = superDet.getSuperDetection(superDataBlock, allowSuperSuper); + if (supersuper != null) { + return supersuper; + } + } + } + } + return null; + } + public SuperDetection getSuperDetection(int ind) { synchronized (superDetectionSyncronisation) { if (superDetections == null || superDetections.size()<=ind) return null; diff --git a/src/annotation/handler/AnnotationChoiceHandler.java b/src/annotation/handler/AnnotationChoiceHandler.java index 929903e0..911cd359 100644 --- a/src/annotation/handler/AnnotationChoiceHandler.java +++ b/src/annotation/handler/AnnotationChoiceHandler.java @@ -146,6 +146,17 @@ public abstract class AnnotationChoiceHandler extends AnnotationHandler { * @param annotationType */ public boolean updateAnnotation(PamDataUnit pamDataUnit, DataAnnotationType annotationType) { + /* + * need to check this is actually the right data unit, which matches the datablock of + * this annotatoin handler. Gets very confused when dealing with superdetections + */ + if (pamDataUnit.getParentDataBlock() != this.getPamDataBlock()) { + pamDataUnit = pamDataUnit.getSuperDetection(getPamDataBlock(), true); + if (pamDataUnit == null) { + return false; + } + } + DataAnnotation existingAnnotation = pamDataUnit.findDataAnnotation(annotationType.getAnnotationClass(), annotationType.getAnnotationName()); boolean changed = false; if (annotationType.canAutoAnnotate()) { diff --git a/src/generalDatabase/PamTableItem.java b/src/generalDatabase/PamTableItem.java index 27a04d0d..1b75fded 100644 --- a/src/generalDatabase/PamTableItem.java +++ b/src/generalDatabase/PamTableItem.java @@ -426,7 +426,11 @@ public class PamTableItem implements Cloneable { return (Double) value; } - + /** + * Get a float value, being aware that some DBMS may have + * decided to store as a Double anyway. Return Float.NaN for null data + * @return float value or NaN + */ public float getFloatValue() { if (value == null) { return Float.NaN; diff --git a/src/offlineProcessing/OfflineTask.java b/src/offlineProcessing/OfflineTask.java index 0905a765..a5ca3ac1 100644 --- a/src/offlineProcessing/OfflineTask.java +++ b/src/offlineProcessing/OfflineTask.java @@ -4,6 +4,7 @@ import generalDatabase.DBControlUnit; import generalDatabase.PamConnection; import generalDatabase.SQLLogging; import generalDatabase.SQLTypes; +import generalDatabase.SuperDetLogging; import generalDatabase.clauses.FixedClause; import generalDatabase.clauses.FromClause; import generalDatabase.clauses.PAMSelectClause; @@ -354,6 +355,7 @@ public abstract class OfflineTask { } aBlock.clearAll(); + SQLLogging logging = aBlock.getLogging(); if (logging == null) { @@ -382,7 +384,15 @@ public abstract class OfflineTask { System.out.println("Unknown data selection option in OfflineTask.deleteOldData: " + taskGroupParams.dataChoice); return; } - logging.deleteData(clause); + while (logging != null) { + logging.deleteData(clause); + if (logging instanceof SuperDetLogging) { + logging = ((SuperDetLogging) logging).getSubLogging(); + } + else { + break; + } + } } From be3dee2e3cfa54d56ddef6020451082ee914b8eb Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 14 Jun 2022 17:48:27 +0100 Subject: [PATCH 25/37] Functions to more easily put dialogs close to where mouse is --- src/PamView/dialog/PamDialog.java | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/PamView/dialog/PamDialog.java b/src/PamView/dialog/PamDialog.java index 4a1b12e1..90d966af 100644 --- a/src/PamView/dialog/PamDialog.java +++ b/src/PamView/dialog/PamDialog.java @@ -3,10 +3,14 @@ package PamView.dialog; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dialog; +import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.awt.MouseInfo; +import java.awt.Point; import java.awt.Rectangle; +import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -225,6 +229,9 @@ abstract public class PamDialog extends JDialog { synchronized (this) { PamColors.getInstance().notifyContianer(this.getContentPane()); } + if (getOwner() == null) { + moveToMouseLocation(); + } } try{ super.setVisible(visible); @@ -235,6 +242,35 @@ abstract public class PamDialog extends JDialog { } } + /** + * put the dialog near the mouse location. + */ + public void moveToMouseLocation() { + Point mouse = MouseInfo.getPointerInfo().getLocation(); + moveToLocation(mouse); + } + + public void moveToLocation(Point point) { + if (point == null) { + return; + } + // check we're not going too far off the screen. + Dimension sz = getPreferredSize(); + Dimension screen = null; + if (getOwner() != null) { + screen = getOwner().getSize(); + } + else { + screen = Toolkit.getDefaultToolkit().getScreenSize(); + } + point.y = Math.min(point.y, screen.height-sz.height-10); + point.y = Math.max(point.y, 0); + point.x = Math.min(point.x, screen.width-sz.width-10); + point.x = Math.max(point.x, 0); + + setLocation(point); + } + /** * Reschedule closing of the window to happen * on the AWT thread using SwingUtilities.invokeLater(...) From 0c3cd2995b30fc1a20b6d51d378ff96e2f608af8 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 17 Jun 2022 09:51:44 +0100 Subject: [PATCH 26/37] Global arguments to set params in acquisition and database. --- src/Acquisition/FolderInputSystem.java | 21 ++++ .../filedate/StandardFileDate.java | 21 ++++ src/generalDatabase/DBControl.java | 2 + .../sqlite/SqliteDialogPanel.java | 60 +--------- src/generalDatabase/sqlite/SqliteSystem.java | 107 +++++++++++++++++- src/group3dlocaliser/Group3DProcess.java | 2 + .../grouper/DetectionGrouper.java | 10 +- .../grouper/FirstGrouping.java | 10 ++ src/pamguard/Pamguard.java | 13 ++- 9 files changed, 183 insertions(+), 63 deletions(-) diff --git a/src/Acquisition/FolderInputSystem.java b/src/Acquisition/FolderInputSystem.java index c9f276cd..227ff2d1 100644 --- a/src/Acquisition/FolderInputSystem.java +++ b/src/Acquisition/FolderInputSystem.java @@ -27,6 +27,7 @@ import Acquisition.layoutFX.AcquisitionPaneFX; import Acquisition.layoutFX.DAQSettingsPane; import Acquisition.layoutFX.FolderInputPane; import javafx.application.Platform; +import pamguard.GlobalArguments; import Acquisition.pamAudio.PamAudioSystem; import PamController.PamControlledUnitSettings; import PamController.PamController; @@ -77,6 +78,8 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings{ protected long eta = -1; private FolderInputParameters folderInputParameters; + + public static final String GlobalWavFolderArg = "wavfilefolder"; /** @@ -110,12 +113,30 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings{ if (folderInputParameters == null) setFolderInputParameters(new FolderInputParameters(getSystemType())); // PamSettingManager.getInstance().registerSettings(this); //calling super already registers this in the FileInputSystem constructor + checkComandLine(); makeSelFileList(); newFileTimer = new Timer(1000, new RestartTimer()); newFileTimer.setRepeats(false); // timer = new Timer(1000, new TimerAction()); } + /** + * Check to see if acquisition source folder was set in the command line. + */ + private void checkComandLine() { + String globalFolder = GlobalArguments.getParam(GlobalWavFolderArg); + if (globalFolder == null) { + return; + } + // see if it at least exists, though will we want to do this for Network folders ? + File aFile = new File(globalFolder); + if (aFile.exists() == false) { + System.err.println("Command line folder does not exist: " + globalFolder); + } + String[] selList = {globalFolder}; + folderInputParameters.setSelectedFiles(selList); + } + /** * Restarts after a file has ended when processing multiple files. * 27 Jan 2011 - this now reschedules in the AWT thread diff --git a/src/Acquisition/filedate/StandardFileDate.java b/src/Acquisition/filedate/StandardFileDate.java index 778bfccd..8fa56de6 100644 --- a/src/Acquisition/filedate/StandardFileDate.java +++ b/src/Acquisition/filedate/StandardFileDate.java @@ -249,6 +249,18 @@ public class StandardFileDate implements FileDate, PamSettings { String name = file.getName(); name = removeWildChars(name, forcedDateFormat); String redFormat = forcedDateFormat.replace("#", ""); + // see if it's only all milliseconds, i.e. format is only 'S's and > 12 of them + if (allSSSS(redFormat)) { + // try pulling a number from the name. + try { + long millis = Long.valueOf(name); + return millis; + } + catch (NumberFormatException e) { + + } + } + SimpleDateFormat sdf = null; try { sdf = new SimpleDateFormat(redFormat); @@ -271,6 +283,15 @@ public class StandardFileDate implements FileDate, PamSettings { return d.getTime(); } + private boolean allSSSS(String redFormat) { + for(int i = 0; i < redFormat.length(); i++) { + if (redFormat.charAt(i) != 'S') { + return false; + } + } + return true; + } + private String removeWildChars(String name, String forcedDateFormat) { // # is the wild field i s# if (name == null || forcedDateFormat == null) { diff --git a/src/generalDatabase/DBControl.java b/src/generalDatabase/DBControl.java index d8ffd9c6..031d8232 100644 --- a/src/generalDatabase/DBControl.java +++ b/src/generalDatabase/DBControl.java @@ -91,6 +91,8 @@ PamSettingsSource { private boolean fullTablesCheck = false; static private String dbUnitType = "Pamguard Database"; + + static public final String GlobalDatabaseNameArg = "-databasefile"; private DBControl THIS; diff --git a/src/generalDatabase/sqlite/SqliteDialogPanel.java b/src/generalDatabase/sqlite/SqliteDialogPanel.java index 656cc9f9..b9e17a27 100644 --- a/src/generalDatabase/sqlite/SqliteDialogPanel.java +++ b/src/generalDatabase/sqlite/SqliteDialogPanel.java @@ -91,69 +91,15 @@ public class SqliteDialogPanel implements SystemDialogPanel { String newDB = sqliteSystem.browseDatabases(parent); if (newDB != null) { - - // see if this file exists in the list and if it does, remove it - for (int i = 0; i < sqliteSystem.getRecentDatabases().size(); i++) { - if (sqliteSystem.getRecentDatabases().get(i).toString().equalsIgnoreCase(newDB)) { - sqliteSystem.getRecentDatabases().remove(i); - } - } - // then insert the file at the top of the list. - File newFile = new File(newDB); - // if the file doesn't exit, consider creating it. - if (newFile.exists() == false) { - newFile = createNewDatabase(newDB); - if (newFile == null) { - System.out.println("Unable to create "+newFile); - return; - } - - } - - sqliteSystem.getRecentDatabases().add(0, newFile); + sqliteSystem.setDatabaseName(newDB); + setParams(); - } } } - public File createNewDatabase(String newDB) { - - File newFile = new File(newDB); - newFile = PamFileFilter.checkFileEnd(newFile, ".sqlite3", true); - - int ans = JOptionPane.showConfirmDialog(parent, "Create blank database " + newFile.getAbsolutePath() + " ?", "Sqlite", JOptionPane.OK_CANCEL_OPTION); - if (ans == JOptionPane.CANCEL_OPTION) { - return null; - } - Connection connection = null; - - try { - // create a database connection; - // Sqlite will automatically create file if it does not exist; - connection = DriverManager.getConnection("jdbc:sqlite:" + newFile); - - } - catch(SQLException e) - { - System.err.println(e.getMessage()); - } - finally - { - try - { - if(connection != null) - connection.close(); - } - catch(SQLException e) - { - // connection close failed. - System.err.println(e); - } - } - return newFile; - } + } diff --git a/src/generalDatabase/sqlite/SqliteSystem.java b/src/generalDatabase/sqlite/SqliteSystem.java index 5b1926e0..2bb92d33 100644 --- a/src/generalDatabase/sqlite/SqliteSystem.java +++ b/src/generalDatabase/sqlite/SqliteSystem.java @@ -3,6 +3,7 @@ package generalDatabase.sqlite; import generalDatabase.pamCursor.NonScrollablePamCursor; import generalDatabase.pamCursor.PamCursor; import javafx.stage.FileChooser; +import pamguard.GlobalArguments; import java.awt.Component; import java.awt.Desktop; @@ -10,10 +11,12 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; import java.sql.Connection; +import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; import javax.swing.JFileChooser; +import javax.swing.JOptionPane; import org.sqlite.SQLiteConfig; @@ -82,6 +85,53 @@ public class SqliteSystem extends DBSystem implements PamSettings { if (sqliteParameters.getRecentDatabases() == null) { sqliteParameters.setRecentDatabases(new ArrayList()); } + checkCommandLineOption(); + } + + /** + * Check to see if the database name was included as a command line option, + * in which case, put it at the top of the list. this is better than just using + * it,since it then gets stored within it's own settings. + */ + private void checkCommandLineOption() { + // TODO Auto-generated method stub + /* + * If a database name was passed as a global argument, then use the passed name instead of + * the name in the list of recent databases. + */ + String commandName = GlobalArguments.getParam(DBControl.GlobalDatabaseNameArg); + if (commandName == null) { + return; + } + setDatabaseName(commandName); + } + + /** + * Set the database name, check it exists, check it's end + * and add to top of list of databases. + * @param databaseName + */ + public void setDatabaseName(String databaseName) { + // see if this file exists in the list and if it does, remove it + for (int i = 0; i < getRecentDatabases().size(); i++) { + if (getRecentDatabases().get(i).toString().equalsIgnoreCase(databaseName)) { + getRecentDatabases().remove(i); + } + } + // then insert the file at the top of the list. + File newFile = new File(databaseName); + // if the file doesn't exit, consider creating it. + if (newFile.exists() == false) { + newFile = createNewDatabase(databaseName, null, true); + if (newFile == null) { + System.out.println("Unable to create "+newFile); + return; + } + + } + + getRecentDatabases().add(0, newFile); + } @Override @@ -111,6 +161,52 @@ public class SqliteSystem extends DBSystem implements PamSettings { } return null; } + + /** + * Create a new empty database file. + * @param newDB full path for database file (can be missing .sqlit3 if you like - this will get checked and added). + * @param parent window (for confirm dialog, can be null) + * @param askFirst show a confirm dialog before creating the database file. + * @return a path to the file, whether created or no. + */ + public File createNewDatabase(String newDB, Component parent, boolean askFirst) { + + File newFile = new File(newDB); + newFile = PamFileFilter.checkFileEnd(newFile, ".sqlite3", true); + + if (askFirst) { + int ans = JOptionPane.showConfirmDialog(parent, "Create blank database " + newFile.getAbsolutePath() + " ?", "Sqlite", JOptionPane.OK_CANCEL_OPTION); + if (ans == JOptionPane.CANCEL_OPTION) { + return null; + } + } + Connection connection = null; + + try { + // create a database connection; + // Sqlite will automatically create file if it does not exist; + connection = DriverManager.getConnection("jdbc:sqlite:" + newFile); + + } + catch(SQLException e) + { + System.err.println(e.getMessage()); + } + finally + { + try + { + if(connection != null) + connection.close(); + } + catch(SQLException e) + { + // connection close failed. + System.err.println(e); + } + } + return newFile; + } @Override public boolean canCreate() { @@ -208,6 +304,15 @@ public class SqliteSystem extends DBSystem implements PamSettings { @Override public String getDatabaseName() { + /* + * If a database name was passed as a global argument, then use the passed name instead of + * the name in the list of recent databases. + */ + String commandName = GlobalArguments.getParam(DBControl.GlobalDatabaseNameArg); + if (commandName != null) { + return commandName; + } + if (getRecentDatabases() == null) return null; if (getRecentDatabases().size() < 1) return null; return getRecentDatabases().get(0).getAbsolutePath(); @@ -271,7 +376,7 @@ public class SqliteSystem extends DBSystem implements PamSettings { public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { Object settings = pamControlledUnitSettings.getSettings(); - if (settings instanceof ArrayList) { + if (settings instanceof ArrayList) { // deal with old format which just stored a list. sqliteParameters.getRecentDatabases().clear(); sqliteParameters.getRecentDatabases().addAll((ArrayList) settings); } diff --git a/src/group3dlocaliser/Group3DProcess.java b/src/group3dlocaliser/Group3DProcess.java index 45d2f466..7822e346 100644 --- a/src/group3dlocaliser/Group3DProcess.java +++ b/src/group3dlocaliser/Group3DProcess.java @@ -193,6 +193,7 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor // will have to make a data unit for each group now... Group3DDataUnit[] group3dDataUnits = new Group3DDataUnit[nGroups]; +// System.out.println("Enter newGRoupedDataSet with groups: " + detectionGroupedSet.getNumGroups()); // if (detectionGroupedSet.hasUID(14045004731L)) { // Debug.out.println(" found it"); // } @@ -209,6 +210,7 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor // localiserAlgorithm3D.process(detectionGroupedSet.getGroup(i)); abstractLocalisation = localiserAlgorithm3D.runModel(group3dDataUnits[i], null, false); if (abstractLocalisation == null) { + System.out.println("Group 3D process null localisation from " + localiserAlgorithm3D.getName()); continue; } // log all outputs .. diff --git a/src/group3dlocaliser/grouper/DetectionGrouper.java b/src/group3dlocaliser/grouper/DetectionGrouper.java index 6ad9bf38..c5ce50aa 100644 --- a/src/group3dlocaliser/grouper/DetectionGrouper.java +++ b/src/group3dlocaliser/grouper/DetectionGrouper.java @@ -160,9 +160,15 @@ public class DetectionGrouper { if (motherGroup.getTotalChannelMap() == 0) { return; } -// if (maybeCloseMotherGroup(0, sampleNumber - (long) (sampleRate / 2))) { +// motherGroup. +// int lastGroup = motherGroup.getLastChannelGroup(); + long buffer = (long) this.maxInterGroupSample; +// if (shouldCloseMotherGroup(lastGroup, sampleNumber, buffer)) { + if (sampleNumber > motherGroup.getVeryLastSample() + buffer + sampleRate) { + closeMotherGroup(); +// if (maybeCloseMotherGroup(motherGroup.getLastChannelGroup(), sampleNumber - (long) (sampleRate / 2))) { // System.out.println("Mother group closed on timer"); -// } + } } private synchronized boolean maybeCloseMotherGroup(int iChanGroup, long currentSample) { diff --git a/src/group3dlocaliser/grouper/FirstGrouping.java b/src/group3dlocaliser/grouper/FirstGrouping.java index 6cdcd03a..b17be86e 100644 --- a/src/group3dlocaliser/grouper/FirstGrouping.java +++ b/src/group3dlocaliser/grouper/FirstGrouping.java @@ -24,6 +24,8 @@ public class FirstGrouping { private long veryLastSample; private int[] groupCount; + + private int lastChannelGroup; public FirstGrouping(int nChannelGroups, int channelGroup, PamDataUnit pamDataUnit) { this.nChannelGroups = nChannelGroups; @@ -44,6 +46,7 @@ public class FirstGrouping { lastSamples[channelGroup] += pamDataUnit.getSampleDuration(); } veryLastSample = Math.max(veryLastSample, lastSamples[channelGroup]); + lastChannelGroup = channelGroup; } // /** @@ -92,4 +95,11 @@ public class FirstGrouping { return dataUnits; } + /** + * @return the lastChannelGroup + */ + public int getLastChannelGroup() { + return lastChannelGroup; + } + } diff --git a/src/pamguard/Pamguard.java b/src/pamguard/Pamguard.java index 3c63090e..0e19e4e3 100644 --- a/src/pamguard/Pamguard.java +++ b/src/pamguard/Pamguard.java @@ -23,6 +23,7 @@ package pamguard; import javax.swing.SwingUtilities; import javax.swing.UIManager; +import Acquisition.FolderInputSystem; import PamController.PamController; import PamController.PamGUIManager; import PamController.PamSettingManager; @@ -41,6 +42,8 @@ import PamView.dialog.warn.WarnOnce; import PamguardMVC.debug.Debug; import binaryFileStorage.BinaryStore; import dataPlotsFX.JamieDev; +import generalDatabase.DBControl; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; @@ -216,11 +219,15 @@ public class Pamguard { } else if (anArg.equalsIgnoreCase(BinaryStore.GlobalFolderArg)) { // output folder for binary files. - GlobalArguments.setParam(anArg, args[iArg++]); + GlobalArguments.setParam(BinaryStore.GlobalFolderArg, args[iArg++]); } - else if (anArg.equalsIgnoreCase("-databasefile")) { + else if (anArg.equalsIgnoreCase(DBControl.GlobalDatabaseNameArg)) { // database file name - GlobalArguments.setParam(anArg, args[iArg++]); + GlobalArguments.setParam(DBControl.GlobalDatabaseNameArg, args[iArg++]); + } + else if (anArg.equalsIgnoreCase(FolderInputSystem.GlobalWavFolderArg)) { + // source folder for wav files (or other supported sound files) + GlobalArguments.setParam(FolderInputSystem.GlobalWavFolderArg, args[iArg++]); } else if (anArg.equalsIgnoreCase("-help")) { System.out.println("--PamGuard Help"); From a7b2e7522e0f8e191063332083905413d28b45b6 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 17 Jun 2022 09:55:18 +0100 Subject: [PATCH 27/37] Global arguments to set params in acquisition and database. (#37) --- src/Acquisition/FolderInputSystem.java | 21 ++++ .../filedate/StandardFileDate.java | 21 ++++ src/generalDatabase/DBControl.java | 2 + .../sqlite/SqliteDialogPanel.java | 60 +--------- src/generalDatabase/sqlite/SqliteSystem.java | 107 +++++++++++++++++- src/group3dlocaliser/Group3DProcess.java | 2 + .../grouper/DetectionGrouper.java | 10 +- .../grouper/FirstGrouping.java | 10 ++ src/pamguard/Pamguard.java | 13 ++- 9 files changed, 183 insertions(+), 63 deletions(-) diff --git a/src/Acquisition/FolderInputSystem.java b/src/Acquisition/FolderInputSystem.java index c9f276cd..227ff2d1 100644 --- a/src/Acquisition/FolderInputSystem.java +++ b/src/Acquisition/FolderInputSystem.java @@ -27,6 +27,7 @@ import Acquisition.layoutFX.AcquisitionPaneFX; import Acquisition.layoutFX.DAQSettingsPane; import Acquisition.layoutFX.FolderInputPane; import javafx.application.Platform; +import pamguard.GlobalArguments; import Acquisition.pamAudio.PamAudioSystem; import PamController.PamControlledUnitSettings; import PamController.PamController; @@ -77,6 +78,8 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings{ protected long eta = -1; private FolderInputParameters folderInputParameters; + + public static final String GlobalWavFolderArg = "wavfilefolder"; /** @@ -110,12 +113,30 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings{ if (folderInputParameters == null) setFolderInputParameters(new FolderInputParameters(getSystemType())); // PamSettingManager.getInstance().registerSettings(this); //calling super already registers this in the FileInputSystem constructor + checkComandLine(); makeSelFileList(); newFileTimer = new Timer(1000, new RestartTimer()); newFileTimer.setRepeats(false); // timer = new Timer(1000, new TimerAction()); } + /** + * Check to see if acquisition source folder was set in the command line. + */ + private void checkComandLine() { + String globalFolder = GlobalArguments.getParam(GlobalWavFolderArg); + if (globalFolder == null) { + return; + } + // see if it at least exists, though will we want to do this for Network folders ? + File aFile = new File(globalFolder); + if (aFile.exists() == false) { + System.err.println("Command line folder does not exist: " + globalFolder); + } + String[] selList = {globalFolder}; + folderInputParameters.setSelectedFiles(selList); + } + /** * Restarts after a file has ended when processing multiple files. * 27 Jan 2011 - this now reschedules in the AWT thread diff --git a/src/Acquisition/filedate/StandardFileDate.java b/src/Acquisition/filedate/StandardFileDate.java index 778bfccd..8fa56de6 100644 --- a/src/Acquisition/filedate/StandardFileDate.java +++ b/src/Acquisition/filedate/StandardFileDate.java @@ -249,6 +249,18 @@ public class StandardFileDate implements FileDate, PamSettings { String name = file.getName(); name = removeWildChars(name, forcedDateFormat); String redFormat = forcedDateFormat.replace("#", ""); + // see if it's only all milliseconds, i.e. format is only 'S's and > 12 of them + if (allSSSS(redFormat)) { + // try pulling a number from the name. + try { + long millis = Long.valueOf(name); + return millis; + } + catch (NumberFormatException e) { + + } + } + SimpleDateFormat sdf = null; try { sdf = new SimpleDateFormat(redFormat); @@ -271,6 +283,15 @@ public class StandardFileDate implements FileDate, PamSettings { return d.getTime(); } + private boolean allSSSS(String redFormat) { + for(int i = 0; i < redFormat.length(); i++) { + if (redFormat.charAt(i) != 'S') { + return false; + } + } + return true; + } + private String removeWildChars(String name, String forcedDateFormat) { // # is the wild field i s# if (name == null || forcedDateFormat == null) { diff --git a/src/generalDatabase/DBControl.java b/src/generalDatabase/DBControl.java index d8ffd9c6..031d8232 100644 --- a/src/generalDatabase/DBControl.java +++ b/src/generalDatabase/DBControl.java @@ -91,6 +91,8 @@ PamSettingsSource { private boolean fullTablesCheck = false; static private String dbUnitType = "Pamguard Database"; + + static public final String GlobalDatabaseNameArg = "-databasefile"; private DBControl THIS; diff --git a/src/generalDatabase/sqlite/SqliteDialogPanel.java b/src/generalDatabase/sqlite/SqliteDialogPanel.java index 656cc9f9..b9e17a27 100644 --- a/src/generalDatabase/sqlite/SqliteDialogPanel.java +++ b/src/generalDatabase/sqlite/SqliteDialogPanel.java @@ -91,69 +91,15 @@ public class SqliteDialogPanel implements SystemDialogPanel { String newDB = sqliteSystem.browseDatabases(parent); if (newDB != null) { - - // see if this file exists in the list and if it does, remove it - for (int i = 0; i < sqliteSystem.getRecentDatabases().size(); i++) { - if (sqliteSystem.getRecentDatabases().get(i).toString().equalsIgnoreCase(newDB)) { - sqliteSystem.getRecentDatabases().remove(i); - } - } - // then insert the file at the top of the list. - File newFile = new File(newDB); - // if the file doesn't exit, consider creating it. - if (newFile.exists() == false) { - newFile = createNewDatabase(newDB); - if (newFile == null) { - System.out.println("Unable to create "+newFile); - return; - } - - } - - sqliteSystem.getRecentDatabases().add(0, newFile); + sqliteSystem.setDatabaseName(newDB); + setParams(); - } } } - public File createNewDatabase(String newDB) { - - File newFile = new File(newDB); - newFile = PamFileFilter.checkFileEnd(newFile, ".sqlite3", true); - - int ans = JOptionPane.showConfirmDialog(parent, "Create blank database " + newFile.getAbsolutePath() + " ?", "Sqlite", JOptionPane.OK_CANCEL_OPTION); - if (ans == JOptionPane.CANCEL_OPTION) { - return null; - } - Connection connection = null; - - try { - // create a database connection; - // Sqlite will automatically create file if it does not exist; - connection = DriverManager.getConnection("jdbc:sqlite:" + newFile); - - } - catch(SQLException e) - { - System.err.println(e.getMessage()); - } - finally - { - try - { - if(connection != null) - connection.close(); - } - catch(SQLException e) - { - // connection close failed. - System.err.println(e); - } - } - return newFile; - } + } diff --git a/src/generalDatabase/sqlite/SqliteSystem.java b/src/generalDatabase/sqlite/SqliteSystem.java index 5b1926e0..2bb92d33 100644 --- a/src/generalDatabase/sqlite/SqliteSystem.java +++ b/src/generalDatabase/sqlite/SqliteSystem.java @@ -3,6 +3,7 @@ package generalDatabase.sqlite; import generalDatabase.pamCursor.NonScrollablePamCursor; import generalDatabase.pamCursor.PamCursor; import javafx.stage.FileChooser; +import pamguard.GlobalArguments; import java.awt.Component; import java.awt.Desktop; @@ -10,10 +11,12 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; import java.sql.Connection; +import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; import javax.swing.JFileChooser; +import javax.swing.JOptionPane; import org.sqlite.SQLiteConfig; @@ -82,6 +85,53 @@ public class SqliteSystem extends DBSystem implements PamSettings { if (sqliteParameters.getRecentDatabases() == null) { sqliteParameters.setRecentDatabases(new ArrayList()); } + checkCommandLineOption(); + } + + /** + * Check to see if the database name was included as a command line option, + * in which case, put it at the top of the list. this is better than just using + * it,since it then gets stored within it's own settings. + */ + private void checkCommandLineOption() { + // TODO Auto-generated method stub + /* + * If a database name was passed as a global argument, then use the passed name instead of + * the name in the list of recent databases. + */ + String commandName = GlobalArguments.getParam(DBControl.GlobalDatabaseNameArg); + if (commandName == null) { + return; + } + setDatabaseName(commandName); + } + + /** + * Set the database name, check it exists, check it's end + * and add to top of list of databases. + * @param databaseName + */ + public void setDatabaseName(String databaseName) { + // see if this file exists in the list and if it does, remove it + for (int i = 0; i < getRecentDatabases().size(); i++) { + if (getRecentDatabases().get(i).toString().equalsIgnoreCase(databaseName)) { + getRecentDatabases().remove(i); + } + } + // then insert the file at the top of the list. + File newFile = new File(databaseName); + // if the file doesn't exit, consider creating it. + if (newFile.exists() == false) { + newFile = createNewDatabase(databaseName, null, true); + if (newFile == null) { + System.out.println("Unable to create "+newFile); + return; + } + + } + + getRecentDatabases().add(0, newFile); + } @Override @@ -111,6 +161,52 @@ public class SqliteSystem extends DBSystem implements PamSettings { } return null; } + + /** + * Create a new empty database file. + * @param newDB full path for database file (can be missing .sqlit3 if you like - this will get checked and added). + * @param parent window (for confirm dialog, can be null) + * @param askFirst show a confirm dialog before creating the database file. + * @return a path to the file, whether created or no. + */ + public File createNewDatabase(String newDB, Component parent, boolean askFirst) { + + File newFile = new File(newDB); + newFile = PamFileFilter.checkFileEnd(newFile, ".sqlite3", true); + + if (askFirst) { + int ans = JOptionPane.showConfirmDialog(parent, "Create blank database " + newFile.getAbsolutePath() + " ?", "Sqlite", JOptionPane.OK_CANCEL_OPTION); + if (ans == JOptionPane.CANCEL_OPTION) { + return null; + } + } + Connection connection = null; + + try { + // create a database connection; + // Sqlite will automatically create file if it does not exist; + connection = DriverManager.getConnection("jdbc:sqlite:" + newFile); + + } + catch(SQLException e) + { + System.err.println(e.getMessage()); + } + finally + { + try + { + if(connection != null) + connection.close(); + } + catch(SQLException e) + { + // connection close failed. + System.err.println(e); + } + } + return newFile; + } @Override public boolean canCreate() { @@ -208,6 +304,15 @@ public class SqliteSystem extends DBSystem implements PamSettings { @Override public String getDatabaseName() { + /* + * If a database name was passed as a global argument, then use the passed name instead of + * the name in the list of recent databases. + */ + String commandName = GlobalArguments.getParam(DBControl.GlobalDatabaseNameArg); + if (commandName != null) { + return commandName; + } + if (getRecentDatabases() == null) return null; if (getRecentDatabases().size() < 1) return null; return getRecentDatabases().get(0).getAbsolutePath(); @@ -271,7 +376,7 @@ public class SqliteSystem extends DBSystem implements PamSettings { public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { Object settings = pamControlledUnitSettings.getSettings(); - if (settings instanceof ArrayList) { + if (settings instanceof ArrayList) { // deal with old format which just stored a list. sqliteParameters.getRecentDatabases().clear(); sqliteParameters.getRecentDatabases().addAll((ArrayList) settings); } diff --git a/src/group3dlocaliser/Group3DProcess.java b/src/group3dlocaliser/Group3DProcess.java index 45d2f466..7822e346 100644 --- a/src/group3dlocaliser/Group3DProcess.java +++ b/src/group3dlocaliser/Group3DProcess.java @@ -193,6 +193,7 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor // will have to make a data unit for each group now... Group3DDataUnit[] group3dDataUnits = new Group3DDataUnit[nGroups]; +// System.out.println("Enter newGRoupedDataSet with groups: " + detectionGroupedSet.getNumGroups()); // if (detectionGroupedSet.hasUID(14045004731L)) { // Debug.out.println(" found it"); // } @@ -209,6 +210,7 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor // localiserAlgorithm3D.process(detectionGroupedSet.getGroup(i)); abstractLocalisation = localiserAlgorithm3D.runModel(group3dDataUnits[i], null, false); if (abstractLocalisation == null) { + System.out.println("Group 3D process null localisation from " + localiserAlgorithm3D.getName()); continue; } // log all outputs .. diff --git a/src/group3dlocaliser/grouper/DetectionGrouper.java b/src/group3dlocaliser/grouper/DetectionGrouper.java index 6ad9bf38..c5ce50aa 100644 --- a/src/group3dlocaliser/grouper/DetectionGrouper.java +++ b/src/group3dlocaliser/grouper/DetectionGrouper.java @@ -160,9 +160,15 @@ public class DetectionGrouper { if (motherGroup.getTotalChannelMap() == 0) { return; } -// if (maybeCloseMotherGroup(0, sampleNumber - (long) (sampleRate / 2))) { +// motherGroup. +// int lastGroup = motherGroup.getLastChannelGroup(); + long buffer = (long) this.maxInterGroupSample; +// if (shouldCloseMotherGroup(lastGroup, sampleNumber, buffer)) { + if (sampleNumber > motherGroup.getVeryLastSample() + buffer + sampleRate) { + closeMotherGroup(); +// if (maybeCloseMotherGroup(motherGroup.getLastChannelGroup(), sampleNumber - (long) (sampleRate / 2))) { // System.out.println("Mother group closed on timer"); -// } + } } private synchronized boolean maybeCloseMotherGroup(int iChanGroup, long currentSample) { diff --git a/src/group3dlocaliser/grouper/FirstGrouping.java b/src/group3dlocaliser/grouper/FirstGrouping.java index 6cdcd03a..b17be86e 100644 --- a/src/group3dlocaliser/grouper/FirstGrouping.java +++ b/src/group3dlocaliser/grouper/FirstGrouping.java @@ -24,6 +24,8 @@ public class FirstGrouping { private long veryLastSample; private int[] groupCount; + + private int lastChannelGroup; public FirstGrouping(int nChannelGroups, int channelGroup, PamDataUnit pamDataUnit) { this.nChannelGroups = nChannelGroups; @@ -44,6 +46,7 @@ public class FirstGrouping { lastSamples[channelGroup] += pamDataUnit.getSampleDuration(); } veryLastSample = Math.max(veryLastSample, lastSamples[channelGroup]); + lastChannelGroup = channelGroup; } // /** @@ -92,4 +95,11 @@ public class FirstGrouping { return dataUnits; } + /** + * @return the lastChannelGroup + */ + public int getLastChannelGroup() { + return lastChannelGroup; + } + } diff --git a/src/pamguard/Pamguard.java b/src/pamguard/Pamguard.java index 3c63090e..0e19e4e3 100644 --- a/src/pamguard/Pamguard.java +++ b/src/pamguard/Pamguard.java @@ -23,6 +23,7 @@ package pamguard; import javax.swing.SwingUtilities; import javax.swing.UIManager; +import Acquisition.FolderInputSystem; import PamController.PamController; import PamController.PamGUIManager; import PamController.PamSettingManager; @@ -41,6 +42,8 @@ import PamView.dialog.warn.WarnOnce; import PamguardMVC.debug.Debug; import binaryFileStorage.BinaryStore; import dataPlotsFX.JamieDev; +import generalDatabase.DBControl; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; @@ -216,11 +219,15 @@ public class Pamguard { } else if (anArg.equalsIgnoreCase(BinaryStore.GlobalFolderArg)) { // output folder for binary files. - GlobalArguments.setParam(anArg, args[iArg++]); + GlobalArguments.setParam(BinaryStore.GlobalFolderArg, args[iArg++]); } - else if (anArg.equalsIgnoreCase("-databasefile")) { + else if (anArg.equalsIgnoreCase(DBControl.GlobalDatabaseNameArg)) { // database file name - GlobalArguments.setParam(anArg, args[iArg++]); + GlobalArguments.setParam(DBControl.GlobalDatabaseNameArg, args[iArg++]); + } + else if (anArg.equalsIgnoreCase(FolderInputSystem.GlobalWavFolderArg)) { + // source folder for wav files (or other supported sound files) + GlobalArguments.setParam(FolderInputSystem.GlobalWavFolderArg, args[iArg++]); } else if (anArg.equalsIgnoreCase("-help")) { System.out.println("--PamGuard Help"); From 68d28152219681e8a42673408d71d654732b61a2 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 17 Jun 2022 10:04:57 +0100 Subject: [PATCH 28/37] Add maven build configuration --- buildconfigurations/Build PAMGuard DG.launch | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 buildconfigurations/Build PAMGuard DG.launch diff --git a/buildconfigurations/Build PAMGuard DG.launch b/buildconfigurations/Build PAMGuard DG.launch new file mode 100644 index 00000000..21d3145c --- /dev/null +++ b/buildconfigurations/Build PAMGuard DG.launch @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + From df5b3b4dfe7659cd0f741c4f4a0261f00fc9190c Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 17 Jun 2022 14:25:31 +0100 Subject: [PATCH 29/37] Fix bug in CTD dataselector panel. --- .../layout/dataselector/CTDataSelectPanel.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java b/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java index 925741fe..10bc4ca0 100644 --- a/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java +++ b/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java @@ -3,6 +3,7 @@ package clickTrainDetector.layout.dataselector; import java.awt.BorderLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.util.ArrayList; import javax.swing.JCheckBox; import javax.swing.JComponent; @@ -15,6 +16,7 @@ import PamView.dialog.PamDialog; import PamView.dialog.PamDialogPanel; import PamView.dialog.PamGridBagContraints; import PamguardMVC.dataSelector.DataSelectParams; +import clickTrainDetector.classification.CTClassifier; import clickTrainDetector.classification.CTClassifierManager; import clickTrainDetector.dataselector.CTDataSelector; import clickTrainDetector.dataselector.CTSelectParams; @@ -287,9 +289,11 @@ public class CTDataSelectPanel implements PamDialogPanel { // System.out.println("No. count: " + count); currentParams.classifier = new int[count]; + ArrayList classifiers = ctDataSelector.getClickControl().getClassifierManager().getCurrentClassifiers(); + int used = 0; for (int i=0; i Date: Fri, 17 Jun 2022 15:04:24 +0100 Subject: [PATCH 30/37] Extra options for creating data selectors Extra options so dataselectors can be kept simple and not include all possible options for super detections as well as their own - more use when selecting data for a superdetection ! --- src/PamguardMVC/PamDataBlock.java | 22 ++ src/PamguardMVC/RawDataTransforms.java | 261 ++++++++++-------- .../dataSelector/DataSelectorCreator.java | 27 +- 3 files changed, 193 insertions(+), 117 deletions(-) diff --git a/src/PamguardMVC/PamDataBlock.java b/src/PamguardMVC/PamDataBlock.java index 4db0263c..20d6aedb 100644 --- a/src/PamguardMVC/PamDataBlock.java +++ b/src/PamguardMVC/PamDataBlock.java @@ -3705,6 +3705,28 @@ public class PamDataBlock extends PamObservable { return blockDataSelector; } + /** + * Convenience method to save programmer from having to call into the creator + * all the time. + * + * @param selectorName + * @param allowScores + * @param selectorType Type of selector, generally a ModuleType name, e.g. Map, + * so that options can be tailored to specific needs + * @param includeAnnotations include options from any annotators of this data stream + * @param includeSuperDetections include any possible super detection data selectors. + * @return null or a DataSelector + */ + public DataSelector getDataSelector(String selectorName, boolean allowScores, String selectorType, + boolean includeAnnotations, boolean includeSuperDetections) { + DataSelector blockDataSelector = null; + DataSelectorCreator dsc = getDataSelectCreator(); + if (dsc != null) { + blockDataSelector = dsc.getDataSelector(selectorName, allowScores, selectorType, includeAnnotations, includeSuperDetections); + } + return blockDataSelector; + } + /** * Get any information from the data block about cross referencing in database * tables. diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index c0684730..81d8e3fe 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -99,11 +99,26 @@ public class RawDataTransforms { */ private int shortestFFTLength; + private Object synchObject; - - public RawDataTransforms(@SuppressWarnings("rawtypes") PamDataUnit rawDataHolder) { + /** + * Create a RawDataTransforms with a specified synchronisation object. This is mostly the data unit, + * but in some circumstances may need to be a different object to avoid thread lock. + * @param rawDataHolder + * @param synchObject + */ + public RawDataTransforms(@SuppressWarnings("rawtypes") PamDataUnit rawDataHolder, Object synchObject) { this.rawData=(RawDataHolder) rawDataHolder; this.dataUnit = rawDataHolder; + this.synchObject = synchObject; + } + + /** + * Create raw data transforms using the rawDatAholder for synchronisation. + * @param rawDataHolder + */ + public RawDataTransforms(@SuppressWarnings("rawtypes") PamDataUnit rawDataHolder) { + this(rawDataHolder, rawDataHolder); } @@ -118,7 +133,7 @@ public class RawDataTransforms { return powerSpectra; } - + /** * Get the shortest FFT length for the number of samples. * The is the @@ -143,23 +158,25 @@ public class RawDataTransforms { * @param fftLength * @return Power spectrum */ - 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(); + 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(); } - - 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(); } /** @@ -170,29 +187,31 @@ public class RawDataTransforms { * @param fftLength * @return Power spectrum */ - 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; - + public double[] getPowerSpectrum(int channel, int fftLength) { + synchronized (synchObject) { + if (powerSpectra == null) { + powerSpectra = new double[PamUtils.getNumChannels(dataUnit.getChannelBitmap())][]; } - if (powerSpectra[channel].length != fftLength/2) { - powerSpectra[channel] = Arrays.copyOf(powerSpectra[channel], fftLength/2); + 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]; } - return powerSpectra[channel]; } @@ -202,25 +221,27 @@ public class RawDataTransforms { * @param fftLength * @return Sum of power spectra */ - 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]; + 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]; + } } } + return totalPowerSpectrum; } - return totalPowerSpectrum; } @@ -235,15 +256,17 @@ 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 synchronized ComplexArray getComplexSpectrumHann(int channel, int fftLength) { - complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())]; - if (complexSpectrum[channel] == null - || complexSpectrum.length != fftLength / 2) { + public ComplexArray getComplexSpectrumHann(int channel, int fftLength) { + synchronized (synchObject) { + complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())]; + if (complexSpectrum[channel] == null + || complexSpectrum.length != fftLength / 2) { - complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength); - currentSpecLen = fftLength; + complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength); + currentSpecLen = fftLength; + } + return complexSpectrum[channel]; } - return complexSpectrum[channel]; } @@ -300,8 +323,8 @@ public class RawDataTransforms { public ClipSpectrogram getSpectrogram(int fftSize, int fftHop) { return getSpectrogram( fftSize, fftHop, 1); } - - + + /** * Get a spectrogram image of the wave clip. The clip is null until called. It is recalculated if the * FFT length and/or hop size are different. @@ -371,29 +394,31 @@ public class RawDataTransforms { * @param fftLength * @return the complex spectrum */ - public synchronized ComplexArray getComplexSpectrum(int channel, int fftLength) { - double[] paddedRawData; - double[] rawData; - int i, mn; + public ComplexArray getComplexSpectrum(int channel, int fftLength) { + synchronized (synchObject) { + 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]; + if (complexSpectrum == null) { + complexSpectrum = new ComplexArray[getNChan()]; } - for (i = mn; i < fftLength; i++) { - paddedRawData[i] = 0; + 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); } - complexSpectrum[channel] = fastFFT.rfft(paddedRawData, fftLength); + return complexSpectrum[channel]; } - return complexSpectrum[channel]; } @@ -402,14 +427,16 @@ public class RawDataTransforms { * @param iChan channel index * @return analytic waveform */ - public synchronized double[] getAnalyticWaveform(int iChan) { - if (analyticWaveform == null) { - analyticWaveform = new double[getNChan()][]; + 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]; } - // if (analyticWaveform[iChan] == null) { - analyticWaveform[iChan] = hilbert.getHilbert(getWaveData(iChan)); - // } - return analyticWaveform[iChan]; } /** @@ -421,12 +448,14 @@ public class RawDataTransforms { * @param fftFilterParams fft filter parameters. * @return analystic waveform. */ - public synchronized double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) { - if (filtered == false || fftFilterParams == null) { - return getAnalyticWaveform(iChan); - } - else { - return getFilteredAnalyticWaveform(fftFilterParams, iChan); + public double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) { + synchronized (synchObject) { + if (filtered == false || fftFilterParams == null) { + return getAnalyticWaveform(iChan); + } + else { + return getFilteredAnalyticWaveform(fftFilterParams, iChan); + } } } @@ -437,15 +466,17 @@ public class RawDataTransforms { * @param iChan channel number * @return envelope of the filtered data. */ - public synchronized double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) { - if (analyticWaveform == null) { - analyticWaveform = new double[getNChan()][]; + public double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) { + synchronized (synchObject) { + if (analyticWaveform == null) { + analyticWaveform = new double[getNChan()][]; + } + // if (analyticWaveform[iChan] == null) { + analyticWaveform[iChan] = hilbert. + getHilbert(getFilteredWaveData(fftFilterParams, iChan)); + // } + return analyticWaveform[iChan]; } - // if (analyticWaveform[iChan] == null) { - analyticWaveform[iChan] = hilbert. - getHilbert(getFilteredWaveData(fftFilterParams, iChan)); - // } - return analyticWaveform[iChan]; } /** @@ -478,9 +509,11 @@ public class RawDataTransforms { * @param channelIndex channel index * @return filtered waveform data */ - public synchronized double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) { - filteredWaveData = getFilteredWaveData(filterParams); - return filteredWaveData[channelIndex]; + public double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) { + synchronized (synchObject) { + filteredWaveData = getFilteredWaveData(filterParams); + return filteredWaveData[channelIndex]; + } } /** @@ -489,12 +522,14 @@ public class RawDataTransforms { * @param filterParams filter parameters * @return array of filtered data */ - public synchronized double[][] getFilteredWaveData(FFTFilterParams filterParams) { - //System.out.println("Make filterred wave data!: " + (filterParams != oldFFTFilterParams)); - if (filteredWaveData == null || filterParams != oldFFTFilterParams) { - filteredWaveData = makeFilteredWaveData(filterParams); + public double[][] getFilteredWaveData(FFTFilterParams filterParams) { + synchronized (synchObject) { + //System.out.println("Make filterred wave data!: " + (filterParams != oldFFTFilterParams)); + if (filteredWaveData == null || filterParams != oldFFTFilterParams) { + filteredWaveData = makeFilteredWaveData(filterParams); + } + return filteredWaveData; } - return filteredWaveData; } private double[][] makeFilteredWaveData(FFTFilterParams filterParams) { @@ -574,7 +609,7 @@ public class RawDataTransforms { public double[] getWaveData(int channel) { return this.rawData.getWaveData()[channel]; } - + /** * Get the wave data for the given channel in int16 format. * @param channel channel index diff --git a/src/PamguardMVC/dataSelector/DataSelectorCreator.java b/src/PamguardMVC/dataSelector/DataSelectorCreator.java index ffc458eb..77e199b5 100644 --- a/src/PamguardMVC/dataSelector/DataSelectorCreator.java +++ b/src/PamguardMVC/dataSelector/DataSelectorCreator.java @@ -62,19 +62,38 @@ public abstract class DataSelectorCreator implements PamSettings { } /** - * Get a data selector of a given name. + * Get a data selector of a given name including all possible options for super detections and annotations. * @param selectorName * @param allowScores - * @return + * @return data selector for given name. */ public synchronized DataSelector getDataSelector(String selectorName, boolean allowScores, String selectorType) { + return getDataSelector(selectorName, allowScores, selectorType, true, true); + } + + /** + * + * Get a data selector of a given name with optional inclusion of options for annotations and super detections + * @param selectorName data selector name + * @param allowScores allow scores + * @param selectorType + * @param includeAnnotations include options from any annotators of this data stream + * @param includeSuperDetections include any possible super detection data selectors. + * @return data selector for given name with appropriate options. + */ + public synchronized DataSelector getDataSelector(String selectorName, boolean allowScores, String selectorType, + boolean includeAnnotations, boolean includeSuperDetections) { DataSelector ds = findDataSelector(selectorName); if (ds == null) { ds = createDataSelector(selectorName, allowScores, selectorType); - ds = addAnnotationOptions(ds, selectorName, allowScores, selectorType); + if (includeAnnotations) { + ds = addAnnotationOptions(ds, selectorName, allowScores, selectorType); + } - ds = addSuperDetectionOptions(ds, selectorName, allowScores, selectorType); + if (includeSuperDetections) { + ds = addSuperDetectionOptions(ds, selectorName, allowScores, selectorType); + } // and get it's params from the centralised list. DataSelectParams params = dataSelectorSettings.getParams(selectorName); From c5c0f9bb44ea1c71c23830e31a60488e527bfe6b Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 17 Jun 2022 17:02:41 +0100 Subject: [PATCH 31/37] Bug fixes for data selectors (#38) * Global arguments to set params in acquisition and database. * Fix bug in CTD dataselector panel. * Extra options for creating data selectors Extra options so dataselectors can be kept simple and not include all possible options for super detections as well as their own - more use when selecting data for a superdetection ! --- src/PamguardMVC/PamDataBlock.java | 22 ++ src/PamguardMVC/RawDataTransforms.java | 261 ++++++++++-------- .../dataSelector/DataSelectorCreator.java | 27 +- .../dataselector/CTDataSelectPanel.java | 6 +- 4 files changed, 198 insertions(+), 118 deletions(-) diff --git a/src/PamguardMVC/PamDataBlock.java b/src/PamguardMVC/PamDataBlock.java index 4db0263c..20d6aedb 100644 --- a/src/PamguardMVC/PamDataBlock.java +++ b/src/PamguardMVC/PamDataBlock.java @@ -3705,6 +3705,28 @@ public class PamDataBlock extends PamObservable { return blockDataSelector; } + /** + * Convenience method to save programmer from having to call into the creator + * all the time. + * + * @param selectorName + * @param allowScores + * @param selectorType Type of selector, generally a ModuleType name, e.g. Map, + * so that options can be tailored to specific needs + * @param includeAnnotations include options from any annotators of this data stream + * @param includeSuperDetections include any possible super detection data selectors. + * @return null or a DataSelector + */ + public DataSelector getDataSelector(String selectorName, boolean allowScores, String selectorType, + boolean includeAnnotations, boolean includeSuperDetections) { + DataSelector blockDataSelector = null; + DataSelectorCreator dsc = getDataSelectCreator(); + if (dsc != null) { + blockDataSelector = dsc.getDataSelector(selectorName, allowScores, selectorType, includeAnnotations, includeSuperDetections); + } + return blockDataSelector; + } + /** * Get any information from the data block about cross referencing in database * tables. diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index c0684730..81d8e3fe 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -99,11 +99,26 @@ public class RawDataTransforms { */ private int shortestFFTLength; + private Object synchObject; - - public RawDataTransforms(@SuppressWarnings("rawtypes") PamDataUnit rawDataHolder) { + /** + * Create a RawDataTransforms with a specified synchronisation object. This is mostly the data unit, + * but in some circumstances may need to be a different object to avoid thread lock. + * @param rawDataHolder + * @param synchObject + */ + public RawDataTransforms(@SuppressWarnings("rawtypes") PamDataUnit rawDataHolder, Object synchObject) { this.rawData=(RawDataHolder) rawDataHolder; this.dataUnit = rawDataHolder; + this.synchObject = synchObject; + } + + /** + * Create raw data transforms using the rawDatAholder for synchronisation. + * @param rawDataHolder + */ + public RawDataTransforms(@SuppressWarnings("rawtypes") PamDataUnit rawDataHolder) { + this(rawDataHolder, rawDataHolder); } @@ -118,7 +133,7 @@ public class RawDataTransforms { return powerSpectra; } - + /** * Get the shortest FFT length for the number of samples. * The is the @@ -143,23 +158,25 @@ public class RawDataTransforms { * @param fftLength * @return Power spectrum */ - 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(); + 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(); } - - 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(); } /** @@ -170,29 +187,31 @@ public class RawDataTransforms { * @param fftLength * @return Power spectrum */ - 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; - + public double[] getPowerSpectrum(int channel, int fftLength) { + synchronized (synchObject) { + if (powerSpectra == null) { + powerSpectra = new double[PamUtils.getNumChannels(dataUnit.getChannelBitmap())][]; } - if (powerSpectra[channel].length != fftLength/2) { - powerSpectra[channel] = Arrays.copyOf(powerSpectra[channel], fftLength/2); + 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]; } - return powerSpectra[channel]; } @@ -202,25 +221,27 @@ public class RawDataTransforms { * @param fftLength * @return Sum of power spectra */ - 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]; + 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]; + } } } + return totalPowerSpectrum; } - return totalPowerSpectrum; } @@ -235,15 +256,17 @@ 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 synchronized ComplexArray getComplexSpectrumHann(int channel, int fftLength) { - complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())]; - if (complexSpectrum[channel] == null - || complexSpectrum.length != fftLength / 2) { + public ComplexArray getComplexSpectrumHann(int channel, int fftLength) { + synchronized (synchObject) { + complexSpectrum = new ComplexArray[PamUtils.getNumChannels(dataUnit.getChannelBitmap())]; + if (complexSpectrum[channel] == null + || complexSpectrum.length != fftLength / 2) { - complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength); - currentSpecLen = fftLength; + complexSpectrum[channel] = getComplexSpectrumHann(rawData.getWaveData()[channel], fftLength); + currentSpecLen = fftLength; + } + return complexSpectrum[channel]; } - return complexSpectrum[channel]; } @@ -300,8 +323,8 @@ public class RawDataTransforms { public ClipSpectrogram getSpectrogram(int fftSize, int fftHop) { return getSpectrogram( fftSize, fftHop, 1); } - - + + /** * Get a spectrogram image of the wave clip. The clip is null until called. It is recalculated if the * FFT length and/or hop size are different. @@ -371,29 +394,31 @@ public class RawDataTransforms { * @param fftLength * @return the complex spectrum */ - public synchronized ComplexArray getComplexSpectrum(int channel, int fftLength) { - double[] paddedRawData; - double[] rawData; - int i, mn; + public ComplexArray getComplexSpectrum(int channel, int fftLength) { + synchronized (synchObject) { + 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]; + if (complexSpectrum == null) { + complexSpectrum = new ComplexArray[getNChan()]; } - for (i = mn; i < fftLength; i++) { - paddedRawData[i] = 0; + 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); } - complexSpectrum[channel] = fastFFT.rfft(paddedRawData, fftLength); + return complexSpectrum[channel]; } - return complexSpectrum[channel]; } @@ -402,14 +427,16 @@ public class RawDataTransforms { * @param iChan channel index * @return analytic waveform */ - public synchronized double[] getAnalyticWaveform(int iChan) { - if (analyticWaveform == null) { - analyticWaveform = new double[getNChan()][]; + 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]; } - // if (analyticWaveform[iChan] == null) { - analyticWaveform[iChan] = hilbert.getHilbert(getWaveData(iChan)); - // } - return analyticWaveform[iChan]; } /** @@ -421,12 +448,14 @@ public class RawDataTransforms { * @param fftFilterParams fft filter parameters. * @return analystic waveform. */ - public synchronized double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) { - if (filtered == false || fftFilterParams == null) { - return getAnalyticWaveform(iChan); - } - else { - return getFilteredAnalyticWaveform(fftFilterParams, iChan); + public double[] getAnalyticWaveform(int iChan, boolean filtered, FFTFilterParams fftFilterParams) { + synchronized (synchObject) { + if (filtered == false || fftFilterParams == null) { + return getAnalyticWaveform(iChan); + } + else { + return getFilteredAnalyticWaveform(fftFilterParams, iChan); + } } } @@ -437,15 +466,17 @@ public class RawDataTransforms { * @param iChan channel number * @return envelope of the filtered data. */ - public synchronized double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) { - if (analyticWaveform == null) { - analyticWaveform = new double[getNChan()][]; + public double[] getFilteredAnalyticWaveform(FFTFilterParams fftFilterParams, int iChan) { + synchronized (synchObject) { + if (analyticWaveform == null) { + analyticWaveform = new double[getNChan()][]; + } + // if (analyticWaveform[iChan] == null) { + analyticWaveform[iChan] = hilbert. + getHilbert(getFilteredWaveData(fftFilterParams, iChan)); + // } + return analyticWaveform[iChan]; } - // if (analyticWaveform[iChan] == null) { - analyticWaveform[iChan] = hilbert. - getHilbert(getFilteredWaveData(fftFilterParams, iChan)); - // } - return analyticWaveform[iChan]; } /** @@ -478,9 +509,11 @@ public class RawDataTransforms { * @param channelIndex channel index * @return filtered waveform data */ - public synchronized double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) { - filteredWaveData = getFilteredWaveData(filterParams); - return filteredWaveData[channelIndex]; + public double[] getFilteredWaveData(FFTFilterParams filterParams, int channelIndex) { + synchronized (synchObject) { + filteredWaveData = getFilteredWaveData(filterParams); + return filteredWaveData[channelIndex]; + } } /** @@ -489,12 +522,14 @@ public class RawDataTransforms { * @param filterParams filter parameters * @return array of filtered data */ - public synchronized double[][] getFilteredWaveData(FFTFilterParams filterParams) { - //System.out.println("Make filterred wave data!: " + (filterParams != oldFFTFilterParams)); - if (filteredWaveData == null || filterParams != oldFFTFilterParams) { - filteredWaveData = makeFilteredWaveData(filterParams); + public double[][] getFilteredWaveData(FFTFilterParams filterParams) { + synchronized (synchObject) { + //System.out.println("Make filterred wave data!: " + (filterParams != oldFFTFilterParams)); + if (filteredWaveData == null || filterParams != oldFFTFilterParams) { + filteredWaveData = makeFilteredWaveData(filterParams); + } + return filteredWaveData; } - return filteredWaveData; } private double[][] makeFilteredWaveData(FFTFilterParams filterParams) { @@ -574,7 +609,7 @@ public class RawDataTransforms { public double[] getWaveData(int channel) { return this.rawData.getWaveData()[channel]; } - + /** * Get the wave data for the given channel in int16 format. * @param channel channel index diff --git a/src/PamguardMVC/dataSelector/DataSelectorCreator.java b/src/PamguardMVC/dataSelector/DataSelectorCreator.java index ffc458eb..77e199b5 100644 --- a/src/PamguardMVC/dataSelector/DataSelectorCreator.java +++ b/src/PamguardMVC/dataSelector/DataSelectorCreator.java @@ -62,19 +62,38 @@ public abstract class DataSelectorCreator implements PamSettings { } /** - * Get a data selector of a given name. + * Get a data selector of a given name including all possible options for super detections and annotations. * @param selectorName * @param allowScores - * @return + * @return data selector for given name. */ public synchronized DataSelector getDataSelector(String selectorName, boolean allowScores, String selectorType) { + return getDataSelector(selectorName, allowScores, selectorType, true, true); + } + + /** + * + * Get a data selector of a given name with optional inclusion of options for annotations and super detections + * @param selectorName data selector name + * @param allowScores allow scores + * @param selectorType + * @param includeAnnotations include options from any annotators of this data stream + * @param includeSuperDetections include any possible super detection data selectors. + * @return data selector for given name with appropriate options. + */ + public synchronized DataSelector getDataSelector(String selectorName, boolean allowScores, String selectorType, + boolean includeAnnotations, boolean includeSuperDetections) { DataSelector ds = findDataSelector(selectorName); if (ds == null) { ds = createDataSelector(selectorName, allowScores, selectorType); - ds = addAnnotationOptions(ds, selectorName, allowScores, selectorType); + if (includeAnnotations) { + ds = addAnnotationOptions(ds, selectorName, allowScores, selectorType); + } - ds = addSuperDetectionOptions(ds, selectorName, allowScores, selectorType); + if (includeSuperDetections) { + ds = addSuperDetectionOptions(ds, selectorName, allowScores, selectorType); + } // and get it's params from the centralised list. DataSelectParams params = dataSelectorSettings.getParams(selectorName); diff --git a/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java b/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java index 925741fe..10bc4ca0 100644 --- a/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java +++ b/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java @@ -3,6 +3,7 @@ package clickTrainDetector.layout.dataselector; import java.awt.BorderLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.util.ArrayList; import javax.swing.JCheckBox; import javax.swing.JComponent; @@ -15,6 +16,7 @@ import PamView.dialog.PamDialog; import PamView.dialog.PamDialogPanel; import PamView.dialog.PamGridBagContraints; import PamguardMVC.dataSelector.DataSelectParams; +import clickTrainDetector.classification.CTClassifier; import clickTrainDetector.classification.CTClassifierManager; import clickTrainDetector.dataselector.CTDataSelector; import clickTrainDetector.dataselector.CTSelectParams; @@ -287,9 +289,11 @@ public class CTDataSelectPanel implements PamDialogPanel { // System.out.println("No. count: " + count); currentParams.classifier = new int[count]; + ArrayList classifiers = ctDataSelector.getClickControl().getClassifierManager().getCurrentClassifiers(); + int used = 0; for (int i=0; i Date: Mon, 20 Jun 2022 16:56:43 +0100 Subject: [PATCH 32/37] Move symbols dialogs close to where mouse is. --- src/PamView/dialog/PamDialog.java | 11 +++++++++-- .../paneloverlay/OverlayCheckboxMenuSelect.java | 3 +++ .../layout/dataselector/CTDataSelectPanel.java | 5 ++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/PamView/dialog/PamDialog.java b/src/PamView/dialog/PamDialog.java index 90d966af..ad20c635 100644 --- a/src/PamView/dialog/PamDialog.java +++ b/src/PamView/dialog/PamDialog.java @@ -257,15 +257,22 @@ abstract public class PamDialog extends JDialog { // check we're not going too far off the screen. Dimension sz = getPreferredSize(); Dimension screen = null; + int w, h; if (getOwner() != null) { + Window owner = getOwner(); + Rectangle bounds = owner.getBounds(); + w = bounds.x+bounds.width; + h = bounds.y+bounds.height; screen = getOwner().getSize(); } else { screen = Toolkit.getDefaultToolkit().getScreenSize(); + w = screen.width; + h = screen.height; } - point.y = Math.min(point.y, screen.height-sz.height-10); + point.y = Math.min(point.y, h-sz.height-10); point.y = Math.max(point.y, 0); - point.x = Math.min(point.x, screen.width-sz.width-10); + point.x = Math.min(point.x, w-sz.width-10); point.x = Math.max(point.x, 0); setLocation(point); diff --git a/src/PamView/paneloverlay/OverlayCheckboxMenuSelect.java b/src/PamView/paneloverlay/OverlayCheckboxMenuSelect.java index 20dfe5e1..fc743661 100644 --- a/src/PamView/paneloverlay/OverlayCheckboxMenuSelect.java +++ b/src/PamView/paneloverlay/OverlayCheckboxMenuSelect.java @@ -48,6 +48,9 @@ public class OverlayCheckboxMenuSelect implements ActionListener { return; } DataSelectDialog dataSelectDialog = new DataSelectDialog(javaFrame, dataBlock, dataSelector, symbolChooser); + if (javaFrame == null) { + dataSelectDialog.moveToMouseLocation(); + } boolean ok = dataSelectDialog.showDialog(); if (ok) { menuItem.setSelected(true); diff --git a/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java b/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java index 925741fe..9241ef56 100644 --- a/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java +++ b/src/clickTrainDetector/layout/dataselector/CTDataSelectPanel.java @@ -3,6 +3,7 @@ package clickTrainDetector.layout.dataselector; import java.awt.BorderLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.util.ArrayList; import javax.swing.JCheckBox; import javax.swing.JComponent; @@ -15,6 +16,7 @@ import PamView.dialog.PamDialog; import PamView.dialog.PamDialogPanel; import PamView.dialog.PamGridBagContraints; import PamguardMVC.dataSelector.DataSelectParams; +import clickTrainDetector.classification.CTClassifier; import clickTrainDetector.classification.CTClassifierManager; import clickTrainDetector.dataselector.CTDataSelector; import clickTrainDetector.dataselector.CTSelectParams; @@ -289,7 +291,8 @@ public class CTDataSelectPanel implements PamDialogPanel { currentParams.classifier = new int[count]; for (int i=0; i currentClassifiers = ctDataSelector.getClickControl().getClassifierManager().getCurrentClassifiers(); + currentParams.classifier[i] = currentClassifiers.get(i).getSpeciesID(); } } From 87376a70ec876c1299700334128a2c0c4fa65874 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 23 Jun 2022 15:55:05 +0100 Subject: [PATCH 33/37] Fix null pointer exception while marking --- .../overlaymark/MarkOverlayDraw.java | 100 ++++++++++-------- .../paneloverlay/overlaymark/OverlayMark.java | 3 + 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src/PamView/paneloverlay/overlaymark/MarkOverlayDraw.java b/src/PamView/paneloverlay/overlaymark/MarkOverlayDraw.java index 20598c41..1a7d6f76 100644 --- a/src/PamView/paneloverlay/overlaymark/MarkOverlayDraw.java +++ b/src/PamView/paneloverlay/overlaymark/MarkOverlayDraw.java @@ -143,52 +143,60 @@ public class MarkOverlayDraw extends PanelOverlayDraw { * To allow for things like the rotated map which no longer show rectangles as * rectangles, we now need to draw rectangles are irregular polygons too ! */ - int[] x = new int[4]; - int[] y = new int[4]; - PamCoordinate m0 = mark.getCoordinate(0); - PamCoordinate m1 = mark.getCoordinate(1); - Point p; - p = generalProjector.getCoord3d(m0.getCoordinate(0), m0.getCoordinate(1), 0).getXYPoint(); - x[0] = p.x; - y[0] = p.y; - p = generalProjector.getCoord3d(m0.getCoordinate(0), m1.getCoordinate(1), 0).getXYPoint(); - x[1] = p.x; - y[1] = p.y; - p = generalProjector.getCoord3d(m1.getCoordinate(0), m1.getCoordinate(1), 0).getXYPoint(); - x[2] = p.x; - y[2] = p.y; - p = generalProjector.getCoord3d(m1.getCoordinate(0), m0.getCoordinate(1), 0).getXYPoint(); - x[3] = p.x; - y[3] = p.y; - Polygon pgon = new Polygon(x, y, 4); - Color fillCol = getFillCol(); - if (fillCol != null) { - g2d.setColor(getFillCol()); - g2d.fillPolygon(pgon); + try { + int[] x = new int[4]; + int[] y = new int[4]; + PamCoordinate m0 = mark.getCoordinate(0); + PamCoordinate m1 = mark.getCoordinate(1); + if (m0 == null || m1 == null) { + return null; + } + Point p; + p = generalProjector.getCoord3d(m0.getCoordinate(0), m0.getCoordinate(1), 0).getXYPoint(); + x[0] = p.x; + y[0] = p.y; + p = generalProjector.getCoord3d(m0.getCoordinate(0), m1.getCoordinate(1), 0).getXYPoint(); + x[1] = p.x; + y[1] = p.y; + p = generalProjector.getCoord3d(m1.getCoordinate(0), m1.getCoordinate(1), 0).getXYPoint(); + x[2] = p.x; + y[2] = p.y; + p = generalProjector.getCoord3d(m1.getCoordinate(0), m0.getCoordinate(1), 0).getXYPoint(); + x[3] = p.x; + y[3] = p.y; + Polygon pgon = new Polygon(x, y, 4); + Color fillCol = getFillCol(); + if (fillCol != null) { + g2d.setColor(getFillCol()); + g2d.fillPolygon(pgon); + } + return pgon; } - return pgon; - -// -// Point p0 = generalProjector.getCoord3d(m0).getXYPoint(); -// Point p1 = generalProjector.getCoord3d(m1).getXYPoint(); -// g2d.setColor(getLineCol()); -// g2d.setStroke(getLineStroke()); -// g2d.drawLine(p0.x, p0.y, p1.x, p0.y); -// g2d.drawLine(p1.x, p0.y, p1.x, p1.y); -// g2d.drawLine(p1.x, p1.y, p0.x, p1.y); -// g2d.drawLine(p0.x, p1.y, p0.x, p0.y); -// int x = Math.min(p0.x, p1.x); -// int y = Math.min(p0.y, p1.y); -// int w = Math.abs(p0.x - p1.x); -// int h = Math.abs(p0.y - p1.y); -// -// Color fillCol = getFillCol(); -// if (fillCol != null) { -// g2d.setColor(getFillCol()); -// g2d.fillRect(x, y, w, h); -// } -// Rectangle r = new Rectangle(x, y, w, h); -// return r; + catch (NullPointerException e) { + return null; + } + + // + // Point p0 = generalProjector.getCoord3d(m0).getXYPoint(); + // Point p1 = generalProjector.getCoord3d(m1).getXYPoint(); + // g2d.setColor(getLineCol()); + // g2d.setStroke(getLineStroke()); + // g2d.drawLine(p0.x, p0.y, p1.x, p0.y); + // g2d.drawLine(p1.x, p0.y, p1.x, p1.y); + // g2d.drawLine(p1.x, p1.y, p0.x, p1.y); + // g2d.drawLine(p0.x, p1.y, p0.x, p0.y); + // int x = Math.min(p0.x, p1.x); + // int y = Math.min(p0.y, p1.y); + // int w = Math.abs(p0.x - p1.x); + // int h = Math.abs(p0.y - p1.y); + // + // Color fillCol = getFillCol(); + // if (fillCol != null) { + // g2d.setColor(getFillCol()); + // g2d.fillRect(x, y, w, h); + // } + // Rectangle r = new Rectangle(x, y, w, h); + // return r; } @Override @@ -223,7 +231,7 @@ public class MarkOverlayDraw extends PanelOverlayDraw { public Stroke getLineStroke() { return plainStroke ; } - + public Stroke getFinalLineStroke() { return dashed; } diff --git a/src/PamView/paneloverlay/overlaymark/OverlayMark.java b/src/PamView/paneloverlay/overlaymark/OverlayMark.java index 00238a6c..2cadab21 100644 --- a/src/PamView/paneloverlay/overlaymark/OverlayMark.java +++ b/src/PamView/paneloverlay/overlaymark/OverlayMark.java @@ -232,6 +232,9 @@ public class OverlayMark { } PamCoordinate c0 = projector.getCoord3d(coordinates.get(0)); PamCoordinate c1 = projector.getCoord3d(coordinates.get(1)); + if (c0 == null || c1 == null) { + return null; + } double x = Math.min(c0.getCoordinate(0), c1.getCoordinate(0)); double y = Math.min(c0.getCoordinate(1), c1.getCoordinate(1)); double w = Math.abs(c0.getCoordinate(0) - c1.getCoordinate(0)); From 7d0df4ca2d254016b45ccfef354903d89cbcb87f Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 23 Jun 2022 16:30:17 +0100 Subject: [PATCH 34/37] Update MarkOverlayDraw.java fix null pointer crash --- src/PamView/paneloverlay/overlaymark/MarkOverlayDraw.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PamView/paneloverlay/overlaymark/MarkOverlayDraw.java b/src/PamView/paneloverlay/overlaymark/MarkOverlayDraw.java index 1a7d6f76..530b6a97 100644 --- a/src/PamView/paneloverlay/overlaymark/MarkOverlayDraw.java +++ b/src/PamView/paneloverlay/overlaymark/MarkOverlayDraw.java @@ -76,6 +76,9 @@ public class MarkOverlayDraw extends PanelOverlayDraw { hoverData.setTransformShape(t); generalProjector.addHoverData(t, pamDataUnit); } + if (drawnShape == null) { + return null; + } return drawnShape.getBounds(); } From 6ffc807803cdef663021351765f16203351ae39d Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 24 Jun 2022 11:28:48 +0100 Subject: [PATCH 35/37] Improve scroller logging Additional options to only log outer scroller (data load) movement, not the very fine stuff. --- src/effortmonitor/EffortControl.java | 82 +++++++++++++++- src/effortmonitor/EffortDataUnit.java | 10 ++ src/effortmonitor/EffortParams.java | 5 + src/effortmonitor/swing/EffortDialog.java | 18 ++++ src/effortmonitor/swing/EffortSidePanel.java | 99 ++++++++++++++++++++ src/pamScrollSystem/PamScrollerData.java | 2 +- 6 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 src/effortmonitor/swing/EffortSidePanel.java diff --git a/src/effortmonitor/EffortControl.java b/src/effortmonitor/EffortControl.java index 318a6bab..a15b1cf2 100644 --- a/src/effortmonitor/EffortControl.java +++ b/src/effortmonitor/EffortControl.java @@ -5,6 +5,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.Serializable; import java.util.ArrayList; +import java.util.HashMap; import java.util.Vector; import javax.swing.JCheckBoxMenuItem; @@ -18,14 +19,18 @@ import PamController.PamController; import PamController.PamSettingManager; import PamController.PamSettings; import PamUtils.PamCalendar; +import PamView.PamSidePanel; import PamguardMVC.PamDataBlock; import PamguardMVC.PamProcess; import PamguardMVC.debug.Debug; import effortmonitor.swing.EffortDataMapGraph; import effortmonitor.swing.EffortDialog; import effortmonitor.swing.EffortDisplayProvider; +import effortmonitor.swing.EffortSidePanel; import pamScrollSystem.AbstractPamScroller; import pamScrollSystem.AbstractScrollManager; +import pamScrollSystem.PamScroller; +import pamScrollSystem.PamScrollerData; import userDisplay.UserDisplayControl; public class EffortControl extends PamControlledUnit implements PamSettings{ @@ -42,6 +47,10 @@ public class EffortControl extends PamControlledUnit implements PamSettings{ private EffortDataMapGraph dataMapGraph; + private HashMap lastOuterStarts = new HashMap<>(); + + private EffortSidePanel effortSidePanel; + public EffortControl(String unitName) { super("unitType", unitName); EffortProcess effortProc = new EffortProcess(); @@ -50,12 +59,22 @@ public class EffortControl extends PamControlledUnit implements PamSettings{ effortProc.addOutputDataBlock(effortDataBlock); effortDataBlock.SetLogging(new EffortLogging(this, effortDataBlock)); dataMapGraph = new EffortDataMapGraph(this); + +// PamScroller UserDisplayControl.addUserDisplayProvider(new EffortDisplayProvider(this)); PamSettingManager.getInstance().registerSettings(this); } + @Override + public PamSidePanel getSidePanel() { + if (effortSidePanel == null) { + effortSidePanel = new EffortSidePanel(this); + } + return effortSidePanel; + } + @Override public void notifyModelChanged(int changeType) { super.notifyModelChanged(changeType); @@ -65,6 +84,10 @@ public class EffortControl extends PamControlledUnit implements PamSettings{ break; case PamController.NEW_SCROLL_TIME: updateScrollerInfo(); + break; + case PamController.DATA_LOAD_COMPLETE: + updateScrollerInfo(); + break; } } @@ -102,7 +125,25 @@ public class EffortControl extends PamControlledUnit implements PamSettings{ */ return; } + + boolean newOuter = isNewOuter(scroller); + if (effortParams.outserScrollOnly) { + if (newOuter) { + logOuterScroll(scroller); + } + } + else { + logInnerScroll(scroller); + } + } + + private void logOuterScroll(AbstractPamScroller scroller) { + EffortDataUnit effortData = new EffortDataUnit(scroller, effortParams.getObserver(), effortParams.getObjective()); + effortData.setOuterLimits(); + effortDataBlock.addPamData(effortData); + } + private void logInnerScroll(AbstractPamScroller scroller) { // checkObserverName(); EffortDataUnit effortData = effortDataBlock.findActiveUnit(scroller); @@ -129,6 +170,38 @@ public class EffortControl extends PamControlledUnit implements PamSettings{ } } + /** + * Has the outer scroll moved. + * @param scroller + * @return true if the outer has moved. + */ + private boolean isNewOuter(AbstractPamScroller scroller) { + PamScrollerData lastData = lastOuterStarts.get(scroller); + PamScrollerData nowVal = scroller.getScrollerData(); + if (nowVal == null) { + return false; // shouldn't happen ! + } + boolean changed = isChanged(lastData, nowVal); + if (changed) { + lastOuterStarts.put(scroller, nowVal.clone()); + } + return changed; + } + + private boolean isChanged(PamScrollerData lastData, PamScrollerData nowVal) { + if (lastData == null || nowVal == null) { + return true; + } + if (lastData.getMinimumMillis() != nowVal.getMinimumMillis()) { + return true; + } + if (lastData.getMaximumMillis() != nowVal.getMaximumMillis()) { + return true; + } + + return false; + } + /** * Quick and dirty to get observer name. */ @@ -203,9 +276,11 @@ public class EffortControl extends PamControlledUnit implements PamSettings{ existingUnit.setObserver(newParams.getObserver()); existingUnit.setObjective(newParams.getObjective()); } - return true; } - return false; + if (effortSidePanel != null) { + effortSidePanel.updateSettings(); + } + return newParams!=null; } public void goToTime(AbstractPamScroller scroller, long timeInMilliseconds) { @@ -235,6 +310,9 @@ public class EffortControl extends PamControlledUnit implements PamSettings{ @Override public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { effortParams = ((EffortParams) pamControlledUnitSettings.getSettings()); + if (effortSidePanel != null) { + effortSidePanel.updateSettings(); + } return true; } diff --git a/src/effortmonitor/EffortDataUnit.java b/src/effortmonitor/EffortDataUnit.java index d4e802c8..99c8eb7f 100644 --- a/src/effortmonitor/EffortDataUnit.java +++ b/src/effortmonitor/EffortDataUnit.java @@ -42,6 +42,7 @@ public class EffortDataUnit extends PamDataUnit { getBasicData().setEndTime(scroller.getValueMillis()+scroller.getVisibleAmount()); } + /** * Constructor to use when reloading from database. * @param databaseIndex @@ -210,4 +211,13 @@ public class EffortDataUnit extends PamDataUnit { return runMode; } + + /** + * Set the limits as the outer limits of the scroller. + */ + public void setOuterLimits() { + setTimeMilliseconds(scroller.getMinimumMillis()); + getBasicData().setEndTime(scroller.getMaximumMillis()); + } + } diff --git a/src/effortmonitor/EffortParams.java b/src/effortmonitor/EffortParams.java index beb35249..fcd5bcb5 100644 --- a/src/effortmonitor/EffortParams.java +++ b/src/effortmonitor/EffortParams.java @@ -16,6 +16,11 @@ public class EffortParams implements Cloneable, Serializable, ManagedParameters public transient boolean isSet; + /** + * Only log outer scroll events when paging forwards, not every little movement. + */ + public boolean outserScrollOnly = false; + private static final int MAX_OBSERVERS = 10; private static final int MAX_OBJECTIVES = 10; diff --git a/src/effortmonitor/swing/EffortDialog.java b/src/effortmonitor/swing/EffortDialog.java index 3c8b6cdf..4f4d8b7f 100644 --- a/src/effortmonitor/swing/EffortDialog.java +++ b/src/effortmonitor/swing/EffortDialog.java @@ -8,9 +8,11 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.LinkedList; +import javax.swing.ButtonGroup; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JRadioButton; import javax.swing.border.TitledBorder; import PamView.DBTextArea; @@ -29,6 +31,7 @@ public class EffortDialog extends PamDialog { private JComboBox observer; private JComboBox oldObjectives; private DBTextArea objective; + private JRadioButton outerOnly, allActions; private EffortDialog(Window parentFrame, EffortControl effortControl) { super(parentFrame, effortControl.getUnitName(), false); @@ -40,6 +43,18 @@ public class EffortDialog extends PamDialog { mainPanel.add(new JLabel("Observer name or initials"), c); c.gridx++; mainPanel.add(observer = new JComboBox(), c); + outerOnly = new JRadioButton("Log uter scroll only"); + allActions = new JRadioButton("Log all scroll actions"); + ButtonGroup bg = new ButtonGroup(); + bg.add(allActions); + bg.add(outerOnly); + c.gridx = 0; + c.gridy++; + c.gridwidth = 1; + mainPanel.add(allActions, c); + c.gridx+=c.gridwidth; + mainPanel.add(outerOnly, c); + c.gridx = 0; c.gridy++; c.gridwidth = 2; @@ -80,6 +95,8 @@ public class EffortDialog extends PamDialog { for (String obs : oldObs) { observer.addItem(obs); } + allActions.setSelected(effortParams.outserScrollOnly == false); + outerOnly.setSelected(effortParams.outserScrollOnly); oldObjectives.removeAllItems(); oldObjectives.addItem(""); for (String obj : effortParams.getRecentObjectives()) { @@ -100,6 +117,7 @@ public class EffortDialog extends PamDialog { if (obs == null || obs.length() == 0) { return showWarning("You must give your name or initials to contine"); } + effortParams.outserScrollOnly = outerOnly.isSelected(); effortParams.setObserver(obs); effortParams.setObjective(objective.getText()); return true; diff --git a/src/effortmonitor/swing/EffortSidePanel.java b/src/effortmonitor/swing/EffortSidePanel.java new file mode 100644 index 00000000..d10ab983 --- /dev/null +++ b/src/effortmonitor/swing/EffortSidePanel.java @@ -0,0 +1,99 @@ +package effortmonitor.swing; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamView.PamSidePanel; +import PamView.component.PamSettingsIconButton; +import PamView.dialog.PamCheckBox; +import PamView.dialog.PamGridBagContraints; +import PamView.dialog.PamLabel; +import PamView.dialog.PamTextField; +import PamView.panel.PamPanel; +import effortmonitor.EffortControl; +import effortmonitor.EffortObserver; +import effortmonitor.EffortParams; + +public class EffortSidePanel implements PamSidePanel, EffortObserver { + + private PamPanel mainPanel; + + private PamTextField person; + + private PamCheckBox logging; + + private PamSettingsIconButton settingsButton; + + private EffortControl effortControl; + + public EffortSidePanel(EffortControl effortControl) { + this.effortControl = effortControl; + + mainPanel = new PamPanel(new GridBagLayout()); + mainPanel.setBorder(new TitledBorder(effortControl.getUnitName())); + GridBagConstraints c = new PamGridBagContraints(); + mainPanel.add(new PamLabel("Person ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(person = new PamTextField(6), c); + person.setEditable(false); + c.gridx = 0; + c.gridy++; + c.gridwidth = 1; + mainPanel.add(logging = new PamCheckBox("Log effort"), c); + c.gridx += c.gridwidth; + mainPanel.add(settingsButton = new PamSettingsIconButton(), c); + settingsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + settings(); + } + }); + effortControl.addObserver(this); + logging.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + loggingButton(); + } + }); + + updateSettings(); + } + + protected void loggingButton() { + effortControl.setOnEffort(logging.isSelected()); + } + + protected void settings() { + effortControl.showSettingsDialog(effortControl.getGuiFrame(), null); + } + + public void updateSettings() { + EffortParams params = effortControl.getEffortParams(); + person.setText(params.getObserver()); + logging.setSelected(params.isSet); + } + + @Override + public JComponent getPanel() { + return mainPanel; + } + + @Override + public void rename(String newName) { + mainPanel.setBorder(new TitledBorder(newName)); + } + + @Override + public void statusChange() { + updateSettings(); + } + +} diff --git a/src/pamScrollSystem/PamScrollerData.java b/src/pamScrollSystem/PamScrollerData.java index 133dde58..11cc7265 100644 --- a/src/pamScrollSystem/PamScrollerData.java +++ b/src/pamScrollSystem/PamScrollerData.java @@ -58,7 +58,7 @@ public class PamScrollerData implements Serializable, Cloneable { @Override - protected PamScrollerData clone() { + public PamScrollerData clone() { try { return (PamScrollerData) super.clone(); } catch (CloneNotSupportedException e) { From e176899a59f8f5ed3d3a29482a60445b4c02a0b0 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 24 Jun 2022 12:29:38 +0100 Subject: [PATCH 36/37] Add fin whale simulator and ROCCA bug Add 20Hz simulator and another go at ROCCA memory leak --- dependency-reduced-pom.xml | 2 +- pom.xml | 2 +- src/PamController/PamguardVersionInfo.java | 4 ++-- src/rocca/RoccaSidePanel.java | 7 +++++-- src/simulatedAcquisition/SimProcess.java | 2 +- src/simulatedAcquisition/sounds/SimSignals.java | 3 +++ 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index 7ee84c51..5c0901f5 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ org.pamguard Pamguard Pamguard Java12+ - 2.02.04 + 2.02.04a Pamguard for Java 12+, using Maven to control dependcies www.pamguard.org diff --git a/pom.xml b/pom.xml index cfccc70f..8a92ccc5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.pamguard Pamguard - 2.02.04 + 2.02.04a Pamguard Java12+ Pamguard for Java 12+, using Maven to control dependcies www.pamguard.org diff --git a/src/PamController/PamguardVersionInfo.java b/src/PamController/PamguardVersionInfo.java index 2c91e596..7864fce0 100644 --- a/src/PamController/PamguardVersionInfo.java +++ b/src/PamController/PamguardVersionInfo.java @@ -31,12 +31,12 @@ public class PamguardVersionInfo { * Version number, major version.minorversion.sub-release. * Note: can't go higher than sub-release 'f' */ - static public final String version = "2.02.04"; + static public final String version = "2.02.04a"; /** * Release date */ - static public final String date = "11 June 2022"; + static public final String date = "23 June 2022"; // /** // * Release type - Beta or Core diff --git a/src/rocca/RoccaSidePanel.java b/src/rocca/RoccaSidePanel.java index 281724cc..35a8fa55 100644 --- a/src/rocca/RoccaSidePanel.java +++ b/src/rocca/RoccaSidePanel.java @@ -115,8 +115,11 @@ public class RoccaSidePanel extends PamObserverAdapter implements PamSidePanel (roccaControl.roccaProcess, roccaControl.roccaParameters.getChannelMap()); - // this one probably OK to never delete ? - rsdb.setNaturalLifetimeMillis(Integer.MAX_VALUE); + /* + * this one probably OK to never delete ? Nope lots of stuff goes in + * here, so it seems it will eventually bring the system down. + */ + rsdb.setNaturalLifetimeMillis(roccaControl.getMaxDataKeepTime()); rdl = new RoccaDetectionLogger(this, rsdb); rsdb.SetLogging(rdl); diff --git a/src/simulatedAcquisition/SimProcess.java b/src/simulatedAcquisition/SimProcess.java index 50f998d3..f8905170 100644 --- a/src/simulatedAcquisition/SimProcess.java +++ b/src/simulatedAcquisition/SimProcess.java @@ -304,7 +304,7 @@ public class SimProcess extends DaqSystem implements PamSettings { /* simulate the noise, need to divide fs by 2 to only generate for 0 to Nyquist to get correct values out. * that match the results of the noise band monitor modules. */ - double dbNse = simParameters.backgroundNoise + 10*Math.log10(getSampleRate()/2); + double dbNse = simParameters.backgroundNoise + 10*Math.log10(getSampleRate()/2.); nse = daqControl.getDaqProcess().dbMicropascalToSignal(i, dbNse); generateNoise(channelData, nse); // generateNoiseQuickly(channelData, nse, i); diff --git a/src/simulatedAcquisition/sounds/SimSignals.java b/src/simulatedAcquisition/sounds/SimSignals.java index 96e0db19..63137a51 100644 --- a/src/simulatedAcquisition/sounds/SimSignals.java +++ b/src/simulatedAcquisition/sounds/SimSignals.java @@ -27,6 +27,9 @@ public class SimSignals { simSignalList.add(new ClickSound("Click", 3000, 3000, 2e-3, WINDOWTYPE.DECAY)); simSignalList.add(new ClickSound("Chirp", 3000, 6000, 0.1, WINDOWTYPE.TAPER10)); simSignalList.add(new ClickSound("Chirp", 3000, 8000, 0.5, WINDOWTYPE.TAPER10)); + simSignalList.add(new ClickSound("Dolphin Click", 60000, 60000, 20.e-6, WINDOWTYPE.DECAY)); + simSignalList.add(new ClickSound("Fin 20Hz", 20, 20, 0.5, WINDOWTYPE.HANN)); + simSignalList.add(new ClickSound("Fin 40Hz", 40, 40, 0.5, WINDOWTYPE.HANN)); // simSignalList.add(new LinearChirp(simProcess.getSampleRate(), 3000, 6000, 0.1)); // simSignalList.add(new LinearChirp(simProcess.getSampleRate(), 3000, 8000, .5)); simSignalList.add(new RandomWhistles()); From b916209f4fda92877a5f117356a02d756bc2ed5f Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 24 Jun 2022 15:53:48 +0100 Subject: [PATCH 37/37] Better GoTo options for click events and groupdetections In tables, can now scroll to 10 or 60s before the marked event, which is often more useful than scrolling to the exact start of those events. --- src/PamModel/PamModel.java | 2 +- src/clickDetector/ClickBTDisplay.java | 6 +- src/clickDetector/ClickControl.java | 5 +- src/clickDetector/ClickDisplayManager.java | 5 +- .../offlineFuncs/EventListDialog.java | 22 ++++- .../DetectionGroupControl.java | 3 +- .../dialogs/DetectionGroupTable.java | 91 +++++++++++-------- 7 files changed, 81 insertions(+), 53 deletions(-) diff --git a/src/PamModel/PamModel.java b/src/PamModel/PamModel.java index f0849981..d14cbb02 100644 --- a/src/PamModel/PamModel.java +++ b/src/PamModel/PamModel.java @@ -451,7 +451,7 @@ final public class PamModel implements PamModelInterface, PamSettings { mi = PamModuleInfo.registerControlledUnit(EffortControl.class.getName(), EffortControl.unitType); mi.setToolTipText("Record observer monitoring effort"); mi.setModulesMenuGroup(utilitiesGroup); - mi.setHidden(SMRUEnable.isEnable() == false); +// mi.setHidden(SMRUEnable.isEnable() == false); mi.setToolTipText("Enables an observer to enter their name and infomation about which displays are being monitored"); mi.setMaxNumber(1); diff --git a/src/clickDetector/ClickBTDisplay.java b/src/clickDetector/ClickBTDisplay.java index a8556bdd..07c208d0 100644 --- a/src/clickDetector/ClickBTDisplay.java +++ b/src/clickDetector/ClickBTDisplay.java @@ -3660,15 +3660,15 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett /** * Scroll the display to a specific event. * @param event event to scroll to + * @param beforeTime seconds before the event to scroll to. */ - public void gotoEvent(OfflineEventDataUnit event) { - long evStart = event.getTimeMilliseconds(); + public void gotoEvent(OfflineEventDataUnit event, int beforeTime) { + long evStart = event.getTimeMilliseconds() - beforeTime*1000; if (evStart < hScrollBar.getMinimumMillis() || evStart > hScrollBar.getMaximumMillis()) { long range = hScrollBar.getMaximumMillis() - hScrollBar.getMinimumMillis(); hScrollBar.setRangeMillis(evStart, evStart + range, true); } hScrollBar.setValueMillis(evStart); - } int playbackStatus = PlaybackProgressMonitor.PLAY_END; diff --git a/src/clickDetector/ClickControl.java b/src/clickDetector/ClickControl.java index e5a783b5..e5b6fce9 100644 --- a/src/clickDetector/ClickControl.java +++ b/src/clickDetector/ClickControl.java @@ -1015,9 +1015,10 @@ public class ClickControl extends PamControlledUnit implements PamSettings { /** * Scrolls the display to a specific event. * @param event event to scroll to + * @param beforeTime */ - public void gotoEvent(OfflineEventDataUnit event) { - tabPanelControl.clickDisplayManager.gotoEvent(event); + public void gotoEvent(OfflineEventDataUnit event, int beforeTime) { + tabPanelControl.clickDisplayManager.gotoEvent(event, beforeTime); } /** diff --git a/src/clickDetector/ClickDisplayManager.java b/src/clickDetector/ClickDisplayManager.java index 23237197..f46aeca8 100644 --- a/src/clickDetector/ClickDisplayManager.java +++ b/src/clickDetector/ClickDisplayManager.java @@ -463,11 +463,12 @@ public class ClickDisplayManager implements PamSettings { /** * Scroll the time displays to a specific event. * @param event + * @param beforeTime */ - public void gotoEvent(OfflineEventDataUnit event) { + public void gotoEvent(OfflineEventDataUnit event, int beforeTime) { for (int i = 0; i < windowList.size(); i++) { if (windowList.get(i).getClass() == ClickBTDisplay.class) { - ((ClickBTDisplay)windowList.get(i)).gotoEvent(event); + ((ClickBTDisplay)windowList.get(i)).gotoEvent(event, beforeTime); } } } diff --git a/src/clickDetector/offlineFuncs/EventListDialog.java b/src/clickDetector/offlineFuncs/EventListDialog.java index 75b5c405..b40d6eab 100644 --- a/src/clickDetector/offlineFuncs/EventListDialog.java +++ b/src/clickDetector/offlineFuncs/EventListDialog.java @@ -175,9 +175,19 @@ public class EventListDialog extends PamDialog { JPopupMenu menu = new JPopupMenu(); JMenuItem menuItem; int evNo = event.getDatabaseIndex(); - menuItem = new JMenuItem(String.format("Goto event %d ...", evNo)); - menuItem.addActionListener(new GotoEvent(event)); - menu.add(menuItem); + int[] beforeTimes = {0, 10, 60}; + for (int i = 0; i < beforeTimes.length; i++) { + String title; + if (beforeTimes[i] == 0) { + title = String.format("Goto event %d ...", evNo); + } + else { + title = String.format("Goto %ds before event %d ...", beforeTimes[i], evNo); + } + menuItem = new JMenuItem(title); + menuItem.addActionListener(new GotoEvent(event, beforeTimes[i])); + menu.add(menuItem); + } menuItem = new JMenuItem(String.format("Edit event %d ...", evNo)); menuItem.addActionListener(new EditEvent(event)); menu.add(menuItem); @@ -219,14 +229,16 @@ public class EventListDialog extends PamDialog { private class GotoEvent implements ActionListener { private OfflineEventDataUnit event; + private int beforeTime; - public GotoEvent(OfflineEventDataUnit event) { + public GotoEvent(OfflineEventDataUnit event, int beforeTime) { this.event = event; + this.beforeTime = beforeTime; } @Override public void actionPerformed(ActionEvent arg0) { - clickControl.gotoEvent(event); + clickControl.gotoEvent(event, beforeTime); } } diff --git a/src/detectiongrouplocaliser/DetectionGroupControl.java b/src/detectiongrouplocaliser/DetectionGroupControl.java index e7aa5c3d..4361fb85 100644 --- a/src/detectiongrouplocaliser/DetectionGroupControl.java +++ b/src/detectiongrouplocaliser/DetectionGroupControl.java @@ -24,7 +24,8 @@ import detectiongrouplocaliser.dialogs.DisplayOptionsHandler; import userDisplay.UserDisplayControl; /** - * Class for grouping any type of data together. + * Class for grouping any type of data together. Not really a localiser, but + * does include some localisation options.
* Will attempt to offer a variety of localisation options and will store data for each group * in a pair of database tables. * @author dg50 diff --git a/src/detectiongrouplocaliser/dialogs/DetectionGroupTable.java b/src/detectiongrouplocaliser/dialogs/DetectionGroupTable.java index a111226b..a51709e8 100644 --- a/src/detectiongrouplocaliser/dialogs/DetectionGroupTable.java +++ b/src/detectiongrouplocaliser/dialogs/DetectionGroupTable.java @@ -56,23 +56,23 @@ import warnings.PamWarning; public class DetectionGroupTable extends UserDisplayComponentAdapter implements DetectionGroupObserver { private JPanel mainPanel; - + private DetectionGroupProcess detectionGroupProcess; - + private DetectionGroupDataBlock detectionGroupDataBlock; - + private DetectionGroupControl detectionGroupControl; - + private JTable table; private TableModel tableModel; private JScrollPane scrollPane; - + private JPanel controlPanel; - + private JRadioButton showAll, showCurrent; - + private JButton checkIntegrity; private boolean isViewer; @@ -80,7 +80,7 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements private DisplayOptionsHandler displayOptionsHandler; private SwingTableColumnWidths widthManager; - + public DetectionGroupTable(DetectionGroupProcess detectionGroupProcess) { super(); this.detectionGroupProcess = detectionGroupProcess; @@ -95,7 +95,7 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements mainPanel.add(BorderLayout.CENTER, scrollPane); detectionGroupControl.addGroupObserver(this); table.addMouseListener(new TableMouseHandler()); - + if (isViewer) { displayOptionsHandler = detectionGroupControl.getDisplayOptionsHandler(); controlPanel = new JPanel(new BorderLayout()); @@ -134,9 +134,9 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements } }); } - - -// sortColumnWidths(); + + + // sortColumnWidths(); } @@ -144,7 +144,7 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements public Component getComponent() { return mainPanel; } - + /* (non-Javadoc) * @see userDisplay.UserDisplayComponentAdapter#openComponent() */ @@ -195,7 +195,7 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements showPopupMenu(e); } } - + } @Override @@ -205,7 +205,7 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements public void editSelectedEvent(MouseEvent e) { // TODO Auto-generated method stub - + } @@ -234,26 +234,39 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements menuItem = annotationHandler.createAnnotationEditMenu(dgdu); menuItem.setIcon(menuIcon); pMenu.add(menuItem); - - menuItem = new JMenuItem("Scroll to Group UID " + dgdu.getUID() + " at " + PamCalendar.formatDBDateTime(dgdu.getTimeMilliseconds())); - menuItem.setIcon(menuIcon); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - scrollToEvent(dgdu.getTimeMilliseconds()); + + int[] beforeTimesSecs = {0, 10, 60}; + pMenu.addSeparator(); + for (int i = 0; i < beforeTimesSecs.length; i++) { + int before = beforeTimesSecs[i]; + String title; + if (before == 0) { + title = "Scroll to Group UID " + dgdu.getUID() + " at " + PamCalendar.formatDBDateTime(dgdu.getTimeMilliseconds()); } - }); - pMenu.add(menuItem); + else { + title = String.format("Scroll to %ds before Group UID %d", before, dgdu.getUID()); + } + menuItem = new JMenuItem(title); + // menuItem.setIcon(menuIcon); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + scrollToEvent(dgdu.getTimeMilliseconds()-before*1000); + } + }); + pMenu.add(menuItem); + } + pMenu.show(e.getComponent(), e.getX(), e.getY()); } protected void scrollToEvent(long timeMilliseconds) { // start a it earlier. timeMilliseconds -= 5000; -// now WTF - how do I tell every scroller to go to this point in time ? + // now WTF - how do I tell every scroller to go to this point in time ? AbstractScrollManager scrollManager = AbstractScrollManager.getScrollManager(); scrollManager.startDataAt(detectionGroupDataBlock, timeMilliseconds); - + } @@ -300,9 +313,9 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements TableColumn tableCol = table.getColumnModel().getColumn(i); tableCol.setPreferredWidth(tableModel.getRelativeWidth(i)*50); } - + } - + private int getViewOption() { if (!isViewer) { return DisplayOptionsHandler.SHOW_ALL; @@ -348,7 +361,7 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements @Override public int getRowCount() { -// System.out.println("getRowCount()"); + // System.out.println("getRowCount()"); if (getViewOption() == DisplayOptionsHandler.SHOW_ALL) { firstRowToShow = 0; return numRowsToShow = detectionGroupDataBlock.getUnitsCount(); @@ -396,9 +409,9 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements return null; } case 2: -// if (iRow == 0) { -// System.out.println("getValueAt(0,0)"); -// } + // if (iRow == 0) { + // System.out.println("getValueAt(0,0)"); + // } return dgdu.getUID(); case 3: return PamCalendar.formatDateTime(dgdu.getTimeMilliseconds()); @@ -416,16 +429,16 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements if (annotation != null) { return annotation.toString(); } - + } - + } catch (Exception e) { return null; } return null; } - + @Override public Class getColumnClass(int col) { if (col == 0) { @@ -433,7 +446,7 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements } return super.getColumnClass(col); } - + /* (non-Javadoc) * @see javax.swing.table.AbstractTableModel#fireTableStructureChanged() */ @@ -486,7 +499,7 @@ public class DetectionGroupTable extends UserDisplayComponentAdapter implements widthManager = new SwingTableColumnWidths(uniqueName, table); super.setUniqueName(uniqueName); } - - - + + + }