This commit is contained in:
kbolaughlin 2023-03-14 08:38:08 -07:00
commit 8842166f43
5 changed files with 298 additions and 75 deletions

View File

@ -473,8 +473,7 @@ final public class PamModel implements PamModelInterface, PamSettings {
mi.setToolTipText("Interface to Tethys Database");
mi.setModulesMenuGroup(utilitiesGroup);
mi.setMaxNumber(1);
}
}
/*

View File

@ -551,6 +551,71 @@ public abstract class SQLLogging {
// }
return resultSet;
}
/**
* Find the data point which is closest in time to that given, or null
* returning whatever type of data unit this deals with.
* @param timeMillis
* @return
*/
public PamDataUnit findClosestDataPoint(PamConnection con, long timeMillis) {
PamCursor pamCursor = loggingCursorFinder.getCursor(con, pamTableDefinition);
// can't really do any math with the string based dates, so will have to query from
// a few s before the time we want.
PamDataUnit[] beforeNafter = new PamDataUnit[2];
SQLTypes sqlTypes = con.getSqlTypes();
for (int i = 0; i < 2; i++) {
String clause;
if (i == 0) {
clause = String.format("WHERE UTC <= %s ORDER BY UTC DESC", sqlTypes.formatDBDateTimeQueryString(timeMillis));
}
else {
clause = String.format("WHERE UTC >= %s ORDER BY UTC ASC", sqlTypes.formatDBDateTimeQueryString(timeMillis));
}
ResultSet result = pamCursor.openReadOnlyCursor(con, clause);
if (result==null) {
return null;
}
PamTableItem tableItem;
try {
if (result.next()) {
// for (int i = 0; i < pamTableDefinition.getTableItemCount(); i++) {
// tableItem = pamTableDefinition.getTableItem(i);
// tableItem.setValue(result.getObject(i + 1));
// }
// return true;
boolean ok = transferDataFromResult(con.getSqlTypes(), result);
result.close();
beforeNafter[i] = createDataUnit(sqlTypes, lastTime, lastLoadIndex);
}
} catch (SQLException ex) {
ex.printStackTrace();
continue;
}
}
// now pick the closest
if (beforeNafter[0] == null) {
return beforeNafter[1];
}
if (beforeNafter[1] == null) {
return beforeNafter[0];
}
long t1 = timeMillis-beforeNafter[0].getTimeMilliseconds();
long t2 = beforeNafter[1].getTimeMilliseconds()-timeMillis;
if (t1 < t2) {
return beforeNafter[0];
}
else {
return beforeNafter[1];
}
}
/**
* Called when a new database is connected to read the last values back in

View File

@ -0,0 +1,85 @@
package tethys;
import Array.ArrayManager;
import Array.HydrophoneLocator;
import Array.PamArray;
import Array.Streamer;
import GPS.GPSControl;
import GPS.GpsData;
import GPS.GpsDataUnit;
import PamUtils.LatLong;
import PamUtils.PamUtils;
import PamguardMVC.PamDataUnit;
import generalDatabase.DBControlUnit;
import generalDatabase.PamConnection;
import nilus.Deployment;
import nilus.DeploymentRecoveryDetails;
/**
* Function(s) to get location information for Tethys in the required format.
* @author dg50
*
*/
public class TethysLocationFuncs {
/**
* Get everything we need for a deployment document including the track #
* and the deployment / recovery information. Basically this means we
* have to load the GPS data, then potentially filter it. Slight risk this
* may all be too much for memory, but give it a go by loading GPS data for
* the deployment times.
* @param deployment
*/
public static void getTrackAndPositionData(Deployment deployment) {
long start = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp());
long end = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp());
/*
* Need to load data for GPS, Hydrophones and Streamers datablocks for this time period. Can then use
* the snapshot geomentry classes to do the rest from the array manager ?
*/
boolean ok = true;
ok &= addPositionData(deployment.getDeploymentDetails());
ok &= addPositionData(deployment.getRecoveryDetails());
}
/**
* Add position data to DeploymentRecoveryDetails.
* @param drd
* @return
*/
public static boolean addPositionData(DeploymentRecoveryDetails drd) {
long timeMillis = TethysTimeFuncs.millisFromGregorianXML(drd.getAudioTimeStamp());
LatLong pos = getLatLongData(timeMillis);
if (pos == null) {
return false;
}
drd.setLongitude(PamUtils.constrainedAngle(pos.getLongitude(), 360));
drd.setLatitude(pos.getLatitude());
drd.setElevationInstrumentM(pos.getHeight());
drd.setDepthInstrumentM(-pos.getHeight());
return true;
}
public static LatLong getLatLongData(long timeMillis) {
// check the array time.
PamArray array = ArrayManager.getArrayManager().getCurrentArray();
Streamer aStreamer = array.getStreamer(0);
GPSControl gpsControl = GPSControl.getGpsControl();
PamConnection con = DBControlUnit.findConnection();
if (gpsControl != null) {
// check GPS data are loaded for times around this.
GpsDataUnit gpsData = (GpsDataUnit) gpsControl.getGpsDataBlock().getLogging().findClosestDataPoint(con, timeMillis);
if (gpsData != null) {
return gpsData.getGpsData();
}
}
HydrophoneLocator hydrophoneLocator = aStreamer.getHydrophoneLocator();
if (hydrophoneLocator == null) {
return null;
}
return hydrophoneLocator.getStreamerLatLong(timeMillis);
}
}

