Merge branch 'main' into main

This commit is contained in:
Douglas Gillespie 2024-05-30 11:14:11 +01:00 committed by GitHub
commit c607f17449
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
567 changed files with 37119 additions and 7625 deletions

View File

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

70
.gitignore vendored
View File

@ -41,3 +41,73 @@ settings.xml
.classpath
.classpath
.classpath
.metadata/version.ini
.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml
.metadata/.plugins/org.eclipse.ui.intro/introstate
.metadata/.plugins/org.eclipse.tips.ide/dialog_settings.xml
.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup
.metadata/.plugins/org.eclipse.m2e.logback/logback.2.1.100.20230106-1511.xml
.metadata/.plugins/org.eclipse.m2e.core/workspaceState.ser
.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml
.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml
.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml
.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat
.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache
.metadata/.plugins/org.eclipse.jdt.core/javaLikeNames.txt
.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache
.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache
.metadata/.plugins/org.eclipse.egit.core/.org.eclipse.egit.core.cmp/.settings/org.eclipse.core.resources.prefs
.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs
.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources
.metadata/.plugins/org.eclipse.core.resources/.root/1.tree
.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version
.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index
.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version
.metadata/.plugins/org.eclipse.core.resources/.projects/.org.eclipse.egit.core.cmp/.location
.metadata/.lock
.metadata/version.ini
.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml
.metadata/.plugins/org.eclipse.ui.intro/introstate
.metadata/.plugins/org.eclipse.tips.ide/dialog_settings.xml
.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup
.metadata/.plugins/org.eclipse.m2e.logback/logback.2.1.100.20230106-1511.xml
.metadata/.plugins/org.eclipse.m2e.core/workspaceState.ser
.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml
.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml
.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml
.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat
.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache
.metadata/.plugins/org.eclipse.jdt.core/javaLikeNames.txt
.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache
.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache
.metadata/.plugins/org.eclipse.egit.core/.org.eclipse.egit.core.cmp/.settings/org.eclipse.core.resources.prefs
.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs
.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs
.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources
.metadata/.plugins/org.eclipse.core.resources/.root/1.tree
.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version
.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index
.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version
.settings/org.eclipse.jdt.core.prefs
.settings/org.eclipse.core.resources.prefs
.classpath
.classpath
.project
.classpath
.classpath
.classpath
.classpath
.classpath
.classpath
.settings/org.eclipse.jdt.core.prefs

0
.metadata/.lock Normal file
View File

View File

@ -0,0 +1,3 @@
eclipse.preferences.version=1
encoding=UTF-8
version=1

View File

@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.ui.formatterprofiles.version=23
spelling_locale=en_GB
spelling_locale_initialized=true
typefilter_migrated_2=true
useAnnotationsPrefPage=true
useQuickDiffPrefPage=true

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
org.eclipse.m2e.discovery.pref.projects=

View File

@ -0,0 +1,4 @@
eclipse.preferences.version=1
platformState=1678968029917
quickStart=false
tipsAndTricks=true

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
showIntro=false

View File

@ -0,0 +1,12 @@
//org.eclipse.ui.commands/state/org.eclipse.ui.navigator.resources.nested.changeProjectPresentation/org.eclipse.ui.commands.radioState=false
PLUGINS_NOT_ACTIVATED_ON_STARTUP=;org.eclipse.m2e.discovery;
eclipse.preferences.version=1
org.eclipse.ui.workbench.ACTIVE_NOFOCUS_TAB_BG_END=41,41,41
org.eclipse.ui.workbench.ACTIVE_NOFOCUS_TAB_BG_START=43,44,45
org.eclipse.ui.workbench.ACTIVE_NOFOCUS_TAB_TEXT_COLOR=204,204,204
org.eclipse.ui.workbench.ACTIVE_TAB_BG_END=41,41,41
org.eclipse.ui.workbench.ACTIVE_TAB_BG_START=43,44,45
org.eclipse.ui.workbench.ACTIVE_TAB_TEXT_COLOR=221,221,221
org.eclipse.ui.workbench.INACTIVE_TAB_BG_END=49,53,56
org.eclipse.ui.workbench.INACTIVE_TAB_BG_START=59,64,66
org.eclipse.ui.workbench.INACTIVE_TAB_TEXT_COLOR=187,187,187

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@ -0,0 +1 @@
java

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<typeInfoHistroy/>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<qualifiedTypeNameHistroy/>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<section name="Workbench">
<section name="org.eclipse.jdt.internal.ui.packageview.PackageExplorerPart">
<item key="group_libraries" value="true"/>
<item key="layout" value="2"/>
<item key="rootMode" value="1"/>
<item key="linkWithEditor" value="false"/>
<item key="memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#x0A;&lt;packageExplorer group_libraries=&quot;1&quot; layout=&quot;2&quot; linkWithEditor=&quot;0&quot; rootMode=&quot;1&quot; workingSetName=&quot;Aggregate for window 1678968099026&quot;&gt;&#x0A;&lt;customFilters userDefinedPatternsEnabled=&quot;false&quot;&gt;&#x0A;&lt;xmlDefinedFilters&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.StaticsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.buildship.ui.packageexplorer.filter.gradle.buildfolder&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonJavaProjectsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer_patternFilterId_.*&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonSharedProjectsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.SyntheticMembersFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.ContainedLibraryFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.internal.ui.PackageExplorer.HideInnerClassFilesFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.internal.ui.PackageExplorer.EmptyInnerPackageFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.m2e.MavenModuleFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.buildship.ui.packageexplorer.filter.gradle.subProject&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.ClosedProjectsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.DeprecatedMembersFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.EmptyLibraryContainerFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.PackageDeclarationFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.ImportDeclarationFilter&quot; isEnabled=&quot;true&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonJavaElementFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.LibraryFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.CuAndClassFileFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.internal.ui.PackageExplorer.EmptyPackageFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.NonPublicFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.LocalTypesFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;child filterId=&quot;org.eclipse.jdt.ui.PackageExplorer.FieldsFilter&quot; isEnabled=&quot;false&quot;/&gt;&#x0A;&lt;/xmlDefinedFilters&gt;&#x0A;&lt;/customFilters&gt;&#x0A;&lt;/packageExplorer&gt;"/>
</section>
</section>

View File

