diff --git a/src/Array/MovingHydrophoneLocator.java b/src/Array/MovingHydrophoneLocator.java index 2c17059f..faf88005 100644 --- a/src/Array/MovingHydrophoneLocator.java +++ b/src/Array/MovingHydrophoneLocator.java @@ -16,5 +16,10 @@ abstract public class MovingHydrophoneLocator extends SimpleHydrophoneLocator { super(pamArray, streamer); } + @Override + public boolean isChangeable() { + return true; + } + } diff --git a/src/PamModel/PamModel.java b/src/PamModel/PamModel.java index 8506197d..ba84ba8b 100644 --- a/src/PamModel/PamModel.java +++ b/src/PamModel/PamModel.java @@ -464,7 +464,7 @@ final public class PamModel implements PamModelInterface, PamSettings { mi = PamModuleInfo.registerControlledUnit(MetaDataContol.class.getName(), MetaDataContol.unitType); - mi.setToolTipText("Deployment Meta Data"); + mi.setToolTipText("Project Meta Data"); mi.setModulesMenuGroup(utilitiesGroup); mi.setMaxNumber(1); diff --git a/src/RightWhaleEdgeDetector/RWEBinaryDataSource.java b/src/RightWhaleEdgeDetector/RWEBinaryDataSource.java index c08a3f68..3b7546c2 100644 --- a/src/RightWhaleEdgeDetector/RWEBinaryDataSource.java +++ b/src/RightWhaleEdgeDetector/RWEBinaryDataSource.java @@ -218,7 +218,7 @@ public class RWEBinaryDataSource extends BinaryDataSource { binaryObjectData.getDataUnitBaseData().setSampleDuration(duration); // rweDataUnit = new RWEDataUnit(aSound.timeMilliseconds, channelMap, // startSample, duration, aSound); - rweDataUnit = new RWEDataUnit(binaryObjectData.getDataUnitBaseData(), aSound); + rweDataUnit = new RWEDataUnit(rweProcess, binaryObjectData.getDataUnitBaseData(), aSound); rweDataUnit.setSequenceBitmap(sequenceMap); double f[] = new double[2]; f[0] = aSound.minFreq * rweDataBlock.getSampleRate()/rweDataBlock.getFftLength(); diff --git a/src/RightWhaleEdgeDetector/RWEDataUnit.java b/src/RightWhaleEdgeDetector/RWEDataUnit.java index 226e0f64..c3144ebf 100644 --- a/src/RightWhaleEdgeDetector/RWEDataUnit.java +++ b/src/RightWhaleEdgeDetector/RWEDataUnit.java @@ -6,26 +6,31 @@ import whistlesAndMoans.AbstractWhistleDataUnit; public class RWEDataUnit extends AbstractWhistleDataUnit { public RWESound rweSound; + private RWEProcess rweProcess; - public RWEDataUnit(long timeMilliseconds, int channelBitmap, + public RWEDataUnit(RWEProcess rweProcess, long timeMilliseconds, int channelBitmap, long startSample, long duration, RWESound rweSound) { super(timeMilliseconds, channelBitmap, startSample, duration); this.rweSound = rweSound; + this.rweProcess = rweProcess; // TODO Auto-generated constructor stub } - public RWEDataUnit(DataUnitBaseData basicData, RWESound rweSound) { + public RWEDataUnit(RWEProcess rweProcess, DataUnitBaseData basicData, RWESound rweSound) { super(basicData); this.rweSound = rweSound; + this.rweProcess = rweProcess; } - double[] freqsHz; @Override public double[] getFreqsHz() { - if (freqsHz == null) { - freqsHz = new double[rweSound.sliceCount]; + double[] f = new double[rweSound.sliceCount]; + RWEDataBlock rweDataBlock = rweProcess.getRweDataBlock(); + double binToHz = rweDataBlock.getSampleRate() / rweDataBlock.getFftLength(); + for (int i = 0; i < f.length; i++) { + f[i] = (double) rweSound.peakFreq[i] * binToHz; } - return null; + return f; } @Override @@ -35,8 +40,16 @@ public class RWEDataUnit extends AbstractWhistleDataUnit { @Override public double[] getTimesInSeconds() { - // TODO Auto-generated method stub - return null; + if (rweSound == null) { + return null; + } + double[] t = new double[rweSound.sliceCount]; + RWEDataBlock rweDataBlock = rweProcess.getRweDataBlock(); + double binToT = rweDataBlock.getFftHop() / rweDataBlock.getSampleRate(); + for (int i = 0; i < t.length; i++) { + t[i] = (double) rweSound.sliceList[i] * binToT; + } + return t; } @Override diff --git a/src/RightWhaleEdgeDetector/RWEProcess.java b/src/RightWhaleEdgeDetector/RWEProcess.java index 07a0584c..e7bf0b23 100644 --- a/src/RightWhaleEdgeDetector/RWEProcess.java +++ b/src/RightWhaleEdgeDetector/RWEProcess.java @@ -42,6 +42,10 @@ public class RWEProcess extends PamProcess { private FFTDataBlock sourceDataBlock; private RWEDataBlock rweDataBlock; + public RWEDataBlock getRweDataBlock() { + return rweDataBlock; + } + private Hashtable bearingLocalisers; private StandardSymbolManager symbolManager; /** @@ -211,7 +215,7 @@ public class RWEProcess extends PamProcess { // System.out.println(String.format("Detected sound type %d on channel %d", // soundType, this.iChannel)); duration = sourceDataBlock.getFftHop() * aSound.duration; - rweDataUnit = new RWEDataUnit(aSound.timeMilliseconds, + rweDataUnit = new RWEDataUnit(RWEProcess.this, aSound.timeMilliseconds, 1< daqInfoDataBlock = daqControl.getAcquisitionProcess().getDaqStatusDataBlock(); - // just load everything. Probably OK for the acqusition, but will bring down + // just load everything. Probably OK for the acqusition, but will bring down daqInfoDataBlock.loadViewerData(0, Long.MAX_VALUE, null); ArrayList allStatusData = daqInfoDataBlock.getDataCopy(); - + ArrayList tempPeriods = null; if (allStatusData == null || allStatusData.size() == 0) { @@ -95,7 +97,7 @@ public class DeploymentHandler { System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); } int nPeriods = tempPeriods.size(); - // now go through those and merge into longer periods where there is no gap between files. + // now go through those and merge into longer periods where there is no gap between files. ListIterator iterator = tempPeriods.listIterator(); RecordingPeriod prevPeriod = null; while (iterator.hasNext()) { @@ -108,7 +110,7 @@ public class DeploymentHandler { long gap = nextPeriod.getRecordStart() - prevPeriod.getRecordStop(); long prevDur = prevPeriod.getRecordStop()-prevPeriod.getRecordStart(); if (gap < 3 || gap < prevDur/50) { - // ignoring up to 3s gap or a sample error < 2%.Dunno if this is sensible or not. + // ignoring up to 3s gap or a sample error < 2%.Dunno if this is sensible or not. prevPeriod.setRecordStop(nextPeriod.getRecordStop()); iterator.remove(); nextPeriod = prevPeriod; @@ -121,16 +123,16 @@ public class DeploymentHandler { DeploymentOverview deploymentOverview = new DeploymentOverview(false, tempPeriods); return deploymentOverview; // find the number of times it started and stopped .... -// System.out.printf("Input map of sound data indicates data from %s to %s with %d starts and %d stops over %d files\n", +// System.out.printf("Input map of sound data indicates data from %s to %s with %d starts and %d stops over %d files\n", // PamCalendar.formatDateTime(dataStart), PamCalendar.formatDateTime(dataEnd), nStart, nStop, nFile+1); - // now work out where there are genuine gaps and make up a revised list of recording periods. + // now work out where there are genuine gaps and make up a revised list of recording periods. + - } - + /** - * Work out whether or not the data are evenly duty cycled by testing the - * distributions of on and off times. + * Work out whether or not the data are evenly duty cycled by testing the + * distributions of on and off times. * @param tempPeriods * @return */ @@ -187,38 +189,38 @@ public class DeploymentHandler { //in each channel public ArrayList getDeployments() { - + DeploymentOverview recordingOverview = createOverview(); - - // first find an acquisition module. + + // first find an acquisition module. PamControlledUnit aModule = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); - if (aModule instanceof AcquisitionControl == false) { - // will return if it's null. Impossible for it to be the wrong type. - // but it's good practice to check anyway before casting. + if (!(aModule instanceof AcquisitionControl)) { + // will return if it's null. Impossible for it to be the wrong type. + // but it's good practice to check anyway before casting. return null; } - // cast it to the right type. + // cast it to the right type. AcquisitionControl daqControl = (AcquisitionControl) aModule; AcquisitionParameters daqParams = daqControl.getAcquisitionParameters(); /** - * The daqParams class has most of what we need about the set up in terms of sample rate, - * number of channels, instrument type, ADC input range (part of calibration), etc. - * It also has a hydrophone list, which maps the input channel numbers to the hydrophon numbers. + * The daqParams class has most of what we need about the set up in terms of sample rate, + * number of channels, instrument type, ADC input range (part of calibration), etc. + * It also has a hydrophone list, which maps the input channel numbers to the hydrophon numbers. * Realistically, this list is always 0,1,2,etc or it goes horribly wrong ! */ - // so write functions here to get information from the daqParams. + // so write functions here to get information from the daqParams. // System.out.printf("Sample regime: %s input with rate %3.1fHz, %d channels, gain %3.1fdB, ADCp-p %3.1fV\n", daqParams.getDaqSystemType(), // daqParams.getSampleRate(), daqParams.getNChannels(), daqParams.preamplifier.getGain(), daqParams.voltsPeak2Peak); /** * then there is the actual sampling. This is a bit harder to find. I thought it would be in the data map - * but the datamap is a simple count of what's in the databasase which is not quite what we want. - * we're going to have to query the database to get more detailed informatoin I think. - * I'll do that here for now, but we may want to move this when we better organise the code. + * but the datamap is a simple count of what's in the databasase which is not quite what we want. + * we're going to have to query the database to get more detailed informatoin I think. + * I'll do that here for now, but we may want to move this when we better organise the code. * It also seems that there are 'bad' dates in the database when it starts new files, which are the date - * data were analysed at. So we really need to check the start and stop records only. + * data were analysed at. So we really need to check the start and stop records only. */ PamDataBlock daqInfoDataBlock = daqControl.getAcquisitionProcess().getDaqStatusDataBlock(); - // just load everything. Probably OK for the acqusition, but will bring down + // just load everything. Probably OK for the acqusition, but will bring down daqInfoDataBlock.loadViewerData(0, Long.MAX_VALUE, null); ArrayList allStatusData = daqInfoDataBlock.getDataCopy(); long dataStart = Long.MAX_VALUE; @@ -241,24 +243,24 @@ public class DeploymentHandler { break; } } - -// System.out.printf("Input map of sound data indicates data from %s to %s with %d starts and %d stops over %d files\n", + +// System.out.printf("Input map of sound data indicates data from %s to %s with %d starts and %d stops over %d files\n", // PamCalendar.formatDateTime(dataStart), PamCalendar.formatDateTime(dataEnd), nStart, nStop, nFile+1); - + } - + // // and we find the datamap within that ... // OfflineDataMap daqMap = daqInfoDataBlock.getOfflineDataMap(DBControlUnit.findDatabaseControl()); // if (daqMap != null) { -// // iterate through it. +// // iterate through it. // long dataStart = daqMap.getFirstDataTime(); // long dataEnd = daqMap.getLastDataTime(); // List mapPoints = daqMap.getMapPoints(); -// System.out.printf("Input map of sound data indicates data from %s to %s with %d individual files\n", +// System.out.printf("Input map of sound data indicates data from %s to %s with %d individual files\n", // PamCalendar.formatDateTime(dataStart), PamCalendar.formatDateTime(dataEnd), mapPoints.size()); // /* // * clearly in the first database I've been looking at of Tinas data, this is NOT getting sensible start and -// * end times. Print them out to see what's going on. +// * end times. Print them out to see what's going on. // */ //// for () // } @@ -267,18 +269,18 @@ public class DeploymentHandler { DeploymentRecoveryDetails recovery = new DeploymentRecoveryDetails(); pair.deploymentDetails = deployment; pair.recoveryDetails = recovery; - + deployment.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataStart)); deployment.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataStart)); recovery.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataEnd)); recovery.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataEnd)); - + ArrayList drPairs = new ArrayList<>(); drPairs.add(pair); return drPairs; - + } - + public Deployment createDeploymentDocument(int i, DeploymentRecoveryPair drd) { Deployment deployment = new Deployment(); String id = String.format("%d", i); @@ -286,25 +288,79 @@ public class DeploymentHandler { deployment.setDeploymentId(i); deployment.setDeploymentDetails(drd.deploymentDetails); deployment.setRecoveryDetails(drd.recoveryDetails); - + TethysLocationFuncs.getTrackAndPositionData(deployment); - + + getProjectData(deployment); + getSamplingDetails(deployment); - + getSensorDetails(deployment); - + /** * Stuff that may need to be put into the UI: - * Audio: can easily get current loc of raw and binary data, but may need to override these. I think - * this may be for the export UI ? - * Tracks: trackline information. General problem in PAMGUard. + * Audio: can easily get current loc of raw and binary data, but may need to override these. I think + * this may be for the export UI ? + * Tracks: trackline information. General problem in PAMGUard. */ - - - + + + return deployment; } + /** + * Add project Metadata to a Deploymnet document. This is currently being + * made available in the MetaDataControl module which should be added to PAMGuard + * as well as the Tethys output module. + * @param deployment + */ + private boolean getProjectData(Deployment deployment) { + PamControlledUnit aUnit = PamController.getInstance().findControlledUnit(MetaDataContol.class, null); + if (aUnit instanceof MetaDataContol == false) { + return false; + } + MetaDataContol metaControl = (MetaDataContol) aUnit; + DeploymentData deploymentData = metaControl.getDeploymentData(); + deployment.setProject(deploymentData.getProject()); + deployment.setDeploymentAlias(deploymentData.getDeploymentAlias()); + deployment.setSite(deploymentData.getSite()); + deployment.setCruise(deploymentData.getCruise()); + deployment.setPlatform(deploymentData.getPlatform()); + deployment.setRegion(deploymentData.getRegion()); + Instrument instrument = new Instrument(); + instrument.setType(deploymentData.getInstrumentType()); + instrument.setInstrumentId(deploymentData.getInstrumentId()); + // get the geometry type from the array manager. + String geomType = getGeometryType(); + instrument.setGeometryType(geomType); + deployment.setInstrument(instrument); + return true; + } + + /** + * Get a geometry type string for Tethys based on information in the array manager. + * @return + */ + private String getGeometryType() { + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + int nStreamer = array.getStreamerCount(); + for (int i = 0; i < nStreamer; i++) { + Streamer streamer = array.getStreamer(i); + HydrophoneLocator locator = streamer.getHydrophoneLocator(); + if (locator == null) { + continue; + } + if (locator instanceof ThreadingHydrophoneLocator) { + return "cabled"; + } + else { + return "rigid"; + } + } + return "unknown"; + } + private boolean getSensorDetails(Deployment deployment) { PamArray array = ArrayManager.getArrayManager().getCurrentArray(); Sensors sensors = new Sensors(); @@ -322,7 +378,7 @@ public class DeploymentHandler { PamVector hydLocs = array.getAbsHydrophoneVector(iPhone, timeMillis); Audio audio = new Audio(); audio.setNumber(BigInteger.valueOf(iPhone)); - audio.setSensorId(String.format("Hydrophone %d", iPhone)); // shold replace with serial number if it exists. + audio.setSensorId(String.format("Hydrophone %d", iPhone)); // shold replace with serial number if it exists. GeometryTypeM geom = new GeometryTypeM(); geom.setXM(hydLocs.getCoordinate(0)); geom.setYM(hydLocs.getCoordinate(1)); @@ -342,7 +398,7 @@ public class DeploymentHandler { // e.printStackTrace(); // } /** - * Need to be able to add the values from hydLocs to the geometry object, but can't. + * Need to be able to add the values from hydLocs to the geometry object, but can't. */ audioList.add(audio); iPhone++; @@ -354,18 +410,18 @@ public class DeploymentHandler { // // TODO Auto-generated catch block // e.printStackTrace(); // } - deployment.setSensors(sensors); + deployment.setSensors(sensors); return true; } /** - * Fill in the sampling details in a Deployment document. + * Fill in the sampling details in a Deployment document. * @param deployment */ private boolean getSamplingDetails(Deployment deployment) { SamplingDetails samplingDetails = new SamplingDetails(); // this is basically going to be a list of almost identical channel information - // currently just for the first acquisition. May extend to more. + // currently just for the first acquisition. May extend to more. AcquisitionControl daq = (AcquisitionControl) PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); if (daq == null) { return false; @@ -377,15 +433,15 @@ public class DeploymentHandler { int[] hydroMap = daqParams.getHydrophoneList(); int[] inputMap = daqParams.getHardwareChannelList(); double vp2p = daqParams.getVoltsPeak2Peak(); - + List channelInfos = samplingDetails.getChannel(); for (int i = 0; i < nChan; i++) { ChannelInfo channelInfo = new ChannelInfo(); channelInfo.setStart(deployment.getDeploymentDetails().getAudioTimeStamp()); channelInfo.setEnd(deployment.getRecoveryDetails().getAudioTimeStamp()); - + BigIntegerConverter biCon = new BigIntegerConverter(); - BigInteger chanNum = BigInteger.valueOf((long) i); + BigInteger chanNum = BigInteger.valueOf(i); channelInfo.setChannelNumber(chanNum); if (hydroMap != null) { channelInfo.setSensorNumber(hydroMap[i]); @@ -402,7 +458,7 @@ public class DeploymentHandler { nilus.ChannelInfo.Gain.Regimen aGain = new nilus.ChannelInfo.Gain.Regimen(); aGain.setGainDB(daqParams.getPreamplifier().getGain()); channelInfo.setGain(gain); - + Sampling sampling = new Sampling(); List regimens = sampling.getRegimen(); Sampling.Regimen regimen = new Sampling.Regimen(); @@ -412,14 +468,14 @@ public class DeploymentHandler { regimen.setSampleBits(system.getSampleBits()); } regimens.add(regimen); - + channelInfo.setSampling(sampling); - + channelInfos.add(channelInfo); - + /** - * Need something about duty cycling. this is probably something that will have to be added - * earlier to a wrapper around the Deployment class. + * Need something about duty cycling. this is probably something that will have to be added + * earlier to a wrapper around the Deployment class. */ } deployment.setSamplingDetails(samplingDetails); diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 48f0f36c..3f752d55 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -13,9 +13,12 @@ import PamController.PamSettings; import PamController.PamguardVersionInfo; import PamController.settings.output.xml.PamguardXMLWriter; import PamUtils.XMLUtils; +import PamguardMVC.DataUnitBaseData; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.PamProcess; +import PamguardMVC.TFContourData; +import PamguardMVC.TFContourProvider; import generalDatabase.DBSchemaWriter; import generalDatabase.SQLLogging; import nilus.AlgorithmType; @@ -27,6 +30,7 @@ import nilus.SpeciesIDType; import tethys.TethysTimeFuncs; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; +import whistleClassifier.WhistleContour; /** * Automatically provides Tethys data based on the SQL database interface @@ -182,9 +186,64 @@ public class AutoTethysProvider implements TethysDataProvider { */ detection.setChannel(BigInteger.valueOf(dataUnit.getChannelBitmap())); + nilus.Detection.Parameters detParams = new nilus.Detection.Parameters(); + detection.setParameters(detParams); + double[] freqs = dataUnit.getFrequency(); + if (freqs != null) { + detParams.setMinFreqHz(freqs[0]); + detParams.setMaxFreqHz(freqs[1]); + } + double ampli = dataUnit.getAmplitudeDB(); + detParams.setReceivedLevelDB(ampli); +// DataUnitBaseData basicData = dataUnit.getBasicData(); + gotTonalContour(dataUnit, detParams); + return detection; } + /** + * Get tonal sounds contour. Sadly there are two slightly different interfaces in use + * in PAMGuard, so try them both. + * @param detParams + * @return true if a contour was added + */ + private boolean gotTonalContour(PamDataUnit dataUnit, nilus.Detection.Parameters detParams) { + if (dataUnit instanceof TFContourProvider) { + TFContourProvider tfcp = (TFContourProvider) dataUnit; + TFContourData cd = tfcp.getTFContourData(); + if (cd != null) { + long[] tMillis = cd.getContourTimes(); + double[] fHz = cd.getRidgeFrequency(); + nilus.Detection.Parameters.Tonal tonal = new nilus.Detection.Parameters.Tonal(); + List offsetS = tonal.getOffsetS(); + List hz = tonal.getHz(); + for (int i = 0; i < tMillis.length; i++) { + offsetS.add((double) (tMillis[i]-tMillis[0]) / 1000.); + hz.add(fHz[i]); + } + detParams.setTonal(tonal); + return true; + } + } + if (dataUnit instanceof WhistleContour) { + WhistleContour wc = (WhistleContour) dataUnit; + double[] t = wc.getTimesInSeconds(); + double[] f = wc.getFreqsHz(); + if (t != null && f != null) { + nilus.Detection.Parameters.Tonal tonal = new nilus.Detection.Parameters.Tonal(); + List offsetS = tonal.getOffsetS(); + List hz = tonal.getHz(); + for (int i = 0; i < t.length; i++) { + offsetS.add(t[i]-t[0]); + hz.add(f[i]); + } + detParams.setTonal(tonal); + return true; + } + } + return false; + } + private SpeciesIDType getSpeciesIdType() { SpeciesIDType species = new SpeciesIDType(); // species.s diff --git a/src/whistlesAndMoans/AbstractWhistleDataUnit.java b/src/whistlesAndMoans/AbstractWhistleDataUnit.java index 5029f79d..9ec59f9f 100644 --- a/src/whistlesAndMoans/AbstractWhistleDataUnit.java +++ b/src/whistlesAndMoans/AbstractWhistleDataUnit.java @@ -7,9 +7,8 @@ import PamguardMVC.DataUnitBaseData; import PamguardMVC.PamDataUnit; import PamguardMVC.TFContourProvider;; -public abstract class AbstractWhistleDataUnit - extends PamDataUnit - implements WhistleContour, PamDetection{ +public abstract class AbstractWhistleDataUnit extends PamDataUnit + implements WhistleContour, PamDetection { public AbstractWhistleDataUnit(long timeMilliseconds, int channelBitmap, long startSample, long duration) {