View File

@ -0,0 +1,39 @@
package tethys;
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
public class TethysTimeFuncs {
/*
* Copied from http://www.java2s.com/Code/Java/Development-Class/ConvertsagiventimeinmillisecondsintoaXMLGregorianCalendarobject.htm
*/
public static XMLGregorianCalendar xmlGregCalFromMillis(long millis) {
try {
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(millis);
return DatatypeFactory.newInstance().newXMLGregorianCalendar(
calendar);
}
catch (final DatatypeConfigurationException ex) {
System.out.println("Unable to convert date '%s' to an XMLGregorianCalendar object");
return null;
}
}
/**
* Convert a Gregorian calendar value back to milliseconds.
* @param xmlGregorian
* @return
*/
public static Long millisFromGregorianXML(XMLGregorianCalendar xmlGregorian) {
if (xmlGregorian == null) {
return null;
}
GregorianCalendar gc2 = xmlGregorian.toGregorianCalendar();
return gc2.getTimeInMillis();
}
}

View File

@ -37,8 +37,11 @@ import generalDatabase.DBSchemaWriter;
import generalDatabase.SQLLogging;
import metadata.MetaDataContol;
import metadata.deployment.DeploymentData;
import nilus.Deployment;
import nilus.DeploymentRecoveryDetails;
import tethys.TethysControl;
import tethys.TethysLocationFuncs;
import tethys.TethysTimeFuncs;
import tethys.dbxml.DBXMLConnect;
import tethys.pamdata.TethysDataProvider;
import tethys.pamdata.TethysSchema;
@ -145,77 +148,83 @@ public class TethysExporter {
/*
* A load of notes Katie put in ....654654654
*/
// 1. grab DeploymentRecoveryPair that has deployment details and recovery
// details
// a. this is based on start and end times
// Douglas calculates out dutycycles to only grab the
// 2. loop through the pairs to populate the extra information
// one pair is one deployment
// see below for matching
// id => unique
// project => project in pamguard
// deploymentId == id
// deploymentAlias => blank
// site => UI addition in pamguard, not done, can be blank
// siteAlias => blank
// cruise => UI addition, optional
// Platform=> UI addition in pamguard
// region => UI addition
// Instrument/Type => UI, array manager details (hydrophone names area)
// Instrument/Id => UI, array manager details
// Instrument/Geometry => in pamguard array manager
// SamplingDetails/Channel
// ChannelNumber => in pamguard, hyrdrophone array
// SensorNumber => in pamguard,
// Start => same as timestamp deployment detail
// End => same as timestamp recovery detail
// Sampling/Regimen (change sample rate, pamgauard doesnt handle, only on, get
// channel info in that loop)
// TimeStamp => start time
// SampleRate_kHz =>
// SampleBits =>
// Gain (another func call to get gain info)
// DutyCycles => needs to be calculated, not fields in pamguard, have fun
// Douglas
// QualityAssurance => not in pamguard, UI, maybe deployment notes, optional
// Data/Audio (static)
// URI => folder where audio is saved
// Data/Tracks
// Track => GPS datatable (granularity filter)
// TrackId => not unique between deployments,
// TrackEffort
// OnPath => scattered throughout pamguard
// URI => option, check with Shannon on how they are doing deployments
// Sensors/Audio (per hydrophone not quad array) streamer info + individual
// hydrophone data together
// pamguard hydrophone data
// number => hydrophoneId
// sensorId => sensor serial number
// Geometry => array geometry field goes to
// Sensors/Depth
// optional
// Sensors/Sensor
// Number => hydrophoneId in pamguard
// SensorId => addition to UI
// Geometry => array geometry fields
// Type => Hydrophone Type
// get list of deployment recovery details (start, stop times and lat/long)
// deployment details and recovery details are same structure
// per pair, go through a loop to fill in each deployment
// ArrayList<DeploymentRecoveryPair> deployRecover = getSamplingDetails();
// if (deployRecover == null) {
// return false;
// }
//
// for (DeploymentRecoveryPair drd : deployRecover) {
//
//
//
//
// }
//1. grab DeploymentRecoveryPair that has deployment details and recovery details
//a. this is based on start and end times
//Douglas calculates out dutycycles to only grab the
//2. loop through the pairs to populate the extra information
//one pair is one deployment
//see below for matching
//id => unique
//project => project in pamguard
//deploymentId == id
//deploymentAlias => blank
//site => UI addition in pamguard, not done, can be blank
//siteAlias => blank
//cruise => UI addition, optional
//Platform=> UI addition in pamguard
//region => UI addition
//Instrument/Type => UI, array manager details (hydrophone names area)
//Instrument/Id => UI, array manager details
//Instrument/Geometry => in pamguard array manager
//SamplingDetails/Channel
//ChannelNumber => in pamguard, hyrdrophone array
//SensorNumber => in pamguard,
//Start => same as timestamp deployment detail
//End => same as timestamp recovery detail
//Sampling/Regimen (change sample rate, pamgauard doesnt handle, only on, get channel info in that loop)
//TimeStamp => start time
//SampleRate_kHz =>
//SampleBits =>
//Gain (another func call to get gain info)
//DutyCycles => needs to be calculated, not fields in pamguard, have fun Douglas
//QualityAssurance => not in pamguard, UI, maybe deployment notes, optional
//Data/Audio (static)
//URI => folder where audio is saved
//Data/Tracks
//Track => GPS datatable (granularity filter)
//TrackId => not unique between deployments,
//TrackEffort
//OnPath => scattered throughout pamguard
//URI => option, check with Shannon on how they are doing deployments
//Sensors/Audio (per hydrophone not quad array) streamer info + individual hydrophone data together
//pamguard hydrophone data
//number => hydrophoneId
//sensorId => sensor serial number
//Geometry => array geometry field goes to
//Sensors/Depth
//optional
//Sensors/Sensor
//Number => hydrophoneId in pamguard
//SensorId => addition to UI
//Geometry => array geometry fields
//Type => Hydrophone Type
//get list of deployment recovery details (start, stop times and lat/long)
//deployment details and recovery details are same structure
//per pair, go through a loop to fill in each deployment
ArrayList<DeploymentRecoveryPair> deployRecover = getSamplingDetails();
if (deployRecover == null) {
return false;
}
/*
* This will become the main loop over deployment documents
*/
int i = 0;
for (DeploymentRecoveryPair drd : deployRecover) {
Deployment deployment = createDeploymentDocument(i++, drd);
}
/*
* Call some general export function
@ -241,6 +250,18 @@ public class TethysExporter {
return true;
}
private Deployment createDeploymentDocument(int i, DeploymentRecoveryPair drd) {
Deployment deployment = new Deployment();
deployment.setDeploymentDetails(drd.deploymentDetails);
deployment.setRecoveryDetails(drd.recoveryDetails);
TethysLocationFuncs.getTrackAndPositionData(deployment);
return deployment;
}
/**
* find Deployment data. This is stored in a separate PAMGuard module, which may
@ -348,9 +369,9 @@ public class TethysExporter {
// just load everything. Probably OK for the acqusition, but will bring down
daqInfoDataBlock.loadViewerData(0, Long.MAX_VALUE, null);
ArrayList<DaqStatusDataUnit> allStatusData = daqInfoDataBlock.getDataCopy();
long dataStart = Long.MAX_VALUE;
long dataEnd = Long.MIN_VALUE;
if (allStatusData != null && allStatusData.size() > 0) {
long dataStart = Long.MAX_VALUE;
long dataEnd = Long.MIN_VALUE;
// find the number of times it started and stopped ....
int nStart = 0, nStop = 0, nFile = 0;
for (DaqStatusDataUnit daqStatus : allStatusData) {
@ -392,8 +413,22 @@ public class TethysExporter {
//// for ()
// }
return null;
DeploymentRecoveryPair pair = new DeploymentRecoveryPair();
DeploymentRecoveryDetails deployment = new DeploymentRecoveryDetails();
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<DeploymentRecoveryPair> drPairs = new ArrayList<>();
drPairs.add(pair);
return drPairs;
}
/**