@ -0,0 +1,41 @@
<configuration scan="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%date [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>OFF</level> <!-- change to DEBUG to mimic '-consolelog' behaviour -->
</filter>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${org.eclipse.m2e.log.dir}/0.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<FileNamePattern>${org.eclipse.m2e.log.dir}/%i.log</FileNamePattern>
<MinIndex>1</MinIndex>
<MaxIndex>10</MaxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%date [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<appender name="EclipseLog" class="org.eclipse.m2e.logback.appender.EclipseLogAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
</appender>
<appender name="MavenConsoleLog" class="org.eclipse.m2e.logback.appender.MavenConsoleAppender">
</appender>
<root level="INFO">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" />
<appender-ref ref="EclipseLog" />
<appender-ref ref="MavenConsoleLog" />
</root>
</configuration>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<setup:Workspace
xmi:version="2.0"
xmlns:xmi="http://www.omg.org/XMI"
xmlns:setup="http://www.eclipse.org/oomph/setup/1.0"
name="workspace"/>

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<section name="Workbench">
</section>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<state reopen="false"/>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<workingSetManager>
<workingSet editPageId="org.eclipse.jdt.internal.ui.DynamicSourcesWorkingSet" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1678968098501_0" label="Java Main Sources" name="Java Main Sources"/>
<workingSet editPageId="org.eclipse.jdt.internal.ui.DynamicSourcesWorkingSet" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1678968098508_1" label="Java Test Sources" name="Java Test Sources"/>
<workingSet aggregate="true" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1678968099026_2" label="Window Working Set" name="Aggregate for window 1678968099026"/>
</workingSetManager>

3
.metadata/version.ini Normal file
View File

@ -0,0 +1,3 @@
#Thu Mar 16 12:01:36 GMT 2023
org.eclipse.core.runtime=2
org.eclipse.platform=4.27.0.v20230302-0300

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>PamGuard Main Tethys</name>
<name>PAMGuard</name>
<comment></comment>
<projects>
</projects>

View File

@ -1,4 +1,5 @@
eclipse.preferences.version=1
encoding//src/rawDeepLearningClassifer/segmenter/SegmenterProcess.java=UTF-8
encoding//src/test/resources=UTF-8
encoding/<project>=UTF-8
encoding/src=UTF-8

View File

@ -1,9 +1,9 @@
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.targetPlatform=17
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.compliance=17
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@ -13,4 +13,4 @@ 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=11
org.eclipse.jdt.core.compiler.source=17

View File

@ -3,16 +3,17 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.pamguard</groupId>
<artifactId>Pamguard</artifactId>
<name>Pamguard Java12+</name>
<version>2.02.10bd</version>
<description>Pamguard for Java 12+, using Maven to control dependcies</description>
<name>Pamguard</name>
<version>2.02.11d</version>
<description>Pamguard using Maven to control dependencies</description>
<url>www.pamguard.org</url>
<organization>
<name>Sea Mammal Research Unit, University of St. Andrews</name>
<url>http://www.smru.st-andrews.ac.uk</url>
</organization>
<build>
<sourceDirectory>src</sourceDirectory>
<sourceDirectory>${basedir}/src</sourceDirectory>
<testSourceDirectory>${basedir}/src/test</testSourceDirectory>
<resources>
<resource>
<directory>src</directory>
@ -54,7 +55,7 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<version>3.12.1</version>
<dependencies>
<dependency>
<groupId>org.eclipse.tycho</groupId>
@ -63,22 +64,14 @@
</dependency>
</dependencies>
<configuration>
<release>11</release>
<release>21</release>
<compilerId>jdt</compilerId>
<compilerArguments>
<properties>.settings/org.eclipse.jdt.core.prefs</properties>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.6</version>
<configuration>
<source>17</source>
<target>17</target>
<release>17</release>
</configuration>
<version>0.0.8</version>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
@ -165,11 +158,8 @@
<url>https://artifacts.unidata.ucar.edu/repository/unidata-all/</url>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>bedatadriven</id>
<name>bedatadriven_renjin</name>
<name>bedatadriven public repo</name>
<url>https://nexus.bedatadriven.com/content/groups/public/</url>
</repository>
<repository>
@ -198,7 +188,7 @@
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javafx.version>16</javafx.version>
<javafx.version>21</javafx.version>
<jaxb.runtime.version>2.4.0-b180830.0438</jaxb.runtime.version>
<jaxb.xjc.version>2.4.0-b180830.0438</jaxb.xjc.version>
<jaxb.api.version>2.4.0-b180830.0359</jaxb.api.version>

Binary file not shown.

Binary file not shown.

Binary file not shown.

1536
pom.xml

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,73 @@
This is the main code repository for the PAMGuard software.
# PAMGuard
PAMGuard is a bioacoustics analysis program designed for use in real time research contexts and for the processing of large datasets. PAMGuard provides users access to a suite of state-of-the-art auotmated analysis algorithms alongside displays for visualisation data and a comprehensive data management systems.
This repository was created on 7 January 2022 from sourceforge SVN repository at https://sourceforge.net/p/pamguard/svn/HEAD/tree/ revision r6278.
# Why do we need PAMGuard?
PAMGuard fufills two main requirements within marine bioacoustics
If you are a PAMGuard developer, you should clone and branch this repository and share with any collaborators in your own workspace. When your work is ready, contact the PAMGuard team to have your changes merged back into this repo.
1) **Real time operation** - Almost all PAMGuard features and modules work in real time - this allows scientists and industry to detect, classify and loclaise animals in real time on a standard consumer laptop, enabling mitigations and research survey without expensive bespoke software solutions and the transparncy of open source software.
2) **Processing and visuslisation of large datasets** -
## Installation
PAMGuard is available on Windows and can be downloaded from the [PAMGuard website](www.pamguard.org). Note that we are considering MacOS installers but they are not available at this time.
## Tutorial
PAMGuard is a modular program with two modes; real-time and viewer. Typically a user will start with real-time model, either in the field collecting data or post processing sound files from a recorder. Once data are processed, users move on to viewer mode where data can be explored and further processed.
Upon opening PAMGuard for the first time you are greeted with a blank screen. You must add a series of modules to create the desired acosutic workflow. For example if processing sound files then first add the Sound Acquisition module **_File->Add Modules->Sound Processing->Sound Acquisition_**. Then add the desired detection algorothms e.g. **_File->Add Modules->Detector->Click Detectors_**. Some modules (such as the click detector) have their own displays, others are added to more generalised displays. For example, the whislte and moan detector module shows detections on a spectrgram display. First add a new tab using **_File->Add Modules->Displays->User Display**. Click on the user display tab and then from the top menu select **_User display-> New Spectrgram_**. Right click on the added spectrgram and select whistle and moan contours to show whistle detections overlaid on the raw spectrgram.
Make sure to add the database and binary file storage modules **_File->Add Modules->Utilities->..._**) to save data then press the run button (red button) and data will process. PAMGuard can handle huge datasets so runing might take hours or even days. Progress is shown on the bottom of the screen.
## Features
### Hardware integration
PAMGuard connects with hardware such as various GPS and AIS systems and a multitude of different sound cards (e.g. [National Instruments](www.ni.com) devices, [SAIL DAQ cards](www.smruconsulting.com/contact-us), almost all ASIO sound cards and standard computer sound cards) for real time data collection and processing. PAMGuard also works with some very bespoke hardware such as [DIFAR Sonobuoys]();
### Real time operation
PAMGuard takes advanatge of multi-core processors to run multiple signal processing automatic analysis algorithms in real time to detect whales, dolphins, bats etc. Data are shown in different displayes, including interactive spectrograms and maps. You might be using PAMGuard for simply viewing a spectrgram and making recordings or running deep learning algorithms for multiple species and loclaising the results to view locations on a map. Whatever acosutic workflow a user creates, PAMGuard can run it in real time.
### Support for compressed audio
PAMGuard supports processing audio data from standard files (e.g. wav, aif) and also compressed files (e.g. .flac and .sud). Notew that sud files are created on SoundTraps widely used marine recorders and can be read by PAMGuard without decompressing - PAMGuard will automtically import click detections if present in sud files. PAMGuard also supports importing detection data from CPODs and FPODs.
### Comprehensive data management system
PAMGuard is designed to collect/process data from large acosutic datasets. PAMGuard stores data in an SQLite databases and "Binary" files. The database stores important metadata such as when data has been processed and some low volume data streams such as GPS. Binary files are not human readbale but efficient to access - PAMGuard stores detection data (e.g. clicks, whistles, noise, etc) in these files. this allows PAMGuard to rapidly access data from large datasets. Data from binary files can be viewed in PAMGuard viewer mode or can be exported to MATLAB using the PAMGuard-MATLAB library or the exported to R using the R PAMBinaries package.
### Access to detection and classification algorithms
PAMGuard allows users to inegrate automated detection and classification algorithms directly into their acosutic workflow. There are a multitude of differwent algorothms to choose from, including a basic click detector, whislte and moan detector, GPL detector, click train detectors and many others. The idea behind PAMGuard is allow researchers to access open source state-of-the-art algorithms devleoped within the scientific community - if you want to contribute and get your algorithm into PAMGuard get in touch.
###Localisation
PAMGuard has a mutltude of different options for acoustic loclaisation. There's a comprehesnive beam forming module for beam forming arrays, a large aperture localiser for 3D loclaisation and target motion analysis for towed hydrophone arrays.
###Soundscape analysis
PAMGuard has a noise band (which supports third octave noise bands) and long term spectral average module for soundscape analysis.
### GIS
Almsot all detection data can be visualised on a map. PAMGaurd also supports plotting GPS and AIS data.
### Suite of data visualisation tools
An important aspect of PAMGuard is the ability for users to explore porcessed data. This is
### Advanced manual annotation
The displays within PAMGuard support a variety of manual annottion tools. A simple spectrogram
### Deep learning integration
### Meatadata standard and Tethys compatibility
## Feature roadmap
There's lots of features we would like to add to PAMGuard. If you want to add a feature you can either code it up yourself in Java and submit a pull request or get in touch with us to discuss how to it might be integrated. Some smaller features might be in our roadmap anyway but larger features usually require funding. Some features we are thinking about (but do not necassarily have time for yet) are;
* Support for decidecade noise bands (base 10 filter bank) in noise band monitor to meet Euopean standards
* Capabaility to export data directly from PAMGaurd e.g. as MAT files.
* Automated test suite to make releases more stable. Note that unit and integration tests are also being slowly incorporated.
## Development
This is the main code repository for the PAMGuard software and was created on 7 January 2022 from a [sourceforge SVN repository](https://sourceforge.net/p/pamguard/svn/HEAD/tree/) revision r6278.
If you are a PAMGuard developer, you should clone and branch this repository and share with any collaborators in your own workspace. When your work is ready, contact the PAMGuard team to have your changes merged back into this repo.
PAMGuard uses Maven as build tool.
# Organisation and License
PAMGuard is open source under an MIT license. It is currently primarily managed by the Sea Mammal Research Unit within the [University of St Andrews](https://www.st-andrews.ac.uk/). Please get in touch if you have any questions.

View File

@ -1,9 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<<<<<<<< HEAD:repo/tethys/org/javaclient/3.0/javaclient-3.0.pom
<groupId>tethys.org</groupId>
<artifactId>javaclient</artifactId>
<version>3.0</version>
========
<groupId>pamguard.org</groupId>
<artifactId>x3</artifactId>
<version>2.2.6</version>
>>>>>>>> upstream/main:repo/pamguard/org/x3/2.2.6/x3-2.2.6.pom
<description>POM was created from install:install-file</description>
</project>

View File

@ -2,8 +2,14 @@
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<<<<<<<< HEAD:repo/tethys/org/nilus/3.0/nilus-3.0.pom
<groupId>tethys.org</groupId>
<artifactId>nilus</artifactId>
<version>3.0</version>
========
<groupId>pamguard.org</groupId>
<artifactId>x3</artifactId>
<version>2.2.7</version>
>>>>>>>> upstream/main:repo/pamguard/org/x3/2.2.7/x3-2.2.7.pom
<description>POM was created from install:install-file</description>
</project>

View File

@ -2,8 +2,14 @@
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<<<<<<<< HEAD:repo/tethys/org/javaclient/3.0/javaclient-3.0.pom
<groupId>tethys.org</groupId>
<artifactId>javaclient</artifactId>
<version>3.0</version>
========
<groupId>pamguard.org</groupId>
<artifactId>x3</artifactId>
<version>2.2.6</version>
>>>>>>>> upstream/main:repo/pamguard/org/x3/2.2.6/x3-2.2.6.pom
<description>POM was created from install:install-file</description>
</project>

View File

@ -2,8 +2,14 @@
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<<<<<<<< HEAD:repo/tethys/org/nilus/3.0/nilus-3.0.pom
<groupId>tethys.org</groupId>
<artifactId>nilus</artifactId>
<version>3.0</version>
========
<groupId>pamguard.org</groupId>
<artifactId>x3</artifactId>
<version>2.2.7</version>
>>>>>>>> upstream/main:repo/pamguard/org/x3/2.2.7/x3-2.2.7.pom
<description>POM was created from install:install-file</description>
</project>

View File

@ -429,6 +429,8 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe
// acquisitionDialog.NotifyChange();
if (file.isFile() && !file.isHidden() && acquisitionDialog != null) {
try {
System.out.println("FileInputSystem - interpretNewFile");
AudioInputStream audioStream = PamAudioFileManager.getInstance().getAudioInputStream(file);
// // Get additional information from the header if it's a wav file.
@ -600,6 +602,8 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe
audioStream.close();
}
System.out.println("FileInputSystem - prepareInputFile");
audioStream = PamAudioFileManager.getInstance().getAudioInputStream(currentFile);
if (audioStream instanceof SudAudioInputStream) {

View File

@ -1,6 +1,7 @@
package Acquisition.filedate;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
@ -15,7 +16,11 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.kordamp.ikonli.materialdesign2.MaterialDesignC;
import org.kordamp.ikonli.swing.FontIcon;
import PamUtils.PamCalendar;
import PamView.component.PamSettingsIconButton;
import PamView.dialog.PamGridBagContraints;
/**
@ -35,7 +40,9 @@ public class FileDateDialogStrip {
private JButton settingsButton;
private ImageIcon settingsIcon = new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png"));
// private ImageIcon settingsIcon = new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png"));
public static FontIcon settingsIcon = FontIcon.of(PamSettingsIconButton.SETTINGS_IKON, PamSettingsIconButton.NORMAL_SIZE, Color.DARK_GRAY);
private Window parent;

View File

@ -135,6 +135,8 @@ public class StandardFileDate implements FileDate, PamSettings {
@Override
public long getTimeFromFile(File file) {
// System.out.println("Get time from file: getTimeFromFile" );
// if the user wants to force the local PC time, return immediately
if (settings.isForcePCTime()) return 0;

View File

@ -290,7 +290,7 @@ public class AcquisitionPaneFX extends SettingsPane<AcquisitionParameters>{
//custom pane for each aquisition system.
systemPane=new PamBorderPane();
offlineDAQPaneFX= new OfflineDAQPane(acquisitionControl, this);
offlineDAQPaneFX= new OfflineDAQPane(acquisitionControl);
//the main pane is for reference only in viewer mode.
Pane samplingPane=createSamplingPane();

View File

@ -147,7 +147,12 @@ public class CheckWavHeadersPane extends PamBorderPane {
else {
folderName.setText(folderInputSystem.getCurrentFolder());
}
folder = new File(folderInputSystem.getCurrentFolder());
if (folderInputSystem.getCurrentFolder()!=null){
folder = new File(folderInputSystem.getCurrentFolder());
}
else folder = null;
textArea.setText(" ");
allFiles.clear();
nFiles = countFiles(folder);
@ -159,6 +164,7 @@ public class CheckWavHeadersPane extends PamBorderPane {
private int countFiles(File folder) {
if (folder == null) return 0;
int nF = 0;
File[] files = folder.listFiles(new PamAudioFileFilter());
if (files == null) return 0;

View File

@ -215,7 +215,7 @@ public class FileDataDialogStripFX extends PamBorderPane {
popOver.show(advSettingsButton);
((Parent) popOver.getSkin().getNode()).getStylesheets()
.add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS());
.addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS());
advDatePane.setParams();
}

View File

@ -193,20 +193,18 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
//TODO
});
browseFileButton.setGraphic(Glyph.create("FontAwesome|FILES_ALT").
size(PamGuiManagerFX.iconSize).color(Color.WHITE.darker()));
browseFileButton.setGraphic(PamGlyphDude.createPamIcon("mdi2f-file-multiple", PamGuiManagerFX.iconSize));
browseFileButton.prefHeightProperty().bind(fileSelectBox.heightProperty()); //make browse button same height as combo box.
browseFileButton.setMinWidth(35);
browseFileButton.setMinWidth(40);
browseFileButton.setOnAction( (action) ->{
selectFolder(false);
});
browseFileButton.setTooltip(new Tooltip("Select a folder of files"));
browseFolderButton.setGraphic(Glyph.create("FontAwesome|FOLDER").
size(PamGuiManagerFX.iconSize).color(Color.WHITE.darker()));
browseFolderButton.setGraphic(PamGlyphDude.createPamIcon("mdi2f-folder", PamGuiManagerFX.iconSize));
browseFolderButton.prefHeightProperty().bind(fileSelectBox.heightProperty()); //make browse button same height as combo box.
browseFolderButton.setMinWidth(35);
browseFolderButton.setMinWidth(40);
browseFolderButton.setOnAction( (action) ->{
selectFolder(true);
});

View File

@ -30,8 +30,7 @@ public class OfflineDAQPane extends SettingsPane<OfflineFileParameters>{
private PamBorderPane mainPane;
public OfflineDAQPane(OfflineFileDataStore acquisitionControl,
AcquisitionPaneFX acquisitionPaneFX){
public OfflineDAQPane(OfflineFileDataStore acquisitionControl){
super(null);
this.mainPane= new PamBorderPane();
mainPane.setCenter(createOfflinePane());

View File

@ -135,7 +135,7 @@ public class PamAudioFileManager {
}
if (stream == null) {
System.err.println("PamAudioFileManager: unable to open an AudioStream for " + file.getName());
System.err.println("PamAudioFileManager: unable to open an AudioStream for " + file.getName() + " size: " + file.length());
}
return stream;
@ -153,7 +153,7 @@ public class PamAudioFileManager {
}
/**
* Get the audio file filter
* Get the audio file filter.
*
* @return the audio file filter.
*/
@ -164,7 +164,7 @@ public class PamAudioFileManager {
/**
* Get the current audio file
*
* @return a list oif the current audio loaders.
* @return a list of the current audio loaders.
*/
public ArrayList<PamAudioFileLoader> getAudioFileLoaders() {
return this.pamAudioFileTypes;

View File

@ -254,7 +254,7 @@ public class WavAudioFile implements PamAudioFileLoader {
@Override
public AudioInputStream getAudioStream(File soundFile) {
if (soundFile.exists() == false) return null;
if (soundFile.exists() == false || soundFile.length()<44) return null;
if (soundFile != null && isSoundFile(soundFile)) {
try {
return WavFileInputStream.openInputStream(soundFile);
@ -262,7 +262,7 @@ public class WavAudioFile implements PamAudioFileLoader {
catch (UnsupportedAudioFileException | IOException e) {
e.printStackTrace();
// don't do anything and it will try the built in Audiosystem
System.err.println("Could not open wav file: trying default audio stream: " + soundFile.getName());
System.err.println("Could not open wav file: trying default audio stream: " + soundFile.getName() + " " + soundFile.length());
}
}
try {
@ -276,9 +276,24 @@ public class WavAudioFile implements PamAudioFileLoader {
}
public boolean isSoundFile(File soundFile) {
public static boolean isSoundFile(File soundFile) {
String extension = FileUtils.getExtension(soundFile.getName());
return (extension.equals(".wav"));
//2023-03-12 - for some reason this was .wav
return (extension.equals("wav"));
}
public static void main(String args[]) {
File wavFile = new File("E:\\SoundNet\\1chan_analysis\\pamguard\\67150826\\mf_wav\\20180529\\PAM_20180529_055114_000.wav");
try {
WavFileInputStream.openInputStream(wavFile);
System.out.println("Wav file opened successfully: " + isSoundFile(wavFile));
} catch (UnsupportedAudioFileException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

View File

@ -51,13 +51,15 @@ public class WavFileInputStream extends AudioInputStream {
if (wavHeader.readHeader(windowsFile) == false) {
throw new UnsupportedAudioFileException("Unsupprted wav file format in " + file.getName());
}
long nFrames = wavHeader.getDataSize() / wavHeader.getBlockAlign();
//29/03/2017 Found that the block align read from header was wrong in SoundTrap. This solves the problem and is still OK for normal
//wav files (this is in fact tha standard emthod for calculated blockalign)
int blockAlign = wavHeader.getNChannels() * (wavHeader.getBitsPerSample() / 8);
//System.out.println("NFRAMES: " + nFrames + " "+ wavHeader.getDataSize() + " " + wavHeader.getBlockAlign() + " "+blockAlign );
// System.out.println("NFRAMES: " + nFrames + " "+ wavHeader.getDataSize() + " " + wavHeader.getBlockAlign() + " "+blockAlign );
Encoding encoding = getEncoding(wavHeader.getFmtTag());
if (encoding == null) {

View File

@ -45,11 +45,17 @@ public class SUDFileTime {
// return Long.MIN_VALUE;
// }
// long t = sudMap.getFirstChunkTimeMillis();
System.out.println("Error getting time from SUD file: " + file==null? null : (file.getName() + " size: " + file.length() / (1024 * 1024) + " MB"));
long t = SudAudioInputStream.quickFileTime(file);
t=t/1000; //turn to milliseconds.
if (t != 0) {
if (t > 0) {
sudTime = t;
}
else {
//an error has occurred
System.err.println("Error getting time from SUD file: " + file==null? null : (file.getName() + " size: " + file.length() / (1024 * 1024) + " MB"));
}
// sudAudioInputStream.addSudFileListener((chunkID, sudChunk)->{
// ChunkHeader chunkHead = sudChunk.chunkHeader;

View File

@ -59,7 +59,7 @@ public class SUDNotificationManager implements SUDNotificationHandler {
@Override
public void interpretNewFile(String newFile, SudAudioInputStream sudAudioStream) {
for (SUDNotificationHandler handler : handlers) {
handler.interpretNewFile(newFile, sudAudioStream);
handler.interpretNewFile(newFile, sudAudioStream);
}
}

View File

@ -10,13 +10,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.JFrame;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import pamMaths.PamQuaternion;
import pamMaths.PamVector;
import userDisplay.UserDisplayControl;
import depthReadout.DepthControl;
import Array.importHydrophoneData.HydrophoneImport;
import Array.importHydrophoneData.StreamerImport;
import Array.layoutFX.ArrayGUIFX;
@ -91,7 +87,7 @@ public class ArrayManager extends PamControlledUnit implements PamSettings, PamO
// private DepthControl depthControl;
private ImportDataSystem<ArrayList<Double>> hydrophoneImportManager;
private ImportDataSystem<Hydrophone> hydrophoneImportManager;
private ImportDataSystem<ArrayList<Double>> streamerImportManager;
@ -140,7 +136,7 @@ public class ArrayManager extends PamControlledUnit implements PamSettings, PamO
//enable importing of time stamped hydrophone and streamer data if in viewer mode.
if (isViewer){
hydrophoneImportManager= new ImportDataSystem<ArrayList<Double>>(new HydrophoneImport(hydrophonesProcess.getHydrophoneDataBlock()));
hydrophoneImportManager= new ImportDataSystem<Hydrophone>(new HydrophoneImport(hydrophonesProcess.getHydrophoneDataBlock()));
hydrophoneImportManager.setName("Hydrophone Data Import");
streamerImportManager = new ImportDataSystem<ArrayList<Double>>(new StreamerImport(hydrophonesProcess.getStreamerDataBlock()));
streamerImportManager.setName("Streamer Data Import");
@ -1054,12 +1050,17 @@ public class ArrayManager extends PamControlledUnit implements PamSettings, PamO
/**
* Rotate the hydrophone about the centre of it's streamer.
*/
Hydrophone hydrophone = currentArray.getHiddenHydrophone(i);
// Hydrophone hydrophone = currentArray.getHiddenHydrophone(i);
Hydrophone hydrophone = currentArray.getHydrophone(i, timeMillis);
if (hydrophone == null) {
continue;
}
PamVector hydrophoneVec = hydrophone.getVector();
PamVector hydrophoneErrorVec = hydrophone.getErrorVector();
if (streamerQuaternion != null) {
hydrophoneVec = PamVector.rotateVector(hydrophoneVec, streamerQuaternion);
hydrophoneErrorVec = PamVector.rotateVector(hydrophoneErrorVec, streamerQuaternion);

View File

@ -203,7 +203,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
/**
* @return error on the hydrophone x coordinate.
*/
protected double getdX() {
public double getdX() {
return getCoordinateError(0);
}
@ -211,7 +211,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
* Set the error on the hydrophone x coordinate
* @param error error in metres.
*/
protected void setdX(double error) {
public void setdX(double error) {
setCoordinateError(0, error);
}
@ -219,7 +219,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
/**
* @return error on the hydrophone y coordinate.
*/
protected double getdY() {
public double getdY() {
return getCoordinateError(1);
}
@ -227,14 +227,14 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
* Set the error on the hydrophone y coordinate
* @param error error in metres.
*/
protected void setdY(double error) {
public void setdY(double error) {
setCoordinateError(1, error);
}
/**
* @return error on the hydrophone depth coordinate.
*/
protected double getdZ(){
public double getdZ(){
return getCoordinateError(2);
}
@ -242,19 +242,19 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
* Set the error on the hydrophone z coordinate
* @param error error in metres.
*/
protected void setdZ(double error) {
public void setdZ(double error) {
setCoordinateError(2, error);
}
protected double getX() {
public double getX() {
return coordinate[0];
}
protected void setX(double x) {
public void setX(double x) {
this.coordinate[0] = x;
}
protected double getY() {
public double getY() {
return coordinate[1];
}
@ -289,7 +289,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
return y2;
}
protected void setY(double y) {
public void setY(double y) {
this.coordinate[1] = y;
}
@ -297,7 +297,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
return coordinate[2];
}
protected void setZ(double z) {
public void setZ(double z) {
this.coordinate[2] = z;
}
@ -316,7 +316,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
// need to explicity create a new copy of the double[3] coordinate
// and the double[2] bandwidth
Hydrophone h = (Hydrophone) super.clone();
h.setBandwidth(Arrays.copyOf(getBandwidth(), h.getBandwidth().length));
h.setBandwidth(getBandwidth() == null ? null : Arrays.copyOf(getBandwidth(), h.getBandwidth().length));
h.setCoordinate(Arrays.copyOf(getCoordinates(), 3));
h.setCoordinateErrors(Arrays.copyOf(getCoordinateErrors(), 3));
h.checkDepthInversion();
@ -379,14 +379,14 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
/**
* @return Returns the iD.
*/
protected int getID() {
public int getID() {
return iD;
}
/**
* @param id The iD to set.
*/
protected void setID(int id) {
public void setID(int id) {
iD = id;
}
//
@ -427,11 +427,11 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters {
this.symbol = symbol;
}
protected int getStreamerId() {
public int getStreamerId() {
return streamerId;
}
protected void setStreamerId(int streamerId) {
public void setStreamerId(int streamerId) {
this.streamerId = streamerId;
}

View File

@ -5,6 +5,7 @@ import java.util.ListIterator;
import GPS.NavDataSynchronisation;
import pamScrollSystem.ViewLoadObserver;
import PamController.PamController;
import PamUtils.PamCalendar;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import PamguardMVC.dataOffline.OfflineDataLoadInfo;
@ -68,6 +69,11 @@ public class HydrophoneDataBlock extends PamDataBlock<HydrophoneDataUnit> {
@Override
public boolean loadViewerData(OfflineDataLoadInfo offlineDataLoadInfo,
ViewLoadObserver loadObserver) {
// if (offlineDataLoadInfo!=null) {
// System.out.print("Load Hydrophones: " + ((offlineDataLoadInfo.getEndMillis() - offlineDataLoadInfo.getStartMillis())/1000/60/60 + " hours"));
// System.out.print("From: " +PamCalendar.formatDateTime(offlineDataLoadInfo.getStartMillis()) + " to " + PamCalendar.formatDateTime(offlineDataLoadInfo.getEndMillis()));
// }
/**
* Always put in default data units at time zero.
*/
@ -103,6 +109,7 @@ public class HydrophoneDataBlock extends PamDataBlock<HydrophoneDataUnit> {
}
unit = listIterator.previous();
difference = Math.abs(startTime - unit.getTimeMilliseconds());
while (listIterator.hasPrevious()) {
preceedingUnit = listIterator.previous();
if (preceedingUnit.getHydrophone().getID()!=ihydrophone) {
@ -110,6 +117,7 @@ public class HydrophoneDataBlock extends PamDataBlock<HydrophoneDataUnit> {
}
newdifference = Math.abs(startTime- preceedingUnit.getTimeMilliseconds());
if (newdifference > difference) {
// System.out.println("Hydrophone datablock: newDifference: " + newdifference + " " + unit.getHydrophone().getZ());
return unit;
}
else {

View File

@ -397,6 +397,8 @@ public class HydrophoneElementDialog extends PamDialog {
dz.setText(null);
}
}
boolean getParams() {
double zCoeff = PamController.getInstance().getGlobalMediumManager().getZCoeff();

View File

@ -39,6 +39,7 @@ import PamModel.parametermanager.PamParameterSet.ParameterSetType;
import PamModel.parametermanager.PrivatePamParameterData;
import PamUtils.LatLong;
import PamUtils.PamArrayUtils;
import PamUtils.PamCalendar;
import PamView.PamSymbol;
import PamguardMVC.ChannelIterator;
import PamguardMVC.PamConstants;
@ -265,7 +266,9 @@ public class PamArray implements Serializable, Cloneable, ManagedParameters {
}
protected Hydrophone getHydrophone(int iPhone, long timeMilliseconds) {
// Debug.println("PAMArray: Get hydrophone coords: " + timeMilliseconds + " iPhone: " + iPhone);
// System.out.println("PAMArray: Get hydrophone coords: " + PamCalendar.formatDateTime(timeMilliseconds) + " iPhone: " + iPhone);
if (hydrophoneInterpolation == ORIGIN_USE_LATEST) {
return getHydrophone(iPhone);
}
@ -275,12 +278,14 @@ public class PamArray implements Serializable, Cloneable, ManagedParameters {
//FIXME - for some reason the above lines were always returning the first hydrophone in the datablock ^
HydrophoneDataUnit hdu = ArrayManager.getArrayManager().getHydrophoneDataBlock().getClosestHydrophone(timeMilliseconds, iPhone);
// System.out.println("PAMArray: hdu: " + hdu + " " + (hdu==null? null: PamCalendar.formatDateTime(hdu.getTimeMilliseconds()) + " Z" + hdu.getHydrophone().getdZ()));
if (hdu != null) {
// Debug.println("PAMArray: found unit: " + hdu.getTimeMilliseconds());
// long firstTime = ArrayManager.getArrayManager().getHydrophoneDataBlock().getFirstUnit().getTimeMilliseconds();
// long lastTime = ArrayManager.getArrayManager().getHydrophoneDataBlock().getLastUnit().getTimeMilliseconds();
// Debug.println("PAMArray: found unit: " + firstTime + " " + lastTime + " no: units: " + ArrayManager.getArrayManager().getHydrophoneDataBlock().getUnitsCount());
// System.out.println("PAMArray: found unit: " + hdu.getTimeMilliseconds());
// long firstTime = ArrayManager.getArrayManager().getHydrophoneDataBlock().getFirstUnit().getTimeMilliseconds();
// long lastTime = ArrayManager.getArrayManager().getHydrophoneDataBlock().getLastUnit().getTimeMilliseconds();
// System.out.println("PAMArray: found unit: " + firstTime + " " + lastTime + " no: units: " + ArrayManager.getArrayManager().getHydrophoneDataBlock().getUnitsCount());
// TODO should maybe do something here to average out two hydrophones if interpolation option is selected.
return hdu.getHydrophone();
}
@ -1305,7 +1310,6 @@ public class PamArray implements Serializable, Cloneable, ManagedParameters {
* @return
*/
public int addStreamer(Streamer streamer) {
synchronized (streamers) {
streamers.add(streamer);
checkStreamerIndexes();

View File

@ -571,7 +571,7 @@ public class Streamer implements Serializable, Cloneable, ManagedParameters {
/**
* Make a streamer data unit and add it to the data block.
*/
protected void makeStreamerDataUnit() {
public void makeStreamerDataUnit() {
StreamerDataUnit sdu = new StreamerDataUnit(PamCalendar.getTimeInMillis(), this);
ArrayManager.getArrayManager().getStreamerDatabBlock().addPamData(sdu);
}
@ -667,8 +667,8 @@ public class Streamer implements Serializable, Cloneable, ManagedParameters {
*/
@Override
public String toString() {
return super.toString() + "; OriginSettings: " + getOriginSettings().toString() + "," + getHydrophoneOrigin().getOriginSettings().toString() +
"; Locator " + getLocatorSettings().toString();
return super.toString() + "; OriginSettings: " + getOriginSettings()==null ? "null" : getOriginSettings().toString() + "," + getHydrophoneOrigin().getOriginSettings().toString() +
"; Locator " + getLocatorSettings()==null ? "null" : getLocatorSettings().toString();
}
public static Streamer getAverage(Streamer sd1,

View File

@ -318,7 +318,7 @@ public class StreamerDialog extends PamDialog {
PamDialog.setDoubleValue(heading, defaultStreamer.getHeading(), "%3.1f");
PamDialog.setDoubleValue(pitch, defaultStreamer.getPitch(), "%3.1f");
PamDialog.setDoubleValue(roll, defaultStreamer.getRoll(), "%3.1f");
interpolationPanel.setSelection(currentArray.getOriginInterpolation());
ArraySensorFieldType[] sensorFields = ArraySensorFieldType.values();
@ -376,6 +376,9 @@ public class StreamerDialog extends PamDialog {
defaultStreamer.setStreamerName(streamerName.getText());
int im = interpolationPanel.getSelection();
System.out.println("GetParams: INTERPOLATION SELECTION: " + currentArray.getOriginInterpolation());
if (im < 0) {
return showWarning("Invalid interpolation selection");
}

View File

@ -1,15 +1,22 @@
package Array.importHydrophoneData;
import java.io.IOException;
import java.util.ArrayList;
import Array.ArrayManager;
import Array.Hydrophone;
import Array.HydrophoneDataBlock;
import Array.HydrophoneDataUnit;
import PamUtils.PamCalendar;
import PamUtils.TxtFileUtils;
import PamView.importData.DataImport;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import us.hebi.matlab.mat.format.Mat5;
import us.hebi.matlab.mat.format.Mat5File;
import us.hebi.matlab.mat.types.Array;
import us.hebi.matlab.mat.types.Matrix;
import us.hebi.matlab.mat.types.Struct;
/**
* Class for importing hydrophone data from external file and saving to database.
@ -27,29 +34,31 @@ import PamguardMVC.PamDataUnit;
*
* @author Jamie Macaulay
*/
public class HydrophoneImport extends DataImport<ArrayList<Double>>{
String[] extensionStrings={".csv"};
public class HydrophoneImport extends DataImport<Hydrophone>{
String[] extensionStrings={".csv", ".mat"};
private ArrayList<ArrayList<Double>> hydrophonePositions;
private int errorCode;
private HydrophoneDataBlock hydrophoneDataBlock;
/**
* Streamer id to use if imported data has no streamer id info
*/
public static int defaultStreamerID=0;
// /**
// *Gain value to use if imported data has no gain info
// */
// public static int defaultGain=0; // use ArrayManager default instead
// /**
// *Sensitivity value to use if imported data has no sensitivty info
// */
// public static int defaultSens=-170; // use ArrayManager default instead
// /**
// *Gain value to use if imported data has no gain info
// */
// public static int defaultGain=0; // use ArrayManager default instead
// /**
// *Sensitivity value to use if imported data has no sensitivty info
// */
// public static int defaultSens=-170; // use ArrayManager default instead
/********NOT IMPLEMENTED YET*************
* Loads from a matlab structure with following format
* structure(i).time =time
@ -58,32 +67,32 @@ public class HydrophoneImport extends DataImport<ArrayList<Double>>{
*
*/
public final static int MATLAB_STRUCT_FORMAT=2;
/**
* Everything seems fine
*/
public final static int DATA_OK=3;
/**
* The data is far in the past or in the future
*/
public final static int ERROR_YEARS=4;
/**
* Something has gone wrong getting the csv file
*/
public final static int ERROR_LOADING_CSV=5;
/**
* Something has gone wrong loading the matlab structure
*/
public final static int ERROR_LOADING_MATLAB_STRUCT=6;
/**
* The number of hydrophones is not the same as the number of hydrophones in the curretn array manager.
* The number of hydrophones is not the same as the number of hydrophones in the current array manager.
*/
public final static int ERROR_NUMBER_OF_HYDROPHONES_ARRAY=7;
/**
* The number of hydrophones is different for different times.
*/
@ -95,11 +104,11 @@ public class HydrophoneImport extends DataImport<ArrayList<Double>>{
@Override
public ArrayList loadDataIntermediate(String filePath) {
if (filePath.endsWith(".csv")){
hydrophonePositions=TxtFileUtils.importCSVData(filePath);
if (hydrophonePositions==null || hydrophonePositions.size()==0 ) errorCode=ERROR_LOADING_CSV;
else{
//we now have two possibilities. either loading in a legacy file or loading in a list of hydrophones.
@ -112,77 +121,153 @@ public class HydrophoneImport extends DataImport<ArrayList<Double>>{
if ((hydrophonePositions.get(0).size()-1)%6==0){
return convertToHydrophoneList(hydrophonePositions);
}
}
}
if (filePath.endsWith(".mat")){
hydrophonePositions=importPositionsFromMatlab(filePath);
ArrayList<Hydrophone> hydrophonePositions = importPositionsFromMatlab(filePath);
if (hydrophonePositions==null) errorCode=ERROR_LOADING_MATLAB_STRUCT;
return hydrophonePositions;
}
return null;
}
/**
* Converts a 2D List of hydrophones into a 1D list of hydrophones.
* @param importData. Each row of the input array must have the following format. time, x0, y0,z0, x0Error, y0Error, z0Error,x1, y1,z1, x1Error, y1Error, z1Error,,..... and so on depending on the number of hydrophones.
* @return a list of hydrophones with the following format for each row. [0]=timeMilliss [1]=x [2]=y [3]=z [4]=xErr [5]=yErr [6]=zErr [7]=streamerId [8]=hydrophoneId;
* Converts a 2D List of hydrophones into a 1D list of hydrophone objecys.
*
* @param importData. Each row of the input array must have the following
* format. time, x0, y0,z0, x0Error, y0Error, z0Error,x1,
* y1,z1, x1Error, y1Error, z1Error,,..... and so on
* depending on the number of hydrophones.
* @return a list of hydrophone objects.
*/
public ArrayList<ArrayList<Double>> convertToHydrophoneList(ArrayList<ArrayList<Double>> importData){
ArrayList<ArrayList<Double>> hydrophonesAll=new ArrayList<ArrayList<Double>>();
public ArrayList<Hydrophone> convertToHydrophoneList(ArrayList<ArrayList<Double>> importData){
ArrayList<Hydrophone> hydrophonesAll=new ArrayList<Hydrophone>();
ArrayList<Double> tempArray;
Hydrophone hydrophone;
double[] cOordinates;
double [] cOordinateErrors;
double sensitivity;
double gain;
for (int i=0; i<importData.size(); i++){
for (int j=0; j<((importData.get(i).size()-1)/6); j++){
tempArray= new ArrayList<Double>();
cOordinates=new double[3];
cOordinateErrors=new double[3];
cOordinates[0]=importData.get(i).get(j*6+1);
cOordinates[1]=importData.get(i).get(j*6+2);
cOordinates[2]=importData.get(i).get(j*6+3);
cOordinateErrors[0]=importData.get(i).get(j*6+4);
cOordinateErrors[1]=importData.get(i).get(j*6+5);
cOordinateErrors[2]=importData.get(i).get(j*6+6);
tempArray.add(importData.get(i).get(0));
tempArray.add(cOordinates[0]);
tempArray.add(cOordinates[1]);
tempArray.add(cOordinates[2]);
tempArray.add(cOordinateErrors[0]);
tempArray.add(cOordinateErrors[1]);
tempArray.add(cOordinateErrors[2]);
sensitivity=ArrayManager.DEFAULT_HYDROPHONE_SENSITIVITY;
gain=ArrayManager.DEFAULT_PREAMP_GAIN;
hydrophone=new Hydrophone(j,
cOordinates[0], cOordinates[1],cOordinates[2],
cOordinateErrors[0], cOordinateErrors[1],cOordinateErrors[2],
"Unknown",
sensitivity,
new double[]{0, 20000},//meh
gain);
long timeMillis= (long) PamUtils.PamCalendar.excelSerialtoMillis(importData.get(i).get(0));
hydrophone.setTimeMillis(timeMillis);
// tempArray.add(importData.get(i).get(0));
// tempArray.add(cOordinates[0]);
// tempArray.add(cOordinates[1]);
// tempArray.add(cOordinates[2]);
// tempArray.add(cOordinateErrors[0]);
// tempArray.add(cOordinateErrors[1]);
// tempArray.add(cOordinateErrors[2]);
//set Streamer ID.
tempArray.add((double) defaultStreamerID);
// tempArray.add((double) defaultStreamerID);
//set hydrophoneID
// System.out.println("Hydrophone iD"+j);
tempArray.add((double) j);
hydrophonesAll.add(tempArray);
// System.out.println("TempArray: "+tempArray);
// System.out.println("Hydrophone iD"+j);
// tempArray.add((double) j);
hydrophonesAll.add(hydrophone);
// System.out.println("TempArray: "+tempArray);
}
}
return hydrophonesAll;
}
private ArrayList<ArrayList<Double>> importPositionsFromMatlab(
/**
* Import the hydrophone positions from a MATLAB mat file.
* @param filePath - the file path.
* @return an array of hydrophones.
*/
private static ArrayList<Hydrophone> importPositionsFromMatlab(
String filePath) {
// TODO- needs to be implemented.
try {
ArrayList<Hydrophone> hydrophones = new ArrayList<Hydrophone>();
Mat5File mat5 = Mat5.readFromFile(filePath);
Struct structArray = mat5.getArray("array_dimensions");
double sensitivity=ArrayManager.DEFAULT_HYDROPHONE_SENSITIVITY;
double gain=ArrayManager.DEFAULT_PREAMP_GAIN;
Matrix posStruct;
Matrix errStruct;
Matrix datetime;
// System.out.println("Number of structures: " + structArray.getNumElements());
Hydrophone hydrophone;
for (int i=0; i<structArray.getNumElements(); i++) {
//hydrophones in channel order.
posStruct= structArray.getMatrix("hydrophones", i);
//errors in channel order
errStruct= structArray.getMatrix("hydrophone_errors", i);
//channels
datetime = structArray.getMatrix("datetime", i);
if (posStruct.getNumElements()<=0) {
continue;
}
for (int j=0; j<posStruct.getNumRows(); j++) {
hydrophone =new Hydrophone(j,
posStruct.getDouble(j, 0), posStruct.getDouble(j, 1),posStruct.getDouble(j, 2),
errStruct.getDouble(j, 0), errStruct.getDouble(j, 1),errStruct.getDouble(j, 2),
"Unknown",
sensitivity,
new double[]{0, 20000},//meh
gain);
hydrophone.setTimeMillis(PamUtils.PamCalendar.dateNumtoMillis(datetime.getDouble(0)));
hydrophones.add(hydrophone);
}
}
return hydrophones;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@ -192,7 +277,7 @@ public class HydrophoneImport extends DataImport<ArrayList<Double>>{
}
@Override
public boolean isDataFormatOK(ArrayList<Double> dataLine) {
public boolean isDataFormatOK(Hydrophone hydrophone) {
// TODO might need to put some extra bits and bobs here eventually.
return true;
}
@ -201,34 +286,43 @@ public class HydrophoneImport extends DataImport<ArrayList<Double>>{
* For hydrophone data imported [0]=timeMilliss [1]=x [2]=y [3]=z [4]=xErr [5]=yErr [6]=zErr [7]=streamerId [8]=hydrophoneId [9]=sensitivity [10]=gain
*/
@Override
public PamDataUnit createDataUnit(ArrayList<Double> dataLine) {
double sensitivity=ArrayManager.DEFAULT_HYDROPHONE_SENSITIVITY;
double gain=ArrayManager.DEFAULT_PREAMP_GAIN;
if (dataLine.size()==10) {
gain=dataLine.get(10);
sensitivity=dataLine.get(9);
}
double[] bandwidth={0, 20000};
public PamDataUnit createDataUnit(Hydrophone hydrophone) {
Hydrophone hydrophone=new Hydrophone(dataLine.get(8).intValue(), dataLine.get(1), dataLine.get(2), dataLine.get(3), dataLine.get(4),dataLine.get(5), dataLine.get(6), "Unknown", sensitivity,
bandwidth, gain);
//need to convert from excel serial to millis.
long timeMillis= (long) PamUtils.PamCalendar.excelSerialtoMillis(dataLine.get(0));
hydrophone.setTimeMillis(timeMillis);
// double sensitivity=ArrayManager.DEFAULT_HYDROPHONE_SENSITIVITY;
// double gain=ArrayManager.DEFAULT_PREAMP_GAIN;
// if (dataLine.size()==10) {
// gain=dataLine.get(10);
// sensitivity=dataLine.get(9);
// }
// double[] bandwidth={0, 20000};
//
// Hydrophone hydrophone=new Hydrophone(dataLine.get(8).intValue(), dataLine.get(1), dataLine.get(2), dataLine.get(3), dataLine.get(4),dataLine.get(5), dataLine.get(6), "Unknown", sensitivity,
// bandwidth, gain);
// //need to convert from excel serial to millis.
// long timeMillis= (long) PamUtils.PamCalendar.excelSerialtoMillis(dataLine.get(0));
// hydrophone.setTimeMillis(timeMillis);
HydrophoneDataUnit hydrophoneDataUnit=new HydrophoneDataUnit(hydrophone);
return hydrophoneDataUnit;
}
@Override
public PamDataBlock getDataBlock() {
return hydrophoneDataBlock;
}
@Override
public String getDataUnitName(){
return "Hydrophone Units";
}
public static void main(String [] args) {
String file = "/Users/au671271/Desktop/test_array_data.mat";
ArrayList<Hydrophone> data = importPositionsFromMatlab(file);
System.out.println("Impotred data size: " + data.size());
System.out.println(data.get(0));
}
}

View File

@ -0,0 +1,586 @@
package Array.layoutFX;
import pamViewFX.fxNodes.PamBorderPane;
import java.util.ArrayList;
import org.fxyz3d.geometry.Point3D;
import org.fxyz3d.shapes.composites.PolyLine3D;
import Array.Hydrophone;
import Array.PamArray;
import Array.Streamer;
import javafx.event.EventHandler;
import javafx.scene.AmbientLight;
import javafx.scene.DepthTest;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.PerspectiveCamera;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.PickResult;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.Shape3D;
import javafx.scene.shape.Sphere;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
/**
* Create a 3D visualisation of the array.
* <p>
PAMGUARD co-rdinate system is
* <p>
* x points right
* <p>
* y points north or into the screen
* <p>
* z is height and points up
* <p><p>
* This is different from the JavAFX 3D system in which
* <p>
* x points right
* <p>
* y points down
* <p>
* z points into the screen
* <p>
* Thus the source code for this class is a little bit more complex. By convention the co-ordinate system is only changed for display purposes and remains
* in PAMGUARD format throughout the rest of code.
* @author Jamie Macaulay
*
*/
public class Array3DPane extends PamBorderPane {
public static final Color DEFAULT_HYDRO_COL = Color.RED;
// private static final Color DEFAULT_SENSOR_COL = Color.LIMEGREEN;
private double scaleFactor=20;
private double axisSize=10*scaleFactor;
//keep track of mouse positions
double mousePosX;
double mousePosY;
double mouseOldX;
double mouseOldY;
double mouseDeltaX;
double mouseDeltaY;
/**
* This is the group which rotates
*/
Group root3D;
/**
* Group which holds array shapes.
*/
Group arrayGroup;
/**
* Group which holds axis and other non changing bits.
*/
Group axisGroup;
/**
* The camera transforms
*/
private Rotate rotateY;
private Rotate rotateX;
private Translate translate;
/**
* The size of the hydrophone for the 3D display.
*/
private double hydrophoneSize = 0.5;
/**
* Holds a list of hydrophone spheres
*/
private ArrayList<HydrophoneSphere> hydrophonesSpheres = new ArrayList<HydrophoneSphere>();
/**
* Allow rotation
*/
private boolean allowRotate = true;
private Text xText;
private Text yText;
private Text zText;
private Box yAxis;
private Shape3D ySphere;
public Array3DPane(){
// Create and position camera
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setFarClip(20000);
camera.setNearClip(0.1);
// camera.setDepthTest(DepthTest.ENABLE);
camera.getTransforms().addAll (
rotateY=new Rotate(-45, Rotate.Y_AXIS),
rotateX=new Rotate(-45, Rotate.X_AXIS),
translate=new Translate(0, 0, -2000));
//create main 3D group
root3D=new Group();
axisGroup=buildAxes(axisSize); //create axis group
arrayGroup=new Group();
root3D.getChildren().add(arrayGroup);
root3D.getChildren().add(axisGroup);
AmbientLight light = new AmbientLight();
light.setColor(Color.WHITE);
Group lightGroup = new Group();
lightGroup.getChildren().add(light);
root3D.getChildren().add(lightGroup);
//Use a SubScene to mix 3D and 2D stuff.
//note- make sure depth buffer in sub scene is enabled.
SubScene subScene = new SubScene(root3D, 500,400, true, SceneAntialiasing.BALANCED);
subScene.widthProperty().bind(this.widthProperty());
subScene.heightProperty().bind(this.heightProperty());
subScene.setDepthTest(DepthTest.ENABLE);
subScene.setOnMouseClicked((MouseEvent me) -> {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
PickResult pr = me.getPickResult();
// System.out.println("Picked something sphere: " + pr);
//clear selected radius
for (HydrophoneSphere sphere : hydrophonesSpheres) {
sphere.setRadius(hydrophoneSize*scaleFactor);
}
if(pr!=null && pr.getIntersectedNode() != null && pr.getIntersectedNode() instanceof Sphere){
//make the selected sphere slightly larger
HydrophoneSphere s = (HydrophoneSphere) pr.getIntersectedNode();
s.setRadius(hydrophoneSize*scaleFactor*1.2);
hydrophoneSelected(s.getHydrophone());
// System.out.println("Picked a sphere: " + pr);
// distance=pr.getIntersectedDistance();
// s = (Sphere) pr.getIntersectedNode();
// isPicking=true;
// vecIni = unProjectDirection(mousePosX, mousePosY, scene.getWidth(),scene.getHeight());
}
});
//note the fill is actually quite important because if you don't have it mouse rotations etc
//onyl work if you select a 3D shape
subScene.setFill(Color.TRANSPARENT);
subScene.setCamera(camera);
//handle mouse events for sub scene
handleMouse(subScene);
resetView();
//create new group to add sub scene to
Group group = new Group();
group.getChildren().add(subScene);
//add group to window.
this.setCenter(group);
this.setDepthTest(DepthTest.ENABLE);
}
/**
* Called whenever a hydrophone is selected.
* @param hydrophone - the selected hydrophone.
*/
public void hydrophoneSelected(Hydrophone hydrophone) {
// TODO Auto-generated method stub
}
/**
* Create a 3D axis with default colours set.
* @param- size of the axis
*/
public Group buildAxes(double axisSize) {
return buildAxes( axisSize,Color.RED, Color.RED,
Color.GREEN, Color.GREEN,
Color.BLUE, Color.BLUE,
Color.WHITE);
}
/**
* Create a 3D axis.
* @param- size of the axis
*/
public Group buildAxes(double axisSize, Color xAxisDiffuse, Color xAxisSpectacular,
Color yAxisDiffuse, Color yAxisSpectacular,
Color zAxisDiffuse, Color zAxisSpectacular,
Color textColour) {
Group axisGroup=new Group();
double length = 2d*axisSize;
double width = axisSize/100d;
double radius = 2d*axisSize/100d;
final PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setDiffuseColor(xAxisDiffuse);
redMaterial.setSpecularColor(xAxisSpectacular);
final PhongMaterial greenMaterial = new PhongMaterial();
greenMaterial.setDiffuseColor(yAxisDiffuse);
greenMaterial.setSpecularColor( yAxisSpectacular);
final PhongMaterial blueMaterial = new PhongMaterial();
blueMaterial.setDiffuseColor(zAxisDiffuse);
blueMaterial.setSpecularColor(zAxisSpectacular);
xText=new Text("x");
xText.setStyle("-fx-font: 20px Tahoma; -fx-fill: white;");
xText.setFill(textColour);
xText.setStroke(textColour);
yText=new Text("z");
yText.setStyle("-fx-font: 20px Tahoma; -fx-fill: white;");
yText.setFill(textColour);
yText.setStroke(textColour);
zText=new Text("y");
zText.setStyle("-fx-font: 20px Tahoma; -fx-fill: white;");
zText.setFill(textColour);
zText.setStroke(textColour);
xText.setTranslateX(axisSize+5);
xText.setTranslateZ(1); //dunno why but shifting a little in z is required to see colour
yText.setTranslateY(-(axisSize+5));
yText.setTranslateZ(1); //dunno why but shifting a little in z is required to see colour
zText.setTranslateZ(axisSize+5);
Sphere xSphere = new Sphere(radius);
ySphere = new Sphere(radius);
Sphere zSphere = new Sphere(radius);
xSphere.setMaterial(redMaterial);
ySphere.setMaterial(greenMaterial);
zSphere.setMaterial(blueMaterial);
xSphere.setTranslateX(axisSize);
ySphere.setTranslateY(-axisSize);
zSphere.setTranslateZ(axisSize);
Box xAxis = new Box(length, width, width);
yAxis = new Box(width, length, width);
Box zAxis = new Box(width, width, length);
xAxis.setMaterial(redMaterial);
yAxis.setMaterial(greenMaterial);
zAxis.setMaterial(blueMaterial);
axisGroup.getChildren().addAll(xAxis, yAxis, zAxis);
axisGroup.getChildren().addAll(xText, yText, zText);
axisGroup.getChildren().addAll(xSphere, ySphere, zSphere);
return axisGroup;
}
private void handleMouse(SubScene scene) {
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent me) {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
}
});
scene.setOnScroll(new EventHandler<ScrollEvent>() {
@Override public void handle(ScrollEvent event) {
// System.out.println("Scroll Event: "+event.getDeltaX() + " "+event.getDeltaY());
translate.setZ(translate.getZ()+ event.getDeltaY() *0.001*translate.getZ()); // +
}
});
scene.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent me) {
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
double modifier = 1.0;
double modifierFactor = 0.1;
if (me.isControlDown()) {
modifier = 0.1;
}
if (me.isShiftDown()) {
modifier = 10.0;
}
if (me.isPrimaryButtonDown() && allowRotate) {
rotateY.setAngle(rotateY.getAngle() + mouseDeltaX * modifierFactor * modifier * 2.0); // +
rotateX.setAngle(rotateX.getAngle() - mouseDeltaY * modifierFactor * modifier * 2.0); // -
// System.out.println("Rotation: x: " + rotateX.getAngle() + " y: " + rotateY.getAngle());
}
if (me.isSecondaryButtonDown()) {
translate.setX(translate.getX() -mouseDeltaX * modifierFactor * modifier * 5);
translate.setY(translate.getY() - mouseDeltaY * modifierFactor * modifier * 5); // +
}
}
});
}
/**
* Draw the hydrophone array.
* @param array - the hydrophone array to draw.
*/
public void drawArray(PamArray array) {
// System.out.println("DRAW ARRAY: " + array);
//clear the array
arrayGroup.getChildren().removeAll(arrayGroup.getChildren());
ArrayList<Hydrophone> hydrophones = array.getHydrophoneArray();
//draw hydrophones
HydrophoneSphere sphere;
Streamer streamer;
hydrophonesSpheres.clear();
for (int i=0; i<hydrophones.size(); i++){
//get the streamer for the hydrophone
streamer = array.getStreamer(hydrophones.get(i).getStreamerId());
double x = (hydrophones.get(i).getX() + streamer.getCoordinate(0));
double y = (hydrophones.get(i).getY() + streamer.getCoordinate(1));
double z = -(hydrophones.get(i).getZ() + streamer.getCoordinate(2));
sphere=new HydrophoneSphere(hydrophoneSize*scaleFactor);
sphere.setTranslateX(x*scaleFactor);
sphere.setTranslateY(z*scaleFactor);
sphere.setTranslateZ(y*scaleFactor);
Color col = Color.RED;
final PhongMaterial aMaterial = new PhongMaterial();
aMaterial.setDiffuseColor(col);
aMaterial.setSpecularColor(col.brighter());
sphere.setMaterial(aMaterial);
sphere.setHydrophone(hydrophones.get(i));
// System.out.println("Add hydrophone: " + x + " " + y + " " +z);
hydrophonesSpheres.add(sphere);
arrayGroup.getChildren().add(sphere);
ArrayList<Point3D> streamerPoints=new ArrayList<Point3D>();
//now plot a line from the streamer to the hydrophone
Point3D newPoint;
newPoint=new Point3D(x*scaleFactor, z*scaleFactor, y*scaleFactor);
streamerPoints.add(newPoint);
newPoint =new Point3D(streamer.getCoordinate(0)*scaleFactor, -streamer.getCoordinate(2)*scaleFactor, streamer.getCoordinate(1)*scaleFactor);
streamerPoints.add(newPoint);
//System.out.println("Streamer points: " + streamerPoints.size());
PolyLine3D polyLine3D=new PolyLine3D(streamerPoints, 4f, Color.DODGERBLUE);
arrayGroup.getChildren().add(polyLine3D);
}
}
private class HydrophoneSphere extends Sphere {
Hydrophone hydrophone;
public Hydrophone getHydrophone() {
return hydrophone;
}
public void setHydrophone(Hydrophone hydrophone) {
this.hydrophone = hydrophone;
}
public HydrophoneSphere() {
super();
}
public HydrophoneSphere(double radius) {
super(radius);
}
}
/**
* Sets the pane to show hydrophones in 2D or 3D.
* @param set3D - true to set to 3D
*/
public void set3D(boolean set3D) {
double textRotation =0;
if (set3D) {
allowRotate=true;
xText.setRotate(0);
rotateY.setAngle(-45);
rotateX.setAngle(-45);
}
else {
allowRotate=false;
rotateY.setAngle(0);
rotateX.setAngle(270);
textRotation=-90;
}
//confusing because the yaxis is the z axis in JavaFX...
yText.setVisible(set3D);
yAxis.setVisible(set3D);
ySphere.setVisible(set3D);
xText.setRotate(textRotation);
xText.setRotationAxis(new javafx.geometry.Point3D(1,0,0));
yText.setRotationAxis(new javafx.geometry.Point3D(1,0,0));
yText.setRotate(textRotation);
zText.setRotationAxis(new javafx.geometry.Point3D(1,0,0));
zText.setRotate(textRotation);
}
/**
* Reset to the defulat view.
*/
public void resetView() {
translate.setX(0);
translate.setY(0);
translate.setZ( -2000);
if (allowRotate) {
rotateY.setAngle(-45);
rotateX.setAngle(-45);
}
}
// /**
// * Draw the entire array
// * @param pos - hydrophone and streamer positions in the same co-ordinate frame as the reference frame.
// */
// public void drawArrays(ArrayList<ArrayList<ArrayPos>> pos){
//
// arrayGroup.getChildren().removeAll(arrayGroup.getChildren());
//
// if (pos==null){
// System.err.println("Array3DPane: Hydrophone positions are null");
// return;
// }
//
// for (int i=0; i< pos.size(); i++){
// for (int j=0; j<pos.get(i).size(); j++){
// drawArray(pos.get(i).get(j));
// }
// }
//
// //System.out.println("Draw 3D hydrophone array");
// }
// /**
// * Draw an array.
// * @param arrayPos - hydrophone and streamer positions in the same co-ordinate frame as the reference frame.
// */
// private void drawArray(ArrayPos arrayPos){
//
// final PhongMaterial redMaterial = new PhongMaterial();
// redMaterial.setDiffuseColor(DEFAULT_HYDRO_COL);
// redMaterial.setSpecularColor(DEFAULT_HYDRO_COL.brighter());
//
// final PhongMaterial greenMaterial = new PhongMaterial();
// greenMaterial.setDiffuseColor(DEFAULT_SENSOR_COL);
// greenMaterial.setSpecularColor(DEFAULT_SENSOR_COL.brighter());
//
// //draw hydrophones
// Sphere sphere;
// for (int i=0; i<arrayPos.getTransformHydrophonePos().size(); i++){
// sphere=new Sphere(settings.hydrophoneSize*scaleFactor);
// sphere.setTranslateX(arrayPos.getTransformHydrophonePos().get(i)[0]*scaleFactor);
// sphere.setTranslateY(-arrayPos.getTransformHydrophonePos().get(i)[2]*scaleFactor);
// sphere.setTranslateZ(arrayPos.getTransformHydrophonePos().get(i)[1]*scaleFactor);
//
// Color hydroCol = settings.hydrophoneColours[arrayPos.getHArray().getHydrophones().get(i).channel.get()];
//
// if (hydroCol == null) {
// sphere.setMaterial(redMaterial);
// }
// else {
// final PhongMaterial aMaterial = new PhongMaterial();
// aMaterial.setDiffuseColor(hydroCol);
// aMaterial.setSpecularColor(hydroCol.brighter());
// sphere.setMaterial(aMaterial);
//
// }
// arrayGroup.getChildren().add(sphere);
//
// }
//
//
//
// //draw streamer
// PolyLine3D polyLine3D;
// ArrayList<Point3D> streamerPoints;
//
// for (int i=0; i<arrayPos.getTransformStreamerPositions().size(); i++){
// if (arrayPos.getTransformStreamerPositions().get(i)==null) return;
// streamerPoints=new ArrayList<Point3D>();
// for (int j=0; j<arrayPos.getTransformStreamerPositions().get(i).size(); j++){
//
// //TODO- use cylinder for line
// // Cylinder cylinder=createConnection(arrayPos.getTransformStreamerPositions().get(i).get(j).multiply(scaleFactor), arrayPos.getTransformStreamerPositions().get(i).get(j+1).multiply(scaleFactor),0.2*scaleFactor);
// // arrayGroup.getChildren().add(cylinder);
//
// //need to convert to fxyz 3D point - stupid but no work around.
// Point3D newPoint=new Point3D((float) (arrayPos.getTransformStreamerPositions().get(i).get(j).getX()*scaleFactor),
// (float) (-arrayPos.getTransformStreamerPositions().get(i).get(j).getZ()*scaleFactor), (float) (arrayPos.getTransformStreamerPositions().get(i).get(j).getY()*scaleFactor));
// streamerPoints.add(newPoint);
// }
// polyLine3D=new PolyLine3D(streamerPoints, 4, Color.BLUE);
// arrayGroup.getChildren().add(polyLine3D);
// }
//
// }
}

View File

@ -0,0 +1,15 @@
package Array.layoutFX;
public interface ArrayChangeListener {
int STREAMER_CHANGE = 0;
int HYDROPHONE_CHANGE = 1;
/**
* Called whenever a hydrophone or streamer changes.
* @param type - the type of change e.g. ArrayChangeListener.HYDROPHONE_CHANGE
* @param changedObject - the changed object - hydrophone or streamer property.
*/
public void arrayChanged(int type, Object changedObject);
}

View File

@ -7,6 +7,7 @@ import pamViewFX.PamControlledGUIFX;
/**
* JavaFX UI bits of the Array Manager.
*
* @author Jamie Macaulay
*
*/
@ -30,12 +31,17 @@ public class ArrayGUIFX extends PamControlledGUIFX {
if (arraySettingsPane==null) {
arraySettingsPane= new ArraySettingsPane();
}
System.out.println("The current array is "+ arrayManager.getCurrentArray());
arraySettingsPane.setParams(arrayManager.getCurrentArray());
return arraySettingsPane;
}
@Override
public void updateParams() {
System.out.println("The current array is "+ arrayManager.getCurrentArray());
PamArray newParams=arraySettingsPane.getParams(arrayManager.getCurrentArray());
if (newParams!=null) arrayManager.setCurrentArray(newParams);
//setup the controlled unit.
arrayManager.setupControlledUnit();

View File

@ -1,45 +1,400 @@
package Array.layoutFX;
import java.io.File;
import org.controlsfx.control.SegmentedButton;
import Array.ArrayManager;
import Array.Hydrophone;
import Array.PamArray;
import PamController.PamController;
import PamController.SettingsPane;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxGlyphs.PamGlyphDude;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxNodes.pamDialogFX.PamDialogFX;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamButton;
import pamViewFX.fxNodes.PamHBox;
/**
* The main settings pane for settings up a hydrophone array.
*
* @author Jamie Macaulay
*
* @param <FlipPane>
*
*/
public class ArraySettingsPane extends SettingsPane<PamArray >{
/**
* Minimum size of the 3D pane.
*/
private static final double MIN_3D_WIDTH = 450;
/**
* Minimum height of the 3D pane.
*/
private static final double MIN_3D_HEIGHT = 600;
/**
* Pane for adding or removing streamers.
*/
private StreamersPane streamerPane;
private PamBorderPane mainPane;
/**
* Pane for adding or removing hydrophones.
*/
private HydrophonesPane hydrophonePane;
// private Pane holder;
private Label hydrophoneLabel;
/**
* Pane which shows a 3D representation of the hydrophone array.
*/
private Array3DPane array3DPane;
private PropogationPane propogationPane;
private Label recivierDiagramLabel;
/**
* Pane with controls to change speed of sound.
*/
private SettingsPane<PamArray> environmentalPane;
private FileChooser fileChooser;
//a copy of the current array
private PamArray currentArray;
public ArraySettingsPane() {
super(null);
// TODO Auto-generated constructor stub
mainPane=new PamBorderPane();
mainPane.setCenter(createArrayPane());
// mainPane.setStyle("-fx-background-color: red;");
mainPane.setMaxWidth(Double.MAX_VALUE);
mainPane.setMinWidth(1100);
mainPane.setStyle("-fx-padding: 0,0,0,0");
recivierDiagramLabel = new Label("Hydrophone Diagram");
PamGuiManagerFX.titleFont1style(recivierDiagramLabel);
recivierDiagramLabel.setPadding(new Insets(5,5,5,5));
Label environmentLabel = new Label("Environment");
PamGuiManagerFX.titleFont1style(environmentLabel);
environmentLabel.setPadding(new Insets(0,0,5,0)); //little more space under this label
environmentalPane = createEnvironmentPane();
PamVBox rightPane = new PamVBox();
rightPane.setSpacing(5);
Pane hydrophone3DPane = create3DPane();
rightPane.getChildren().addAll(recivierDiagramLabel, hydrophone3DPane, environmentLabel, new PamBorderPane(environmentalPane.getContentNode()));
VBox.setVgrow(hydrophone3DPane, Priority.ALWAYS);
mainPane.setRight(rightPane);
// streamerPane.getStreamerTable().getItems().addListener((ListChangeListener<? super StreamerProperty>) c->{
// //the streamer table has changed and so the streamer needs changed
// System.out.println("Streamer Changed!!!");
// });
streamerPane.addStreamerListener((x,y)->{
PamArray array = getParams(new PamArray("temp_array: ", null)) ;
System.out.println("Streamer changed!");
array3DPane.drawArray(array);
});
hydrophonePane.addStreamerListener((x,y)->{
PamArray array = getParams(new PamArray("temp_array: ", null)) ;
System.out.println("Hydrophone changed!" + array.getHydrophoneCount());
array3DPane.drawArray(array);
});
// mainPane.setMinWidth(800);
// mainPane.setCenter(createArrayPane());
//
// mainPane.getAdvPane().setCenter(new Label("Advanced Settings"));
// //mainPane.getFront().setStyle("-fx-background-color: grey;");
// mainPane.setStyle("-fx-background-color: red;");
//
// FlipPane aflipPane = new FlipPane();
// aflipPane.setStyle("-fx-background-color: red;");
//
// PamHBox stackPane = new PamHBox();
// stackPane.setStyle("-fx-background-color: red;");
//
// Button button = new Button();
// button.setOnAction((action)->{
// System.out.println(" 1 " + stackPane.getPadding());
// System.out.println(" 2 " +PamBorderPane.getMargin(stackPane));
// System.out.println(" 3 " + holder.getPadding());
// });
//
// stackPane.getChildren().add(button);
//
//
// mainPane.setPadding(new Insets(0,0,0,0));
// holder = new StackPane();
// holder.getChildren().add(mainPane);
// holder.setStyle("-fx-padding: 0,0,0,0");
}
private Pane create3DPane() {
StackPane stackPane = new StackPane();
this.array3DPane = new HydrophoneArray3DPane();
//important because the 3D pane has not default size
stackPane.setMinWidth(MIN_3D_WIDTH);
stackPane.setMinHeight(MIN_3D_HEIGHT);
// stackPane.prefHeightProperty().bind(mainPane.heightProperty().subtract(100));
stackPane.getChildren().add(array3DPane);
//add buttons
ToggleButton b1 = new ToggleButton("2D");
ToggleButton b2 = new ToggleButton("3D");
b1.setOnAction((action)->{
array3DPane.set3D(false);
});
b2.setOnAction((action)->{
array3DPane.set3D(true);
});
SegmentedButton segmentedButton = new SegmentedButton();
segmentedButton.getButtons().addAll(b1, b2);
segmentedButton.setPadding(new Insets(5,5,5,5));
segmentedButton.setMinWidth(100);
StackPane.setAlignment(segmentedButton, Pos.TOP_RIGHT);
stackPane.getChildren().add(segmentedButton);
final ContextMenu contextMenu = new ContextMenu();
final MenuItem item1 = new MenuItem("Reset");
item1.setOnAction((action)->{
array3DPane.resetView();
});
contextMenu.getItems().add(item1);
segmentedButton.setContextMenu(contextMenu);
// stackPane.setOnContextMenuRequested(e ->
// contextMenu.show(stackPane, e.getScreenX(), e.getScreenY()));
b2.setSelected(true);
return stackPane;
}
/**
* Create the environment pane.
* @return the environment pane.
*/
private SettingsPane<PamArray> createEnvironmentPane() {
this.propogationPane = new PropogationPane();
return propogationPane;
}
/**
* Create the main pane.
* @return the main array pane.
*/
private Pane createArrayPane() {
Label arrayLabel = new Label("Array");
arrayLabel.setPadding(new Insets(5,5,5,5));
PamGuiManagerFX.titleFont1style(arrayLabel);
//holds the array label and also some button for import and export.
PamHBox arrayImportExportBox = new PamHBox();
arrayImportExportBox.setSpacing(5);
arrayImportExportBox.setAlignment(Pos.CENTER_LEFT);
arrayImportExportBox.setPadding(new Insets(5,0,0,0));
fileChooser = new FileChooser();
fileChooser.setTitle("Open Resource File");
fileChooser.getExtensionFilters().addAll(
new ExtensionFilter("PAMGuard Array Files", "*.paf"));
PamButton importButton = new PamButton("Import...");
importButton.setOnAction((action)->{
importArray();
});
importButton.setGraphic(PamGlyphDude.createPamIcon("mdi2f-file-import", PamGuiManagerFX.iconSize));
importButton.setTooltip(new Tooltip("Import array settings from a .pgaf file"));
PamButton exportButton = new PamButton("Export...");
exportButton.setOnAction((action)->{
exportArray();
});
exportButton.setGraphic(PamGlyphDude.createPamIcon("mdi2f-file-export", PamGuiManagerFX.iconSize));
exportButton.setTooltip(new Tooltip("Export array settings to a .pgaf file"));
//balnk region to make it look nicer
Region blank = new Region();
blank.setPrefWidth(70);
arrayImportExportBox.getChildren().addAll(importButton, exportButton, blank);
PamBorderPane titleHolder = new PamBorderPane();
titleHolder.setLeft(arrayLabel);
titleHolder.setRight(arrayImportExportBox);
//the streamer pane for changing streamer settings.
streamerPane = new StreamersPane();
streamerPane.setMaxWidth(Double.MAX_VALUE);
hydrophoneLabel = new Label("Hydrophones");
PamGuiManagerFX.titleFont1style(hydrophoneLabel);
hydrophoneLabel.setPadding(new Insets(5,5,5,5));
hydrophonePane = new HydrophonesPane();
hydrophonePane.setMaxWidth(Double.MAX_VALUE);
// PamButton advancedButton = new PamButton();
// advancedButton.setOnAction((action)->{
// mainPane.flipToBack();
// });
// advancedButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-cog"));
// PamHBox advancedPane = new PamHBox();
// advancedPane.setSpacing(5);
// advancedPane.setAlignment(Pos.CENTER_RIGHT);
// advancedPane.getChildren().addAll(new Label("Advanced"), advancedButton);
PamVBox vBox = new PamVBox();
vBox.setSpacing(5);
vBox.getChildren().addAll(titleHolder, streamerPane, hydrophoneLabel,
hydrophonePane);
return vBox;
}
/**
* Select a file to export array settings to.
*/
private void exportArray() {
File file = fileChooser.showSaveDialog(getFXWindow());
if (file==null) return;
PamArray pamArray = getParams(new PamArray("saved_array: ", null));
boolean isSaved = ArrayManager.saveArrayToFile(pamArray);
if (isSaved==false) {
PamDialogFX.showError("Unable to save the array file to \n" + file.toString());
}
}
/**
* Select a file to import array settings
*/
private void importArray() {
File file = fileChooser.showOpenDialog(getFXWindow());
if (file==null) return;
PamArray pamArray = ArrayManager.loadArrayFromFile(file.getPath());
this.setParams(pamArray);
}
/**
* Set correct text for the receiver in the current medium (e.g. air or water);
*/
private void setReceieverLabels() {
hydrophonePane.setRecieverLabels();
streamerPane.setRecieverLabels();
hydrophoneLabel.setText(PamController.getInstance().getGlobalMediumManager().getRecieverString(true) + "s");
recivierDiagramLabel.setText(PamController.getInstance().getGlobalMediumManager().getRecieverString(true) + " diagram");
// if (singleInstance!=null) {
// singleInstance.setTitle("Pamguard "+ PamController.getInstance().getGlobalMediumManager().getRecieverString(false) +" array");
// }
}
@Override
public PamArray getParams(PamArray currParams) {
// TODO Auto-generated method stub
return null;
public PamArray getParams(PamArray currParams) {
if (currParams==null) currParams = this.currentArray;
currParams = streamerPane.getParams(currParams);
currParams = hydrophonePane.getParams(currParams);
currParams.setHydrophoneInterpolation(hydrophonePane.getHydrophoneInterp());
currParams = environmentalPane.getParams(currParams);
// System.out.println("Array settings pane: No. streamers: " + currParams.getStreamerCount());
return currParams;
}
@Override
public void setParams(PamArray input) {
// TODO Auto-generated method stub
this.currentArray = input.clone();
// System.out.println("Hydrophone array is: "+ input);
setReceieverLabels();
hydrophonePane.setParams(input);
streamerPane.setParams(input);
environmentalPane.setParams(input);
//draw the array
array3DPane.drawArray(input);
}
@Override
public String getName() {
// TODO Auto-generated method stub
return "Array Parameters";
}
@Override
public Node getContentNode() {
// TODO Auto-generated method stub
return new Label("TODO: The Array Manager needs an FX GUI");
return mainPane;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
private class HydrophoneArray3DPane extends Array3DPane {
@Override
public void hydrophoneSelected(Hydrophone hydrophone) {
hydrophonePane.selectHydrophone(hydrophone);
}
}
}

View File

@ -0,0 +1,58 @@
package Array.layoutFX;
/**
* Default hydrophone parameters.
*
* @author Jamie Macaulay
*
*/
public enum DefaultHydrophone {
SoundTrap600HF("SoundTrap 600 HF", -176., 0), SoundTrap300HF("SoundTrap 300 HF", -176., 0), HydroMoth_1_0_0("HydroMoth 1.0.0", -180., 0);
/**
* The name of the hydrophones.
*/
private String name;
/**
* The sensitivity of the hydrophone in dB re 1V/uPa.
*/
private double sens;
/**
* The gain in dB.
*/
private double gain;
/**
* The name of the hydrophone.
* @param name - the name of the hydrophone.
* @param sens - the sensitivity of the hydrophone.
* @param gain - the gain of the hydrophone.
*/
DefaultHydrophone(String name, double sens, double gain) {
this.name = name;
this.sens = sens;
this.gain = gain;
}
/**
* The sensitivity of the hydrophone in dB re 1V/uPa.
*/
public double getSens() {
return sens;
}
/**
* The gain in dB.
*/
public double getGain() {
return gain;
}
}

View File

@ -0,0 +1,116 @@
package Array.layoutFX;
import Array.Hydrophone;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
/**
* Property class for a hydrophone object.
*
* @author Jamie Macaulay
*
*/
public class HydrophoneProperty {
SimpleDoubleProperty x = new SimpleDoubleProperty();
SimpleDoubleProperty y = new SimpleDoubleProperty();
SimpleDoubleProperty z = new SimpleDoubleProperty();
SimpleDoubleProperty xErr = new SimpleDoubleProperty();
SimpleDoubleProperty yErr = new SimpleDoubleProperty();
SimpleDoubleProperty zErr = new SimpleDoubleProperty();
SimpleIntegerProperty id = new SimpleIntegerProperty();
private Hydrophone hydrophone;
public HydrophoneProperty(Hydrophone hydrophone) {
setHydrophone(hydrophone);
}
public void setHydrophone(Hydrophone hydrophone) {
this.hydrophone = hydrophone;
x .set(hydrophone.getX());
y .set(hydrophone.getY());
z .set(hydrophone.getZ());
xErr .set(hydrophone.getdX());
yErr .set(hydrophone.getdY());
zErr .set(hydrophone.getdZ());
id.set(hydrophone.getID());
}
/**
* The x-coordinate property.
* @return the x coordintae property.
*/
public SimpleDoubleProperty getX() {
return x;
}
/**
* The y-coordinate property.
* @return the y coordintae property.
*/
public SimpleDoubleProperty getY() {
return y;
}
/**
* The z-coordinate property.
* @return the z coordintae property.
*/
public SimpleDoubleProperty getZ() {
return z;
}
public SimpleIntegerProperty getID() {
return id;
}
public Hydrophone getHydrophone() {
//incase table data changes.
this.hydrophone.setID(this.id.get());
this.hydrophone.setX(x.get());
this.hydrophone.setY(y.get());
this.hydrophone.setZ(z.get());
this.hydrophone.setdX(xErr.get());
this.hydrophone.setdY(yErr.get());
this.hydrophone.setdZ(xErr.get());
return hydrophone;
}
/**
* The x-coordinate property.
* @return the x coordintae property.
*/
public SimpleDoubleProperty getXErr() {
return xErr;
}
/**
* The y-coordinate property.
* @return the y coordintae property.
*/
public SimpleDoubleProperty getYErr() {
return yErr;
}
/**
* The z-coordinate property.
* @return the z coordintae property.
*/
public SimpleDoubleProperty getZErr() {
return zErr;
}
}

View File

@ -0,0 +1,589 @@
package Array.layoutFX;
import Array.Hydrophone;
import javafx.scene.Node;
import javafx.scene.control.Label;
import Array.PamArray;
import Array.Streamer;
import PamController.PamController;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Labeled;
import javafx.scene.control.TextField;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import net.synedra.validatorfx.Validator;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamGridPane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamSpinner;
import pamViewFX.validator.PamValidator;
/**
* The settings pane for a single hydrophones.
*
* @author Jamie Macaulay
*
*/
public class HydrophoneSettingsPane extends DynamicSettingsPane<Hydrophone> {
private static final double COLUMN_0_WIDTH = 120;
private final static double MAX_TEXTFIELD_WIDTH = 80;
/**
*
* Check inputs in real time
*/
PamValidator validator = new PamValidator();
@Override
public String getName() {
return "Hydrophone Settings";
}
@Override
public Node getContentNode() {
return mainPane;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
private TextField iD;
private TextField yPos;
private TextField xPos;
private TextField zPos;
private TextField yPosErr;
private TextField xPosErr;
private TextField zPosErr;
private PamSpinner<Double> hSens;
private PamSpinner<Double> preampGain;
private ComboBox<String> streamers;
private ChoiceBox<DefaultHydrophone> defaultArrays;
private TextField nameField;
private PamArray currentArray;
///Labels which might change name if in air or water (i.e. hydrophone or microphone).
private Labeled depthLabel;
private Labeled depthLabel2;
private Label recieverIDLabel;
private Label recieverTypeLabel;
private Label recieverSensLabel;
private Label dBSensLabel;
private boolean ressetHydrophoneType = false;
/**
* The main holder pane.
*/
private PamBorderPane mainPane;
private InterpChoicePane interpPane;
private ComboBox<String> defaultHydro;
//create the dialog
public HydrophoneSettingsPane() {
super(null);
PamVBox holderPane = new PamVBox();
holderPane.setSpacing(5);
recieverIDLabel = new Label("General");
PamGuiManagerFX.titleFont2style(recieverIDLabel);
Label coOrdLabel = new Label("Coordinates");
PamGuiManagerFX.titleFont2style(coOrdLabel);
Label interpLabel = new Label("Interpolation");
PamGuiManagerFX.titleFont2style(interpLabel);
interpPane = new InterpChoicePane();
PamHBox interpHolder = new PamHBox();
interpHolder.setSpacing(5);
interpHolder.setAlignment(Pos.CENTER_LEFT);
interpHolder.getChildren().addAll(new Label("Method"), interpPane);
holderPane.getChildren().addAll(recieverIDLabel, createGeneralPane(), coOrdLabel, createPositionPane(), interpLabel, interpHolder);
mainPane = new PamBorderPane();
mainPane.setCenter(holderPane);
}
//
// public Boolean getParams(){
// array.nameProperty().setValue(nameField.getText());
// array.hArrayTypeProperty().setValue(arrayType.getValue());
// try {
// array.xPosProperty().setValue(Double.valueOf(xPos.getText()));
// array.yPosProperty().setValue(Double.valueOf(yPos.getText()));
// array.zPosProperty().setValue(Double.valueOf(zPos.getText()));
// }
// catch (Exception e){
// System.err.println("Invalid field in Array Dialog");
// return false;
// }
// return true;
// }
//
// public void setParams(Hydrophone hydrophone){
//
// iD.setText(String.format("%d", hydrophone.getID()));
// streamers.getItems().clear();
//
// //set thre text values for the recieevrs.
//// setRecieverLabelText();
// if (currentArray != null) {
// Streamer s;
// for (int i = 0; i < currentArray.getNumStreamers(); i++) {
// s = currentArray.getStreamer(i);
// streamers.getItems().add(String.format("Streamer %d, x=%3.1f", i, s.getX()));
// }
// }
// if (hydrophone.getStreamerId() < currentArray.getNumStreamers()) {
// streamers.getSelectionModel().select(hydrophone.getStreamerId());
// }
// hSens.setText(String.format("%.1f", hydrophone.getSensitivity()-PamController.getInstance().getGlobalMediumManager().getdBSensOffset()));
// preampGain.setText(String.format("%.1f", hydrophone.getPreampGain()));
// // bandwidth0.setText(String.format("%.1f", hydrophone.getBandwidth()[0]));
// // bandwidth1.setText(String.format("%.1f", hydrophone.getBandwidth()[1]));
//
//
//// this.array=array;
//// nameField.setText(hydrophone.getType());
// parentArrayComboBox.setValue(array.get);
//
// //attachmentComboBox.setItems(ArrayModelControl.getInstance().getArrays());
// parentArrayComboBox.setValue(array.parentHArrayProperty().getValue());
//
// xPos.setText(Double.toString(hydrophone.);
// yPos.setText(Double.toString(array.yPosProperty().get()));
// zPos.setText(Double.toString(array.zPosProperty().get()));
//
// createArrayPane(array);
//
// }
/**
* Set the receiver labels depending on whether air or water is being used.
*/
private void setGeneralInfoLabelText() {
String recieverString = PamController.getInstance().getGlobalMediumManager().getRecieverString();
String dbSens = PamController.getInstance().getGlobalMediumManager().getdBSensString();
recieverIDLabel.setText(recieverString+ " ID Info");
recieverTypeLabel.setText(recieverString + " type ");
recieverSensLabel.setText(recieverString + " sens ");
dBSensLabel.setText(dbSens);
}
/**
* Set the receiver labels depending on whether air or water is being used.
*/
private void setCoordsText() {
String recieverDepthString = PamController.getInstance().getGlobalMediumManager().getZString();
depthLabel.setText(recieverDepthString + " ");
switch (PamController.getInstance().getGlobalMediumManager().getCurrentMedium()) {
case Air:
depthLabel2.setText(" m (height above streamer)");
break;
case Water:
depthLabel2.setText(" m (depth below streamer)");
break;
}
}
/**
* Create the pane to allow users to change the position of hydrophones
*/
private Pane createGeneralPane() {
PamGridPane mainControls=new PamGridPane();
mainControls.setHgap(5);
mainControls.setVgap(5);
int gridy = 0;
Label parentArrayLabel = new Label("Parent Array");
parentArrayLabel.setAlignment(Pos.CENTER_LEFT);
mainControls.add(parentArrayLabel, 0, gridy);
streamers = new ComboBox<String>();
mainControls.add(streamers, 1, gridy);
gridy++;
mainControls.add(recieverTypeLabel = new Label(""), 0, gridy);
recieverTypeLabel.setAlignment(Pos.CENTER_LEFT);
defaultHydro = new ComboBox<String>();
for (int i=0; i<DefaultHydrophone.values().length; i++) {
defaultHydro.getItems().add(DefaultHydrophone.values()[i].toString());
}
defaultHydro.getItems().add(0, "User defined");
defaultHydro.getSelectionModel().select(0);
defaultHydro.setOnAction((action)->{
//don't want to trigger this if we are programtically setting it back
if (defaultHydro.getSelectionModel().getSelectedIndex() <= 0 || ressetHydrophoneType) {
//do nothing.
return;
}
ressetHydrophoneType=true;
hSens.getValueFactory().setValue(Double.valueOf(DefaultHydrophone.values()[defaultHydro.getSelectionModel().getSelectedIndex()-1].getSens()));
preampGain.getValueFactory().setValue(Double.valueOf(DefaultHydrophone.values()[defaultHydro.getSelectionModel().getSelectedIndex()-1].getGain()));
ressetHydrophoneType=false;
});
mainControls.add(defaultHydro, 1, gridy);
gridy++;
mainControls.add(recieverSensLabel = new Label(""), 0, gridy);
recieverSensLabel.setAlignment(Pos.CENTER_LEFT);
hSens = new PamSpinner<Double>(-Double.MAX_VALUE, Double.MAX_VALUE, -200., 1.);
hSens.setEditable(true);
hSens.valueProperty().addListener((obs, oldval, newVal)->{
if (ressetHydrophoneType) return;
ressetHydrophoneType = true; //make sure we don't trigger anything when resetting the combo box
defaultHydro.getSelectionModel().select(0);
ressetHydrophoneType= false;
});
mainControls.add(hSens, 1, gridy);
mainControls.add(dBSensLabel = new Label(""), 2, gridy);
gridy++;
Label preAmpLabel = new Label("Preamplifier gain");
mainControls.add(preAmpLabel, 0, gridy);
preAmpLabel.setAlignment(Pos.CENTER_LEFT);
preampGain =new PamSpinner<Double>(-Double.MAX_VALUE, Double.MAX_VALUE, 0., 1.);
preampGain.valueProperty().addListener((obs, oldval, newVal)->{
if (ressetHydrophoneType) return;
ressetHydrophoneType = true;//make sure we don't trigger anything when resetting the combo box
defaultHydro.getSelectionModel().select(0);
ressetHydrophoneType= false;
});
preampGain.setEditable(true);
mainControls.add(preampGain, 1, gridy);
mainControls.add(new Label("dB"), 2, gridy);
ColumnConstraints col1 = new ColumnConstraints();
col1.setMinWidth(COLUMN_0_WIDTH);
col1.setMaxWidth(COLUMN_0_WIDTH);
mainControls.getColumnConstraints().addAll(col1);
setGeneralInfoLabelText();
return mainControls;
}
/**
* Create the pane to allow users to change the position of hydrophones
*/
private Pane createPositionPane(){
double sectionPadding=15;
PamVBox mainControls=new PamVBox();
mainControls.setSpacing(5);
Insets h;
Label nameLabel=new Label("Array Name");
nameLabel.setPadding(new Insets(5,0,0,0));
nameField=new TextField();
//parent array.
Label parentArrayLabel=new Label("Parent Streamer");
parentArrayLabel.setPadding(new Insets(sectionPadding,0,0,0));
PamGridPane positionPane = new PamGridPane();
positionPane.setHgap(5);
positionPane.setVgap(5);
ColumnConstraints rc = new ColumnConstraints(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE, MAX_TEXTFIELD_WIDTH);
//this sets all text fields to the correct width - but of naff hack but what grid pane needs to work.
for (int i=1; i<5; i++) {
positionPane.getColumnConstraints().add(rc);
}
double maxWidth =10;
xPos=new TextField();
xPos.textProperty().addListener((obsVal, oldVal, newVal)->{
notifySettingsListeners();
});
xPos.setMaxWidth(maxWidth);
addTextValidator(xPos, "x position", validator);
yPos=new TextField();
yPos.setMaxWidth(maxWidth);
addTextValidator(yPos, "y position", validator);
yPos.textProperty().addListener((obsVal, oldVal, newVal)->{
notifySettingsListeners();
});
zPos=new TextField();
zPos.setMaxWidth(maxWidth);
zPos.textProperty().addListener((obsVal, oldVal, newVal)->{
notifySettingsListeners();
});
addTextValidator(zPos, "z position", validator);
depthLabel = new Label("Depth");
depthLabel.setAlignment(Pos.CENTER);
xPosErr=new TextField();
xPosErr.setMaxWidth(50);
addTextValidator(xPosErr, "x error",validator);
yPosErr=new TextField();
yPosErr.setMaxWidth(50);
addTextValidator(yPosErr, "y error",validator);
zPosErr=new TextField();
zPosErr.setMaxWidth(50);
depthLabel2 = new Label(""); //changes with air or water mode.
depthLabel2.setAlignment(Pos.CENTER);
addTextValidator(zPosErr, "z error", validator);
int col=0;
int row =0;
Label xLabel = new Label("x");
xLabel.setAlignment(Pos.CENTER);
Label yLabel = new Label("y");
yLabel.setAlignment(Pos.CENTER);
col=1;
positionPane.add(xLabel, col++, row);
positionPane.add(yLabel, col++, row);
positionPane.add(depthLabel, col++, row);
col=0;
row++;
Label positionLabel = new Label("Position");
positionPane.add(positionLabel, col++, row);
positionPane.add(xPos, col++, row);
positionPane.add(yPos, col++, row);
positionPane.add(zPos, col++, row);
positionPane.add(new Label("(m)"), col++, row);
col=0;
row++;
Label errLabel = new Label("Error");
positionPane.add(errLabel, col++, row);
positionPane.add(xPosErr, col++, row);
positionPane.add(yPosErr, col++, row);
positionPane.add(zPosErr, col++, row);
positionPane.add(new Label("(m)"), col++, row);
// positionPane.add(new Label("\u00B1"), col, 2);
// positionPane.add(xPosErr, col, 3);
// positionPane.add(new Label("m (right of streamer)"), col, 5);
col++;
// Label yLabel = new Label("y");
// yLabel.setAlignment(Pos.CENTER);
// positionPane.add(yLabel, col, 0);
// positionPane.add(yPos, col, 1);
// positionPane.add(new Label("\u00B1"), col, 2);
// positionPane.add(yPosErr, col, 3);
// positionPane.add(new Label("m (ahead of streamer)"), col, 4);
// col++;
//
//
// positionPane.add(depthLabel, col, 0);
// positionPane.add(zPos, col, 1);
// positionPane.add(new Label("\u00B1"), col, 2);
// positionPane.add(zPosErr, col, 3);
// positionPane.add(depthLabel2, col, 4);
// ColumnConstraints col1 = new ColumnConstraints();
// col1.setHgrow(Priority.ALWAYS);
// positionPane.getColumnConstraints().add(col1);
// Label positionLabel = new Label("Coordinates");
// PamGuiManagerFX.titleFont2style(positionLabel);
mainControls.getChildren().addAll(positionPane);
ColumnConstraints col1 = new ColumnConstraints();
col1.setMinWidth(COLUMN_0_WIDTH);
col1.setMaxWidth(COLUMN_0_WIDTH);
positionPane.getColumnConstraints().addAll(col1);
setCoordsText();
return mainControls;
}
/**
* Creates a text filed and adds a validator to check that the input is OK.
* @return
*/
protected static void addTextValidator(TextField userTextField, String description, Validator validator) {
//userTextField.setPrefColumnCount(8);
validator.createCheck()
.dependsOn(description, userTextField.textProperty())
.withMethod(c -> {
String posVal = c.get(description);
/**
* Ok, this is weird. So if the c.error is called then somehow it messes up
* the sizing of the pane i.e. it does not resize..
*/
try {
if (posVal.isEmpty() || Double.valueOf(posVal)==null) {
c.error("The input for " + description + " is invalid");
}
}
catch (Exception e) {
c.error("The input for " + description + " is invalid");
}
})
.decorates(userTextField).immediate();
}
@Override
public void setParams(Hydrophone hydrophone) {
//parent array stuff.
//iD.setText(String.format("%d", hydrophone.getID()));
streamers.getItems().clear();
//set thre text values for the recieevrs.
setGeneralInfoLabelText();
if (currentArray != null) {
Streamer s;
for (int i = 0; i < currentArray.getNumStreamers(); i++) {
s = currentArray.getStreamer(i);
streamers.getItems().add(String.format("Streamer %d, x=%3.1f", i, s.getX()));
}
}
if (hydrophone.getStreamerId() < currentArray.getNumStreamers()) {
streamers.getSelectionModel().select(hydrophone.getStreamerId());
}
//hydrophone stuff
hSens.getValueFactory().setValue(hydrophone.getSensitivity()-PamController.getInstance().getGlobalMediumManager().getdBSensOffset());
preampGain.getValueFactory().setValue(hydrophone.getPreampGain());
double zCoeff = PamController.getInstance().getGlobalMediumManager().getZCoeff();
setCoordsText();
interpPane.setSelection(currentArray.getHydrophoneInterpolation());
xPos.setText(Double.toString(hydrophone.getX()));
yPos.setText(Double.toString(hydrophone.getY()));
zPos.setText(Double.toString(zCoeff*hydrophone.getZ()));
xPosErr.setText(Double.toString(hydrophone.getdX()));
yPosErr.setText(Double.toString(hydrophone.getdY()));
zPosErr.setText(Double.toString(hydrophone.getdZ()));
}
@Override
public Hydrophone getParams(Hydrophone hydrophone) {
double zCoeff = PamController.getInstance().getGlobalMediumManager().getZCoeff();
try {
//hydrophone.setID(Integer.valueOf(iD.getText()));
//hydrophone.setType(type.getText());
hydrophone.setStreamerId(streamers.getSelectionModel().getSelectedIndex());
hydrophone.setSensitivity(hSens.getValue()+PamController.getInstance().getGlobalMediumManager().getdBSensOffset());
hydrophone.setPreampGain(preampGain.getValue());
// double[] bw = new double[2];
// bw[0] = Double.valueOf(bandwidth0.getText());
// bw[1] = Double.valueOf(bandwidth1.getText());
// hydrophone.setBandwidth(bw);
hydrophone.setX(Double.valueOf(xPos.getText()));
hydrophone.setY(Double.valueOf(yPos.getText()));
hydrophone.setZ(zCoeff*Double.valueOf(zPos.getText()));
hydrophone.setdX(Double.valueOf(xPosErr.getText()));
hydrophone.setdY(Double.valueOf(yPosErr.getText()));
hydrophone.setdZ(Double.valueOf(zPosErr.getText()));
int hi = interpPane.getSelection();
if (hi >= 0) {
this.currentArray.setHydrophoneInterpolation(interpPane.getSelectedInterpType());
}
}
catch (Exception Ex) {
System.err.println("There is a problem with one of the parameters in the hydrophone panel");
return null;
}
return hydrophone;
}
/**
* Set the current array associated with the hydrophone.
* @param currentArray - the current array.
*/
public void setCurrentArray(PamArray currentArray) {
this.currentArray= currentArray;
}
public void setRecieverLabels() {
setGeneralInfoLabelText();
}
}

View File

@ -0,0 +1,412 @@
package Array.layoutFX;
import java.util.ArrayList;
import java.util.List;
import Array.Hydrophone;
import Array.PamArray;
import PamController.PamController;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Dialog;
import javafx.scene.control.TableCell;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.util.Callback;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.IntegerStringConverter;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamColorsFX;
import pamViewFX.fxNodes.flipPane.PamFlipPane;
import pamViewFX.fxNodes.table.TableSettingsPane;
/**
* Table which allows users to add and edit hydrophones.
*
* @author Jamie Macaulay
*
*/
public class HydrophonesPane extends PamBorderPane {
static final double defaultx = 0.;
static final double defaulty = 0.;
static final double defaultz = 0.;
static final double defaultxErr = 0.;
static final double defaultyErr = 0.;
static final double defaultzErr = 0.;
static final String defaulttype = "Unknown";
static final double defaultsensitivity = -201;
/**
* Reference to the current array
*/
protected PamArray currentArray;
/**
* The current hydrophone data.
*/
private HydrophoneProperty currentHydrophoneData;
/**
* A list of all the current hydrophones.
*/
ObservableList<HydrophoneProperty> hydrophoneList = FXCollections.observableArrayList();
/**
* The hydrophone array table.
*/
private HydrophoneTable tableArrayPane;
private PamFlipPane pamFlipePane;
/**
* Settings pane for a single hydrophone.
*/
private HydrophoneSettingsPane hydrophonePane = new HydrophoneSettingsPane();
/**
* A list of listeners which are called whenever a hydrophone is added removed or changed.
*/
public ArrayList<ArrayChangeListener> hydrophoneChangeListeners = new ArrayList<ArrayChangeListener>();
public HydrophonesPane() {
tableArrayPane = new HydrophoneTable(hydrophoneList);
tableArrayPane.setPadding(new Insets(5,5,5,5));
pamFlipePane = new PamFlipPane();
pamFlipePane.getAdvLabel().setText(PamController.getInstance().getGlobalMediumManager().getRecieverString());
// pamFlipePane.minWidthProperty().bind(this.widthProperty());
// pamFlipePane.setStyle("-fx-background-color: green;");
((Pane) hydrophonePane.getContentNode()).setPadding(new Insets(5,5,5,15));
pamFlipePane.setAdvPaneContent(hydrophonePane.getContentNode());
pamFlipePane.setFrontContent(tableArrayPane);
pamFlipePane.getFront().setPadding(new Insets(5,5,5,10));
pamFlipePane.backButtonProperty().addListener((obsval, oldVal, newVal)->{
// System.out.println("Hello back button pressed: " + newVal.intValue());
//the flip pane
if (newVal.intValue()==PamFlipPane.OK_BACK_BUTTON) {
Hydrophone hydro = hydrophonePane.getParams(currentHydrophoneData.getHydrophone());
if (hydro==null) {
//the warning dialog is shown in the streamer settings pane
return;
}
// System.out.println("Hydro: " + currentHydrophoneData.getX().get()+ " " + currentHydrophoneData.getY().get() + " " + currentHydrophoneData.getZ().get() + " ID: " +hydro.getID());
// System.out.println("Hydro err: " + currentHydrophoneData.getXErr().get()+ " " + currentHydrophoneData.getYErr().get() + " " + currentHydrophoneData.getZErr().get());
currentHydrophoneData.setHydrophone(hydro);
notifyHydrophoneListeners(currentHydrophoneData);
//need to refresh table to show symbol.
tableArrayPane.getTableView().refresh();
//
// System.out.println("Table size: " + tableArrayPane.getTableView().getItems().size());
// for (int i=0; i<tableArrayPane.getTableView().getItems().size(); i++) {
// System.out.println("Item : " + tableArrayPane.getTableView().getItems().get(i) + " " + currentHydrophoneData);
// }
}
});
this.setCenter(pamFlipePane);
}
/**
* Notify the hydrophone listeners of a change
* @param streamer - the changed streamer
*/
public void notifyHydrophoneListeners(HydrophoneProperty hydrophone) {
for (ArrayChangeListener listener: hydrophoneChangeListeners) {
listener.arrayChanged(ArrayChangeListener.HYDROPHONE_CHANGE, hydrophone);
}
}
/**
* Class which extends TableSettingsPane and creates a sliding pane instead of a dialog when an item is added.
* @author Jamie Macaulay
*
*/
class HydrophoneTable extends TableSettingsPane<HydrophoneProperty> {
/**
* The z table
*/
private TableColumn<HydrophoneProperty, Number> z;
public HydrophoneTable(ObservableList<HydrophoneProperty> hydrophoneData) {
super(hydrophoneData);
z = new TableColumn<HydrophoneProperty,Number>("depth");
z.setCellValueFactory(cellData -> cellData.getValue().getZ().multiply(PamController.getInstance().getGlobalMediumManager().getZCoeff()));
z.setEditable(true);
//need to set up all the rows.
TableColumn<HydrophoneProperty,Integer> hydroID = new TableColumn<HydrophoneProperty,Integer>("ID");
hydroID.setCellValueFactory(cellData -> cellData.getValue().getID().asObject());
hydroID.setEditable(false);
// Default cell factory provides text field for editing and converts text in text field to int.
Callback<TableColumn<HydrophoneProperty, Integer>, TableCell<HydrophoneProperty, Integer>> defaultCellFactory =
TextFieldTableCell.forTableColumn(new IntegerStringConverter());
// Cell factory implementation that uses default cell factory above, and augments the implementation
// by updating the value of the looked-up color cell-selection-color for the cell when the item changes:
Callback<TableColumn<HydrophoneProperty, Integer>, TableCell<HydrophoneProperty, Integer>> cellFactory = col -> {
TableCell<HydrophoneProperty, Integer> cell = defaultCellFactory.call(col);
cell.itemProperty().addListener((obs, oldValue, newValue) -> {
// System.out.println("Hello set colour: " + newValue);
if (newValue == null) {
cell.setStyle("cell-selection-color: -fx-selection-bar ;");
} else {
Color color = createColor(newValue.intValue());
String formattedColor = formatColor(color);
// cell.setStyle("cell-selection-color: "+ formattedColor + " ;");
cell.setStyle("-fx-background: "+ formattedColor + " ;");
cell.setStyle("-fx-background-color: "+ formattedColor + " ;");
// System.out.println("Hello set style: " + formattedColor);
}
});
return cell;
};
hydroID.setCellFactory(cellFactory);
TableColumn<HydrophoneProperty,Number> x = new TableColumn<HydrophoneProperty,Number>("x");
x.setCellValueFactory(cellData -> cellData.getValue().getX());
x.setEditable(true);
TableColumn<HydrophoneProperty,Number> y = new TableColumn<HydrophoneProperty,Number>("y");
y.setCellValueFactory(cellData -> cellData.getValue().getY());
y.setEditable(true);
TableColumn posColumn=new TableColumn("Position (m)");
posColumn.getColumns().addAll(x, y, z);
TableColumn<HydrophoneProperty,Number> xErr = new TableColumn<HydrophoneProperty,Number>("x");
xErr.setCellValueFactory(cellData -> cellData.getValue().getXErr());
xErr.setEditable(true);
TableColumn<HydrophoneProperty,Number> yErr = new TableColumn<HydrophoneProperty,Number>("y");
yErr.setCellValueFactory(cellData -> cellData.getValue().getYErr());
yErr.setEditable(true);
TableColumn<HydrophoneProperty,Number> zErr = new TableColumn<HydrophoneProperty,Number>("z");
zErr.setCellValueFactory(cellData -> cellData.getValue().getZErr());
zErr.setEditable(true);
TableColumn errorColumn=new TableColumn("Errors (m)");
errorColumn.getColumns().addAll(xErr, yErr, zErr);
getTableView().getColumns().addAll(hydroID, posColumn, errorColumn);
}
// Create color based on int value. Just use value as hue, full saturation and brightness:
private Color createColor(int i) {
//get channel colour and add a bit of transparancy to make less abnoxious
return PamColorsFX.getInstance().getChannelColor(i).deriveColor(1, 1, 1, 0.5);
// return Color.hsb(x, 1.0, 1.0);
}
// Format color as string for CSS (#rrggbb format, values in hex).
private String formatColor(Color c) {
int r = (int) (255 * c.getRed());
int g = (int) (255 * c.getGreen());
int b = (int) (255 * c.getBlue());
return String.format("#%02x%02x%02x", r, g, b);
}
@Override
public void dialogClosed(HydrophoneProperty data) {
System.out.println("Get hydrophone paramters");
Hydrophone hydro = hydrophonePane.getParams(data.getHydrophone());
data.setHydrophone(hydro);
}
@Override
public Dialog<HydrophoneProperty> createSettingsDialog(HydrophoneProperty data) {
//we do not use dialogs here- sliding pane instead.
// setClassifierPane(data);
pamFlipePane.flipToBack();
return null;
}
@Override
public void editData(HydrophoneProperty data){
// setClassifierPane(data);
pamFlipePane.getAdvLabel().setText("Hydrophone " + data.getID().get() + " Settings");
hydrophonePane.setCurrentArray(currentArray);
hydrophonePane.setParams(data.getHydrophone());
currentHydrophoneData = data;
pamFlipePane.flipToBack();
}
private PamArray getCurrentArray() {
return currentArray;
}
/**
* Get the button which closes the hiding pane.
* @return button which closes the hiding pane.
*/
public Button getFlipPaneCloseButton() {
return pamFlipePane.getBackButton();
}
@Override
public void createNewData(){
HydrophoneProperty hydrophone = createDefaultHydrophoneProperty(hydrophoneList.size());
//create a new classifier.
hydrophoneList.add(hydrophone);
notifyHydrophoneListeners(hydrophone);
}
@Override
public void deleteData(HydrophoneProperty data){
super.deleteData(data);
//the ID number for hydrophone sis actually important for where they are in the list. Bit a legacy issue but no
//point in messes everything up to fix. So, when a hydrophone is deleted must update all the ID numbers.
updateIDNumbers();
notifyHydrophoneListeners(data);
}
/**
* Update the ID numbers.
*/
private void updateIDNumbers() {
for (int i=0; i<getData().size(); i++){
getData().get(i).id.set(i);
}
}
private HydrophoneProperty createDefaultHydrophoneProperty(int id) {
Hydrophone hydrophone = new Hydrophone(id, defaultx, defaulty,defaultz, defaultxErr, defaultyErr, defaultzErr, defaulttype, defaultsensitivity,
null, 0. );
return new HydrophoneProperty(hydrophone);
}
public TableColumn<HydrophoneProperty, Number> getZColumn() {
return z;
}
/**
* Get the current streamers.
* @return the current streamers.
*/
public ObservableList<HydrophoneProperty> getHydrophones() {
return getData();
}
}
public void setParams(PamArray currentArray) {
this.currentArray=currentArray;
tableArrayPane.getHydrophones().clear();
for (int i=0; i<currentArray.getHydrophoneCount(); i++) {
tableArrayPane.getHydrophones().add(new HydrophoneProperty(currentArray.getHiddenHydrophone(i)));
}
//update ID numbers just incase.
tableArrayPane.updateIDNumbers();
}
public synchronized PamArray getParams(PamArray currParams) {
currParams.clearArray();
Hydrophone hydrophone;
for (int i=0; i<tableArrayPane.getHydrophones().size(); i++) {
hydrophone = tableArrayPane.getHydrophones().get(i).getHydrophone();
hydrophone.setID(i);
currParams.addHydrophone(hydrophone);
}
return currParams;
}
/**
* Get the current hydrophone list.
* @return the current hydrophone list.
*/
public ObservableList<HydrophoneProperty> getHydrophoneList() {
return hydrophoneList;
}
public void setHydrophoneList(ObservableList<HydrophoneProperty> hydrophoneList) {
this.hydrophoneList = hydrophoneList;
}
public void setRecieverLabels() {
tableArrayPane.getZColumn().setText(PamController.getInstance().getGlobalMediumManager().getZString());
hydrophonePane.setRecieverLabels();
}
/**
* Add a listener which is called whenever a hydrophone is added, removed or changed.
* @param e - the listener to add
*/
public void addStreamerListener(ArrayChangeListener e) {
hydrophoneChangeListeners.add(e);
}
/**
* Select the current hydrophone in table.
*/
public void selectHydrophone(Hydrophone hydrophone) {
//select the current hydrophone in the table
tableArrayPane.getTableView().getSelectionModel().select(hydrophone.getID());
}
public void setCurrentArray(PamArray currentArray) {
this.currentArray=currentArray;
}
/**
* Get the hydrophone interpolation. Note that this is stored in the
* currentArray because the interpolator must be the same for all hydrophones.
*
* @return the inteprolation selection.
*/
public int getHydrophoneInterp() {
return currentArray.getHydrophoneInterpolation();
}
}

View File

@ -0,0 +1,104 @@
package Array.layoutFX;
import pamViewFX.fxNodes.pamDialogFX.PamDialogFX;
import javafx.scene.control.ChoiceBox;
import javafx.util.StringConverter;
/**
* Choice box which allows selection of interpolation options.
*
* @author Jamie Macaulay
*
*/
public class InterpChoicePane extends InterpSettingsPane {
/**
* Interp choice box.
*/
private ChoiceBox<Integer> interpChoiceBox;
public InterpChoicePane() {
interpChoiceBox = new ChoiceBox<Integer>();
interpChoiceBox.getItems().addAll(interpChoice);
interpChoiceBox.setMaxWidth(Double.MAX_VALUE);
interpChoiceBox.setConverter(new StringConverter<>() {
@Override
public String toString(Integer item) {
if (item ==null) return "null";
return getInterpString(item);
}
@Override
public Integer fromString(String unused) {
throw new UnsupportedOperationException();
}
});
this.setCenter(interpChoiceBox);
}
public void setSelection(int option) {
System.out.println("Select interp option: " + option);
interpChoiceBox.getSelectionModel().select(Integer.valueOf(option));
// useLatest.setSelected(option == PamArray.ORIGIN_USE_LATEST);
// useInterpolate.setSelected(option == PamArray.ORIGIN_INTERPOLATE);
// usePrevious.setSelected(option == PamArray.ORIGIN_USE_PRECEEDING);
}
public int getSelection() {
int sel = getSelectedInterpType();
if (((1<<sel) & allowedValues) == 0) {
PamDialogFX.showWarning("The selected interpolation is not available with the selected reference position");
return -1;
}
else {
return sel;
}
}
@Override
protected void enableControls() {
//get the current selection
Integer item = interpChoiceBox.getSelectionModel().getSelectedItem();
//clear items
interpChoiceBox.getItems().clear();
//set allowed values
for (int i=0; i<interpChoice.length ; i++) {
if ((allowedValues & (1<<interpChoice[i])) != 0){
interpChoiceBox.getItems().add(interpChoice[i]);
}
}
//reselect the previously selected item if possible.
if (interpChoiceBox.getItems().contains(item)) {
interpChoiceBox.getSelectionModel().select(item);
}
else {
interpChoiceBox.getSelectionModel().select(0);
}
}
@Override
protected int getSelectedInterpType() {
Integer choice = interpChoiceBox.getSelectionModel().getSelectedItem();
if (choice == null) {
return -1;
}
else return choice;
}
}

View File

@ -0,0 +1,160 @@
package Array.layoutFX;
import Array.PamArray;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamGridPane;
import pamViewFX.fxNodes.pamDialogFX.PamDialogFX;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Pane;
import javafx.scene.control.ToggleGroup;
/**
* Radio buttons which allow selection of interpolation options.
*
* @author Jamie Macaulay
*
*/
public class InterpSettingsPane extends PamBorderPane {
/**
* Interp choice.
*/
public static Integer[] interpChoice = new Integer[] {PamArray.ORIGIN_USE_LATEST,
PamArray.ORIGIN_INTERPOLATE, PamArray.ORIGIN_USE_PRECEEDING};
private RadioButton[] radioButton;
protected int allowedValues = 0xFF; // bitmap of banned values !
public InterpSettingsPane() {
this.setCenter(createInterpPane());
}
protected Pane createInterpPane() {
PamGridPane gridPane = new PamGridPane();
gridPane.setVgap(5);
int gridy=0;
radioButton = new RadioButton[interpChoice.length];
ToggleGroup group = new ToggleGroup();
for (int i=0; i<radioButton.length ; i++) {
gridPane.add(radioButton[i] = new RadioButton( getInterpString(interpChoice[i])), 0, gridy);
radioButton[i].setTooltip(new Tooltip( getInterpTip(interpChoice[i])));
gridy++;
radioButton[i].setToggleGroup(group);
}
return gridPane;
}
/**
* Get a description of the interpolation type for an interp-type flag.
* @param interpType - the interpolation type flag.
* @return a description of that interpolation methid.
*/
public String getInterpString(int interpType) {
String description = null;
switch ( interpType) {
case PamArray.ORIGIN_USE_LATEST:
description = "Use only the latest value";
break;
case PamArray.ORIGIN_INTERPOLATE:
description = "Interpolate between values";
break;
case PamArray.ORIGIN_USE_PRECEEDING:
description = "Use the location for the time preceeding each data unit";
break;
}
return description;
}
/**
* Get a description of the interpolation type for an interp-type flag.
* @param interpType - the interpolation type flag.
* @return a description of that interpolation methid.
*/
public String getInterpTip(int interpType) {
String description = null;
switch ( interpType) {
case PamArray.ORIGIN_USE_LATEST:
description = "Select this option if you have a simple static array in a single location for the entire data set";
break;
case PamArray.ORIGIN_INTERPOLATE:
description = "Select this option if you are storing multiple locations for slowely moving (i.e. not quite fixed) devices";
break;
case PamArray.ORIGIN_USE_PRECEEDING:
description = "Select this option if you have devices which are periodically moved from one spot to another";
break;
}
return description;
}
public void setSelection(int option) {
for (int i=0; i<interpChoice.length; i++) {
radioButton[i].setSelected(option == interpChoice[i]);
}
}
public int getSelection() {
int sel = getSelectedInterpType();
if (((1<<sel) & allowedValues) == 0) {
PamDialogFX.showWarning("The selected interpolation is not available with the selected reference position");
return -1;
}
else {
return sel;
}
}
/**
* @return the allowedValues
*/
protected int getAllowedValues() {
return allowedValues;
}
/**
* @param allowedValues the allowedValues to set
*/
protected void setAllowedValues(int allowedValues) {
this.allowedValues = allowedValues;
enableControls();
}
protected void enableControls() {
for (int i=0; i<interpChoice.length; i++) {
radioButton[i].setDisable((allowedValues & (1<<interpChoice[i])) == 0);
}
}
protected int getSelectedInterpType() {
for (int i=0; i<interpChoice.length; i++) {
if (radioButton[i].isSelected()) {
return interpChoice[i];
}
radioButton[i].setDisable((allowedValues & (1<<interpChoice[i])) == 0);
}
return -1;
}
}

View File

@ -0,0 +1,119 @@
package Array.layoutFX;
import Array.PamArray;
import PamController.SettingsPane;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.validator.PamValidator;
/**
* Pane for settings some basic environmental variables.
*/
public class PropogationPane extends SettingsPane<PamArray> {
Pane mainPane;
private TextField speedOfSound;
private TextField speedOfSoundError;
private PamValidator validator;
public PropogationPane() {
super(null);
validator= new PamValidator();
mainPane = createEnvironmentPane();
}
/**
* Create the pane for setting propogation conditions.
* @return the pane with controls to change environmental variables.
*/
private Pane createEnvironmentPane() {
speedOfSound = new TextField();
speedOfSound.setPrefColumnCount(6);
validator.createCheck()
.dependsOn("speed_of_sound", speedOfSound.textProperty())
.withMethod(c -> {
try {
String posVal = c.get("speed_of_sound");
if (posVal.isEmpty() || Double.valueOf(posVal)==null) {
c.error("The input for speed of sound is invalid");
}
}
catch (Exception e) {
c.error("The input for speed of sound is invalid");
}
})
.decorates(speedOfSound).immediate();
speedOfSoundError = new TextField();
speedOfSoundError.setPrefColumnCount(4);
validator.createCheck()
.dependsOn("speed_of_sound_error", speedOfSoundError.textProperty())
.withMethod(c -> {
try {
String posVal = c.get("speed_of_sound_error");
if (posVal.isEmpty() || Double.valueOf(posVal)==null) {
c.error("The input for speed of sound error is invalid");
}
}
catch (Exception e) {
c.error("The input for speed of sound is invalid");
}
})
.decorates(speedOfSoundError).immediate();
PamHBox hBox = new PamHBox();
hBox.setSpacing(5);
hBox.setAlignment(Pos.CENTER);
hBox.getChildren().addAll(new Label("Speed of sound"), speedOfSound, new Label("\u00B1"), speedOfSoundError, new Label("m/s"));
return hBox;
}
@Override
public PamArray getParams(PamArray currParams) {
if (validator.containsErrors()) return null;
currParams.setSpeedOfSound(Double.valueOf(speedOfSound.getText()));
currParams.setSpeedOfSoundError(Double.valueOf(speedOfSoundError.getText()));
return currParams;
}
@Override
public void setParams(PamArray input) {
//set the current params.
speedOfSound.setText(String.valueOf(input.getSpeedOfSound()));
speedOfSoundError.setText(String.valueOf(input.getSpeedOfSoundError()));
}
@Override
public String getName() {
return "Propogation pane";
}
@Override
public Node getContentNode() {
return mainPane;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,162 @@
package Array.layoutFX;
import java.util.ArrayList;
import Array.sensors.ArrayParameterType;
import Array.sensors.ArraySensorDataBlock;
import Array.sensors.ArraySensorDataUnit;
import Array.sensors.ArraySensorFieldType;
import PamController.PamController;
import PamguardMVC.PamDataBlock;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Tooltip;
public class SensorSourcePane {
private ArraySensorFieldType sensorType;
private ComboBox<String> sensorDropDown;
private ArrayList<PamDataBlock> currentBlocks;
private boolean defaultOption;
private boolean fixedOption;
private int nSpecials;
private ArrayParameterType[] specialTypes = new ArrayParameterType[2];
public SensorSourcePane(ArraySensorFieldType sensorType, boolean fixedOption, boolean defaultOption) {
this.sensorType = sensorType;
this.fixedOption = fixedOption;
this.defaultOption = defaultOption;
sensorDropDown = new ComboBox<>();
fillDropDown();
sensorDropDown.setTooltip(new Tooltip("Sensor updates for " + sensorType.toString()));
}
/**
* Get the type of sensor.
* @return the type of sensor.
*/
public ArraySensorFieldType getSensorType() {
return sensorType;
}
public void setOnAction(EventHandler<ActionEvent> e) {
sensorDropDown.setOnAction(e);
}
public void fillDropDown() {
currentBlocks = getDataBlocks();
sensorDropDown.getItems().clear();
nSpecials = 0;
if (fixedOption) {
sensorDropDown.getItems().add("Fixed Value");
specialTypes[nSpecials++] = ArrayParameterType.FIXED;
}
if (defaultOption) {
sensorDropDown.getItems().add("Default value");
specialTypes[nSpecials++] = ArrayParameterType.DEFAULT;
}
for (PamDataBlock aBlock : currentBlocks) {
sensorDropDown.getItems().add(aBlock.getDataName());
}
}
/**
* Set the type of parameter being used, fixed, default or sensor
* @param paramType
*/
public void setParameterType(ArrayParameterType paramType) {
for (int i = 0; i < nSpecials; i++) {
if (paramType == specialTypes[i]) {
sensorDropDown.getSelectionModel().select(i);
return;
}
}
}
/**
* Get the type of parameter being used, fixed, default or sensor
* @return
*/
public ArrayParameterType getParameterType() {
int ind = sensorDropDown.getSelectionModel().getSelectedIndex();
if (ind < 0) {
return null;
}
if (ind < nSpecials) {
return specialTypes[ind];
}
return ArrayParameterType.SENSOR;
}
/**
* Set the selected datablock for sensor data. Before calling this, you should call
* fillDropDown to make sure list of blocks is up to date.
* @param aDataBlock datablock to select
* @return true if that block was selected OK, i.e. it was in the list.
*/
public boolean setDataBlock(PamDataBlock aDataBlock) {
if (currentBlocks == null) {
return false;
}
int ind = currentBlocks.indexOf(aDataBlock);
if (ind < 0) {
return false;
}
sensorDropDown.getSelectionModel().select(ind+nSpecials); // offset by 1 to allow for null option.
return true;
}
/**
*
* @return Currently selected datablock for this sensor (can be null)
*/
public PamDataBlock getDataBlock() {
int ind = sensorDropDown.getSelectionModel().getSelectedIndex();
if (ind <= 0 || currentBlocks == null) {
return null;
}
else {
return currentBlocks.get(ind-nSpecials);
}
}
/**
* Get the sensor pane.
* @return the sensor pane
*/
public Node getPane() {
return sensorDropDown;
}
/**
* Get a list of datablocks that might be able to provide info on this
* sensor field type
* @return
*/
private ArrayList<PamDataBlock> getDataBlocks() {
ArrayList<PamDataBlock> allDataBlocks = PamController.getInstance().getDataBlocks(ArraySensorDataUnit.class, true);
ArrayList<PamDataBlock> sensBlocks = new ArrayList<>();
if (allDataBlocks == null) {
return sensBlocks;
}
// go through take out the ones that support this sensor.
for (PamDataBlock aBlock : allDataBlocks) {
if (aBlock instanceof ArraySensorDataBlock == false) {
continue;
}
ArraySensorDataBlock sensBlock = (ArraySensorDataBlock) aBlock;
if (sensBlock.hasSensorField(sensorType)) {
sensBlocks.add(aBlock);
}
}
return sensBlocks;
}
}

View File

@ -0,0 +1,121 @@
package Array.layoutFX;
import Array.Streamer;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
/**
* Property class for a Streamer. Create property bindings for certain Streamer values which allows
* for much easier integration into UI components in JavaFX.
*
* @author Jamie Macaulay
*
*/
public class StreamerProperty {
/**
* The simple name property.
*/
private SimpleStringProperty name = new SimpleStringProperty();
private SimpleStringProperty reference = new SimpleStringProperty();
private SimpleStringProperty origin = new SimpleStringProperty();
/**
* Get the x, y and z.
*/
private SimpleDoubleProperty x = new SimpleDoubleProperty();
private SimpleDoubleProperty y = new SimpleDoubleProperty();
private SimpleDoubleProperty z = new SimpleDoubleProperty();
private Streamer streamer;
private SimpleIntegerProperty streamerIDProperty = new SimpleIntegerProperty();
public StreamerProperty(Streamer streamer) {
setStreamer( streamer);
}
public void setStreamer(Streamer streamer) {
this.streamer = streamer;
name.setValue(streamer.getStreamerName());
x.setValue(streamer.getX());
y.setValue(streamer.getY());
z.setValue(streamer.getZ());
streamerIDProperty.setValue(streamer.getStreamerIndex());
reference.setValue(streamer.getHydrophoneLocator() != null ? streamer.getHydrophoneLocator().getName() : "null");
origin.setValue(streamer.getHydrophoneOrigin() != null ? streamer.getHydrophoneOrigin().getName() : "null");
}
public SimpleStringProperty getName() {
return name;
}
public void setName(SimpleStringProperty name) {
this.name = name;
}
public SimpleDoubleProperty getX() {
return x;
}
public void setX(SimpleDoubleProperty x) {
this.x = x;
}
public SimpleDoubleProperty getY() {
return y;
}
public void setY(SimpleDoubleProperty y) {
this.y = y;
}
public SimpleDoubleProperty getZ() {
return z;
}
public void setZ(SimpleDoubleProperty z) {
this.z = z;
}
public Streamer getStreamer() {
return streamer;
}
/**
* Get the index property of the streamer.
* @return the streamer index.
*/
public SimpleIntegerProperty getID() {
return streamerIDProperty;
}
/**
* Get the reference property.
* @return the reference property.
*/
public SimpleStringProperty getHydrophineLocator() {
return reference;
}
/**
* Get the origin string property.
* @return the origin string property.
*/
public SimpleStringProperty getHydrophoneOrigin() {
return origin;
}
}

View File

@ -0,0 +1,752 @@
package Array.layoutFX;
import org.controlsfx.control.PopOver;
import Array.HydrophoneLocator;
import Array.HydrophoneLocators;
import Array.PamArray;
import Array.Streamer;
import Array.sensors.ArrayParameterType;
import Array.sensors.ArraySensorFieldType;
import Array.streamerOrigin.HydrophoneOriginMethod;
import Array.streamerOrigin.HydrophoneOriginMethods;
import Array.streamerOrigin.HydrophoneOriginSystem;
import Array.streamerOrigin.OriginDialogComponent;
import Array.streamerOrigin.OriginSettings;
import PamController.PamController;
import PamController.SettingsPane;
import PamUtils.LatLong;
import PamguardMVC.PamDataBlock;
import javafx.geometry.Pos;
import javafx.scene.Node;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxGlyphs.PamGlyphDude;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamButton;
import pamViewFX.fxNodes.PamGridPane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.validator.PamValidator;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import net.synedra.validatorfx.Validator;
/**
* A JavaFX settings pane for a streamer.
*
* @author Jamie Macaulay
*
*/
public class StreamerSettingsPane extends SettingsPane<Streamer> {
private final static double MAX_TEXTFIELD_WIDTH = 80;
public PamBorderPane mainPane;
/**
* Combo Box which shows which origin methods are available.
*/
private ComboBox<HydrophoneOriginSystem> originMethod;
/**
* The origin pane;
*/
private PamBorderPane originPane;
/**
* The default streamer
*/
public Streamer defaultStreamer;
/**
* The current array
*/
private PamArray currentArray;
/**
* The current origin methods
*/
private HydrophoneOriginMethod currentOriginMethod;
/*
* The current origin method pane.
*/
private Pane currentOriginComponent;
/**
* Interpolation panel
*/
private InterpChoicePane interpPane;
private TextField xPos;
private Validator validator = new PamValidator();
private TextField yPos;
private TextField zPos;
private TextField zPosErr;
private TextField xPosErr;
private Label depthLabel;
private TextField yPosErr;
private Label depthLabel2;
private TextField heading;
private TextField roll;
private TextField pitch;
private ComboBox localiserMethod;
private SensorSourcePane[] sensorComponents;
private Label depthSensorLabel;
/**
* Button for extra origin parameters.
*/
private PamButton originButton;
public StreamerSettingsPane() {
super(null);
mainPane = new PamBorderPane();
mainPane.setCenter(getStreamerPane());
}
/**
* Create the streamer pane
* @return get the pane.
*/
private Pane getStreamerPane(){
String reciever = PamController.getInstance().getGlobalMediumManager().getRecieverString();
Label label = new Label("Geo-reference Position");
PamGuiManagerFX.titleFont2style(label);
//holds advanced setings for new origin methods.
originPane = new PamBorderPane();
PopOver popOver = new PopOver();
popOver.setContentNode(originPane);
originMethod = new ComboBox<HydrophoneOriginSystem>();
originButton = new PamButton();
originButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-crosshairs-gps"));
originButton.setOnAction((a)->{
popOver.show(originButton);
});
originMethod.setMaxWidth(Double.MAX_VALUE);
PamHBox originHolder = new PamHBox();
originHolder.setSpacing(5);
originHolder.setAlignment(Pos.CENTER_LEFT);
originHolder.getChildren().addAll(originMethod,originButton);
originHolder.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(originMethod, Priority.ALWAYS);
int n = HydrophoneOriginMethods.getInstance().getCount();
for (int i = 0; i < n; i++) {
originMethod.getItems().add(HydrophoneOriginMethods.getInstance().getMethod(i));
}
Label hydroMovementLabel = new Label(reciever +" Movement Model");
//listener for when a new origin method is called.
originMethod.setOnAction((action)->{
newOriginMethod();
});
interpPane = new InterpChoicePane();
Label inteprlabel = new Label("Interpolation");
PamGuiManagerFX.titleFont2style(inteprlabel);
PamHBox interpBox = new PamHBox();
interpBox.setSpacing(5);
Label interpMethodLabel = new Label("Method");
Region spacer = new Region();
spacer.prefWidthProperty().bind(originButton.widthProperty());
interpBox.getChildren().addAll(interpMethodLabel, interpPane, spacer);
interpBox.setAlignment(Pos.CENTER_LEFT);
interpBox.setMaxWidth(Double.MAX_VALUE);
interpPane.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(interpPane, Priority.ALWAYS);
//add all stuff to the holder
PamVBox holder = new PamVBox();
holder.getChildren().addAll(label, originHolder, hydroMovementLabel, createLocatorPane(), inteprlabel, interpBox);
holder.setSpacing(5);
return holder;
}
/**
* Create the locator pane.
* @return the pane containing controls.
*/
public Pane createLocatorPane() {
localiserMethod = new ComboBox<>();
int n = HydrophoneLocators.getInstance().getCount();
for (int i = 0; i < n; i++) {
localiserMethod.getItems().add(HydrophoneLocators.getInstance().getSystem(i));
}
localiserMethod.setMaxWidth(Double.MAX_VALUE);
PamHBox loclaiserMethodHolder = new PamHBox();
loclaiserMethodHolder.setSpacing(5);
loclaiserMethodHolder.setAlignment(Pos.CENTER_LEFT);
Label spacer = new Label();
spacer.prefWidthProperty().bind(originButton.widthProperty());
loclaiserMethodHolder.getChildren().addAll(localiserMethod, spacer);
loclaiserMethodHolder.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(localiserMethod, Priority.ALWAYS);
//hydrophone position and
PamGridPane positionPane = new PamGridPane();
positionPane.setHgap(5);
positionPane.setVgap(5);
ColumnConstraints rc = new ColumnConstraints(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE, MAX_TEXTFIELD_WIDTH);
//Orientation pane.
//create data sources for sensors.
ArraySensorFieldType[] sensorFields = ArraySensorFieldType.values();
sensorComponents = new SensorSourcePane[sensorFields.length];
//EnableOrientation eo = new EnableOrientation();
for (int i = 0; i < sensorFields.length; i++) {
sensorComponents[i] = new SensorSourcePane(sensorFields[i], true, sensorFields[i] != ArraySensorFieldType.HEIGHT);
sensorComponents[i].setOnAction((e)->{
enableOrientationPane();
});
}
PamButton button = new PamButton("Sensors");
button.setGraphic(PamGlyphDude.createPamIcon("mdi2c-compass-outline", PamGuiManagerFX.iconSize));
PopOver popOver = new PopOver(createSensorPane());
popOver.setDetachable(true);
button.setOnAction((a)->{
popOver.show(button);
});
//this sets all text fields to the correct width - but of naff hack but what grid pane needs to work.
for (int i=1; i<5; i++) {
positionPane.getColumnConstraints().add(rc);
}
xPos=new TextField();
xPos.setMaxWidth(MAX_TEXTFIELD_WIDTH);
HydrophoneSettingsPane.addTextValidator(xPos, "x position", validator);
yPos=new TextField();
yPos.setMaxWidth(MAX_TEXTFIELD_WIDTH);
HydrophoneSettingsPane.addTextValidator(yPos, "y position", validator);
zPos=new TextField();
zPos.setMaxWidth(MAX_TEXTFIELD_WIDTH);
HydrophoneSettingsPane.addTextValidator(zPos, "z position", validator);
depthLabel = new Label("Depth");
depthLabel.setAlignment(Pos.CENTER);
depthSensorLabel = new Label("Depth Sensor");
depthSensorLabel.setAlignment(Pos.CENTER);
xPosErr=new TextField();
xPosErr.setMaxWidth(MAX_TEXTFIELD_WIDTH);
HydrophoneSettingsPane.addTextValidator(xPosErr, "x error", validator);
yPosErr=new TextField();
yPosErr.setMaxWidth(MAX_TEXTFIELD_WIDTH);
HydrophoneSettingsPane.addTextValidator(yPosErr, "y error", validator);
zPosErr=new TextField();
zPosErr.setMaxWidth(MAX_TEXTFIELD_WIDTH);
depthLabel2 = new Label(""); //changes with air or water mode.
depthLabel2.setAlignment(Pos.CENTER);
HydrophoneSettingsPane.addTextValidator(zPosErr, "z error", validator);
int col=0;
int row=0;
Label xLabel = new Label("x");
xLabel.setAlignment(Pos.CENTER);
Label yLabel = new Label("y");
yLabel.setAlignment(Pos.CENTER);
//Orientations
String degsLab = LatLong.deg + " ";
col=1;
positionPane.add(xLabel, col++, row);
positionPane.add(yLabel, col++, row);
positionPane.add(depthLabel, col++, row);
col++;
positionPane.add(depthSensorLabel, col++, row);
col=0;
row++;
Label positionLabel = new Label("Position");
positionPane.add(positionLabel, col++, row);
positionPane.add(xPos, col++, row);
positionPane.add(yPos, col++, row);
positionPane.add(zPos, col++, row);
positionPane.add(new Label("(m)"), col++, row);
positionPane.add(sensorComponents[ArraySensorFieldType.HEIGHT.ordinal()].getPane(), col++, row);
col=0;
row++;
Label errLabel = new Label("Error");
positionPane.add(errLabel, col++, row);
positionPane.add(xPosErr, col++, row);
positionPane.add(yPosErr, col++, row);
positionPane.add(zPosErr, col++, row);
positionPane.add(new Label("(m)"), col++, row);
//Orientation
col=1;
row++;
Label headingLabel = new Label("Heading");
headingLabel.setAlignment(Pos.CENTER);
Label pitchLabel = new Label("Pitch");
pitchLabel.setAlignment(Pos.CENTER);
Label rolllabel = new Label("Roll");
rolllabel.setAlignment(Pos.CENTER);
positionPane.add(headingLabel, col++, row);
positionPane.add(pitchLabel, col++, row);
positionPane.add(rolllabel, col++, row);
row++;
heading = new TextField();
heading.setMaxWidth(MAX_TEXTFIELD_WIDTH);
HydrophoneSettingsPane.addTextValidator(heading, "heading", validator);
pitch = new TextField();
pitch.setMaxWidth(MAX_TEXTFIELD_WIDTH);
HydrophoneSettingsPane.addTextValidator(pitch, "pitch", validator);
roll = new TextField();
roll.setMaxWidth(MAX_TEXTFIELD_WIDTH);
HydrophoneSettingsPane.addTextValidator(roll, "roll", validator);
col=0;
Label orientation = new Label("Orientation");
positionPane.add(orientation, col++, row);
positionPane.add(heading, col++, row);
positionPane.add(pitch, col++, row);
positionPane.add(roll, col++, row);
positionPane.add(new Label(degsLab), col++, row);
positionPane.add(button, col++, row);
PamVBox holder= new PamVBox();
holder.setSpacing(5);
holder.getChildren().addAll(loclaiserMethodHolder, positionPane);
return holder;
}
/**
* Enables or disables controls in the orientation pane.
*/
private void enableOrientationPane() {
for (int i=0; i<sensorComponents.length; i++) {
if (sensorComponents[i]==null || sensorComponents[i].getParameterType()==null) continue;
boolean enable = sensorComponents[i].getParameterType().equals(ArrayParameterType.FIXED);
switch (sensorComponents[i].getSensorType()) {
case HEADING:
heading.setDisable(!enable);
break;
case HEIGHT:
break;
case PITCH:
pitch.setDisable(!enable);
break;
case ROLL:
roll.setDisable(!enable);
break;
default:
break;
}
}
}
/**
* Create a pane to set where sensor data comes from
* @return the sensor pane.
*/
private Pane createSensorPane() {
PamBorderPane pane = new PamBorderPane();
//hydrophone position and
PamGridPane positionPane = new PamGridPane();
positionPane.setHgap(5);
positionPane.setVgap(5);
int col=0;
int row=0;
positionPane.add(new Label("Heading"), col++, row);
positionPane.add(sensorComponents[ArraySensorFieldType.HEADING.ordinal()].getPane(), col++, row);
row++;
col=0;
positionPane.add(new Label("Pitch"), col++, row);
positionPane.add(sensorComponents[ArraySensorFieldType.PITCH.ordinal()].getPane(), col++, row);
row++;
col=0;
positionPane.add(new Label("Roll"), col++, row);
positionPane.add(sensorComponents[ArraySensorFieldType.ROLL.ordinal()].getPane(), col++, row);
Label orientLabel = new Label("Orientation Data");
PamGuiManagerFX.titleFont2style(orientLabel);
pane.setTop(orientLabel);
pane.setCenter(positionPane);
return pane;
}
/**
* Create a new origin method.
*/
public void newOriginMethod() {
int methInd = originMethod.getSelectionModel().getSelectedIndex();
if (methInd < 0) {
return;
}
HydrophoneOriginSystem currentSystem = HydrophoneOriginMethods.getInstance().getMethod(this.originMethod.getSelectionModel().getSelectedIndex());
currentOriginMethod = currentSystem.createMethod(currentArray, defaultStreamer);
try {
OriginSettings os = defaultStreamer.getOriginSettings(currentOriginMethod.getClass());
if (os != null) {
currentOriginMethod.setOriginSettings(os);
}
}
catch (Exception e) {
// will throw if it tries to set the wrong type of settings.
e.printStackTrace();
}
OriginDialogComponent mthDialogComponent = currentOriginMethod.getDialogComponent();
if (mthDialogComponent == null) {
originPane.getChildren().clear();
currentOriginComponent = null;
this.originButton.setDisable(true);
}
else {
this.originButton.setDisable(false);
Pane newComponent = mthDialogComponent.getSettingsPane();
if (currentOriginComponent != newComponent) {
originPane.setCenter(newComponent);
currentOriginComponent = newComponent;
mthDialogComponent.setParams();
}
}
enableControls();
}
private void enableControls() {
if (currentOriginMethod != null) {
System.out.println("Enable: selected interp: " + interpPane.getSelectedInterpType());
interpPane.setAllowedValues(currentOriginMethod.getAllowedInterpolationMethods());
System.out.println("Enable controls: " + interpPane.getSelectedInterpType());
if (interpPane.getSelectedInterpType()<0) {
interpPane.setSelection(0);
}
}
enableOrientationPane();
}
@Override
public Streamer getParams(Streamer currParams) {
// System.out.println("GETPARAMS: " + currParams);
double zCoeff = PamController.getInstance().getGlobalMediumManager().getZCoeff();
try {
defaultStreamer.setX(Double.valueOf(xPos.getText()));
defaultStreamer.setY(Double.valueOf(yPos.getText()));
defaultStreamer.setZ(zCoeff*Double.valueOf(zPos.getText()));
defaultStreamer.setDx(Double.valueOf(xPosErr.getText()));
defaultStreamer.setDy(Double.valueOf(yPosErr.getText()));
defaultStreamer.setDz(Double.valueOf(zPosErr.getText()));
}
catch (NumberFormatException e) {
System.err.println("Streamer getParams: There is a problem with one of the position parameters in the streamer panel");
return null;
}
defaultStreamer.setStreamerName(currParams.getStreamerName());
int im = interpPane.getSelectedInterpType();
System.out.println("Streamer gwetParams: Origin interpolator: " + interpPane.getSelectedInterpType());
if (im < 0) {
System.err.println("Streamer getParams: There is an index problem with the interpolation selection streamer panel: index = " + im);
}
currentArray.setOriginInterpolation(im);
// try {
// streamer.setBuoyId1(Integer.valueOf(buoyId.getText()));
// }
// catch (NumberFormatException e) {
// streamer.setBuoyId1(null);
// }
HydrophoneLocator locator = HydrophoneLocators.getInstance().
getSystem(localiserMethod.getSelectionModel().getSelectedIndex()).getLocator(currentArray, defaultStreamer);
if (originPane != null) {
// MasterLocator masterLocator = currentArray.getMasterLocator();
// int streamerIndex = currentArray.indexOfStreamer(streamer);
// if (streamerIndex < 0) {
// streamerIndex = currentArray.getNumStreamers();
// }
// masterLocator.setHydrophoneLocator(streamerIndex, locator);
if (currentOriginMethod == null) {
System.err.println("Streamer getParams: No hydrophoneorigin method selected in streamer panel");
}
}
OriginDialogComponent mthDialogComponent = currentOriginMethod.getDialogComponent();
if (mthDialogComponent != null) {
if (mthDialogComponent.getParams() == false) {
System.err.println("Streamer: The origin settings pane returned false suggesting paramters are not correct.");
return null;
}
}
// defaultStreamer.setEnableOrientation(enableOrientation.isSelected());
// if (enableOrientation.isSelected()) {
defaultStreamer.setHeading(getDoubleValue(heading));
defaultStreamer.setPitch(getDoubleValue(pitch));
defaultStreamer.setRoll(getDoubleValue(roll));
// }
if (!heading.isDisable() && defaultStreamer.getHeading() == null) {
System.err.println("Streamer getParams: You must enter a fixed value for the streamer heading");
}
if (!pitch.isDisable() && defaultStreamer.getPitch() == null) {
System.err.println("Streamer getParams: You must enter a fixed value for the streamer pitch");
}
if (!roll.isDisable() && defaultStreamer.getRoll() == null) {
System.err.println("Streamer getParams: You must enter a fixed value for the streamer roll");
}
/**
* We may have large lists of the streamers which we meant to use the
* orientation data from or not. The enable orientation check box will enable or
* disable orientation for ALL streamers which are loaded into memory.
*/
// System.out.println("CURRENTORIGINMETHOD: " + currentOriginMethod);
// System.out.println("LOCATORMETHOD: " + locator);
defaultStreamer.setHydrophoneOrigin(currentOriginMethod);
defaultStreamer.setHydrophoneLocator(locator);
defaultStreamer.setOriginSettings(currentOriginMethod.getOriginSettings());
defaultStreamer.setLocatorSettings(locator.getLocatorSettings());
ArraySensorFieldType[] sensorFields = ArraySensorFieldType.values();
for (int i = 0; i < sensorFields.length; i++) {
ArrayParameterType fieldType = sensorComponents[i].getParameterType();
defaultStreamer.setOrientationTypes(sensorFields[i], fieldType);
if (fieldType == ArrayParameterType.SENSOR) {
PamDataBlock dataBlock = sensorComponents[i].getDataBlock();
defaultStreamer.setSensorDataBlocks(sensorFields[i], dataBlock == null ? null : dataBlock.getLongDataName());
}
}
return defaultStreamer;
}
@Override
public void setParams(Streamer input) {
if (input==null) {
System.out.print("Streamer setParams: The input streamer is null");
}
this.defaultStreamer=input;
// origin methods
// MasterLocator masterLocator = currentArray.getMasterLocator();
// int streamerIndex = currentArray.indexOfStreamer(streamer);
HydrophoneLocator hLocator = defaultStreamer.getHydrophoneLocator();
if (hLocator != null) {
int locatorIndex = HydrophoneLocators.getInstance().indexOfClass(hLocator.getClass());
localiserMethod.getSelectionModel().select(locatorIndex);
HydrophoneOriginMethod originMethod = defaultStreamer.getHydrophoneOrigin();
if (originMethod != null) {
int originIndex = HydrophoneOriginMethods.getInstance().indexOfClass(originMethod.getClass());
this.originMethod.getSelectionModel().select(originIndex);
}
}
else {
localiserMethod.getSelectionModel().select(0);
}
//streamerName.setText(defaultStreamer.getStreamerName());
xPos.setText(String.valueOf(defaultStreamer.getX()));
yPos.setText(String.valueOf(defaultStreamer.getY()));
zPos.setText(String.valueOf(PamController.getInstance().getGlobalMediumManager().getZCoeff()*defaultStreamer.getZ()));
xPosErr.setText(String.valueOf(defaultStreamer.getDx()));
yPosErr.setText(String.valueOf(defaultStreamer.getDy()));
zPosErr.setText(String.valueOf(defaultStreamer.getDz()));
// if (streamer.getBuoyId1() != null) {
// buoyId.setText(streamer.getBuoyId1().toString());
// }
// else {
// buoyId.setText("");
// }
HydrophoneOriginMethod mth = defaultStreamer.getHydrophoneOrigin();
if (mth==null) {
originMethod.getSelectionModel().select(0);
newOriginMethod();
mth = currentOriginMethod;
//defaultStreamer.setHydrophoneOrigin(HydrophoneOriginMethods.getInstance().getMethod(0).createMethod(currentArray, defaultStreamer));
}
OriginDialogComponent mthDialogComponent = mth.getDialogComponent();
if (mthDialogComponent != null) {
System.out.println("Streamer setParams: Set origin component: ");
mthDialogComponent.setParams();
}
// System.out.println("Streamer setParams: Set orientation: " + defaultStreamer.getHeading() + " " + defaultStreamer.getPitch() + " " + defaultStreamer.getRoll());
heading .setText(orientation2Text(defaultStreamer.getHeading()));
pitch .setText(orientation2Text(defaultStreamer.getPitch()));
roll .setText(orientation2Text(defaultStreamer.getRoll()));
System.out.println("Streamer setParams: Origin interpolator: " + currentArray.getOriginInterpolation() + " " + currentOriginMethod.getAllowedInterpolationMethods());
if (currentArray.getOriginInterpolation()<0 || currentArray.getOriginInterpolation()>=currentOriginMethod.getAllowedInterpolationMethods()) {
System.err.println("Streamer setParams: Origin interpolator value of " + currentArray.getOriginInterpolation() + " not allowed for origin method - setting to first allowed method:");
interpPane.setSelection(0);
}
else {
interpPane.setSelection(currentArray.getOriginInterpolation());
}
System.out.println("Streamer setParams: selected interp: " + interpPane.getSelectedInterpType());
ArraySensorFieldType[] sensorFields = ArraySensorFieldType.values();
for (int i = 0; i < sensorFields.length; i++) {
ArrayParameterType fieldType = defaultStreamer.getOrientationTypes(sensorFields[i]);
String fieldDataBlock = defaultStreamer.getSensorDataBlocks(sensorFields[i]);
sensorComponents[i].setParameterType(fieldType);
if (fieldType == ArrayParameterType.SENSOR && fieldDataBlock != null) {
sensorComponents[i].setDataBlock(PamController.getInstance().getDataBlockByLongName(fieldDataBlock));
}
}
setRecieverLabels() ;
enableControls();
}
private String orientation2Text(Double ang) {
if (ang == null) return String.valueOf(0.0);
else return String.format("%3.1f", ang);
}
@Override
public String getName() {
return "Streamer Pane";
}
@Override
public Node getContentNode() {
return mainPane;
}
@Override
public void paneInitialized() {
}
public void setRecieverLabels() {
String recieverDepthString = PamController.getInstance().getGlobalMediumManager().getZString();
depthLabel.setText(recieverDepthString );
depthSensorLabel.setText(recieverDepthString + " Sensor");
}
private Double getDoubleValue(TextField textField) {
String txt = textField.getText();
if (txt == null || txt.length() == 0) {
return null;
}
Double val;
try {
val = Double.valueOf(txt);
return val;
}
catch (NumberFormatException e) {
System.err.println("Invalid orientation information: " + txt);
return null;
}
}
public void setCurrentArray(PamArray currentArray2) {
this.currentArray=currentArray2;
}
}

View File

@ -0,0 +1,315 @@
package Array.layoutFX;
import java.util.ArrayList;
import Array.PamArray;
import Array.Streamer;
import PamController.PamController;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Dialog;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.flipPane.PamFlipPane;
import pamViewFX.fxNodes.table.TableSettingsPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.Pane;
import javafx.geometry.Insets;
/**
* A pane for setting up hydrophones. Note that this is entirely separate from PAMGuard so can be used in
* other projects.
*
* @author Jamie Macaulay
*
*/
public class StreamersPane extends PamBorderPane {
BasicArrayTable tableArrayPane;
ObservableList<StreamerProperty> streamerData = FXCollections.observableArrayList();
/**
* The current hydrophone array
*/
private PamArray currentArray;
/**
* The pam flip pane.
*/
private PamFlipPane pamFlipePane;
/**
* The current streamer data.
*/
private StreamerProperty currentStreamerData;
/**
* Settings pane for a single hydrophone.
*/
private StreamerSettingsPane streamerPane = new StreamerSettingsPane();
/**
* A list of listeners which are called whenever a streamer is added removed or changed.
*/
public ArrayList<ArrayChangeListener> streamerChangeListeners = new ArrayList<ArrayChangeListener>();
public StreamersPane() {
tableArrayPane = new BasicArrayTable(streamerData);
tableArrayPane.setPadding(new Insets(5,5,5,5));
this.setCenter(tableArrayPane);
pamFlipePane = new PamFlipPane();
pamFlipePane.getAdvLabel().setText("Streamer");
pamFlipePane.setMaxWidth(Double.MAX_VALUE);
((Pane) streamerPane.getContentNode()).setPadding(new Insets(5,5,5,15));
pamFlipePane.setAdvPaneContent(streamerPane.getContentNode());
pamFlipePane.setFrontContent(tableArrayPane);
pamFlipePane.getFront().setPadding(new Insets(5,5,5,10));
pamFlipePane.setAdvLabelEditable(true);
pamFlipePane.getPostAdvLabel().setText("Settings");
pamFlipePane.backButtonProperty().addListener((obsval, oldVal, newVal)->{
// System.out.println("Hello back button pressed: " + newVal.intValue());
//the flip pane
if (newVal.intValue()==PamFlipPane.OK_BACK_BUTTON) {
Streamer streamer = streamerPane.getParams(currentStreamerData.getStreamer());
if (streamer==null) {
//the warning dialog is shown in the streamer settings pane
return;
}
streamer.setStreamerName(pamFlipePane.getAdvLabel().getText());
currentStreamerData.setStreamer(streamer);
notifyStreamerListeners(currentStreamerData);
// System.out.println("Update streamer: " + tableArrayPane.getStreamers().indexOf(currentStreamerData) + " no. streamers: " + currentArray.getNumStreamers());
//need to refresh table to show symbol.
tableArrayPane.getTableView().refresh();
if (streamer != null) {
streamer.setupLocator(currentArray);
streamer.makeStreamerDataUnit();
//update the streamer in the current array
}
}
});
this.setCenter(pamFlipePane);
}
/**
* Notify the streamer listeners of a change
* @param streamer - the changed streamer
*/
public void notifyStreamerListeners(StreamerProperty streamer) {
for (ArrayChangeListener listener: streamerChangeListeners) {
listener.arrayChanged(ArrayChangeListener.STREAMER_CHANGE, streamer);
}
}
/**
* Class which extends TableSettingsPane and creates a sliding pane instead of a dialog when an item is added.
* @author Jamie Macaulay
*
*/
class BasicArrayTable extends TableSettingsPane<StreamerProperty> {
private TableColumn<StreamerProperty, Number> z;
public BasicArrayTable(ObservableList<StreamerProperty> data) {
super(data);
//need to set up all the rows.
TableColumn<StreamerProperty,Number> streamerID = new TableColumn<StreamerProperty,Number>("ID");
streamerID.setCellValueFactory(cellData -> cellData.getValue().getID());
streamerID.setEditable(false);
TableColumn<StreamerProperty,String> name = new TableColumn<StreamerProperty,String>("Name");
name.setCellValueFactory(cellData -> cellData.getValue().getName());
name.setEditable(true);
TableColumn<StreamerProperty,Number> x = new TableColumn<StreamerProperty,Number>("x");
x.setCellValueFactory(cellData -> cellData.getValue().getX());
x.setEditable(false);
TableColumn<StreamerProperty,Number> y = new TableColumn<StreamerProperty,Number>("y");
y.setCellValueFactory(cellData -> cellData.getValue().getY());
y.setEditable(false);
z = new TableColumn<StreamerProperty,Number>("depth");
z.setCellValueFactory(cellData -> cellData.getValue().getZ().multiply(PamController.getInstance().getGlobalMediumManager().getZCoeff()));
z.setEditable(false);
TableColumn posColumn=new TableColumn("Position (m)");
posColumn.getColumns().addAll(x, y, z);
TableColumn<StreamerProperty,String> reference = new TableColumn<StreamerProperty,String>("Reference");
reference.setCellValueFactory(cellData -> cellData.getValue().getHydrophoneOrigin());
reference.setEditable(true);
TableColumn<StreamerProperty,String> locator = new TableColumn<StreamerProperty,String>("Locator");
locator.setCellValueFactory(cellData -> cellData.getValue().getHydrophineLocator());
locator.setEditable(true);
TableColumn geoColumn=new TableColumn("Geo-reference");
geoColumn.getColumns().addAll(reference, locator);
getTableView().getColumns().addAll(streamerID, name, posColumn, geoColumn);
}
/**
* Get the current streamers.
* @return the current streamers.
*/
public ObservableList<StreamerProperty> getStreamers() {
return getData();
}
@Override
public void dialogClosed(StreamerProperty data) {
Streamer hydro = streamerPane.getParams(data.getStreamer());
data.setStreamer(hydro);
}
@Override
public Dialog<StreamerProperty> createSettingsDialog(StreamerProperty data) {
//we do not use dialogs here- sliding pane instead.
// setClassifierPane(data);
// showFlipPane(true);
pamFlipePane.flipToBack();
return null;
}
@Override
public void editData(StreamerProperty data){
//edit streamer data.
if (data.getName() == null){
pamFlipePane.getAdvLabel().setText("Streamer " + data.getID().get());
}
streamerPane.setCurrentArray(currentArray);
streamerPane.setParams(data.getStreamer());
currentStreamerData = data;
pamFlipePane.flipToBack();
}
@Override
public void createNewData(){
StreamerProperty newStreamer = createDefaultStreamerProperty();
//create a new classifier.
streamerData.add(newStreamer);
//add to the current array.
currentArray.addStreamer(newStreamer.getStreamer());
System.out.println("Create new streamer: " + currentArray.getNumStreamers());
notifyStreamerListeners(newStreamer);
}
@Override
public void deleteData(StreamerProperty data){
super.deleteData(data);
notifyStreamerListeners(null);
}
private StreamerProperty createDefaultStreamerProperty() {
Streamer streamer = new Streamer(1, 0.,0.,0.,0.,0.,0.);
return new StreamerProperty(streamer);
}
public TableColumn<StreamerProperty, Number> getZColumn() {
return z;
}
}
/**
* Set the parameters for the streamer pane.
* @param currentArray - the current array.
*/
public void setParams(PamArray currentArray) {
this.currentArray=currentArray.clone();
System.out.println("Set params streamer: " + currentArray.getNumStreamers());
tableArrayPane.getStreamers().clear();
for (int i=0; i<currentArray.getStreamerCount(); i++) {
tableArrayPane.getStreamers().add(new StreamerProperty(currentArray.getStreamer(i)));
}
}
/**
* Get the parameters from the streamer pane.
* @param currParams - the current parameters.
* @return the PamArray with updated streamers.
*/
public PamArray getParams(PamArray currParams) {
//add all new streamers - bit weird because the PamArray requires that at least one streamer exists.
for (int i=0; i<tableArrayPane.getStreamers().size(); i++) {
if (i<currentArray.getStreamerCount()) {
currParams.updateStreamer(i,tableArrayPane.getStreamers().get(i).getStreamer());
}
else {
currParams.addStreamer(tableArrayPane.getStreamers().get(i).getStreamer());
}
}
while (currParams.getStreamerCount()>tableArrayPane.getStreamers().size()) {
currParams.removeStreamer(currParams.getStreamerCount()-1);
}
// currentArray.updateStreamer(tableArrayPane.getStreamers().indexOf(currentStreamerData), streamer);
return currParams;
}
public void setRecieverLabels() {
tableArrayPane.getZColumn().setText(PamController.getInstance().getGlobalMediumManager().getZString());
streamerPane.setRecieverLabels();
}
public TableView<StreamerProperty> getStreamerTable() {
return tableArrayPane.getTableView();
}
/**
* Add a listener which is called whenever a streamer is added, removed or changed.
* @param e - the listener to add
*/
public void addStreamerListener(ArrayChangeListener e) {
this.streamerChangeListeners.add(e);
}
public void setCurrentArray(PamArray currentArray) {
this.currentArray=currentArray;
}
}

View File

@ -1,7 +1,15 @@
package Array.streamerOrigin;
import PamView.dialog.DialogComponent;
import javafx.scene.layout.Pane;
public abstract class OriginDialogComponent implements DialogComponent{
/**
* Get a JavaFX pane for the origin method.
* @return the JavaFX pane.
*/
public abstract Pane getSettingsPane();
}

View File

@ -0,0 +1,97 @@
package Array.streamerOrigin;
import GPS.GpsData;
import GPS.GpsDataUnit;
import PamUtils.LatLong;
import javafx.scene.control.Label;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.utilityPanes.LatLongPane;
/**
* JavaFX settings pane for a static hydrophones.
*/
public class StaticHydrophonePane extends PamBorderPane {
/**
* Reference to static origin mwthod.
*/
private StaticOriginMethod staticOriginMethod;
LatLongPane latLongPane;
public StaticHydrophonePane(StaticOriginMethod staticOriginMethod) {
this.staticOriginMethod=staticOriginMethod;
latLongPane = new LatLongPane("Static streamer position");
this.setCenter(latLongPane.getContentNode());
}
public void setParams() {
GpsDataUnit dataUnit = getStaticOriginSettings().getStaticPosition();
if (dataUnit == null) {
setLatLong(null);
}
else {
GpsData gpsData = dataUnit.getGpsData();
setLatLong(gpsData);
}
// if (gpsData == null) {
// return;
// }
// else {
// setLatLong(gpsData);
// }
}
private StaticOriginSettings getStaticOriginSettings() {
return ((StaticOriginSettings) staticOriginMethod.getOriginSettings());
}
public boolean getParams() {
LatLong latLong = latLongPane.getParams(null);
if (latLong==null) {
System.err.println("StaticHydrophonePane: latitude and longitude is null");
return false;
}
if (getStaticOriginSettings()==null) {
System.err.println("StaticHydrophonePane: static origin is null");
return false;
}
//set
getStaticOriginSettings().setStaticPosition(staticOriginMethod.getStreamer(), new GpsData(latLong));
//
// boolean ok = getStaticOriginSettings()!= null && getStaticOriginSettings() .getStaticPosition() != null;
//
// System.out.println("StaticHydrophonePane: Get params from static origin 1 : " + getStaticOriginSettings());
//
// System.out.println("StaticHydrophonePane: Get params from static origin 2: " + getStaticOriginSettings() .getStaticPosition());
return true;
}
/**
* Just set the lat long without resetting the heading.
* @param latLong
*/
private void setLatLong(LatLong latLong) {
if (latLong==null) {
//create a default latitude and longitude - Rockall (why not).
LatLong latLongdefault = new LatLong();
latLongdefault.setLatitude(57.595833333333);
latLongdefault.setLongitude(-13.686944444444);
latLongPane.setParams(latLongdefault);
}
else latLongPane.setParams(latLong);
}
}

View File

@ -29,10 +29,11 @@ import PamUtils.LatLongDialog;
import PamUtils.PamCalendar;
import PamView.dialog.PamDialog;
import PamView.dialog.PamGridBagContraints;
import javafx.scene.layout.Pane;
public class StaticOriginMethod extends HydrophoneOriginMethod {
private StaticHydrophoneDialogComponent staticHydrophoneDialogComponent;
private StaticHydrophoneComponent staticHydrophoneDialogComponent;
private StaticOriginSettings staticOriginSettings = new StaticOriginSettings();
@ -48,12 +49,107 @@ public class StaticOriginMethod extends HydrophoneOriginMethod {
@Override
public OriginDialogComponent getDialogComponent() {
if (staticHydrophoneDialogComponent == null) {
staticHydrophoneDialogComponent = new StaticHydrophoneDialogComponent();
staticHydrophoneDialogComponent = new StaticHydrophoneComponent();
}
return staticHydrophoneDialogComponent;
}
@Override
public OriginSettings getOriginSettings() {
return staticOriginSettings;
}
@Override
public void setOriginSettings(OriginSettings originSettings) {
staticOriginSettings = (StaticOriginSettings) originSettings;
}
@Override
public boolean prepare() {
return true;
}
@Override
public StreamerDataUnit getLastStreamerData() {
StreamerDataBlock streamerDataBlock = ArrayManager.getArrayManager().getStreamerDatabBlock();
StreamerDataUnit sdu = streamerDataBlock.getLastUnit(1<<streamer.getStreamerIndex());
if (sdu == null) {
sdu = new StreamerDataUnit(PamCalendar.getTimeInMillis(), streamer);
//System.out.println("StaticOriginMethod: Streamer rotation: " +sdu.getGpsData().getQuaternion().toHeading());
}
return sdu;
}
@Override
public OriginIterator getGpsDataIterator(int wherefrom) {
return new StreamerDataIterator(wherefrom, streamer);
}
@Override
public Object getSynchronizationObject() {
return NavDataSynchronisation.getSynchobject();
}
/**
* GUI components for the static hydrophone locator. This returns a JavaFX or Swing component depending on the current GUI.
*/
private class StaticHydrophoneComponent extends OriginDialogComponent {
/**
* Swing component.
*/
private StaticHydrophoneDialogComponent staticHydrophoneDialog;
/**
* JavaFX pane for static hydrophones.
*/
private StaticHydrophonePane staticHydrophonePane;
@Override
public JComponent getComponent(Window owner) {
if (staticHydrophoneDialog==null) {
staticHydrophoneDialog = new StaticHydrophoneDialogComponent();
}
return staticHydrophoneDialog.getComponent(owner);
}
@Override
public void setParams() {
if (staticHydrophoneDialog!=null) {
staticHydrophoneDialog.setParams();
}
if (staticHydrophonePane!=null) {
staticHydrophonePane.setParams();
}
}
@Override
public boolean getParams() {
if (staticHydrophoneDialog!=null) {
return staticHydrophoneDialog.getParams();
}
if (staticHydrophonePane!=null) {
return staticHydrophonePane.getParams();
}
return false;
}
@Override
public Pane getSettingsPane() {
if (staticHydrophonePane==null) {
staticHydrophonePane = new StaticHydrophonePane(StaticOriginMethod.this);
}
return staticHydrophonePane;
}
}
/**
* Swing components for the static hydrophone locator.
*/
private class StaticHydrophoneDialogComponent extends OriginDialogComponent {
private JPanel outerPanel;
@ -268,41 +364,12 @@ public class StaticOriginMethod extends HydrophoneOriginMethod {
}
}
}
@Override
public OriginSettings getOriginSettings() {
return staticOriginSettings;
}
@Override
public void setOriginSettings(OriginSettings originSettings) {
staticOriginSettings = (StaticOriginSettings) originSettings;
}
@Override
public boolean prepare() {
return true;
}
@Override
public StreamerDataUnit getLastStreamerData() {
StreamerDataBlock streamerDataBlock = ArrayManager.getArrayManager().getStreamerDatabBlock();
StreamerDataUnit sdu = streamerDataBlock.getLastUnit(1<<streamer.getStreamerIndex());
if (sdu == null) {
sdu = new StreamerDataUnit(PamCalendar.getTimeInMillis(), streamer);
//System.out.println("StaticOriginMethod: Streamer rotation: " +sdu.getGpsData().getQuaternion().toHeading());
@Override
public Pane getSettingsPane() {
// TODO Auto-generated method stub
return null;
}
return sdu;
}
@Override
public OriginIterator getGpsDataIterator(int wherefrom) {
return new StreamerDataIterator(wherefrom, streamer);
}
@Override
public Object getSynchronizationObject() {
return NavDataSynchronisation.getSynchobject();
}
}

View File

@ -27,7 +27,7 @@ public interface LocaliserModel<T extends PamDataUnit> {
public String getToolTipText();
/**
* The type of localisation information the localizer can accept. e.g. bearings, time delays etc. The types are
* The type of localisation information the localiser can accept. e.g. bearings, time delays etc. The types are
* defined in the AbstractLocalisation class.
* @return integer bitmap of the type of localisation information the localiser can use.
*/
@ -35,20 +35,20 @@ public interface LocaliserModel<T extends PamDataUnit> {
/**
* Get the settings pane for the localiser. Allows users to change localiser settings.
* @return the settings pane for the loclaiser.
* @return the settings pane for the localiser.
*/
public LocaliserPane<?> getSettingsPane();
public LocaliserPane<?> getAlgorithmSettingsPane();
/**
* True if the model has paramaters to set. If has the loclaiser has a settings pane it will have
* True if the model has parameters to set. If has the localiser has a settings pane it will have
* parameters. This generally puts an extra button onto a display panel.
*/
public boolean hasParams();
/**
* Run the localisation model. Once completed the results are added to the AbstractLoclaisation class of the input PamDataUnit.
* Note that algorithms may run on a separate thread. Once processing has finished the notifyModelFinished function is called with
*Note that algorithms may run on a separate thread. Once processing has finished the notifyModelFinished function is called with
* a progress of 1;.
* @param pamDataUnit the pamDataUnit. This can be a super unit if multiple detections are required.
* @param addLoc automatically add the localisation result to the data unit, replacing it's current localisation info.
@ -56,7 +56,7 @@ public interface LocaliserModel<T extends PamDataUnit> {
public AbstractLocalisation runModel(T pamDataUnit, DetectionGroupOptions detectionGroupOptions, boolean addLoc);
/**
* This should be called whenever the localiser has finished processing and, if the loclaisation process is long, then updates progress.
* This should be called whenever the localiser has finished processing and, if the localisation process is long, then updates progress.
*/
public void notifyModelProgress(double progress);

View File

@ -1,5 +1,17 @@
package Localiser;
public interface LocaliserPane<T> {
import PamController.SettingsPane;
public abstract class LocaliserPane<T> extends SettingsPane<T> {
public LocaliserPane() {
super(null);
// TODO Auto-generated constructor stub
}
}

View File

@ -152,8 +152,7 @@ public class ModelControlPanel {
@Override
//settings panel
public void actionPerformed(ActionEvent arg0) {
model.getSettingsPane();
model.getAlgorithmSettingsPane();
//AWT implementation.
}
}

View File

@ -258,6 +258,8 @@ public class Correlations {
correlationValue = newPeak[1];
return newPeak[0];
}
/**
* Measure the time delay between pulses on two channels. Inputs in this case are the
* spectrum data (most of the cross correlation is done in the frequency domain)<p>

View File

@ -185,7 +185,7 @@ public class Chi2TimeDelays implements MinimisationFunction {
}
/**
* Set the time delays. Each row is a set fo delays from N synchronised hydrophones. Different rows
* Set the time delays. Each row is a set for delays from N synchronised hydrophones. Different rows
* can have different numbers of synchronised hydrophones.
* @param timeDelays - a set of time delays in seconds.
*/

View File

@ -18,17 +18,19 @@ import PamUtils.CoordUtils;
import org.apache.commons.math3.ml.clustering.Cluster;
import org.apache.commons.math3.ml.clustering.DoublePoint;
import org.apache.commons.math3.ml.clustering.KMeansPlusPlusClusterer;
/**
* Markov chain Monte Carlo (MCMC) is a minimisation technique used widely in a variety of field, from finding exo planets,
* to solving complex intergals.
* <p>
* This is an advanced and highly computationally intensive localisation algorithm based on MCMC methods. For a good description see;
* This is a highly computationally intensive localisation algorithm based on MCMC methods. For a good description see;
* The Transit Light Curve (TLC) Project.I. Four Consecutive Transits of the Exoplanet XO-1b Matthew J. Holman1
* <p>
* This is an abstract class and requires a chi2 function to operate.
* A chi2 function is required to define the minimisation problem.
* <p>
* Multiple MCMC chains can and should be run. These are executed on different threads to take advantage of multi-core processing as much as possible.
* Even so a large number of chains or large observation set can result in significant processing times.
* Multiple MCMC chains can and should be run. These are executed on different threads to take advantage of multi-core processing if possible.
* Even so, a large number of chains or large observation set can result in significant processing times.
* <p>
* Results are analysed for convergence and final locations packed into an MCMCTDResults class.
*
@ -117,7 +119,7 @@ public class MCMC implements MinimisationAlgorithm {
double newChi;
ArrayList<Double> successChi=new ArrayList<Double>(settings.numberOfJumps/5);
ArrayList<double[]> successJump=new ArrayList<double[]>(settings.numberOfJumps/5);
ArrayList<float[]> successJump=new ArrayList<float[]>(settings.numberOfJumps/5);
// System.out.println("Start MCMC milliseconds: "+ System.currentTimeMillis());
@ -143,7 +145,7 @@ public class MCMC implements MinimisationAlgorithm {
chainPos=potentialNewJump;
currentChi=newChi;
successChi.add(newChi);
successJump.add(chainPos);
successJump.add(PamArrayUtils.double2Float(chainPos));
//System.out.println(ChainPos);
//System.out.println(NewChi);
//System.out.println(ObservedTimeDelays);
@ -153,7 +155,7 @@ public class MCMC implements MinimisationAlgorithm {
chainPos=potentialNewJump;
currentChi=newChi;
successChi.add(newChi);
successJump.add(chainPos);
successJump.add(PamArrayUtils.double2Float(chainPos));
//System.out.println(ChainPos);
//System.out.println(NewChi);
//System.out.println(ObservedTimeDelays);
@ -164,7 +166,7 @@ public class MCMC implements MinimisationAlgorithm {
ChainResult chainResult=new ChainResult(successJump, successChi);
chainResult= analyseChain( chainResult);
chainResult.nDim=this.chi2.getDim();
chainResult.nDim=chi2.getDim();
// System.out.println("Chain results is: " + chainResult.mean[0] + " " + chainResult.mean[1] + " " + chainResult.mean[2]);
// System.out.println("End MCMC millis: "+ System.currentTimeMillis());
@ -184,7 +186,7 @@ public class MCMC implements MinimisationAlgorithm {
* @param successJump - list of successful jumps
* @param successChi - list of successful chi2 values.
*/
public ChainResult(ArrayList<double[]> successJump, ArrayList<Double> successChi) {
public ChainResult(ArrayList<float[]> successJump, ArrayList<Double> successChi) {
this.successJump=successJump;
this.successChi=successChi;
}
@ -197,7 +199,7 @@ public class MCMC implements MinimisationAlgorithm {
/**
* A list of points of the successful jumps.
*/
public ArrayList<double[]> successJump;
public ArrayList<float[]> successJump;
/**
* The number of dimensions.
@ -584,7 +586,7 @@ public class MCMC implements MinimisationAlgorithm {
//find min value
int minIndex = chainResult.successChi.indexOf(Collections.min(chainResult.successChi));
minChi2=chainResult.successChi.get(minIndex);
minChi2Pos=chainResult.successJump.get(minIndex);
minChi2Pos=PamArrayUtils.float2Double(chainResult.successJump.get(minIndex));
break;
}
@ -728,19 +730,19 @@ public class MCMC implements MinimisationAlgorithm {
*/
private EllipticalError getLocError(ArrayList<ChainResult> data) {
ArrayList<double[]> successJumpAll = new ArrayList<double[]>();
List<double[]> successJump;
ArrayList<float[]> successJumpAll = new ArrayList<float[]>();
List<float[]> successJump;
for (int i=0; i<data.size(); i++) {
successJump = data.get(i).successJump.subList((int) this.settings.percentageToIgnore*data.get(i).successJump.size(),
data.get(i).successJump.size()-1);
successJumpAll.addAll(successJump);
}
double[][] results= new double[successJumpAll.size()][3];
float[][] results= new float[successJumpAll.size()][3];
results=successJumpAll.toArray(results);
//Elliptical error
EllipticalError ellError= new EllipticalError(results);
EllipticalError ellError= new EllipticalError(PamArrayUtils.float2Double(results));
return ellError;
}

View File

@ -6,6 +6,12 @@ import PamModel.parametermanager.ManagedParameters;
import PamModel.parametermanager.PamParameterSet;
import PamModel.parametermanager.PamParameterSet.ParameterSetType;
/**
* Paramters for running a Marklov chain Monte Carlo algorithm.
*
* @author Jamie Macaulay
*
*/
public class MCMCParams2 implements Serializable, Cloneable, ManagedParameters {
public static final long serialVersionUID = 3L;
@ -30,7 +36,7 @@ public class MCMCParams2 implements Serializable, Cloneable, ManagedParameters
public int numberOfJumps=250000; //int
/**
* Where in paramter space the chains should start. Each value in the array is for one dimension. When
* Where in parameter space the chains should start. Each value in the array is for one dimension. When
* chains start a random number between the two numbers is chosen as the start position in that dimension for the
* chain.
*/
@ -42,12 +48,12 @@ public class MCMCParams2 implements Serializable, Cloneable, ManagedParameters
public int numberOfChains=4; //int
/**
* Use cyclindrical jumps.
* Use cylindrical jumps.
*/
public boolean cylindricalCoOrdinates = false;
/**
* Set the chain jump size- sets the jump size of all dimensions to the input jumpsize
* @param jumpsize the jumpsize for all dimensions.
* Set the chain jump size- sets the jump size of all dimensions to the input jump size.
* @param jumpsize the jump size for all dimensions.
* @param nDim the number of dimensions.
*/
public void setJumpSize(double jumpsize, int nDim){
@ -59,8 +65,8 @@ public class MCMCParams2 implements Serializable, Cloneable, ManagedParameters
/**
* Set chain start dispersion to one value for all dimensions. The start dispersion is a random number centered
* on zero and from a distribution with a size defined by the dispersion input paramater.
* @param dispersion the chain start dispersion for all dimensions. element 1 is th
* on zero and from a distribution with a size defined by the dispersion input parameter.
* @param dispersion the chain start dispersion for all dimensions.
*/
public double[][] setChainDispersion(double dispersion, int nDim){
chainStartDispersion=new double[nDim][2];
@ -87,11 +93,20 @@ public class MCMCParams2 implements Serializable, Cloneable, ManagedParameters
public double percentageToIgnore=0.7; //%
//cluster analysis;
/**
* The type of cluster analysis to use to merge the different chains if they converge on the same or ambiguous results. See K_MEANS and NONE constants.
*/
public int clusterAnalysis=K_MEANS;
/**
* Do not attempt to cluster the chains.
*/
public static final int NONE=0;
/**
* Use K_MEANS to cluster the different chains
*/
public static final int K_MEANS=1;
public Integer nKMeans=2; //int
@ -99,12 +114,12 @@ public class MCMCParams2 implements Serializable, Cloneable, ManagedParameters
public double maxClusterSize=5; //meters
/**
* The number of times to perform a kmeans algorithm on results The algorithm starts clusters at random locations.
* The number of times to perform a k-means algorithm on results The algorithm starts clusters at random locations.
*/
public int kmeanAttempts=10;
/**
* The number of iterations for each kmeans attempt. Kmeans converges to a result but requires a certain number of iteration to
* The number of iterations for each k-means attempt. K-means converges to a result but requires a certain number of iteration to
* do so.
*/
public int kmeansIterations=20;

View File

@ -6,6 +6,7 @@ import javax.vecmath.Point3f;
import Localiser.algorithms.genericLocaliser.MCMC.MCMC.ChainResult;
import Localiser.algorithms.locErrors.EllipticalError;
import PamUtils.PamArrayUtils;
public class MCMCResult {
@ -122,17 +123,89 @@ public class MCMCResult {
public ArrayList<ArrayList<Point3f>> getJumps() {
ArrayList<ArrayList<Point3f>> jumps=new ArrayList<ArrayList<Point3f>>();
ArrayList<Point3f> chainJumps;
double[] ajump;
float[] ajump;
for (int i=0; i<this.data.size(); i++) {
chainJumps= new ArrayList<Point3f>();
for (int j=0; j<this.data.get(i).successJump.size(); j++) {
ajump= this.data.get(i).successJump.get(j);
chainJumps.add(new Point3f((float) ajump[0], (float) ajump[1], (float) ajump[2]));
chainJumps.add(new Point3f(ajump[0], ajump[1], ajump[2]));
}
jumps.add(chainJumps);
}
return jumps;
}
public double[][] getJumpsd() {
return getJumpsd(1);
}
public double[][] getJumpsf() {
return getJumpsd(1);
}
/**
* Get the jumps for the MCMC algorithm in double[][] format. This is for legacy code.
* @param div - reduce the data by div times (e.g. for plotting).
* @return the MCMC jumps from all chains.
*/
public double[][] getJumpsd(int div) {
if (div<1) div = 1;
// int nJumps = 0;
// for (int i=0; i<this.data.size(); i++) {
// nJumps = (int) (nJumps + Math.floor(this.data.get(i).successJump.size()/div)+1);
// }
//
int nJumps = 0;
for (int i=0; i<this.data.size(); i++) {
for (int j=0; j<this.data.get(i).successJump.size(); j=j+div) {
nJumps++;
}
}
double[][] jumps = new double[nJumps][];
double[] ajump;
int n=0;
for (int i=0; i<this.data.size(); i++) {
for (int j=0; j<this.data.get(i).successJump.size(); j=j+div) {
ajump= PamArrayUtils.float2Double(this.data.get(i).successJump.get(j));
jumps[n] = ajump;
n++;
}
}
return jumps;
}
/**
* Get the jumps for the MCMC algorithm in double[][] format. This is for legacy code.
* @param div - reduce the data by div times (e.g. for plotting).
* @return the MCMC jumps from all chains.
*/
public float[][] getJumpsf(int div) {
if (div<1) div =1;
int nJumps = 0;
for (int i=0; i<this.data.size(); i++) {
nJumps = nJumps + this.data.get(i).successJump.size();
}
float[][] jumps = new float[(int) Math.floor(nJumps/div)][];
float[] ajump;
int n=0;
for (int i=0; i<this.data.size(); i=i++) {
for (int j=0; j<this.data.get(i).successJump.size(); j=j+div) {
ajump= this.data.get(i).successJump.get(j);
jumps[n] = ajump;
n++;
}
}
return jumps;
}
}

View File

@ -20,8 +20,12 @@ import Localiser.algorithms.locErrors.LikilihoodError;
import Localiser.algorithms.locErrors.LocaliserError;
/**
* Time delay based simplex method for localisation. Note that a lot of the code here has been referenced directly from static MCMC functions. This is because both MCMC and Simplex are based on the forward time delay problem
* hence are mathematically very similar. Simplex is much faster than MCMC but does not provide a full 3D probability distribution.
* Time delay based simplex method for localisation. Note that a lot of the code
* here has been referenced directly from static MCMC functions. This is because
* both MCMC and Simplex are based on the forward time delay problem hence are
* mathematically very similar. Simplex is much faster than MCMC but does not
* provide a full 3D probability distribution.
*
* @author Jamie Macaulay
*
*/

View File

@ -10,8 +10,10 @@ import java.awt.geom.Point2D;
import PamUtils.Coordinate3d;
import PamUtils.LatLong;
import PamUtils.PamArrayUtils;
import PamView.TransformShape;
import PamView.GeneralProjector;
import PamguardMVC.PamConstants;
import PamguardMVC.PamDataUnit;
import pamMaths.PamVector;
@ -28,15 +30,20 @@ public class EllipseLocErrorDraw implements LocErrorGraphics {
*/
private EllipticalError ellipticalError;
public EllipticalError getEllipticalError() {
return ellipticalError;
}
public static final int DRAW_LINES = 1;
public static final int DRAW_OVALS = 2;
private int drawType = DRAW_OVALS;
/**
* Constructor for drawing an ellipsoid.
* @param ellipticalError
*/
EllipseLocErrorDraw(EllipticalError ellipticalError){
protected EllipseLocErrorDraw(EllipticalError ellipticalError){
this.ellipticalError=ellipticalError;
}
@ -55,6 +62,8 @@ public class EllipseLocErrorDraw implements LocErrorGraphics {
}
return null;
}
public TransformShape drawLinesOnMap(Graphics g, PamDataUnit pamDetection, LatLong errorOrigin,
GeneralProjector generalProjector, Color ellipseColor) {
Graphics2D g2d = (Graphics2D) g;
@ -102,20 +111,27 @@ public class EllipseLocErrorDraw implements LocErrorGraphics {
return null;
}
public TransformShape drawOvalsOnMap(Graphics g, PamDataUnit pamDetection, LatLong errorOrigin,
GeneralProjector generalProjector, Color ellipseColor) {
//this is 2D- need to make a slice through the ellipse and get the localisation points.
double[] errors2D=ellipticalError.getErrorEllipse2D(ErrorEllipse.PLANE_XY_PROJ);
// if (1>0) return null;
if (errors2D==null) return null;
//System.out.println("EllipseLocErrorDraw: draw ellipse:"+errors2D[0]+" "+errors2D[1]+" "+Math.toDegrees(errors2D[2]));
if (errors2D[0] > PamConstants.EARTH_RADIUS_METERS || errors2D[1] > PamConstants.EARTH_RADIUS_METERS) {
return null; //don't draw infintie stuff - causes nasty errors.
}
// System.out.println("Draw ovals on map");
// System.out.println("EllipseLocErrorDraw: draw ellipse:"+errors2D[0]+" "+errors2D[1]+" "+Math.toDegrees(errors2D[2]));
//System.out.println("Plot errors: perp: "+ perpError+ " horz: "+horzError+ " " + errorDirection);
Graphics2D g2d = (Graphics2D)g;
if (errors2D==null) return null;
//draw oval
// //need to work out the size of the horizontal error.
// perpError=Math.max(perpError, 100);
@ -140,7 +156,7 @@ public class EllipseLocErrorDraw implements LocErrorGraphics {
//draw the ellipse and rotate.
Ellipse2D oval=new Ellipse2D.Double(errorOriginXY.getX()-horzErrPix/2, errorOriginXY.getY()-perpErrPix/2, horzErrPix, perpErrPix);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 5 * 0.1f));
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 5 * 0.05f));
g2d.setPaint(ellipseColor.brighter());
if (!Double.isNaN(errorDirection)) g2d.rotate(-paintAngle, errorOriginXY.getX(), errorOriginXY.getY());
@ -158,4 +174,8 @@ public class EllipseLocErrorDraw implements LocErrorGraphics {
}
public int getDrawType() {
return drawType;
}
}

View File

@ -51,6 +51,8 @@ public class EllipticalError implements LocaliserError {
errorEllipse = new ErrorEllipse(errors, angles);
}
@Override
public double getError(PamVector errorDirection) {
return errorEllipse.getErrorMagnitude(errorDirection.getUnitVector().getVector());
@ -78,7 +80,7 @@ public class EllipticalError implements LocaliserError {
/**
* Get the 2D elliptical error.
* @param planeXy - the plane on whihc to find the ellipse for.
* @param planeXy - the plane on which to find the ellipse for.
* @return the elliptical error.
*/
public double[] getErrorEllipse2D(int planeXy) {
@ -111,6 +113,9 @@ public class EllipticalError implements LocaliserError {
@Override
public String getStringResult() {
String description="";
if (errorEllipse==null) {
return "Elliptical error is null";
}
if (!errorEllipse.is3D()){
description+=String.format("<i>&ensp 2D Elliptical error: <br>&ensp Radii: ");
for (int i=0; i<errorEllipse.getEllipseDim().length; i++){

View File

@ -14,10 +14,13 @@ import org.apache.commons.math3.stat.correlation.Covariance;
/**
* Class for calculating errors from localisation data. An error ellipse describes N dimensional error based on a scatter of points or
* chi2 surface. The dimensions and rotation of the ellipse describe the distribution of error. Note that although an ellipse will often be a satisfactory
* description of an error surface, in some cases it will not adequately represent errors. e.g. a linear array produces a doughnut shaped error
* surface which would not be described well by an ellipse.
* Class for calculating errors from localisation data. An error ellipse
* describes N dimensional error based on a scatter of points or chi2 surface.
* The dimensions and rotation of the ellipse describe the distribution of
* error. Note that although an ellipse will often be a satisfactory description
* of an error surface, in some cases it will not adequately represent errors.
* e.g. a linear array produces a doughnut shaped error surface which would not
* be described well by an ellipse.
*
* @author Jamie Macaulay
*
@ -41,17 +44,17 @@ public class ErrorEllipse {
public static final int PLANE_ZX=2;
/**
*Project the ellipse ontpo the XY plane- the extremties oif the ellipse are kept
*Project the ellipse ontpo the XY plane- the extremities of the ellipse are kept
*/
public static final int PLANE_XY_PROJ=3;
/**
*Project the ellipse ontpo the Yz plane- the extremties oif the ellipse are kept
*Project the ellipse ontpo the Yz plane- the extremities of the ellipse are kept
*/
public static final int PLANE_ZY_PROJ=4;
/**
*Project the ellipse ontpo the ZX plane- the extremties oif the ellipse are kept
*Project the ellipse ontpo the ZX plane- the extremities of the ellipse are kept
*/
public static final int PLANE_ZX_PROJ=5;
@ -88,13 +91,12 @@ public class ErrorEllipse {
* The dimensions of the ellipse/ellipsoid. This is generally a,b for 2D and a, b, c for 3D
* the ellipse/ellipsoid is described by x^2/a + y^2/b+ z^2/c =1. c is -1 if a 2D ellipse.
*/
private double[] ellipseDim;
private double[] ellipseDim = new double[]{0,0,0};
/**
* The angle of the ellipsoid in RADIANS. Angles are euler angles and in order heading pitch and roll.
*/
private double[] angles;
private double[] angles = new double[]{0,0,0};
/**
* Generate an error ellipse from a set of points. The dimensions of the error ellipse is by
@ -163,6 +165,10 @@ public class ErrorEllipse {
if (points==null){
return;
}
if (points.length<3) {
return;
}
Covariance cov= new Covariance(points, false);
@ -191,7 +197,7 @@ public class ErrorEllipse {
/**
* Calculate the error ellipse from eigenvalues and eigenvectors.
* @param eigenVal - matrix the eigenvalues - this is the size of the ellipse
* @param eigenVec - matrix eigenvectors- the direction of the ellispe. Not necassarily in order.
* @param eigenVec - matrix eigenvectors- the direction of the ellipse. Not necassarily in order.
*/
private void calcErrorEllipse(RealMatrix eigenVal, RealMatrix eigenVec){
@ -339,8 +345,10 @@ public class ErrorEllipse {
}
/**
*
*FIXME - this needs work to make sure roll works
* Get the ellipse projected onto a 3D plane.
* @return an array. array [0] is the first radii. array[1] is the second radii. array[2] is the rotation irelative to the plane in radians.
* @return an array. array [0] is the first radii. array[1] is the second radii. array[2] is the rotation relative to the plane in RADIANS.
*/
public double[] getErrorEllipse2D(int planeType){
@ -356,7 +364,7 @@ public class ErrorEllipse {
}
else {
if(errorEllipseXY==null){
//here we calculate and save the prjection- once saved the projection is never
//here we calculate and save the projection- once saved the projection is never
//recalculated.
errorEllipseXY = calc2DEllipse(planeType);
if (errorEllipseXY == null) return null;
@ -365,7 +373,7 @@ public class ErrorEllipse {
data[0]=errorEllipseXY.getEllipseDim()[0];
data[1]=errorEllipseXY.getEllipseDim()[1];
data[2]=errorEllipseXY.getAngles()[0];
//System.out.println("ErrorEllipse: data: "+data[0]+ " "+data[1]+" "+data[1] + " Ellipse largest vector: "+getEllipseDim()[0]);
//System.out.println("ErrorEllipse: data: "+data[0]+ " "+data[1]+" "+data[2] + " Ellipse largest vector: "+getEllipseDim()[0]);
}
break;
@ -388,7 +396,7 @@ public class ErrorEllipse {
/**
* Claculate a 2D projec tion of the 3D ellipse.
* Calculate a 2D projection of the 3D ellipse.
* @param planeType
* @return
*/
@ -426,8 +434,10 @@ public class ErrorEllipse {
dim1=0;
dim2=1;
//22 Aug 2023 - dim[1] was using sin instead of cos - for projecting onto a 2d plane cos
//is the correct trig function to use.
dim[0]=this.ellipseDim[0]*Math.cos(this.angles[1]); //the major axis on 2D
dim[1]=this.ellipseDim[1]*Math.sin(this.angles[2]); //the minor Axis.
dim[1]=this.ellipseDim[1]*Math.cos(this.angles[2]); //the minor Axis.
angles[0]=this.angles[0];
break;
@ -450,13 +460,11 @@ public class ErrorEllipse {
ErrorEllipse errorEllipse2D=new ErrorEllipse(dim, angles);
return errorEllipse2D;
}
/**
* Generate a projection of the ellipse into 2D. The prjection is a defined plane slicing throug the center of the ellipse
* Generate a projection of the ellipse into 2D. The projection is a defined plane slicing through the center of the ellipse
* @param planeType - the type of plane. PLANE_XY, PLANE_ZY, PLANE_ZX
* @return the 2D projection of the ellipse onto a plane.
*/

View File

@ -1,10 +1,10 @@
package Localiser.algorithms.locErrors;
public class ErrorEllipseXMLData extends ErrorXMLData {
public class ErrorEllipseJSONData extends ErrorJSONData {
private EllipticalError ellipticalError;
public ErrorEllipseXMLData(EllipticalError ellipticalError) {
public ErrorEllipseJSONData(EllipticalError ellipticalError) {
this.ellipticalError = ellipticalError;
}

View File

@ -2,7 +2,7 @@ package Localiser.algorithms.locErrors;
import java.text.DecimalFormat;
public abstract class ErrorXMLData {
public abstract class ErrorJSONData {
protected static final DecimalFormat errorDigitFormat = new DecimalFormat("0.###E0");

View File

@ -42,7 +42,7 @@ public class LikilihoodError extends EllipticalError {
super();
this.chi2=chi2;
this.point=point;
super.setErrorEllipse(chi2SurftoErrorEllipse( chi2, point, 3));
super.setErrorEllipse(chi2SurftoErrorEllipse(point, 3));
}
@ -50,7 +50,7 @@ public class LikilihoodError extends EllipticalError {
super();
this.chi2=chi2;
this.point=point;
super.setErrorEllipse(chi2SurftoErrorEllipse( chi2, point, nDim));
super.setErrorEllipse(chi2SurftoErrorEllipse(point, nDim));
}
@ -62,17 +62,17 @@ public class LikilihoodError extends EllipticalError {
* @param nDim - the number of dimensions. 2 or 3.
* @return the approximate 3D ellipse which represents the error.
*/
private ErrorEllipse chi2SurftoErrorEllipse(MinimisationFunction chi2, double[] point, int nDim){
private ErrorEllipse chi2SurftoErrorEllipse(double[] point, int nDim){
ErrorEllipse errorEllipse;
if (nDim==2){
errorEllipse = chi2SurfToErrorEllipse2D(chi2, point);
errorEllipse = chi2SurfToErrorEllipse2D( point);
}
else if (nDim==3){
errorEllipse = chi2SurfToErrorEllipse3D(chi2, point);
errorEllipse = chi2SurfToErrorEllipse3D (point);
if (errorEllipse==null) {
//maight have failed due to a 3D model trying to fit a 2D problem and therefore
//heading off to infinity...
errorEllipse = chi2SurfToErrorEllipse2D(chi2, point);
errorEllipse = chi2SurfToErrorEllipse2D( point);
}
}
else{
@ -90,7 +90,7 @@ public class LikilihoodError extends EllipticalError {
* @param point - the point on the surface to search around for error.
* @return the approximate 3D ellipse which represents the error.
*/
private ErrorEllipse chi2SurfToErrorEllipse2D(MinimisationFunction chi2, double[] point2) {
private ErrorEllipse chi2SurfToErrorEllipse2D(double[] point2) {
//create a set of numbers around a sphere;
double angleBin=(2*Math.PI)/nSpherePoints;
@ -100,14 +100,14 @@ public class LikilihoodError extends EllipticalError {
int ind=-1;
for (int i=0; i<nSpherePoints; i++){
//now because the sphere has a radius of 1, all the points are already unit vectors.
curvatureError=getLLCurvature(chi2, point, PamVector.fromHeadAndSlant(Math.toDegrees(angleBin*i), 0));
curvatureError=getLLCurvature(point, PamVector.fromHeadAndSlant(Math.toDegrees(angleBin*i), 0));
if (curvatureError>max){
max=curvatureError;
ind=i; //record index of max value;
}
}
double[] dim={max, getLLCurvature(chi2, point, PamVector.fromHeadAndSlant(Math.toDegrees(angleBin*ind)+90, 0)),-1 };
double[] dim={max, getLLCurvature(point, PamVector.fromHeadAndSlant(Math.toDegrees(angleBin*ind)+90, 0)),-1 };
double[] angles={angleBin*ind, 0,0};
for (int i=0; i<dim.length; i++){
@ -130,7 +130,7 @@ public class LikilihoodError extends EllipticalError {
* @param point - the point on the surface to search around for error.
* @return the approximate 3D ellipse which represents the error. Returns null if it is not possible to calculate an error.
*/
private ErrorEllipse chi2SurfToErrorEllipse3D(MinimisationFunction chi2, double[] point){
private ErrorEllipse chi2SurfToErrorEllipse3D(double[] point){
//first, find the largest error.
//create a set of numbers around a sphere;
@ -141,7 +141,7 @@ public class LikilihoodError extends EllipticalError {
int ind=-1;
for (int i=0; i<spherePoints.length; i++){
//now because the sphere has a radius of 1, all the points are already unit vectors.
curvatureError=getLLCurvature(chi2, point, new PamVector(spherePoints[i]));
curvatureError=getLLCurvature(point, new PamVector(spherePoints[i]));
//System.out.printf("Curve error %d = %3.2f\n", i, curvatureError);
if (curvatureError>max){
max=curvatureError;
@ -171,7 +171,7 @@ public class LikilihoodError extends EllipticalError {
Vector3D location3D=plane.getPointAt(vector2D, 0);
// get the max error in this plane.
curvatureError=getLLCurvature(chi2, point, new PamVector(location3D.toArray()));
curvatureError=getLLCurvature(point, new PamVector(location3D.toArray()));
if (curvatureError>maxPlane){
maxPlane=curvatureError;
indPlane=n; //record index of max value;
@ -203,7 +203,7 @@ public class LikilihoodError extends EllipticalError {
ArrayList<Vector3D> vectors=new ArrayList<Vector3D>();
vectors.add(firstEigenvector.scalarMultiply(max)); //have already calculated error in previous steps.
vectors.add(secondVector.scalarMultiply(maxPlane)); //have already calculated error in previous steps.
vectors.add(thirdVector.scalarMultiply(getLLCurvature( chi2, point, new PamVector(thirdVector.toArray()))));
vectors.add(thirdVector.scalarMultiply(getLLCurvature( point, new PamVector(thirdVector.toArray()))));
//create an error ellipse.
ErrorEllipse errorEllipse=new ErrorEllipse(vectors);
@ -240,7 +240,7 @@ public class LikilihoodError extends EllipticalError {
* direction specified and in the opposite direction. Curvature is expressed as 1 standard deviation
* error
*/
private double getLLCurvature(MinimisationFunction chi2, double[] point, PamVector errorVector) {
private double getLLCurvature(double[] point, PamVector errorVector) {
double dis = 10; //the jump along the chi2 surface.
double err = 0;

View File

@ -42,10 +42,7 @@ public class SimpleError implements LocaliserError {
* @param xyzError - an array of the x/perpindicular y/parallel and z error in meters {x y z}
*/
public SimpleError(Double xyzError[]) {
simpleErrorDraw=new SimpleLocErrorDraw(this);
perpindiuclarError=xyzError[0];
parallelError=xyzError[1];
zError=xyzError[2];
this(xyzError, 0.);
}
/**

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