diff --git a/.classpath b/.classpath index 188c7291..576b210a 100644 --- a/.classpath +++ b/.classpath @@ -6,8 +6,9 @@ - + + diff --git a/.gitignore b/.gitignore index 61250077..a379d5bb 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,71 @@ 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 diff --git a/.metadata/.lock b/.metadata/.lock new file mode 100644 index 00000000..e69de29b diff --git a/.metadata/.plugins/org.eclipse.core.resources/.projects/.org.eclipse.egit.core.cmp/.location b/.metadata/.plugins/org.eclipse.core.resources/.projects/.org.eclipse.egit.core.cmp/.location new file mode 100644 index 00000000..98851383 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.core.resources/.projects/.org.eclipse.egit.core.cmp/.location differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version new file mode 100644 index 00000000..25cb955b --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index new file mode 100644 index 00000000..d2372511 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version new file mode 100644 index 00000000..6b2aaa76 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.metadata/.plugins/org.eclipse.core.resources/.root/1.tree b/.metadata/.plugins/org.eclipse.core.resources/.root/1.tree new file mode 100644 index 00000000..89bbc184 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.core.resources/.root/1.tree differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources b/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources new file mode 100644 index 00000000..bd315dac Binary files /dev/null and b/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources differ diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..30841ebf --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding=UTF-8 +version=1 diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000..67094041 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.ui.prefs @@ -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 diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs new file mode 100644 index 00000000..67b1d96c --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.m2e.discovery.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.m2e.discovery.pref.projects= diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs new file mode 100644 index 00000000..bc1720e5 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +platformState=1678968029917 +quickStart=false +tipsAndTricks=true diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs new file mode 100644 index 00000000..08076f23 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +showIntro=false diff --git a/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs new file mode 100644 index 00000000..7e380fb0 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs @@ -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 diff --git a/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi b/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi new file mode 100644 index 00000000..8106a865 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi @@ -0,0 +1,2360 @@ + + + + activeSchemeId:org.eclipse.ui.defaultAcceleratorConfiguration + + + + + + + + topLevel + + + + + persp.actionSet:org.eclipse.ui.cheatsheets.actionSet + persp.actionSet:org.eclipse.search.searchActionSet + persp.actionSet:org.eclipse.text.quicksearch.actionSet + persp.actionSet:org.eclipse.ui.edit.text.actionSet.annotationNavigation + persp.actionSet:org.eclipse.ui.edit.text.actionSet.navigation + persp.actionSet:org.eclipse.ui.edit.text.actionSet.convertLineDelimitersTo + persp.actionSet:org.eclipse.ui.externaltools.ExternalToolsSet + persp.actionSet:org.eclipse.ui.actionSet.keyBindings + persp.actionSet:org.eclipse.ui.actionSet.openFiles + persp.actionSet:org.eclipse.debug.ui.launchActionSet + persp.actionSet:org.eclipse.jdt.ui.JavaActionSet + persp.actionSet:org.eclipse.jdt.ui.JavaElementCreationActionSet + persp.actionSet:org.eclipse.ui.NavigateActionSet + persp.viewSC:org.eclipse.jdt.ui.PackageExplorer + persp.viewSC:org.eclipse.jdt.ui.TypeHierarchy + persp.viewSC:org.eclipse.jdt.ui.SourceView + persp.viewSC:org.eclipse.jdt.ui.JavadocView + persp.viewSC:org.eclipse.search.ui.views.SearchView + persp.viewSC:org.eclipse.ui.console.ConsoleView + persp.viewSC:org.eclipse.ui.views.ContentOutline + persp.viewSC:org.eclipse.ui.views.ProblemView + persp.viewSC:org.eclipse.ui.views.TaskList + persp.viewSC:org.eclipse.ui.views.ProgressView + persp.viewSC:org.eclipse.ui.navigator.ProjectExplorer + persp.viewSC:org.eclipse.ui.texteditor.TemplatesView + persp.viewSC:org.eclipse.pde.runtime.LogView + persp.newWizSC:org.eclipse.jdt.ui.wizards.JavaProjectWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewPackageCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewClassCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewInterfaceCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewEnumCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewRecordCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewAnnotationCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewSourceFolderCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewSnippetFileCreationWizard + persp.newWizSC:org.eclipse.jdt.ui.wizards.NewJavaWorkingSetWizard + persp.newWizSC:org.eclipse.ui.wizards.new.folder + persp.newWizSC:org.eclipse.ui.wizards.new.file + persp.newWizSC:org.eclipse.ui.editors.wizards.UntitledTextFileWizard + persp.perspSC:org.eclipse.jdt.ui.JavaBrowsingPerspective + persp.perspSC:org.eclipse.debug.ui.DebugPerspective + persp.showIn:org.eclipse.jdt.ui.PackageExplorer + persp.showIn:org.eclipse.team.ui.GenericHistoryView + persp.showIn:org.eclipse.ui.navigator.ProjectExplorer + persp.actionSet:org.eclipse.debug.ui.breakpointActionSet + persp.actionSet:org.eclipse.jdt.debug.ui.JDTDebugActionSet + persp.showIn:org.eclipse.egit.ui.RepositoriesView + persp.newWizSC:org.eclipse.m2e.core.wizards.Maven2ProjectWizard + persp.actionSet:org.eclipse.eclemma.ui.CoverageActionSet + persp.showIn:org.eclipse.eclemma.ui.CoverageView + persp.viewSC:org.eclipse.tm.terminal.view.ui.TerminalsView + persp.showIn:org.eclipse.tm.terminal.view.ui.TerminalsView + persp.viewSC:org.eclipse.jdt.bcoview.views.BytecodeOutlineView + persp.newWizSC:org.eclipse.jdt.junit.wizards.NewTestCaseCreationWizard + persp.actionSet:org.eclipse.jdt.junit.JUnitActionSet + persp.viewSC:org.eclipse.ant.ui.views.AntView + + + + org.eclipse.e4.primaryNavigationStack + active + noFocus + + View + categoryTag:Java + + + View + categoryTag:Java + + + View + categoryTag:General + + + View + categoryTag:Java + + + + + View + categoryTag:Git + + + + + + + + org.eclipse.e4.secondaryNavigationStack + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:Java + + + View + categoryTag:Ant + + + + + org.eclipse.e4.secondaryDataStack + + View + categoryTag:General + + + View + categoryTag:Java + + + View + categoryTag:Java + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:General + + + View + categoryTag:Terminal + + + + + + + + + View + categoryTag:Help + + + View + categoryTag:General + + + View + categoryTag:Help + + + + + + + View + categoryTag:Help + + + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + + + View + categoryTag:Help + + + + org.eclipse.e4.primaryDataStack + EditorStack + + + + + + + View + categoryTag:Java + active + activeOnClose + + ViewMenu + menuContribution:menu + + + + + + + View + categoryTag:Java + + + + + View + categoryTag:General + + + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + + View + categoryTag:General + + ViewMenu + menuContribution:menu + + + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:Git + + + + + View + categoryTag:Terminal + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:Ant + + + + toolbarSeparator + + + + Draggable + + + + toolbarSeparator + + + + Draggable + + + toolbarSeparator + + + + Draggable + + + Draggable + + + Draggable + + + toolbarSeparator + + + + Draggable + + + + toolbarSeparator + + + + toolbarSeparator + + + + Draggable + + + stretch + SHOW_RESTORE_MENU + + + Draggable + HIDEABLE + SHOW_RESTORE_MENU + + + + + stretch + + + Draggable + + + Draggable + + + + + TrimStack + Draggable + + + + + + + + + + + + + platform:cocoa + + + platform:cocoa + + + + + + platform:cocoa + + + platform:cocoa + + + + + + platform:cocoa + + + + + + + + + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + + + + + + + + platform:cocoa + + + + + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + + + + + + platform:cocoa + + + platform:cocoa + + + + + + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + + + + + platform:cocoa + + + + + + + platform:cocoa + + + + + + + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + + + platform:cocoa + + + + platform:cocoa + + + + + platform:cocoa + + + + platform:cocoa + + + + + + + + + platform:cocoa + + + + + platform:cocoa + + + platform:cocoa + + + + + + + + platform:cocoa + + + + + + + + + + platform:cocoa + + + platform:cocoa + + + + + + + platform:cocoa + + + + + + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + platform:cocoa + + + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + + + + platform:cocoa + + + + + + + + + + + + + + + + platform:cocoa + + + + + platform:cocoa + + + platform:cocoa + + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + platform:cocoa + + + + + platform:cocoa + + + + + + + platform:cocoa + + + + platform:cocoa + + + + + + + + + + + + + + + + + + + + + + + platform:cocoa + + + platform:cocoa + + + + + + + platform:cocoa + + + + platform:cocoa + + + + + platform:cocoa + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + platform:cocoa + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Editor + removeOnHide + + + + + View + categoryTag:Ant + + + + + View + categoryTag:Gradle + + + + + View + categoryTag:Gradle + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Debug + + + View + categoryTag:Debug + + + + + View + categoryTag:Java + + + + + View + categoryTag:Git + + + + + View + categoryTag:Git + + + + + View + categoryTag:Git + + + + + View + categoryTag:Git + NoRestore + + + + + View + categoryTag:Git + + + + + View + categoryTag:Help + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:Debug + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java Browsing + + + + + View + categoryTag:Java Browsing + + + + + View + categoryTag:Java Browsing + + + + + View + categoryTag:Java Browsing + + + + + View + categoryTag:Java + + + + + View + categoryTag:General + + + + + View + categoryTag:Java + + + + + View + categoryTag:Java + + + + + View + categoryTag:Maven + + + + + View + categoryTag:Maven + + + + + View + categoryTag:Maven + + + + + View + categoryTag:Oomph + + + + + View + categoryTag:General + + + + + View + categoryTag:Version Control (Team) + + + + + View + categoryTag:Version Control (Team) + + + View + categoryTag:Help + + + + + View + categoryTag:Terminal + + + + + View + categoryTag:Other + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:Help + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + + View + categoryTag:General + + + + glue + move_after:PerspectiveSpacer + SHOW_RESTORE_MENU + + + move_after:Spacer Glue + HIDEABLE + SHOW_RESTORE_MENU + + + glue + move_after:SearchField + SHOW_RESTORE_MENU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.metadata/.plugins/org.eclipse.egit.core/.org.eclipse.egit.core.cmp/.settings/org.eclipse.core.resources.prefs b/.metadata/.plugins/org.eclipse.egit.core/.org.eclipse.egit.core.cmp/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..99f26c02 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.egit.core/.org.eclipse.egit.core.cmp/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache b/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache new file mode 100644 index 00000000..593f4708 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/assumedExternalFilesCache differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache b/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache new file mode 100644 index 00000000..593f4708 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/javaLikeNames.txt b/.metadata/.plugins/org.eclipse.jdt.core/javaLikeNames.txt new file mode 100644 index 00000000..85863977 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.core/javaLikeNames.txt @@ -0,0 +1 @@ +java \ No newline at end of file diff --git a/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache b/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache new file mode 100644 index 00000000..593f4708 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat b/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat new file mode 100644 index 00000000..0edae4b2 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.jdt.core/variablesAndContainers.dat differ diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml b/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml new file mode 100644 index 00000000..a4ee3cbc --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml @@ -0,0 +1,2 @@ + + diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml b/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml new file mode 100644 index 00000000..9e390f50 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml @@ -0,0 +1,2 @@ + + diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml b/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml new file mode 100644 index 00000000..c8468c1f --- /dev/null +++ b/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml @@ -0,0 +1,10 @@ + +
+
+ + + + + +
+
diff --git a/.metadata/.plugins/org.eclipse.m2e.core/workspaceState.ser b/.metadata/.plugins/org.eclipse.m2e.core/workspaceState.ser new file mode 100644 index 00000000..abbf8e52 Binary files /dev/null and b/.metadata/.plugins/org.eclipse.m2e.core/workspaceState.ser differ diff --git a/.metadata/.plugins/org.eclipse.m2e.logback/logback.2.1.100.20230106-1511.xml b/.metadata/.plugins/org.eclipse.m2e.logback/logback.2.1.100.20230106-1511.xml new file mode 100644 index 00000000..abdf1c73 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.m2e.logback/logback.2.1.100.20230106-1511.xml @@ -0,0 +1,41 @@ + + + + %date [%thread] %-5level %logger{35} - %msg%n + + + OFF + + + + + ${org.eclipse.m2e.log.dir}/0.log + + ${org.eclipse.m2e.log.dir}/%i.log + 1 + 10 + + + 10MB + + + %date [%thread] %-5level %logger{35} - %msg%n + + + + + + WARN + + + + + + + + + + + + + diff --git a/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup b/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup new file mode 100644 index 00000000..1f73e14c --- /dev/null +++ b/.metadata/.plugins/org.eclipse.oomph.setup/workspace.setup @@ -0,0 +1,6 @@ + + diff --git a/.metadata/.plugins/org.eclipse.tips.ide/dialog_settings.xml b/.metadata/.plugins/org.eclipse.tips.ide/dialog_settings.xml new file mode 100644 index 00000000..5ca0b776 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.tips.ide/dialog_settings.xml @@ -0,0 +1,3 @@ + +
+
diff --git a/.metadata/.plugins/org.eclipse.ui.intro/introstate b/.metadata/.plugins/org.eclipse.ui.intro/introstate new file mode 100644 index 00000000..02f134f0 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.ui.intro/introstate @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml b/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml new file mode 100644 index 00000000..cc5a0219 --- /dev/null +++ b/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.metadata/version.ini b/.metadata/version.ini new file mode 100644 index 00000000..c005b7a5 --- /dev/null +++ b/.metadata/version.ini @@ -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 diff --git a/.project b/.project index 48129c11..c8e79578 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - PamGuard Main Tethys + PAMGuard diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 4df196f4..51bb81c3 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,4 +1,5 @@ eclipse.preferences.version=1 encoding//src/rawDeepLearningClassifer/segmenter/SegmenterProcess.java=UTF-8 +encoding//src/test/resources=UTF-8 encoding/=UTF-8 encoding/src=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index fbf1aacf..5c924643 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -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=18 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.compliance=18 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=18 diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index 77d475b0..d6c2ea8e 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -3,16 +3,17 @@ 4.0.0 org.pamguard Pamguard - Pamguard Java12+ - 2.02.10bd - Pamguard for Java 12+, using Maven to control dependcies + Pamguard + 2.02.11c + Pamguard using Maven to control dependencies www.pamguard.org Sea Mammal Research Unit, University of St. Andrews http://www.smru.st-andrews.ac.uk - src + ${basedir}/src + ${basedir}/src/test src @@ -54,7 +55,7 @@ maven-compiler-plugin - 3.8.1 + 3.12.1 org.eclipse.tycho @@ -63,22 +64,14 @@ - 11 + 21 jdt - - .settings/org.eclipse.jdt.core.prefs - org.openjfx javafx-maven-plugin - 0.0.6 - - 17 - 17 - 17 - + 0.0.8 maven-shade-plugin @@ -198,7 +191,7 @@ 11 11 UTF-8 - 16 + 21 2.4.0-b180830.0438 2.4.0-b180830.0438 2.4.0-b180830.0359 diff --git a/nullPamguardSettings_20171106_185953.psfx b/nullPamguardSettings_20171106_185953.psfx new file mode 100644 index 00000000..b2e2bed1 Binary files /dev/null and b/nullPamguardSettings_20171106_185953.psfx differ diff --git a/nullPamguardSettings_20240401_141954.psfx b/nullPamguardSettings_20240401_141954.psfx new file mode 100644 index 00000000..4896ca9b Binary files /dev/null and b/nullPamguardSettings_20240401_141954.psfx differ diff --git a/nullPamguardSettings_20240401_143317.psfx b/nullPamguardSettings_20240401_143317.psfx new file mode 100644 index 00000000..a77426c4 Binary files /dev/null and b/nullPamguardSettings_20240401_143317.psfx differ diff --git a/pom.xml b/pom.xml index 23a2f82a..ca91aa43 100644 --- a/pom.xml +++ b/pom.xml @@ -1,13 +1,12 @@ - 4.0.0 org.pamguard Pamguard - 2.02.10bd - Pamguard Java12+ - Pamguard for Java 12+, using Maven to control dependcies + 2.02.11c + Pamguard + Pamguard using Maven to control dependencies www.pamguard.org Sea Mammal Research Unit, University of St. Andrews @@ -15,80 +14,73 @@ - - 16 - 11 - 11 - UTF-8 - 2.4.0-b180830.0438 + + UTF-8 + 21 + 11 + 11 + + 2.4.0-b180830.0438 2.4.0-b180830.0359 2.4.0-b180830.0438 - - - - src - - - src - - META-INF/*.SF,META-INF/*.DSA,META-INF/*.RSA - **/*.java - jars/*.* - - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - - - 11 - jdt - - .settings/org.eclipse.jdt.core.prefs - - - - - - - org.eclipse.tycho - tycho-compiler-jdt - 1.5.1 - - - - - - - org.openjfx - javafx-maven-plugin - 0.0.6 - - 17 - 17 - 17 - - + + + + ${basedir}/src + ${basedir}/src/test + + + src + + META-INF/*.SF,META-INF/*.DSA,META-INF/*.RSA + **/*.java + jars/*.* + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.12.1 + + + 21 + jdt + + + + + org.eclipse.tycho + tycho-compiler-jdt + 1.5.1 + + + + + + + org.openjfx + javafx-maven-plugin + 0.0.8 + + @@ -135,114 +127,114 @@ - + + - - com.github.marschall - jdeps-maven-plugin - 0.5.1 - - - - - org.apache.maven.plugins - maven-dependency-plugin - 3.1.1 - - - copy-dependencies - package - - copy-dependencies - - - ${project.build.directory}/tempDependencies - false - false - true - - - - - - - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.apache.maven.plugins - - - maven-dependency-plugin - - - [3.1.1,) - - - - purge-local-repository - - - - - - - - - - - - - - - - - - - com.github.marschall - jdeps-maven-plugin - 0.5.1 - - - - - - - - + this site for details: https://maven.apache.org/plugins/maven-jdeps-plugin/index.html --> + + com.github.marschall + jdeps-maven-plugin + 0.5.1 + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/tempDependencies + false + false + true + + + + + + + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.apache.maven.plugins + + + maven-dependency-plugin + + + [3.1.1,) + + + + purge-local-repository + + + + + + + + + + + + + + + + + + + com.github.marschall + jdeps-maven-plugin + 0.5.1 + + + + + + + @@ -256,35 +248,18 @@ file://${project.basedir}/repo - - - - - - - - - - - - - - - - false - - unidata-all - Unidata netCDF - https://artifacts.unidata.ucar.edu/repository/unidata-all/ - - - + + + false + + unidata-all + Unidata netCDF + https://artifacts.unidata.ucar.edu/repository/unidata-all/ + + + - - - - - false - - bedatadriven - bedatadriven_renjin - https://nexus.bedatadriven.com/content/groups/public/ - - - - - false - - talan - talan - https://nexus.talanlabs.com/content/repositories/releases/ - - - - central - https://repo1.maven.org/maven2 - - - + + + + + false + + bedatadriven + bedatadriven_renjin + https://nexus.bedatadriven.com/content/groups/public/ + + + + + false + + talan + talan + https://nexus.talanlabs.com/content/repositories/releases/ + + + + central + https://repo1.maven.org/maven2 + + + - - + + + + + + io.github.macster110 + jpamutils + 0.0.58 + - - - io.github.macster110 - jpamutils - 0.0.56 - - - - - io.github.macster110 - jdl4pam - 0.0.94 - - - - - gov.nist.math - jama - 1.0.3 - - - - + + io.github.macster110 + jdl4pam + 0.0.98 + + + + + gov.nist.math + jama + 1.0.3 + + + + - - org.openjfx - javafx-controls - ${javafx.version} - + + org.openjfx + javafx-controls + ${javafx.version} + - - + - - org.openjfx - javafx-swing - ${javafx.version} - - - - + - - org.openjfx - javafx-media - ${javafx.version} - + + org.openjfx + javafx-media + ${javafx.version} + - - + - - org.openjfx - javafx-web - ${javafx.version} - + + org.openjfx + javafx-web + ${javafx.version} + + + + + net.synedra + validatorfx + 0.4.2 + + + + + org.apache.commons + commons-compress + 1.19 + - - - net.synedra - validatorfx - 0.4.0 - - - - - org.apache.commons - commons-compress - 1.19 - - - - - org.apache.commons - commons-csv - 1.7 - - - - - commons-io - commons-io - 2.6 - - - - - org.apache.commons - commons-lang3 - 3.9 - - - - - org.apache.commons - commons-math3 - 3.6.1 - - - - - org.apache.commons - commons-math - 2.2 - - - - - commons-net - commons-net - 3.6 - - - - - org.controlsfx - controlsfx - 11.0.0 - - - org.openjfx - javafx-base - - - org.openjfx - javafx-controls - - - org.openjfx - javafx-graphics - - - org.openjfx - javafx-media - - - org.openjfx - javafx-web - - - - - - + + org.apache.commons + commons-csv + 1.7 + + + + + commons-io + commons-io + 2.6 + + + + + org.apache.commons + commons-lang3 + 3.9 + + + + + org.apache.commons + commons-math3 + 3.6.1 + + + + + org.apache.commons + commons-math + 2.2 + + + + + commons-net + commons-net + 3.6 + + + + + org.controlsfx + controlsfx + 11.2.0 + + + org.openjfx + javafx-base + + + org.openjfx + javafx-controls + + + org.openjfx + javafx-graphics + + + org.openjfx + javafx-media + + + org.openjfx + javafx-web + + + + + + - - org.kordamp.ikonli - ikonli-javafx - 12.2.0 - - - - + + org.kordamp.ikonli + ikonli-swing + 12.3.1 + + + org.kordamp.ikonli - ikonli-fontawesome5-pack - 12.2.0 + ikonli-materialdesign2-pack + 12.3.1 + + + + + org.kordamp.ikonli + ikonli-fileicons-pack + 12.3.1 - --> - - - org.kordamp.ikonli - ikonli-materialdesign2-pack - 12.2.0 - + + + net.sf.geographiclib + GeographicLib-Java + 1.50 + + + + - - - net.sf.geographiclib - GeographicLib-Java - 1.50 - - - - - org.jogamp.gluegen - gluegen-rt-main - 2.3.2 - - - - - - - - - com.healthmarketscience.jackcess - jackcess - 3.0.1 - - - - - com.fasterxml.jackson.core - jackson-databind - 2.10.1 - - - - - org.jogamp.jogl - jogl-all-main - 2.3.2 - - - + + + + com.fasterxml.jackson.core + jackson-databind + 2.10.1 + + + + + - - - - org.jflac - jflac-codec - 1.5.2 - - - - - javax.help - javahelp - 2.0.05 - - - - - net.java.dev.jna - jna - 5.5.0 - - + + + + org.jflac + jflac-codec + 1.5.2 + + + + + javax.help + javahelp + 2.0.05 + + + + + net.java.dev.jna + jna + 5.5.0 + + - - net.java.dev.jna - jna-platform - 5.5.0 - + + net.java.dev.jna + jna-platform + 5.5.0 + + + + + com.jcraft + jsch + 0.1.55 + + + + + com.fazecast + jSerialComm + 2.5.3 + - - - com.jcraft - jsch - 0.1.55 - + + + edu.emory.mathcs + JTransforms + 2.4 + - - - com.fazecast - jSerialComm - 2.5.3 - + + + com.sun.mail + javax.mail + 1.6.2 + - - - edu.emory.mathcs - JTransforms - 2.4 - - - - com.sun.mail - javax.mail - 1.6.2 - + + + com.drewnoakes + metadata-extractor + 2.12.0 + - - - com.diffplug.matsim - matfilerw - 3.1.1 - + + + mysql + mysql-connector-java + 8.0.18 + - - - com.drewnoakes - metadata-extractor - 2.12.0 - - - - - mysql - mysql-connector-java - 8.0.18 - - - - - + + - + + com.google.protobuf + protobuf-java + 3.17.0 + + + + + + edu.ucar + netcdfAll + 4.6.14 + + com.google.protobuf protobuf-java - 3.17.0 - + + + com.google.protobuf + protobuf-java-util + + + compile + - - - - edu.ucar - netcdfAll - 4.6.14 - - - com.google.protobuf - protobuf-java - - - com.google.protobuf - protobuf-java-util - - - compile - + + + com.opencsv + opencsv + 5.0 + - - - com.opencsv - opencsv - 5.0 - + - + + + org.postgresql + postgresql + 42.2.24 + - - - org.postgresql - postgresql - 42.2.24 - - - - - org.renjin - renjin-script-engine - 0.9.2725 - - - + + org.renjin + renjin-script-engine + 0.9.2725 + + + - - org.slf4j - slf4j-api - 1.8.0-beta4 - - - - - - org.slf4j - slf4j-nop - 1.8.0-beta4 - + + org.slf4j + slf4j-nop + 1.8.0-beta4 + + + - - - + - - - org.docx4j - docx4j-JAXB-ReferenceImpl - 11.1.3 - - + - - - org.xerial - sqlite-jdbc - 3.28.0 - + + + org.xerial + sqlite-jdbc + 3.28.0 + - - - net.sf.ucanaccess - ucanaccess - 5.0.1 - + + + net.sf.ucanaccess + ucanaccess + 4.0.4 + - - - nz.ac.waikato.cms.weka - weka-dev - 3.7.7 - + + + nz.ac.waikato.cms.weka + weka-dev + 3.7.7 + - - - javax.vecmath - vecmath - 1.5.2 - + + + javax.vecmath + vecmath + 1.5.2 + - - - + + + + + org.eclipse.persistence + org.eclipse.persistence.moxy + 2.5.0 + + + javax.xml.bind + jaxb-api + ${jaxb.api.version} + + + org.glassfish.jaxb + jaxb-runtime + ${jaxb.runtime.version} + + + org.glassfish.jaxb + jaxb-xjc + ${jaxb.xjc.version} + + + + + + com.sun.jersey.contribs + jersey-multipart + 1.18.1 + + + + + commons-cli + commons-cli + 1.2 + + + + + org.apache.poi + poi + 3.10-beta1 + + + + + com.sun.jersey + jersey-client + 1.18.1 + + + + + com.sun.jersey.contribs + jersey-apache-client + 1.18.1 + + + + + com.miglayout + miglayout + 3.7.4 + + + + + ca.juliusdavies + not-yet-commons-ssl + 0.3.11 + + + + + javax.ws.rs + javax.ws.rs-api + 2.1.1 + + + + + javax.xml.bind + jaxb-api + 2.2.11 + + + + + com.sun.xml.bind + jaxb-impl + 2.2.11 + + + + + javax.activation + activation + 1.1 + + + + + org.glassfish.jaxb + jaxb-core + 2.2.11 + + + + + org.glassfish.jersey.core + jersey-common + 2.2 + + + + + org.apache.commons + commons-text + 1.9 + + + + + + + + tethys.org + nilus + 3.0 + - - - org.eclipse.persistence - org.eclipse.persistence.moxy - 2.5.0 - - - javax.xml.bind - jaxb-api - ${jaxb.api.version} - - - org.glassfish.jaxb - jaxb-runtime - ${jaxb.runtime.version} - - - org.glassfish.jaxb - jaxb-xjc - ${jaxb.xjc.version} - - - - com.sun.jersey.contribs - jersey-multipart - 1.18.1 - - - commons-cli - commons-cli - 1.2 - - - org.apache.poi - poi - 3.10-beta1 - - - com.sun.jersey - jersey-client - 1.18.1 - - - com.sun.jersey.contribs - jersey-apache-client - 1.18.1 - - - com.miglayout - miglayout - 3.7.4 - - - ca.juliusdavies - not-yet-commons-ssl - 0.3.11 - - - javax.ws.rs - javax.ws.rs-api - 2.1.1 - - - javax.xml.bind - jaxb-api - 2.2.11 - - - com.sun.xml.bind - jaxb-impl - 2.2.11 - - - javax.activation - activation - 1.1 - - - org.glassfish.jaxb - jaxb-core - 2.2.11 - - - org.glassfish.jersey.core - jersey-common - 2.2 - - - org.apache.commons - commons-text - 1.9 - + + tethys.org + javaclient + 3.0 + - - + - pamguard.org + org.pamguard x3 2.2.7 - + + + + it.sauronsoftware + jave + 1.0.2 + + + + + com.synthbot + jasiohost + 1.0.0 + - - tethys.org - nilus - 3.0 - - - - tethys.org - javaclient - 3.0 - - - - - - it.sauronsoftware - jave - 1.0.2 - - - - - com.synthbot - jasiohost - 1.0.0 - - - - + + + + + org.springframework + spring-core + 5.2.3.RELEASE + + + + + + com.1stleg + jnativehook + 2.1.0 + + + + + + org.swinglabs.swingx + swingx-all + 1.6.5-1 + - - - org.springframework - spring-core - 5.2.3.RELEASE - + + + + org.fxyz3d + fxyz3d + 0.6.0 + + + + + io.github.mkpaz + atlantafx-base + 2.0.1 + - - - - com.1stleg - jnativehook - 2.1.0 - - - - - - org.swinglabs.swingx - swingx-all - 1.6.5-1 - - - - - io.github.mkpaz - atlantafx-base - 1.0.0 - - - - + \ No newline at end of file diff --git a/repo/pamguard/org/x3/2.2.6/x3-2.2.6.pom b/repo/pamguard/org/x3/2.2.6/x3-2.2.6.pom index 609bc1d5..8d48a0d5 100644 --- a/repo/pamguard/org/x3/2.2.6/x3-2.2.6.pom +++ b/repo/pamguard/org/x3/2.2.6/x3-2.2.6.pom @@ -1,9 +1,15 @@ - 4.0.0 +<<<<<<<< HEAD:repo/tethys/org/javaclient/3.0/javaclient-3.0.pom + tethys.org + javaclient + 3.0 +======== pamguard.org x3 2.2.6 +>>>>>>>> upstream/main:repo/pamguard/org/x3/2.2.6/x3-2.2.6.pom POM was created from install:install-file diff --git a/repo/pamguard/org/x3/2.2.7/x3-2.2.7.pom b/repo/pamguard/org/x3/2.2.7/x3-2.2.7.pom index 7eeede30..dc2f63cc 100644 --- a/repo/pamguard/org/x3/2.2.7/x3-2.2.7.pom +++ b/repo/pamguard/org/x3/2.2.7/x3-2.2.7.pom @@ -2,8 +2,14 @@ 4.0.0 +<<<<<<<< HEAD:repo/tethys/org/nilus/3.0/nilus-3.0.pom + tethys.org + nilus + 3.0 +======== pamguard.org x3 2.2.7 +>>>>>>>> upstream/main:repo/pamguard/org/x3/2.2.7/x3-2.2.7.pom POM was created from install:install-file diff --git a/repo/tethys/org/javaclient/3.0/javaclient-3.0.pom b/repo/tethys/org/javaclient/3.0/javaclient-3.0.pom index 80fee21c..8d48a0d5 100644 --- a/repo/tethys/org/javaclient/3.0/javaclient-3.0.pom +++ b/repo/tethys/org/javaclient/3.0/javaclient-3.0.pom @@ -2,8 +2,14 @@ 4.0.0 +<<<<<<<< HEAD:repo/tethys/org/javaclient/3.0/javaclient-3.0.pom tethys.org javaclient 3.0 +======== + pamguard.org + x3 + 2.2.6 +>>>>>>>> upstream/main:repo/pamguard/org/x3/2.2.6/x3-2.2.6.pom POM was created from install:install-file diff --git a/repo/tethys/org/nilus/3.0/nilus-3.0.pom b/repo/tethys/org/nilus/3.0/nilus-3.0.pom index ba47dee9..dc2f63cc 100644 --- a/repo/tethys/org/nilus/3.0/nilus-3.0.pom +++ b/repo/tethys/org/nilus/3.0/nilus-3.0.pom @@ -2,8 +2,14 @@ 4.0.0 +<<<<<<<< HEAD:repo/tethys/org/nilus/3.0/nilus-3.0.pom tethys.org nilus 3.0 +======== + pamguard.org + x3 + 2.2.7 +>>>>>>>> upstream/main:repo/pamguard/org/x3/2.2.7/x3-2.2.7.pom POM was created from install:install-file diff --git a/src/Acquisition/FileInputSystem.java b/src/Acquisition/FileInputSystem.java index 87f86046..33779534 100644 --- a/src/Acquisition/FileInputSystem.java +++ b/src/Acquisition/FileInputSystem.java @@ -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) { diff --git a/src/Acquisition/filedate/FileDateDialogStrip.java b/src/Acquisition/filedate/FileDateDialogStrip.java index 836850e6..fb97ab26 100644 --- a/src/Acquisition/filedate/FileDateDialogStrip.java +++ b/src/Acquisition/filedate/FileDateDialogStrip.java @@ -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,6 +16,9 @@ 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.dialog.PamGridBagContraints; @@ -35,7 +39,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(MaterialDesignC.COG, 20, Color.DARK_GRAY); + private Window parent; diff --git a/src/Acquisition/filedate/StandardFileDate.java b/src/Acquisition/filedate/StandardFileDate.java index 53033f60..54f6d998 100644 --- a/src/Acquisition/filedate/StandardFileDate.java +++ b/src/Acquisition/filedate/StandardFileDate.java @@ -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; diff --git a/src/Acquisition/layoutFX/AcquisitionPaneFX.java b/src/Acquisition/layoutFX/AcquisitionPaneFX.java index 7f866f7c..59400599 100644 --- a/src/Acquisition/layoutFX/AcquisitionPaneFX.java +++ b/src/Acquisition/layoutFX/AcquisitionPaneFX.java @@ -290,7 +290,7 @@ public class AcquisitionPaneFX extends SettingsPane{ //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(); diff --git a/src/Acquisition/layoutFX/CheckWavHeadersPane.java b/src/Acquisition/layoutFX/CheckWavHeadersPane.java index c97cd6d4..4f849e86 100644 --- a/src/Acquisition/layoutFX/CheckWavHeadersPane.java +++ b/src/Acquisition/layoutFX/CheckWavHeadersPane.java @@ -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; diff --git a/src/Acquisition/layoutFX/FileDataDialogStripFX.java b/src/Acquisition/layoutFX/FileDataDialogStripFX.java index e4fae902..7658f7da 100644 --- a/src/Acquisition/layoutFX/FileDataDialogStripFX.java +++ b/src/Acquisition/layoutFX/FileDataDialogStripFX.java @@ -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(); } diff --git a/src/Acquisition/layoutFX/FolderInputPane.java b/src/Acquisition/layoutFX/FolderInputPane.java index d815fd06..cf0b0e3e 100644 --- a/src/Acquisition/layoutFX/FolderInputPane.java +++ b/src/Acquisition/layoutFX/FolderInputPane.java @@ -193,20 +193,18 @@ public class FolderInputPane extends DAQSettingsPane{ //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); }); diff --git a/src/Acquisition/layoutFX/OfflineDAQPane.java b/src/Acquisition/layoutFX/OfflineDAQPane.java index f5ff7726..56cb2003 100644 --- a/src/Acquisition/layoutFX/OfflineDAQPane.java +++ b/src/Acquisition/layoutFX/OfflineDAQPane.java @@ -30,8 +30,7 @@ public class OfflineDAQPane extends SettingsPane{ private PamBorderPane mainPane; - public OfflineDAQPane(OfflineFileDataStore acquisitionControl, - AcquisitionPaneFX acquisitionPaneFX){ + public OfflineDAQPane(OfflineFileDataStore acquisitionControl){ super(null); this.mainPane= new PamBorderPane(); mainPane.setCenter(createOfflinePane()); diff --git a/src/Acquisition/pamAudio/PamAudioFileManager.java b/src/Acquisition/pamAudio/PamAudioFileManager.java index 76fc6910..bf13f904 100644 --- a/src/Acquisition/pamAudio/PamAudioFileManager.java +++ b/src/Acquisition/pamAudio/PamAudioFileManager.java @@ -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 getAudioFileLoaders() { return this.pamAudioFileTypes; diff --git a/src/Acquisition/pamAudio/WavAudioFile.java b/src/Acquisition/pamAudio/WavAudioFile.java index 77d07b54..e8789da0 100644 --- a/src/Acquisition/pamAudio/WavAudioFile.java +++ b/src/Acquisition/pamAudio/WavAudioFile.java @@ -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(); + } } diff --git a/src/Acquisition/pamAudio/WavFileInputStream.java b/src/Acquisition/pamAudio/WavFileInputStream.java index a0f2c0e5..fbb2126a 100644 --- a/src/Acquisition/pamAudio/WavFileInputStream.java +++ b/src/Acquisition/pamAudio/WavFileInputStream.java @@ -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) { diff --git a/src/Acquisition/sud/SUDFileTime.java b/src/Acquisition/sud/SUDFileTime.java index 92ac94ca..c0ccc16b 100644 --- a/src/Acquisition/sud/SUDFileTime.java +++ b/src/Acquisition/sud/SUDFileTime.java @@ -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; diff --git a/src/Acquisition/sud/SUDNotificationManager.java b/src/Acquisition/sud/SUDNotificationManager.java index 28bed217..b34758f2 100644 --- a/src/Acquisition/sud/SUDNotificationManager.java +++ b/src/Acquisition/sud/SUDNotificationManager.java @@ -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); } } diff --git a/src/Array/ArrayManager.java b/src/Array/ArrayManager.java index 445688ea..3fa7a21a 100644 --- a/src/Array/ArrayManager.java +++ b/src/Array/ArrayManager.java @@ -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> hydrophoneImportManager; + private ImportDataSystem hydrophoneImportManager; private ImportDataSystem> 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>(new HydrophoneImport(hydrophonesProcess.getHydrophoneDataBlock())); + hydrophoneImportManager= new ImportDataSystem(new HydrophoneImport(hydrophonesProcess.getHydrophoneDataBlock())); hydrophoneImportManager.setName("Hydrophone Data Import"); streamerImportManager = new ImportDataSystem>(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); diff --git a/src/Array/Hydrophone.java b/src/Array/Hydrophone.java index c2f82bd5..be3b11f8 100644 --- a/src/Array/Hydrophone.java +++ b/src/Array/Hydrophone.java @@ -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; } diff --git a/src/Array/HydrophoneDataBlock.java b/src/Array/HydrophoneDataBlock.java index 43ec7fb3..aaa16f6d 100644 --- a/src/Array/HydrophoneDataBlock.java +++ b/src/Array/HydrophoneDataBlock.java @@ -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 { @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 { } 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 { } newdifference = Math.abs(startTime- preceedingUnit.getTimeMilliseconds()); if (newdifference > difference) { +// System.out.println("Hydrophone datablock: newDifference: " + newdifference + " " + unit.getHydrophone().getZ()); return unit; } else { diff --git a/src/Array/HydrophoneElementDialog.java b/src/Array/HydrophoneElementDialog.java index a3f482cd..76eabacd 100644 --- a/src/Array/HydrophoneElementDialog.java +++ b/src/Array/HydrophoneElementDialog.java @@ -397,6 +397,8 @@ public class HydrophoneElementDialog extends PamDialog { dz.setText(null); } } + + boolean getParams() { double zCoeff = PamController.getInstance().getGlobalMediumManager().getZCoeff(); diff --git a/src/Array/PamArray.java b/src/Array/PamArray.java index a116108d..8bc8fd8d 100644 --- a/src/Array/PamArray.java +++ b/src/Array/PamArray.java @@ -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(); diff --git a/src/Array/Streamer.java b/src/Array/Streamer.java index 7910da0f..f7fad355 100644 --- a/src/Array/Streamer.java +++ b/src/Array/Streamer.java @@ -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, diff --git a/src/Array/StreamerDialog.java b/src/Array/StreamerDialog.java index 73a81469..52bea18c 100644 --- a/src/Array/StreamerDialog.java +++ b/src/Array/StreamerDialog.java @@ -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"); } diff --git a/src/Array/importHydrophoneData/HydrophoneImport.java b/src/Array/importHydrophoneData/HydrophoneImport.java index 93a924be..1d634d39 100644 --- a/src/Array/importHydrophoneData/HydrophoneImport.java +++ b/src/Array/importHydrophoneData/HydrophoneImport.java @@ -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>{ - - String[] extensionStrings={".csv"}; +public class HydrophoneImport extends DataImport{ + + String[] extensionStrings={".csv", ".mat"}; private ArrayList> 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>{ * */ 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>{ @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>{ if ((hydrophonePositions.get(0).size()-1)%6==0){ return convertToHydrophoneList(hydrophonePositions); } - + } } - + if (filePath.endsWith(".mat")){ - - hydrophonePositions=importPositionsFromMatlab(filePath); - + + ArrayList 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> convertToHydrophoneList(ArrayList> importData){ - - ArrayList> hydrophonesAll=new ArrayList>(); + public ArrayList convertToHydrophoneList(ArrayList> importData){ + + ArrayList hydrophonesAll=new ArrayList(); ArrayList tempArray; Hydrophone hydrophone; double[] cOordinates; double [] cOordinateErrors; + double sensitivity; + double gain; for (int i=0; i(); 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> importPositionsFromMatlab( + + /** + * Import the hydrophone positions from a MATLAB mat file. + * @param filePath - the file path. + * @return an array of hydrophones. + */ + private static ArrayList importPositionsFromMatlab( String filePath) { - // TODO- needs to be implemented. + + try { + + ArrayList hydrophones = new ArrayList(); + 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>{ } @Override - public boolean isDataFormatOK(ArrayList 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>{ * 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 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 data = importPositionsFromMatlab(file); + + System.out.println("Impotred data size: " + data.size()); + System.out.println(data.get(0)); + + } + } diff --git a/src/Array/layoutFX/Array3DPane.java b/src/Array/layoutFX/Array3DPane.java new file mode 100644 index 00000000..f7b9c8b4 --- /dev/null +++ b/src/Array/layoutFX/Array3DPane.java @@ -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. + *

+ PAMGUARD co-rdinate system is + *

+ * x points right + *

+ * y points north or into the screen + *

+ * z is height and points up + *

+ * This is different from the JavAFX 3D system in which + *

+ * x points right + *

+ * y points down + *

+ * z points into the screen + *

+ * 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 hydrophonesSpheres = new ArrayList(); + + /** + * 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() { + + @Override public void handle(MouseEvent me) { + mousePosX = me.getSceneX(); + mousePosY = me.getSceneY(); + mouseOldX = me.getSceneX(); + mouseOldY = me.getSceneY(); + } + }); + + scene.setOnScroll(new EventHandler() { + @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() { + + + @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 hydrophones = array.getHydrophoneArray(); + + //draw hydrophones + HydrophoneSphere sphere; + Streamer streamer; + hydrophonesSpheres.clear(); + for (int i=0; i streamerPoints=new ArrayList(); + //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> 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 streamerPoints; + // + // for (int i=0; i(); + // for (int j=0; j + * + */ public class ArraySettingsPane extends SettingsPane{ + /** + * 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 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) 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 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); + } + } } diff --git a/src/Array/layoutFX/DefaultHydrophone.java b/src/Array/layoutFX/DefaultHydrophone.java new file mode 100644 index 00000000..5d571775 --- /dev/null +++ b/src/Array/layoutFX/DefaultHydrophone.java @@ -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; + } + + +} diff --git a/src/Array/layoutFX/HydrophoneProperty.java b/src/Array/layoutFX/HydrophoneProperty.java new file mode 100644 index 00000000..72679010 --- /dev/null +++ b/src/Array/layoutFX/HydrophoneProperty.java @@ -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; + } + +} diff --git a/src/Array/layoutFX/HydrophoneSettingsPane.java b/src/Array/layoutFX/HydrophoneSettingsPane.java new file mode 100644 index 00000000..0e8f4507 --- /dev/null +++ b/src/Array/layoutFX/HydrophoneSettingsPane.java @@ -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 { + + 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 hSens; + private PamSpinner preampGain; + + private ComboBox streamers; + private ChoiceBox 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 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(); + mainControls.add(streamers, 1, gridy); + + gridy++; + mainControls.add(recieverTypeLabel = new Label(""), 0, gridy); + recieverTypeLabel.setAlignment(Pos.CENTER_LEFT); + defaultHydro = new ComboBox(); + + for (int i=0; i{ + //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.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.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(); + + } + + + + + + +} diff --git a/src/Array/layoutFX/HydrophonesPane.java b/src/Array/layoutFX/HydrophonesPane.java new file mode 100644 index 00000000..4d574fa4 --- /dev/null +++ b/src/Array/layoutFX/HydrophonesPane.java @@ -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 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 hydrophoneChangeListeners = new ArrayList(); + + 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 { + + + /** + * The z table + */ + private TableColumn z; + + public HydrophoneTable(ObservableList hydrophoneData) { + super(hydrophoneData); + + z = new TableColumn("depth"); + z.setCellValueFactory(cellData -> cellData.getValue().getZ().multiply(PamController.getInstance().getGlobalMediumManager().getZCoeff())); + z.setEditable(true); + + //need to set up all the rows. + TableColumn hydroID = new TableColumn("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, TableCell> 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, TableCell> cellFactory = col -> { + TableCell 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 x = new TableColumn("x"); + x.setCellValueFactory(cellData -> cellData.getValue().getX()); + x.setEditable(true); + + TableColumn y = new TableColumn("y"); + y.setCellValueFactory(cellData -> cellData.getValue().getY()); + y.setEditable(true); + + + TableColumn posColumn=new TableColumn("Position (m)"); + posColumn.getColumns().addAll(x, y, z); + + TableColumn xErr = new TableColumn("x"); + xErr.setCellValueFactory(cellData -> cellData.getValue().getXErr()); + xErr.setEditable(true); + + TableColumn yErr = new TableColumn("y"); + yErr.setCellValueFactory(cellData -> cellData.getValue().getYErr()); + yErr.setEditable(true); + + TableColumn zErr = new TableColumn("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 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 getZColumn() { + return z; + } + + /** + * Get the current streamers. + * @return the current streamers. + */ + public ObservableList getHydrophones() { + return getData(); + } + + + } + + public void setParams(PamArray currentArray) { + this.currentArray=currentArray; + + tableArrayPane.getHydrophones().clear(); + + for (int i=0; i getHydrophoneList() { + return hydrophoneList; + } + + public void setHydrophoneList(ObservableList 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(); + } + +} diff --git a/src/Array/layoutFX/InterpChoicePane.java b/src/Array/layoutFX/InterpChoicePane.java new file mode 100644 index 00000000..b5e7613b --- /dev/null +++ b/src/Array/layoutFX/InterpChoicePane.java @@ -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 interpChoiceBox; + + public InterpChoicePane() { + + interpChoiceBox = new ChoiceBox(); + 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< { + + 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 + + } + + + +} diff --git a/src/Array/layoutFX/SensorSourcePane.java b/src/Array/layoutFX/SensorSourcePane.java new file mode 100644 index 00000000..9c6f61f0 --- /dev/null +++ b/src/Array/layoutFX/SensorSourcePane.java @@ -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 sensorDropDown; + + private ArrayList 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 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 getDataBlocks() { + ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(ArraySensorDataUnit.class, true); + ArrayList 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; + } +} diff --git a/src/Array/layoutFX/StreamerProperty.java b/src/Array/layoutFX/StreamerProperty.java new file mode 100644 index 00000000..eb82adc5 --- /dev/null +++ b/src/Array/layoutFX/StreamerProperty.java @@ -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; + } + + + +} diff --git a/src/Array/layoutFX/StreamerSettingsPane.java b/src/Array/layoutFX/StreamerSettingsPane.java new file mode 100644 index 00000000..f2880f52 --- /dev/null +++ b/src/Array/layoutFX/StreamerSettingsPane.java @@ -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 { + + private final static double MAX_TEXTFIELD_WIDTH = 80; + + + public PamBorderPane mainPane; + + /** + * Combo Box which shows which origin methods are available. + */ + private ComboBox 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(); + 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=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; + + } +} diff --git a/src/Array/layoutFX/StreamersPane.java b/src/Array/layoutFX/StreamersPane.java new file mode 100644 index 00000000..b5adf047 --- /dev/null +++ b/src/Array/layoutFX/StreamersPane.java @@ -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 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 streamerChangeListeners = new ArrayList(); + + + 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 { + + private TableColumn z; + + + public BasicArrayTable(ObservableList data) { + super(data); + //need to set up all the rows. + TableColumn streamerID = new TableColumn("ID"); + streamerID.setCellValueFactory(cellData -> cellData.getValue().getID()); + streamerID.setEditable(false); + + TableColumn name = new TableColumn("Name"); + name.setCellValueFactory(cellData -> cellData.getValue().getName()); + name.setEditable(true); + + + TableColumn x = new TableColumn("x"); + x.setCellValueFactory(cellData -> cellData.getValue().getX()); + x.setEditable(false); + + TableColumn y = new TableColumn("y"); + y.setCellValueFactory(cellData -> cellData.getValue().getY()); + y.setEditable(false); + + z = new TableColumn("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 reference = new TableColumn("Reference"); + reference.setCellValueFactory(cellData -> cellData.getValue().getHydrophoneOrigin()); + reference.setEditable(true); + + TableColumn locator = new TableColumn("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 getStreamers() { + return getData(); + } + + @Override + public void dialogClosed(StreamerProperty data) { + Streamer hydro = streamerPane.getParams(data.getStreamer()); + data.setStreamer(hydro); + } + + @Override + public Dialog 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 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; itableArrayPane.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 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; + + } + + +} diff --git a/src/Array/streamerOrigin/OriginDialogComponent.java b/src/Array/streamerOrigin/OriginDialogComponent.java index f6ab058b..26dbcb79 100644 --- a/src/Array/streamerOrigin/OriginDialogComponent.java +++ b/src/Array/streamerOrigin/OriginDialogComponent.java @@ -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(); + + } diff --git a/src/Array/streamerOrigin/StaticHydrophonePane.java b/src/Array/streamerOrigin/StaticHydrophonePane.java new file mode 100644 index 00000000..fc85a583 --- /dev/null +++ b/src/Array/streamerOrigin/StaticHydrophonePane.java @@ -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); + } + +} diff --git a/src/Array/streamerOrigin/StaticOriginMethod.java b/src/Array/streamerOrigin/StaticOriginMethod.java index 8c413c9c..4f42c922 100644 --- a/src/Array/streamerOrigin/StaticOriginMethod.java +++ b/src/Array/streamerOrigin/StaticOriginMethod.java @@ -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< { 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 { /** * 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 { 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); diff --git a/src/Localiser/LocaliserPane.java b/src/Localiser/LocaliserPane.java index 4ca335cf..e275a3f8 100644 --- a/src/Localiser/LocaliserPane.java +++ b/src/Localiser/LocaliserPane.java @@ -1,5 +1,17 @@ package Localiser; -public interface LocaliserPane { +import PamController.SettingsPane; + +public abstract class LocaliserPane extends SettingsPane { + + public LocaliserPane() { + super(null); + // TODO Auto-generated constructor stub + } + + + + + } diff --git a/src/Localiser/ModelControlPanel.java b/src/Localiser/ModelControlPanel.java index 21172a14..2b63bfa6 100644 --- a/src/Localiser/ModelControlPanel.java +++ b/src/Localiser/ModelControlPanel.java @@ -152,8 +152,7 @@ public class ModelControlPanel { @Override //settings panel public void actionPerformed(ActionEvent arg0) { - model.getSettingsPane(); - + model.getAlgorithmSettingsPane(); //AWT implementation. } } diff --git a/src/Localiser/algorithms/Correlations.java b/src/Localiser/algorithms/Correlations.java index 7b726338..294ad138 100644 --- a/src/Localiser/algorithms/Correlations.java +++ b/src/Localiser/algorithms/Correlations.java @@ -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)

diff --git a/src/Localiser/algorithms/genericLocaliser/Chi2TimeDelays.java b/src/Localiser/algorithms/genericLocaliser/Chi2TimeDelays.java index 093d8fd7..c757c009 100644 --- a/src/Localiser/algorithms/genericLocaliser/Chi2TimeDelays.java +++ b/src/Localiser/algorithms/genericLocaliser/Chi2TimeDelays.java @@ -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. */ diff --git a/src/Localiser/algorithms/genericLocaliser/MCMC/MCMC.java b/src/Localiser/algorithms/genericLocaliser/MCMC/MCMC.java index a1ebc80b..1413e943 100644 --- a/src/Localiser/algorithms/genericLocaliser/MCMC/MCMC.java +++ b/src/Localiser/algorithms/genericLocaliser/MCMC/MCMC.java @@ -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. *

-* 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 *

-* This is an abstract class and requires a chi2 function to operate. +* A chi2 function is required to define the minimisation problem. *

-* 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. *

* 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 successChi=new ArrayList(settings.numberOfJumps/5); - ArrayList successJump=new ArrayList(settings.numberOfJumps/5); + ArrayList successJump=new ArrayList(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 successJump, ArrayList successChi) { + public ChainResult(ArrayList successJump, ArrayList 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 successJump; + public ArrayList 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 data) { - ArrayList successJumpAll = new ArrayList(); - List successJump; + ArrayList successJumpAll = new ArrayList(); + List successJump; for (int i=0; i> getJumps() { ArrayList> jumps=new ArrayList>(); ArrayList chainJumps; - double[] ajump; + float[] ajump; for (int i=0; i(); for (int j=0; j0) 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; + } + } diff --git a/src/Localiser/algorithms/locErrors/EllipticalError.java b/src/Localiser/algorithms/locErrors/EllipticalError.java index 501cbeb2..9fc25df5 100644 --- a/src/Localiser/algorithms/locErrors/EllipticalError.java +++ b/src/Localiser/algorithms/locErrors/EllipticalError.java @@ -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("&ensp 2D Elliptical error:
&ensp Radii: "); for (int i=0; imax){ 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; imax){ 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 vectors=new ArrayList(); 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; diff --git a/src/Localiser/algorithms/locErrors/SimpleError.java b/src/Localiser/algorithms/locErrors/SimpleError.java index 02b64d88..2b00cb4a 100644 --- a/src/Localiser/algorithms/locErrors/SimpleError.java +++ b/src/Localiser/algorithms/locErrors/SimpleError.java @@ -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.); } /** diff --git a/src/Localiser/algorithms/locErrors/SimpleErrorXMLData.java b/src/Localiser/algorithms/locErrors/SimpleErrorJSONData.java similarity index 78% rename from src/Localiser/algorithms/locErrors/SimpleErrorXMLData.java rename to src/Localiser/algorithms/locErrors/SimpleErrorJSONData.java index b2942829..afffd260 100644 --- a/src/Localiser/algorithms/locErrors/SimpleErrorXMLData.java +++ b/src/Localiser/algorithms/locErrors/SimpleErrorJSONData.java @@ -1,14 +1,13 @@ package Localiser.algorithms.locErrors; -import java.text.DecimalFormat; -public class SimpleErrorXMLData extends ErrorXMLData { +public class SimpleErrorJSONData extends ErrorJSONData { private String errorType; private double[] errorData; - public SimpleErrorXMLData(String errorType, double[] errorData) { + public SimpleErrorJSONData(String errorType, double[] errorData) { this.errorType = errorType; this.errorData = errorData; } diff --git a/src/Localiser/algorithms/locErrors/SimpleLocErrorDraw.java b/src/Localiser/algorithms/locErrors/SimpleLocErrorDraw.java index 48c433be..148177ee 100644 --- a/src/Localiser/algorithms/locErrors/SimpleLocErrorDraw.java +++ b/src/Localiser/algorithms/locErrors/SimpleLocErrorDraw.java @@ -27,6 +27,7 @@ public class SimpleLocErrorDraw implements LocErrorGraphics { @Override public TransformShape drawOnMap(Graphics g, PamDataUnit pamDetection, LatLong errorOrigin, GeneralProjector generalProjector, Color ellipseColor) { + g.setColor(ellipseColor); if (simpleError == null) { return null; diff --git a/src/Localiser/algorithms/timeDelayLocalisers/hyperbolic/Hyperbolic.java b/src/Localiser/algorithms/timeDelayLocalisers/hyperbolic/Hyperbolic.java index 6aa2eaf7..4875aefa 100644 --- a/src/Localiser/algorithms/timeDelayLocalisers/hyperbolic/Hyperbolic.java +++ b/src/Localiser/algorithms/timeDelayLocalisers/hyperbolic/Hyperbolic.java @@ -17,6 +17,7 @@ import PamUtils.PamUtils; *

* The hyperbolic localiser also includes an error estimation by sampling time delays from a distribution of errors. As a localisation has to * occur for each sample, this drastically increases the computational time for the hyperbolic localiser. + * * @author Jamie Macaulay * */ @@ -62,7 +63,7 @@ public class Hyperbolic implements TimeDelayLocaliserModel { * @param timeDelays - time delay values in seconds. Each is a list of time delays using to indexM1 and indexM2 conventions and corresponding to hydrophones in hydrophoneArray list. * @param timeDelayErrors - time delay error values in seconds. Each is a list of time delay errors using to indexM1 and indexM2 conventions and corresponding to hydrophones in hydrophoneArray list. * @param speedOfSound - the speed of sound in m/s - * @param hyperbolicParams - hyperbolic paramaters to use for this instance of the localiser. + * @param hyperbolicParams - hyperbolic parameters to use for this instance of the localiser. */ public Hyperbolic(ArrayList> hydrophoneArray ,ArrayList> timeDelays, ArrayList< ArrayList> timeDelayErrors, float speedOfSound, HyperbolicParams hyperbolicParams){ this.speedOfSound=speedOfSound; diff --git a/src/Localiser/controls/MCMCPane.java b/src/Localiser/controls/MCMCPane.java new file mode 100644 index 00000000..8674a419 --- /dev/null +++ b/src/Localiser/controls/MCMCPane.java @@ -0,0 +1,305 @@ +package Localiser.controls; + +import Localiser.algorithms.genericLocaliser.MCMC.MCMCParams2; +import PamController.SettingsPane; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.Spinner; +import javafx.scene.control.ComboBox; + +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Pane; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamGridPane; +import pamViewFX.fxNodes.PamHBox; +import pamViewFX.fxNodes.PamSpinner; +import pamViewFX.fxNodes.PamVBox; + +public class MCMCPane extends SettingsPane { + + private PamBorderPane mainPane; + + private double JUMP_SPINNER_WIDTH = 60; + + + //controls + private PamSpinner numJumps; + + + private PamSpinner jumpXSpinner; + private PamSpinner jumpYSpinner; + private PamSpinner jumpZSpinner; + + private PamSpinner startDispersion; + private PamSpinner numChains; + private ComboBox clustering; + private PamSpinner numkMeans; + private PamSpinner maxClustDist; + + public MCMCPane() { + super(null); + mainPane = new PamBorderPane(); + mainPane.setTop(createMCMCPane()); + + } + + private Pane createMCMCPane() { + PamVBox vBox = new PamVBox(); + + PamGridPane gridPane = new PamGridPane(); + gridPane.setHgap(5); + gridPane.setVgap(5); + + int row = 0; + int col = 0; + + Label chainTitleLabel = new Label("Markov chain settings"); +// PamGuiManagerFX.titleFont2style(chainTitleLabel); + chainTitleLabel.setFont(Font.font(null,FontWeight.BOLD, 11)); + + gridPane.add(chainTitleLabel, col, row); + GridPane.setColumnSpan(chainTitleLabel, 7); + row++; + + + col=0; + PamHBox chainHolder = new PamHBox(); + chainHolder.setSpacing(5); + chainHolder.setAlignment(Pos.CENTER_LEFT); + + Label label3 = new Label("Start"); + label3.setAlignment(Pos.CENTER_RIGHT); + gridPane.add(label3, col, row); + col++; + + chainHolder.getChildren().add(numChains = new PamSpinner(1,Integer.MAX_VALUE, 20, 1)); + numChains.setEditable(true); + numChains.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + numChains.setMaxWidth(JUMP_SPINNER_WIDTH); + + chainHolder.getChildren().add(new Label("chains seperated by")); + + chainHolder.getChildren().add(startDispersion = new PamSpinner(0.,Double.MAX_VALUE, 100., 1.)); + startDispersion.setEditable(true); + startDispersion.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + startDispersion.setMaxWidth(JUMP_SPINNER_WIDTH+10); + + chainHolder.getChildren().add(new Label("m")); + + gridPane.add(chainHolder, col, row); + GridPane.setColumnSpan(chainHolder, 7); + + row++; + col=0; + + + //chain propertires + + Label label = new Label("No. jumps"); + label.setAlignment(Pos.CENTER_RIGHT); + + gridPane.add(label, col, row); + col++; + gridPane.add(numJumps = new PamSpinner(10,Integer.MAX_VALUE, 2500000, 10000), col, row); + numJumps.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + GridPane.setColumnSpan(numJumps, 3); + numJumps.setEditable(true); + //mcmJumpSpinner.setMaxWidth(JUMP_SPINNER_WIDTH*2); + + col+=col+2; + Label chainLabel = new Label("per chain"); + gridPane.add(chainLabel, col, row); + GridPane.setColumnSpan(chainLabel, 3); + + row++; + col=0; + Label label2 = new Label("Jump size x"); + label2.setAlignment(Pos.CENTER_RIGHT); + + gridPane.add(label2, col, row); + col++; + + + gridPane.add(jumpXSpinner = new PamSpinner(0.,Double.MAX_VALUE, 1., 0.5), col, row); + jumpXSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + jumpXSpinner.setMaxWidth(JUMP_SPINNER_WIDTH); + jumpXSpinner.setEditable(true); + col++; + + gridPane.add(new Label("y"), col, row); + col++; + + gridPane.add(jumpYSpinner = new PamSpinner(0.,Double.MAX_VALUE, 1., 0.5), col, row); + jumpYSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + jumpYSpinner.setMaxWidth(JUMP_SPINNER_WIDTH); + jumpYSpinner.setEditable(true); + col++; + + gridPane.add(new Label("z"), col, row); + col++; + + gridPane.add(jumpZSpinner = new PamSpinner(0.,Double.MAX_VALUE, 1., 0.5), col, row); + jumpZSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + jumpZSpinner.setMaxWidth(JUMP_SPINNER_WIDTH); + jumpZSpinner.setEditable(true); + col++; + + gridPane.add(new Label("m"), col, row); + row++; + col=0; + + + + //chain clustering + Label clusterTitleLabel = new Label("Result clustering"); + //PamGuiManagerFX.titleFont2style(clusterTitleLabel); + clusterTitleLabel.setFont(Font.font(null,FontWeight.BOLD, 11)); + + gridPane.add(clusterTitleLabel, col, row); + GridPane.setColumnSpan(clusterTitleLabel, 7); + row++; + + + row++; + col=0; + Label label5 = new Label("Clustering"); + label5.setAlignment(Pos.CENTER_RIGHT); + gridPane.add(label5, col, row); + col++; + clustering = new ComboBox(); + clustering.getItems().addAll("None", "kMeans"); + clustering.getSelectionModel().select(1); + gridPane.add(clustering, col, row); + + GridPane.setColumnSpan(clustering, 3); + + clustering.setOnAction((action)->{ + enableControls(); + }); + + + row++; + col=0; + + + //kmeans settings - TODO - would be better to have a custom pane for each clustering algorithm + //but not worth the effort until more clustering algorithms are implemented. + Label label6 = new Label("Start"); + label6.setAlignment(Pos.CENTER_RIGHT); + gridPane.add(label6, col, row); + col++; + + + PamHBox kMeansHolder = new PamHBox(); + kMeansHolder.setSpacing(5); + kMeansHolder.setAlignment(Pos.CENTER_LEFT); + + kMeansHolder.getChildren().add(numkMeans = new PamSpinner(1,Integer.MAX_VALUE, 20, 1)); + numkMeans.setEditable(true); + numkMeans.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + numkMeans.setMaxWidth(JUMP_SPINNER_WIDTH); + + + kMeansHolder.getChildren().add(new Label("k-means and merge at <")); + kMeansHolder.getChildren().add(maxClustDist = new PamSpinner(0.,Double.MAX_VALUE, 5., 1)); + maxClustDist.setEditable(true); + maxClustDist.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + maxClustDist.setMaxWidth(JUMP_SPINNER_WIDTH); + kMeansHolder.getChildren().add(new Label("m")); + + gridPane.add(kMeansHolder, col, row); + GridPane.setColumnSpan(kMeansHolder, 7); + + vBox.getChildren().add(gridPane); + + enableControls(); + + return vBox; + } + + private void enableControls() { + //clustering + numkMeans.setDisable(true); + maxClustDist.setDisable(true); + switch (clustering.getSelectionModel().getSelectedIndex()) { + case MCMCParams2.K_MEANS: + numkMeans.setDisable(false); + maxClustDist.setDisable(false); + break; + case MCMCParams2.NONE: + break; + } + } + + @Override + public MCMCParams2 getParams(MCMCParams2 currParams) { + + //chain settings. + currParams.numberOfChains = numChains.getValue(); + currParams.numberOfJumps = numJumps.getValue(); + + double[] jumpSize = new double[3]; + + jumpSize[0] = jumpXSpinner.getValue(); + jumpSize[1] = jumpYSpinner.getValue(); + jumpSize[2] = jumpZSpinner.getValue(); + + currParams.jumpSize = jumpSize; + + //bit messy but works... + currParams.setChainDispersion(startDispersion.getValue(), 3); + + //cluster settings + currParams.clusterAnalysis = clustering.getSelectionModel().getSelectedIndex(); + currParams.kmeanAttempts = numkMeans.getValue(); + currParams.maxClusterSize = maxClustDist.getValue(); + + return currParams; + } + + @Override + public void setParams(MCMCParams2 currParams) { + + //chain settings. + numJumps.getValueFactory().setValue(currParams.numberOfJumps); + + jumpXSpinner.getValueFactory().setValue(currParams.jumpSize[0]); + jumpYSpinner.getValueFactory().setValue(currParams.jumpSize[1]); + jumpZSpinner.getValueFactory().setValue(currParams.jumpSize[2]); + + numChains.getValueFactory().setValue(currParams.numberOfChains); + + //bit messy but works... + startDispersion.getValueFactory().setValue(Math.abs(currParams.chainStartDispersion[0][0])); + + + //cluster settings + clustering.getSelectionModel().select(currParams.clusterAnalysis); + numkMeans.getValueFactory().setValue(currParams.kmeanAttempts); + maxClustDist.getValueFactory().setValue(currParams.maxClusterSize); + + //enable the controls. + enableControls(); + } + + @Override + public String getName() { + return "MCMC Settings"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + + +} diff --git a/src/Localiser/detectionGroupLocaliser/DetectionGroupLocaliser2.java b/src/Localiser/detectionGroupLocaliser/DetectionGroupLocaliser2.java index 1420233f..964d83ef 100644 --- a/src/Localiser/detectionGroupLocaliser/DetectionGroupLocaliser2.java +++ b/src/Localiser/detectionGroupLocaliser/DetectionGroupLocaliser2.java @@ -132,7 +132,7 @@ public class DetectionGroupLocaliser2 implements Local } @Override - public LocaliserPane getSettingsPane() { + public LocaliserPane getAlgorithmSettingsPane() { return locAlgorithm.getSettingsPane(); } diff --git a/src/Localiser/detectionGroupLocaliser/GroupLocResult.java b/src/Localiser/detectionGroupLocaliser/GroupLocResult.java index 2b6b1c24..4b361768 100644 --- a/src/Localiser/detectionGroupLocaliser/GroupLocResult.java +++ b/src/Localiser/detectionGroupLocaliser/GroupLocResult.java @@ -16,6 +16,7 @@ import targetMotionOld.TargetMotionModel; * * Result class for the a group localiser. Usually this will be a target motion localisation, however could also be a group of DIFAR buoys * or other systems which uses a set of detections to localise animals. + * * @author Doug Gillespie * */ @@ -75,8 +76,6 @@ public class GroupLocResult implements Comparable, LocalisationC */ private int dim=3; - private ArrayList> MCMCJumpResults; - /** * @param latLong * @param chi2 @@ -132,16 +131,7 @@ public class GroupLocResult implements Comparable, LocalisationC return side; } - /**Not the correct place to store**/ - @Deprecated - public void setMCMCJumps(ArrayList> MCMCResults){ - this.MCMCJumpResults=MCMCResults; - } - @Deprecated - public ArrayList> getMCMCJumps(){ - return MCMCJumpResults; - } /** diff --git a/src/Map/MapDetectionsDialog.java b/src/Map/MapDetectionsDialog.java index bb85bdb9..1a9b100b 100644 --- a/src/Map/MapDetectionsDialog.java +++ b/src/Map/MapDetectionsDialog.java @@ -1,6 +1,7 @@ package Map; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -16,6 +17,9 @@ import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.TitledBorder; +import org.kordamp.ikonli.materialdesign2.MaterialDesignC; +import org.kordamp.ikonli.swing.FontIcon; + import PamController.PamController; import PamView.dialog.PamDialog; import PamguardMVC.PamDataBlock; @@ -85,7 +89,8 @@ public class MapDetectionsDialog extends PamDialog { MapDetectionData md; - ImageIcon settingsIcon = new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png")); +// ImageIcon settingsIcon = new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png")); + FontIcon settingsIcon = FontIcon.of(MaterialDesignC.COG, 20, Color.DARK_GRAY); for (int i = 0; i < n; i++) { md = mapDetectionsParameters.mapDetectionDatas.get(i); diff --git a/src/PamController/fileprocessing/ReprocessStoreChoice.java b/src/PamController/fileprocessing/ReprocessStoreChoice.java index 6a6bd019..ec41f6b4 100644 --- a/src/PamController/fileprocessing/ReprocessStoreChoice.java +++ b/src/PamController/fileprocessing/ReprocessStoreChoice.java @@ -24,7 +24,7 @@ public enum ReprocessStoreChoice { case DONTSSTART: return "Don't start processing"; case OVERWRITEALL: - return "Overwrite existing output data"; + return "Overwrite all existing output data"; default: break; } @@ -42,7 +42,7 @@ public enum ReprocessStoreChoice { case DONTSSTART: return "Processing will not start. Select alternative storage locations / databases and try again"; case OVERWRITEALL: - return "Overwrite existing output data. Existing data will be deleted"; + return "Overwrite existing output data. All existing data will be deleted"; default: break; } diff --git a/src/PamController/soundMedium/GlobalMediumManager.java b/src/PamController/soundMedium/GlobalMediumManager.java index f546caea..2fff40d5 100644 --- a/src/PamController/soundMedium/GlobalMediumManager.java +++ b/src/PamController/soundMedium/GlobalMediumManager.java @@ -27,7 +27,7 @@ public class GlobalMediumManager implements PamSettings { private GlobalMediumParams globalMediumParams = new GlobalMediumParams(); - private String warning = " Changing to between air and water requires a PAMGuard restart\n" + public String warning = " Changing to between air and water requires a PAMGuard restart\n" + " for some display changes to take effect. Settings such as\n" + " sound speed, reciever sensitivity values and data unit amplitudes\n" + " will be recalculated or set to new default values.\n" @@ -74,6 +74,7 @@ public class GlobalMediumManager implements PamSettings { subMenu.add(rbMenuItem); } } + /** diff --git a/src/PamModel/PamModel.java b/src/PamModel/PamModel.java index d2bcb919..2ff3b92e 100644 --- a/src/PamModel/PamModel.java +++ b/src/PamModel/PamModel.java @@ -45,7 +45,6 @@ import whistlesAndMoans.AbstractWhistleDataUnit; import fftManager.FFTDataUnit; import fftManager.PamFFTControl; import group3dlocaliser.Group3DLocaliserControl; -import metadata.MetaDataContol; import meygenturbine.MeygenTurbine; import printscreen.PrintScreenControl; import rockBlock.RockBlockControl; @@ -472,6 +471,7 @@ final public class PamModel implements PamSettings { mi.setToolTipText("Interface to Tethys Database"); mi.setModulesMenuGroup(utilitiesGroup); mi.setMaxNumber(1); + //mi.addGUICompatabilityFlag(PamGUIManager.FX); //has FX enabled GUI. mi.setHidden(SMRUEnable.isEnable() == false); } @@ -522,7 +522,8 @@ final public class PamModel implements PamSettings { mi.setModulesMenuGroup(sensorsGroup); mi.setToolTipText("Imports CPOD data"); mi.setHidden(SMRUEnable.isEnable() == false); - + mi.addGUICompatabilityFlag(PamGUIManager.FX); //has FX enabled GUI. + /* * ************* Start Displays Group ******************* */ @@ -591,6 +592,7 @@ final public class PamModel implements PamSettings { mi.addDependency(new PamDependency(RawDataUnit.class, "Acquisition.AcquisitionControl")); mi.setToolTipText("Decimates (reduces the frequency of) audio data"); mi.setModulesMenuGroup(processingGroup); + mi.addGUICompatabilityFlag(PamGUIManager.FX); //has FX enabled GUI. mi = PamModuleInfo.registerControlledUnit(CepstrumControl.class.getName(), CepstrumControl.unitType); mi.addDependency(new PamDependency(FFTDataUnit.class, PamFFTControl.class.getName())); @@ -875,7 +877,7 @@ final public class PamModel implements PamSettings { mi.setModulesMenuGroup(displaysGroup); mi.addGUICompatabilityFlag(PamGUIManager.FX); - mi = PamModuleInfo.registerControlledUnit("detectionPlotFX.DetectionDisplayControl", "Detection Display" ); + mi = PamModuleInfo.registerControlledUnit("detectionPlotFX.DetectionDisplayControl2", "Detection Display" ); mi.setToolTipText("Display detection data"); mi.setModulesMenuGroup(displaysGroup); mi.addGUICompatabilityFlag(PamGUIManager.FX); diff --git a/src/PamUtils/FileParts.java b/src/PamUtils/FileParts.java index ff2c8aba..ef185556 100644 --- a/src/PamUtils/FileParts.java +++ b/src/PamUtils/FileParts.java @@ -6,6 +6,7 @@ import java.net.URI; /** * Class for breaking a file name down into it's constituent * parts. + * * @author Doug Gillespie * */ diff --git a/src/PamUtils/PamArrayUtils.java b/src/PamUtils/PamArrayUtils.java index e6467abd..997a7138 100644 --- a/src/PamUtils/PamArrayUtils.java +++ b/src/PamUtils/PamArrayUtils.java @@ -10,6 +10,8 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.math.linear.Array2DRowRealMatrix; import org.apache.commons.math.linear.RealMatrix; +import us.hebi.matlab.mat.types.Matrix; + /** * Some math and utility functions for arrays and Lists. * @@ -22,19 +24,19 @@ public class PamArrayUtils { /** * Calculate the mean of one dimension within a list of points. e.g. the points might be a list of [x y z] co-ordinates in * which case the dim=0 would return the mean of all x points. - * @param successJump - a list of points + * @param array - a list of points * @param InitialtoIgnorePercentage: ignore the first percentage of results * @param dim - the dimension of the point to calculate the average for * @return the mean of one dimension of the list of the points. */ - public static double mean(ArrayList successJump, double InitialtoIgnorePercentage, int dim){ + public static double mean(ArrayList array, double InitialtoIgnorePercentage, int dim){ double meanTotal=0; int n=0; - int forStart=(int) Math.round((InitialtoIgnorePercentage)*successJump.size()); + int forStart=(int) Math.round((InitialtoIgnorePercentage)*array.size()); - for (int i=forStart; i successJump, double initialtoIgnorePercentage, int dim){ + public static double std(ArrayList array, double initialtoIgnorePercentage, int dim){ double std=0.0; int n=0; - int forStart=(int) Math.round((initialtoIgnorePercentage)*successJump.size()); + int forStart=(int) Math.round((initialtoIgnorePercentage)*array.size()); - double meanTotal= mean(successJump, initialtoIgnorePercentage, dim); + double meanTotal= mean(array, initialtoIgnorePercentage, dim); //calculate standard deviation - for (int k=forStart;k { private BackgroundWorker backgroundWorker; + private volatile boolean running = false; /** @@ -82,11 +83,11 @@ public class PamWorker { */ public boolean start() { if (backgroundWorker.isCancelled() || backgroundWorker.isDone() || running) return false; - backgroundWorker.execute(); + backgroundWorker.execute(); return true; } - protected class BackgroundWorker extends SwingWorker { + public class BackgroundWorker extends SwingWorker { @Override protected T doInBackground() throws Exception { @@ -139,6 +140,12 @@ public class PamWorker { } } + + + public BackgroundWorker getBackgroundWorker() { + return backgroundWorker; + } + diff --git a/src/PamView/PamDetectionOverlayGraphics.java b/src/PamView/PamDetectionOverlayGraphics.java index 78917ecc..e4667b8b 100644 --- a/src/PamView/PamDetectionOverlayGraphics.java +++ b/src/PamView/PamDetectionOverlayGraphics.java @@ -1,5 +1,6 @@ package PamView; +import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; @@ -9,6 +10,7 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.Window; +import java.awt.geom.Ellipse2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -282,7 +284,6 @@ public class PamDetectionOverlayGraphics extends PanelOverlayDraw { protected Rectangle drawOnMap(Graphics g, PamDataUnit pamDetection, GeneralProjector generalProjector) { - /* * * four possibilities here. @@ -390,7 +391,12 @@ public class PamDetectionOverlayGraphics extends PanelOverlayDraw { generalProjector.addHoverData(endPoint, pamDetection); //plot localisation errors. if (localisation.getLocError(i)!=null){ - TransformShape shape=localisation.getLocError(i).getErrorDraw().drawOnMap(g, pamDetection, endLatLong, generalProjector, symbol.getLineColor()); + + Color col =symbol.getLineColor(); + if (drawingOptions != null) { + col = drawingOptions.createColor(symbol.getLineColor(), drawingOptions.getLineOpacity()); + } + TransformShape shape=localisation.getLocError(i).getErrorDraw().drawOnMap(g, pamDetection, endLatLong, generalProjector, col); generalProjector.addHoverData(shape, pamDetection); } else { @@ -496,14 +502,14 @@ public class PamDetectionOverlayGraphics extends PanelOverlayDraw { } - /** - * Draw a localisation symbol. This is just a circel with a specified radius. - * @param g - graphics handle - * @param pamDataUnit - the PAM data unit which holds the localisation that is being plotted/ - * @param originPoint - the origin of the localisation. - * @param locPoint - the location of the localisation - * @return a rectangle which is the bounds of the localisation symbol. - */ +// /** +// * Draw a localisation symbol. This is just a circel with a specified radius. +// * @param g - graphics handle +// * @param pamDataUnit - the PAM data unit which holds the localisation that is being plotted/ +// * @param originPoint - the origin of the localisation. +// * @param locPoint - the location of the localisation +// * @return a rectangle which is the bounds of the localisation symbol. +// */ // protected Rectangle drawLocSymbol(Graphics g, PamDataUnit pamDataUnit, Point originPoint, Point locPoint, Color color){ // // //need some better graphics options- cast to Graphics2D. @@ -527,79 +533,79 @@ public class PamDetectionOverlayGraphics extends PanelOverlayDraw { // // return oval.getBounds(); // } - - - // /** - // * Plots errors as an ellipse. - // * @param g - the graphics handle. - // * @param errorOrigin - the error origin point. - // * @param errorDirection. The direction of the perpendicular error. In RADIANS - // * @param perpError - the error perpendicular to the track line or (if no track line this is simply the error in x) - // * @param horzError - the error horizontal to the track line or (if no track line this is simply the error in x) - // * @param color - // * @return - // */ - // private Rectangle drawErrors(Graphics g, PamDataUnit pamDataUnit, GeneralProjector generalProjector, LatLong errorOrigin, double errorDirection, double perpError, double horzError, Color color) { - // - // //System.out.println("Plot errors: perp: "+ perpError+ " horz: "+horzError+ " " + errorDirection); - // - // Graphics2D g2d = (Graphics2D)g; - // - // //draw oval - //// //need to work out the size of the horizontal error. - //// perpError=Math.max(perpError, 100); - //// horzError=Math.max(horzError, 50); - // - // //must work out the horizontal and perpindicular error size in pixels using the projector - // //this is a bit of round about way to do thinfs but best use of framework here. - // LatLong llperp=errorOrigin.addDistanceMeters(0, perpError); - // LatLong l2perp=errorOrigin.addDistanceMeters(horzError, 0); - // - // Point pointPerp = generalProjector.getCoord3d(llperp.getLatitude(), llperp.getLongitude(), 0).getXYPoint(); - // Point pointHorz = generalProjector.getCoord3d(l2perp.getLatitude(), l2perp.getLongitude(), 0).getXYPoint(); - // Point errorOriginXY=generalProjector.getCoord3d(errorOrigin.getLatitude(), errorOrigin.getLongitude(), 0).getXYPoint(); - // - // double perpErrPix=errorOriginXY.distance(pointPerp); - // double horzErrPix=errorOriginXY.distance(pointHorz); - // - // //draw the ellipse and rotate if possible. - // 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.setPaint(color.brighter()); - // - // if (!Double.isNaN(errorDirection)) g2d.rotate(errorDirection,errorOriginXY.getX(), errorOriginXY.getY()); - // g2d.draw(oval); - // g2d.fill(oval); - // - // //reset transparency. - // g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1)); - // //need to reset the rotation - // if (!Double.isNaN(errorDirection)) g2d.rotate(-errorDirection,errorOriginXY.getX(), errorOriginXY.getY()); - // - // - // return oval.getBounds(); - // - // - //// // refBEaring should be an angle in radians from the x axis (trig coordinates) - //// // convert this to a compass heading and get the positions of the ends. - //// g.setColor(col); - //// double compassHeading = 90 - (refAngle * 180 / Math.PI); - //// Coordinate3d centre = generalProjector.getCoord3d(refPoint.getLatitude(), refPoint.getLongitude(), 0); - //// LatLong ll1 = refPoint.travelDistanceMeters(compassHeading, err1); - //// LatLong ll2 = refPoint.travelDistanceMeters(compassHeading+90, err2); - //// Coordinate3d p1 = generalProjector.getCoord3d(ll1.getLatitude(), ll1.getLongitude(), 0); - //// Coordinate3d p2 = generalProjector.getCoord3d(ll2.getLatitude(), ll2.getLongitude(), 0); - //// int cx = (int) centre.x; - //// int cy = (int) centre.y; - //// int dx = (int) (p1.x- centre.x); - //// int dy = (int) (p1.y- centre.y); - //// g.drawLine(cx + dx, cy - dy, cx - dx, cy + dy); - //// dx = (int) (p2.x- centre.x); - //// dy = (int) (p2.y- centre.y); - //// g.drawLine(cx + dx, cy - dy, cx - dx, cy + dy); - //// - //// return null; - // } +// +// +// /** +// * Plots errors as an ellipse. +// * @param g - the graphics handle. +// * @param errorOrigin - the error origin point. +// * @param errorDirection. The direction of the perpendicular error. In RADIANS +// * @param perpError - the error perpendicular to the track line or (if no track line this is simply the error in x) +// * @param horzError - the error horizontal to the track line or (if no track line this is simply the error in x) +// * @param color +// * @return +// */ +// private Rectangle drawErrors(Graphics g, PamDataUnit pamDataUnit, GeneralProjector generalProjector, LatLong errorOrigin, double errorDirection, double perpError, double horzError, Color color) { +// +// //System.out.println("Plot errors: perp: "+ perpError+ " horz: "+horzError+ " " + errorDirection); +// +// Graphics2D g2d = (Graphics2D)g; +// +// //draw oval +// // //need to work out the size of the horizontal error. +// // perpError=Math.max(perpError, 100); +// // horzError=Math.max(horzError, 50); +// +// //must work out the horizontal and perpindicular error size in pixels using the projector +// //this is a bit of round about way to do thinfs but best use of framework here. +// LatLong llperp=errorOrigin.addDistanceMeters(0, perpError); +// LatLong l2perp=errorOrigin.addDistanceMeters(horzError, 0); +// +// Point pointPerp = generalProjector.getCoord3d(llperp.getLatitude(), llperp.getLongitude(), 0).getXYPoint(); +// Point pointHorz = generalProjector.getCoord3d(l2perp.getLatitude(), l2perp.getLongitude(), 0).getXYPoint(); +// Point errorOriginXY=generalProjector.getCoord3d(errorOrigin.getLatitude(), errorOrigin.getLongitude(), 0).getXYPoint(); +// +// double perpErrPix=errorOriginXY.distance(pointPerp); +// double horzErrPix=errorOriginXY.distance(pointHorz); +// +// //draw the ellipse and rotate if possible. +// 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.setPaint(color.brighter()); +// +// if (!Double.isNaN(errorDirection)) g2d.rotate(errorDirection,errorOriginXY.getX(), errorOriginXY.getY()); +// g2d.draw(oval); +// g2d.fill(oval); +// +// //reset transparency. +// g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1)); +// //need to reset the rotation +// if (!Double.isNaN(errorDirection)) g2d.rotate(-errorDirection,errorOriginXY.getX(), errorOriginXY.getY()); +// +// +// return oval.getBounds(); +// +// +// // // refBEaring should be an angle in radians from the x axis (trig coordinates) +// // // convert this to a compass heading and get the positions of the ends. +// // g.setColor(col); +// // double compassHeading = 90 - (refAngle * 180 / Math.PI); +// // Coordinate3d centre = generalProjector.getCoord3d(refPoint.getLatitude(), refPoint.getLongitude(), 0); +// // LatLong ll1 = refPoint.travelDistanceMeters(compassHeading, err1); +// // LatLong ll2 = refPoint.travelDistanceMeters(compassHeading+90, err2); +// // Coordinate3d p1 = generalProjector.getCoord3d(ll1.getLatitude(), ll1.getLongitude(), 0); +// // Coordinate3d p2 = generalProjector.getCoord3d(ll2.getLatitude(), ll2.getLongitude(), 0); +// // int cx = (int) centre.x; +// // int cy = (int) centre.y; +// // int dx = (int) (p1.x- centre.x); +// // int dy = (int) (p1.y- centre.y); +// // g.drawLine(cx + dx, cy - dy, cx - dx, cy + dy); +// // dx = (int) (p2.x- centre.x); +// // dy = (int) (p2.y- centre.y); +// // g.drawLine(cx + dx, cy - dy, cx - dx, cy + dy); +// // +// // return null; +// } protected Rectangle drawLineAndSymbol(Graphics g, PamDataUnit pamDataUnit, GeneralProjector generalProjector, LatLong LL1, LatLong LL2, PamSymbol symbol, ProjectorDrawingOptions drawingOptions) { return drawLineAndSymbol(g, pamDataUnit, generalProjector.getCoord3d(LL1.getLatitude(), LL1.getLongitude(), 0).getXYPoint(), diff --git a/src/PamView/component/PamSettingsIconButton.java b/src/PamView/component/PamSettingsIconButton.java index 817903a5..20e6feda 100644 --- a/src/PamView/component/PamSettingsIconButton.java +++ b/src/PamView/component/PamSettingsIconButton.java @@ -1,8 +1,13 @@ package PamView.component; +import java.awt.Color; + import javax.swing.ImageIcon; import javax.swing.JButton; +import org.kordamp.ikonli.materialdesign2.MaterialDesignC; +import org.kordamp.ikonli.swing.FontIcon; + public class PamSettingsIconButton extends JButton { /** @@ -10,8 +15,9 @@ public class PamSettingsIconButton extends JButton { */ private static final long serialVersionUID = 1L; - private static final ImageIcon settingsIcon = new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png")); - +// private static final ImageIcon settingsIcon = new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png")); + private static final FontIcon settingsIcon = FontIcon.of(MaterialDesignC.COG, 20, Color.DARK_GRAY); + /** * Create a simple square button using the given icon. */ diff --git a/src/PamView/dialog/SettingsButton.java b/src/PamView/dialog/SettingsButton.java index d3b029f4..510a1711 100644 --- a/src/PamView/dialog/SettingsButton.java +++ b/src/PamView/dialog/SettingsButton.java @@ -1,9 +1,15 @@ package PamView.dialog; +import java.awt.Color; + import javax.swing.Action; +import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; +import org.kordamp.ikonli.materialdesign2.MaterialDesignC; +import org.kordamp.ikonli.swing.FontIcon; + /** * Standard settings button with the little cogwheel for use throughout Swing components. * @author dg50 @@ -20,8 +26,9 @@ public class SettingsButton extends JButton { super(makeIcon()); } - private static ImageIcon makeIcon() { - return new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png")); + private static Icon makeIcon() { + return FontIcon.of(MaterialDesignC.COG, 20, Color.DARK_GRAY); +// return new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png")); } /** diff --git a/src/PamView/importData/DataImport.java b/src/PamView/importData/DataImport.java index 2e881386..349c223f 100644 --- a/src/PamView/importData/DataImport.java +++ b/src/PamView/importData/DataImport.java @@ -5,66 +5,95 @@ import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; /** - * This class is used to define what data to import and how to import it. It is used in conjunction with ImportDataSystem to create a generic system to load any type of data into a data block. + * This class is used to define what data to import and how to import it. It is + * used in conjunction with ImportDataSystem to create a generic system to load + * any type of data into a data block. *

- * First getExtensionsStrings() defines which file types can be selected for import. + * First getExtensionsStrings() defines which file types can be selected for + * import. *

- * Before loading in any data we need to perform pre-checks with the function performPreChecks(). This function is called before the load thread is called. Often this will remain blank but say you needed extra user imput then this function could be used - * to create another dialog box before the data is loaded. + * Before loading in any data we need to perform pre-checks with the function + * performPreChecks(). This function is called before the load thread is called. + * Often this will remain blank but say you needed extra user imput then this + * function could be used to create another dialog box before the data is + * loaded. *

- * To create a new importer first we need to load a file into memory using the loadDataIntermediate(String filePath) function. This will create an ArrayList were T is the type of data we're trying to save. So for example if you are loading in NMEA data then - * you would use create loadDataIntermediate(String filePath) which imports the file (filepath) and converts into an ArrayList of strings io.e ArrayList such that T=String; + * To create a new importer first we need to load a file into memory using the + * loadDataIntermediate(String filePath) function. This will create an + * ArrayList were T is the type of data we're trying to save. So for example + * if you are loading in NMEA data then you would use create + * loadDataIntermediate(String filePath) which imports the file (filepath) and + * converts into an ArrayList of strings io.e ArrayList such that + * T=String; *

- * Once the data has been loaded into memory it must be converted into a data unit and saved to a data block. The function createDataUnit(T dataLine) converts data into a PamDatUnit. This function would for example, convert an NMEA strings into a GPSDataUnit. The function - * isDataFormatOK() needs to be used to check that each T is in the correct format. + * Once the data has been loaded into memory it must be converted into a data + * unit and saved to a data block. The function createDataUnit(T dataLine) + * converts data into a PamDatUnit. This function would for example, convert an + * NMEA strings into a GPSDataUnit. The function isDataFormatOK() needs to be + * used to check that each T is in the correct format. *

* * @author Jamie Macaulay * - * @param - the data type loaded from a file. This could be a String, ArrayList, ArrayList> etc. + * @param - the data type loaded from a file. This could be a String, + * ArrayList, ArrayList> etc. */ -public abstract class DataImport { +public abstract class DataImport { - public abstract ArrayList loadDataIntermediate(String filePath); - /** - * Check that a row of imported data is in the correct format. + * Loads the file into memory - each element of the output array list is then processed. + * @param filePath - the filepath. + * @return a list of imported objects. + */ + public abstract ArrayList loadDataIntermediate(String filePath); + + /** + * Check that a row of imported data is in the correct format. + * * @param dataLine-a row of data loaded from file - * @return true if the format is OK. + * @return true if the format is OK. */ public abstract boolean isDataFormatOK(T dataLine); - + /** - * Use this function to perform any pre checks on data/ bring up extra dialog boxes before laoding data. - * @return true iof pre checks are OK. + * Use this function to perform any pre checks on data/ bring up extra dialog + * boxes before loading data. + * + * @return true iof pre checks are OK. */ - public boolean performPreChecks(){ return true; } - + public boolean performPreChecks() { + return true; + } + /** - * Create a data unit from the data loaded from the imported file. + * Create a data unit from the data loaded from the imported file. + * * @param dataLine-a row of data loaded from file - * @return a data unit to be saved to the datablock. + * @return a data unit to be saved to the datablock. */ public abstract PamDataUnit createDataUnit(T dataLine); /** - * Get the data block to to save data to. + * Get the data block to to save data to. + * * @return */ @SuppressWarnings("rawtypes") public abstract PamDataBlock getDataBlock(); /** - * Return the file extensions that can be loaded. - * @return an array of file extensions that can be loaded. + * Return the file extensions that can be loaded. + * + * @return an array of file extensions that can be loaded. */ public abstract String[] getExtensionsStrings(); - + /** - * The name of the data unit to appear on the dialog boxes. - * @return name of the data unit to be imported. + * The name of the data unit to appear on the dialog boxes. + * + * @return name of the data unit to be imported. */ - public String getDataUnitName(){ + public String getDataUnitName() { return "Data Units"; } diff --git a/src/PamView/importData/ImportDataSystem.java b/src/PamView/importData/ImportDataSystem.java index 8f166883..820dbfa9 100644 --- a/src/PamView/importData/ImportDataSystem.java +++ b/src/PamView/importData/ImportDataSystem.java @@ -288,7 +288,7 @@ public class ImportDataSystem { } public void setSaveProgress(int prog){ - System.out.println("prog "+prog); + //System.out.println("prog "+prog); setProgress(prog); } diff --git a/src/PamView/symbol/modifier/PeakFreqModifier.java b/src/PamView/symbol/modifier/PeakFreqModifier.java index a989d419..1a8d504a 100644 --- a/src/PamView/symbol/modifier/PeakFreqModifier.java +++ b/src/PamView/symbol/modifier/PeakFreqModifier.java @@ -25,11 +25,11 @@ import pamViewFX.fxNodes.utilsFX.PamUtilsFX; * */ public class PeakFreqModifier extends SymbolModifier { - + public final static String PEAK_FREQ_MODIFIER_NAME = "Peak Frequency"; - + private SymbolData symbolData = new SymbolData(PamSymbolType.SYMBOL_CIRCLE, 5, 5, true, Color.BLACK, Color.BLACK); - + /** * The colour array for frequency */ @@ -49,23 +49,23 @@ public class PeakFreqModifier extends SymbolModifier { public PeakFreqModifier(PamSymbolChooser symbolChooser) { super(PEAK_FREQ_MODIFIER_NAME, symbolChooser, SymbolModType.FILLCOLOUR | SymbolModType.LINECOLOUR ); - checkColourArray(); - setToolTipText("Colour by the peak frequency of the sound"); + checkColourArray(); + setToolTipText("Colour by the peak frequency of the sound"); } - + public SymbolData modifySymbol(SymbolData symbolData, GeneralProjector projector, PamDataUnit dataUnit) { -// checkColourArray(); + // checkColourArray(); return super.modifySymbol(symbolData, projector, dataUnit); } - + @Override public SymbolData getSymbolData(GeneralProjector projector, PamDataUnit dataUnit) { return colourByFreq(symbolData, projector, dataUnit); } - - + + /** * Colour the symbol by frequency. * @param symbolData - the symbol data. @@ -74,9 +74,19 @@ public class PeakFreqModifier extends SymbolModifier { * @return the symbol data */ private SymbolData colourByFreq(SymbolData symbolData, GeneralProjector projector, PamDataUnit dataUnit) { - + double frequency = Double.NaN; - if (dataUnit instanceof RawDataHolder) { + + if (dataUnit instanceof CPODClick) { + + //A bit of a HACK + + frequency = ((CPODClick) dataUnit).getkHz()*1000; + + // System.out.println("Frequency: " + frequency + " Upper freq: " + peakFreqSymbolOptions.freqLimts[1] " Lower freq: " + peakFreqSymbolOptions.freqLimts[0] ); + } + else if (dataUnit instanceof RawDataHolder) { + RawDataHolder click = (RawDataHolder) dataUnit; @@ -86,32 +96,25 @@ public class PeakFreqModifier extends SymbolModifier { frequency= (maxIndex/(double) powerSpectrum.length)*dataUnit.getParentDataBlock().getSampleRate()/2; - } - else if (dataUnit instanceof CPODClick) { - - frequency = ((CPODClick) dataUnit).getkHz()*1000; - -// System.out.println("Frequency: " + frequency + " Upper freq: " + peakFreqSymbolOptions.freqLimts[1] " Lower freq: " + peakFreqSymbolOptions.freqLimts[0] ); - } else { return null; } - + frequency=(frequency - peakFreqSymbolOptions.freqLimts[0])/(peakFreqSymbolOptions.freqLimts[1]-peakFreqSymbolOptions.freqLimts[0]); checkColourArray(); Color freqCol = PamUtilsFX.fxToAWTColor(this.colourArray.getColour(frequency)); - -// System.out.println("Freq colour: " + freqCol.getRed() + " " + freqCol.getGreen() + " " + freqCol.getBlue()); + + // System.out.println("Freq colour: " + freqCol.getRed() + " " + freqCol.getGreen() + " " + freqCol.getBlue()); symbolData.setFillColor(freqCol); symbolData.setLineColor(freqCol); return symbolData; } - + /** @@ -123,7 +126,7 @@ public class PeakFreqModifier extends SymbolModifier { this.colourArrayType=peakFreqSymbolOptions.freqColourArray; } } - + @Override public SymbolModifierPane getOptionsPane() { //System.out.println("PEAK FREQ COLOUR ARRAY2: " + peakFreqSymbolOptions.freqColourArray); @@ -133,7 +136,7 @@ public class PeakFreqModifier extends SymbolModifier { } return peakFreqOptions; } - + @Override public SymbolModifierParams getSymbolModifierParams() { //System.out.println("PEAK FREQ COLOUR ARRAY3: " + peakFreqSymbolOptions.freqColourArray); @@ -154,7 +157,7 @@ public class PeakFreqModifier extends SymbolModifier { //System.out.println("PEAK FREQ COLOUR ARRAY: " + peakFreqSymbolOptions.freqColourArray); checkColourArray(); } - + } diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index 98ffd01b..44690ed1 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -161,7 +161,9 @@ public class RawDataTransforms { */ public double[] getPowerSpectrum(int channel, int minBin, int maxBin, int fftLength) { synchronized (synchObject) { + if (minBin==0 && maxBin>=this.getWaveData(0).length-1) { + return getPowerSpectrum(channel, fftLength); } if (fftLength == 0) { @@ -169,8 +171,8 @@ public class RawDataTransforms { } double[] waveformTrim = new double[maxBin-minBin]; - - //System.out.println("minBin: " + minBin + " maxBin: " + maxBin + " raw waveform: " + this.getWaveData(channel).length); + +// System.out.println("minBin: " +minBin + " maxBin: " + maxBin + " " + Math.min(this.getWaveData(channel).length, waveformTrim.length) + " " + this.getWaveData(channel).length + " " + this.getSampleDuration()); System.arraycopy(this.getWaveData(channel), minBin, waveformTrim, 0, Math.min(this.getWaveData(channel).length-minBin-1, waveformTrim.length)); @@ -305,7 +307,7 @@ public class RawDataTransforms { * Get the spectrum length * @return the spectrogram length. */ - private int getCurrentSpectrumLength() { + public int getCurrentSpectrumLength() { if (currentSpecLen<=0) { currentSpecLen = PamUtils.getMinFftLength(dataUnit.getSampleDuration()); } @@ -573,14 +575,21 @@ public class RawDataTransforms { */ public FFTFilter getFFTFilter(FFTFilterParams fftFilterParams) { if (fftFilter == null) { - fftFilter = new FFTFilter(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); + fftFilter = new FFTFilter(fftFilterParams,getSampleRate()); } else { - fftFilter.setParams(fftFilterParams, this.dataUnit.getParentDataBlock().getSampleRate()); + fftFilter.setParams(fftFilterParams, getSampleRate()); } return fftFilter; } + /** + * Get the sample rate to use for transforms. + * @return the sample rate. + */ + public float getSampleRate() { + return this.dataUnit.getParentDataBlock().getSampleRate(); + } /** * Get a correction based on the slope of the waveform which diff --git a/src/PamguardMVC/toad/GenericTOADCalculator.java b/src/PamguardMVC/toad/GenericTOADCalculator.java index 1a3e2644..3dbc5a13 100644 --- a/src/PamguardMVC/toad/GenericTOADCalculator.java +++ b/src/PamguardMVC/toad/GenericTOADCalculator.java @@ -134,6 +134,7 @@ public class GenericTOADCalculator implements TOADCalculator, PamSettings { * to give better timing accuracy. */ FFTDataList fftData = fftDataOrganiser.createFFTDataList(dataUnit, sampleRate, dataUnit.getChannelBitmap() & channelMap); +// System.out.println("fftData: " + fftData); if (fftData == null || fftData.getMaxChannelCount() == 0) { // debug stuff ... System.out.println("No FFT Data for " + dataUnit.getSummaryString()); diff --git a/src/PamguardMVC/toad/TOADCalculator.java b/src/PamguardMVC/toad/TOADCalculator.java index 92d0ae9c..818ed1f4 100644 --- a/src/PamguardMVC/toad/TOADCalculator.java +++ b/src/PamguardMVC/toad/TOADCalculator.java @@ -1,10 +1,10 @@ package PamguardMVC.toad; import java.awt.Window; +import java.io.Serializable; import java.util.List; import Array.SnapshotGeometry; -import PamController.SettingsPane; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import group3dlocaliser.algorithm.toadbase.TOADInformation; diff --git a/src/Resources/Ishmael_Energy_Sum.svg b/src/Resources/Ishmael_Energy_Sum.svg new file mode 100644 index 00000000..91103d4c --- /dev/null +++ b/src/Resources/Ishmael_Energy_Sum.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/css/delphinid_logo01.svg b/src/Resources/css/delphinid_logo01.svg new file mode 100644 index 00000000..744670bf --- /dev/null +++ b/src/Resources/css/delphinid_logo01.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + delphinID + + + + + + diff --git a/src/Resources/css/pamCSS.css b/src/Resources/css/pamCSS.css index b2b2d58d..a951f86c 100644 --- a/src/Resources/css/pamCSS.css +++ b/src/Resources/css/pamCSS.css @@ -179,7 +179,7 @@ /******************************************************************************* * * - * Hiding Tab * + * Menu button * * * ******************************************************************************/ diff --git a/src/Resources/css/pamSettingsCSS.css b/src/Resources/css/pamSettingsCSS.css index 66d99081..af9f9cf9 100644 --- a/src/Resources/css/pamSettingsCSS.css +++ b/src/Resources/css/pamSettingsCSS.css @@ -163,7 +163,7 @@ */ /** top-left, top-right, bottom-right, and bottom-left corners, in that order. */ .close-button-right{ - -fx-background-color: transparent; + -fx-background-color: -fx-darkbackground-trans; -fx-background-radius: 0 10 10 0; -fx-border-color: transparent; -fx-border-radius: 0 10 10 0; @@ -171,7 +171,7 @@ .close-button-left{ - -fx-background-color: transparent; + -fx-background-color: -fx-darkbackground-trans; -fx-background-radius: 10 0 0 10; -fx-border-color: transparent; -fx-border-radius: 10 0 0 10; @@ -179,17 +179,17 @@ .close-button-top{ - -fx-background-color: transparent; + -fx-background-color: -fx-darkbackground-trans; -fx-background-radius: 10 10 0 0; - -fx-border-color: transparent; + -fx-border-color: transparent; -fx-border-radius: 10 10 0 0; } .close-button-bottom{ - -fx-background-color: transparent; + -fx-background-color: -fx-darkbackground-trans; -fx-background-radius: 0 0 10 10; - -fx-border-color: transparent; + -fx-border-color: transparent; -fx-border-radius: 0 0 10 10; } diff --git a/src/Resources/css/primer-dark.css b/src/Resources/css/primer-dark.css index 6c1e53ca..a5f0a267 100644 --- a/src/Resources/css/primer-dark.css +++ b/src/Resources/css/primer-dark.css @@ -3666,58 +3666,41 @@ Text { } -/******************************************** -* * -* Spinner * -* * -*********************************************/ -.spinner { - -fx-pref-width: 120px; -} - - -/******************************************** -* * -* Chart * -* * -*********************************************/ - - - -.thin-chart .chart-series-line { - -fx-stroke-width: 1px; -} /******************************************************************************* * * - * Label * + * Pop over * * * ******************************************************************************/ - -#label-title1 { - -fx-font: bold 14pt -fx-font-family; +.popover { + -fx-background-color: -color-button-bg; + -fx-background-radius: 5; + -fx-border-radius: 5; } -#label-title2 { - -fx-font: bold 13pt -fx-font-family; - } - -/******************************************************************************* - * * - * Label * - * * - ******************************************************************************/ - - -.validatorfx-error { - -fx-text-fill: rgb(164, 0, 0); +.popover > .content { + -fx-background-color: -color-button-bg; + -fx-background-radius: 5; + -fx-border-radius: 5; } -.validatorfx-warning { - -fx-text-fill: rgb(196, 160, 0); +.popover > .arrow { + -fx-background-color: -color-button-bg; + -fx-background-radius: 5; + -fx-border-radius: 5; } + +/*weird way to do it but need this to colour the arrow*/ +.popover > .border { + /*-fx-stroke: linear-gradient(to bottom, rgba(0,0,0, .3), rgba(0, 0, 0, .7)) ;*/ + -fx-stroke-width: 0.5; + -fx-fill: -color-button-bg; /* instead -fx-background-color */ + -fx-background-color: -color-button-bg; + } + +} \ No newline at end of file diff --git a/src/Resources/css/primer-pamguard-dark.css b/src/Resources/css/primer-pamguard-dark.css new file mode 100644 index 00000000..ab1d4ecc --- /dev/null +++ b/src/Resources/css/primer-pamguard-dark.css @@ -0,0 +1,320 @@ +{ + + -color-dark: #010409; + -color-light: #ffffff; + -color-base-0: #f0f6fc; + -color-base-1: #c9d1d9; + -color-base-2: #b1bac4; + -color-base-3: #8b949e; + -color-base-4: #6e7681; + -color-base-5: #484f58; + -color-base-6: #30363d; + -color-base-7: #21262d; + -color-base-8: #161b22; + -color-base-9: #0d1117; + -color-accent-0: #cae8ff; + -color-accent-1: #a5d6ff; + -color-accent-2: #79c0ff; + -color-accent-3: #58a6ff; + -color-accent-4: #388bfd; + -color-accent-5: #1f6feb; + -color-accent-6: #1158c7; + -color-accent-7: #0d419d; + -color-accent-8: #0c2d6b; + -color-accent-9: #051d4d; + -color-success-0: #aff5b4; + -color-success-1: #7ee787; + -color-success-2: #56d364; + -color-success-3: #3fb950; + -color-success-4: #2ea043; + -color-success-5: #238636; + -color-success-6: #196c2e; + -color-success-7: #0f5323; + -color-success-8: #033a16; + -color-success-9: #04260f; + -color-warning-0: #f8e3a1; + -color-warning-1: #f2cc60; + -color-warning-2: #e3b341; + -color-warning-3: #d29922; + -color-warning-4: #bb8009; + -color-warning-5: #9e6a03; + -color-warning-6: #845306; + -color-warning-7: #693e00; + -color-warning-8: #4b2900; + -color-warning-9: #341a00; + -color-danger-0: #ffdcd7; + -color-danger-1: #ffc1ba; + -color-danger-2: #ffa198; + -color-danger-3: #ff7b72; + -color-danger-4: #f85149; + -color-danger-5: #da3633; + -color-danger-6: #b62324; + -color-danger-7: #8e1519; + -color-danger-8: #67060c; + -color-danger-9: #490202; + -color-fg-default: #c9d1d9; + -color-fg-muted: #8b949e; + -color-fg-subtle: #6e7681; + -color-fg-emphasis: #ffffff; + -color-bg-default: #0d1117; + -color-bg-overlay: #0d1117; + -color-bg-subtle: #161b22; + -color-bg-inset: #010409; + -color-border-default: #30363d; + -color-border-muted: #21262d; + -color-border-subtle: rgba(240, 246, 252, 0.1); + -color-shadow-default: #010409; + -color-neutral-emphasis-plus: #6e7681; + -color-neutral-emphasis: #6e7681; + -color-neutral-muted: rgba(110, 118, 129, 0.4); + -color-neutral-subtle: rgba(110, 118, 129, 0.1); + -color-accent-fg: #58a6ff; + -color-accent-emphasis: #1f6feb; + -color-accent-muted: rgba(56, 139, 253, 0.4); + -color-accent-subtle: rgba(56, 139, 253, 0.15); + -color-warning-fg: #d29922; + -color-warning-emphasis: #9e6a03; + -color-warning-muted: rgba(187, 128, 9, 0.4); + -color-warning-subtle: rgba(187, 128, 9, 0.15); + -color-success-fg: #3fb950; + -color-success-emphasis: #238636; + -color-success-muted: rgba(46, 160, 67, 0.4); + -color-success-subtle: rgba(46, 160, 67, 0.15); + -color-danger-fg: #f85149; + -color-danger-emphasis: #da3633; + -color-danger-muted: rgba(248, 81, 73, 0.4); + -color-danger-subtle: rgba(248, 81, 73, 0.15); + -color-chart-1: #f3622d; + -color-chart-2: #fba71b; + -color-chart-3: #57b757; + -color-chart-4: #41a9c9; + -color-chart-5: #4258c9; + -color-chart-6: #9a42c8; + -color-chart-7: #c84164; + -color-chart-8: #888888; + -color-chart-1-alpha70: rgba(243, 98, 45, 0.7); + -color-chart-2-alpha70: rgba(251, 167, 27, 0.7); + -color-chart-3-alpha70: rgba(87, 183, 87, 0.7); + -color-chart-4-alpha70: rgba(65, 169, 201, 0.7); + -color-chart-5-alpha70: rgba(66, 88, 201, 0.7); + -color-chart-6-alpha70: rgba(154, 66, 200, 0.7); + -color-chart-7-alpha70: rgba(200, 65, 100, 0.7); + -color-chart-8-alpha70: rgba(136, 136, 136, 0.7); + -color-chart-1-alpha20: rgba(243, 98, 45, 0.2); + -color-chart-2-alpha20: rgba(251, 167, 27, 0.2); + -color-chart-3-alpha20: rgba(87, 183, 87, 0.2); + -color-chart-4-alpha20: rgba(65, 169, 201, 0.2); + -color-chart-5-alpha20: rgba(66, 88, 201, 0.2); + -color-chart-6-alpha20: rgba(154, 66, 200, 0.2); + -color-chart-7-alpha20: rgba(200, 65, 100, 0.2); + -color-chart-8-alpha20: rgba(136, 136, 136, 0.2); + + -fx-pambackground: rgba(238,238,238); + -fx-darkbackground: -color-bg-default; + -fx-darkbackground-trans: rgba(13, 17, 23,0.90); + -fx-highlight: -color-base-6; + -fx-highlight_border: -color-button-fg; + -fx-text: -color-fg-default; + -fx-border_col: -color-border-default; + -fx-border_color: -color-border-default; + -fx-icon_col: -color-fg-default; +} + + +/******************************************************************************* + * * + * For some reason highlighting on hover goes all worng so need to fix this * + * * + ******************************************************************************/ + +.button { + -color-button-border: -color-border-default; + -color-button-bg-hover: -color-base-6; + -color-button-fg-hover: -color-button-fg; + -color-button-border-hover: -color-button-border; +} + +.menu-button, +.split-menu-button { + + -color-button-bg-hover: -color-base-6; + -color-button-fg-hover: -color-button-fg; + -color-button-border-hover: -color-button-border; + -color-button-bg-focused: -color-button-bg; + -color-button-fg-focused: -color-button-fg; + -color-button-border-focused: -color-accent-emphasis; + -color-button-bg-pressed: -color-bg-subtle; + -color-button-fg-pressed: -color-button-fg; +} + +.menu-button.accent, +.split-menu-button.accent { + -color-button-bg: -color-accent-emphasis; + -color-button-fg: -color-fg-emphasis; + -color-button-border: -color-accent-emphasis; + -color-button-bg-hover: -color-accent-emphasis; + -color-button-fg-hover: -color-fg-emphasis; + -color-button-border-hover: -color-accent-emphasis; + -color-button-bg-focused: -color-accent-6; + -color-button-fg-focused: -color-fg-emphasis; + -color-button-border-focused: -color-accent-emphasis; + -color-button-bg-pressed: -color-accent-emphasis; + -color-button-fg-pressed: -color-fg-emphasis; + -color-button-border-pressed: transparent; +} +.menu-button.accent.button-outlined, +.split-menu-button.accent.button-outlined { + -color-button-bg: -color-bg-default; + -color-button-fg: -color-accent-fg; + -color-button-bg-hover: -color-accent-emphasis; + -color-button-fg-hover: -color-fg-emphasis; +} +.menu-button.accent.flat, +.split-menu-button.accent.flat { + -color-button-fg: -color-accent-fg; + -color-button-bg-hover: -color-accent-subtle; +} +.menu-button.success, +.split-menu-button.success { + -color-button-bg: -color-success-emphasis; + -color-button-fg: -color-fg-emphasis; + -color-button-border: -color-success-emphasis; + -color-button-bg-hover: -color-success-emphasis; + -color-button-fg-hover: -color-fg-emphasis; + -color-button-border-hover: -color-success-emphasis; + -color-button-bg-focused: -color-success-6; + -color-button-fg-focused: -color-fg-emphasis; + -color-button-border-focused: -color-success-emphasis; + -color-button-bg-pressed: -color-success-emphasis; + -color-button-fg-pressed: -color-fg-emphasis; + -color-button-border-pressed: transparent; +} +.menu-button.success.button-outlined, +.split-menu-button.success.button-outlined { + -color-button-bg: -color-bg-default; + -color-button-fg: -color-success-fg; + -color-button-bg-hover: -color-success-emphasis; + -color-button-fg-hover: -color-fg-emphasis; +} +.menu-button.success.flat, +.split-menu-button.success.flat { + -color-button-fg: -color-success-fg; + -color-button-bg-hover: -color-success-subtle; +} +.menu-button.danger, +.split-menu-button.danger { + -color-button-bg: -color-danger-emphasis; + -color-button-fg: -color-fg-emphasis; + -color-button-border: -color-danger-emphasis; + -color-button-bg-hover: -color-danger-emphasis; + -color-button-fg-hover: -color-fg-emphasis; + -color-button-border-hover: -color-danger-emphasis; + -color-button-bg-focused: -color-danger-6; + -color-button-fg-focused: -color-fg-emphasis; + -color-button-border-focused: -color-danger-emphasis; + -color-button-bg-pressed: -color-danger-emphasis; + -color-button-fg-pressed: -color-fg-emphasis; + -color-button-border-pressed: transparent; +} +.menu-button.danger.button-outlined, +.split-menu-button.danger.button-outlined { + -color-button-bg: -color-bg-default; + -color-button-fg: -color-danger-fg; + -color-button-bg-hover: -color-danger-emphasis; + -color-button-fg-hover: -color-fg-emphasis; +} +.menu-button.danger.flat, +.split-menu-button.danger.flat { + -color-button-fg: -color-danger-fg; + -color-button-bg-hover: -color-danger-subtle; +} +.menu-button.flat, +.split-menu-button.flat { + -color-button-bg: transparent; + -color-button-fg: -color-fg-default; + -color-button-border: transparent; + -color-button-bg-hover: -color-bg-subtle; + -color-button-fg-hover: -color-button-fg; + -color-button-border-hover: -color-bg-subtle; + -color-button-bg-focused: -color-button-bg; + -color-button-fg-focused: -color-button-fg; + -color-button-border-focused: -color-button-bg; + -color-button-bg-pressed: -color-button-bg; + -color-button-fg-pressed: -color-button-fg; + -color-button-border-pressed: transparent; +} + + +.toggle-button { + -color-button-bg: -color-bg-subtle; + -color-button-fg: -color-fg-default; + -color-button-border: -color-border-default; + -color-button-bg-hover: -color-base-6; + -color-button-fg-hover: -color-button-fg; + -color-button-border-hover: -color-button-border; + -color-button-bg-focused: -color-button-bg; + -color-button-fg-focused: -color-button-fg; + -color-button-border-focused: -color-accent-emphasis; + -color-button-bg-pressed: -color-bg-subtle; + -color-button-fg-pressed: -color-button-fg; + -color-button-border-pressed: transparent; +} + + +.list-view { + -color-cell-bg-selected: -color-base-6; + -color-cell-fg-selected: -color-fg-default; +} + +.table-view { + -color-cell-bg-selected: -color-base-6; + -color-cell-fg-selected: -color-fg-default; +} + +.titled-pane > .title > .text { + -fx-font-size: 1em; +} + +.tree-view { + -color-cell-bg-selected: -color-base-6; + -color-cell-fg-selected: -color-fg-default; +} + +.tree-table-view { + + -color-cell-bg-selected: -color-base-6; + -color-cell-fg-selected: -color-fg-default; + +} + +.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected, +.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:selected, +.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected, +.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:selected { + -color-cell-fg: -color-cell-fg-selected; + -fx-background-color: -color-cell-border, -color-cell-bg-selected; +} + +/******************************************************************************* + * * + * For some reason the table background is white and so this needs fixed. + * * + ******************************************************************************/ + + +.table-view { + -color-cell-bg: -color-bg-default; + -color-cell-fg: -color-fg-default; + -color-cell-bg-selected: -color-base-6; + -color-cell-fg-selected: -color-fg-default; + -color-cell-bg-odd: -color-bg-subtle; + -color-cell-border: -color-border-default; + -fx-border-color: -color-cell-border; + -fx-control-inner-background: -color-bg-default; + -fx-border-width: 1px; + -fx-border-radius: 0; + -color-header-bg: -color-bg-subtle; + -color-header-fg: -color-fg-default; +} + + diff --git a/src/Resources/css/primer-pamguard.css b/src/Resources/css/primer-pamguard.css index 1e28539e..ece247b6 100644 --- a/src/Resources/css/primer-pamguard.css +++ b/src/Resources/css/primer-pamguard.css @@ -1,114 +1,5 @@ - { - -color-dark: #010409; - -color-light: #ffffff; - -color-base-0: #f0f6fc; - -color-base-1: #c9d1d9; - -color-base-2: #b1bac4; - -color-base-3: #8b949e; - -color-base-4: #6e7681; - -color-base-5: #484f58; - -color-base-6: #30363d; - -color-base-7: #21262d; - -color-base-8: #161b22; - -color-base-9: #0d1117; - -color-accent-0: #cae8ff; - -color-accent-1: #a5d6ff; - -color-accent-2: #79c0ff; - -color-accent-3: #58a6ff; - -color-accent-4: #388bfd; - -color-accent-5: #1f6feb; - -color-accent-6: #1158c7; - -color-accent-7: #0d419d; - -color-accent-8: #0c2d6b; - -color-accent-9: #051d4d; - -color-success-0: #aff5b4; - -color-success-1: #7ee787; - -color-success-2: #56d364; - -color-success-3: #3fb950; - -color-success-4: #2ea043; - -color-success-5: #238636; - -color-success-6: #196c2e; - -color-success-7: #0f5323; - -color-success-8: #033a16; - -color-success-9: #04260f; - -color-warning-0: #f8e3a1; - -color-warning-1: #f2cc60; - -color-warning-2: #e3b341; - -color-warning-3: #d29922; - -color-warning-4: #bb8009; - -color-warning-5: #9e6a03; - -color-warning-6: #845306; - -color-warning-7: #693e00; - -color-warning-8: #4b2900; - -color-warning-9: #341a00; - -color-danger-0: #ffdcd7; - -color-danger-1: #ffc1ba; - -color-danger-2: #ffa198; - -color-danger-3: #ff7b72; - -color-danger-4: #f85149; - -color-danger-5: #da3633; - -color-danger-6: #b62324; - -color-danger-7: #8e1519; - -color-danger-8: #67060c; - -color-danger-9: #490202; - -color-fg-default: #c9d1d9; - -color-fg-muted: #8b949e; - -color-fg-subtle: #6e7681; - -color-fg-emphasis: #ffffff; - -color-bg-default: #0d1117; - -color-bg-overlay: #0d1117; - -color-bg-subtle: #161b22; - -color-bg-inset: #010409; - -color-border-default: #30363d; - -color-border-muted: #21262d; - -color-border-subtle: rgba(240, 246, 252, 0.1); - -color-shadow-default: #010409; - -color-neutral-emphasis-plus: #6e7681; - -color-neutral-emphasis: #6e7681; - -color-neutral-muted: rgba(110, 118, 129, 0.4); - -color-neutral-subtle: rgba(110, 118, 129, 0.1); - -color-accent-fg: #58a6ff; - -color-accent-emphasis: #1f6feb; - -color-accent-muted: rgba(56, 139, 253, 0.4); - -color-accent-subtle: rgba(56, 139, 253, 0.15); - -color-warning-fg: #d29922; - -color-warning-emphasis: #9e6a03; - -color-warning-muted: rgba(187, 128, 9, 0.4); - -color-warning-subtle: rgba(187, 128, 9, 0.15); - -color-success-fg: #3fb950; - -color-success-emphasis: #238636; - -color-success-muted: rgba(46, 160, 67, 0.4); - -color-success-subtle: rgba(46, 160, 67, 0.15); - -color-danger-fg: #f85149; - -color-danger-emphasis: #da3633; - -color-danger-muted: rgba(248, 81, 73, 0.4); - -color-danger-subtle: rgba(248, 81, 73, 0.15); - -color-chart-1: #f3622d; - -color-chart-2: #fba71b; - -color-chart-3: #57b757; - -color-chart-4: #41a9c9; - -color-chart-5: #4258c9; - -color-chart-6: #9a42c8; - -color-chart-7: #c84164; - -color-chart-8: #888888; - -color-chart-1-alpha70: rgba(243, 98, 45, 0.7); - -color-chart-2-alpha70: rgba(251, 167, 27, 0.7); - -color-chart-3-alpha70: rgba(87, 183, 87, 0.7); - -color-chart-4-alpha70: rgba(65, 169, 201, 0.7); - -color-chart-5-alpha70: rgba(66, 88, 201, 0.7); - -color-chart-6-alpha70: rgba(154, 66, 200, 0.7); - -color-chart-7-alpha70: rgba(200, 65, 100, 0.7); - -color-chart-8-alpha70: rgba(136, 136, 136, 0.7); - -color-chart-1-alpha20: rgba(243, 98, 45, 0.2); - -color-chart-2-alpha20: rgba(251, 167, 27, 0.2); - -color-chart-3-alpha20: rgba(87, 183, 87, 0.2); - -color-chart-4-alpha20: rgba(65, 169, 201, 0.2); - -color-chart-5-alpha20: rgba(66, 88, 201, 0.2); - -color-chart-6-alpha20: rgba(154, 66, 200, 0.2); - -color-chart-7-alpha20: rgba(200, 65, 100, 0.2); - -color-chart-8-alpha20: rgba(136, 136, 136, 0.2); -fx-pambackground: rgba(238,238,238); -fx-darkbackground: -color-bg-default; @@ -117,17 +8,19 @@ -fx-highlight_border: -color-button-fg; -fx-text: -color-fg-default; -fx-border_col: -color-border-default; + -fx-border_color: -color-border-default; -fx-icon_col: -color-fg-default; + -fx-highlight_border_settings: rgba(0,204,204,1); + -fx-highlight_settings: rgba(0,204,204,1); } -.root { - -fx-background-color:-fx-darkbackground; - -fx-font-size: 12px; - -fx-background-radius: inherit; - -fx-background-insets: inherit; - -fx-padding: 5px 5px 5px 5px; + +.root{ + -fx-font-size: 10pt; + -fx-font-family: "Ubuntu"; } + /******************************************************************************* * * * Panes * @@ -151,240 +44,6 @@ -fx-background-color: -fx-plotbackground; } - -.button { - -color-button-bg: -color-bg-subtle; - -color-button-fg: -color-fg-default; - -color-button-border: -color-border-default; - -color-button-bg-hover: -color-base-6; - -color-button-fg-hover: -color-button-fg; - -color-button-border-hover: -color-button-border; - -color-button-bg-focused: -color-button-bg; - -color-button-fg-focused: -color-button-fg; - -color-button-border-focused: -color-accent-emphasis; - -color-button-bg-pressed: -color-bg-subtle; - -color-button-fg-pressed: -color-button-fg; - -color-button-border-pressed: transparent; - -fx-background-color: -color-button-border, -color-button-bg; - -fx-background-insets: 0, 1px; - -fx-background-radius: 4px; - -fx-graphic-text-gap: 6px; - -fx-text-fill: -color-button-fg; - -fx-alignment: CENTER; - -fx-padding: 8px 12px 8px 12px; -} - -.menu-button, -.split-menu-button { - -color-button-bg: -color-bg-subtle; - -color-button-fg: -color-fg-default; - -color-button-border: -color-border-default; - -color-button-bg-hover: -color-base-6; - -color-button-fg-hover: -color-button-fg; - -color-button-border-hover: -color-button-border; - -color-button-bg-focused: -color-button-bg; - -color-button-fg-focused: -color-button-fg; - -color-button-border-focused: -color-accent-emphasis; - -color-button-bg-pressed: -color-bg-subtle; - -color-button-fg-pressed: -color-button-fg; - -color-button-border-pressed: transparent; - -fx-background-color: -color-button-border, -color-button-bg; - -fx-background-insets: 0, 1px; - -fx-background-radius: 4px; - -fx-graphic-text-gap: 6px; - -fx-text-fill: -color-button-fg; - -fx-alignment: CENTER; - -fx-padding: 0; - -fx-alignment: CENTER_LEFT; -} - -.menu-button.accent, -.split-menu-button.accent { - -color-button-bg: -color-accent-emphasis; - -color-button-fg: -color-fg-emphasis; - -color-button-border: -color-accent-emphasis; - -color-button-bg-hover: -color-accent-emphasis; - -color-button-fg-hover: -color-fg-emphasis; - -color-button-border-hover: -color-accent-emphasis; - -color-button-bg-focused: -color-accent-6; - -color-button-fg-focused: -color-fg-emphasis; - -color-button-border-focused: -color-accent-emphasis; - -color-button-bg-pressed: -color-accent-emphasis; - -color-button-fg-pressed: -color-fg-emphasis; - -color-button-border-pressed: transparent; -} -.menu-button.accent.button-outlined, -.split-menu-button.accent.button-outlined { - -color-button-bg: -color-bg-default; - -color-button-fg: -color-accent-fg; - -color-button-bg-hover: -color-accent-emphasis; - -color-button-fg-hover: -color-fg-emphasis; -} -.menu-button.accent.flat, -.split-menu-button.accent.flat { - -color-button-fg: -color-accent-fg; - -color-button-bg-hover: -color-accent-subtle; -} -.menu-button.success, -.split-menu-button.success { - -color-button-bg: -color-success-emphasis; - -color-button-fg: -color-fg-emphasis; - -color-button-border: -color-success-emphasis; - -color-button-bg-hover: -color-success-emphasis; - -color-button-fg-hover: -color-fg-emphasis; - -color-button-border-hover: -color-success-emphasis; - -color-button-bg-focused: -color-success-6; - -color-button-fg-focused: -color-fg-emphasis; - -color-button-border-focused: -color-success-emphasis; - -color-button-bg-pressed: -color-success-emphasis; - -color-button-fg-pressed: -color-fg-emphasis; - -color-button-border-pressed: transparent; -} -.menu-button.success.button-outlined, -.split-menu-button.success.button-outlined { - -color-button-bg: -color-bg-default; - -color-button-fg: -color-success-fg; - -color-button-bg-hover: -color-success-emphasis; - -color-button-fg-hover: -color-fg-emphasis; -} -.menu-button.success.flat, -.split-menu-button.success.flat { - -color-button-fg: -color-success-fg; - -color-button-bg-hover: -color-success-subtle; -} -.menu-button.danger, -.split-menu-button.danger { - -color-button-bg: -color-danger-emphasis; - -color-button-fg: -color-fg-emphasis; - -color-button-border: -color-danger-emphasis; - -color-button-bg-hover: -color-danger-emphasis; - -color-button-fg-hover: -color-fg-emphasis; - -color-button-border-hover: -color-danger-emphasis; - -color-button-bg-focused: -color-danger-6; - -color-button-fg-focused: -color-fg-emphasis; - -color-button-border-focused: -color-danger-emphasis; - -color-button-bg-pressed: -color-danger-emphasis; - -color-button-fg-pressed: -color-fg-emphasis; - -color-button-border-pressed: transparent; -} -.menu-button.danger.button-outlined, -.split-menu-button.danger.button-outlined { - -color-button-bg: -color-bg-default; - -color-button-fg: -color-danger-fg; - -color-button-bg-hover: -color-danger-emphasis; - -color-button-fg-hover: -color-fg-emphasis; -} -.menu-button.danger.flat, -.split-menu-button.danger.flat { - -color-button-fg: -color-danger-fg; - -color-button-bg-hover: -color-danger-subtle; -} -.menu-button.flat, -.split-menu-button.flat { - -color-button-bg: transparent; - -color-button-fg: -color-fg-default; - -color-button-border: transparent; - -color-button-bg-hover: -color-bg-subtle; - -color-button-fg-hover: -color-button-fg; - -color-button-border-hover: -color-bg-subtle; - -color-button-bg-focused: -color-button-bg; - -color-button-fg-focused: -color-button-fg; - -color-button-border-focused: -color-button-bg; - -color-button-bg-pressed: -color-button-bg; - -color-button-fg-pressed: -color-button-fg; - -color-button-border-pressed: transparent; -} - - -.toggle-button { - -color-button-bg: -color-bg-subtle; - -color-button-fg: -color-fg-default; - -color-button-border: -color-border-default; - -color-button-bg-hover: -color-base-6; - -color-button-fg-hover: -color-button-fg; - -color-button-border-hover: -color-button-border; - -color-button-bg-focused: -color-button-bg; - -color-button-fg-focused: -color-button-fg; - -color-button-border-focused: -color-accent-emphasis; - -color-button-bg-pressed: -color-bg-subtle; - -color-button-fg-pressed: -color-button-fg; - -color-button-border-pressed: transparent; - -fx-background-color: -color-button-border, -color-button-bg; - -fx-background-insets: 0, 1px; - -fx-background-radius: 4px; - -fx-graphic-text-gap: 6px; - -fx-text-fill: -color-button-fg; - -fx-alignment: CENTER; - -color-button-bg-selected: -color-accent-emphasis; - -color-button-fg-selected: -color-fg-emphasis; - -fx-padding: 8px 12px 8px 12px; -} - - -.list-view { - -color-cell-bg: -color-bg-default; - -color-cell-fg: -color-fg-default; - -color-cell-bg-selected: -color-base-6; - -color-cell-fg-selected: -color-fg-default; - -color-cell-bg-odd: -color-bg-subtle; - -color-cell-border: -color-border-default; - -fx-border-color: -color-cell-border; - -fx-border-width: 1px; - -fx-border-radius: 0; -} - -.table-view { - -color-cell-bg: -color-bg-default; - -color-cell-fg: -color-fg-default; - -color-cell-bg-selected: -color-base-6; - -color-cell-fg-selected: -color-fg-default; - -color-cell-bg-odd: -color-bg-subtle; - -color-cell-border: -color-border-default; - -fx-border-color: -color-cell-border; - -fx-border-width: 1px; - -fx-border-radius: 0; - -color-header-bg: -color-bg-subtle; - -color-header-fg: -color-fg-default; -} - -.titled-pane > .title > .text { - -fx-font-size: 1em; -} - -.tree-view { - -color-cell-bg: -color-bg-default; - -color-cell-fg: -color-fg-default; - -color-cell-bg-selected: -color-base-6; - -color-cell-fg-selected: -color-fg-default; - -color-cell-bg-odd: -color-bg-subtle; - -color-cell-border: -color-border-default; - -fx-border-color: -color-cell-border; - -fx-border-width: 1px; - -fx-border-radius: 0; -} - -.tree-table-view { - -color-cell-bg: -color-bg-default; - -color-cell-fg: -color-fg-default; - -color-cell-bg-selected: -color-base-6; - -color-cell-fg-selected: -color-fg-default; - -color-cell-bg-odd: -color-bg-subtle; - -color-cell-border: -color-border-default; - -fx-border-color: -color-cell-border; - -fx-border-width: 1px; - -fx-border-radius: 0; - -color-header-bg: -color-bg-subtle; - -color-header-fg: -color-fg-default; -} - -.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected, -.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:selected, -.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected, -.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:selected { - -color-cell-fg: -color-cell-fg-selected; - -fx-background-color: -color-cell-border, -color-cell-bg-selected; -} - /******************************************************************************* * * * ikonli icons * @@ -402,54 +61,50 @@ -fx-effect: none; } +/******************************************************************************* + * * + * Label * + * * + ******************************************************************************/ -/**************************************************************** - - ScrollPane - -****************************************************************/ -.scroll-pane { - -fx-background: #0d1117; - -fx-background-color: transparent; -} - -.scroll-pane > .viewport { - -fx-background-color: transparent; -} - -.scroll-pane-dark { - -fx-background: -fx-darkbackground; - -fx-background-color: -fx-darkbackground; + +#label-title1 { + -fx-font: bold 14pt -fx-font-family; } +#label-title2 { + -fx-font: bold 13pt -fx-font-family; + } /******************************************************************************* * * - * Hiding Tab * + * Validator * * * ******************************************************************************/ - #hide-tab { - -fx-background-color: transparent; - -fx-border-radius: 5 5 0 0; - -fx-background-radius: 5 5 0 0; - -fx-border-color: -fx-border_col; + + +.validatorfx-error { + -fx-text-fill: rgb(164, 0, 0); } - #show-tab { - -fx-background-color: transparent; - -fx-border-radius: 5 5 0 0; - -fx-background-radius: 5 5 0 0; - -fx-border-color: -fx-border_col; +.validatorfx-warning { + -fx-text-fill: rgb(196, 160, 0); } - #hide-tab-highlight { - -fx-background-color: -fx-highlight; - -fx-border-radius: 5 5 0 0; - -fx-background-radius: 5 5 0 0; - -fx-border-color: -fx-highlight_border; -} +/******************************************** +* * +* Chart * +* * +*********************************************/ + + + +.thin-chart .chart-series-line { + -fx-stroke-width: 1px; +} + /******************************************************************************* * * @@ -475,13 +130,14 @@ -fx-border-color: -fx-highlight; } */ + /** * Button for closing a hiding panel. Right indicates bottoms points towards the right, closing a hiding panel * on the right hand side of the screen. These buttons have a transparent background and rounded corners. */ /** top-left, top-right, bottom-right, and bottom-left corners, in that order. */ .close-button-right{ - -fx-background-color: transparent; + -fx-background-color: -fx-darkbackground-trans; -fx-background-radius: 0 10 10 0; -fx-border-color: transparent; -fx-border-radius: 0 10 10 0; @@ -489,7 +145,7 @@ .close-button-left{ - -fx-background-color: transparent; + -fx-background-color: -fx-darkbackground-trans; -fx-background-radius: 10 0 0 10; -fx-border-color: transparent; -fx-border-radius: 10 0 0 10; @@ -497,21 +153,22 @@ .close-button-top{ - -fx-background-color: transparent; + -fx-background-color: -fx-darkbackground-trans; -fx-background-radius: 10 10 0 0; --fx-border-color: transparent; + -fx-border-color: transparent; -fx-border-radius: 10 10 0 0; } .close-button-bottom{ - -fx-background-color: transparent; + -fx-background-color: -fx-darkbackground-trans; -fx-background-radius: 0 0 10 10; - -fx-border-color: transparent; + -fx-border-color: transparent; -fx-border-radius: 0 0 10 10; } + .close-button-bottom-trans{ -fx-border-color: transparent; -fx-background-color: transparent; @@ -577,7 +234,8 @@ .popover > .content { -fx-background-color: -fx-darkbackground; -fx-background-radius: 5; - -fx-border-radius: 5; + -fx-border-radius: 5; + -fx-padding: 0 0 0 0; } .popover > .arrow { @@ -636,8 +294,11 @@ } -/**********/ - +/******************************************************************************* + * * + * Menu Items * + * * + ******************************************************************************/ .menu-item { -fx-background-color: -color-bg-default; @@ -671,8 +332,6 @@ * Spinner * * * *********************************************/ - - .spinner { -fx-pref-width: 120px; } @@ -682,98 +341,89 @@ } +/**************************************************************** + + ScrollPane + +****************************************************************/ +.scroll-pane { + -fx-background: #0d1117; + -fx-background-color: transparent; +} + +.scroll-pane > .viewport { + -fx-background-color: transparent; +} + +.scroll-pane-dark { + -fx-background: -fx-darkbackground; + -fx-background-color: -fx-darkbackground; +} + /******************************************************************************* * * - * Hiding Pane * + * Hiding Tab * + * * + ******************************************************************************/ + #hide-tab { + -fx-background-color: transparent; + -fx-border-radius: 5 5 0 0; + -fx-background-radius: 5 5 0 0; + -fx-border-color: -fx-border_col; +} + + #show-tab { + -fx-background-color: transparent; + -fx-border-radius: 5 5 0 0; + -fx-background-radius: 5 5 0 0; + -fx-border-color: -fx-border_col; +} + + #hide-tab-highlight { + -fx-background-color: -fx-highlight; + -fx-border-radius: 5 5 0 0; + -fx-background-radius: 5 5 0 0; + -fx-border-color: -fx-highlight_border; +} + +/******************************************************************************* + * * + * Pam Internal Pane * + * * + ******************************************************************************/ + +.circle-internal { + -fx-fill: -fx-highlight_settings; + -fx-stroke: -fx-highlight_border_settings; + -fx-stroke-width: 2; +} + +.line-internal { + -fx-fill: -fx-highlight_border_settings; + -fx-stroke: -fx-highlight_border_settings; + -fx-stroke-width: 3; + -fx-stroke-dash-array: 3.0 7.0 3.0 7.0; +} + +.button-internal { + -fx-background-color: -fx-highlight_settings; + -fx-border-color: -fx-highlight_border_settings; + -fx-background-radius: 10; + -fx-border-radius: 10; +} + +.button-internal:hover { + -fx-background-color: -fx-highlight_border_settings; +} + +/******************************************************************************* + * * + * Dialog * * * ******************************************************************************/ -/** - * Button for closing a hiding panel. Right indicates bottoms points towards the right, closing a hiding panel - * on the right hand side of the screen. These buttons have a transparent background and rounded corners. - */ - /** top-left, top-right, bottom-right, and bottom-left corners, in that order. */ -.close-button-right{ - -fx-background-color: transparent; - -fx-background-radius: 0 10 10 0; - -fx-border-radius: 0 10 10 0; -} - -.close-button-right:hover { - -fx-background-color: -fx-highlight; -} - - -.close-button-left{ - -fx-background-color: transparent; - -fx-background-radius: 10 0 0 10; - -fx-border-radius: 10 0 0 10; -} - -.close-button-left:hover { - -fx-background-color: -fx-highlight; -} - - -.close-button-top{ - -fx-background-color: transparent; - -fx-background-radius: 10 10 0 0; - -fx-border-radius: 10 10 0 0; -} - - -.close-button-bottom { - -fx-background-color: transparent; - -fx-background-radius: 0 0 10 10; - -fx-border-radius: 0 0 10 10; -} - -.close-button-bottom:hover { - -fx-background-color: -fx-highlight; -} - - -.close-button-bottom-grey{ --fx-border-color: transparent; - -fx-background-radius: 0 0 10 10; - -fx-border-radius: 0 0 10 10; -} - -.close-button-left-trans{ - -fx-border-color: transparent; - -fx-background-color: transparent; - -fx-background-radius: 10 0 0 10; - -fx-border-radius: 10 0 0 10; -} - -.close-button-left-trans:focused { - -fx-border-color: transparent; - -fx-background-color: transparent; - -fx-background-radius: 10 0 0 10; - -fx-border-radius: 10 0 0 10; -} - -.close-button-right-trans{ - -fx-border-color: transparent; - -fx-background-color: transparent; - -fx-background-radius: 0 10 10 0; - -fx-border-radius: 0 10 10 0; -} - -.close-button-right-trans:focused { - -fx-border-color: transparent; - -fx-background-color: transparent; - -fx-background-radius: 0 10 10 0; - -fx-border-radius: 0 10 10 0; -} - - -.menu-square-button { --fx-border-color: -fx_border_col; - -fx-background-color: transparent; - -fx-background-radius: 0 0 0 0; - -fx-border-radius: 0 0 0 0; -} - +.dialog-pane > .content { + -fx-padding: 0,0,0,0; +} \ No newline at end of file diff --git a/src/Resources/delphinid_logo01.svg b/src/Resources/delphinid_logo01.svg new file mode 100644 index 00000000..744670bf --- /dev/null +++ b/src/Resources/delphinid_logo01.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + delphinID + + + + + + diff --git a/src/Resources/exampleSounds/Blue_whale.wav b/src/Resources/exampleSounds/Blue_whale.wav new file mode 100644 index 00000000..8bca2058 Binary files /dev/null and b/src/Resources/exampleSounds/Blue_whale.wav differ diff --git a/src/Resources/modules/Array Icon2.svg b/src/Resources/modules/Array Icon2.svg new file mode 100644 index 00000000..2ca3e501 --- /dev/null +++ b/src/Resources/modules/Array Icon2.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/modules/Click Detector Icon.svg b/src/Resources/modules/Click Detector Icon.svg new file mode 100644 index 00000000..25f15d3e --- /dev/null +++ b/src/Resources/modules/Click Detector Icon.svg @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/modules/clicktrain.svg b/src/Resources/modules/clicktrain.svg new file mode 100644 index 00000000..046aa6fe --- /dev/null +++ b/src/Resources/modules/clicktrain.svg @@ -0,0 +1,393 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/modules/decimator.svg b/src/Resources/modules/decimator.svg new file mode 100644 index 00000000..79be1add --- /dev/null +++ b/src/Resources/modules/decimator.svg @@ -0,0 +1,482 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/modules/detectionDisplay.svg b/src/Resources/modules/detectionDisplay.svg new file mode 100644 index 00000000..8ed70427 --- /dev/null +++ b/src/Resources/modules/detectionDisplay.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/modules/fft.svg b/src/Resources/modules/fft.svg new file mode 100644 index 00000000..f65bbefc --- /dev/null +++ b/src/Resources/modules/fft.svg @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/modules/filters.svg b/src/Resources/modules/filters.svg new file mode 100644 index 00000000..8997d124 --- /dev/null +++ b/src/Resources/modules/filters.svg @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/modules/matched_click_classifier.svg b/src/Resources/modules/matched_click_classifier.svg new file mode 100644 index 00000000..9ec8e0c8 --- /dev/null +++ b/src/Resources/modules/matched_click_classifier.svg @@ -0,0 +1,483 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/binaryFileStorage/BinaryMapMakeProgress.java b/src/binaryFileStorage/BinaryMapMakeProgress.java index 02e13578..3cb8a10e 100644 --- a/src/binaryFileStorage/BinaryMapMakeProgress.java +++ b/src/binaryFileStorage/BinaryMapMakeProgress.java @@ -87,42 +87,30 @@ public class BinaryMapMakeProgress extends PamTaskUpdate { @Override public double getProgress(){ - //System.out.println("BinaryProgress: " + currentStream + " tot: " + totalStreams); +// System.out.println("BinaryProgress: " + currentStream + " of: " + totalStreams + " status: " + getStatus()); if (totalStreams==0) return 1.; double progress = 0; switch (getStatus()){ - case PamTaskUpdate.STATUS_ANALYSING_FILES: - if (totalStreams==0) progress=0; + case STATUS_ANALYSING_FILES: + if (totalStreams==0) progress=-1; else progress=((double) currentStream)/totalStreams; break; - case PamTaskUpdate.STATUS_COUNTING_FILES: - progress=ProgressIndicator.INDETERMINATE_PROGRESS; - break; - case PamTaskUpdate.STATUS_DESERIALIZING: - progress=ProgressIndicator.INDETERMINATE_PROGRESS; - break; - case PamTaskUpdate.STATUS_DONE: - progress=ProgressIndicator.INDETERMINATE_PROGRESS; - break; - case PamTaskUpdate.STATUS_DONE_ERROR: - progress=ProgressIndicator.INDETERMINATE_PROGRESS; - break; - case PamTaskUpdate.STATUS_IDLE: - progress=ProgressIndicator.INDETERMINATE_PROGRESS; - break; - case PamTaskUpdate.STATUS_SERIALIZING: - progress=ProgressIndicator.INDETERMINATE_PROGRESS; - break; - case PamTaskUpdate.STATUS_SORTING: + default: progress=ProgressIndicator.INDETERMINATE_PROGRESS; break; } return progress; } + @Override public String getName() { return "Binary Data Map"; } + + @Override + public String getProgressString() { + return this.streamName; + } } diff --git a/src/clickDetector/ClickClassifiers/basicSweep/SweepClassifierWorker.java b/src/clickDetector/ClickClassifiers/basicSweep/SweepClassifierWorker.java index 6a52b01c..15af8d1d 100644 --- a/src/clickDetector/ClickClassifiers/basicSweep/SweepClassifierWorker.java +++ b/src/clickDetector/ClickClassifiers/basicSweep/SweepClassifierWorker.java @@ -629,7 +629,8 @@ public class SweepClassifierWorker { * @return true if bearing limits are passed */ private boolean testBearings(ClickDetection click, SweepClassifierSet scs) { - + if (click.getLocalisation()==null) return true; + if (click.getLocalisation().getAngles()==null) return true; //passes the test if there is no bearing info. double bearing = click.getLocalisation().getAngles()[0]; diff --git a/src/clickDetector/ClickTemplate.java b/src/clickDetector/ClickTemplate.java index 61a4b4ef..3d778229 100644 --- a/src/clickDetector/ClickTemplate.java +++ b/src/clickDetector/ClickTemplate.java @@ -1,7 +1,6 @@ package clickDetector; import java.awt.Color; -import java.awt.Paint; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; diff --git a/src/clickDetector/ClickTrainDetector.java b/src/clickDetector/ClickTrainDetector.java index a0141e03..8bfe6bb3 100644 --- a/src/clickDetector/ClickTrainDetector.java +++ b/src/clickDetector/ClickTrainDetector.java @@ -58,7 +58,6 @@ public class ClickTrainDetector extends PamProcess implements PamSettings { this.clickDataBlock = clickDataBlock; - PamSettingManager.getInstance().registerSettings(this); clickDataBlock.addObserver(this); diff --git a/src/clickDetector/layoutFX/ClickSettingsPane.java b/src/clickDetector/layoutFX/ClickSettingsPane.java index a62f3be0..1f10f122 100644 --- a/src/clickDetector/layoutFX/ClickSettingsPane.java +++ b/src/clickDetector/layoutFX/ClickSettingsPane.java @@ -7,20 +7,18 @@ import clickDetector.ClickControl; import clickDetector.ClickParameters; import clickDetector.layoutFX.clickClassifiers.ClickClassifyPaneFX; import javafx.geometry.HPos; +import javafx.geometry.Insets; import javafx.geometry.Orientation; -import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.control.Spinner; import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; -import javafx.scene.paint.Color; -import javafx.scene.shape.Line; +import javafx.scene.layout.Region; import javafx.util.StringConverter; import net.synedra.validatorfx.Validator; import PamController.PamController; @@ -31,12 +29,12 @@ import PamguardMVC.PamConstants; import PamguardMVC.PamDataBlock; import PamguardMVC.PamRawDataBlock; import pamViewFX.PamGuiManagerFX; -import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamGridPane; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamSpinner; -import pamViewFX.fxNodes.PamTabPane; import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.navigationDrawer.NavigationDrawer; import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; import pamViewFX.fxNodes.utilityPanes.FilterPaneFX; import pamViewFX.fxNodes.utilityPanes.GroupedSourcePaneFX; @@ -68,7 +66,7 @@ public class ClickSettingsPane extends SettingsPane{ /** * The main tab pane. */ - private PamTabPane pamTabbedPane; + private NavigationDrawer pamTabbedPane; /** * Pane for the pre-filter. The acoustic data detected click waveforms are extracted from but not @@ -163,10 +161,6 @@ public class ClickSettingsPane extends SettingsPane{ */ private Tab tdoaTab; - /** - * The main holder pane. - */ - private PamBorderPane mainPane; /** * The default pane height. @@ -176,7 +170,7 @@ public class ClickSettingsPane extends SettingsPane{ /** * The default pane width */ - public static double PREF_PANE_WIDTH=560; + public static double PREF_PANE_WIDTH=750; /** @@ -188,14 +182,14 @@ public class ClickSettingsPane extends SettingsPane{ public ClickSettingsPane(ClickControl clickControl){ super(null); - this.clickControl=clickControl; - mainPane= new PamBorderPane(); + this.clickControl=clickControl; clickValidator = new PamValidator(); - pamTabbedPane=new PamTabPane(); - pamTabbedPane.setAddTabButton(false); - pamTabbedPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); + pamTabbedPane=new NavigationDrawer(); +// pamTabbedPane.setAddTabButton(false); +// pamTabbedPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); +// pamTabbedPane.setStyle("-fx-background-color: blue;"); //create a combined detection and length pane PamVBox detectionPane=new PamVBox(); @@ -211,9 +205,18 @@ public class ClickSettingsPane extends SettingsPane{ detectionPane.getChildren().add(createTriggerGraph()); //add everything to tabs. - pamTabbedPane.getTabs().add(new Tab("Click Detection", detectionPane)); + Tab clickDetectionTabe =new Tab("Click Detection", detectionPane); + clickDetectionTabe.setGraphic(PamGlyphDude.createPamIcon("mdi2w-waveform")); + detectionPane.setPadding(new Insets(5,5,5,5)); + pamTabbedPane.getTabs().add(clickDetectionTabe); + + clickDelayPane=createDelayPane(); - pamTabbedPane.getTabs().add(tdoaTab=new Tab("TDOA and Echoes", clickDelayPane.getContentNode())); + ((Region) clickDelayPane.getContentNode()).setPadding(new Insets(5,5,5,5)); + tdoaTab=new Tab("TDOA and Echoes", clickDelayPane.getContentNode()); + tdoaTab.setGraphic(PamGlyphDude.createPamIcon("mdi2a-av-timer")); + pamTabbedPane.getTabs().add(tdoaTab); + tdoaTab.setOnSelectionChanged((event)->{ if (pamTabbedPane.getSelectionModel().getSelectedItem()==tdoaTab){ //System.out.println("clickDelayPane: "+clickDelayPane); @@ -225,11 +228,20 @@ public class ClickSettingsPane extends SettingsPane{ //pre filter pane. preFilter=new FilterPaneFX(Orientation.VERTICAL); - pamTabbedPane.getTabs().add(new Tab("Pre Filter", preFilter.getContentNode())); + ((Region) preFilter.getContentNode()).setPadding(new Insets(5,5,5,5)); + Tab preFilterTab =new Tab("Pre Filter", preFilter.getContentNode()); + preFilterTab.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chart-bell-curve-cumulative")); + pamTabbedPane.getTabs().add(preFilterTab); //trigger pane triggerFilter=new FilterPaneFX(Orientation.VERTICAL); - pamTabbedPane.getTabs().add(new Tab("Trigger Filter", triggerFilter.getContentNode())); + ((Region) triggerFilter.getContentNode()).setPadding(new Insets(5,5,5,5)); + Tab triggerFilterTab =new Tab("Trigger Filter", triggerFilter.getContentNode()); + triggerFilterTab.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chart-bell-curve")); + pamTabbedPane.getTabs().add(triggerFilterTab); + + + // //echo detection pane. // echoDetection= new ClickEchoPane(clickControl); @@ -238,15 +250,20 @@ public class ClickSettingsPane extends SettingsPane{ /***Note: FX does not implment click train detection in click detector****/ //classifiaction pane. - pamTabbedPane.getTabs().add(new Tab("Classification", clickClassificationPane=new ClickClassifyPaneFX(clickControl))); + Tab classifierTab =new Tab("Classification", clickClassificationPane=new ClickClassifyPaneFX(clickControl)); + classifierTab.setGraphic(PamGlyphDude.createPamIcon("mdi2g-graph")); + pamTabbedPane.getTabs().add(classifierTab); - //want a slightly bigger pane as a lot going on in this dialog. - //Note JavaFX 8u61 + has auto DPI scaling so this is really the size of a dialog on a standard HD monitor of - //reasonable size, rather than actual pixels - mainPane.setPrefSize(PREF_PANE_WIDTH, PREF_PANE_HEIGHT); +// //want a slightly bigger pane as a lot going on in this dialog. +// //Note JavaFX 8u61 + has auto DPI scaling so this is really the size of a dialog on a standard HD monitor of +// //reasonable size, rather than actual pixels + pamTabbedPane.setMinSize(PREF_PANE_WIDTH, PREF_PANE_HEIGHT); //addTabListeners(); - mainPane.setCenter(new PamBorderPane(pamTabbedPane)); + pamTabbedPane.setPadding(new Insets(0,0,0,0)); + + + } // private void addTabListeners(){ @@ -890,7 +907,7 @@ public class ClickSettingsPane extends SettingsPane{ @Override public Node getContentNode() { - return mainPane; + return pamTabbedPane; } @Override diff --git a/src/clickDetector/layoutFX/clickClassifiers/ClickTypeProperty.java b/src/clickDetector/layoutFX/clickClassifiers/ClickTypeProperty.java index 23b41615..83370efd 100644 --- a/src/clickDetector/layoutFX/clickClassifiers/ClickTypeProperty.java +++ b/src/clickDetector/layoutFX/clickClassifiers/ClickTypeProperty.java @@ -11,6 +11,7 @@ import javafx.beans.property.StringProperty; /** * A property wrapper for a basic click with JavaFX properties. This is used so JavaFX controls * automatically change. + * * @author Jamie Macaulay * */ @@ -23,11 +24,12 @@ public class ClickTypeProperty { public ClickTypeProperty(ClickTypeCommonParams clickType){ - this.commonClickParams=clickType; - name.setValue(clickType.getName()); - discardClassifier.setValue(clickType.getDiscard()); - code.setValue(clickType.getSpeciesCode()); - +// this.commonClickParams=clickType; +// name.setValue(clickType.getName()); +// discardClassifier.setValue(clickType.getDiscard()); +// code.setValue(clickType.getSpeciesCode()); + setClickType( clickType); + } public StringProperty name= new SimpleStringProperty("blank"); @@ -47,7 +49,7 @@ public class ClickTypeProperty { this.commonClickParams = clickType; //TODO-need to set properties; name.setValue(clickType.getName()); - enableClassifier.setValue(clickType.enable); + enableClassifier.setValue(clickType.getEnable()); discardClassifier.setValue(clickType.getDiscard()); code.setValue(clickType.getSpeciesCode()); } diff --git a/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierPaneFX.java b/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierPaneFX.java index ba4fbf5e..a300684c 100644 --- a/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierPaneFX.java +++ b/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierPaneFX.java @@ -52,8 +52,7 @@ public class SweepClassifierPaneFX extends BasicIdentifierPaneFX { getFlipPane().getAdvLabel().textProperty().unbind(); getFlipPane().getAdvLabel().textProperty().bind(sweepPane.getNameTextProperty()); - // removed DG 2023 12 14 since not everything was in this merge that was required Needs to be put back. -// getFlipPane().getPreAdvLabel().graphicProperty().bind(sweepPane.getNameGraphicProperty()); + getFlipPane().getPreAdvLabel().graphicProperty().bind(sweepPane.getNameGraphicProperty()); sweepPane.classifierItemRow = sweepClickClassifier.getSweepClassifierParams().getSetRow((SweepClassifierSet) clickTypeProperty.getClickType()); diff --git a/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierSetPaneFX.java b/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierSetPaneFX.java index a86f27f5..b464813a 100644 --- a/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierSetPaneFX.java +++ b/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierSetPaneFX.java @@ -4,9 +4,12 @@ package clickDetector.layoutFX.clickClassifiers; import fftFilter.FFTFilterParams; import fftManager.FFTLengthModeled; import javafx.beans.property.StringProperty; +import javafx.beans.value.ObservableValue; +import javafx.beans.property.SimpleObjectProperty; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Orientation; +import javafx.geometry.Point2D; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.CheckBox; @@ -19,9 +22,11 @@ import javafx.scene.control.TabPane; import javafx.scene.control.TabPane.TabClosingPolicy; import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; +import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import javafx.scene.text.Font; +import javafx.scene.canvas.Canvas; import net.synedra.validatorfx.GraphicDecoration; import net.synedra.validatorfx.ValidationMessage; import net.synedra.validatorfx.Validator; @@ -29,6 +34,7 @@ import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamGridPane; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamSpinner; +import pamViewFX.fxNodes.PamSymbolFX; import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; import pamViewFX.fxNodes.picker.SymbolPicker; @@ -39,19 +45,6 @@ import pamViewFX.fxNodes.utilsFX.PamUtilsFX; import pamViewFX.PamGuiManagerFX; import pamViewFX.fxGlyphs.PamGlyphDude; -import java.awt.BorderLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextField; - -import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.ArrayList; import java.util.List; @@ -60,7 +53,6 @@ import javax.swing.JPanel; import PamController.SettingsPane; import PamUtils.PamUtils; import PamView.PamSymbol; -import PamView.dialog.PamGridBagContraints; import PamView.symbol.SymbolData; import clickDetector.ClickControl; import clickDetector.ClickClassifiers.basicSweep.CodeHost; @@ -125,13 +117,17 @@ public class SweepClassifierSetPaneFX extends SettingsPane { */ private AmplitudeBlock amplitudeBlock; - /** - * Changes the bearing paramters fro clicks. + * Changes the bearing parameters for clicks. */ private BearingBlock bearingBox; + + /** + * Allows classification based on correlation scores. + */ + private XCorrBlock xCorrBox; - + private PamBorderPane mainPane = new PamBorderPane(); @@ -183,15 +179,17 @@ public class SweepClassifierSetPaneFX extends SettingsPane { spectrumHolder.setPadding(new Insets(10,0,0,0)); spectrumTab.setContent(spectrumHolder); - /*********Spectrum Tab****************/ + /*********Bearing Tab****************/ Tab bearingTab=new Tab("Bearing"); - spectrumTab.setGraphic(PamGlyphDude.createPamIcon("mdi2c-compass-outline", PamGuiManagerFX.iconSize)); + bearingTab.setGraphic(PamGlyphDude.createPamIcon("mdi2c-compass-outline", PamGuiManagerFX.iconSize)); + bearingTab.setStyle("-fx-font-size: 14px; -fx-font-weight: bold;"); PamVBox bearingHolder=new PamVBox(5); bearingBox=new BearingBlock(); - - bearingHolder.getChildren().addAll(bearingBox); + xCorrBox=new XCorrBlock(); + + bearingHolder.getChildren().addAll(bearingBox, xCorrBox); bearingHolder.setPadding(new Insets(10,0,0,0)); bearingTab.setContent(bearingHolder); @@ -200,7 +198,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { TabPane tabPane= new TabPane(); tabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); - tabPane.getTabs().addAll(waveformTab, spectrumTab); + tabPane.getTabs().addAll(waveformTab, spectrumTab, bearingTab); holder.getChildren().add(optionBox); holder.getChildren().add(tabPane); @@ -237,7 +235,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { private Font disableFont; private Label label; - + SweepBox(String borderTitle, Boolean enableButton) { @@ -279,6 +277,9 @@ public class SweepClassifierSetPaneFX extends SettingsPane { } this.setTop(hBox); + //little bit of space between the top and bottom button.m + BorderPane.setMargin(hBox, new Insets(0,0,5,0)); + /**Don't like this in old swing version*/ //tP.setCenter( description = new Label("", JLabel.CENTER)); @@ -356,12 +357,19 @@ public class SweepClassifierSetPaneFX extends SettingsPane { /** - * General options for the sweep classifier set + * General options for the sweep classifier set. This inlcudes, name, symbol, and some basics on + * what part of the click to analyse. + * * @author Jamie Macaulay * */ private class OptionsBox extends SweepBox implements FFTLengthModeled, CodeHost { + /** + * The size of the preview symbol to show next to the title + */ + private static final double TITLE_SYMBOL_SIZE = 20.; + /** * Text field to set classifier name. */ @@ -396,6 +404,13 @@ public class SweepClassifierSetPaneFX extends SettingsPane { * Shows lengths of extraction samples in millis. */ private Label lengthMS; + + /** + * The property for the PAMSymbol + */ + private SimpleObjectProperty pamSymbolProperty = new SimpleObjectProperty(); + + private PamSymbolFX currentSymbol = new PamSymbolFX(); private ComboBox lengthTypeBox; @@ -414,6 +429,8 @@ public class SweepClassifierSetPaneFX extends SettingsPane { //create the general options private Node createOptionsPane(){ + //set the canvas for the canvas property. + pamSymbolProperty.set(new Canvas(25,25)); PamGridPane pamGridPane=new PamGridPane(); pamGridPane.setHgap(5); @@ -453,6 +470,9 @@ public class SweepClassifierSetPaneFX extends SettingsPane { //create colour picker to allow users to change symbol colour. symbolPicker=new SymbolPicker(); + symbolPicker.setOnAction((action)->{ + drawSymbolProperty(); + }); pamGridPane.add(symbolPicker, 3,1); pamGridPane.add(new Label("Symbol"), 2,1); @@ -461,6 +481,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { lineColourPicker.setStyle("-fx-color-label-visible: false ;"); lineColourPicker.setOnAction((action)->{ symbolPicker.setLineColour(lineColourPicker.getValue()); + drawSymbolProperty(); }); pamGridPane.add(lineColourPicker, 4, 1); @@ -469,6 +490,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { fillColourPicker.setStyle("-fx-color-label-visible: false ;"); fillColourPicker.setOnAction((action)->{ symbolPicker.setFillColour(fillColourPicker.getValue()); + drawSymbolProperty(); }); pamGridPane.add(fillColourPicker, 5, 1); @@ -543,6 +565,21 @@ public class SweepClassifierSetPaneFX extends SettingsPane { return holder; } + private void drawSymbolProperty() { + + this.pamSymbolProperty.get().getGraphicsContext2D().clearRect(0, 0, TITLE_SYMBOL_SIZE, TITLE_SYMBOL_SIZE); + + currentSymbol.setSymbol(this.symbolPicker.getValue() == null ? null : this.symbolPicker.getValue().getSymbol()); + currentSymbol.setLineColor(this.lineColourPicker.getValue()); + currentSymbol.setFillColor(this.fillColourPicker.getValue()); + + if (currentSymbol.getSymbol()!=null) { + currentSymbol.draw(this.pamSymbolProperty.get().getGraphicsContext2D(), new Point2D(TITLE_SYMBOL_SIZE/2,TITLE_SYMBOL_SIZE/2), TITLE_SYMBOL_SIZE, TITLE_SYMBOL_SIZE); + } + + this.pamSymbolProperty.setValue(this.pamSymbolProperty.get()); + } + @Override public int getCode() { return codeSpinner.getValue(); @@ -575,11 +612,13 @@ public class SweepClassifierSetPaneFX extends SettingsPane { @Override protected void setParams() { + + //must have a symbol selected by default or a null symbol will be returned. + symbolPicker.getSelectionModel().select(0); //set basic data if (sweepClassifierSet == null) { //symbolViewer.setSymbol(null); - symbolPicker.getSelectionModel().select(0); nameField.setText(""); setCode(sweepClassifier.getNextFreeCode(0)); } @@ -619,6 +658,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { // //length stuff // clickLengthSpinner.getValueFactory().setValue(sweepClassifierSet.restrictedBins); + drawSymbolProperty(); } @@ -632,9 +672,9 @@ public class SweepClassifierSetPaneFX extends SettingsPane { //get the symbol data. SymbolData symbolData = new SymbolData(this.symbolPicker.getValue().getSymbol(), 10,10,true, - PamUtilsFX.fxToAWTColor(this.lineColourPicker.getValue()), PamUtilsFX.fxToAWTColor(this.fillColourPicker.getValue())); + PamUtilsFX.fxToAWTColor(this.fillColourPicker.getValue()), PamUtilsFX.fxToAWTColor(this.lineColourPicker.getValue())); sweepClassifierSet.symbol= new PamSymbol(symbolData); - if (sweepClassifierSet.getName().length() <= 0) { + if (sweepClassifierSet.getName()==null || sweepClassifierSet.getName().length() <= 0) { return showWarning("You must enter a name for this type of click"); } sweepClassifierSet.setSpeciesCode(getCode()); @@ -675,6 +715,11 @@ public class SweepClassifierSetPaneFX extends SettingsPane { return this.nameField; } + public ObservableValue getNameGraphicsProperty() { + return pamSymbolProperty; + } + + } @@ -695,9 +740,9 @@ public class SweepClassifierSetPaneFX extends SettingsPane { //create the general options private Node createOptionsPane(){ - PamGridPane pamGridPane=new PamGridPane(); - pamGridPane.setHgap(5); - pamGridPane.setVgap(5); +// PamGridPane pamGridPane=new PamGridPane(); +// pamGridPane.setHgap(5); +// pamGridPane.setVgap(5); simpleFilterPane=new SimpleFilterPaneFX(); @@ -831,7 +876,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { // PamGridPane.setFillWidth(clickLengthHolder2, false); - PamVBox vboxholder=new PamVBox(); + vboxholder=new PamVBox(); vboxholder.setSpacing(5); vboxholder.getChildren().addAll(clickLengthHolder1, clickLengthHolder2); @@ -921,7 +966,9 @@ public class SweepClassifierSetPaneFX extends SettingsPane { /** * Spinner for the first control band */ - private ArrayList> thresholdSpinners; + private ArrayList> thresholdSpinners; + + private PamGridPane gridPaneHolder; EnergyBandBox() { @@ -933,18 +980,18 @@ public class SweepClassifierSetPaneFX extends SettingsPane { //create the general options private Node createOptionsPane(){ - PamGridPane pamGridPane=new PamGridPane(); - pamGridPane.setHgap(5); - pamGridPane.setVgap(5); + gridPaneHolder=new PamGridPane(); + gridPaneHolder.setHgap(5); + gridPaneHolder.setVgap(5); Label freqLabel=new Label("Frequency (Hz)"); -//// gridPaneHolder.add(freqLabel, 0, 0); -//// gridPaneHolder.add(new Label("Threshold (dB)"), 2, 0); -// PamGridPane.setHalignment(freqLabel, HPos.CENTER); -// PamGridPane.setColumnSpan(gridPaneHolder, 2); -// -// //test band -// gridPaneHolder.add(new Label("Test Band"), 0, 1); + gridPaneHolder.add(freqLabel, 0, 0); + gridPaneHolder.add(new Label("Threshold (dB)"), 2, 0); + PamGridPane.setHalignment(freqLabel, HPos.CENTER); + PamGridPane.setColumnSpan(gridPaneHolder, 2); + + //test band + gridPaneHolder.add(new Label("Test Band"), 0, 1); testBandFreqPane=new FreqBandPane(Orientation.HORIZONTAL); testBandFreqPane.setBandText(""); @@ -954,7 +1001,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { - pamGridPane.add(testBandFreqPane, 1, 1); + gridPaneHolder.add(testBandFreqPane, 1, 1); contralBandFreqPanes = new ArrayList(); @@ -962,13 +1009,13 @@ public class SweepClassifierSetPaneFX extends SettingsPane { for (int i=0; i(0,100.,6.,0)); thresholdSpinners.get(i).setEditable(true); - pamGridPane.add(thresholdSpinners.get(i), 2, i+2); + gridPaneHolder.add(thresholdSpinners.get(i), 2, i+2); thresholdSpinners.get(i).getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); thresholdSpinners.get(i).setMaxWidth(100); @@ -989,7 +1036,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { } - return pamGridPane; + return gridPaneHolder; } @@ -1073,11 +1120,13 @@ public class SweepClassifierSetPaneFX extends SettingsPane { @Override protected void disbleControls(boolean disable) { - testBandFreqPane.setDisableFreqPane(disable); - for (int j = 0; j < SweepClassifierSet.nControlBands; j++) { - contralBandFreqPanes.get(j).setDisableFreqPane(disable); - thresholdSpinners.get(j).setDisable(disable); - } + this.gridPaneHolder.setDisable(disable); + +// testBandFreqPane.setDisableFreqPane(disable); +// for (int j = 0; j < SweepClassifierSet.nControlBands; j++) { +// contralBandFreqPanes.get(j).setDisableFreqPane(disable); +// thresholdSpinners.get(j).setDisable(disable); +// } } } @@ -1116,34 +1165,34 @@ public class SweepClassifierSetPaneFX extends SettingsPane { int gridy=0; - PamGridPane pamGridPane=new PamGridPane(); - pamGridPane.setHgap(5); - pamGridPane.setVgap(5); + gridPaneHolder=new PamGridPane(); + gridPaneHolder.setHgap(5); + gridPaneHolder.setVgap(5); //search and integration range - pamGridPane.add(new Label("Search Range"),1,gridy); + gridPaneHolder.add(new Label("Search Range"),1,gridy); searchRange=new FreqBandPane(Orientation.HORIZONTAL); searchRange.setBandText(""); - pamGridPane.add(searchRange,2,gridy); + gridPaneHolder.add(searchRange,2,gridy); GridPane.setColumnSpan(searchRange, GridPane.REMAINING); gridy++; - pamGridPane.add(new Label("Smooth"), 1,gridy); + gridPaneHolder.add(new Label("Smooth"), 1,gridy); smoothing=new PamSpinner(3,101,5,2); smoothing.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); smoothing.setMaxWidth(100); - pamGridPane.add(smoothing, 2,gridy); + gridPaneHolder.add(smoothing, 2,gridy); GridPane.setMargin(smoothing, new Insets(0,0,0,5)); //bit of a hack to make sure everything lines up nicely with the frequency pane. //GridPane.setHgrow(smoothing, Priority.NEVER); - pamGridPane.add(new Label("bins"), 3,gridy); + gridPaneHolder.add(new Label("bins"), 3,gridy); gridy++; @@ -1153,14 +1202,14 @@ public class SweepClassifierSetPaneFX extends SettingsPane { peakFreqPane.setDisableFreqPane(!peakFreqCheckBox.isSelected()); }); - pamGridPane.add(peakFreqCheckBox,0,gridy); + gridPaneHolder.add(peakFreqCheckBox,0,gridy); - pamGridPane.add(new Label("Peak Frequency"),1,gridy); + gridPaneHolder.add(new Label("Peak Frequency"),1,gridy); peakFreqPane=new FreqBandPane(Orientation.HORIZONTAL); //peakFreqPane.setHgap(0); peakFreqPane.setBandText(""); - pamGridPane.add(peakFreqPane,2,gridy); + gridPaneHolder.add(peakFreqPane,2,gridy); GridPane.setColumnSpan(peakFreqPane, GridPane.REMAINING); @@ -1177,26 +1226,28 @@ public class SweepClassifierSetPaneFX extends SettingsPane { }); - pamGridPane.add(peakWidthCheckBox,0,gridy); - pamGridPane.add(new Label("Peak Width"),1,gridy); + gridPaneHolder.add(peakWidthCheckBox,0,gridy); + gridPaneHolder.add(new Label("Peak Width"),1,gridy); peakWidthPane=new FreqBandPane(Orientation.HORIZONTAL); peakWidthPane.setBandText(""); - pamGridPane.add(peakWidthPane,2,gridy); + gridPaneHolder.add(peakWidthPane,2,gridy); GridPane.setColumnSpan(peakWidthPane, GridPane.REMAINING); addValidatorFreqCheck(getValidator(), peakWidthPane, "peak width ", "peak_width"); gridy++; - pamGridPane.add(new Label(""), 1,gridy); + + gridPaneHolder.add(new Label(""), 1,gridy); threshold=new PamSpinner(1., 300., 6.,1.); threshold.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); threshold.setPrefWidth(100); GridPane.setMargin(threshold, new Insets(0,0,0,5)); //bit of a hack to make sure everything lines up nicely with the frequency pane. - pamGridPane.add(threshold,2,gridy); - pamGridPane.add(new Label("dB"), 3,gridy); + gridPaneHolder.add(threshold,2,gridy); + + gridPaneHolder.add(new Label("dB"), 3,gridy); gridy++; @@ -1207,19 +1258,19 @@ public class SweepClassifierSetPaneFX extends SettingsPane { meanFreq.setDisableFreqPane(!meanFreqCheckBox.isSelected()); }); - pamGridPane.add(meanFreqCheckBox,0,gridy); + gridPaneHolder.add(meanFreqCheckBox,0,gridy); - pamGridPane.add(new Label("Mean Frequency"),1,gridy); + gridPaneHolder.add(new Label("Mean Frequency"),1,gridy); meanFreq=new FreqBandPane(Orientation.HORIZONTAL); meanFreq.setBandText(""); - pamGridPane.add(meanFreq,2,gridy); + gridPaneHolder.add(meanFreq,2,gridy); GridPane.setColumnSpan(meanFreq, GridPane.REMAINING); addValidatorFreqCheck(getValidator(), meanFreq, "mean freq. ", "mean_freq"); - return pamGridPane; + return gridPaneHolder; } @@ -1235,8 +1286,8 @@ public class SweepClassifierSetPaneFX extends SettingsPane { } @Override -// protected void disbleControls(boolean disable) { -// this.gridPaneHolder.setDisable(disable); + protected void disbleControls(boolean disable) { + this.gridPaneHolder.setDisable(disable); // peakFreqCheckBox.setDisable(enable); // peakWidthCheckBox.setDisable(enable); @@ -1251,20 +1302,6 @@ public class SweepClassifierSetPaneFX extends SettingsPane { // threshold.setDisable(enable); // searchRange.setDisable(enable); // meanFreq.setDisable(enable); - protected void disbleControls(boolean enable) { - peakFreqCheckBox.setDisable(enable); - peakWidthCheckBox.setDisable(enable); - meanFreqCheckBox.setDisable(enable); - - - /** - * Pane to set frequency band range */ - peakFreqPane.setDisable(enable); - smoothing.setDisable(enable); - peakWidthPane.setDisable(enable); - threshold.setDisable(enable); - searchRange.setDisable(enable); - meanFreq.setDisable(enable); } } @@ -1310,27 +1347,27 @@ public class SweepClassifierSetPaneFX extends SettingsPane { private Node createZeroCrossPane(){ - PamGridPane pamGridPane=new PamGridPane(); - pamGridPane.setHgap(5); - pamGridPane.setVgap(5); + gridPaneHolder=new PamGridPane(); + gridPaneHolder.setHgap(5); + gridPaneHolder.setVgap(5); //Number of zeros crossings Label zeroLabel; - pamGridPane.add(zeroLabel = new Label("Number of zero crossings"),0,0); + gridPaneHolder.add(zeroLabel = new Label("Number of zero crossings"),0,0); zeroCrossingsMin=new PamSpinner(0,999999,0,1); zeroCrossingsMin.setEditable(true); zeroCrossingsMin.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); zeroCrossingsMin.setPrefWidth(100); - pamGridPane.add(zeroCrossingsMin, 1,0); + gridPaneHolder.add(zeroCrossingsMin, 1,0); - pamGridPane.add(new Label("to"),2,0); + gridPaneHolder.add(new Label("to"),2,0); zeroCrossingsMax=new PamSpinner(0,999999,0,1); zeroCrossingsMax.setEditable(true); zeroCrossingsMax.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); zeroCrossingsMax.setPrefWidth(100); - pamGridPane.add(zeroCrossingsMax, 3,0); + gridPaneHolder.add(zeroCrossingsMax, 3,0); //this.zeroCrossingsMax=zeroCorssingsMax; @@ -1377,13 +1414,13 @@ public class SweepClassifierSetPaneFX extends SettingsPane { //zero crossing freuquency sweep - pamGridPane.add(new Label("Zero crossing frequency sweep"),0,1); + gridPaneHolder.add(new Label("Zero crossing frequency sweep"),0,1); freqZeroMin=new PamSpinner(0.,999999.,0.,1.); freqZeroMin.setEditable(true); freqZeroMin.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); freqZeroMin.setPrefWidth(100); - pamGridPane.add(freqZeroMin, 1,1); + gridPaneHolder.add(freqZeroMin, 1,1); @@ -1392,13 +1429,13 @@ public class SweepClassifierSetPaneFX extends SettingsPane { }); - pamGridPane.add(new Label("to"),2,1); + gridPaneHolder.add(new Label("to"),2,1); freqZeroMax=new PamSpinner(0.,999999.,0,1); freqZeroMax.setEditable(true); freqZeroMax.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); freqZeroMax.setPrefWidth(100); - pamGridPane.add(freqZeroMax, 3,1); + gridPaneHolder.add(freqZeroMax, 3,1); getValidator().createCheck() .dependsOn("minzerofreq", freqZeroMin.valueProperty()) @@ -1434,10 +1471,10 @@ public class SweepClassifierSetPaneFX extends SettingsPane { }); - pamGridPane.add(new Label("KHz/ms"),4,1); + gridPaneHolder.add(new Label("KHz/ms"),4,1); - return pamGridPane; + return gridPaneHolder; } @@ -1496,10 +1533,11 @@ public class SweepClassifierSetPaneFX extends SettingsPane { @Override protected void disbleControls(boolean disable) { - zeroCrossingsMin.setDisable(disable); - zeroCrossingsMax.setDisable(disable); - freqZeroMin.setDisable(disable); - freqZeroMax.setDisable(disable); + gridPaneHolder.setDisable(disable); +// zeroCrossingsMin.setDisable(disable); +// zeroCrossingsMax.setDisable(disable); +// freqZeroMin.setDisable(disable); +// freqZeroMax.setDisable(disable); } } @@ -1513,12 +1551,17 @@ public class SweepClassifierSetPaneFX extends SettingsPane { private TextField[] ampRange = new TextField[2]; + + /** + * The main holder + */ + private PamGridPane gridPane; public AmplitudeBlock() { super("Amplitude Range", true); setDescription("Set a minimum and maximum click amplitude for this type"); - PamGridPane gridPane= new PamGridPane(); + gridPane= new PamGridPane(); gridPane.setHgap(5); gridPane.setVgap(5); @@ -1572,13 +1615,16 @@ public class SweepClassifierSetPaneFX extends SettingsPane { @Override protected void disbleControls(boolean disable) { - for (int i = 0; i < 2; i++) { - ampRange[i].setDisable(!getEnableBox()); - } + gridPane.setDisable(disable); + +// for (int i = 0; i < 2; i++) { +// ampRange[i].setDisable(!getEnableBox()); +// } } } - + + /** * * Parameters for testing bearing values. @@ -1591,18 +1637,12 @@ public class SweepClassifierSetPaneFX extends SettingsPane { /** * The minimum correlation value. */ - private JTextField minBearing; + private PamSpinner minBearing; /** * The maximum bearing field */ - private JTextField maxBearing; - - /** - * The enable bearings check box. - */ - private JCheckBox enableBearings; - + private PamSpinner maxBearing; /** * True if using multi-channel data */ @@ -1611,55 +1651,100 @@ public class SweepClassifierSetPaneFX extends SettingsPane { /** * Combo box to select whetrher bearings should be kept or excluded within limits. */ - private JComboBox bearingsExcludeBox; + private ComboBox bearingsExcludeBox; + private PamHBox bearingHolder; + BearingBlock() { - super("Bearings", false); + super("Bearings", true); JPanel p = new JPanel(); - minBearing = new JTextField(5); - maxBearing = new JTextField(5); + minBearing = new PamSpinner(-180., 180., 0., 1.); + minBearing.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + minBearing.setMaxWidth(110); + minBearing.setEditable(true); + maxBearing = new PamSpinner(-180., 180., 0., 1.); + maxBearing.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + maxBearing.setMaxWidth(110); + maxBearing.setEditable(true); + + bearingsExcludeBox = new ComboBox(); + bearingsExcludeBox.getItems().add("Include only"); + bearingsExcludeBox.getItems().add("Exclude"); - bearingsExcludeBox = new JComboBox(); - bearingsExcludeBox.addItem("Include only"); - bearingsExcludeBox.addItem("Exclude"); + bearingHolder = new PamHBox(); + bearingHolder.setSpacing(5); + bearingHolder.setAlignment(Pos.CENTER_LEFT); p.setLayout(new GridBagLayout()); - GridBagConstraints c = new PamGridBagContraints(); -// c.gridx = 0; -// addComponent(p, enableBearings, c); -// c.gridx += c.gridwidth; -// addComponent(p, bearingsExcludeBox, c); -// c.gridx += c.gridwidth; -// addComponent(p, new JLabel("bearings between ", JLabel.RIGHT), c); -// c.gridx += c.gridwidth; -// addComponent(p, minBearing, c); -// c.gridx += c.gridwidth; -// addComponent(p, new JLabel(" and ", JLabel.RIGHT), c); -// c.gridx += c.gridwidth; -// addComponent(p, maxBearing, c); -// c.gridx += c.gridwidth; -// addComponent(p, new JLabel("(\u00B0)", JLabel.LEFT), c); -// -// add(BorderLayout.WEST, p); + + bearingHolder.getChildren().add(bearingsExcludeBox); + bearingHolder.getChildren().add(new Label("bearings between ")); + bearingHolder.getChildren().add(minBearing); + bearingHolder.getChildren().add(new Label("\u00B0 and ")); + bearingHolder.getChildren().add(maxBearing); + bearingHolder.getChildren().add(new Label("\u00B0")); + + + getValidator() .createCheck().dependsOn("maxbearing_", maxBearing.valueProperty()) + .withMethod(c -> { + Double maxBearingVal = c.get("maxbearing_"); + if (maxBearingVal<=minBearing.getValue() && isPaneShowing()) { + c.error("Maximum bearing must be greater than the minimum bearing"); + } + if (maxBearingVal<-180 || maxBearingVal>180) { + c.error("Bearing values must be between -180\u00B0 and 180\u00B0"); + } + }) + .decorates(maxBearing) + .immediate(); + + getValidator() .createCheck().dependsOn("minbearing_", minBearing.valueProperty()) + .withMethod(c -> { + Double minBearingVal = c.get("minbearing_"); + if (minBearingVal>maxBearing.getValue() && isPaneShowing()) { + c.error("Minimum bearing must be less than the maximum bearing"); + } + if (minBearingVal<-180 || minBearingVal>180) { + c.error("Bearing values must be between -180\u00B0 and 180\u00B0"); + } + }) + .decorates(minBearing) + .immediate(); + + + maxBearing.valueProperty().addListener((obsVal, oldVal, newVal)->{ + getValidator().validate(); + }); + + minBearing.valueProperty().addListener((obsVal, oldVal, newVal)->{ + getValidator().validate(); + }); this.multiChan = checkMultiChan(); + + this.getHolderPane().setCenter(bearingHolder); + + //setParams(); } @Override - protected void setParams() { + protected void setParams() { + + this.setEnableBox(sweepClassifierSet.enableBearingLims); + sweepClassifierSet.checkBearingAllocation(); // setEnableBox(sweepClassifierSet.enableZeroCrossings); - enableBearings.setSelected(sweepClassifierSet.enableBearingLims); + //enableBearings.setSelected(sweepClassifierSet.enableBearingLims); - if (sweepClassifierSet.excludeBearingLims) bearingsExcludeBox.setSelectedIndex(1); - else bearingsExcludeBox.setSelectedIndex(0); + if (sweepClassifierSet.excludeBearingLims) bearingsExcludeBox.getSelectionModel().select(1); + else bearingsExcludeBox.getSelectionModel().select(0); - this.minBearing.setText(String.format("%3.1f", Math.toDegrees(sweepClassifierSet.bearingLims[0]))); - this.maxBearing.setText(String.format("%3.1f", Math.toDegrees(sweepClassifierSet.bearingLims[1]))); + this.minBearing.getValueFactory().setValue(Math.toDegrees(sweepClassifierSet.bearingLims[0])); + this.maxBearing.getValueFactory().setValue(Math.toDegrees(sweepClassifierSet.bearingLims[1])); this.multiChan = checkMultiChan(); } @@ -1667,46 +1752,31 @@ public class SweepClassifierSetPaneFX extends SettingsPane { @Override protected boolean getParams() { - if (enableBearings.isSelected()) { - sweepClassifierSet.enableBearingLims = enableBearings.isSelected(); - sweepClassifierSet.excludeBearingLims = bearingsExcludeBox.getSelectedIndex()==1 ? true : false; + sweepClassifierSet.excludeBearingLims = bearingsExcludeBox.getSelectionModel().getSelectedIndex()==1 ? true : false; - try { - sweepClassifierSet.bearingLims[0] = Math.toRadians(Double.valueOf(minBearing.getText())); - } - catch (NumberFormatException e) { - return showWarning("Invalid minimum correlation value"); - } - - try { - sweepClassifierSet.bearingLims[1] = Math.toRadians(Double.valueOf(maxBearing.getText())); - } - catch (NumberFormatException e) { - return showWarning("Invalid maximum bearing limits value"); - } - } + sweepClassifierSet.bearingLims[0] = Math.toRadians(minBearing.getValue()); + + sweepClassifierSet.bearingLims[1] = Math.toRadians(maxBearing.getValue()); + return true; } - protected void enableControls() { - checkMultiChan(); - - enableBearings.setEnabled(multiChan); - bearingsExcludeBox.setEnabled(multiChan); - minBearing.setEnabled(multiChan); - maxBearing.setEnabled(multiChan); - - if (!multiChan) return; - - bearingsExcludeBox.setEnabled(enableBearings.isSelected()); - minBearing.setEnabled(enableBearings.isSelected()); - maxBearing.setEnabled(enableBearings.isSelected()); - } @Override protected void disbleControls(boolean disable) { - // TODO Auto-generated method stub + checkMultiChan(); + + bearingHolder.setDisable(!multiChan); +// bearingsExcludeBox.setDisable(!multiChan); +// minBearing.setDisable(!multiChan); +// maxBearing.setDisable(!multiChan); + if (!multiChan) return; + bearingHolder.setDisable(disable); + +// bearingsExcludeBox.setDisable(disable); +// minBearing.setDisable(disable); +// maxBearing.setDisable(disable); } } @@ -1719,25 +1789,150 @@ public class SweepClassifierSetPaneFX extends SettingsPane { private boolean checkMultiChan() { boolean multiChan = false; //do we have multi-channel clicks? -// if (clickControl!=null) { -// int[] chanGroups = clickControl.getClickParameters().getGroupedSourceParameters().getChannelGroups(); -// multiChan = false; -// -// if (chanGroups==null) return multiChan; -// -// for (int i=0; i1) { -// multiChan = true; -// break; -// } -// } -// } + if (this.sweepClassifier.getClickDetector().getClickControl()!=null) { + int[] chanGroups = getClickControl().getClickParameters().getGroupedSourceParameters().getChannelGroups(); + multiChan = false; + + if (chanGroups==null) return multiChan; + + for (int i=0; i1) { + multiChan = true; + break; + } + } + } + else multiChan = true; // Debug.out.println("Check multi-channel: " + multiChan); return multiChan; } + + /** + * + * Parameters for testing the cross correlation value. + * + * @author Jamie Macaulay + * + */ + private class XCorrBlock extends SweepBox { + + /** + * The minimum correlation value. + */ + private PamSpinner minCorrelation; + + private PamSpinner minPeakTorugh; + + private PamToggleSwitch minXCorrEnable; + + private PamToggleSwitch minPeakTroughEnable; + + private PamSpinner minPeakFactor; + + /** + * True if using multi-channel data + */ + boolean multiChan = false; + + private GridPane gridPane; + + + XCorrBlock() { + super("Cross Correlation", true); + + minXCorrEnable = new PamToggleSwitch(""); + minXCorrEnable.selectedProperty().addListener((obsVal, oldVal, newVal)->{ + disbleControls(!newVal); + }); + + minCorrelation = new PamSpinner(-Double.MAX_VALUE, Double.MAX_VALUE, 0, 0.1); + minCorrelation.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + + minPeakTroughEnable = new PamToggleSwitch(""); + minPeakTroughEnable.selectedProperty().addListener((obsVal, oldVal, newVal)->{ + disbleControls(!newVal); + }); + + minPeakFactor = new PamSpinner(-Double.MAX_VALUE, Double.MAX_VALUE, 1., 0.1); + minPeakFactor.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + + int x = 0; + int y = 0; + + gridPane = new GridPane(); + gridPane.setHgap(5); + gridPane.setVgap(5); + + gridPane.add(minXCorrEnable, x, y); + gridPane.add(new Label("Min. xcorr value "), ++x, y); + gridPane.add(minCorrelation, ++x, y); + + y++; + x=0; + gridPane.add(minPeakTroughEnable, x, y); + gridPane.add(new Label("Max. xcorr value greater than "), ++x, y); + gridPane.add(minPeakFactor,++x, y); + gridPane.add(new Label("* absolute min. value"),++x, y); + + this.getHolderPane().setCenter(gridPane); + + this.multiChan = checkMultiChan(); + } + + @Override + protected void setParams() { + + this.setEnableBox(sweepClassifierSet.enableMinXCrossCorr); + + sweepClassifierSet.checkXCCorrAllocation(); +// setEnableBox(sweepClassifierSet.enableZeroCrossings); + minXCorrEnable.setSelected(sweepClassifierSet.enableMinXCrossCorr); + minPeakTroughEnable.setSelected(sweepClassifierSet.enablePeakXCorr); + + this.minCorrelation.getValueFactory().setValue(sweepClassifierSet.minCorr); + this.minPeakFactor.getValueFactory().setValue(sweepClassifierSet.corrFactor); + + this.multiChan = checkMultiChan(); + } + + @Override + protected boolean getParams() { + sweepClassifierSet.enableMinXCrossCorr = minXCorrEnable.isSelected(); + sweepClassifierSet.enablePeakXCorr = minPeakTroughEnable.isSelected(); + + if (minXCorrEnable.isSelected()) { + sweepClassifierSet.minCorr = minCorrelation.getValue(); + } + + if (minPeakTroughEnable.isSelected()) { + sweepClassifierSet.corrFactor = minPeakFactor.getValue(); + } + return true; + } + + @Override + protected void disbleControls(boolean disable) { + this.multiChan = checkMultiChan(); + + minXCorrEnable.setDisable(!multiChan); + minCorrelation.setDisable(!multiChan); + minPeakTroughEnable.setDisable(!multiChan); + minPeakFactor.setDisable(!multiChan); + + if (!multiChan) return; + + gridPane.setDisable(disable); + + minCorrelation.setDisable(!minXCorrEnable.isSelected()); + minPeakFactor.setDisable(!minPeakTroughEnable.isSelected()); + } + + + } + /**~*main set and get params functions***/ @@ -1753,6 +1948,9 @@ public class SweepClassifierSetPaneFX extends SettingsPane { freqBox.getParams(); zeroCrossingsBox.getParams(); amplitudeBlock.getParams(); + bearingBox.getParams(); + xCorrBox.getParams(); + currentClickProperty.setClickType(sweepClassifierSet); @@ -1772,6 +1970,8 @@ public class SweepClassifierSetPaneFX extends SettingsPane { freqBox.setParams(); zeroCrossingsBox.setParams(); amplitudeBlock.setParams(); + bearingBox.setParams(); + xCorrBox.setParams(); } @@ -1797,6 +1997,12 @@ public class SweepClassifierSetPaneFX extends SettingsPane { } + public ObservableValue getNameGraphicProperty() { // TODO Auto-generated method stub + return optionBox.getNameGraphicsProperty(); + + } + + /** @@ -1859,6 +2065,12 @@ public class SweepClassifierSetPaneFX extends SettingsPane { } + /** + * Show a warning. + * @param string - the + * @param string2 + * @return false usually + */ private boolean showWarning(String string, String string2) { //PamController.getInstance(); PamDialogFX.showWarning(null, string, string2); @@ -1896,4 +2108,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { private boolean isPaneShowing() { return sweepClassifier.getClassifierPane().getFlipPane().isBackVisible(); } -} \ No newline at end of file + + + +} diff --git a/src/clickDetector/localisation/GeneralGroupLocaliser.java b/src/clickDetector/localisation/GeneralGroupLocaliser.java index 7a6af5cd..ed592672 100644 --- a/src/clickDetector/localisation/GeneralGroupLocaliser.java +++ b/src/clickDetector/localisation/GeneralGroupLocaliser.java @@ -86,7 +86,7 @@ abstract public class GeneralGroupLocaliser implements LocaliserModel getSettingsPane() { + public LocaliserPane getAlgorithmSettingsPane() { // no settings pane for this. Settings come from click control params //and so are never called here. return null; diff --git a/src/clickDetector/offlineFuncs/ClickWaveTask.java b/src/clickDetector/offlineFuncs/ClickWaveTask.java index 49f4368a..a5bfb24c 100644 --- a/src/clickDetector/offlineFuncs/ClickWaveTask.java +++ b/src/clickDetector/offlineFuncs/ClickWaveTask.java @@ -131,8 +131,11 @@ public class ClickWaveTask extends OfflineTask { // setParentDataBlock(clickControl.getClickDataBlock()); addAffectedDataBlock(clickControl.getClickDataBlock()); - rawDataSource = (PamRawDataBlock) clickDetector.getRawSourceDataBlock(clickDetector.getSampleRate()); - daqStatusDataBlock = ((AcquisitionProcess) rawDataSource.getParentProcess()).getDaqStatusDataBlock(); + rawDataSource = (PamRawDataBlock) clickDetector.getRawSourceDataBlock(); + + if (rawDataSource!=null) { + daqStatusDataBlock = ((AcquisitionProcess) rawDataSource.getParentProcess()).getDaqStatusDataBlock(); + } //TODO- Should not hard wire these but this is not going to be used very often. filterParams = new FilterParams(FilterType.BUTTERWORTH, FilterBand.HIGHPASS, 250000, 5000, 4); @@ -155,6 +158,12 @@ public class ClickWaveTask extends OfflineTask { public void prepareTask() { Debug.out.println("Load data units from: daqStatusDataBlock: " + daqStatusDataBlock.getUnitsCount()); + if (daqStatusDataBlock==null) { + if (rawDataSource!=null) { + daqStatusDataBlock = ((AcquisitionProcess) rawDataSource.getParentProcess()).getDaqStatusDataBlock(); + } + } + filterMethod = FilterMethod.createFilterMethod(clickDetector.getSampleRate(), filterParams); daqStatusDataBlock.loadViewerData(new OfflineDataLoadInfo(clickControl.getClickDataBlock().getFirstUnit().getTimeMilliseconds()-DEFAULTDAQUNITLOAD, diff --git a/src/clickDetector/offlineFuncs/ClicksOffline.java b/src/clickDetector/offlineFuncs/ClicksOffline.java index 6b72da91..4eff23ae 100644 --- a/src/clickDetector/offlineFuncs/ClicksOffline.java +++ b/src/clickDetector/offlineFuncs/ClicksOffline.java @@ -587,10 +587,10 @@ public class ClicksOffline { offlineTaskGroup.addTask(new EchoDetectionTask(clickControl)); offlineTaskGroup.addTask(new ClickDelayTask(clickControl)); offlineTaskGroup.addTask(new ClickBearingTask(clickControl)); - if (JamieDev.isEnabled()) { - //re import waveform data from raw wave files. - offlineTaskGroup.addTask(new ClickWaveTask(clickControl)); - } +// if (JamieDev.isEnabled()) { +// //re import waveform data from raw wave files. +// offlineTaskGroup.addTask(new ClickWaveTask(clickControl)); +// } /* * Add the click detector tasks first since these will need to operate diff --git a/src/clickDetector/tdPlots/ClickEventSymbolChooser.java b/src/clickDetector/tdPlots/ClickEventSymbolChooser.java index 3da72c23..8d9c5074 100644 --- a/src/clickDetector/tdPlots/ClickEventSymbolChooser.java +++ b/src/clickDetector/tdPlots/ClickEventSymbolChooser.java @@ -1,17 +1,11 @@ package clickDetector.tdPlots; -import java.awt.Color; - import PamView.GeneralProjector; -import PamView.PamColors; import PamView.symbol.StandardSymbolChooser; import PamView.symbol.StandardSymbolManager; import PamView.symbol.StandardSymbolOptions; import PamView.symbol.SymbolData; import PamguardMVC.PamDataBlock; -import PamguardMVC.PamDataUnit; -import clickDetector.ClickTrainDetection; -import clickDetector.offlineFuncs.OfflineEventDataUnit; public class ClickEventSymbolChooser extends StandardSymbolChooser { diff --git a/src/clickDetector/tdPlots/ClickEventSymbolManager.java b/src/clickDetector/tdPlots/ClickEventSymbolManager.java index 89ce1d02..0c068486 100644 --- a/src/clickDetector/tdPlots/ClickEventSymbolManager.java +++ b/src/clickDetector/tdPlots/ClickEventSymbolManager.java @@ -1,12 +1,6 @@ package clickDetector.tdPlots; -import PamView.GeneralProjector; -import PamView.symbol.StandardSymbolChooser; -import PamView.symbol.StandardSymbolManager; import PamView.symbol.SymbolData; -import PamView.symbol.modifier.SuperDetSymbolModifier; -import PamguardMVC.PamDataBlock; -import PamguardMVC.superdet.SuperDetDataBlock; import PamguardMVC.superdet.swing.SuperDetectionSymbolManager; import clickDetector.offlineFuncs.OfflineEventDataBlock; diff --git a/src/clickTrainDetector/CTDetectionGroupDataUnit.java b/src/clickTrainDetector/CTDetectionGroupDataUnit.java index e7464363..f91df1a4 100644 --- a/src/clickTrainDetector/CTDetectionGroupDataUnit.java +++ b/src/clickTrainDetector/CTDetectionGroupDataUnit.java @@ -6,9 +6,7 @@ import java.util.ListIterator; import PamDetection.LocContents; import PamDetection.PamDetection; -import PamguardMVC.AcousticDataUnit; import PamguardMVC.PamDataUnit; -import PamguardMVC.dataSelector.DataSelector; import clickTrainDetector.dataselector.CTSelectParams; import detectiongrouplocaliser.DetectionGroupDataUnit; diff --git a/src/clickTrainDetector/clickTrainAlgorithms/mht/test/ExampleClickTrains.java b/src/clickTrainDetector/clickTrainAlgorithms/mht/test/ExampleClickTrains.java index ade0870a..b8036bd4 100644 --- a/src/clickTrainDetector/clickTrainAlgorithms/mht/test/ExampleClickTrains.java +++ b/src/clickTrainDetector/clickTrainAlgorithms/mht/test/ExampleClickTrains.java @@ -4,12 +4,11 @@ import java.io.File; import java.util.ArrayList; import java.util.Collections; -import com.jmatio.io.MatFileReader; -import com.jmatio.types.MLCell; -import com.jmatio.types.MLDouble; -import com.jmatio.types.MLInt32; - +import PamUtils.PamArrayUtils; import clickTrainDetector.clickTrainAlgorithms.mht.mhtMAT.SimpleClick; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.format.Mat5File; +import us.hebi.matlab.mat.types.Cell; /** * Simulates some clicks to test the MHT algorithm on. @@ -127,7 +126,6 @@ public class ExampleClickTrains { public SimpleClickDataBlock importSimClicks(SimClickImport type) { File file = getSimClicksFile(type); return importSimClicks(file); - } /** @@ -135,25 +133,29 @@ public class ExampleClickTrains { * @param file - the file. */ private ArrayList importSimpleClickMAT(File file) { - MatFileReader mfr = null; + Mat5File mfr = null; try { if (file ==null) { System.out.println("The imported file is null"); return null; } - - mfr = new MatFileReader(file); + + + mfr = Mat5.readFromFile(file); + + + mfr.getCell("clicksMHT"); //get array of a name "my_array" from file - MLCell mlArrayRetrived = (MLCell) mfr.getMLArray( "clicksMHT" ); - MLDouble mlSampleRate = (MLDouble) mfr.getMLArray( "sR" ); + Cell mlArrayRetrived = mfr.getCell("clicksMHT"); + Double mlSampleRate = mfr.getMatrix( "sR" ).getDouble(0); ArrayList simpleClicks= new ArrayList(); //now the cell array can be anywhere from 2- 6 columns depending on the data contained. - int nColumns = mlArrayRetrived.getN(); - int nClks = mlArrayRetrived.getM(); + int nColumns = mlArrayRetrived.getNumRows(); + int nClks = mlArrayRetrived.getNumCols(); SimpleClick simpleClick; @@ -163,25 +165,27 @@ public class ExampleClickTrains { Double bearing = null; double[][] waveform =null; - float sR = mlSampleRate.get(0).floatValue(); + float sR =(float) mlSampleRate.doubleValue(); for (int i=0; i2) amplitude = ((MLDouble) mlArrayRetrived.get(i,2)).get(0); + if (nColumns>2) amplitude = mlArrayRetrived.getCell(i,2).get(0); //fourth column is bearing in degrees (double) - if (nColumns<3) bearing = ((MLDouble) mlArrayRetrived.get(i,3)).get(0); + if (nColumns<3) bearing = mlArrayRetrived.getCell(i,3).get(0); //fifth column is a waveform array (double[][]) - if (nColumns>4) waveform = ((MLDouble) mlArrayRetrived.get(i,4)).getArray(); - + if (nColumns>4) waveform = PamArrayUtils.matrix2array(mlArrayRetrived.getCell(i,4).getMatrix(0)); //tranpose the waveform waveform = PamUtils.PamArrayUtils.transposeMatrix(waveform); diff --git a/src/clickTrainDetector/dataselector/CTDataSelector.java b/src/clickTrainDetector/dataselector/CTDataSelector.java index 8fd9e4d7..a011ac00 100644 --- a/src/clickTrainDetector/dataselector/CTDataSelector.java +++ b/src/clickTrainDetector/dataselector/CTDataSelector.java @@ -1,7 +1,5 @@ package clickTrainDetector.dataselector; -import java.util.Arrays; - import PamDetection.LocContents; import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelectParams; diff --git a/src/clickTrainDetector/logging/ClickTrainDetLogging.java b/src/clickTrainDetector/logging/ClickTrainDetLogging.java index 4370dad9..6918442a 100644 --- a/src/clickTrainDetector/logging/ClickTrainDetLogging.java +++ b/src/clickTrainDetector/logging/ClickTrainDetLogging.java @@ -30,7 +30,6 @@ import pamMaths.PamInterp; */ public class ClickTrainDetLogging extends SuperDetLogging { - /** * The delimiter used for the average spectrum in the database. */ @@ -46,7 +45,6 @@ public class ClickTrainDetLogging extends SuperDetLogging { */ private final static int DEFAULT_SPECTRUM_LEN = 256; - /** * Reference to the click train data block. */ @@ -58,7 +56,7 @@ public class ClickTrainDetLogging extends SuperDetLogging { private ClickTrainControl clickTrainControl; /** - * The pam table items for saving. + * The PAM table items for saving. */ private PamTableItem endTime, dataCount, chi2, medianIDI, meanIDI, stdIDI, algorithmInfo, avrg_Spectrum, speciesFlag, avrg_Spectrum_max; diff --git a/src/clickTrainDetector/logging/ClickTrainDetSubLogging.java b/src/clickTrainDetector/logging/ClickTrainDetSubLogging.java index 6126b973..67866f25 100644 --- a/src/clickTrainDetector/logging/ClickTrainDetSubLogging.java +++ b/src/clickTrainDetector/logging/ClickTrainDetSubLogging.java @@ -9,7 +9,7 @@ import generalDatabase.SuperDetLogging; /** * Logs the the children of the click train detection. - * @author macst + * @author Jamie Macaulay * */ public class ClickTrainDetSubLogging extends SQLLogging { diff --git a/src/cpod/CPODBinaryStore.java b/src/cpod/CPODBinaryStore.java index 6c3453f2..9cff8285 100644 --- a/src/cpod/CPODBinaryStore.java +++ b/src/cpod/CPODBinaryStore.java @@ -9,18 +9,20 @@ import java.io.IOException; import PamguardMVC.DataUnitBaseData; import PamguardMVC.PamDataUnit; +import PamguardMVC.RawDataTransforms; import binaryFileStorage.BinaryDataSource; import binaryFileStorage.BinaryHeader; import binaryFileStorage.BinaryObjectData; import binaryFileStorage.ModuleFooter; import binaryFileStorage.ModuleHeader; +import cpod.CPODClick.CPODWaveTransforms; /** * Binary storage for CPOD data. *

* Seems a little silly to re create a new file format for CPOD data but by storing * as binary files we can take advantage of the PAMGuard datagram, UID manager, super detections, a - * annotations etc. without creating a very hackey module. + * annotations etc. without creating a very hacky module. * * @author Jamie Macauly * @@ -28,11 +30,14 @@ import binaryFileStorage.ModuleHeader; public class CPODBinaryStore extends BinaryDataSource { + + /** *

Module version changes

* Version 2: Moved start sample, channel bitmap, duration to DataUnitBaseData general data structure
+ * Version 3: Implemented FPOD data which includes waveforms
*/ - private static final int currentVersion = 2; + private static final int currentVersion = 3; /** * Reference to the CPOD control. @@ -84,8 +89,6 @@ public class CPODBinaryStore extends BinaryDataSource { CPODClick cd = (CPODClick) pamDataUnit; //System.out.println("DLDetecitonBinarySource: packed: " + pamDataUnit.getBasicData().getMeasuredAmplitudeType()); - - // make a byte array output stream and write the data to that, // then dump that down to the main storage stream if (dos == null || bos == null) { @@ -96,7 +99,7 @@ public class CPODBinaryStore extends BinaryDataSource { } try { - writeCPODData(dos, cd); + writeCPODdata3(dos, cd); } catch (IOException e) { e.printStackTrace(); @@ -106,13 +109,49 @@ public class CPODBinaryStore extends BinaryDataSource { return new BinaryObjectData(1, bos.toByteArray()); } + + + private void writeCPODdata3(DataOutputStream dos2, CPODClick cd) throws IOException { + +// data[3]=nCyc; +// data[4]=bw; +// data[5]=kHz; +// data[6]=endF; +// data[7]=spl; +// data[8]=slope; + + dos.writeShort(cd.getnCyc()); + dos.writeShort(cd.getBw()); + dos.writeShort(cd.getkHz()); + dos.writeShort(cd.getEndF()); + dos.writeShort(cd.getSpl()); + dos.writeShort(cd.getSlope()); + + boolean hasWav = cd.getWaveData()!=null; + dos.writeBoolean(cd.getWaveData()!=null); + + if (hasWav) { + //the number of samples - note FPODs only have one sample + dos.writeShort(cd.getWaveData()[0].length); + + //convert back to shorts using scale factor + for (int i=0; i implements AcousticDataUnit { +/** + * CPOD or FPOD click. + */ +public class CPODClick extends PamDataUnit implements RawDataHolder { + private double[][] wavData; + private short nCyc; private short bw; private short kHz; @@ -18,11 +27,19 @@ public class CPODClick extends PamDataUnit implement private long iciSamples; private short[] rawData; + /** * The amplitude in dB. */ private Double amplitudedB; + /** + * The raw data transforms for the CPOD click + */ + private RawDataTransforms rawDataTransforms = null; + + private CPODClassification cpodClassification; + /** * Create a CPOD click. (This is used to load CPOD clicks from the binary store) * @@ -41,6 +58,7 @@ public class CPODClick extends PamDataUnit implement short slope = shortData[8]; CPODClick cpodClick = new CPODClick(baseData.getTimeMilliseconds(), baseData.getStartSample(), nCyc, bw, kHz, endF, spl, slope, shortData); +// cpodClick.setDurationInMilliseconds(baseData.getMillisecondDuration()); return cpodClick; } @@ -52,11 +70,13 @@ public class CPODClick extends PamDataUnit implement * @param shortData * @return */ + @Deprecated public static CPODClick makeClick(CPODControl cpodControl, long minuteMillis, short[] shortData) { int t = shortData[0]<<16 | shortData[1]<<8 | shortData[2]; // 5 microsec intervals ! + long tMillis = minuteMillis + t/200; // now a bit of time stretching. Need to get the start time and see how // different this is, then it's a linear stretch. @@ -72,44 +92,80 @@ public class CPODClick extends PamDataUnit implement short endF = shortData[6]; short spl = shortData[7]; short slope = shortData[8]; + CPODClick cpodClick = new CPODClick(tMillis, fileSamples, nCyc, bw, kHz, endF, spl, slope, shortData); + +// //estimate the duration in millis - not accurate but gives an idea. +// double duration = (nCyc/(double) kHz); +// cpodClick.setDurationInMilliseconds(duration); + return cpodClick; } /** - * Create a CPOD click. This is usually called whenever the CPOD click is imported from a CSV file. - * - * @param minuteMillis The time in milliseconds of the minute block preceding the - * current click. - * @param shortData - * @return + * Make a FPOD click. This called whenever click has been imported from a FP1 or FP3 file + * @param tMillis - the time in milliseconds datenum. + * @param fileSamples - the number of samples into the file the CPOD click is at - this is calculated from CPODClickDataBlock.CPOD_SR + * @param shortData - the raw data from the CPOD click. This can be 8 bytes or 30 bytes if a click train clcik + * @return a CPODClick object. */ - public static CPODClick makeClick(CPODControl2 cpodControl, long minuteMillis, short[] shortData) { + public static CPODClick makeFPODClick(long tMillis, long fileSamples, FPODdata fpodData) { + + + CPODClick cpodClick = new CPODClick(tMillis, fileSamples, (short) fpodData.Ncyc, (short) fpodData.BW, + (short) FPODReader.IPItoKhz(fpodData.IPIatMax), (short) FPODReader.IPItoKhz(fpodData.EndIPI), + (short) fpodData.MaxPkExtnd, (short) 0, null); + + //durartion is measured more accurately in FPOD data + cpodClick.setDurationInMilliseconds((fpodData.duration*5.)/1000.); + cpodClick.setSampleDuration((long) ((fpodData.duration*5./1e6)*CPODClickDataBlock.CPOD_SR)); + + //some FPOD clicks have raw wave data - some do not. + if (fpodData.getWavData()!=null) { + + int[] waveData = FPODReader.makeResampledWaveform(fpodData); + + //now need to scale the data so it fits as a raw data holder. + double[] waveDataD = FPODReader.scaleWavData(waveData); + + cpodClick.wavData = new double[1][];//create a 2D array + cpodClick.wavData[0]=waveDataD; + + cpodClick.setRawDataTransfroms(new CPODWaveTransforms(cpodClick)); + } + + return cpodClick; + } + - int t = shortData[0]<<16 | - shortData[1]<<8 | - shortData[2]; // 5 microsec intervals ! - long tMillis = minuteMillis + t/200; - // now a bit of time stretching. Need to get the start time and see how - // different this is, then it's a linear stretch. - tMillis = cpodControl.stretchClicktime(tMillis); + /** + * Make a CPOD click. This called whenever click has been imported from a CP1 or CP3 file + * @param tMillis - the time in milliseconds datenum. + * @param fileSamples - the number of samples into the file the CPOD click is at - this is calculated from CPODClickDataBlock.CPOD_SR + * @param shortData - the raw data from the CPOD click. This can be 8 bytes or 40 bytes if a click train clcik + * @return a CPODClick object. + */ + public static CPODClick makeCPODClick(long tMillis, long fileSamples, short[] shortData) { - - // do a sample number within the file as 5us intervals - long fileSamples = t + minuteMillis * 200; -// int micros = t%200; short nCyc = shortData[3]; - short bw = shortData[4]; + short bw = shortData[4]; //bandwidth is an arbitary scale between 0 and 31; + bw = (short) ((255./31.) * (bw+1)); //make some attempt to convert to kHz short kHz = shortData[5]; short endF = shortData[6]; short spl = shortData[7]; short slope = shortData[8]; CPODClick cpodClick = new CPODClick(tMillis, fileSamples, nCyc, bw, kHz, endF, spl, slope, shortData); + +// //estimate the duration in millis - not accurate but gives an idea. + double duration = (nCyc/(double) kHz); + cpodClick.setDurationInMilliseconds(duration); + + return cpodClick; } /** - * Constructor for a CPOD click. + * Constructor for a CPOD click. This adds all basic information that is required for a CPOD or FPOD click * @param tMillis - the time in millis. * @param fileSamples - the file samples * @param nCyc - the number of cycles @@ -130,15 +186,19 @@ public class CPODClick extends PamDataUnit implement this.spl = spl; this.slope = slope; double[] f = new double[2]; - f[0] = Math.min(kHz, endF)*1000; - f[1] = Math.max(kHz, endF)*1000; + + f[0] = (kHz - bw/2.)*1000.; + f[1] = (kHz + bw/2.)*1000.; + setFrequency(f); - //estimate the duration in millis - not accurate but gives an idea. - double duration = (nCyc/(double) kHz); - this.setDurationInMilliseconds(duration); - - this.rawData = shortData.clone(); +// double duration = (nCyc/(double) kHz); +// setDurationInMilliseconds(duration); + + if (shortData!=null) { + //only CPOD + this.rawData = shortData.clone(); + } } /** @@ -252,6 +312,7 @@ public class CPODClick extends PamDataUnit implement } return amplitudedB; } + /* (non-Javadoc) * @see PamDetection.AcousticDataUnit#getSummaryString() @@ -267,6 +328,7 @@ public class CPODClick extends PamDataUnit implement } long tm = getTimeMilliseconds(); str += PamCalendar.formatDate(tm) + " " + PamCalendar.formatTime(tm, 3) + "

"; + str += String.format("UID: %dkHz

", this.getUID()); str += String.format("Start Freq: %dkHz

", getkHz()); str += String.format("N Cycles: %d

", getnCyc()); str += String.format("BandWidth: %dkHz

", getBw()); @@ -274,12 +336,12 @@ public class CPODClick extends PamDataUnit implement str += String.format("Slope: %d

", getSlope()); str += String.format("SPL: %d", getSpl()); if (rawData.length == 40) { - str += String.format("

QClass %d, SpClass %d", getBits(rawData[19], (short) 0x3), - getBits(rawData[19], (short) 0b11100)); + str += String.format("

QClass %d, SpClass %d", CPODUtils.getBits(rawData[19], (short) 0x3), + CPODUtils.getBits(rawData[19], (short) 0b11100)); str += String.format("

Train %d, %d click", rawData[20], rawData[23]); str += String.format("

Qn %d, RateGood %d, SpGood %d, SpClass %d", - getBits(rawData[36], (short)3), getBits(rawData[36], (short) 4), - getBits(rawData[36], (short)8), getBits(rawData[36], (short) 240)); + CPODUtils.getBits(rawData[36], (short)3), CPODUtils.getBits(rawData[36], (short) 4), + CPODUtils.getBits(rawData[36], (short)8), CPODUtils.getBits(rawData[36], (short) 240)); } if (rawData != null) { int nRaw = rawData.length; @@ -291,19 +353,83 @@ public class CPODClick extends PamDataUnit implement } } } + CPODClickTrainDataUnit clicktrain = getCPODClickTrain(); + if (clicktrain!=null) { + str += "

" + clicktrain.getStringInfo() + "

"; + } + else { + str += "

" + String.format("No click train info

"); + } + // str += "<\html>"; return str; } - short getBits(short data, short bitMap) { - short firstBit = 0; - for (int i = 0; i < 8; i++) { - if ((bitMap & (1<>firstBit); + + @Override + public double[][] getWaveData() { + return this.wavData; } + + @Override + public RawDataTransforms getDataTransforms() { + return rawDataTransforms; + } + + public void setRawDataTransfroms(RawDataTransforms rawDataTransforms) { + this.rawDataTransforms = rawDataTransforms; + + } + + public void setWavData(double[][] ds) { + this.wavData=ds; + } + + static class CPODWaveTransforms extends RawDataTransforms { + + CPODClick rawDataHolder; + + public CPODWaveTransforms(PamDataUnit rawDataHolder) { + super(rawDataHolder); + this.rawDataHolder = (CPODClick) rawDataHolder; + + } + + @Override + public float getSampleRate() { + return FPODReader.FPOD_WAV_SAMPLERATE; + } + + @Override + public int getCurrentSpectrumLength() { + //note that the waveform has a higher sample rate than the CPODclicks. + return PamUtils.getMinFftLength(rawDataHolder.getWaveData()[0].length); + } + + } + + /** + * Get the click train detection from the click detection + * @return the click train detection + */ + public CPODClickTrainDataUnit getCPODClickTrain() { + for (int i=0; i { super(CPODClick.class, dataName, parentProcess, 1); this.clikcType = clikcType; + if (parentProcess!=null) { this.setDataSelectCreator(new CPODDataSelectorCreator(parentProcess.getPamControlledUnit(), this)); + } } - /* (non-Javadoc) - * @see PamguardMVC.PamDataBlock#getDataName() - */ - @Override - public String getDataName() { - PamControlledUnit cpodControl = getParentProcess().getPamControlledUnit(); - if (cpodControl == null) { - return super.getDataName(); - } - return cpodControl.getUnitName() + "_" + getDataTypeString(); - } +// /* (non-Javadoc) +// * @see PamguardMVC.PamDataBlock#getDataName() +// */ +// @Override +// public String getDataName() { +// if (getParentProcess()!=null) { +// PamControlledUnit cpodControl = getParentProcess().getPamControlledUnit(); +// if (cpodControl == null) { +// return super.getDataName(); +// } +// return cpodControl.getUnitName() + "_" + getDataTypeString(); +// } +// return "CPOD_temporary"; +// +// } - private String getDataTypeString() { - switch (clikcType) { - case CPODMap.FILE_CP1: - return "CP1"; - case CPODMap.FILE_CP3: - return "CP3"; - } - return null; - } - @Override public float getSampleRate() { diff --git a/src/cpod/CPODClickTrainDataBlock.java b/src/cpod/CPODClickTrainDataBlock.java new file mode 100644 index 00000000..dc989911 --- /dev/null +++ b/src/cpod/CPODClickTrainDataBlock.java @@ -0,0 +1,141 @@ +package cpod; + +import PamUtils.PamCalendar; +import PamguardMVC.PamDataUnit; +import PamguardMVC.PamProcess; +import PamguardMVC.dataOffline.OfflineDataLoadInfo; +import PamguardMVC.debug.Debug; +import PamguardMVC.superdet.SuperDetDataBlock; +import clickTrainDetector.CTDataUnit; +import pamScrollSystem.ViewLoadObserver; + +/** + * + * The click train detector data block. Holds all detected click trains in + * ClickTrainDataUnits which themselves contain a list of detected data units + * within a train. + * + * @author Jamie Macaulay + * + */ +public class CPODClickTrainDataBlock extends SuperDetDataBlock { + + /** + * Convenience reference to the click train control + */ + private CPODControl2 cpodControl; + +// /** +// * The data selector for click trains. +// */ +// private CTDataSelectCreator clickDataSelectCreator; + + + public CPODClickTrainDataBlock(CPODControl2 cpodControl, PamProcess parentProcess, String name, int channelMap) { + super(CTDataUnit.class, name, parentProcess, channelMap, SuperDetDataBlock.ViewerLoadPolicy.LOAD_OVERLAPTIME); + this.cpodControl = cpodControl; + } + + +// public ClickTrainDataBlock(ClickTrainControl clickTrainControl, Class class1, String name, PamProcess parentProcess, int channelMap) { +// super(CTDataUnit.class, name, parentProcess, channelMap, SuperDetDataBlock.LOAD_VIEWERDATA); +// this.clickTrainControl = clickTrainControl; +// } + + + @Override + public boolean saveViewerData() { + return super.saveViewerData(); + } + + @Override + public boolean loadViewerData(OfflineDataLoadInfo offlineDataLoadInfo, ViewLoadObserver loadObserver) { + Debug.out.println("Loading click train viewer data: startMillis: " + PamCalendar.formatDateTime(offlineDataLoadInfo.getStartMillis()) + + " endMillis: " + PamCalendar.formatDateTime(offlineDataLoadInfo.getEndMillis())); + return super.loadViewerData(offlineDataLoadInfo, loadObserver); + } + + + /* (non-Javadoc) + * @see PamguardMVC.PamDataBlock#addPamData(PamguardMVC.PamDataUnit) + */ + @Override + public void addPamData(CPODClickTrainDataUnit pamDataUnit) { + super.addPamData(pamDataUnit); + //this.sortData(); + } + + /* (non-Javadoc) + * @see PamguardMVC.PamDataBlock#addPamData(PamguardMVC.PamDataUnit, java.lang.Long) + */ + @Override + public void addPamData(CPODClickTrainDataUnit pamDataUnit, Long uid) { + super.addPamData(pamDataUnit, uid); + //this.sortData(); + } + + /* (non-Javadoc) + * @see PamguardMVC.PamDataBlock#updatePamData(PamguardMVC.PamDataUnit, long) + */ + @Override + public void updatePamData(CPODClickTrainDataUnit pamDataUnit, long updateTimeMillis) { + super.updatePamData(pamDataUnit, updateTimeMillis); + //this.sortData(); + } + + @Override + public float getSampleRate() { + return super.getSampleRate(); + } + + + @Override + public boolean needViewerDataLoad(OfflineDataLoadInfo offlineDataLoadInfo) { + // always reload these data to make sure everything gets linked up correctly. +// 22/10/2019. Changed a bunch of viewer load stuff including have the click train detector +// load up with a longer scroll than click detector. Have disabled because it now really messes up +// the click train detector. +// Debug.out.println("-- CLICK TRAIN DATABLOCK: Need viewer load: " + super.needViewerDataLoad(offlineDataLoadInfo)); + return super.needViewerDataLoad(offlineDataLoadInfo); + } + +// @Override +// public void clearAll() { +// Debug.out.println("-- CLICK TRAIN DATABLOCK: clear 1: "); +// +// ListIterator iterator = this.getListIterator(0); +// int count = 0; +// T dataUnit; +// while (iterator.hasNext()) { +// dataUnit= iterator.next(); +// dataUnit.removeAllSubDetections(); +// dataUnit.clearSubdetectionsRemoved(); +// if (count%100==0) { +// Debug.out.println("-- CLICK TRAIN DATABLOCK: clear: " + count+++ " " + dataUnit); +// } +// } +// super.clearAll(); +// +// Debug.out.println("-- CLICK TRAIN DATABLOCK: clear 2: "); +// } + + + + + public CPODControl2 getClickTrainControl() { + return cpodControl; + } + +// /* (non-Javadoc) +// * @see PamguardMVC.PamDataBlock#getDataSelectCreator() +// */ +// @Override +// public synchronized DataSelectorCreator getDataSelectCreator() { +// if (clickDataSelectCreator == null) { +// clickDataSelectCreator = new CTDataSelectCreator(clickTrainControl, this); +// } +// return clickDataSelectCreator; +// +// } + +} \ No newline at end of file diff --git a/src/cpod/CPODClickTrainDataUnit.java b/src/cpod/CPODClickTrainDataUnit.java new file mode 100644 index 00000000..2883e660 --- /dev/null +++ b/src/cpod/CPODClickTrainDataUnit.java @@ -0,0 +1,49 @@ +package cpod; + +import java.util.List; +import PamDetection.PamDetection; +import PamguardMVC.PamDataUnit; +import cpod.CPODClassification.CPODSpeciesType; +import detectiongrouplocaliser.DetectionGroupDataUnit; + +/** + * Base class for a click train data unit. + * Note this must implrement PamDetection to work with the clip generator. + * + * @author Jamie Macaulay + * + */ +public class CPODClickTrainDataUnit extends DetectionGroupDataUnit implements PamDetection { + + CPODClassification cpodClassification; + + + public CPODClickTrainDataUnit(long timeMilliseconds, List list, CPODClassification cpodClassification) { + super(timeMilliseconds, null); + this.cpodClassification=cpodClassification; + } + + public CPODSpeciesType getSpecies() { + return cpodClassification.species; + } + + public int getConfidence() { + return cpodClassification.qualitylevel; + } + + + public boolean isEcho() { + return cpodClassification.isEcho; + } + + /** + * Gte information on the click train + * @return + */ + public String getStringInfo() { + + + return String.format("Species: %s Confidence %d is echo? %b", getSpecies(), getConfidence(), isEcho()); + } + +} \ No newline at end of file diff --git a/src/cpod/CPODControl.java b/src/cpod/CPODControl.java index c095a825..58eec388 100644 --- a/src/cpod/CPODControl.java +++ b/src/cpod/CPODControl.java @@ -30,7 +30,8 @@ import PamguardMVC.dataOffline.OfflineDataLoadInfo; * @author Doug Gillespie * @author Jamie Macaulay * - */ + */ +@Deprecated public class CPODControl extends OfflineFileControl implements PamSettings { private CPODClickDataBlock cp1DataBlock, cp3DataBlock; @@ -91,7 +92,7 @@ public class CPODControl extends OfflineFileControl implements PamSettings { * @return milliseconds. */ public static long podTimeToMillis(long podTime) { - return podTime * 60L * 1000L - (25569L*3600L*24000L); + return CPODUtils.podTimeToMillis(podTime); } public long stretchClicktime(long rawTime) { diff --git a/src/cpod/CPODControl2.java b/src/cpod/CPODControl2.java index a8e0d92b..fb350c1d 100644 --- a/src/cpod/CPODControl2.java +++ b/src/cpod/CPODControl2.java @@ -3,6 +3,7 @@ package cpod; import java.awt.Frame; import java.io.File; import java.io.Serializable; +import java.util.List; import javax.swing.JMenuItem; @@ -16,16 +17,22 @@ import PamController.SettingsPane; import PamView.PamDetectionOverlayGraphics; import PamView.PamSymbol; import PamView.WrapperControlledGUISwing; +import cpod.dataPlotFX.CPODDPlotProvider; import cpod.dataPlotFX.CPODPlotProviderFX; import cpod.fx.CPODGUIFX; import cpod.fx.CPODSettingsPane; +import cpod.logging.CPODClickTrainLogging; +import cpod.logging.CPODSubDetLogging; import dataPlotsFX.data.TDDataProviderRegisterFX; +import detectionPlotFX.data.DDPlotRegister; import fileOfflineData.OfflineFileParams; +import javafx.concurrent.Task; +import pamScrollSystem.AbstractScrollManager; import pamViewFX.fxNodes.pamDialogFX.PamDialogFX2AWT; /** - * CPOD control. Loads and manages CPOD (and hopefully soon FPOD) data into - * PAMGaurd. + * CPOD control. Loads and manages CPOD and FPOD data into + *PAMGaurd. *

* Note that this module (CPODControl) originally used a folder of CP1 files as it's file store but * this version now converts CP1/CP3 etc. into binary files instead in order to integrate into PAMGuard's @@ -36,9 +43,10 @@ import pamViewFX.fxNodes.pamDialogFX.PamDialogFX2AWT; */ public class CPODControl2 extends PamControlledUnit implements PamSettings { - private static final float CPOD_SAMPLE_RATE = 200000; - - private CPODClickDataBlock cp1DataBlock, cp3DataBlock; + /** + * The CPOD detection datablock + */ + private CPODClickDataBlock cp1DataBlock; /** * The CPOD paramters. @@ -78,19 +86,23 @@ public class CPODControl2 extends PamControlledUnit implements PamSettings { * The JavaFX settings pane for the cpod */ private CPODSettingsPane settingsPane; - + /** * CPOD importer. */ private CPODImporter cpodImporter; + private CPODClickTrainDataBlock clickTrainDataBlock; + + private CPODClickTrainLogging clickTrainDetLogging; + public CPODControl2(String unitName) { super("CPOD", unitName); addPamProcess(cpodProcess = new CPODProcess(this)); - cpodProcess.addOutputDataBlock(cp1DataBlock = new CPODClickDataBlock("CP1 Data", + cpodProcess.addOutputDataBlock(cp1DataBlock = new CPODClickDataBlock("CPOD Detections", cpodProcess, CPODMap.FILE_CP1)); cp1DataBlock.setDatagramProvider(cpodDataGramProvider[0] = new CPODDataGramProvider(this)); cp1DataBlock.setPamSymbolManager(new CPODSymbolManager(this, cp1DataBlock)); @@ -98,29 +110,44 @@ public class CPODControl2 extends PamControlledUnit implements PamSettings { //must set overlay draw so that hover text can be extracted from general projector and thus plotted on TDGraphFX . This is a HACK and should be sorted. cp1DataBlock.setOverlayDraw(new PamDetectionOverlayGraphics(cp1DataBlock, new PamSymbol())); - // add the CP3 data block - cpodProcess.addOutputDataBlock(cp3DataBlock = new CPODClickDataBlock("CP3 Data", - cpodProcess, CPODMap.FILE_CP3)); - cp3DataBlock.setPamSymbolManager(new CPODSymbolManager(this, cp3DataBlock)); - cp3DataBlock.setDatagramProvider(cpodDataGramProvider[1] = new CPODDataGramProvider(this)); - cp3DataBlock.setBinaryDataSource(new CPODBinaryStore(this, cp1DataBlock)); - //must set overlay draw so that hover text can be extracted from general projector and thus plotted on TDGraphFX . This is a HACK and should be sorted. - cp3DataBlock.setOverlayDraw(new PamDetectionOverlayGraphics(cp3DataBlock, new PamSymbol())); + + + // // add the CP3 data block + // cpodProcess.addOutputDataBlock(cp3DataBlock = new CPODClickDataBlock("CP3 Data", + // cpodProcess, CPODMap.FILE_CP3)); + // + // cp3DataBlock.setPamSymbolManager(new CPODSymbolManager(this, cp3DataBlock)); + // cp3DataBlock.setDatagramProvider(cpodDataGramProvider[1] = new CPODDataGramProvider(this)); + // cp3DataBlock.setBinaryDataSource(new CPODBinaryStore(this, cp1DataBlock)); + // //must set overlay draw so that hover text can be extracted from general projector and thus plotted on TDGraphFX . This is a HACK and should be sorted. + // cp3DataBlock.setOverlayDraw(new PamDetectionOverlayGraphics(cp3DataBlock, new PamSymbol())); + + + clickTrainDataBlock= new CPODClickTrainDataBlock(this, cpodProcess, "CPOD Click Trains", 0); + clickTrainDataBlock.SetLogging(clickTrainDetLogging = new CPODClickTrainLogging(this, clickTrainDataBlock)); + clickTrainDetLogging.setSubLogging(new CPODSubDetLogging(clickTrainDetLogging, clickTrainDataBlock)); + //create symbols for clicks trains + clickTrainDataBlock.setPamSymbolManager(new CPODTrainSymbolManager(clickTrainDataBlock)); + + //makes sure the click trains are loaded + int maxndays = 5; //maximum days to load. + AbstractScrollManager.getScrollManager().addToSpecialDatablock(clickTrainDataBlock, maxndays*24*60*60*1000L , maxndays*24*60*60*1000L); + + cpodProcess.addOutputDataBlock(clickTrainDataBlock); PamSettingManager.getInstance().registerSettings(this); - cpodProcess.setSampleRate(CPOD_SAMPLE_RATE, false); + cpodProcess.setSampleRate(CPODClickDataBlock.CPOD_SR, false); cpodImporter = new CPODImporter(this); - - + //FX display data providers CPODPlotProviderFX cpodPlotProviderFX = new CPODPlotProviderFX(this, cp1DataBlock); TDDataProviderRegisterFX.getInstance().registerDataInfo(cpodPlotProviderFX); - - cpodPlotProviderFX = new CPODPlotProviderFX(this, cp3DataBlock); - TDDataProviderRegisterFX.getInstance().registerDataInfo(cpodPlotProviderFX); - + // register the DD display + DDPlotRegister.getInstance().registerDataInfo(new CPODDPlotProvider(this, cp1DataBlock)); + // DDPlotRegister.getInstance().registerDataInfo(new CPODDPlotProvider(this, cp3DataBlock)); + // //swing time display data providers. // CPODPlotProvider cpodPlotProvider = new CPODPlotProvider(this, cp1DataBlock); @@ -138,26 +165,6 @@ public class CPODControl2 extends PamControlledUnit implements PamSettings { } - protected static File getCP3File(File cp1File) { - String newName = cp1File.getAbsolutePath(); - newName = newName.substring(0, newName.length()-4) + ".CP3"; - File cp3File = new File(newName); - return cp3File; - } - - - - - /** - * Convert POD time to JAVA millis - POD time is - * integer minutes past the same epoc as Windows uses - * i.e. 0th January 1900. - * @param podTime - * @return milliseconds. - */ - public static long podTimeToMillis(long podTime) { - return podTime * 60L * 1000L - (25569L*3600L*24000L); - } public long stretchClicktime(long rawTime) { if (cp1DataBlock.getDatagrammedMap() == null) { @@ -191,8 +198,16 @@ public class CPODControl2 extends PamControlledUnit implements PamSettings { return cp1DataBlock; } - public CPODClickDataBlock getCP3DataBlock() { - return cp3DataBlock; + // public CPODClickDataBlock getCP3DataBlock() { + // return cp3DataBlock; + // } + + /** + * Get the CPOD click train data block. + * @return the CPOD click train data block. + */ + public CPODClickTrainDataBlock getClickTrainDataBlock() { + return this.clickTrainDataBlock; } /** @@ -213,9 +228,6 @@ public class CPODControl2 extends PamControlledUnit implements PamSettings { /**** GUI ****/ - - - /** * Get the settings pane. * @@ -302,4 +314,15 @@ public class CPODControl2 extends PamControlledUnit implements PamSettings { return cpodImporter; } + /** + * Import POD data. This will either be a list of CPOD or FPOD files. + * @param files - the files to import. + * @return a list of import Tasks. + */ + public Task importPODData(List files) { + return cpodImporter.importCPODData(files); + } + + + } diff --git a/src/cpod/CPODDataGramProvider.java b/src/cpod/CPODDataGramProvider.java index 8c3dc2ce..6e2395b2 100644 --- a/src/cpod/CPODDataGramProvider.java +++ b/src/cpod/CPODDataGramProvider.java @@ -24,7 +24,17 @@ public class CPODDataGramProvider implements DatagramProvider { @Override public int addDatagramData(PamDataUnit dataUnit, float[] dataGramLine) { CPODClick cpodClick = (CPODClick) dataUnit; - dataGramLine[cpodClick.getkHz()] ++; + + int minKhz = cpodClick.getkHz()- cpodClick.getBw()/2; + int maxkHz = cpodClick.getkHz()- cpodClick.getBw()/2; + //each datagram line is a 1kHz bin + for (int i=0; i=minKhz && i<=maxkHz) { + dataGramLine[i] ++; + } + } + +// dataGramLine[cpodClick.getkHz()] ++; return 1; } diff --git a/src/cpod/CPODDialog.java b/src/cpod/CPODDialog.java index abe27518..99d845d7 100644 --- a/src/cpod/CPODDialog.java +++ b/src/cpod/CPODDialog.java @@ -15,6 +15,7 @@ import PamUtils.SelectFolder; import PamView.dialog.PamDialog; import PamView.dialog.PamGridBagContraints; +@Deprecated public class CPODDialog extends PamDialog { private CPODControl cpodControl; diff --git a/src/cpod/CPODFile.java b/src/cpod/CPODFile.java new file mode 100644 index 00000000..6d739771 --- /dev/null +++ b/src/cpod/CPODFile.java @@ -0,0 +1,45 @@ +package cpod; + +import java.io.File; + +import cpod.CPODUtils.CPODFileType; + +/** + * A CP1 along with it's CP3 file + * @author Jamie Macaulay + * + */ +public class CPODFile { + + /** + * A CP1 or FP1 file + */ + public File cp1File = null; + + /** + * A CP3 or CP3 file + */ + public File cp3File = null; + + /** + * Get the name of the file + * @return the name of the file; + */ + public String getName() { + String fileName = ""; + if (cp1File!=null) fileName+=cp1File.getName() + " "; + if (cp3File!=null) fileName+=cp3File.getName() + " "; + return fileName; + } + + /** + * Check whether the current CPOD file is a CPOD or FPOD + * @return true if FPOD. + */ + public boolean isFPOD() { + if (cp1File !=null) return CPODUtils.getFileType(cp1File).equals(CPODFileType.FP1); + if (cp3File !=null) return CPODUtils.getFileType(cp3File).equals(CPODFileType.FP3); + return false; + } + +} diff --git a/src/cpod/CPODImporter.java b/src/cpod/CPODImporter.java index a9294a3b..0fe65221 100644 --- a/src/cpod/CPODImporter.java +++ b/src/cpod/CPODImporter.java @@ -1,14 +1,14 @@ package cpod; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.concurrent.ExecutorService; @@ -24,10 +24,15 @@ import binaryFileStorage.BinaryFooter; import binaryFileStorage.BinaryObjectData; import binaryFileStorage.BinaryOutputStream; import binaryFileStorage.BinaryStore; +import cpod.CPODReader.CPODHeader; +import cpod.CPODUtils.CPODFileType; +import cpod.FPODReader.FPODHeader; +import cpod.FPODReader.FPODdata; +import javafx.application.Platform; import javafx.concurrent.Task; /** - * Imports data CPOD data and converts into binary files. + * Imports FPOD and CPOD data and converts into binary files. * * @author Jamie Macaulay * @@ -36,32 +41,10 @@ public class CPODImporter { public static final int MAX_SAVE = 2000000; - - private File cpFile; - private long fileStart; - - private long fileEnd; - private short podId; - private byte waterDepth; - private CPODControl2 cpodControl; - float[] tempDataGramData; - - // /** - // * Flag for a CP1 file. - // */ - // public static final int FILE_CP1 = 1; - // - // /** - // * Flag for a CP3 file. - // */ - // public static final int FILE_CP3 = 3; - - - CPODFileType cpFileType = CPODFileType.CP1; /** - * Hnadles the queue for importing files tasks + * Handles the queue for importing files tasks */ private ExecutorService exec = Executors.newSingleThreadExecutor(r -> { Thread t = new Thread(r); @@ -69,312 +52,53 @@ public class CPODImporter { return t ; }); - /** - * CPOD file types - * @author Jamie Macaulay - * - */ - public enum CPODFileType { - CP1("CP1"), - CP3("CP3"); - // FP1("fp1"), - // FP3("fp3"); - - private String text; - - CPODFileType(String text) { - this.text = text; - } - - public String getText() { - return this.text; - } - - public static CPODFileType fromString(String text) { - for (CPODFileType b : CPODFileType.values()) { - if (b.text.equalsIgnoreCase(text)) { - return b; - } - } - return null; - } - } - public CPODImporter(CPODControl2 cpodControl) { this.cpodControl = cpodControl; - // if (fileType != cpFileType) { - // System.err.println("CPOD Mismatched file type " + cpFile.getAbsolutePath()); - // } } - public static CPODFileType getFileType(File cpFile) { - for (int i=0; ifrom && nClicks<(from+maxNum))) { - - //System.out.println("Create a new CPOD click: "); - CPODClick cpodClick = processClick(nMinutes, shortData); - - dataBlock.addPamData(cpodClick); - - } - - // // now remove the data unit from the data block in order to clear up memory. Note that the remove method - // // saves the data unit to the Deleted-Items list, so clear that as well (otherwise we'll just be using - // // up all the memory with that one) - // dataBlock.remove(cpodClick); - // dataBlock.clearDeletedList(); - } - else { - nMinutes ++; - processMinute(byteData); - } - totalBytes += dataSize; - } - bis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - System.out.println(String.format("File read: Clicks %d, minutes %d", nClicks, nMinutes)); - - return nClicks; + return cpodClick; } - /** - * A new minute. Don;t think we need to do anything here.? - * @param byteData - */ - private void processMinute(byte[] byteData) { - // TODO Auto-generated method stub - - } - - private CPODClick processClick(int nMinutes, short[] shortData) { - /* - * - */ - return CPODClick.makeClick(cpodControl, fileStart + nMinutes * 60000L, shortData); - } - - /** - * Java will only have read signed bytes. Nick clearly - * uses a lot of unsigned data, so convert and inflate to int16. - * @param signedByte - * @return unsigned version as int16. - */ - static short toUnsigned(byte signedByte) { - short ans = signedByte; - if (ans < 0) { - ans += 256; - } - return ans; - } - - /** - * Is it the end of the file ? - * @param byteData - * @return true if all bytes == 255 - */ - static boolean isFileEnd(byte[] byteData) { - for (int i = 0; i < byteData.length; i++) { - // if ((byteData[i] ^ 0xFF) != 0) { - // return false; - // } - if (byteData[i] != -1) { - return false; - } - } - return true; - } - - boolean readHeader(BufferedInputStream bis) { - int bytesRead; - byte[] headData = new byte[getHeadSize(cpFileType)]; - try { - bytesRead = bis.read(headData); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - if (bytesRead != headData.length) { - return false; - } - // read as a load of 4 byte integers and see what we get ! - DataInputStream dis = new DataInputStream(new ByteArrayInputStream(headData)); - int nShort = headData.length / 2; - short[] shortData = new short[nShort]; - for (int i = 0; i < shortData.length; i++) { - try { - shortData[i] = dis.readShort(); - if (shortData[i] == 414) { -// System.out.println("Found id at %d" + i); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - dis = new DataInputStream(new ByteArrayInputStream(headData)); - int nFloat = headData.length / 4; - float[] floatData = new float[nFloat]; - for (int i = 0; i < floatData.length; i++) { - try { - floatData[i] = dis.readFloat(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - dis = new DataInputStream(new ByteArrayInputStream(headData)); - int nInt = headData.length / 4; - int[] intData = new int[nInt]; - for (int i = 0; i < nInt; i++) { - try { - intData[i] = dis.readInt(); - int bOff = i*4; - int sOff = i*2; -// if (intData[i] > 0) -// System.out.println(String.format("%d, Int = %d, Float = %3.5f, Short = %d,%d, bytes = %d,%d,%d,%d", i, intData[i], -// floatData[i], -// shortData[sOff], shortData[sOff+1], -// headData[bOff], headData[bOff+1], headData[bOff+2], headData[bOff+3])); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - fileStart = CPODControl.podTimeToMillis(intData[64]); - fileEnd = CPODControl.podTimeToMillis(intData[65]); - // other times seem to be packed in ints 66 - 69. - podId = shortData[50]; - waterDepth = headData[8]; - - return true; - } - - /** - * @return the fileStart - */ - public long getFileStart() { - return fileStart; - } - - /** - * Get the data - * @param type - * @return - */ - public CPODClickDataBlock getDataBlock(CPODFileType type) { - switch (type) { - case CP1: - return this.cpodControl.getCP1DataBlock(); - case CP3: - return this.cpodControl.getCP3DataBlock(); - } - return null; - } /** * Run the import task. */ - public void runImportTask(ArrayList files, CPODClickDataBlock clickDataBlock) { - Task cpodTask = new CPODImportTask(files, clickDataBlock); + public void runImportTask(ArrayList files, CPODClickDataBlock clickDataBlock, CPODClickTrainDataBlock clickTrainDataBlock) { + Task cpodTask = new CPODImportTask(files, clickDataBlock, clickTrainDataBlock); Thread th = new Thread(cpodTask); @@ -383,61 +107,102 @@ public class CPODImporter { th.start(); } + /** * Import the CPOD data from a certain file type. * @param files to import - a list of CPOD compatible files (can be a mix but only the files corresponding to type will be processed) * @param type - the type flag of the file e.g. CPODFileType.CP1 * @return the CPOD import task. */ - public Task importCPODDataTask(List files, CPODFileType type) { + public Task importCPODDataTask(List files) { - List cpXFIles = new ArrayList(); - - for (int i=0; i> importCPODData(List files) { - - List> tasks = new ArrayList>(); - - for (int i=0; i cp1Task = importCPODDataTask(files, CPODFileType.values()[i]); - tasks.add(cp1Task); + public Task importCPODData(List files) { + // Need to create a list of cp1 and cp3 files. + ArrayList cpodFiles = new ArrayList(); + + CPODFile cpodFile; + while (files.size()>0) { + cpodFile = new CPODFile(); + + CPODFileType type = CPODUtils.getFileType(files.get(0)); + + File currentFile = files.get(0); + switch (type) { + case CP1: + case FP1: + cpodFile.cp1File= files.get(0); + break; + case CP3: + case FP3: + cpodFile.cp3File= files.get(0); + break; + default: + break; + + } + files.remove(0); + + //now search for the corresponding CP3 or CP1 file + for (int i=0; i{ - PamController.getInstance().updateDataMap(); - }); - - //TODO what if a task is cancelled... - return tasks; + else return false; } + /** * Run the tasks - * @param tasks - the tasks. + * @param task - the tasks. */ - public void runTasks(List> tasks) { - - for (int i=0; i task) { + this.exec.execute(task); } @@ -446,14 +211,13 @@ public class CPODImporter { * @author Jamie Macaulay * */ - class CPODImportTask extends Task { - + class CPODImportTask extends Task { /** * List of files, either CP1 or CP3 */ - private List cpxFile; + private List cpxFile; /** * Reference to the binary store. @@ -468,27 +232,37 @@ public class CPODImporter { /** * The binary stream */ - private BinaryOutputStream binaryStream; + private BinaryOutputStream binaryStream; + + /** + * Reference to the CPOD click train datablock. + */ + private CPODClickTrainDataBlock clickTrainDataBlock; + + private ArrayList cpodTrainList; /** * * @param cpxfiles - a list of CP1 or CP3 files. * @param cpodDataBlock - the CPOD data block. */ - public CPODImportTask(List cpxfiles, CPODClickDataBlock cpodDataBlock) { + public CPODImportTask(List cpxfiles, CPODClickDataBlock cpodDataBlock, CPODClickTrainDataBlock clickTrainDataBlock) { this.cpxFile = cpxfiles; this.cpodDataBlock=cpodDataBlock; + this.clickTrainDataBlock=clickTrainDataBlock; } + @Override protected Integer call() throws Exception { try { + BinaryDataSource binarySource = cpodDataBlock.getBinaryDataSource(); binaryStore = (BinaryStore) PamController.getInstance().findControlledUnit(BinaryStore.defUnitType); if (binaryStore == null) { String msg = "Error: Can't convert CPOD files unless you have a Binary Storage module.
" + "Please close this dialog and add/configure a binary store first."; - int ans = WarnOnce.showWarning(null, "CPOD Import", msg, WarnOnce.OK_OPTION); + WarnOnce.showWarning(null, "CPOD Import", msg, WarnOnce.OK_OPTION); System.out.println("Can't convert CPOD files unless you have a binary storage module"); return null; } @@ -497,26 +271,54 @@ public class CPODImporter { binarySource.setBinaryStorageStream(outputStream); binaryStream = cpodDataBlock.getBinaryDataSource().getBinaryStorageStream(); - for (int i=0; i iterator = cpodDataBlock.getListIterator(0); + this.updateProgress(-1, 1); + + System.out.println("Number of CPOD data units in the data block: " + cpodDataBlock.getUnitsCount() + " progress: " + (i+1) + " " + cpxFile.size() ); + + //need to make a copy of the data incase we clear the cp2 datablock to look for previously + //loaded detection. Not memory efficient but the easiest way to do and only occurs + //in the laoding process. + ListIterator iterator = cpodDataBlock.getDataCopy().listIterator(); + cpodDataBlock.clearAll(); + //save the click trains to the database + clickTrainDataBlock.saveViewerData(); + + //save the raw clicks to the binary file.s CPODClick click; double day = -1; @@ -525,15 +327,16 @@ public class CPODImporter { while (iterator.hasNext()) { if (this.isCancelled()) return -1; click = iterator.next(); - count++; - - //System.out.println("Saving click: " + click.getUID()); //new binary file every daya; cal.setTimeInMillis(click.getTimeMilliseconds()); int dayYear = cal.get(Calendar.DAY_OF_YEAR); + if (day!=dayYear) { - this.updateProgress(i+(count/(double) totalClicks), cpxFile.size()); + + //set the progress + double progress=(((double) click.getTimeMilliseconds()-fileStartEnd[0]))/(fileStartEnd[1]-fileStartEnd[0]); + this.updateProgress(i+(progress), cpxFile.size()); if (day>-1) { //close current file @@ -544,7 +347,11 @@ public class CPODImporter { } System.out.println("Open new binary file: " + PamCalendar.formatDBDateTime(click.getTimeMilliseconds())); - this.updateMessage("Saving file: " + PamCalendar.formatDBDateTime(click.getTimeMilliseconds())); + + //send an update message + final String timeMillisStr = PamCalendar.formatDBDateTime(click.getTimeMilliseconds()); + + this.updateMessage(("Saving file: " + timeMillisStr)); //write the module head binaryStream.openOutputFiles(click.getTimeMilliseconds()); @@ -556,24 +363,286 @@ public class CPODImporter { data = cpodDataBlock.getBinaryDataSource().getPackedData(click); this.binaryStream.storeData(data.getObjectType(), click.getBasicData(), data); + nClicks++; } - cpodDataBlock.clearAll(); - - //update number of clicks. - nClicks=nClicks+MAX_SAVE; + + nClicks=nClicks+1; //so we start at the right click. + cpodDataBlock.clearAll(); + } - + } } catch (Exception e) { e.printStackTrace(); } - System.out.println("CPOD import thread finished: "); + this.updateMessage("Finished saving detections"); + System.out.println("CPOD import thread finished: " + this); return 1; } + + + /** + * Rounds millis to start of da=y + * @param millis + * @return + */ + long roundToDay(long millis) { + Date date = new Date(millis); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTimeInMillis(); + } + + /** + * Import the CPOD file. + * @param cpFile2 - the cp1 file + * @param dataBlock - the data block + * @param from - the click index to save from. e.g. 100 means that only click 100 + in the file is saved + * @param maxNum - the maximum number to import + * @return the total number of clicks in the file. + */ + private int importCPODFile(CPODFile cpFile, CPODClickDataBlock dataBlock, CPODClickTrainDataBlock clickTrainDataBlock, int from, int maxNum) { + + ArrayList cpodCP1Data = null; + ArrayList cpodCP3Data = null; + + try { + if (cpFile.isFPOD()) { + //load a chunk of FP1 data + cpodCP1Data = FPODReader.importFPODFile(cpFile.cp1File, from, maxNum); + //load all FP3 data + cpodCP3Data = FPODReader.importFPODFile(cpFile.cp3File, 0, Integer.MAX_VALUE); + } + else { + //load a chunk of CP1 data + cpodCP1Data = CPODReader.importCPODFile(cpFile.cp1File, from, maxNum); + //load all CP3 data + cpodCP3Data = CPODReader.importCPODFile(cpFile.cp3File, 0, Integer.MAX_VALUE); + } + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + + //create an ArrayList + ArrayList cpodData = new ArrayList(); + + int repcount = 0; + //now we get rid if duplicate clicks + if (cpodCP1Data != null && cpodCP3Data != null) { + CPODClickOmparator cpodComparator = new CPODClickOmparator(); + + //make sure to sort the list. + Collections.sort(cpodCP3Data, cpodComparator);; + //here we need to replace the CP1 detection in the CP1 file with the CP3 detections in the CP3 file + for (int j=0; j=0) { + cpodCP1Data.set(j, cpodCP3Data.get(index)); + repcount++; + } + } + cpodData= cpodCP1Data; //cp1 data now contains CP3 clicks. + } + else if (cpodCP1Data != null) cpodData= cpodCP1Data; //only CP1 data imported + else if (cpodCP3Data != null) cpodData= cpodCP3Data; //only CP3 data imported + + //now iterate through the detections and grab the click classiifcations. + + HashMap cpodClickTrains = new HashMap(); + + int nClicks = 0; + for (int i=0; i { + @Override public int compare(CPODClick s1, CPODClick s2) + { + if (s1.getTimeMilliseconds() > s2.getTimeMilliseconds()) { + return 1; + } + else if (s1.getTimeMilliseconds() < s2.getTimeMilliseconds()) { + return -1; + } + else if (s1.getTimeMilliseconds() == s2.getTimeMilliseconds()) { + +// return 0; + if (s1.getStartSample()>(s2.getStartSample())) return 1; + else if (s1.getStartSample()<(s2.getStartSample())) return -1; + else return 0; + } + return -1; + } + } + + + public static void main(String[] args) { + + + CPODFile cpFile = new CPODFile(); + try { + + //CPOD + cpFile.cp1File = new File("D:\\Dropbox\\PAMGuard_dev\\tutorials\\CPOD_wav\\data\\Hyskeir\\CPOD\\0740 Hyskeir 2022 12 02 POD1655 file01.CP1"); + cpFile.cp3File = new File("D:\\Dropbox\\PAMGuard_dev\\tutorials\\CPOD_wav\\data\\Hyskeir\\CPOD\\0740 Hyskeir 2022 12 02 POD1655 file01.CP3"); + +// //FPOD +// cpFile.cp1File = new File("D:\\Dropbox\\PAMGuard_dev\\tutorials\\CPOD_wav\\data\\NunBank\\FPOD\\0866 NunBankB 2023 06 27 FPOD_6480 file0.FP1"); +// cpFile.cp3File = new File("D:\\Dropbox\\PAMGuard_dev\\tutorials\\CPOD_wav\\data\\NunBank\\FPOD\\0866 NunBankB 2023 06 27 FPOD_6480 file0.FP3"); +// + + ArrayList cpodCP1Data; + ArrayList cpodCP3Data; + if (cpFile.isFPOD()) { + //load a chunk of FP1 data + cpodCP1Data = FPODReader.importFPODFile(cpFile.cp1File, 0, MAX_SAVE); + + //load all FP3 data + cpodCP3Data = FPODReader.importFPODFile(cpFile.cp3File, 0, Integer.MAX_VALUE); + } + else { + //load a chunk of CP1 data + cpodCP1Data = CPODReader.importCPODFile(cpFile.cp1File, 0, MAX_SAVE); + //load all CP3 data + cpodCP3Data = CPODReader.importCPODFile(cpFile.cp3File, 0, Integer.MAX_VALUE); + } + + //create an ArrayList + ArrayList cpodData = new ArrayList(); + + int repcount = 0; + int lastCP1=-1; + //now we get rid if duplicate clicks + if (cpodCP1Data != null && cpodCP3Data != null) { + CPODClickOmparator cpodComparator = new CPODClickOmparator(); + //make sure to sort the list. + Collections.sort(cpodCP3Data, cpodComparator); + Collections.sort(cpodCP1Data, cpodComparator); + + long lastCP1Date = cpodCP1Data.get(cpodCP1Data.size()-1).getTimeMilliseconds(); + + + for (int j=0; jlastCP1Date) { + lastCP1 = j; + break; + } + } + + //here we need to replace the CP1 detection in the CP1 file with the CP3 detections in the CP3 file + for (int j=0; jlastCP1) { + break; + } + + int index = Collections.binarySearch(cpodCP1Data, cpodCP3Data.get(j), cpodComparator ); + //replace + if (index>=0) { + repcount++; + if (j%1000==0) { + System.out.println("Match: cp1 " + cpodCP1Data.get(index).getStartSample() + " " + cpodCP3Data.get(j).getStartSample() + +" diff " + (cpodCP1Data.get(index).getStartSample() - cpodCP3Data.get(j).getStartSample())); + } + } + } + cpodData= cpodCP1Data; //cp1 data now contains CP3 clicks. + } + else if (cpodCP1Data != null) cpodData= cpodCP1Data; //only CP1 data imported + else if (cpodCP3Data != null) cpodData= cpodCP3Data; //only CP3 data imported + + + System.out.println("CPOD data: n CP1: " + cpodCP1Data.size() + " n CP3: " + cpodCP3Data.size() + " n matches " + repcount + " of " + lastCP1); + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } } diff --git a/src/cpod/CPODLoader.java b/src/cpod/CPODLoader.java index 73d4dbfb..2c26315c 100644 --- a/src/cpod/CPODLoader.java +++ b/src/cpod/CPODLoader.java @@ -17,6 +17,7 @@ import fileOfflineData.OfflineFileMapPoint; * @author Doug Gillespie * */ +@Deprecated public class CPODLoader { private CPODControl cpodControl; diff --git a/src/cpod/CPODMap.java b/src/cpod/CPODMap.java index cd0b8301..df85bfe5 100644 --- a/src/cpod/CPODMap.java +++ b/src/cpod/CPODMap.java @@ -17,6 +17,7 @@ import PamguardMVC.RequestCancellationObject; import dataGram.DatagramDataPoint; import fileOfflineData.OfflineFileMapPoint; +@Deprecated public class CPODMap { private File cpFile; @@ -29,6 +30,7 @@ public class CPODMap { public static final int FILE_CP1 = 1; public static final int FILE_CP3 = 3; + int cpFileType = 0; diff --git a/src/cpod/CPODReader.java b/src/cpod/CPODReader.java new file mode 100644 index 00000000..8d368783 --- /dev/null +++ b/src/cpod/CPODReader.java @@ -0,0 +1,369 @@ +package cpod; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +import PamUtils.PamCalendar; +import cpod.CPODUtils.CPODFileType; +import cpod.FPODReader.FPODdata; + +/** + * Read CPOD data. + *

+ * Note this should, as much as possible not have any PAMGuard based code. + * + * @author Jamie Macaulay + * @author Douglas Gillespie + * + */ +public class CPODReader { + + + /** + * A new minute. Don;t think we need to do anything here.? + * @param byteData + */ + private static void processMinute(byte[] byteData) { + // TODO Auto-generated method stub + + } + + public static CPODHeader readHeader(File cpFile) { + + BufferedInputStream bis = null; + FileInputStream fileInputStream = null; + + try { + bis = new BufferedInputStream(fileInputStream = new FileInputStream(cpFile)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + + return readHeader( bis, CPODUtils.getFileType(cpFile)); + + } + + + + public static CPODHeader readHeader(BufferedInputStream bis, CPODFileType cpFileType) { + int bytesRead; + byte[] headData = new byte[getHeadSize(cpFileType)]; + try { + bytesRead = bis.read(headData); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + if (bytesRead != headData.length) { + return null; + } + // read as a load of 4 byte integers and see what we get ! + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(headData)); + int nShort = headData.length / 2; + short[] shortData = new short[nShort]; + for (int i = 0; i < shortData.length; i++) { + try { + shortData[i] = dis.readShort(); + if (shortData[i] == 414) { + // System.out.println("Found id at %d" + i); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + dis = new DataInputStream(new ByteArrayInputStream(headData)); + int nFloat = headData.length / 4; + float[] floatData = new float[nFloat]; + for (int i = 0; i < floatData.length; i++) { + try { + floatData[i] = dis.readFloat(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + dis = new DataInputStream(new ByteArrayInputStream(headData)); + int nInt = headData.length / 4; + int[] intData = new int[nInt]; + for (int i = 0; i < nInt; i++) { + try { + intData[i] = dis.readInt(); + int bOff = i*4; + int sOff = i*2; + // if (intData[i] > 0) + // System.out.println(String.format("%d, Int = %d, Float = %3.5f, Short = %d,%d, bytes = %d,%d,%d,%d", i, intData[i], + // floatData[i], + // shortData[sOff], shortData[sOff+1], + // headData[bOff], headData[bOff+1], headData[bOff+2], headData[bOff+3])); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + CPODHeader header = new CPODHeader(); + + header.fileStart = CPODUtils.podTimeToMillis(intData[64]); + header.fileEnd = CPODUtils.podTimeToMillis(intData[65]); + // other times seem to be packed in ints 66 - 69. + header.podId = shortData[50]; + header.waterDepth = headData[8]; + + return header; + } + + + /** + * Import a CPOD file. + * @param cpFile - the CP1 or CP3 file. + * @param from - the click index to save from. e.g. 100 means that only click 100 + in the file is saved + * @param maxNum - the maximum number to import + * @return the total number of clicks in the file. + */ + protected static ArrayList importCPODFile(File cpFile, int from, int maxNum) { + + if (cpFile==null) return null; + + //holds a map of the click train detections. + HashMap clickTrains = new HashMap(); + //the current classification + CPODClassification cpodClassification; + + ArrayList clicks = new ArrayList(); + BufferedInputStream bis = null; + int bytesRead; + FileInputStream fileInputStream = null; + long totalBytes = 0; + + CPODFileType cpFileType = CPODUtils.getFileType(cpFile); + + try { + bis = new BufferedInputStream(fileInputStream = new FileInputStream(cpFile)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + + CPODHeader header = readHeader(bis, cpFileType); + if (header == null) { + return null; + }; + + totalBytes = getHeadSize(cpFileType); + int dataSize = getDataSize(cpFileType); + byte[] byteData = new byte[dataSize]; + short[] shortData = new short[dataSize]; + int fileEnds = 0; + boolean isClick; + // first record is always a minute mark, so start + // at -1 to avoid being skipped forward one minute. + int nClicks = 0, nMinutes = -1; + try { + while (true) { + bytesRead = bis.read(byteData); + for (int i = 0; i < bytesRead; i++) { + shortData[i] = CPODUtils.toUnsigned(byteData[i]); + } + if (isFileEnd(byteData)) { + fileEnds++; + } + else { + fileEnds = 0; + } + if (fileEnds == 2) { + break; + } + + isClick = byteData[dataSize-1] != -2; + if (isClick) { + + if (from<0 || (nClicks>from && nClicks<(from+maxNum))) { + + //System.out.println("Create a new CPOD click: "); + CPODClick cpodClick = processCPODClick(nMinutes, shortData, header); + + + if (cpFileType.equals(CPODFileType.CP3)) { + + short trainID = shortData[20]; + +// short species = (short) (shortData[36] & (112 >> 4)); + + short quality = (short) (shortData[36] & 3); + + short species = (short) (shortData[36] >> 3); + +// short species = (short) ((shortData[19] >> 2) & 7); +// short quality = (short) (shortData[19] & 3); + + //generate a unique train ID within the file + int trainUID = Integer.valueOf(String.format("%06d", nMinutes) + String.format("%d", trainID)); + + //find the click train from the hash map - if it is not there, create a new one. + cpodClassification = clickTrains.get(trainUID); + + if (cpodClassification==null) { + cpodClassification = new CPODClassification(); + cpodClassification.isEcho = false; + cpodClassification.clicktrainID = trainUID; + cpodClassification.species = CPODUtils.getCPODSpecies(species); + cpodClassification.qualitylevel = quality; + + clickTrains.put(trainUID, cpodClassification); +// long timeMillis = PamCalendar.msFromDate(2023, 1, 6, 22, 32, 41, 200); +// if (cpodClick.getTimeMilliseconds()>timeMillis && cpodClick.getTimeMilliseconds()<(timeMillis+20000)) { +// System.out.println("Click train ID: " + trainUID + " minutes: " + nMinutes + " species: " + species + " quality level: " + quality); +// System.out.println(String.format("

QClass %d, SpClass %d", CPODUtils.getBits(shortData[19], (short) 0x3), +// CPODUtils.getBits(shortData[19], (short) 0b11100))); +// } + } + cpodClick.setClassification(cpodClassification); + } + + clicks.add(cpodClick); + + if (nClicks%100000==0) { + System.out.println("CPOD data: " + nClicks + " " + PamCalendar.formatDateTime(cpodClick.getTimeMilliseconds()) + " " +cpodClick.getBw() + " " +shortData[4] ); +// if (nClicks>0)return clicks; //FIXME + } + } + + nClicks++; + // // now remove the data unit from the data block in order to clear up memory. Note that the remove method + // // saves the data unit to the Deleted-Items list, so clear that as well (otherwise we'll just be using + // // up all the memory with that one) + // dataBlock.remove(cpodClick); + // dataBlock.clearDeletedList(); + } +// else { +// nMinutes ++; +// processMinute(byteData); +// } + else if(CPODUtils.toUnsigned(byteData[dataSize-1])==254){ + nMinutes ++; + } + totalBytes += dataSize; + } + bis.close(); + } catch (IOException e) { + e.printStackTrace(); + } + System.out.println(String.format("File read: Clicks %d, minutes %d", nClicks, nMinutes)); + + return clicks; + } + + public static int getHeadSize(CPODFileType fileType) { + switch (fileType) { + case CP1: + return 360; + case CP3: + return 720; + case FP1: + return 1024; + case FP3: + return 1024; + } + return 0; + } + + public static int getDataSize(CPODFileType fileType) { + switch (fileType) { + case CP1: + return 10; + case CP3: + return 40; + case FP1: + return 16; + case FP3: + return 32; + } + return 0; + } + + /** + * Holds an CPOD header information + *

+ * Note that is pretty opaque what all this means. The important parameters have been commented. + */ + public static class CPODHeader { + + public byte waterDepth; + public short podId; + public long fileEnd; + public long fileStart; + + } + + /** + * Is it the end of the file ? + * @param byteData + * @return true if all bytes == 255 + */ + public static boolean isFileEnd(byte[] byteData) { + for (int i = 0; i < byteData.length; i++) { + // if ((byteData[i] ^ 0xFF) != 0) { + // return false; + // } + if (byteData[i] != -1) { + return false; + } + } + return true; + } + + /** + * Create a CPOD click object from CPOD data. + * @param nMinutes + * @param shortData + * @param header + * @return + */ + private static CPODClick processCPODClick(int nMinutes, short[] shortData, CPODHeader header) { + + long minuteMillis = header.fileStart + nMinutes * 60000L; + + int t = shortData[0]<<16 | + shortData[1]<<8 | + shortData[2]; // 5 microsec intervals ! + long tMillis = minuteMillis + t/200; + + + + // do a sample number within the file as 5us intervals + long fileSamples = (long) (((minuteMillis*60) + (t*5/1000000.))*CPODClickDataBlock.CPOD_SR); + + /* + * + */ + return CPODClick.makeCPODClick(tMillis, fileSamples, shortData); + } + + /** + * Test the program + * @param args + */ + public static void main(String[] args) { + // String filePath = "/Users/au671271/Library/CloudStorage/GoogleDrive-macster110@gmail.com/My Drive/PAMGuard_dev/CPOD/FPOD_NunBank/0866 NunBankB 2023 06 27 FPOD_6480 file0.FP1"; + + String filePath = "D:\\Dropbox\\PAMGuard_dev\\tutorials\\CPOD_wav\\data\\Hyskeir\\CPOD\\0740 Hyskeir 2022 12 02 POD1655 file01.CP3"; + // String filePath = "D:\\DropBox\\PAMGuard_dev\\CPOD\\FPOD_NunBank\\0866 NunBankB 2023 06 27 FPOD_6480 file0.FP3"; + + File fpfile = new File(filePath); + + + importCPODFile( fpfile, 0, Integer.MAX_VALUE); + + + } + +} diff --git a/src/cpod/CPODSpeciesModifier.java b/src/cpod/CPODSpeciesModifier.java new file mode 100644 index 00000000..446d58b2 --- /dev/null +++ b/src/cpod/CPODSpeciesModifier.java @@ -0,0 +1,62 @@ +package cpod; + +import java.awt.Color; + +import PamView.GeneralProjector; +import PamView.PamSymbolType; +import PamView.symbol.PamSymbolChooser; +import PamView.symbol.SymbolData; +import PamView.symbol.modifier.SymbolModType; +import PamView.symbol.modifier.SymbolModifier; +import PamguardMVC.PamDataUnit; +import cpod.CPODClassification.CPODSpeciesType; + +public class CPODSpeciesModifier extends SymbolModifier { + + final Color porpColor = new Color(93,30,255); + + final Color dolphColor = new Color(255,160,0); + + private SymbolData symbolData = new SymbolData(PamSymbolType.SYMBOL_CIRCLE, 5, 5, true, java.awt.Color.BLACK, java.awt.Color.BLACK); + + public CPODSpeciesModifier(PamSymbolChooser symbolChooser) { + super("Species", symbolChooser, SymbolModType.FILLCOLOUR | SymbolModType.LINECOLOUR); + } + + @Override + public SymbolData getSymbolData(GeneralProjector projector, PamDataUnit dataUnit) { + + CPODClick cpodClick = (CPODClick) dataUnit; + + Color color = Color.BLACK; + + + if (cpodClick.getCPODClickTrain()!=null) { + + CPODSpeciesType species = cpodClick.getCPODClickTrain().getSpecies(); + + switch(species) { + case DOLPHIN: + color=dolphColor; + break; + case NBHF: + color=porpColor;; + break; + case SONAR: + color = Color.DARK_GRAY; + break; + case UNKNOWN: + break; + default: + break; + } + + } + + symbolData.setFillColor(color); + symbolData.setLineColor(color); + + return symbolData; + } + +} diff --git a/src/cpod/CPODSymbolManager.java b/src/cpod/CPODSymbolManager.java index 361f6e33..92903425 100644 --- a/src/cpod/CPODSymbolManager.java +++ b/src/cpod/CPODSymbolManager.java @@ -1,11 +1,13 @@ package cpod; +import java.awt.Color; + import PamController.PamControlledUnit; +import PamView.PamSymbolType; import PamView.symbol.PamSymbolChooser; import PamView.symbol.StandardSymbolManager; import PamView.symbol.SymbolData; import PamView.symbol.modifier.PeakFreqModifier; -import PamView.symbol.modifier.SuperDetSymbolModifier; import PamguardMVC.PamDataBlock; public class CPODSymbolManager extends StandardSymbolManager { @@ -15,6 +17,9 @@ public class CPODSymbolManager extends StandardSymbolManager { */ private PamControlledUnit cpodControl; + + private static SymbolData defaultSymbol = new SymbolData(PamSymbolType.SYMBOL_CIRCLE, 10, 10, true, Color.BLACK, Color.BLACK); + // /** // * Flag to colour clicks by their frequency. It has to be one higher than the other options. // * @@ -22,7 +27,7 @@ public class CPODSymbolManager extends StandardSymbolManager { // public static final int COLOUR_BY_FREQ= 6; public CPODSymbolManager(PamControlledUnit cpodControl, PamDataBlock pamDataBlock) { - super(pamDataBlock, new SymbolData()); + super(pamDataBlock, defaultSymbol); this.cpodControl = cpodControl; addSymbolOption(HAS_SYMBOL); } @@ -44,13 +49,15 @@ public class CPODSymbolManager extends StandardSymbolManager { @Override public void addSymbolModifiers(PamSymbolChooser psc) { - super.addSymbolModifiers(psc); //add the peak frequency modifier that allows clicks to be coloured by peak frequency. psc.addSymbolModifier(new PeakFreqModifier(psc)); - // we can also add some default behaviour here to match the old behaviour + //add the peak frequency modifier that allows clicks to be coloured by peak frequency. + psc.addSymbolModifier(new CPODSpeciesModifier(psc)); + + // we can also add some default behaviour here to match the old behaviours // these will get overridden once user options are set, but it's good to give defaults. // SymbolModifier eventMod = psc.hasSymbolModifier(SuperDetSymbolModifier.class); // if (eventMod != null) { diff --git a/src/cpod/CPODTrainSymbolManager.java b/src/cpod/CPODTrainSymbolManager.java new file mode 100644 index 00000000..599bec05 --- /dev/null +++ b/src/cpod/CPODTrainSymbolManager.java @@ -0,0 +1,16 @@ +package cpod; + +import PamView.symbol.SymbolData; +import PamguardMVC.superdet.swing.SuperDetectionSymbolManager; + +public class CPODTrainSymbolManager extends SuperDetectionSymbolManager { + + public CPODTrainSymbolManager(CPODClickTrainDataBlock pamDataBlock) { + super(pamDataBlock, new SymbolData()); + super.setSpecialColourName("Click Train"); + } + + +} + + diff --git a/src/cpod/CPODUtils.java b/src/cpod/CPODUtils.java new file mode 100644 index 00000000..a89c5741 --- /dev/null +++ b/src/cpod/CPODUtils.java @@ -0,0 +1,152 @@ +package cpod; + +import java.io.File; + +import cpod.CPODClassification.CPODSpeciesType; + + +/** + * Some useful utility function for CPOD and FPOD data + */ +public class CPODUtils { + + /** + * CPOD file types + * @author Jamie Macaulay + * + */ + public enum CPODFileType { + CP1("CP1"), + CP3("CP3"), + FP1("FP1"), + FP3("FP3"); + + private String text; + + CPODFileType(String text) { + this.text = text; + } + + public String getText() { + return this.text; + } + + public static CPODFileType fromString(String text) { + for (CPODFileType b : CPODFileType.values()) { + if (b.text.equalsIgnoreCase(text)) { + return b; + } + } + return null; + } + } + + /** + * Java will only have read signed bytes. Nick clearly + * uses a lot of unsigned data, so convert and inflate to int16. + * @param signedByte + * @return unsigned version as int16. + */ + public static short toUnsigned(byte signedByte) { +// short ans = signedByte; + + short ans = (short) (signedByte & 0xff); + +// if (ans < 0) { +// ans += 256; +// } + + return ans; + } + + /** + * Convert POD time to JAVA millis - POD time is + * integer minutes past the same epoc as Windows uses + * i.e. 0th January 1900. + * @param podTime + * @return milliseconds. + */ + public static long podTimeToMillis(long podTime) { + return podTime * 60L * 1000L - (25569L*3600L*24000L); + } + + public static CPODFileType getFileType(File cpFile) { + for (int i=0; i>firstBit); + } + + /** + * Get the FPOD species from an int flag + * @param species - integer flag representing the species + * @return the ENUM species type. + */ + public static CPODSpeciesType getFPODSpecies(short species) { + CPODSpeciesType type= CPODSpeciesType.UNKNOWN; + switch (species) { + case 0: + type = CPODSpeciesType.NBHF; + break; + case 1: + type = CPODSpeciesType.DOLPHIN; + break; + case 2: + type = CPODSpeciesType.UNKNOWN; + break; + case 4: + type = CPODSpeciesType.SONAR; + break; + } + return type; + } + + +} diff --git a/src/cpod/FPODReader.java b/src/cpod/FPODReader.java new file mode 100644 index 00000000..09a4d01a --- /dev/null +++ b/src/cpod/FPODReader.java @@ -0,0 +1,1432 @@ +package cpod; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import PamUtils.PamArrayUtils; +import PamUtils.PamCalendar; +import cpod.FPODReader.FPODdata; + + +/** + * Functions for importing FPOD files. + *

+ * Note this class should be independent of any PAMGuard functionality. + *

+ * Although some anming has changed a lot of the variable names are consistent with Pascal code + * used in FPOD.exe software from whihc this is absed. + * + * @author Jamie Macaulay + */ +public class FPODReader { + + /** + * Look up array to convert byte values to linear sound level + */ + private static int[] LinearPkValsArr; + + /** + * Look up array to convert byte values to linear sound level if using extended amps. + */ + private static int[][] ClippedPkArr; + + + /** + * Look up sine array fro reconstructing waveforms. + */ + private static double[] SineArr; + + /** + * Look up sine array for converting IPI (inter-pulse-interval) to kHz values + */ + private static int[] IPItoKHZ = new int[257]; + + /** + * Length of the FPOD header in bytes. + */ + public static final int FPOD_HEADER = 1024; + + + /** + * The length of a standard FPOD entry + */ + public static final int FP1_FPOD_DATA_LEN = 16; + + + /** + * The click length for FP3 files. + */ + public static final int FP3_FPOD_DATA_LEN = 32; + + /** + * Scale factor to convert waveform measurements to PAMGuard -1 to 1 measurements. + */ + public static final double WAV_SCALE_FACTOR = 255.; + + + public static final float FPOD_WAV_SAMPLERATE = 2000000; + + public static FPODHeader readHeader(File cpFile) { + + BufferedInputStream bis = null; + int bytesRead; + FileInputStream fileInputStream = null; + int totalBytes = 0; + try { + bis = new BufferedInputStream(fileInputStream = new FileInputStream(cpFile)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + + FPODHeader header = new FPODHeader(); + try { + if (readHeader(bis, header) != FPOD_HEADER) { + return null; + } + bis.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return header; + + } + + /** + * Import FPOD data and return a structure of CPOD clicks. + * @param cpFile - the FP1 file. + * @param from - the click index to save from. e.g. 100 means that only click 100 + in the file is saved + * @param maxNum - the maximum number of data units to import. + * @return an array of CPOD clicks. + * @throws IOException + */ + public static ArrayList importFPODFile(File cpFile, int from, int maxNum ) throws IOException { + if (cpFile==null) return null; + ArrayList fpodData = new ArrayList(); + FPODReader.importFile(cpFile, fpodData, from, maxNum); + + ArrayList cpodClickData = new ArrayList(); + + CPODClick cpodClick; + for (int i=0; i fpodData, int from, int maxNum ) throws IOException { + + populateRawToRealPkArrays(); + populateIPIArray(); + + BufferedInputStream bis = null; + int bytesRead; + FileInputStream fileInputStream = null; + int totalBytes = 0; + try { + bis = new BufferedInputStream(fileInputStream = new FileInputStream(cpFile)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return 0; + } + FPODHeader header = new FPODHeader(); + if (readHeader(bis, header) != FPOD_HEADER) { + return 0; + } + + int fileEnds = 0; + boolean isClick; + FPODdata fpodClick; + // first record is always a minute mark, so start + // at -1 to avoid being skipped forward one minute. + int nClicks = 0, nMinutes = -1; + int nWavClicks = 0; + + //keep track of wav data + FPODWavData wavData = null; + + //holds a map of the click train detections. + HashMap clickTrains = new HashMap(); + + //the click train of the current data unit. + CPODClassification cpodClassification = null; + short lastSpecies = 0; + int wavbufpos = 0; + try { + while (true) { + + fpodClick = new FPODdata(); + + byte[] bufPosN = new byte[FP1_FPOD_DATA_LEN]; + + int byteRead = bis.read(bufPosN, 0, FP1_FPOD_DATA_LEN); + if (byteRead!=FP1_FPOD_DATA_LEN) { + System.out.println("Total len: " + totalBytes); + break; + } + + // ftFP3 is FP3 file in Padcal? + // ftFP2 is FP2 file in Padcal? + // ftFP1 is FP1 file + + totalBytes+=byteRead; + + isClick = toUnsigned(bufPosN[0]) < 184; + + if (isClick) { + // time within minute in 5 microsecond units + fpodClick.FiveMusec = ((bufPosN[0] & 0xFF) << 16) | // Shift first byte 16 bits to the left + ((bufPosN[1] & 0xFF) << 8) | // Shift second byte 8 bits to the left + (bufPosN[2] & 0xFF); // Leave third byte as is + + //N of cycles in the click - up to 255cycles + fpodClick.Ncyc = toUnsigned(bufPosN[3]); //number of cycles + + + //Wavenumber of loudest cycle; range of IPIs in click + fpodClick.PkAt = (bufPosN[4] & 0xF0) >> 4; + + // decompress the click IPI values + if ((bufPosN[4] & 0xF) == 15) { + fpodClick.ClkIPIrange = 65; // Since > 64 isn't directly assignable, assign 65 instead. + } else if ((bufPosN[4] & 0x8) == 8) { + fpodClick.ClkIPIrange = (((bufPosN[4] & 0x7) + 1) << 3); + } else { + fpodClick.ClkIPIrange = (bufPosN[4] & 0x7); + } + + // IPI of Pk-1 + fpodClick.IPIpreMax = toUnsigned(bufPosN[5]) + 1; // Increment byte at BufPosn[5] and assign to + // NrClk.IPIpreMax + + // IPI of Pk this is the loudest cycle in the click + fpodClick.IPIatMax = toUnsigned(bufPosN[6]) + 1; // Increment byte at BufPosn[6] and assign to + // NrClk.IPIatMax + + int localIPIatMax; + if (header.FPGAcodeVersion > 801) { + localIPIatMax = fpodClick.IPIatMax; + } else { + localIPIatMax = fpodClick.IPIpreMax; + } + + if (bufPosN[7] > 0) { + // IPI of Pk+1 + fpodClick.IPIplus1 = toUnsigned(bufPosN[7]) + 1; + if (bufPosN[8] > 0) { + // IPI of Pk+1 + fpodClick.IPIplus2 = toUnsigned(bufPosN[8]) + 1; + } + } + + // now on to amplitudes + if (bufPosN[9] > 0) { + // Amplitude of P-1 + fpodClick.RawPkminus1 = toUnsigned(bufPosN[9]); + + fpodClick.Pkminus1Extnd = RawToRealPk(toUnsigned(bufPosN[9]), localIPIatMax, + header.HasExtendedAmps); + } + + fpodClick.MaxPkRaw = (short) Math.max(2, toUnsigned(bufPosN[10])); + // fpodClick.MaxPkLinear = RawToLinearPk(toUnsigned(bufPosN[10])); + fpodClick.MaxPkExtnd = RawToRealPk(toUnsigned(bufPosN[10]), localIPIatMax, + header.HasExtendedAmps); + + if (toUnsigned(bufPosN[11]) > 0) { + fpodClick.RawPkplus1 = toUnsigned(bufPosN[11]); + fpodClick.Pkplus1Extnd = RawToRealPk(toUnsigned(bufPosN[11]), localIPIatMax, + header.HasExtendedAmps); + } + + fpodClick.IPIbefore = toUnsigned(bufPosN[12]) + 1; + fpodClick.AmpReversals = bufPosN[13] & 15; + + // Defer MSnibble[13][14] until [15] has been read + if (header.FileType == 2) { + // fpodClick.Clstr.cNall = toUnsigned(bufPosN[15]); //not implemented in PAMGuard + } else if (toUnsigned(bufPosN[15]) < 2) { // POD marks where a sonar has been found + fpodClick.EndIPI = fpodClick.IPIpreMax; + } else { + //fpodClick.HasWave = (bufPosN[15] & 1) == 1; + fpodClick.EndIPI = bufPosN[15] & 254 + 1; // + 1 on all IPIs because counts from the POD start at + } + + //the duration is in 5us units. + fpodClick.duration = ((bufPosN[13] & 240)*16 + toUnsigned(bufPosN[14])); + + ///rm...can't exactly explain this but it's translated from FPOD Pascal code - calculates bandwidth + int ampDfSum = Math.abs(fpodClick.Pkminus1Extnd - fpodClick.MaxPkRaw); + int ampSum = Math.round((fpodClick.Pkminus1Extnd + fpodClick.MaxPkRaw) / 2); + int ipIdfSum = Math.abs(fpodClick.IPIpreMax - fpodClick.IPIatMax); + int ipISum = fpodClick.IPIatMax; + + if (fpodClick.Pkplus1Extnd > 0) { + ampDfSum += Math.abs(fpodClick.Pkplus1Extnd - fpodClick.MaxPkRaw); + ampSum += Math.round((fpodClick.Pkplus1Extnd + fpodClick.MaxPkRaw) / 2); + ipIdfSum += Math.abs(fpodClick.IPIatMax - fpodClick.Pkplus1Extnd); + ipISum += fpodClick.Pkplus1Extnd; + } + + //set the nadwidth + fpodClick.BW = Math.max(0, Math.min(11, Math.round(ipIdfSum << 4 / ipISum)) + Math.min(20, Math.round((ampDfSum + (ampDfSum >> 1)) << 3 / ampSum) - 1)); + + + // if (fpodClick.HasWave) { + // System.err.println("Pod data: " + nClicks + " " + fpodClick.FiveMusec + " hasWav: " + fpodClick.HasWave); + // } + + //set the time in millis. + + long timeMillis = (long) (CPODUtils.podTimeToMillis(header.FirstLoggedMin) + (nMinutes*60*1000.) + fpodClick.FiveMusec/200); + fpodClick.setTimeMillis(timeMillis); + fpodClick.HasWave = false; + + + /** + * + * Set the raw (sort of - actually peak positions and IPI) waveform data + * + * Note that the wav data is recorded before the click - this is why this + * section of code is here. A non null wavData object indicates that wav data has been processed + * and it must belong to this click. We then set wavData to null indicated there is no wav data + * until the wavData is again present. + */ + if (wavData!=null) { + + fpodClick.HasWave = true; + // fpodClick.setNWavRecs(0); + wavData.setClickCyclesStartAt(21 - fpodClick.Ncyc); + + for (int count = wavData.getWavPtr(); count >= 1; count--) { + wavData.setWavValsIPI((short) 255, count); + wavData.setWavValsSPL((short) 0, count); + } + + fpodClick.setWavData(wavData); + // int[] wave = makeResampledWaveform(fpodClick); + + //Test clicks + // if (fpodClick.getTimeMillis()==1689706702252L) {//porpoise click + // if (fpodClick.getTimeMillis()== 1689615091621L) { + // int[] wave = makeResampledWaveform(fpodClick); + // System.out.println("WAV DATA " + nClicks + " " + fpodClick.getWavData().getNWavRecs() + " " + wavData.getWavPtr()); + // + // System.out.println(" Peaks: "); + // for (int i = 0; i< wavData.getWavValsSPL().length; i++) { + // System.out.print(" " + wavData.getWavValsSPL()[i]); + // } + // + // System.out.println(); + // + // System.out.println(" Waveform: "); + // for (int i = 0; i< wave.length; i++) { + // System.out.print(" " + wave[i]); + // } + // return 0; + // } + + // fpodClick.getWavData(); + + + //does this set the reference to null - NOPE; + wavData = null; + + } + + /** + * Set the classification (if FP3 file) + */ + if (clickTrains!=null) { + fpodClick.setClassification(cpodClassification); + + //TEST +// long timeMillis2 = PamCalendar.msFromDate(2023, 8, 31, 2, 52, 46, 200); //NBHF +// //long timeMillis2 = PamCalendar.msFromDate(2023, 7, 17, 16, 33, 2, 200); //DOLPH +// if (fpodClick.getTimeMillis()>timeMillis2 && fpodClick.getTimeMillis()<(timeMillis2+5000)) { +// System.out.println("Click train ID: " + cpodClassification.clicktrainID + " minutes: " + nMinutes + " species: " + cpodClassification.species + " species code: " + lastSpecies + " quality level: " + cpodClassification.qualitylevel); +// } + + } + + fpodClick.setMinute(nMinutes); + + if (from<0 || (nClicks>from && nClicks<(from+maxNum))) { + //add the click to the FPODdata. + fpodData.add(fpodClick); + } + + + if (nClicks%400000==0) { + System.out.println("Pod data: " + nClicks + " " + PamCalendar.formatDateTime(fpodClick.getTimeMillis()) + " " +fpodClick.dataString() + " " +toUnsigned(bufPosN[13]) + " " + toUnsigned(bufPosN[14])); + } + + nClicks++; + + + } + else if (toUnsigned(bufPosN[0])==250) { + + //wav data preceedes the next click + + if (wavData==null) { + //starting a new wav record + wavData = new FPODWavData(); + wavData.setNWavRecs(0); + wavData.setWavPtr(21); + wavbufpos = 0; + nWavClicks++; + } + + //the block has already been read... + // if (Fs[FN].isInPlay() && bufpos >= Fs[FN].getNumBytesRead()) { + // readNextBlock(); // Assumes you have a method for reading the next block + // } + if (toUnsigned(bufPosN[wavbufpos]) == 250) { //a little redundant but keep in just incase + wavData.setNWavRecs(wavData.getNWavRecs() + 1); + if (wavData.getNWavRecs() < 4) { + int posn = wavData.getWavPtr(); + for (int count = 0; count <= 6; count++) { + wavData.setWavValsIPI(toUnsigned((bufPosN[wavbufpos + (count << 1) + 1])), posn); + wavData.setWavValsSPL(toUnsigned((bufPosN[wavbufpos + (count << 1) + 2])), posn); + posn--; + } + wavData.setWavPtr(wavData.getWavPtr() - 7); + } + } + + // JamArr.printArray(fpodClick.getWavValsSPL()); + //wav data + nWavClicks++; + } + + else if(toUnsigned(bufPosN[0])==249) { + //click train data - this is not included - for now + //click train data precedes the next click + + //the train ID is unique to the minute, + short trainID = toUnsigned(bufPosN[15]); + + //0 is NBHF + //1 is dolphin + //2 spUnclassiifed? + //4 sonar? + short species = (short) ((bufPosN[14] >>> 2) & 3); + lastSpecies = species; + + //quality level for the click train + short qualitylevel = (short) ((bufPosN[14]) & 3); + + boolean echo = false; + if ((bufPosN[14] & 32) == 32) { + echo = true; + } + + + //generate a unique train ID within the file + int trainUID = Integer.valueOf(String.format("%06d", nMinutes) + String.format("%d", trainID)); + + //find the click train from the hash map - if it is not there, create a new one. + cpodClassification = clickTrains.get(trainUID); + + if (cpodClassification==null) { + cpodClassification = new CPODClassification(); + cpodClassification.isEcho = echo; + cpodClassification.clicktrainID = trainUID; + cpodClassification.species = CPODUtils.getFPODSpecies(species); //add onme to make same format as CPOD...? + cpodClassification.qualitylevel = qualitylevel; + + clickTrains.put(trainUID, cpodClassification); + +// System.out.println(PamCalendar.formatDateTime2(fpodClick.getTimeMillis()) + "Click train ID: " + trainUID + " minutes: " + nMinutes + " species: " + species + " quality level: " + qualitylevel); + + } + + + } + else if(toUnsigned(bufPosN[0])==254){ + nMinutes ++; + } + + } + + System.out.println("Number clks " + nClicks + " nWav: " + nWavClicks + " minutes: " + nMinutes); + bis.close(); + return totalBytes; + + } catch (IOException e) { + e.printStackTrace(); + return totalBytes; + } + } + + + /** + * Convert a raw binary peak to true linear peak. + * @param Pk - the raw maximum peak + * @param IPI - the inter pulse interval + * @param UseExtendedAmps - true to use extended amps + * @return the real linear peak of he wave + */ + public static int RawToRealPk(int Pk, int IPI, boolean UseExtendedAmps) { + if (Pk == 0) { + return 1; + } + if (IPI < 10) { // or (IPI > 256) + return LinearPkValsArr[Pk]; + } + return UseExtendedAmps && (Pk > 222) ? ClippedPkArr[Pk][IPI - 1] : LinearPkValsArr[Pk]; + } + + + /** + * Populate the arrays used for calculatung true peaks + */ + private static void populateRawToRealPkArrays() { + final int constMaxAmpKHZScaler = 50; + final int constMinPkampAllowed = 384; // (224 - 128) shl 2; + + int count, IPI, val, MaxPkampAllowed, Pk; + boolean MaxExceeded; + int[] RiseTimeConversionArr = new int[256 - 223 + 1]; // Initialized for indices 223 to 255 + + LinearPkValsArr = new int[256]; + + // Populate LinearPkValsArr + for (count = 0; count <= 127; count++) { + LinearPkValsArr[count] = count; + } + for (count = 128; count <= 191; count++) { + LinearPkValsArr[count] = (count - 64) << 1; + } + for (count = 192; count <= 255; count++) { + LinearPkValsArr[count] = (count - 128) << 2; + } + + + + // Populate RiseTimeConversionArr + for (Pk = 223; Pk <= 255; Pk++) { + if (Pk < 232) { + RiseTimeConversionArr[Pk - 223] = 32 + (231 - Pk) * 4; + } else if (Pk < 240) { + RiseTimeConversionArr[Pk - 223] = 16 + (239 - Pk) * 2; + } else { + RiseTimeConversionArr[Pk - 223] = 255 - Pk; + } + } + + ClippedPkArr = new int[256][256]; + + // Populate ClippedPkArr + for (IPI = 10; IPI <= 256; IPI++) { + MaxExceeded = false; + MaxPkampAllowed = IPI * constMaxAmpKHZScaler; + for (Pk = 223; Pk <= 255; Pk++) { + if (!MaxExceeded) { + val = (int) Math.round(Math.pow(4000.0 / IPI, -0.75) * Math.pow(10, 5.24 * Math.pow(RiseTimeConversionArr[Pk - 223], -0.11))); + } else { + val = MaxPkampAllowed; + } + if (val > MaxPkampAllowed) { + val = MaxPkampAllowed; + MaxExceeded = true; + } else { + val = Math.max(val, constMinPkampAllowed); + } + ClippedPkArr[Pk][IPI - 1] = Math.min(IPI * constMaxAmpKHZScaler, Math.max(384, val)); + } + } + } + + + /** + * Convert IPI to KHz. + * @param IPI - the IPI + * @return the kHz value + */ + public static int IPItoKhz(int IPI) { + return IPItoKHZ[IPI]; + } + + /** + * Ppulate the IPI array + */ + public static void populateIPIArray() { + + for (int count = 0; count < 16; count++) { + IPItoKHZ[count] = 255; + } + + for (int count = 16; count < 256; count++) { + IPItoKHZ[count] = Math.round(4000 / count); + } + + IPItoKHZ[64] = 63; // Smoothes an uncomfortable step here + IPItoKHZ[256] = 1; // An 'indicative' value + } + + + /** + * Read the FPOD header + * @param bis + * @return the number of bytes read. + * @throws IOException + */ + private static int readHeader(BufferedInputStream bis, FPODHeader PODset) throws IOException { + + byte[] HdrBuf = new byte[FPOD_HEADER]; + bis.read(HdrBuf, 0, FPOD_HEADER); + + // INITS - GET F METADATA gfmd + PODset.gain = HdrBuf[1]; + // ... (Previously translated code for PODID and PodNstr5char) + + // Assuming MinToDDMMYrstr is a function converting minutes to a DDMMYYYY string + PODset.DateOfCalibrationStr = MinToDDMMYrstr((HdrBuf[5] << 8 | HdrBuf[6]) * 1440); + + int mainBoardNumber = HdrBuf[7] * 10000 + HdrBuf[8] * 100 + HdrBuf[9]; + PODset.MainBoardNumber = mainBoardNumber; + + PODset.PreAmpVersion = toUnsigned(HdrBuf[10]); + PODset.HydrophoneVersion = toUnsigned(HdrBuf[11]); + PODset.FPGAtype = toUnsigned(HdrBuf[12]); + PODset.FromWavFileData = toUnsigned(HdrBuf[13]) != 0; // Convert boolean value + PODset.PaDataVersion = 1; // xx revise this + PODset.PICtype = toUnsigned(HdrBuf[14]); + PODset.HousingType = toUnsigned(HdrBuf[16]); + PODset.ReleaseTone1Min= toUnsigned(HdrBuf[17]); + PODset.ReleaseTone1Max= toUnsigned(HdrBuf[18]); + PODset.ReleaseTone2Min= toUnsigned(HdrBuf[19]); + PODset.ReleaseTone2Max= toUnsigned(HdrBuf[20]); + PODset.ReleaseTone3Min= toUnsigned(HdrBuf[21]); + PODset.ReleaseTone3Max= toUnsigned(HdrBuf[22]); + PODset.ReleaseTone4Min= toUnsigned(HdrBuf[23]); + PODset.ReleaseTone4Max= toUnsigned(HdrBuf[24]); + PODset.ReleaseTone5Min= toUnsigned(HdrBuf[25]); + PODset.ReleaseTone5Max= toUnsigned(HdrBuf[26]); + PODset.AcRelNcyc= toUnsigned(HdrBuf[27]); + PODset.AcRelSlotLength= toUnsigned(HdrBuf[28]); + PODset.AcRelSlotSpacing= toUnsigned(HdrBuf[29]); + PODset.RelaysSecs= toUnsigned(HdrBuf[30]); // xxcheck where this is + //unused + // these were zero + PODset.PICcodeMajorVersion= toUnsigned(HdrBuf[37]); + + if (PODset.PICcodeMajorVersion > 14) { + PODset.SteeperStepping = true; + } + + PODset.PICcodeVersionStr = String.format("%d.%d", toUnsigned(HdrBuf[37]), toUnsigned(HdrBuf[38])); + PODset.FPGAcodeVersion = (HdrBuf[39] << 8) | HdrBuf[40]; + PODset.FPGAcodeVersionStr =String.format("%d.%d", toUnsigned(HdrBuf[39]), toUnsigned(HdrBuf[40])); + + // // Assuming F.CurrentActivityLbl is a label component + // F.CurrentActivityLbl.setText("PIC code v" + PODset.PICcodeVersionStr + + // " FPGA code v" + PODset.FPGAcodeVersionStr); + + if (PODset.FromWavFileData) { + PODset.PODID = (HdrBuf[2] >>> 24) | (HdrBuf[3] >>> 16) | (HdrBuf[4] >>> 8) | HdrBuf[5]; + } + + if (PODset.FPGAcodeVersion > 0) { + PODset.HasExtendedAmps = true; + } + + // SETS: + PODset.UTCoffset = toUnsigned(HdrBuf[64]); + + switch (HdrBuf[65]) { + case 0: + PODset.FilterKHZ = 120; + break; + case 1: + PODset.FilterKHZ = 80; + break; + case 2: + PODset.FilterKHZ = 40; + break; + case 3: + PODset.FilterKHZ = 20; + break; + } + + switch (HdrBuf[66]) { + case 0: + PODset.FileMinutecountLimit = 2048; + break; + case 1: + PODset.FileMinutecountLimit = 8192; + break; + case 2: + PODset.FileMinutecountLimit = 4096; + break; + case 3: + PODset.FileMinutecountLimit = 16384; + break; + case 16: + PODset.FileMinutecountLimit = 4096; + break; + case 17: + PODset.FileMinutecountLimit = 8192; + break; + case 18: + PODset.FileMinutecountLimit = 16384; + break; + case 19: + PODset.FileMinutecountLimit = 32768; + break; + case 32: + PODset.FileMinutecountLimit = 8192; + break; + + case 33: + PODset.FileMinutecountLimit = 16384; + break; + case 34: + PODset.FileMinutecountLimit = 32768; + break; + case 35: + PODset.FileMinutecountLimit = 65536; + break; + default: + // Handle error or unexpected value + System.out.println("FileMinutecountLimit error in reading header of file " + ", code = " + HdrBuf[66]); + PODset.FileMinutecountLimit = 65536; // Set a default value + } + + + PODset.MinimumCycleAmplitude = toUnsigned(HdrBuf[67]); + PODset.MinIPI = toUnsigned(HdrBuf[68]); + PODset.MaxIPI = toUnsigned(HdrBuf[69]); + PODset.MinNofCyc = HdrBuf[70] & 15; + PODset.PeakDetectionMode = (HdrBuf[70] >> 6); + PODset.MaxSPLforReversalCount = toUnsigned(HdrBuf[71]); // ww check these + PODset.BWaddon = toUnsigned(HdrBuf[72]); + + PODset.StrongLimit1 = toUnsigned( HdrBuf[73]); // ? rescale these to true values, as currently on non-linear scale ww + PODset.StrongLimit2 = toUnsigned(HdrBuf[74]); + PODset.StrongLimit3 = toUnsigned(HdrBuf[75]); + PODset.StrongLimit4 = toUnsigned(HdrBuf[76]); + PODset.UsingQtrAmpDrop = (HdrBuf[77] & 1) == 1; + // PODset.MaxSPLforBWtest = HdrBuf[77]; add code re versions before 14 that used this.... ww + PODset.SonarLongNcyc = toUnsigned(HdrBuf[78]); + PODset.SonarFiltering = toUnsigned(HdrBuf[78]) > 0; + PODset.MaxSPLforAmpDropTest = toUnsigned(HdrBuf[79]); + // unused HdrBuf[80]; + PODset.ThermalGainControl = toUnsigned(HdrBuf[81]) == 1; + PODset.BatterySwitchLevel = toUnsigned(HdrBuf[82]); + + PODset.WavMode = tWavMode(HdrBuf[83]); + PODset.WavSNR = toUnsigned(HdrBuf[84]); + PODset.WavPkAddOn = toUnsigned(HdrBuf[85]); + PODset.WavMinPk = toUnsigned(HdrBuf[86]); + PODset.WavMinLimit = (HdrBuf[87] << 3); + PODset.WavTotalLimit = (HdrBuf[88] >> 2); + PODset.WavMinICI = toUnsigned(HdrBuf[89]); + PODset.WavMaxICI = toUnsigned(HdrBuf[90]); + PODset.WavOKseqN = toUnsigned(HdrBuf[91]); + PODset.WavMaxRawPerTrain = toUnsigned(HdrBuf[92]); + PODset.NoisyLevel = ( HdrBuf[93] << 8); + PODset.QuietLevel = ( HdrBuf[94] << 8); + PODset.NoisyMinsLimit = toUnsigned(HdrBuf[95]); + PODset.QuietMinsLimit = toUnsigned(HdrBuf[96]); + + PODset.SwitchNmin = toUnsigned( HdrBuf[98]); + PODset.SwitchNmax = toUnsigned(HdrBuf[100]); + + + if (HdrBuf[101] < 10) { + PODset.ReleaseAfterStr = "200" + Integer.toString(HdrBuf[101]); + } else { + PODset.ReleaseAfterStr = "20" + Integer.toString(HdrBuf[101]); + } + PODset.ReleaseAfterStr += " m" + Integer.toString(HdrBuf[102]) + " d" + Integer.toString(HdrBuf[103]) + " h" + Integer.toString(HdrBuf[104]); + + if (HdrBuf[105] < 10) { + PODset.ReleaseOnStr = "200" + Integer.toString(HdrBuf[105]); + } else { + PODset.ReleaseOnStr = "20" + Integer.toString(HdrBuf[105]); + } + PODset.ReleaseOnStr += " m" + Integer.toString(HdrBuf[106]) + " d" + Integer.toString(HdrBuf[107]) + " h" + Integer.toString(HdrBuf[108]); + + if (HdrBuf[109] < 10) { + PODset.StartOnStr = "200" + Integer.toString(HdrBuf[109]); + } else { + PODset.StartOnStr = "20" + Integer.toString(HdrBuf[109]); + } + PODset.StartOnStr += " m" + Integer.toString(HdrBuf[110]) + " d" + Integer.toString(HdrBuf[111]) + "@00:00"; + + + switch (HdrBuf[112]) { + case 0: + PODset.MinsOFFbetweenON = 0; + break; + case (byte) 128: + PODset.MinsOFFbetweenON = 1; + break; + case (byte) 131: + PODset.MinsOFFbetweenON = 4; + break; + case (byte) 136: + PODset.MinsOFFbetweenON = 9; + break; + default: + PODset.MinsOFFbetweenON = 0; + } + + // ... (more translations) + + PODset.DeploymentDepth = (HdrBuf[129] << 8) + HdrBuf[130]; // pOpenF + PODset.WaterDepth = (HdrBuf[131] << 8) + HdrBuf[132]; + + + // Lat Long and Location are in the same position in all file structures + String tempS = ""; + for (int posn = 133; posn <= 144; posn++) { + if (Character.isLetterOrDigit(HdrBuf[posn])) { + tempS += (char) HdrBuf[posn]; + } + } + PODset.LatText = tempS; + // PODset.LatText = ParseLatLong(tempS, true).Value; // Uncomment if needed + + tempS = ""; + for (int posn = 145; posn <= 156; posn++) { + if (Character.isLetterOrDigit(HdrBuf[posn])) { + tempS += (char) HdrBuf[posn]; + } + } + PODset.LongText = tempS; + // PODset.LongText = ParseLatLong(tempS, false).Value; // Uncomment if needed + + // Extract LocationText, NotesText, and GMTText + for (int posn = 157; posn <= 187; posn++) { + if (Character.isLetterOrDigit(HdrBuf[posn])) { + tempS += (char) HdrBuf[posn]; + } + } + PODset.LocationText = tempS; + + tempS = ""; + for (int posn = 188; posn <= 231; posn++) { + if (Character.isLetterOrDigit(HdrBuf[posn])) { + tempS += (char) HdrBuf[posn]; + } + } + PODset.NotesText = tempS; + + tempS = ""; + for (int posn = 232; posn <= 233; posn++) { + if (Character.isLetterOrDigit(HdrBuf[posn])) { + tempS += (char) HdrBuf[posn]; + } + } + PODset.GMTText = tempS; + + // Combine bytes for NclxInFP1file + PODset.NclxInFP1file = (HdrBuf[235] << 24) | (HdrBuf[236] << 16) | (HdrBuf[237] << 8) | HdrBuf[238]; + + // Create TDAversionStr + PODset.TDAversionStr = Integer.toString(HdrBuf[239]); + if (HdrBuf[240] > 0) { + PODset.TDAversionStr += "." + Integer.toString(HdrBuf[240]); + } + + // // Conditionally create ShortNameV + // if (PODset.FileType == ftFP3) { + // PODset.ShortNameV = PODset/ShortName + "(v" + TDAversionStr + ")"; + // } else { + // PODset. ShortNameV = PODset.ShortName; + // } + + + // Info for warnings + PODset.AllSpHiModRatio = toUnsigned(HdrBuf[241]); + PODset.DolHiModTrnCount = toUnsigned(Expanded(HdrBuf[242])); // Assuming Expanded is defined + PODset.NBHFHiModTrnCount = toUnsigned(Expanded(HdrBuf[243])); + PODset.SonarHiModTrnCount = toUnsigned(Expanded(HdrBuf[244])); + PODset.NBHFmode = toUnsigned(HdrBuf[245]); + PODset.NBHF10to5KHZbelow = toUnsigned(HdrBuf[246]); + PODset.NBHF5to10KHZabove = toUnsigned(HdrBuf[247]); + PODset.NBHFtargetMode = toUnsigned(HdrBuf[248]); + PODset.NBHFdownsweeps = toUnsigned(HdrBuf[249]); + // PODset.DolBadFrac = Expanded(HdrBuf[250]); + // PODset.NBHFBadFrac = Expanded(HdrBuf[251]); + // PODset.SonarBadFrac = Expanded(HdrBuf[252]); + PODset.nLandmarks = toUnsigned(HdrBuf[253]); + + // These are same positions as CPOD files + PODset.FirstLoggedMin = Math.max(Int32FromBytes(HdrBuf, 256, 4), 1); // MoveFilePosToMin exits if it gets a zero + PODset.LastLoggedMin = Math.max(Int32FromBytes(HdrBuf, 260, 4), PODset.FirstLoggedMin); + + + if (PODset.LastLoggedMin == 0 || PODset.LastLoggedMin < PODset.FirstLoggedMin) { + // ShowTimedMessage(5000, 'PODset.LastLoggedMin = 0) or (PODset.LastLoggedMin < PODset.FirstLoggedMin'); // Adjust for Java + } + + PODset.GoToMin = Math.min(Int32FromBytes(HdrBuf, 264, 4), PODset.LastLoggedMin); + PODset.GoToFiveMusec = Int32FromBytes(HdrBuf, 268, 4); + PODset.StoredFirstProcessMin = Math.max(Int32FromBytes(HdrBuf, 272, 4), PODset.FirstLoggedMin); + if (PODset.StoredFirstProcessMin < PODset.FirstLoggedMin || PODset.StoredFirstProcessMin > PODset.LastLoggedMin) { + PODset.StoredFirstProcessMin = PODset.FirstLoggedMin; + } + PODset.StoredLastProcessMin = Math.min(Int32FromBytes(HdrBuf, 276, 4), PODset.LastLoggedMin); + if (PODset.StoredLastProcessMin < PODset.FirstLoggedMin || PODset.StoredLastProcessMin > PODset.LastLoggedMin) { + PODset.StoredLastProcessMin = PODset.LastLoggedMin; + } + PODset.nLoggingMins = (HdrBuf[280] << 24) | (HdrBuf[281] << 16) | (HdrBuf[282] << 8) | HdrBuf[283]; + + return FPOD_HEADER; + } + + /** + * Convert 4 bytes to an Java integer + * @param hdrBuf - a byte array of any length + * @param i - the index to start from + * @param j - the number of bytes to convert to an integer + * @return the integer value + */ + private static int Int32FromBytes(byte[] hdrBuf, int start, int nbytes) { + int ret = 0; + for (int i=start; i + * Note that is pretty opaque what all this means. The important parameters have been commented. + */ + public static class FPODHeader { + + public int FileType; + public int nLoggingMins; + public int StoredLastProcessMin; + public int StoredFirstProcessMin; + public int GoToFiveMusec; + public int GoToMin; + public int LastLoggedMin; + public int FirstLoggedMin; + public short nLandmarks; + public short DolHiModTrnCount; + public short SonarHiModTrnCount; + public short NBHFmode; + public short NBHF10to5KHZbelow; + public short NBHF5to10KHZabove; + public short NBHFdownsweeps; + public short NBHFtargetMode; + public short NBHFHiModTrnCount; + public short AllSpHiModRatio; + public String GMTText; + public String NotesText; + public String LocationText; + public Object ShortNameV; + public String TDAversionStr; + public int NclxInFP1file; + public String LongText; + public String LatText; + public String StartOnStr; + public String ReleaseOnStr; + public int WaterDepth; + public int DeploymentDepth; + public int MinsOFFbetweenON; + public String ReleaseAfterStr; + public short SwitchNmax; + public short SwitchNmin; + public short QuietMinsLimit; + public short NoisyMinsLimit; + public int QuietLevel; + public int NoisyLevel; + public short WavMaxICI; + public short WavOKseqN; + public short WavMaxRawPerTrain; + public short WavMinICI; + public int WavTotalLimit; + public boolean SonarFiltering; + public short WavMinPk; + public int WavMinLimit; + public short WavSNR; + public short WavPkAddOn; + public short WavMode; + public short BatterySwitchLevel; + public boolean ThermalGainControl; + public short MaxSPLforAmpDropTest; + public short SonarLongNcyc; + public boolean UsingQtrAmpDrop; + public short StrongLimit3; + public short StrongLimit4; + public short StrongLimit2; + public short StrongLimit1; + public short BWaddon; + public short MaxSPLforReversalCount; + public int PeakDetectionMode; + public int MinNofCyc; + public short MaxIPI; + public short MinIPI; + public short MinimumCycleAmplitude; + public int FileMinutecountLimit; + public short UTCoffset; + public boolean HasExtendedAmps; + public int FilterKHZ; + public boolean SteeperStepping; + public int FPGAcodeVersion; + public String PICcodeVersionStr; + public String FPGAcodeVersionStr; + public int PODID; + public short PICcodeMajorVersion; + public short RelaysSecs; + public short AcRelSlotLength; + public short AcRelSlotSpacing; + public short AcRelNcyc; + public short ReleaseTone5Min; + public short ReleaseTone5Max; + public short ReleaseTone4Max; + public short ReleaseTone4Min; + public short ReleaseTone2Max; + public short ReleaseTone3Min; + public short ReleaseTone3Max; + public short ReleaseTone1Max; + public short HousingType; + public short ReleaseTone1Min; + public short ReleaseTone2Min; + public short PICtype; + public int PaDataVersion; + public boolean FromWavFileData; + public short FPGAtype; + public short HydrophoneVersion; + public int MainBoardNumber; + public short PreAmpVersion; + public short gain; + public String DateOfCalibrationStr; + + } + + + + public static void BuildSineArray() { + SineArr = new double[2001]; + final double constPiFrac = Math.PI / 1000; + double S; + for (int count = 0; count < 2000; count++) { + S = Math.sin(constPiFrac * count); + SineArr[count] = S; + } + } + + /** + * Scale wave data so it is returned as a double + * @param wavData - the wavdata + * @return the scaled wav data between -1 and 1; + */ + public static double[] scaleWavData(int[] wavData) { + double[] wavArr = new double[wavData.length]; + for (int i=0; i 0 && click.getWavData().getWavValsIPI()[RawStartPtr] < 255) { + click.getWavData().getWavValsSPL()[RawStartPtr] = (short) LinearPkValsArr[click.getWavData().getWavValsSPL()[RawStartPtr]]; + RawStartPtr--; + } + + RawStartPtr = Math.min(21, RawStartPtr + 1); + FirstClkCyc = 21 - click.Ncyc; + + // Construct each cycle in MhzSampledArr + SinePtr = 0; + int MHzArrPtr = 0; + int MaxSPLval = 0; + IPIsum = 0; + + cyc = 21; + + do { + // Populate MhzSampledArr + SinePtsPerUs = Math.round(4000 / click.getWavData().getWavValsIPI()[cyc]); + while (SinePtr <= 1999 && MHzArrPtr < MhzSampledArr.length) { + MhzSampledArr[MHzArrPtr] = (int) Math.round(SineArr[SinePtr] + * click.getWavData().getWavValsSPL()[cyc]); + + // System.out.println( MhzSampledArr[MHzArrPtr] +" index: " + MHzArrPtr + " SinePtr " + SinePtr + " cyc " + cyc); + MHzArrPtr++; + SinePtr += SinePtsPerUs; + } + + if (MHzArrPtr >= MhzSampledArr.length) { + //System.err.println("FPOD Waveform index greater than len? " + MhzSampledArr.length); + break; // Fix: extend this array if needed + } + + SinePtr -= 2000; + if (cyc == FirstClkCyc) { + int StartOfClickHighRes = MHzArrPtr; + } + IPIsum += click.getWavData().getWavValsIPI()[cyc]; + cyc--; + } while (cyc > click.wavData.WavPtr); + + // Bring line up to zero + if (MHzArrPtr < MhzSampledArr.length) { + MhzSampledArr[MHzArrPtr] = 0; + } + + + + int[] waveform = Arrays.copyOf(MhzSampledArr, MHzArrPtr); + //waveform is backwards so flip it. + return PamArrayUtils.flip(waveform); + } + + + /** + * Test the program + * @param args + */ + public static void main(String[] args) { + // String filePath = "/Users/au671271/Library/CloudStorage/GoogleDrive-macster110@gmail.com/My Drive/PAMGuard_dev/CPOD/FPOD_NunBank/0866 NunBankB 2023 06 27 FPOD_6480 file0.FP1"; + +// String filePath = "D:\\DropBox\\PAMGuard_dev\\CPOD\\FPOD_NunBank\\0866 NunBankB 2023 06 27 FPOD_6480 file0.FP1"; + String filePath = "D:\\DropBox\\PAMGuard_dev\\CPOD\\FPOD_NunBank\\0866 NunBankB 2023 06 27 FPOD_6480 file0.FP3"; + + File fpfile = new File(filePath); + + ArrayList fpodData = new ArrayList(); + + try { + importFile( fpfile, fpodData, 0, Integer.MAX_VALUE); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + } + + +} diff --git a/src/cpod/dataPlotFX/CPODDDDataInfo.java b/src/cpod/dataPlotFX/CPODDDDataInfo.java new file mode 100644 index 00000000..e287db6f --- /dev/null +++ b/src/cpod/dataPlotFX/CPODDDDataInfo.java @@ -0,0 +1,107 @@ +package cpod.dataPlotFX; + + + +import PamView.symbol.PamSymbolChooser; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import cpod.CPODClick; +import cpod.FPODReader; +import dataPlotsFX.TDManagedSymbolChooserFX; +import dataPlotsFX.TDSymbolChooserFX; +import dataPlotsFX.clickPlotFX.ClickSymbolChooserFX; +import dataPlotsFX.data.TDDataInfoFX; +import detectionPlotFX.data.DDDataInfo; +import detectionPlotFX.layout.DetectionPlotDisplay; +import detectionPlotFX.projector.DetectionPlotProjector; +import detectionPlotFX.rawDDPlot.RawSpectrumPlot; +import detectionPlotFX.rawDDPlot.RawWaveformPlot; +import javafx.geometry.Side; +import pamViewFX.fxNodes.PamSymbolFX; + +/** + * Data info for showing a CPOD waveform. + * + * @author Jamie Macaulay + * + */ +public class CPODDDDataInfo extends DDDataInfo { + + + public CPODDDDataInfo(PamDataBlock dataBlock, + DetectionPlotDisplay displayPlot) { + super(displayPlot, dataBlock); + + //add the various click plots + super.addDetectionPlot(new RawWaveformPlot(displayPlot)); + super.addDetectionPlot(new CPODSpectrumPlot(displayPlot)); + + super.setCurrentDetectionPlot(0); + } + + @Override + public float getHardSampleRate() { + return FPODReader.FPOD_WAV_SAMPLERATE; + } + + /** + * Plots CPOD waveform spectrum. K + * @author Jamie Macaulay + * + */ + class CPODSpectrumPlot extends RawSpectrumPlot { + + public CPODSpectrumPlot(DetectionPlotDisplay detectionPlotDisplay) { + super(detectionPlotDisplay); + // TODO Auto-generated constructor stub + } + + @Override + public void setupAxis(PamDataUnit data, double sR, DetectionPlotProjector plotProjector) { + super.setupAxis(data, sR, plotProjector); + + + CPODClick click = (CPODClick) data; + if (click.getWaveData()==null) { + return; + } + + double lenMS = (1000.*click.getWaveData()[0].length)/FPODReader.FPOD_WAV_SAMPLERATE; + //set the scroller minimum and maximum + plotProjector.setMinScrollLimit(0); + //need this othewriwse the multiple sample rates relaly screw things up. + plotProjector.setMaxScrollLimit(lenMS); + plotProjector.setEnableScrollBar(true); + + plotProjector.setAxisMinMax(0, 250, Side.BOTTOM); + } + + + @Override + public double getSampleRate(PamDataUnit currentDetection) { + return (double) FPODReader.FPOD_WAV_SAMPLERATE; + } + } + + + /** + * Plots CPOD waveform spectrum. + * @author Jamie Macaulay + * + */ + class CPODWaveformPlot extends RawSpectrumPlot { + + public CPODWaveformPlot(DetectionPlotDisplay detectionPlotDisplay) { + super(detectionPlotDisplay); + } + + @Override + public double getSampleRate(PamDataUnit currentDetection) { + return (double) FPODReader.FPOD_WAV_SAMPLERATE; + } + } + + + + +} diff --git a/src/cpod/dataPlotFX/CPODDPlotProvider.java b/src/cpod/dataPlotFX/CPODDPlotProvider.java new file mode 100644 index 00000000..7e6c1716 --- /dev/null +++ b/src/cpod/dataPlotFX/CPODDPlotProvider.java @@ -0,0 +1,26 @@ +package cpod.dataPlotFX; + + +import PamguardMVC.PamDataBlock; +import cpod.CPODClick; +import cpod.CPODControl2; +import detectionPlotFX.data.DDDataProvider; +import detectionPlotFX.layout.DetectionPlotDisplay; + +public class CPODDPlotProvider extends DDDataProvider { + + + private PamDataBlock cpodDataBlock; + + public CPODDPlotProvider(CPODControl2 dlControl, PamDataBlock parentDataBlock) { + super(parentDataBlock); + this.cpodDataBlock= parentDataBlock; + } + + @Override + public CPODDDDataInfo createDataInfo(DetectionPlotDisplay dddisplay) { + return new CPODDDDataInfo(cpodDataBlock, dddisplay); + } + +} + diff --git a/src/cpod/dataPlotFX/CPODPlotInfoFX.java b/src/cpod/dataPlotFX/CPODPlotInfoFX.java index a9446a5b..5e7a5b50 100644 --- a/src/cpod/dataPlotFX/CPODPlotInfoFX.java +++ b/src/cpod/dataPlotFX/CPODPlotInfoFX.java @@ -3,15 +3,21 @@ package cpod.dataPlotFX; import java.awt.geom.Path2D; import PamController.PamControlledUnit; +import PamUtils.PamArrayUtils; import PamUtils.PamUtils; +import PamView.GeneralProjector; import PamView.HoverData; import PamView.GeneralProjector.ParameterType; import PamView.GeneralProjector.ParameterUnits; +import PamView.symbol.PamSymbolChooser; +import PamView.symbol.PamSymbolManager; import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelector; import cpod.CPODClick; import cpod.CPODClickDataBlock; +import dataPlotsFX.TDManagedSymbolChooserFX; import dataPlotsFX.TDSymbolChooserFX; +import dataPlotsFX.data.TDDataInfoFX; import dataPlotsFX.data.TDScaleInfo; import dataPlotsFX.data.generic.GenericDataPlotInfo; import dataPlotsFX.data.generic.GenericScaleInfo; @@ -22,6 +28,7 @@ import javafx.geometry.Orientation; import javafx.scene.canvas.GraphicsContext; import javafx.scene.paint.Color; import javafx.scene.shape.Polygon; +import pamViewFX.fxNodes.PamSymbolFX; /** * CPOD plot info. @@ -94,6 +101,17 @@ public class CPODPlotInfoFX extends GenericDataPlotInfo { } return cpodSettingsPane; } + + @Override + public TDSymbolChooserFX createSymbolChooser() { + PamSymbolManager symbolManager = getDataBlock().getPamSymbolManager(); + if (symbolManager == null) { + return null; + } + GeneralProjector p = this.getTDGraph().getGraphProjector(); + PamSymbolChooser sc = symbolManager.getSymbolChooser(getTDGraph().getUniqueName(), p); + return new CPODSymbolChooserFX(this, sc, TDSymbolChooserFX.DRAW_SYMBOLS); + } // /** @@ -124,45 +142,98 @@ public class CPODPlotInfoFX extends GenericDataPlotInfo { public Polygon drawDataUnit(int plotNumber, PamDataUnit pamDataUnit, GraphicsContext g, double scrollStart, TDProjectorFX tdProjector, int type) { // if (getScaleInfoIndex()==getScaleInfos().indexOf(frequencyInfo)) drawClickFFT( plotNumber, pamDataUnit,g , scrollStart, tdProjector, type); - //System.out.println("ClickPlotInfo: Draw data unit: " + pamDataUnit.getUID()); //draw special data units. Path2D path2D = null; ParameterType paramType = getCurrentScaleInfo().getDataType(); - - // if (getScaleInfoIndex()==getScaleInfos().indexOf(stemScaleInfo)) - if (getCurrentScaleInfo().getDataType() == ParameterType.AMPLITUDE_STEM){ - //draw on a stem plot. + + switch (getCurrentScaleInfo().getDataType()) { + + case AMPLITUDE_STEM: + case NCYCLES: + case AMPLITUDE_LIN: + case BANDWIDTH: + case FREQUENCY: path2D = drawStemClick( plotNumber, pamDataUnit,g , scrollStart, tdProjector, type, paramType); + break; + default: + return super.drawDataUnit(plotNumber, pamDataUnit, g, scrollStart, tdProjector, type); } - else if (getCurrentScaleInfo().getDataType() == ParameterType.NCYCLES){ - //draw CPOD cycles. - path2D = drawStemClick( plotNumber, pamDataUnit,g , scrollStart, tdProjector, type, paramType); - - } - - else if (getCurrentScaleInfo().getDataType() == ParameterType.AMPLITUDE_LIN){ - //draw cpod SPL. - path2D = drawStemClick( plotNumber, pamDataUnit,g , scrollStart, tdProjector, type, paramType); - } - - else if (getCurrentScaleInfo().getDataType() == ParameterType.BANDWIDTH){ - //draw cpod SPL. - path2D = drawStemClick( plotNumber, pamDataUnit,g , scrollStart, tdProjector, type, paramType); - } - - else return super.drawDataUnit(plotNumber, pamDataUnit, g, scrollStart, tdProjector, type); - //add to hover list if special data units. if (path2D!=null && type!=TDSymbolChooserFX.HIGHLIGHT_SYMBOL && type!=TDSymbolChooserFX.HIGHLIGHT_SYMBOL_MARKED ){ tdProjector.addHoverData(new HoverData(path2D, pamDataUnit, 0, plotNumber)); return null; } - return null; } + + /** + * Draw a frequency line + * @param plotNumber + * @param pamDataUnit + * @param g + * @param scrollStart + * @param tdProjector + * @param type + * @return + */ + @Override + public Polygon drawFrequencyData(int plotNumber, PamDataUnit pamDataUnit, GraphicsContext g, double scrollStart, + TDProjectorFX tdProjector, int type) { + + + g.setLineDashes(null); + g.setLineWidth(2); + + double[] f = pamDataUnit.getFrequency(); + if (f == null) { + return null; + } + if (f.length == 1) { + System.out.println("CPODPlot: Single frequency measure in data unit " + pamDataUnit.toString()); + } + + // draw a frequency box. + double y0 = tdProjector.getYPix(f[0]); + double y1 = tdProjector.getYPix(f[1]); + double x0 = tdProjector.getTimePix(pamDataUnit.getTimeMilliseconds()-scrollStart); + double x1 = tdProjector.getTimePix(pamDataUnit.getEndTimeInMilliseconds()-scrollStart); + + + System.out.println("CPODPlotInfoFX: Draw freq data unit: x: " + x0 + " y " + " y1: " + y1 + " to " + y0 + " cpod_data " + ((CPODClick) pamDataUnit).getBw()); + PamArrayUtils.printArray(f); + + g.setStroke(getSymbolChooser().getPamSymbol(pamDataUnit, type).getLineColor()); + + Color fillCol = getSymbolChooser().getPamSymbol(pamDataUnit, type).getFillColor(); + g.setFill(Color.color(fillCol.getRed(), fillCol.getGreen(), fillCol.getBlue(), 0.4)); //add alpha + + g.strokeLine(x0, y0, x0, y1); + + + //create the polygon. + + + + Path2D path2D= new Path2D.Double(0,1); + + // if (Math.abs(x1-x0)>50) { + // + // System.out.println("Generic Data Plot: " + "x0: " + x0 + " x1: " + x1 + " y1: " + y1 + " y: " + y0); + // } + + path2D.moveTo(x0, y0); + path2D.lineTo(x0, y1); + + + tdProjector.addHoverData(new HoverData(path2D, pamDataUnit, 0, plotNumber)); + + + return null; + } + /** * Specific ways to plot CPOD data as NYCLES, AMPLITUDE_LIN and AMPLITUDE_STEM. * @param plotNumber - the plot on which the FFT is drawing. @@ -206,7 +277,9 @@ public class CPODPlotInfoFX extends GenericDataPlotInfo { int[] chanClick=PamUtils.getChannelArray(pamDataUnit.getChannelBitmap()); int[] chanPlot=PamUtils.getChannelArray(getTDGraph().getCurrentScaleInfo().getPlotChannels()[plotNumber]); - Path2D path2D = new Path2D.Double(0,1); + Path2D path2D = new Path2D.Double(0,1); + + CPODClick clk = ((CPODClick) pamDataUnit); double x1 = 0, y1 = 0, x2 = 0, y2 = 0; for (int i=0; i getSettingsPane(){ - dlControl.getSettingsPane().setParams(dlControl.getCPODParam()); - return dlControl.getSettingsPane(); + cpodControl.getSettingsPane().setParams(cpodControl.getCPODParam()); + return cpodControl.getSettingsPane(); } @Override public void updateParams() { - CPODParams newParams=dlControl.getSettingsPane().getParams(dlControl.getCPODParam()); + CPODParams newParams=cpodControl.getSettingsPane().getParams(cpodControl.getCPODParam()); if (newParams!=null) { - dlControl.setCPODParams(newParams); + cpodControl.setCPODParams(newParams); } //setup the controlled unit. - dlControl.setupControlledUnit(); + cpodControl.setupControlledUnit(); } @Override diff --git a/src/cpod/fx/CPODSettingsPane.java b/src/cpod/fx/CPODSettingsPane.java index 59a6ffde..31c2a37b 100644 --- a/src/cpod/fx/CPODSettingsPane.java +++ b/src/cpod/fx/CPODSettingsPane.java @@ -2,6 +2,7 @@ package cpod.fx; import java.io.File; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import javax.swing.SwingUtilities; @@ -11,8 +12,8 @@ import org.apache.commons.io.FileUtils; import PamController.PamGUIManager; import PamController.SettingsPane; import cpod.CPODControl2; -import cpod.CPODImporter.CPODFileType; import cpod.CPODParams; +import cpod.CPODUtils.CPODFileType; import javafx.application.Platform; import javafx.concurrent.Task; import javafx.geometry.Insets; @@ -64,7 +65,8 @@ public class CPODSettingsPane extends SettingsPane { /** * The extension filter. Holds the types of files that can be imported. */ - private ExtensionFilter extensionFilter; + private FileChooser.ExtensionFilter extensionFilterCPOD; + // private FileChooser.ExtensionFilter extensionFilterFPOD; /** @@ -97,7 +99,7 @@ public class CPODSettingsPane extends SettingsPane { private PamButton importButton; - private List> tasks; + private Task task; private boolean running = false; @@ -110,6 +112,10 @@ public class CPODSettingsPane extends SettingsPane { super(null); this.cpodControl = cpodControl2; + + //define the types of files to be imported ("Note: add FP1 and FP3 here) + extensionFilterCPOD = new ExtensionFilter("CPOD file", "*.cp1", "*.cp3", "*.fp1", "*.fp3"); + //file chooser fileChooser = new FileChooser(); fileChooser.getExtensionFilters().addAll(getExtensionFilters()); @@ -117,40 +123,35 @@ public class CPODSettingsPane extends SettingsPane { //folder chooser folderChooser = new DirectoryChooser(); - //define the types of files to be imported ("Note: add FP1 and FP3 here) - extensionFilter = new ExtensionFilter("CPOD file", "*.cp1", "*.cp3"); pathLabel = new TextField("No classifier file selected"); pathLabel.setEditable(false); - + filesInfoLabel = new Label(); // PamButton browsFileButton = new PamButton("", PamGlyphDude.createPamGlyph(MaterialDesignIcon.FILE_MULTIPLE, PamGuiManagerFX.iconSize)); PamButton browsFileButton = new PamButton("", PamGlyphDude.createPamIcon("mdi2f-file-multiple", PamGuiManagerFX.iconSize)); browsFileButton.setMinWidth(30); - browsFileButton.setTooltip(new Tooltip("Browse to select a CP1 or CP3 file")); + browsFileButton.setTooltip(new Tooltip("Browse to select individual or mutliple CP1 or CP3 files or FP1 or FP3 files")); browsFileButton.setOnAction((action)->{ - List files = fileChooser.showOpenMultipleDialog(this.getFXWindow()); - setFileList(files); + List files = new LinkedList(fileChooser.showOpenMultipleDialog(this.getFXWindow())); + setNewFiles(files); - if (this.files.size()>0) { - importButton.setDisable(false); - } }); - - + + // PamButton browsFolderButton = new PamButton("", PamGlyphDude.createPamGlyph(MaterialDesignIcon.FOLDER, PamGuiManagerFX.iconSize)); PamButton browsFolderButton = new PamButton("", PamGlyphDude.createPamIcon("mdi2f-folder", PamGuiManagerFX.iconSize)); browsFolderButton.setMinWidth(30); - browsFolderButton.setTooltip(new Tooltip("Browse to a folder containg CP1 and CP3 files")); + browsFolderButton.setTooltip(new Tooltip("Browse to a folder contaning CP1 and CP3 or FP1 and FP3 files")); browsFolderButton.setOnAction((action)->{ File file = folderChooser.showDialog(this.getFXWindow()); - setNewFile(file); + setNewFolder(file); }); @@ -168,7 +169,7 @@ public class CPODSettingsPane extends SettingsPane { pathLabel.setMaxWidth(Double.MAX_VALUE); pathLabel.prefHeightProperty().bind(browsFolderButton.heightProperty()); - filesPane.getChildren().addAll(pathLabel, browsFolderButton); + filesPane.getChildren().addAll(pathLabel, browsFileButton, browsFolderButton); //time offset pane. startOffset = new PamSpinner(); @@ -293,56 +294,32 @@ public class CPODSettingsPane extends SettingsPane { private boolean importCPODData() { if (files==null) return false; - System.out.println("Import CPOD data: " + files.size()); //begins the import - this.tasks = this.cpodControl.getCpodImporter().importCPODData(files); + // this.tasks = this.cpodControl.getCpodImporter().importCPODData(files); + this.task = this.cpodControl.importPODData(files); - if (tasks ==null) return false; + if (task ==null) return false; + this.progressBar.setProgress(-1.); - this.progressBar.progressProperty().bind(tasks.get(0).progressProperty()); - this.progressLabel.textProperty().bind(tasks.get(0).messageProperty()); - - //binds the progress bar - imports are - for (int i=0; i{ - this.progressBar.progressProperty().bind(tasks.get(ii).progressProperty()); - this.progressLabel.textProperty().bind(tasks.get(ii).messageProperty()); - }); - - tasks.get(tasks.size()-1).setOnSucceeded((worker)->{ - this.progressBar.progressProperty().bind(tasks.get(ii).progressProperty()); - this.progressLabel.textProperty().bind(tasks.get(ii).messageProperty()); - }); - - } - - - //Will be called if the tasks are cancelled or succeed. - tasks.get(tasks.size()-1).setOnCancelled((worker)->{ - System.out.println("Importing cancelled:"); - - importingFinished(); - }); - - tasks.get(tasks.size()-1).setOnSucceeded((worker)->{ - System.out.println("Importing succeeeded:"); - - importingFinished(); - }); - - tasks.get(tasks.size()-1).setOnFailed((worker)->{ - System.out.println("Importing failed:"); - - importingFinished(); - }); + this.progressBar.progressProperty().bind(task.progressProperty()); + this.progressLabel.textProperty().bind(task.messageProperty()); //run the tasks - this.cpodControl.getCpodImporter().runTasks(tasks); + this.cpodControl.getCpodImporter().runTasks(task); + task.setOnCancelled((a)->{ + importingFinished(); + }); + + task.setOnSucceeded((a)->{ + importingFinished(); + }); + + task.setOnFailed((a)->{ + importingFinished(); + }); return true; } @@ -373,19 +350,13 @@ public class CPODSettingsPane extends SettingsPane { progressBar.setProgress(1.); }); - System.out.println("Importing finished 3:"); - } /* * Stop the import. */ private void stopImport() { - if (tasks!=null) { - for (int i=0; i { //have a look through the files and check that there are some files to import. for (int i=0; i cp1 = (List) FileUtils.listFiles(folder, - new String[]{CPODFileType.values()[i].getText()}, this.subFolder.isSelected()); - System.out.println("Files out: " + cp1); - files.addAll(cp1); + List cp1 = (List) FileUtils.listFiles(folder, + new String[]{CPODFileType.values()[i].getText()}, this.subFolder.isSelected()); + System.out.println("Files out: " + cp1); + files.addAll(cp1); } catch (Exception e) { System.err.println("Current directory does not exist: " + folder ); @@ -428,8 +399,13 @@ public class CPODSettingsPane extends SettingsPane { * new ExtensionFilter("Pytorch Model", "*.pk") * @return a list of extension fitlers for the file dialog. */ - public ExtensionFilter getExtensionFilters(){ - return extensionFilter; + public ArrayList getExtensionFilters(){ + ArrayList filters = new ArrayList(); + filters.add(extensionFilterCPOD); + //don't add an exstra filter - just have all as one - otherwise the user has to change the + //file dialog to FPODs to get it to work. + // filters.add(extensionFilterFPOD); + return filters; } @@ -442,7 +418,7 @@ public class CPODSettingsPane extends SettingsPane { @Override public CPODParams getParams(CPODParams currParams) { - currParams.offlineFolder = currentFolder.getAbsolutePath(); + currParams.offlineFolder = currentFolder==null? null:currentFolder.getAbsolutePath(); currParams.subFolders = subFolder.selectedProperty().get(); try { currParams.startOffset = startOffset.getValue(); @@ -464,14 +440,14 @@ public class CPODSettingsPane extends SettingsPane { timeStretch.getValueFactory().setValue(cpodParams.timeStretch); - setNewFile(cpodParams.offlineFolder==null ? null : new File(cpodParams.offlineFolder)); + setNewFolder(cpodParams.offlineFolder==null ? null : new File(cpodParams.offlineFolder)); } /** * Called whenever there is a new selected file or folder. * @param file - the file label to set. */ - private void setNewFile(File file) { + private void setNewFolder(File file) { if (file==null) { this.pathLabel.setText("No folder or file selected"); @@ -480,17 +456,40 @@ public class CPODSettingsPane extends SettingsPane { this.pathLabel.setText(file.getAbsolutePath()); setFileList(file); - if (files.size()>1) { - this.filesInfoLabel.setText(files.size() + " CPOD files to import"); - } - else { - this.filesInfoLabel.setText(files.size() + " CPOD file to import"); - } + setFileInfoLabel(); } importButton.setDisable(this.files==null || this.files.size()<1); } + /** + * Called whenever there is a new selected file or folder. + * @param file - the file label to set. + */ + private void setNewFiles(List files) { + + if (files==null || files.size()==0) { + this.pathLabel.setText("No folder or file selected"); + } + else { + this.pathLabel.setText(files.get(0).getAbsolutePath()); + + this.files = files; + + setFileInfoLabel(); + } + + importButton.setDisable(this.files==null || this.files.size()<1); + } + + private void setFileInfoLabel() { + if (files.size()>=1) { + this.filesInfoLabel.setText(files.size() + " CPOD files to import"); + } + else { + this.filesInfoLabel.setText(files.size() + " CPOD file to import"); + } + } private CPODParams showWarning(String string) { // TODO Auto-generated method stub diff --git a/src/cpod/logging/CPODClickTrainLogging.java b/src/cpod/logging/CPODClickTrainLogging.java new file mode 100644 index 00000000..7e3ee47a --- /dev/null +++ b/src/cpod/logging/CPODClickTrainLogging.java @@ -0,0 +1,86 @@ +package cpod.logging; + +import java.sql.Types; + +import PamguardMVC.PamDataUnit; +import PamguardMVC.superdet.SuperDetDataBlock; +import cpod.CPODClassification; +import cpod.CPODClassification.CPODSpeciesType; +import cpod.CPODClickTrainDataUnit; +import cpod.CPODControl2; +import cpod.CPODUtils; +import generalDatabase.PamTableDefinition; +import generalDatabase.PamTableItem; +import generalDatabase.SQLTypes; +import generalDatabase.SuperDetLogging; + +public class CPODClickTrainLogging extends SuperDetLogging { + + /** + * The pam table items for saving. + */ + private PamTableItem species, quality, isEcho, cpodFileID; + + private CPODControl2 cpodControl; + + public CPODClickTrainLogging(CPODControl2 cpodControl, SuperDetDataBlock pamDataBlock) { + super(pamDataBlock); + this.cpodControl=cpodControl; + setTableDefinition(createBaseTable()); + } + + + /** + * Create the basic table definition for the group detection. + * @return basic table - annotations will be added shortly ! + */ + public PamTableDefinition createBaseTable() { + + PamTableDefinition tableDef = new PamTableDefinition(cpodControl.getUnitName(), UPDATE_POLICY_OVERWRITE); + tableDef.addTableItem(cpodFileID = new PamTableItem("CPOD_file_ID", Types.INTEGER)); + tableDef.addTableItem(species = new PamTableItem("Species", Types.CHAR, 10)); + tableDef.addTableItem(quality = new PamTableItem("Confidence", Types.INTEGER)); + tableDef.addTableItem(isEcho = new PamTableItem("isEcho", Types.BOOLEAN)); + + return tableDef; + } + + @Override + public void setTableData(SQLTypes sqlTypes, PamDataUnit pamDataUnit) { + CPODClickTrainDataUnit ctDataUnit = (CPODClickTrainDataUnit) pamDataUnit; + if (ctDataUnit.getSubDetectionsCount()>0) { + species.setValue(ctDataUnit.getSpecies().toString()); + quality.setValue(ctDataUnit.getConfidence()); + isEcho.setValue(ctDataUnit.isEcho()); + + } + + } + + @Override + protected CPODClickTrainDataUnit createDataUnit(SQLTypes sqlTypes, long timeMilliseconds, int databaseIndex) { + + + CPODClassification classification = new CPODClassification(); + + String speciesStr = species.getStringValue(); + + CPODSpeciesType speciesType = CPODUtils.getSpecies(speciesStr); + + classification.clicktrainID = cpodFileID.getIntegerValue(); + classification.species = speciesType; + classification.clicktrainID = cpodFileID.getIntegerValue(); + classification.isEcho = isEcho.getBooleanValue(); + + CPODClickTrainDataUnit dataUnit = new CPODClickTrainDataUnit(timeMilliseconds, null, classification); + + return dataUnit; + } + + + public CPODControl2 getClickTrainControl() { + return cpodControl; + } + + +} diff --git a/src/cpod/logging/CPODSubDetLogging.java b/src/cpod/logging/CPODSubDetLogging.java new file mode 100644 index 00000000..811c4192 --- /dev/null +++ b/src/cpod/logging/CPODSubDetLogging.java @@ -0,0 +1,30 @@ +package cpod.logging; + +import PamguardMVC.PamDataUnit; +import cpod.CPODClickTrainDataBlock; +import generalDatabase.PamSubtableDefinition; +import generalDatabase.SQLLogging; +import generalDatabase.SQLTypes; + +/** + * Logs the the children of the click train detection. + * @author Jamie Macaulay + * + */ +public class CPODSubDetLogging extends SQLLogging { + + + + public CPODSubDetLogging(CPODClickTrainLogging clickTrainDetLogging, CPODClickTrainDataBlock clickTRainDaatBlock) { + super(clickTRainDaatBlock); + setTableDefinition(new PamSubtableDefinition(clickTrainDetLogging.getClickTrainControl().getUnitName()+"_Children")); + + } + + @Override + public void setTableData(SQLTypes sqlTypes, PamDataUnit pamDataUnit) { + // TODO Auto-generated method stub + + } + + } \ No newline at end of file diff --git a/src/dataGram/DatagramManager.java b/src/dataGram/DatagramManager.java index 5ae8aa66..d82f79dd 100644 --- a/src/dataGram/DatagramManager.java +++ b/src/dataGram/DatagramManager.java @@ -551,6 +551,8 @@ public class DatagramManager { else { PamController.getInstance().notifyTaskProgress( new SimplePamTaskUpdate("Finished Datagram Mapping", PamTaskUpdate.STATUS_DONE)); + publish(new DatagramProgress(PamTaskUpdate.STATUS_DONE, 1, 1)); + } datagramCreator = null; } diff --git a/src/dataGram/DatagramProgress.java b/src/dataGram/DatagramProgress.java index 0a6287d6..3fedf3b8 100644 --- a/src/dataGram/DatagramProgress.java +++ b/src/dataGram/DatagramProgress.java @@ -77,8 +77,7 @@ public class DatagramProgress extends PamTaskUpdate { } @Override - public double getProgress() { -// System.out.println("DatagramProgress: " + processedUnits + " tot: " + totalUnits); + public double getProgress2() { double progress= ProgressIndicator.INDETERMINATE_PROGRESS; switch(getStatus()) { case DatagramProgress.STATUS_BLOCKCOUNT: @@ -95,13 +94,17 @@ public class DatagramProgress extends PamTaskUpdate { else progress=0; break; } + + //System.out.println("DatagramProgress: " + processedUnits + " tot: " + totalUnits + " progress: " + progress); + return progress; } @Override - public double getProgress2(){ + public double getProgress(){ + //seems like the current point is simply the percentage done... double progress; - if (pointsToUpdate>0) progress=((double)currentPoint)/pointsToUpdate; + if (pointsToUpdate>0) progress=((double)currentPoint)/100.; else progress=0; switch(getStatus()) { case DatagramProgress.STATUS_BLOCKCOUNT: @@ -110,14 +113,16 @@ public class DatagramProgress extends PamTaskUpdate { progress=0; break; case DatagramProgress.STATUS_STARTINGFILE: - progress=((double)currentPoint)/pointsToUpdate; + progress=((double)currentPoint)/100.; break; case DatagramProgress.STATUS_ENDINGFILE: - progress=((double)currentPoint)/pointsToUpdate; + progress=((double)currentPoint)/100.; break; case DatagramProgress.STATUS_UNITCOUNT: break; } + //System.out.println("DatagramProgress2: " + currentPoint + " tot: " + pointsToUpdate + " progress: " + progress); + return progress; } diff --git a/src/dataMap/layoutFX/DataMapPaneFX.java b/src/dataMap/layoutFX/DataMapPaneFX.java index dfcb284a..82834eae 100644 --- a/src/dataMap/layoutFX/DataMapPaneFX.java +++ b/src/dataMap/layoutFX/DataMapPaneFX.java @@ -10,23 +10,33 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.geometry.Side; import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; import pamViewFX.PamGuiManagerFX; import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamButton; +import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxNodes.hidingPane.HidingPane; import pamViewFX.fxNodes.internalNode.PamInternalPane; +import pamViewFX.fxNodes.pamAxis.PamDateAxis; +import pamViewFX.fxStyles.PamStylesManagerFX; +import userDisplayFX.UserDisplayControlFX; import userDisplayFX.UserDisplayNodeFX; import userDisplayFX.UserDisplayNodeParams; /** - * Pane which shows users all data currently stored in binary files and databases within PAMGuard. Also allows for navigation to - * different part of the data time series. + * Pane which shows users all data currently stored in binary files and + * databases within PAMGuard. Also allows for navigation to different part of + * the data time series. + * * @author Jamie Macaulay * */ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { + private static final double HIDE_PANE_WIDTH = 400; + /** * Reference to the data map control. */ @@ -57,7 +67,12 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { */ private ScalePaneFX scalePane; - private PamBorderPane topHolder; + private PamVBox settingsPane; + + /** + * Axis which shows the current dates + */ + private PamDateAxis dateAxis; public DataMapPaneFX(DataMapControl dataMapControl){ this.dataMapControl=dataMapControl; @@ -71,50 +86,55 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { //create all the different panes, summaryPane = new SummaryPaneFX(dataMapControl, this); - summaryPane.getStyleClass().add("pane"); scalePane=new ScalePaneFX(dataMapControl,this); - scalePane.getStyleClass().add("pane"); scrollingDataPanel= new ScrollingDataPaneFX(dataMapControl, this); - //create top section - topHolder=new PamBorderPane(); - topHolder.getStyleClass().add("pane"); - topHolder.setLeft(summaryPane); - topHolder.setRight(scalePane); - topHolder.setPadding(new Insets(10,10,10,10)); - topHolder.setPrefHeight(120); - + //create the setting spane + settingsPane=new PamVBox(); +// settingsPane.getChildren().add(summaryPane); + settingsPane.getChildren().add(scalePane); + settingsPane.setPadding(new Insets(40,10,10,10)); + settingsPane.setPrefWidth(HIDE_PANE_WIDTH); + // //have a horizontal scroll pane // PamScrollPane topScrollHolder=new PamScrollPane(topHolder); // topScrollHolder.setPrefHeight(180); -// topScrollHolder.setVbarPolicy(ScrollBarPolicy.NEVER); - - +// topScrollHolder.setVbarPolicy(ScrollBarPolicy.NEVER) //topHolder.prefHeightProperty().bind(summaryPane.prefHeightProperty()); //hiding summary pane - hidingSummaryPane=new HidingPane(Side.TOP, topHolder, this, false); - hidingSummaryPane.getStyleClass().add("pane"); + hidingSummaryPane=new HidingPane(Side.RIGHT, settingsPane, this, true); hidingSummaryPane.setVisibleImmediatly(false); hidingSummaryPane.showHidePane(true); - hidingSummaryPane.getStylesheets().add(PamController.getInstance().getGuiManagerFX().getPamSettingsCSS()); //style as a settings pane. - + hidingSummaryPane.getStyleClass().add("pane-trans"); + hidingSummaryPane.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); + StackPane.setAlignment(hidingSummaryPane, Pos.TOP_RIGHT); + hidingSummaryPane.setPrefWidth(HIDE_PANE_WIDTH); + //style the show button. showButton=hidingSummaryPane.getShowButton(); - showButton.getStyleClass().add("transparent-button-square"); - showButton.setStyle("-fx-background-radius: 0 0 10 10;"); + showButton.getStyleClass().add("close-button-left"); + showButton.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); -// showButton.setGraphic(PamGlyphDude.createPamGlyph(FontAwesomeIcon.CHEVRON_DOWN, PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize)); - showButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-down", PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize)); - showButton.setPrefWidth(60); - scrollingDataPanel.setTop(showButton); - PamBorderPane.setAlignment(showButton, Pos.TOP_CENTER); + +// showButton.setGraphic(PamGlyphDude.createPamGlyph(FontAwesomeIcon.CHEVRON_DOWN, PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize));\ + showButton.setGraphic( PamGlyphDude.createPamIcon("mdi2c-cog", Color.WHITE, PamGuiManagerFX.iconSize)); + showButton.setPrefHeight(60); + scrollingDataPanel.setRight(showButton); - - this.setTop(hidingSummaryPane); - this.setCenter(scrollingDataPanel); + StackPane.setAlignment(showButton, Pos.CENTER_RIGHT); + + StackPane stackPane = new StackPane(); + stackPane.getChildren().addAll(scrollingDataPanel, hidingSummaryPane, showButton); + + dateAxis = new PamDateAxis(); + dateAxis.setMinHeight(50); + dateAxis.prefWidthProperty().bind(scrollingDataPanel.widthProperty()); + + this.setTop(dateAxis); + this.setCenter(stackPane); } public void newSettings() { @@ -151,7 +171,7 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { public void newDataSources() { scrollingDataPanel.newDataSources(); summaryPane.newDataSources(); - hidingSummaryPane.resetHideAnimation(); + //hidingSummaryPane.resetHideAnimation(); } /** @@ -258,13 +278,6 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { summaryPane.setSelectedDataTime(timeStart, timeEnd); } - /** - * Pane which holds all top controls. Sits within a hiding pane. - * @return the pane which holds top controls and indicators. - */ - public PamBorderPane getTopHolder() { - return topHolder; - } @Override public boolean isMinorDisplay() { @@ -284,4 +297,10 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { } + @Override + public UserDisplayControlFX getUserDisplayControl() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/src/dataMap/layoutFX/DataStreamPaneFX.java b/src/dataMap/layoutFX/DataStreamPaneFX.java index e2c13d04..8d923f0b 100644 --- a/src/dataMap/layoutFX/DataStreamPaneFX.java +++ b/src/dataMap/layoutFX/DataStreamPaneFX.java @@ -17,6 +17,7 @@ import dataPlotsFX.layout.AxisPane; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.geometry.Orientation; +import javafx.geometry.Pos; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.Label; @@ -31,7 +32,9 @@ import PamController.PamController; import PamUtils.PamCalendar; import PamguardMVC.PamDataBlock; import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.pamAxis.PamAxisFX; +import pamViewFX.fxNodes.pamAxis.PamAxisPane; import pamViewFX.fxNodes.utilsFX.ColourArray; public class DataStreamPaneFX extends PamBorderPane { @@ -140,9 +143,10 @@ public class DataStreamPaneFX extends PamBorderPane { * Create pane which holds datasream label and allows the split pane to collapse */ private Pane createTopPane(){ - Pane topPane=new Pane(); - topPane.getStyleClass().add("pane"); + PamHBox topPane=new PamHBox(); + topPane.getStyleClass().add("pane-opaque"); topPane.getChildren().add(new Label(this.dataBlock.getDataName())); + topPane.setAlignment(Pos.CENTER); return topPane; } @@ -200,7 +204,7 @@ public class DataStreamPaneFX extends PamBorderPane { /** * The wheel scroll factor. */ - private double wheelScrollFactor = 0.2; + private double wheelScrollFactor = 0.1; /** * Writable image for 3D datagram. @@ -210,7 +214,7 @@ public class DataStreamPaneFX extends PamBorderPane { /** * Pane which holds the axis. */ - private AxisPane axisPane; + private PamAxisPane axisPane; /** * Pane which holds the plot canvas. @@ -342,7 +346,7 @@ public class DataStreamPaneFX extends PamBorderPane { datastreamAxis.setFractionalScale(true); datastreamAxis.setLogScale(false); - axisPane=new AxisPane(datastreamAxis); + axisPane=new PamAxisPane(datastreamAxis, Orientation.VERTICAL); axisPane.getStyleClass().add("pane"); axisPane.setOrientation(Orientation.VERTICAL); axisPane.setPrefWidth(DataStreamPaneFX.axisPrefWidth); @@ -366,16 +370,24 @@ public class DataStreamPaneFX extends PamBorderPane { if (timeline!=null) timeline.stop(); timeline = new Timeline(new KeyFrame( Duration.millis(tm), - ae -> paintCanvas(0))); + ae -> { +// System.out.println("Paint Canvas zero"); + paintCanvas(0); + })); timeline.play(); return; } lastTime=currentTime; + long time1 = System.currentTimeMillis(); paintPlotCanvas(plotCanvas.getGraphicsContext2D()); paintDrawCanvas(drawCanvas.getGraphicsContext2D()); + long time2 = System.currentTimeMillis(); + + //System.out.println("Paint Canvas: " + this + " " + System.currentTimeMillis() + " " + (time2-time1)); + } /** @@ -593,14 +605,16 @@ public class DataStreamPaneFX extends PamBorderPane { } datagramImage = new WritableImage(imageData.length, imageData[0].length); PixelWriter writableRaster = datagramImage.getPixelWriter(); + g.setFill(Color.LIGHTGRAY); + g.fillRect(0, 0, nXPoints, nYPoints); for (int i = 0; i < nXPoints; i++) { for (int j = 0; j < nYPoints; j++) { y = nYPoints-j-1; if (imageData[i][j] < 0) { - writableRaster.setColor(i,y,Color.LIGHTGRAY); + //writableRaster.setColor(i,y,Color.LIGHTGRAY); } else if (imageData[i][j] == 0) { - writableRaster.setColor(i,y, Color.LIGHTGRAY); + //writableRaster.setColor(i,y, Color.LIGHTGRAY); } else { iCol = (int) (NCOLOURPOINTS * (Math.log(imageData[i][j]) - minMaxValue[0]) / scaleRange); @@ -1028,10 +1042,6 @@ public class DataStreamPaneFX extends PamBorderPane { repaint(ScrollingDataPaneFX.REPAINTMILLIS); //update at 10 frames per second } - private void repaint() { - this.repaint(0); - } - /** * Get the pane which sits at the top of the datagraph and contains a label showing the datablock being displayed. * @return the pane which sits at the top of the datagraph diff --git a/src/dataMap/layoutFX/ScalePaneFX.java b/src/dataMap/layoutFX/ScalePaneFX.java index 021bee8e..49569edb 100644 --- a/src/dataMap/layoutFX/ScalePaneFX.java +++ b/src/dataMap/layoutFX/ScalePaneFX.java @@ -9,11 +9,15 @@ import PamUtils.PamCalendar; import binaryFileStorage.BinaryStore; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; +import javafx.scene.control.Slider; +import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; +import javafx.util.StringConverter; import pamViewFX.PamGuiManagerFX; import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamGridPane; @@ -21,9 +25,11 @@ import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; import pamViewFX.fxNodes.sliders.PamSlider; +import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; /** * Allows uses to change the horizontal and vertical scales on the data map. + * * @author Jamie Macaulay * */ @@ -42,7 +48,7 @@ public class ScalePaneFX extends PamBorderPane { /** * The slider which determines time scale. */ - private PamSlider timeSlider; + private Slider timeSlider; /** * Shows the time scale in pix/hour @@ -57,14 +63,17 @@ public class ScalePaneFX extends PamBorderPane { /** * Check box for log vertical scale. */ - private CheckBox logScaleBox; + private PamToggleSwitch logScaleBox; /** * The chosen time values. */ private double[] timeScaleChoices = DataMapParameters.hScaleChoices; - private Pane datagramPane; + /** + * Combo box holding options to channge the datargam bin size + */ + private ComboBox datagramBox; /** * Holds a list of times for the datagram bin size. @@ -74,7 +83,14 @@ public class ScalePaneFX extends PamBorderPane { /** * Holds everything. */ - private PamHBox holder; + private PamVBox holder; + + /** + * Grid pane with settings for the data scales + */ + private PamGridPane scaleSettingsPane; + + private Label dataGramLabel; @@ -82,9 +98,16 @@ public class ScalePaneFX extends PamBorderPane { this.dataMapControl = dataMapControl; this.dataMapPane = dataMapPane; - holder=new PamHBox(); +// //create the holder pane. +// PamVBox vBoxHolder=new PamVBox(); +// vBoxHolder.setSpacing(5); +// Label title=new Label("Scales"); +// PamGuiManagerFX.titleFont2style(title); +// vBoxHolder.getChildren().addAll(title, controlPane); + + holder=new PamVBox(); holder.setSpacing(20); - holder.getChildren().add(createScalePane()); + holder.getChildren().add(scaleSettingsPane = createScalePane()); this.setCenter(holder); @@ -101,31 +124,28 @@ public class ScalePaneFX extends PamBorderPane { public void checkDataGramPane(){ if (BinaryStore.findBinaryStoreControl()!=null){ DatagramManager dataGramManager=BinaryStore.findBinaryStoreControl().getDatagramManager(); - if (dataGramManager!=null && datagramPane==null) { - datagramPane=createDatagramPane(dataGramManager); - holder.getChildren().add(datagramPane); + if (dataGramManager!=null && datagramBox==null) { + datagramBox=createDatagramPane(dataGramManager); + scaleSettingsPane.add(dataGramLabel= new Label("Datagram bin size"), 0, 2); + scaleSettingsPane.add(dataGramComboBox, 1, 2); } } else { - holder.getChildren().remove(datagramPane); - datagramPane=null; + scaleSettingsPane.getChildren().remove(dataGramLabel); + scaleSettingsPane.getChildren().remove(dataGramComboBox); + + datagramBox=null; } } /** - * Create the pane to reset the datagram pane bin size. + * Create the a datagram combo box to change the size of the datagram * @param dataGramManager - the datagram manager for the current biinary store. - * @return + * @return a combo box with datagram bin sizes. */ - private Pane createDatagramPane(DatagramManager dataGramManager){ + private ComboBox createDatagramPane(DatagramManager dataGramManager){ - - PamGridPane dataGramOptions=new PamGridPane(); - dataGramOptions.setHgap(5); - dataGramOptions.setVgap(5); - - dataGramOptions.add(new Label("Datagram Bin Size"),0,0); dataGramComboBox=new ComboBox(createDurationList(dataGramManager)); dataGramComboBox.getSelectionModel().select(durationToString(dataGramManager.getDatagramSettings().datagramSeconds*1000L)); @@ -145,19 +165,7 @@ public class ScalePaneFX extends PamBorderPane { } }); - dataGramOptions.add(dataGramComboBox,1,0); - - //create the holder pane. - PamVBox vBoxHolder=new PamVBox(); - vBoxHolder.setSpacing(5); - Label title=new Label("Datagram"); -// title.setFont(PamGuiManagerFX.titleFontSize2); - PamGuiManagerFX.titleFont2style(title); - - vBoxHolder.getChildren().addAll(title, dataGramOptions); - - - return vBoxHolder; + return dataGramComboBox; } @@ -182,17 +190,25 @@ public class ScalePaneFX extends PamBorderPane { } - private Node createScalePane() { + private PamGridPane createScalePane() { PamGridPane controlPane=new PamGridPane(); controlPane.setHgap(5); controlPane.setVgap(5); //create time scale controls - controlPane.add(new Label("Time"),0,1); + controlPane.add(new Label("Time window"),0,1); //create time slider - timeSlider=new PamSlider(0, timeScaleChoices.length-1, 1); + timeSlider=new Slider(0, timeScaleChoices.length-1, 1); + timeSlider.setShowTickLabels(true); + timeSlider.setShowTickMarks(true); + timeSlider.setMajorTickUnit(1); + + timeSlider.setLabelFormatter(new ScaleStringConverter()); + +// PamGridPane.setHalignment(timeSlider, Pos.BOTTOM_CENTER); + controlPane.add(timeSlider,1,1); //add listener to time slider to change datamap. timeSlider.valueProperty().addListener((ov, oldVal, newVal)->{ @@ -200,10 +216,11 @@ public class ScalePaneFX extends PamBorderPane { dataMapPane.scaleChanged(); }); - controlPane.add(timeScaleLabel=new Label(""),2,1); + controlPane.add(timeScaleLabel=new Label(""),3,1); //create vertical scale controls - controlPane.add(new Label("Data Counts"),0,0); + controlPane.add(new Label("Data counts"),0,0); + scaleBox=new ComboBox (); scaleBox.getItems().add("No Scaling"); @@ -212,28 +229,37 @@ public class ScalePaneFX extends PamBorderPane { scaleBox.getItems().add("per Hour"); scaleBox.getItems().add("per Day"); controlPane.add(scaleBox,1,0); + scaleBox.setPrefWidth(200); scaleBox.valueProperty().addListener((ov, oldVal, newVal)->{ dataMapPane.scaleChanged(); }); - logScaleBox=new CheckBox("Log Scale"); - logScaleBox.setOnAction((action)->{ + logScaleBox=new PamToggleSwitch("Log Scale"); + logScaleBox.selectedProperty().addListener((obsVal, oldVal, newVal)->{ dataMapPane.scaleChanged(); - });; - controlPane.add(logScaleBox,2,0); + }); - //create the holder pane. - PamVBox vBoxHolder=new PamVBox(); - vBoxHolder.setSpacing(5); - Label title=new Label("Scales"); - PamGuiManagerFX.titleFont2style(title); - vBoxHolder.getChildren().addAll(title, controlPane); + + controlPane.add(logScaleBox,3,0); + return controlPane; + } + + class ScaleStringConverter extends StringConverter { + + @Override + public String toString(Double object) { + return String.valueOf(timeScaleChoices[object.intValue()]); + } + + @Override + public Double fromString(String string) { + return Double.valueOf(string); + } - return vBoxHolder; } /** diff --git a/src/dataMap/layoutFX/ScrollingDataPaneFX.java b/src/dataMap/layoutFX/ScrollingDataPaneFX.java index c518b36d..2faffbdb 100644 --- a/src/dataMap/layoutFX/ScrollingDataPaneFX.java +++ b/src/dataMap/layoutFX/ScrollingDataPaneFX.java @@ -8,28 +8,32 @@ import PamUtils.PamCalendar; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.geometry.Insets; +import javafx.geometry.Side; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.ScrollBar; -import javafx.scene.control.ScrollPane.ScrollBarPolicy; import javafx.scene.paint.Color; -import javafx.scene.text.Text; import javafx.util.Duration; import PamguardMVC.PamDataBlock; import dataMap.DataMapControl; -import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; -import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.PamColorsFX; import pamViewFX.fxNodes.PamScrollPane; -import pamViewFX.fxNodes.sashPane.SashPane; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.pamAxis.PamDateAxis; public class ScrollingDataPaneFX extends PamBorderPane { /** * Standard millis to wait for repaint. + * Do not ake this too high i.e. above 50 or the display gets very jerky */ - public static final long REPAINTMILLIS = 200; + public static final long REPAINTMILLIS = 50; + + /** + * The default expanded hieght for each pane. + */ + private static final int DATASTREAMPANE_HEIGHT = 200; /** * Reference to the DataMapControl. @@ -54,7 +58,7 @@ public class ScrollingDataPaneFX extends PamBorderPane { /** * Split pane whihc holds different graphs. */ - private SashPane dataPanePanes; + private PamVBox dataPanePanes; private ArrayList offlineDataStores; @@ -92,18 +96,10 @@ public class ScrollingDataPaneFX extends PamBorderPane { private PamBorderPane holder; - - /** - * Height at which a DataStreamPaneFX is considered to be 'collapsed' + * Axis which shows the current dates */ - private double collapseHeight=15; - - /* - * Height to expand a split to if the 'expand' button action is used. - */ - private double expandHeight=200; - + private PamDateAxis dateAxis; /** * Constructor for the ScrollingDataPaneFX @@ -131,15 +127,14 @@ public class ScrollingDataPaneFX extends PamBorderPane { //create the split pane to hold the graphs. - dataPanePanes=new SashPane(); - dataPanePanes.setHorizontal(false); + dataPanePanes=new PamVBox(); //dataPanePanes.setOrientation(Orientation.VERTICAL); dataPanePanes.prefWidthProperty().bind(mainScrollPane.widthProperty()); - dataPanePanes.prefHeightProperty().bind(mainScrollPane.heightProperty()); + //dataPanePanes.prefHeightProperty().bind(mainScrollPane.heightProperty()); mainScrollPane.setContent(dataPanePanes); //we have a custom scroll bar for horizontal stuff. - mainScrollPane.setHbarPolicy(ScrollBarPolicy.NEVER); +// mainScrollPane.setHbarPolicy(ScrollBarPolicy.NEVER); // ///TEMP/// // Button buttonTest=new Button("Test Map"); @@ -152,11 +147,11 @@ public class ScrollingDataPaneFX extends PamBorderPane { holder.setCenter(mainScrollPane); holder.setBottom(createScrollBar()); - PamButton test = new PamButton("Test"); - test.setOnAction((action)->{ - updateScrollBar(); - }); - holder.setLeft(test); +// PamButton test = new PamButton("Test"); +// test.setOnAction((action)->{ +// updateScrollBar(); +// }); +// holder.setLeft(test); setupScrollBar(); @@ -166,6 +161,18 @@ public class ScrollingDataPaneFX extends PamBorderPane { notifyScrollChange(300); }); + + dateAxis = new PamDateAxis(); + dateAxis.setAutoRanging(false); + dateAxis.setLabel("Time"); + dateAxis.setSide(Side.TOP); + dateAxis.setAnimated(false); + dateAxis.setMinHeight(50); +// dateAxis.prefWidthProperty().bind(scrollingDataPanel.widthProperty()); +// dateAxis.setStyle("-fx-background-color: ORANGE;"); + dateAxis.setForceZeroInRange(false); + holder.setTop(dateAxis); + return holder; } @@ -208,6 +215,8 @@ public class ScrollingDataPaneFX extends PamBorderPane { notifyScrollChange(); }); + timeScrollBar.setPrefHeight(20); + holder.setCenter(timeScrollBar); holder.setBottom(timeLabelPane); @@ -240,10 +249,13 @@ public class ScrollingDataPaneFX extends PamBorderPane { dataStreamPanels.get(i).scrollChanged(); } settingsStrip.scrollChanged(); + + updateDateAxis(); } Timeline timeline; long lastTime = 0; + /** * Notify all panels and the settings strip that the scroll bar moved - but have a timer to wait to not call too often. */ @@ -263,9 +275,19 @@ public class ScrollingDataPaneFX extends PamBorderPane { } lastTime=currentTime; + + updateDateAxis(); } + private void updateDateAxis() { + calcStartEndMillis(); + dateAxis.setUpperBound(screenEndMillis); + dateAxis.setLowerBound(screenStartMillis); + double[] ticks = dateAxis.recalculateTicks(); +// System.out.println("Ticks: " + (ticks[3]/1000/3600) + "hours"); + dateAxis.setTickUnit(ticks[3]); + } /** * Create the data graphs to go into the pane. @@ -274,7 +296,7 @@ public class ScrollingDataPaneFX extends PamBorderPane { public synchronized int createDataGraphs() { //clear the panes from list and split pane. dataStreamPanels.clear(); - dataPanePanes.getItems().clear(); + dataPanePanes.getChildren().clear(); //now create new set of data stream panes. ArrayList dataBlocks = dataMapControl.getMappedDataBlocks(); @@ -285,65 +307,18 @@ public class ScrollingDataPaneFX extends PamBorderPane { DataStreamPaneFX aStreamPanel; for (int i = 0; i < dataBlocks.size(); i++) { aStreamPanel = new DataStreamPaneFX(dataMapControl, this, dataBlocks.get(i)); - addCollapseButton(aStreamPanel); dataStreamPanels.add(aStreamPanel); + dataStreamPanels.get(i).setMinHeight(DATASTREAMPANE_HEIGHT); //now add to a split pane. //SplitPane.setResizableWithParent(aStreamPanel, true); - dataPanePanes.getItems().add(aStreamPanel); + dataPanePanes.getChildren().add(aStreamPanel); //dataPanePanes.setDividerPosition(0,1.0/dataBlocks.size()); } return dataBlocks.size(); } - /** - * Add a button to the top of a DataStreamPaneFX which allows the pane to collapse inside the split pane. - * @param datastreamPane- the DataStreamPaneFX to add the button to. - */ - private void addCollapseButton(DataStreamPaneFX datastreamPane){ - final CollapseButton collapseButton=new CollapseButton(); - collapseButton.getStyleClass().add("close-button-bottom"); - collapseButton.setOnAction((action)->{ - int index= dataStreamPanels.indexOf(datastreamPane); - if (index>=0){ - if (datastreamPane.getDataGraph().getHeight()<=collapseHeight) { - expandSPlitPane(datastreamPane, true); - } - else { - expandSPlitPane(datastreamPane, false); - } - } - }); - - datastreamPane.getTopPane().getChildren().add(collapseButton); - //set button on the center of the pane. - collapseButton.layoutXProperty().bind(datastreamPane.getTopPane().widthProperty().divide(2)); - - -// PamButton testButton=new PamButton("Test"); -// testButton.setOnAction( (action)->{ -// double[] dividersPos=dataPanePanes.getDividerPositions(); -// for (int i=0; i{ - collapseButton.setCollapseButtonGraphic(collapseButton, newVal.doubleValue()); - //make sure collapse flag is changed if the pane is dragged. - //System.out.println("datastreamPane: " + newVal.doubleValue() + " "+datastreamPane); - if (newVal.doubleValue()>collapseHeight+1){ - datastreamPane.setCollapsed(false); - } - else { - datastreamPane.setCollapsed(true); - } - }); - } - /*** * Get the number of panes which are expanded. * @return the number of expanded panes. @@ -356,113 +331,7 @@ public class ScrollingDataPaneFX extends PamBorderPane { } return nExpanded; } - - /** - * Expanding a split pane is a little involved. You have to resize all the other panes too. - */ - private void expandSPlitPane(DataStreamPaneFX datastreamPane, boolean expand){ - //System.out.println("---------------------"); - - //work out the number of expanded dividers - int nexpanded=getNExpandedPanes(); - if (nexpanded<=1 && !expand) { - //cannot collapse the last pane. - return; - } - - int[] formerWeights=dataPanePanes.getWeights(); - -// for (int i=0; icollapseHeight){ - //collapse graphic - graphic=imageDown; - } - else { - //expand graphic - graphic=imageUp; - } - if (collapseButton.getGraphic()!=graphic) collapseButton.setGraphic(graphic); - - } - } - /** * Called whenever new data sources are added. diff --git a/src/dataMap/layoutFX/SummaryPaneFX.java b/src/dataMap/layoutFX/SummaryPaneFX.java index 7751ba14..7476b81f 100644 --- a/src/dataMap/layoutFX/SummaryPaneFX.java +++ b/src/dataMap/layoutFX/SummaryPaneFX.java @@ -159,10 +159,6 @@ public class SummaryPaneFX extends PamBorderPane { long[] dataExtent; dataStorePane.getChildren().clear(); - - this.setPrefHeight(n*30+40); //set preferred height for hiding pane. - dataMapPaneFX.getTopHolder().setPrefHeight(n*30+40); - for (int i = 0; i < n; i++) { dataStorePane.add( dataNames[i], 0, i); dataStorePane.add( dataStarts[i] , 1, i); diff --git a/src/dataModelFX/DataModelConnectPane.java b/src/dataModelFX/DataModelConnectPane.java index 21883cb6..a7315723 100644 --- a/src/dataModelFX/DataModelConnectPane.java +++ b/src/dataModelFX/DataModelConnectPane.java @@ -174,7 +174,7 @@ public class DataModelConnectPane extends ConnectionPane { PamController pamController = PamController.getInstance(); ArrayList connectionNodes = getAllConnectionNodes(); - System.out.println("checkModulesAdded: Number of controlled units: " + pamController.getNumControlledUnits() ); + //System.out.println("checkModulesAdded: Number of controlled units: " + pamController.getNumControlledUnits() ); for (int i = 0; i <= pamController.getNumControlledUnits(); i++) { checkModuleAdded(pamController.getControlledUnit(i), connectionNodes); } @@ -190,7 +190,7 @@ public class DataModelConnectPane extends ConnectionPane { */ public boolean checkModuleAdded(PamControlledUnit pamControlledUnit, ArrayList connectionNodes) { - System.out.println("datamodelConnectPane: checkModuleAdded: " + pamControlledUnit + " No. nodes: " + connectionNodes.size()); + //System.out.println("datamodelConnectPane: checkModuleAdded: " + pamControlledUnit + " No. nodes: " + connectionNodes.size()); if (pamControlledUnit==null) return false; @@ -204,17 +204,17 @@ public class DataModelConnectPane extends ConnectionPane { ModuleConnectionNode moduleConnectionNode; for (int j = 0; j < connectionNodes.size(); j++) { moduleConnectionNode = (ModuleConnectionNode) connectionNodes.get(j); - System.out.println("Looking for node: " + moduleConnectionNode.getPamControlledUnit()); + //System.out.println("Looking for node: " + moduleConnectionNode.getPamControlledUnit()); if (moduleConnectionNode.getPamControlledUnit()!=null && moduleConnectionNode.getPamControlledUnit().equals(pamControlledUnit)) { - System.out.println("There is already a node for : " + moduleConnectionNode.getPamControlledUnit().getUnitName()); + //System.out.println("There is already a node for : " + moduleConnectionNode.getPamControlledUnit().getUnitName()); return true; } else if (moduleConnectionNode.getConnectionNodeParams()!=null && moduleConnectionNode.getConnectionNodeParams().unitName!=null && moduleConnectionNode.getConnectionNodeParams().unitName.equals(pamControlledUnit.getUnitName())){ //set the pamcontrolled unit reference within the connection node. node. moduleConnectionNode.setPamControlledUnit(pamControlledUnit); - System.out.println("There is already a node waiting for : " + moduleConnectionNode.getPamControlledUnit().getUnitName()); + //System.out.println("There is already a node waiting for : " + moduleConnectionNode.getPamControlledUnit().getUnitName()); if (PamController.getInstance().isInitializationComplete()) { //only to be used on drag and dropped nodes. @@ -224,7 +224,7 @@ public class DataModelConnectPane extends ConnectionPane { } } - System.out.println("Could not find the node: " + pamControlledUnit.getUnitName()); + //System.out.println("Could not find the node: " + pamControlledUnit.getUnitName()); //possibility 3 ModuleConnectionNode newNode; @@ -309,7 +309,7 @@ public class DataModelConnectPane extends ConnectionPane { * Check module connections. Attempts to make the GUI data model reflect the current pamDataModel. For example * use this function if source data is changed in external dialogs. */ - protected void dataModeltoPamModel(){ + public void dataModeltoPamModel(){ dataModelPaneFX.dataModeltoPamModel(); } diff --git a/src/dataModelFX/DataModelModulePane.java b/src/dataModelFX/DataModelModulePane.java index 5c6d4d62..bedd9763 100644 --- a/src/dataModelFX/DataModelModulePane.java +++ b/src/dataModelFX/DataModelModulePane.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import PamController.PamController; import PamModel.PamModuleInfo; -import atlantafx.base.theme.PrimerDark; import dataModelFX.ConnectionNodeParams.PAMConnectionNodeType; import dataModelFX.connectionNodes.ModuleIconFactory; import javafx.beans.property.ObjectProperty; @@ -13,7 +12,6 @@ import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.SnapshotParameters; -import javafx.scene.control.Button; import javafx.scene.control.Tooltip; import javafx.scene.image.ImageView; import javafx.scene.input.ClipboardContent; @@ -29,7 +27,6 @@ import pamViewFX.fxNodes.PamTilePane; import pamViewFX.fxNodes.PamTitledPane; import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxNodes.connectionPane.structures.ConnectionGroupBody; -import pamViewFX.fxNodes.connectionPane.structures.ConnectionStructure.ConnectionStructureType; import pamViewFX.fxStyles.PamStylesManagerFX; import pamViewFX.fxNodes.connectionPane.structures.ExtensionSocketStructure; @@ -75,7 +72,7 @@ public class DataModelModulePane extends PamBorderPane { private PamScrollPane createPane(){ moduleSelectPane=new PamScrollPane(); - moduleSelectPane.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); + moduleSelectPane.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); moduleSelectPane.setPrefWidth(250); moduleSelectPane.getStyleClass().add("scroll-pane-dark"); diff --git a/src/dataModelFX/DataModelPaneFX.java b/src/dataModelFX/DataModelPaneFX.java index 91b4aa20..74635850 100644 --- a/src/dataModelFX/DataModelPaneFX.java +++ b/src/dataModelFX/DataModelPaneFX.java @@ -312,8 +312,8 @@ public class DataModelPaneFX extends PamBorderPane { * reflect the current pamDataModel. For example use this function if source * data is changed in external dialogs. */ - protected void dataModeltoPamModel() { - // System.out.println("************DATAMODELTOPAMMODEL*************"); + public void dataModeltoPamModel() { +// System.out.println("************DATAMODELTOPAMMODEL*************"); if (disableNotification) return; @@ -328,8 +328,8 @@ public class DataModelPaneFX extends PamBorderPane { childNode = (ModuleConnectionNode) dataModelPane.getConnectionNodes().get(i); if (childNode.getPamControlledUnit() == null) continue; - // System.out.println("Children of " + ((ModuleConnectionNode) - // dataModelPane.getConnectionNodes().get(i)).getPamControlledUnit().getUnitType()); + //System.out.println("Children of " + ((ModuleConnectionNode) +// dataModelPane.getConnectionNodes().get(i)).getPamControlledUnit().getUnitType()); checkParentChildConnections(childNode, true); } enableConectionListerner(true); @@ -356,7 +356,7 @@ public class DataModelPaneFX extends PamBorderPane { * modules with dependencies are added. */ protected void pamModeltoDataModel(boolean disconnect) { - // System.out.println("************PAMMODELTODATAMODEL*************"); +// System.out.println("************PAMMODELTODATAMODEL*************"); if (disableNotification) return; disableNotification = true; @@ -367,10 +367,13 @@ public class DataModelPaneFX extends PamBorderPane { */ for (int i = 0; i < this.dataModelPane.getConnectionNodes().size(); i++) { childNode = (ModuleConnectionNode) dataModelPane.getConnectionNodes().get(i); +// System.out.println("Node " + i); + if (childNode.getPamControlledUnit() == null) continue; - // System.out.println("Children of " + ((ModuleConnectionNode) - // dataModelPane.getConnectionNodes().get(i)).getPamControlledUnit().getUnitType()); +// System.out.println("Children of " + ((ModuleConnectionNode) +// dataModelPane.getConnectionNodes().get(i)).getPamControlledUnit().getUnitType()); + checkModuleConnection(childNode); checkParentChildConnections(childNode, !disconnect); } @@ -396,7 +399,7 @@ public class DataModelPaneFX extends PamBorderPane { * error. */ private boolean checkModuleConnection(ModuleConnectionNode childNode) { - + // find all parent nodes. ArrayList parentNodesS = dataModelPane.getParentConnectionNodes(childNode); @@ -413,13 +416,13 @@ public class DataModelPaneFX extends PamBorderPane { } else { - // System.out.println(" CHILD NODE: " - // +childNode.getPamControlledUnit().getUnitName()); - // for (int i=0; i{ - if (this.pamControlledUnit.getPamModuleInfo().canRemove()){ + if (this.pamControlledUnit==null || this.pamControlledUnit.getPamModuleInfo()== null || this.pamControlledUnit.getPamModuleInfo().canRemove()){ connectionPane.removeModuleNode(this); } }); @@ -422,7 +424,7 @@ public class ModuleConnectionNode extends StandardConnectionNode implements PAMC this.holderPane = HolderPane; this.top= top; - this.getStylesheets().add(PamController.getInstance().getGuiManagerFX().getPamSettingsCSS()); + this.getStylesheets().addAll(PamController.getInstance().getGuiManagerFX().getPamSettingsCSS()); showButton=new PamButton(); showButton.setMaxWidth(Double.MAX_VALUE); @@ -620,7 +622,7 @@ public class ModuleConnectionNode extends StandardConnectionNode implements PAMC //add tool tip if (pamControlledUnit!=null){ - //sometimes seems to cause an issue woith dialogs disappearing. + //sometimes seems to cause an issue with dialogs disappearing. // Tooltip tp = new Tooltip(pamControlledUnit.getUnitType()); // tp.getStyleClass().removeAll(tp.getStyleClass()); // Tooltip.install(this, tp); @@ -639,16 +641,24 @@ public class ModuleConnectionNode extends StandardConnectionNode implements PAMC } + Tooltip tooltip = ModuleToolTipFactory.getToolTip(pamControlledUnit); + + if (tooltip!=null) { + Tooltip. install(this, tooltip); + } + } - Node icon = ModuleIconFactory.getInstance(). - getModuleNode(pamControlledUnit.getPamModuleInfo().getClassName()); - if (pamControlledUnit.getPamModuleInfo()!=null && icon!=null){ - StackPane iconPane = new StackPane(icon); - iconPane.setPrefSize(DataModelStyle.iconSize, DataModelStyle.iconSize); - iconPane.setAlignment(Pos.CENTER); - StackPane.setAlignment(iconPane, Pos.CENTER); //make sure the image or node is centered. - this.getConnectionNodeBody().getChildren().add(iconPane); + if (pamControlledUnit.getPamModuleInfo()!=null) { + Node icon = ModuleIconFactory.getInstance(). + getModuleNode(pamControlledUnit.getPamModuleInfo().getClassName()); + if (icon!=null){ + StackPane iconPane = new StackPane(icon); + iconPane.setPrefSize(DataModelStyle.iconSize, DataModelStyle.iconSize); + iconPane.setAlignment(Pos.CENTER); + StackPane.setAlignment(iconPane, Pos.CENTER); //make sure the image or node is centered. + this.getConnectionNodeBody().getChildren().add(iconPane); + } } @@ -965,7 +975,9 @@ public class ModuleConnectionNode extends StandardConnectionNode implements PAMC } - + public DataModelConnectPane getDataModelConnectionPane() { + return this.connectionPane; + } } diff --git a/src/dataModelFX/connectionNodes/ModuleIconFactory.java b/src/dataModelFX/connectionNodes/ModuleIconFactory.java index 8f4ddaab..86e170e4 100644 --- a/src/dataModelFX/connectionNodes/ModuleIconFactory.java +++ b/src/dataModelFX/connectionNodes/ModuleIconFactory.java @@ -2,6 +2,7 @@ package dataModelFX.connectionNodes; import dataModelFX.DataModelStyle; import javafx.scene.Node; +import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.StackPane; @@ -34,7 +35,8 @@ public class ModuleIconFactory { */ public enum ModuleIcon { DATAMAP, NMEA, GPS, MAP, SOUND_AQ, SOUND_OUTPUT, FFT, FILTER, CLICK, CLICK_TRAIN, RECORDER, WHISTLE_MOAN, - NOISE_BAND, NOISE_FILT, DATABASE, BINARY, TIME_DISPLAY, DETECTION_DISPLAY, ARRAY, DEEP_LEARNING + NOISE_BAND, NOISE_FILT, DATABASE, BINARY, TIME_DISPLAY, DETECTION_DISPLAY, ARRAY, DEEP_LEARNING, MATCHED_CLICK_CLASSIFIER, + DECIMATOR, CPOD, TETHYS } /** @@ -43,77 +45,128 @@ public class ModuleIconFactory { * @return the icon for the controlled unit */ public Node getModuleNode(ModuleIcon icon){ + long time1 = System.currentTimeMillis(); + Node iconNode =null; + switch (icon){ case ARRAY: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/array_manager.png"))); +// return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/array_manager.png"))); + iconNode = getSVGIcon("/Resources/modules/Array Icon2.svg",Color.BLACK, 3); + break; case BINARY: // return PamGlyphDude.createModuleGlyph(OctIcon.FILE_BINARY); - return PamGlyphDude.createModuleIcon("mdi2f-file-star-outline"); + iconNode = PamGlyphDude.createModuleIcon("mdi2f-file-star-outline"); + break; case CLICK: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/click.png"))); +// return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/click.png"))); + iconNode = getSVGIcon("/Resources/modules/Click Detector Icon.svg", Color.BLACK, 2); + break; case CLICK_TRAIN: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/clicktrain.png"))); +// return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/clicktrain.png"))); + iconNode = getSVGIcon("/Resources/modules/clicktrain.svg",Color.BLACK, 2); + break; case DATABASE: // return PamGlyphDude.createModuleGlyph(FontAwesomeIcon.DATABASE); - return PamGlyphDude.createModuleIcon("mdi2d-database"); + iconNode = PamGlyphDude.createModuleIcon("mdi2d-database"); + break; case DATAMAP: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/dataMap.png"))); + iconNode = new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/dataMap.png"))); + break; case FFT: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/fft.png"))); +// return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/fft.png"))); + iconNode = getSVGIcon("/Resources/modules/fft.svg",Color.BLACK, 2); + break; case FILTER: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/filters.png"))); +// return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/filters.png"))); + iconNode = getSVGIcon("/Resources/modules/filters.svg",Color.BLACK, 2); + break; case GPS: // return PamGlyphDude.createModuleGlyph(MaterialIcon.GPS_FIXED); - return PamGlyphDude.createModuleIcon("mdi2c-crosshairs-gps"); + iconNode = PamGlyphDude.createModuleIcon("mdi2c-crosshairs-gps"); + break; case MAP: // return PamGlyphDude.createModuleGlyph(MaterialIcon.MAP); - return PamGlyphDude.createModuleIcon("mdi2m-map"); + iconNode = PamGlyphDude.createModuleIcon("mdi2m-map"); + break; case NMEA: - return createNMEASymbol(); + iconNode = createNMEASymbol(); case NOISE_BAND: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/filterdNoiseMeasurementBank.png"))); + iconNode = new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/filterdNoiseMeasurementBank.png"))); + break; case NOISE_FILT: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/filterdNoiseMeasurement.png"))); + iconNode = new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/filterdNoiseMeasurement.png"))); + break; case RECORDER: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/recorder.png"))); + iconNode = new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/recorder.png"))); + break; case SOUND_AQ: - return getSVGIcon("/Resources/modules/noun_Soundwave_1786340.svg"); + iconNode = getSVGIcon("/Resources/modules/noun_Soundwave_1786340.svg"); + break; + case MATCHED_CLICK_CLASSIFIER: + iconNode = getSVGIcon("/Resources/modules/matched_click_classifier.svg",Color.BLACK, 2); + break; //return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/aquisition.png"))); case SOUND_OUTPUT: // return PamGlyphDude.createModuleGlyph(MaterialDesignIcon.HEADPHONES); - return PamGlyphDude.createModuleIcon("mdi2h-headphones"); - //return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/playback.png"))); + iconNode = PamGlyphDude.createModuleIcon("mdi2h-headphones"); + break; +//return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/playback.png"))); case WHISTLE_MOAN: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/whistles.png"))); + iconNode = new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/whistles.png"))); + break; case TIME_DISPLAY: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/timeDisplay.png"))); + iconNode = new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/timeDisplay.png"))); + break; case DETECTION_DISPLAY: - return new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/detectionDisplay.png"))); + iconNode = new ImageView(new Image(getClass().getResourceAsStream("/Resources/modules/detectionDisplay.png"))); + break; case DEEP_LEARNING: - //System.out.println("------GET THE SVG ICON FOR DEEP LEARNING--------"); - return getSVGIcon("/Resources/modules/noun_Deep Learning_2486374.svg"); + iconNode = getSVGIcon("/Resources/modules/noun_Deep Learning_2486374.svg"); + break; + case DECIMATOR: + iconNode = getSVGIcon("/Resources/modules/decimator.svg"); + break; + case CPOD: + iconNode = new Label("CPOD"); //TEMP + break; + case TETHYS: + iconNode = PamGlyphDude.createModuleIcon("file-codemeta"); + break; default: break; - } - return null; + + long time2 = System.currentTimeMillis(); + return iconNode; }; - + /** * Get an SVG icon. * @param resourcePath - the path from the src folder * @return a node for the SVG icon. */ private Node getSVGIcon(String resourcePath) { + return getSVGIcon( resourcePath, Color.BLACK, 1); + } + + + + /** + * Get an SVG icon. + * @param resourcePath - the path from the src folder + * @return a node for the SVG icon. + */ + private Node getSVGIcon(String resourcePath, Color colour, double lineWidth) { try { PamSVGIcon iconMaker= new PamSVGIcon(); - PamSVGIcon svgsprite = iconMaker.create(getClass().getResource(resourcePath).toURI().toURL(), Color.BLACK); - svgsprite.getSpriteNode().setStyle("-fx-text-color: black"); - svgsprite.getSpriteNode().setStyle("-fx-fill: black"); + PamSVGIcon svgsprite = iconMaker.create(getClass().getResource(resourcePath).toURI().toURL(), colour, lineWidth); +// svgsprite.getSpriteNode().setStyle("-fx-text-color: black"); +// svgsprite.getSpriteNode().setStyle("-fx-fill: black"); svgsprite.setFitHeight(DataModelStyle.iconSize-10); svgsprite.setFitWidth(DataModelStyle.iconSize-10); + return svgsprite.getSpriteNode(); } catch (Exception e) { @@ -162,7 +215,7 @@ public class ModuleIconFactory { * @return the module icon enum */ public ModuleIcon getModuleIcon(String className) { - //System.out.println("CLASS NAME: " + className); +// System.out.println("Get module icon: " + className); ModuleIcon icon = null; switch (className) { case "Acquisition.AcquisitionControl": @@ -196,6 +249,7 @@ public class ModuleIconFactory { icon=ModuleIcon.TIME_DISPLAY; break; case "detectionPlotFX.DetectionDisplayControl": + case "detectionPlotFX.DetectionDisplayControl2": icon=ModuleIcon.DETECTION_DISPLAY; break; case "dataMap.DataMapControl": @@ -207,6 +261,18 @@ public class ModuleIconFactory { case "rawDeepLearningClassifier.DLControl": icon=ModuleIcon.DEEP_LEARNING; break; + case "matchedTemplateClassifer.MTClassifierControl": + icon=ModuleIcon.MATCHED_CLICK_CLASSIFIER; + break; + case "decimator.DecimatorControl": + icon=ModuleIcon.DECIMATOR; + break; + case "cpod.CPODControl2": + icon=ModuleIcon.CPOD; + break; + case "MetaDataControl": + icon = ModuleIcon.TETHYS; + break; } return icon; } diff --git a/src/dataModelFX/connectionNodes/ModuleProcessDiagram.java b/src/dataModelFX/connectionNodes/ModuleProcessDiagram.java index 7134e542..7369cb76 100644 --- a/src/dataModelFX/connectionNodes/ModuleProcessDiagram.java +++ b/src/dataModelFX/connectionNodes/ModuleProcessDiagram.java @@ -81,7 +81,7 @@ public class ModuleProcessDiagram extends PamBorderPane { private void createControlDataModel(){ this.setMinWidth(100); this.setPrefWidth(USE_COMPUTED_SIZE); - //this.setStyle("-fx-background-color: -fx-darkbackground"); + this.setStyle("-fx-background-color: -fx-darkbackground"); this.populateDataModel(); } diff --git a/src/dataModelFX/connectionNodes/ModuleToolTipFactory.java b/src/dataModelFX/connectionNodes/ModuleToolTipFactory.java new file mode 100644 index 00000000..43185a4e --- /dev/null +++ b/src/dataModelFX/connectionNodes/ModuleToolTipFactory.java @@ -0,0 +1,16 @@ +package dataModelFX.connectionNodes; + +import PamController.PamControlledUnit; +import javafx.scene.control.Tooltip; + +public class ModuleToolTipFactory { + + public static Tooltip getToolTip(PamControlledUnit pamControlledUnit) { + + if (pamControlledUnit.getPamModuleInfo()==null) return null; + + return new Tooltip(pamControlledUnit.getPamModuleInfo().getToolTipText()); + + } + +} diff --git a/src/dataPlotsFX/TDControl.java b/src/dataPlotsFX/TDControl.java index 543301e3..8125955f 100644 --- a/src/dataPlotsFX/TDControl.java +++ b/src/dataPlotsFX/TDControl.java @@ -13,6 +13,7 @@ import dataPlots.TDParameters; import dataPlotsFX.data.TDDataInfoFX; import dataPlotsFX.layout.TDDisplayFX; import dataPlotsFX.layout.TDGraphFX; +import detectiongrouplocaliser.DetectionGroupSummary; /** * The main class for the TDDisplay. @@ -222,5 +223,13 @@ public abstract class TDControl implements PamSettings { this.uniqueDisplayName = uniqueName; } + /** + * A new detection group has been selected. + * @param detectionGroup - the detection group that has been selected + */ + public void newSelectedDetectionGroup(DetectionGroupSummary detectionGroup, TDGraphFX tdGraph) { + + } + } diff --git a/src/dataPlotsFX/TDControlAWT.java b/src/dataPlotsFX/TDControlAWT.java index f4f4f8ce..e79e87b9 100644 --- a/src/dataPlotsFX/TDControlAWT.java +++ b/src/dataPlotsFX/TDControlAWT.java @@ -85,7 +85,7 @@ public class TDControlAWT extends TDControl implements UserDisplayComponent { Group root= new Group(); Scene scene = new Scene(root, Color.GRAY); scene.getStylesheets().clear(); - scene.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getGUICSS()); + scene.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getGUICSS()); setTDDisplay(new TDDisplayFX(tdControlfx)); root.getChildren().add(getTDDisplay()); diff --git a/src/dataPlotsFX/TDControlFX.java b/src/dataPlotsFX/TDControlFX.java index 089edd93..3f162249 100644 --- a/src/dataPlotsFX/TDControlFX.java +++ b/src/dataPlotsFX/TDControlFX.java @@ -6,12 +6,15 @@ import java.util.ArrayList; import javafx.application.Platform; import javafx.scene.layout.Region; import pamViewFX.fxNodes.internalNode.PamInternalPane; +import userDisplayFX.UserDisplayControlFX; import userDisplayFX.UserDisplayNodeFX; import userDisplayFX.UserDisplayNodeParams; import dataPlotsFX.data.TDDataInfoFX; import dataPlotsFX.data.TDDataProviderFX; import dataPlotsFX.data.TDDataProviderRegisterFX; import dataPlotsFX.layout.TDDisplayFX; +import dataPlotsFX.layout.TDGraphFX; +import detectiongrouplocaliser.DetectionGroupSummary; import PamController.PamController; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -20,6 +23,7 @@ import PamguardMVC.PamObserverAdapter; import PamguardMVC.PamRawDataBlock; /** + * * The controller for the display if the main PAMGuard GUI is in JavaFX mode. * * @author Jamie Macaulay @@ -115,12 +119,12 @@ public class TDControlFX extends TDControl implements UserDisplayNodeFX { System.out.println("TDControldFX: parent datablock null"); return dataBlocks; } + for (int i=0; i dataBlocks){ -// System.out.println("Set parent datablocks: " + dataBlocks.size()); allowProcessNotify=false; while (getUserDisplayProcess().getNumMuiltiplexDataBlocks()>0){ @@ -80,6 +92,7 @@ public class TDDisplayController extends UserDisplayControlFX { //disables notifications. boolean allowProcessNotify=true; + @Override public void notifyModelChanged(int type){ // System.out.println("---------------------------------" ); @@ -145,6 +158,14 @@ public class TDDisplayController extends UserDisplayControlFX { System.out.println(tdPlotProviders.get(i).getDataBlock().getUnitClass()); } } + + /** + * Get the datablock for selected data units from the display. + * @return datablock for selected data units. + */ + public PamDataBlock getDisplayDataBlock() { + return selectedDataUnits; + } } diff --git a/src/dataPlotsFX/TDManagedSymbolChooserFX.java b/src/dataPlotsFX/TDManagedSymbolChooserFX.java index 5dce48fc..b522f90e 100644 --- a/src/dataPlotsFX/TDManagedSymbolChooserFX.java +++ b/src/dataPlotsFX/TDManagedSymbolChooserFX.java @@ -8,7 +8,7 @@ import dataPlotsFX.data.TDDataInfoFX; import pamViewFX.fxNodes.PamSymbolFX; /** - * Default symbol chooser for the display. + * Default symbol chooser for the display. This based on the data blocks symbol chooser. * * @author Jamie Maaulay * @@ -55,7 +55,6 @@ public class TDManagedSymbolChooserFX implements TDSymbolChooserFX{ } else if (type==TDSymbolChooserFX.HIGHLIGHT_SYMBOL) { symbol=ClickSymbolChooserFX.highLightClick; - } return symbol; } diff --git a/src/dataPlotsFX/clickPlotFX/ClickPlotInfoFX.java b/src/dataPlotsFX/clickPlotFX/ClickPlotInfoFX.java index 13a5216c..59924c42 100644 --- a/src/dataPlotsFX/clickPlotFX/ClickPlotInfoFX.java +++ b/src/dataPlotsFX/clickPlotFX/ClickPlotInfoFX.java @@ -517,7 +517,7 @@ public class ClickPlotInfoFX extends TDDataInfoFX { if (pamDataUnit.getSuperDetectionsCount()==0) return null; - ListIterator iterator = this.getPamDataBlock().getListIterator(PamDataBlock.ITERATOR_END); + ListIterator iterator = this.getDataBlock().getListIterator(PamDataBlock.ITERATOR_END); PamDataUnit testDataUnit; while (iterator.hasPrevious()) { diff --git a/src/dataPlotsFX/data/TDDataInfoFX.java b/src/dataPlotsFX/data/TDDataInfoFX.java index ca279fd9..c070ed31 100644 --- a/src/dataPlotsFX/data/TDDataInfoFX.java +++ b/src/dataPlotsFX/data/TDDataInfoFX.java @@ -12,6 +12,7 @@ import javafx.scene.canvas.GraphicsContext; import javafx.scene.shape.Polygon; import PamController.PamController; import PamUtils.Coordinate3d; +import PamUtils.PamCalendar; import PamUtils.PamUtils; import PamView.HoverData; import PamView.GeneralProjector.ParameterType; @@ -373,8 +374,8 @@ public abstract class TDDataInfoFX { */ public void drawAllDataUnits(int plotNumber, GraphicsContext g, double scrollStart, TDProjectorFX tdProjector) { -// System.out.println("Max double value: " + PamCalendar.formatDateTime((long) Double.MAX_EXPONENT)); -// System.out.println("Max long value: " + PamCalendar.formatDateTime((long) Long.MAX_VALUE)); +// System.out.println("Max double value: " + this.getDataName() + PamCalendar.formatDateTime((long) Double.MAX_EXPONENT)); +// System.out.println("Max long value: " + this.getDataName() + PamCalendar.formatDateTime((long) Long.MAX_VALUE)); PamDataUnit dataUnit = null; @@ -391,7 +392,10 @@ public abstract class TDDataInfoFX { // scrollStart = PamCalendar.getTimeInMillis(); //work out start and stop times long loopEnd = (long) (scrollStart + (tdProjector.getVisibleTime() * 1.5)); - long loopStart = (long) (scrollStart - (tdProjector.getVisibleTime() * .5)); + long loopStart = (long) (scrollStart - (tdProjector.getVisibleTime() * 1.5)); + +// System.out.println("Loop start: " + this.getDataName() + PamCalendar.formatDateTime((long) loopStart)); +// System.out.println("Loop end: " + this.getDataName() + PamCalendar.formatDateTime((long) loopEnd)); //find a number close to the index start, ListIterator it = getUnitIterator( loopStart, plotNumber); @@ -402,6 +406,7 @@ public abstract class TDDataInfoFX { dataUnit = it.next(); count++; if (dataUnit!=null && shouldDraw(plotNumber, dataUnit)) { + if (dataUnit.getEndTimeInMilliseconds() < loopStart) { continue; } @@ -409,11 +414,13 @@ public abstract class TDDataInfoFX { break; } + drawCalls++; drawDataUnit(plotNumber, dataUnit, g, scrollStart, tdProjector ,TDSymbolChooserFX.NORMAL_SYMBOL); } } +// System.out.println("Found: " + drawCalls + " " + this.getDataName() + " to draw"); lastUnitDrawn(g, scrollStart, tdProjector, plotNumber); } //System.out.println("Total data units: " + count+ " draw calls: " +drawCalls ); @@ -645,8 +652,8 @@ public abstract class TDDataInfoFX { drawCalls++; if ((symbolchoser.getDrawTypes(pamDataUnit) & TDSymbolChooserFX.DRAW_LINES) != 0) { //WARNING<- the getPreviousDataUnit function is currently very slow and caused serious issue - //in display speed. Onyl use draw lines with super detections if data units are very sparse. - // For example not suitbale for the the click detector. + //in display speed. Only use draw lines with super detections if data units are very sparse. + // For example not suitable for the the click detector. Point2D lastDrawPoint=getPreviousDataUnit(tdProjector, pamDataUnit); if (lastDrawPoint!= null) g.setFill(symbolchoser.getPamSymbol(pamDataUnit,type).getLineColor()); @@ -731,13 +738,6 @@ public abstract class TDDataInfoFX { return false; } - /** - * @return the pamDataBlock - */ - protected PamDataBlock getPamDataBlock() { - return pamDataBlock; - } - /** * @return the tdDataProvider */ diff --git a/src/dataPlotsFX/data/TDDataProviderRegisterFX.java b/src/dataPlotsFX/data/TDDataProviderRegisterFX.java index da0a3beb..8901845e 100644 --- a/src/dataPlotsFX/data/TDDataProviderRegisterFX.java +++ b/src/dataPlotsFX/data/TDDataProviderRegisterFX.java @@ -80,6 +80,7 @@ public class TDDataProviderRegisterFX { */ synchronized public TDDataProviderFX findDataProvider(PamDataBlock pamDataBlock) { for (TDDataProviderFX aProvider:dataProviders) { +// System.out.println("Data providers: " + aProvider.getName() + " | " + aProvider.getDataBlock() + " | " +pamDataBlock); if (aProvider.getDataBlock()==pamDataBlock) { return aProvider; } diff --git a/src/dataPlotsFX/data/generic/GenericDataPlotInfo.java b/src/dataPlotsFX/data/generic/GenericDataPlotInfo.java index f479b86c..eddf4d7b 100644 --- a/src/dataPlotsFX/data/generic/GenericDataPlotInfo.java +++ b/src/dataPlotsFX/data/generic/GenericDataPlotInfo.java @@ -99,6 +99,7 @@ public class GenericDataPlotInfo extends TDDataInfoFX { } else { return super.drawDataUnit(plotNumber, pamDataUnit, g, scrollStart, tdProjector, type); + } } @@ -266,7 +267,7 @@ public class GenericDataPlotInfo extends TDDataInfoFX { return managedSymbolChooser; } - private TDSymbolChooserFX createSymbolChooser() { + public TDSymbolChooserFX createSymbolChooser() { PamSymbolManager symbolManager = getDataBlock().getPamSymbolManager(); if (symbolManager == null) { return null; @@ -293,7 +294,7 @@ public class GenericDataPlotInfo extends TDDataInfoFX { * do some which may be associated with other annotations ? */ protected void updateAvailability() { - LocalisationInfo locInfo = getPamDataBlock().getLocalisationContents(); + LocalisationInfo locInfo = getDataBlock().getLocalisationContents(); bearingScaleInfo.setAvailable(locInfo.hasLocContent(LocContents.HAS_BEARING)); slantScaleInfo.setAvailable(locInfo.hasLocContent(LocContents.HAS_BEARING)); } diff --git a/src/dataPlotsFX/data/generic/GenericLinePlotInfo.java b/src/dataPlotsFX/data/generic/GenericLinePlotInfo.java index affeb7b8..92e19034 100644 --- a/src/dataPlotsFX/data/generic/GenericLinePlotInfo.java +++ b/src/dataPlotsFX/data/generic/GenericLinePlotInfo.java @@ -2,6 +2,7 @@ package dataPlotsFX.data.generic; import PamController.PamController; import PamUtils.Coordinate3d; +import PamUtils.PamCalendar; import PamView.GeneralProjector; import PamView.HoverData; import PamView.symbol.PamSymbolChooser; @@ -23,8 +24,9 @@ import rawDeepLearningClassifier.dataPlotFX.LineInfo; /** - * A Data Info which plots 1D line data i.e. usually used to plot continuous 1D data e.g. deep learning predictions, - * Ishmael data, click trigger data. + * A Data Info which plots 1D line data i.e. usually used to plot continuous 1D + * data e.g. deep learning predictions, Ishmael data, click trigger data. + * * @author Jamie Macaulay * */ @@ -46,21 +48,18 @@ public abstract class GenericLinePlotInfo extends TDDataInfoFX { } - - /* (non-Javadoc) * @see dataPlotsFX.data.TDDataInfoFX#drawDataUnit(int, PamguardMVC.PamDataUnit, javafx.scene.canvas.GraphicsContext, long, dataPlotsFX.projector.TDProjectorFX, int) */ @Override public Polygon drawDataUnit(int plotNumber, PamDataUnit pamDataUnit, GraphicsContext g, double scrollStart, TDProjectorFX tdProjector, int type) { - return drawPredicition(plotNumber, pamDataUnit, g, scrollStart, tdProjector, type); - } + /** - * Get the line data. Each double[] is a seperate line with N evenly spaced data points. + * Get the line data. Each double[] is a separate line with N evenly spaced data points. * @param pamDataUnit - the pam data unit containing the data. * @return the line data. */ @@ -94,18 +93,15 @@ public abstract class GenericLinePlotInfo extends TDDataInfoFX { } //use the center of the window for plotting - double timeMillis = pamDataUnit.getTimeMilliseconds(); + double timeMillis = pamDataUnit.getTimeMilliseconds()+pamDataUnit.getDurationInMilliseconds()/2; double tC=tdProjector.getTimePix(timeMillis-scrollStart); - //draws lines so tc should be some slop in pixels. if (tC < -1000 || tC>tdProjector.getWidth()+1000) { + //System.out.println("Line is outside display " + tC); return null; } - //TODO -must sort out wrap - //dlControl.getDLParams().sampleHop; - double dataPixel; Coordinate3d c; Color color; @@ -127,16 +123,13 @@ public abstract class GenericLinePlotInfo extends TDDataInfoFX { //brighten the colour up. //color = Color.color(color.getRed()*0.8, color.getGreen()*0.8, color.getBlue()*0.8); - - //System.out.println("TDDataInfoFX: tc: "+tC+ " dataUnitTime: "+PamCalendar.formatTime(timeMillis)+" scrollStart: " - //+PamCalendar.formatTime((long) scrollStart)+" (timeMillis-scrollStart)/1000. "+((timeMillis-scrollStart)/1000.)); - - +// System.out.println("TDDataInfoFX: tc: "+tC+ " dataUnitTime: "+PamCalendar.formatTime((long) timeMillis)+" scrollStart: " +// +PamCalendar.formatTime((long) scrollStart)+" (timeMillis-scrollStart)/1000. "+((timeMillis-scrollStart)/1000.)); + c = tdProjector.getCoord3d(timeMillis, detData[i][j], 0); dataPixel = tdProjector.getYPix(detData[i][j]); - if (lastUnits[chan][i]==null) { lastUnits[chan][i] = new Point2D(tC, dataPixel); g.fillOval(tC, dataPixel, 5,5); @@ -146,7 +139,6 @@ public abstract class GenericLinePlotInfo extends TDDataInfoFX { if (tC>lastUnits[chan][i].getX() && (!this.getTDGraph().isWrap() || (tC=0 && lastUnits[chan][i].getX()0)) { //in wrap mode we can get some weird effects with tC co-ordintates. Still have not quite cracked this... - // if (Math.abs(tC - lastUnits[chan][i].getX())>100) { // System.out.println("tC: " + tC + " lastUnits[i].getX(): " + lastUnits[chan][i].getX() // + " " + tdProjector. getTimeAxis().getPosition((timeMillis-scrollStart)/1000.) + " " + tdProjector.getWidth()); @@ -154,12 +146,9 @@ public abstract class GenericLinePlotInfo extends TDDataInfoFX { g.strokeLine(tC, dataPixel, lastUnits[chan][i].getX(), lastUnits[chan][i].getY()); } lastUnits[chan][i] = new Point2D(tC, dataPixel); - } tdProjector.addHoverData(new HoverData(c , pamDataUnit, 0, plotNumber)); - } - //getSymbolChooser().getPamSymbol(pamDataUnit,type).draw(g, new Point2D(tC, dataPixel)); } } @@ -191,7 +180,8 @@ public abstract class GenericLinePlotInfo extends TDDataInfoFX { * @return the color for that prediciton */ public abstract LineInfo getColor(int i); - + + @Override public Double getDataValue(PamDataUnit pamDataUnit) { //this is not used because we have overridden the super drawing class. @@ -205,15 +195,13 @@ public abstract class GenericLinePlotInfo extends TDDataInfoFX { * @param changeType - notification flag. */ public void notifyChange(int changeType) { - //System.out.println("Prediction NOTIFYMODELCHANGED: "); +// System.out.println("Prediction NOTIFYMODELCHANGED: " + changeType); switch (changeType) { case PamController.CHANGED_PROCESS_SETTINGS: lastUnits = new Point2D[PamConstants.MAX_CHANNELS][]; -; break; case PamController.RUN_NORMAL: lastUnits = new Point2D[PamConstants.MAX_CHANNELS][]; - break; case PamController.PAM_STOPPING: lastUnits = new Point2D[PamConstants.MAX_CHANNELS][]; diff --git a/src/dataPlotsFX/layout/TDControlPaneFX.java b/src/dataPlotsFX/layout/TDControlPaneFX.java index b9340535..bd5fd94d 100644 --- a/src/dataPlotsFX/layout/TDControlPaneFX.java +++ b/src/dataPlotsFX/layout/TDControlPaneFX.java @@ -110,7 +110,7 @@ public class TDControlPaneFX extends Pane { this.tdDisplay=tdMainDsiplay; //CSS styling- pane has the standard PAMGUARD settings pane styling. - this.getStylesheets().add(tdMainDsiplay.getCSSSettingsResource()); + this.getStylesheets().addAll(tdMainDsiplay.getCSSSettingsResource()); this.getStyleClass().add("pane"); PamBorderPane mainPane=new PamBorderPane(); diff --git a/src/dataPlotsFX/layout/TDDataSelPaneFX.java b/src/dataPlotsFX/layout/TDDataSelPaneFX.java index 2b1de7d7..8ba6609c 100644 --- a/src/dataPlotsFX/layout/TDDataSelPaneFX.java +++ b/src/dataPlotsFX/layout/TDDataSelPaneFX.java @@ -531,7 +531,7 @@ public class TDDataSelPaneFX extends PamVBox { //makes the arrow black ((Parent) popOver.getSkin().getNode()).getStylesheets() - .add(tdGraph.getTDDisplay().getCSSSettingsResource()); + .addAll(tdGraph.getTDDisplay().getCSSSettingsResource()); // show the graph add and remove menu // this.showingProperty().addListener(new ShowTDGraphMenu(this)); diff --git a/src/dataPlotsFX/layout/TDDisplayFX.java b/src/dataPlotsFX/layout/TDDisplayFX.java index 9c6fc66c..afbcdb52 100644 --- a/src/dataPlotsFX/layout/TDDisplayFX.java +++ b/src/dataPlotsFX/layout/TDDisplayFX.java @@ -242,7 +242,7 @@ public class TDDisplayFX extends PamBorderPane { hidingControlPane=new HidingPane(Side.TOP, controlPane, this, false ); hidingControlPane.showHidePane(tdParametersFX.showControl); - hidingControlPane.getStylesheets().add(this.getCSSSettingsResource()); //style as a settings pane. + hidingControlPane.getStylesheets().addAll(this.getCSSSettingsResource()); //style as a settings pane. hidingControlPane.showingProperty().addListener((valProp, oldVal, newVal)->{ //set correct showing property. tdParametersFX.showControl=newVal; @@ -615,9 +615,10 @@ public class TDDisplayFX extends PamBorderPane { * @param milliSeconds - the master clock position. */ public void scrollDisplayEnd(long milliSeconds) { + - if (!isViewer() && milliSeconds <= lastUpdate && milliSeconds > lastUpdate - tdParametersFX.visibleTimeRange) { - //System.out.println("milliSeconds <= lastUpdate && milliSeconds > lastUpdate - visibleRange"); + if (!isViewer() && lastUpdate>0 && milliSeconds <= lastUpdate && milliSeconds > lastUpdate - tdParametersFX.visibleTimeRange) { +// System.out.println("milliSeconds <= lastUpdate && milliSeconds > lastUpdate - visibleRange"); return; } @@ -849,7 +850,6 @@ public class TDDisplayFX extends PamBorderPane { @Override public void scrollValueChanged(AbstractPamScroller pamScroller) { - // System.out.println("TDDisplayFX: Scroll Value changed: " + System.currentTimeMillis()); // System.out.println(String.format("Scroller value changed get start %s, End %s, pos %s", PamCalendar.formatTime(timeScrollerFX.getMinimumMillis()), // PamCalendar.formatTime(timeScrollerFX.getMaximumMillis()),PamCalendar.formatTime(timeScrollerFX.getValueMillis()))); //have to set the repaint wait millis param to zero or else when scroll bar is moved quickly painting does not occur correctly. @@ -1027,7 +1027,7 @@ public class TDDisplayFX extends PamBorderPane { return tdGraphs; } - public String getCSSSettingsResource() { + public ArrayList getCSSSettingsResource() { return PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS(); } @@ -1183,11 +1183,15 @@ public class TDDisplayFX extends PamBorderPane { break; } + //forces a repaint after any changes. + lastUpdate=-1; + if (tdGraphs != null) { for (TDGraphFX tdg:tdGraphs) { tdg.notifyModelChanged(changeType); } } + } /** diff --git a/src/dataPlotsFX/layout/TDGraphFX.java b/src/dataPlotsFX/layout/TDGraphFX.java index 661e4fc2..a0fa1dc5 100644 --- a/src/dataPlotsFX/layout/TDGraphFX.java +++ b/src/dataPlotsFX/layout/TDGraphFX.java @@ -349,6 +349,7 @@ public class TDGraphFX extends PamBorderPane { // set the default overlay style. setOverlayColour(LIGHT_TD_DISPLAY); + //show the left hiding pane byu default. stackPane.getLeftHidingPane().showHidePane(true); } @@ -417,6 +418,8 @@ public class TDGraphFX extends PamBorderPane { Text chevronRight = null; Text settingsRight = null; switch (displayCol) { + //Note that this is now redundant as the buttons have a dark background - keeping for now just incase. + case LIGHT_TD_DISPLAY: case DARK_TD_DISPLAY: // System.out.println("SET DARK THEME FOR HIDING BUTTONS"); // chevronRight = PamGlyphDude.createPamGlyph(FontAwesomeIcon.CHEVRON_RIGHT, Color.WHITE, @@ -424,13 +427,13 @@ public class TDGraphFX extends PamBorderPane { // settingsRight = PamGlyphDude.createPamGlyph(MaterialIcon.SETTINGS, Color.WHITE, PamGuiManagerFX.iconSize); settingsRight = PamGlyphDude.createPamIcon("mdi2c-cog", Color.WHITE, PamGuiManagerFX.iconSize); break; - case LIGHT_TD_DISPLAY: - //System.out.println("SET LIGHT THEME FOR HIDING BUTTONS"); -// chevronRight = PamGlyphDude.createPamGlyph(FontAwesomeIcon.CHEVRON_RIGHT, PamGuiManagerFX.iconColor, - chevronRight = PamGlyphDude.createPamIcon("mdi2c-chevron-right", PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize); -// settingsRight = PamGlyphDude.createPamGlyph(MaterialIcon.SETTINGS, PamGuiManagerFX.iconColor, - settingsRight = PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize); - break; +// case LIGHT_TD_DISPLAY: +// //System.out.println("SET LIGHT THEME FOR HIDING BUTTONS"); +//// chevronRight = PamGlyphDude.createPamGlyph(FontAwesomeIcon.CHEVRON_RIGHT, PamGuiManagerFX.iconColor, +// chevronRight = PamGlyphDude.createPamIcon("mdi2c-chevron-right", PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize); +//// settingsRight = PamGlyphDude.createPamGlyph(MaterialIcon.SETTINGS, PamGuiManagerFX.iconColor, +// settingsRight = PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize); +// break; default: setOverlayColour(LIGHT_TD_DISPLAY); break; @@ -1121,7 +1124,7 @@ public class TDGraphFX extends PamBorderPane { * */ public synchronized void repaint(long tm, int flag) { - + // clear the current canvas's if (hasCanvas(flag, BASE_CANVAS)) { baseCanvas.getGraphicsContext2D().clearRect(0, 0, baseCanvas.getWidth(), baseCanvas.getHeight()); @@ -1148,16 +1151,18 @@ public class TDGraphFX extends PamBorderPane { boolean hasBase = false; synchronized (dataList) { for (TDDataInfoFX dataInfo : dataList) { + base = false; + if (!dataInfo.isShowing()) { - // System.out.println("dataInfo.isShowing(): " + dataInfo.getDataName()); + //System.out.println("!dataInfo.isShowing(): " + dataInfo.getDataName()); continue; } scaleInfo = dataInfo.getScaleInfo(); if (scaleInfo == null) { - // System.out.println("scale info null " + dataInfo.getDataName() + "index: - // "+dataInfo.getScaleInfoIndex()); +// System.out.println("scale info null " + dataInfo.getDataName() + "index: +// "+dataInfo.getScaleInfoIndex()); continue; } @@ -1180,7 +1185,7 @@ public class TDGraphFX extends PamBorderPane { } ; - // ok so only repaint if we have the right CANVAS + // OK so only repaint if we have the right CANVAS if (base && hasCanvas(flag, BASE_CANVAS)) { paintDataUnits(gc, dataInfo, false); } else if (!base && hasCanvas(flag, FRONT_CANVAS)) { @@ -1284,6 +1289,7 @@ public class TDGraphFX extends PamBorderPane { // PamDataBlock dataBlock = dataInfo.getDataBlock(); // scroll start is the end of the display i.e. the last visible time in the past + // in real time mode. scrollStart = tdDisplay.getTimeScroller().getValueMillisD(); @@ -1521,7 +1527,7 @@ public class TDGraphFX extends PamBorderPane { * @param tm- if within millis of last repaint don't repaint */ public synchronized void repaint(long tm, int flag) { - + // Start of block moved over from the panel repaint(tm) function. long currentTime = System.currentTimeMillis(); if (currentTime - lastTime < tm) { diff --git a/src/dataPlotsFX/layout/TDGraphSettingsPane.java b/src/dataPlotsFX/layout/TDGraphSettingsPane.java index 46c15b6d..b545c654 100644 --- a/src/dataPlotsFX/layout/TDGraphSettingsPane.java +++ b/src/dataPlotsFX/layout/TDGraphSettingsPane.java @@ -71,7 +71,7 @@ public class TDGraphSettingsPane extends DynamicSettingsPane { -// @Override -// public void handle(MouseEvent event) { -//// System.out.println("Mouse Clicked: " + event.toString()); -// overlayMarkers.get(currentMarker).mouseClicked(event); -// } -// } -// -// class MousePressed implements EventHandler { -// @Override -// public void handle(MouseEvent event) { -//// System.out.println("Mouse Pressed: " + event.toString()); -// overlayMarkers.get(currentMarker).mousePressed(event); -// } -// } -// class MouseReleased implements EventHandler { -// @Override -// public void handle(MouseEvent event) { -//// System.out.println("Mouse Released: " + event.toString()); -// overlayMarkers.get(currentMarker).mouseReleased(event); -// } -// } -// class MouseMoved implements EventHandler { -// @Override -// public void handle(MouseEvent event) { -// overlayMarkers.get(currentMarker).mouseMoved(event); -// } -// } -// class MouseDragged implements EventHandler { -// @Override -// public void handle(MouseEvent event) { -// overlayMarkers.get(currentMarker).mouseDragged(event); -// } -// } -// -// class MouseEntered implements EventHandler { -// @Override -// public void handle(MouseEvent event) { -//// System.out.println("Mouse Entered: " + event.toString()); -// overlayMarkers.get(currentMarker).mouseEntered(event); -// } -// } -// -// class MouseExited implements EventHandler { -// @Override -// public void handle(MouseEvent event) { -//// System.out.println("Mouse Exited: " + event.toString()); -// overlayMarkers.get(currentMarker).mouseExited(event); -// } -// } - public ArrayList getOverlayMarkers() { return markerAdapters; diff --git a/src/dataPlotsFX/overlaymark/TDMarkerAdapter.java b/src/dataPlotsFX/overlaymark/TDMarkerAdapter.java index d0d0f696..c10b8c3b 100644 --- a/src/dataPlotsFX/overlaymark/TDMarkerAdapter.java +++ b/src/dataPlotsFX/overlaymark/TDMarkerAdapter.java @@ -2,9 +2,15 @@ package dataPlotsFX.overlaymark; import java.util.List; +import javax.swing.JPopupMenu; + +import PamView.GeneralProjector.ParameterType; +import PamView.paneloverlay.overlaymark.MarkDataSelector; import PamView.paneloverlay.overlaymark.MarkRelationships; import PamView.paneloverlay.overlaymark.OverlayMark; +import PamView.paneloverlay.overlaymark.OverlayMarkObserver; import PamView.paneloverlay.overlaymark.OverlayMarkProviders; +import PamView.paneloverlay.overlaymark.OverlayMarker; import dataPlotsFX.layout.TDGraphFX; import dataPlotsFX.layout.TDGraphFX.TDPlotPane; import detectiongrouplocaliser.DetectionGroupSummary; @@ -30,7 +36,7 @@ public class TDMarkerAdapter extends TDOverlayAdapter { /** * The overlay marker with hooks into other modules which subscribe to the display. */ - private StandardOverlayMarker pamMarker; + private TDMarkerFX pamMarker; public TDMarkerAdapter(TDGraphFX tdGraphFX){ @@ -51,16 +57,20 @@ public class TDMarkerAdapter extends TDOverlayAdapter { MarkRelationships.getInstance().subscribeObservers(pamMarker); } pamMarker.setProjector(tdGraphFX.getGraphProjector()); + + pamMarker.addDetectionGroupListener((detectionGroup)->{ + tdGraphFX.getTDDisplay().getTDControl().newSelectedDetectionGroup(detectionGroup, tdGraphFX); + }); } + + /** * Get the selected detections * @return class containing selected detections */ public DetectionGroupSummary getSelectedDetectionGroup(){ //get the currently selected data units. - - //System.out.println("Get selected detection group: " + pamMarker.getCurrentDetectionGroup()); return pamMarker.getCurrentDetectionGroup(); } diff --git a/src/dataPlotsFX/overlaymark/TDMarkerFX.java b/src/dataPlotsFX/overlaymark/TDMarkerFX.java index 173289e3..86862b59 100644 --- a/src/dataPlotsFX/overlaymark/TDMarkerFX.java +++ b/src/dataPlotsFX/overlaymark/TDMarkerFX.java @@ -1,6 +1,7 @@ package dataPlotsFX.overlaymark; import java.awt.Point; +import java.util.ArrayList; import java.util.List; import javax.swing.JPopupMenu; @@ -36,17 +37,20 @@ public class TDMarkerFX extends StandardOverlayMarker { * The current detection group. */ private DetectionGroupSummary currentDetectionGroup; + + /** + * The detection groups. + */ + public ArrayList detectionGroupListeners = new ArrayList(); /** * The maximum distance a click can be from a data unit to be selected. */ private static double maxMarkDistance=15; - - + public TDMarkerFX(TDGraphFX tdGraphFX) { super(tdGraphFX); - } /* @@ -57,6 +61,23 @@ public class TDMarkerFX extends StandardOverlayMarker { return true; } + /** + * Add the detection group listener. + * @param detectionGroupListener - the detection group listener + */ + public void addDetectionGroupListener(DetectionGroupListener detectionGroupListener) { + detectionGroupListeners.add(detectionGroupListener); + } + + /** + * Notify all listeners that there is a new detection group. + * @param detectionGroup - the detection groups. + */ + private void notifyNewDetectionGroup(DetectionGroupSummary detectionGroup) { + for (DetectionGroupListener aDetectionGroupListener : detectionGroupListeners) { + aDetectionGroupListener.newSelectedGroup(detectionGroup); + } + } /** * Override to allow a highlighting of selected data units. @@ -110,7 +131,7 @@ public class TDMarkerFX extends StandardOverlayMarker { currentDetectionGroup = new DetectionGroupSummary(e, this, this.getCurrentMark(), getSelectedMarkedDataUnits(this.getCurrentMark(), null) ); - + notifyNewDetectionGroup(currentDetectionGroup); // Debug.out.println("TDMarkerFX: Marked data units: " + currentDetectionGroup.getNumDataUnits()); this.setNowMarking(false); @@ -160,6 +181,7 @@ public class TDMarkerFX extends StandardOverlayMarker { //System.out.println("PamMarkerFX: Found a data unit @ distance: " + foundDataUnit.distance + " " // + PamCalendar.formatDateTime(foundDataUnit.dataUnit.getTimeMilliseconds())); currentDetectionGroup = new DetectionGroupSummary(e, this, this.getCurrentMark(), foundDataUnit.dataUnit); + notifyNewDetectionGroup(currentDetectionGroup); return true; } else{ @@ -168,6 +190,7 @@ public class TDMarkerFX extends StandardOverlayMarker { //only do this if the primary mouse button. Otherwise we might be right clicking on a markout out area in which //all saved markes will be removed and the pop up menu will show nothing. So the user can only use the primary button to select data units. currentDetectionGroup =null; + notifyNewDetectionGroup(currentDetectionGroup); } foundDataUnit=null; return false; diff --git a/src/dataPlotsFX/overlaymark/TDOverlayAdapter.java b/src/dataPlotsFX/overlaymark/TDOverlayAdapter.java index 25ebc518..313ed2d7 100644 --- a/src/dataPlotsFX/overlaymark/TDOverlayAdapter.java +++ b/src/dataPlotsFX/overlaymark/TDOverlayAdapter.java @@ -9,7 +9,7 @@ import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.Tooltip; /** - * An overlay adapter essentially allows user mouse and touch interactions with the display. + * An overlay adapter allows user mouse and touch interactions with the display. * * @author Jamie Macaulay * diff --git a/src/dataPlotsFX/overlaymark/menuOptions/CSVExport/CSVExportManager.java b/src/dataPlotsFX/overlaymark/menuOptions/CSVExport/CSVExportManager.java deleted file mode 100644 index 1e64519f..00000000 --- a/src/dataPlotsFX/overlaymark/menuOptions/CSVExport/CSVExportManager.java +++ /dev/null @@ -1,5 +0,0 @@ -package dataPlotsFX.overlaymark.menuOptions.CSVExport; - -public class CSVExportManager { - -} diff --git a/src/dataPlotsFX/overlaymark/menuOptions/CSVExport/CSVExportOverlayMenu.java b/src/dataPlotsFX/overlaymark/menuOptions/CSVExportOverlayMenu.java similarity index 88% rename from src/dataPlotsFX/overlaymark/menuOptions/CSVExport/CSVExportOverlayMenu.java rename to src/dataPlotsFX/overlaymark/menuOptions/CSVExportOverlayMenu.java index d70dd167..0a890053 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/CSVExport/CSVExportOverlayMenu.java +++ b/src/dataPlotsFX/overlaymark/menuOptions/CSVExportOverlayMenu.java @@ -1,8 +1,6 @@ -package dataPlotsFX.overlaymark.menuOptions.CSVExport; +package dataPlotsFX.overlaymark.menuOptions; import PamView.paneloverlay.overlaymark.OverlayMark; -import dataPlotsFX.overlaymark.menuOptions.ExportOverlayMenu; -import dataPlotsFX.overlaymark.menuOptions.OverlayMenuItem; import detectiongrouplocaliser.DetectionGroupSummary; import javafx.scene.control.Labeled; import javafx.scene.control.Tooltip; diff --git a/src/dataPlotsFX/overlaymark/menuOptions/ExportOverlayMenu.java b/src/dataPlotsFX/overlaymark/menuOptions/ExportOverlayMenu.java index dd3e4af1..d26eb63d 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/ExportOverlayMenu.java +++ b/src/dataPlotsFX/overlaymark/menuOptions/ExportOverlayMenu.java @@ -67,7 +67,7 @@ public abstract class ExportOverlayMenu implements OverlayMenuItem { /** - * Allows files to be copied ot clipboard. + * Allows files to be copied to clipboard. * */ public static class FileTransferable implements Transferable { diff --git a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLClickExport.java b/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLClickExport.java deleted file mode 100644 index d86e3d7a..00000000 --- a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLClickExport.java +++ /dev/null @@ -1,50 +0,0 @@ -package dataPlotsFX.overlaymark.menuOptions.MLExport; - -import com.jmatio.types.MLInt32; -import com.jmatio.types.MLInt8; -import com.jmatio.types.MLStructure; - -import PamguardMVC.PamDataUnit; -import clickDetector.ClickDetection; - -/** - * Export a click to a MATLAB structure. - * @author Jamie Macaulay - * - */ -public class MLClickExport extends MLRawExport { - - @Override - public MLStructure addDetectionSpecificFields(MLStructure mlStruct, PamDataUnit dataUnit, int index) { - - mlStruct = super.addDetectionSpecificFields(mlStruct, dataUnit, index); - - ClickDetection clickDetection = (ClickDetection) dataUnit; - - //the trigger map - MLInt32 triggerMap = new MLInt32(null, new Integer[]{clickDetection.getTriggerList()}, 1); - - MLInt8 type = new MLInt8(null, new Byte[]{clickDetection.getClickType()}, 1); - - - MLInt32 flag = new MLInt32(null, new Integer[]{clickDetection.getClickFlags()},1); - - //add to the structure. - mlStruct.setField("triggerMap", triggerMap, index); - mlStruct.setField("type", type, index); - mlStruct.setField("flag", flag, index); - - return mlStruct; - } - - @Override - public Class getUnitClass() { - return ClickDetection.class; - } - - @Override - public String getName() { - return "clicks"; - } - -} diff --git a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLDataUnitExport.java b/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLDataUnitExport.java deleted file mode 100644 index 304d37d6..00000000 --- a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLDataUnitExport.java +++ /dev/null @@ -1,115 +0,0 @@ -package dataPlotsFX.overlaymark.menuOptions.MLExport; - -import com.jmatio.types.MLDouble; -import com.jmatio.types.MLInt32; -import com.jmatio.types.MLInt64; -import com.jmatio.types.MLStructure; - -import PamUtils.PamCalendar; -import PamguardMVC.PamDataUnit; - -/** - * Export a detection to MATLAB - * @author Jamie Macaulay - * - */ -public abstract class MLDataUnitExport> { - - /** - * Annotations manager for nadling adding annotations to data units. - */ - private MLAnnotationsManager mlAnnotationsManager; - - public MLDataUnitExport() { - this.mlAnnotationsManager = new MLAnnotationsManager(); - } - - /** - * Create a MATLAB structure which contains all information for a data unit. - * @param dataUnit - the data unit to convert to a MATLAB structure - * @return detection data MATLAB structure ready to be exported to a .mat file or added to a ArrayList. - */ - public MLStructure detectionToStruct(MLStructure mlStruct, T dataUnit, int index){ - - //all the default stuff every data unit has. - MLInt64 millis = new MLInt64(null, new Long[]{dataUnit.getTimeMilliseconds()}, 1); - - //the channel bit map. - MLInt32 channelMap = new MLInt32(null, new Integer[]{dataUnit.getChannelBitmap()}, 1); - - //the sequence map, or 0 if there is no sequence number - MLInt32 sequenceMap = new MLInt32(null, new Integer[]{0}, 1); - if (dataUnit.getSequenceBitmapObject()!=null) { - sequenceMap = new MLInt32(null, new Integer[]{dataUnit.getSequenceBitmapObject()},1); - } - - //UID for the detection. - MLInt64 UID = new MLInt64(null, new Long[]{dataUnit.getUID()}, 1); - - //the start sample. - MLInt64 startSample = new MLInt64(null, new Long[]{dataUnit.getStartSample()}, 1); - - //the duration of the detection in samples. - MLInt64 sampleDuration = new MLInt64(null, new Long[]{dataUnit.getSampleDuration()},1); - - //the frequency limits. - MLDouble freqLimits = new MLDouble(null, dataUnit.getBasicData().getFrequency(), 1); - - //create the structure. - mlStruct.setField("millis", millis, index); - mlStruct.setField("channelMap", channelMap, index); - mlStruct.setField("sequenceMap", sequenceMap, index); - mlStruct.setField("UID", UID, index); - mlStruct.setField("startSample", startSample, index); - mlStruct.setField("sampleDuration", sampleDuration, index); - mlStruct.setField("freqLimits", freqLimits, index); - - //there may be no delay info - if (dataUnit.getBasicData().getTimeDelaysSeconds()!=null && dataUnit.getBasicData().getTimeDelaysSeconds().length>=1){ - MLInt32 numTimeDelays = new MLInt32(null, new Integer[]{dataUnit.getBasicData().getTimeDelaysSeconds().length}, 1); - MLDouble timeDelays = new MLDouble(null, dataUnit.getBasicData().getTimeDelaysSeconds(), 1); - mlStruct.setField("numTimeDelays", numTimeDelays, index); - mlStruct.setField("timeDelays", timeDelays, index); - } - else { - mlStruct.setField("numTimeDelays", new MLInt32(null, new Integer[]{0},1), index); - //mlStruct.setField("timeDelays", new MLEmptyArray(), index); //22-02-2018 - this caused an exception when saving... - mlStruct.setField("timeDelays", new MLInt32(null, new Integer[]{0},1), index); - } - - //MATLAB date number. - double datenumMT = PamCalendar.millistoDateNum(dataUnit.getTimeMilliseconds()); - MLDouble date = new MLDouble(null, new Double[]{datenumMT}, 1); - - mlStruct.setField("date", date, index); - - //add detection specific data - mlStruct= addDetectionSpecificFields(mlStruct, dataUnit, index); - - this.mlAnnotationsManager.addAnnotations(mlStruct, dataUnit); - - return mlStruct; - } - - /** - *Add detection specific fields to a structure. - *@param structure containing all generic info from PamDataUnit - *@param the data unit. - */ - public abstract MLStructure addDetectionSpecificFields(MLStructure mlStruct, T dataUnit, int index); - - /** - * Get the unit class - * @return - */ - public abstract Class getUnitClass(); - - /** - * Get the name of the structure - * @return - */ - public abstract String getName(); - - - -} diff --git a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLDetectionsManager.java b/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLDetectionsManager.java deleted file mode 100644 index 5f117d0f..00000000 --- a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLDetectionsManager.java +++ /dev/null @@ -1,121 +0,0 @@ -package dataPlotsFX.overlaymark.menuOptions.MLExport; - -import java.util.ArrayList; -import java.util.List; - -import com.jmatio.types.MLArray; -import com.jmatio.types.MLDouble; -import com.jmatio.types.MLStructure; - -import PamguardMVC.PamDataUnit; - - -/** - * Handles the conversion of data units into MATLAB structures. - * @author Jamie Macaulay - * - */ -public class MLDetectionsManager { - - /** - * - * All the possible MLDataUnitExport export classes. - */ - ArrayList mlDataUnitsExport = new ArrayList(); - - - public MLDetectionsManager(){ - mlDataUnitsExport.add(new MLClickExport()); - mlDataUnitsExport.add(new MLWhistleMoanExport()); - mlDataUnitsExport.add(new MLRawExport()); - - } - - /** - * Check whether there are compatible data units to be exported. - * @param dataUnits - the data unit list - * @return true if MATLAB export is possible for the current data units. - */ - public boolean hasCompatibleUnits(List dataUnits) { - //first need to figure out how many data units there are. - for (int j=0; j dataUnits2MAT(ArrayList dataUnits){ - - //ArrayList> struct = new ArrayList>(); - //if there's a mixed bunch of data units then we want separate arrays of structures. So a structure of arrays of structures. - //so, need to sort compatible data units. - - ArrayList list = new ArrayList(); - - //keep a track of the data units that have been transcribed. This means data units that are multiple types - //(e.g. a raw data holder and click) are not added to two different list of structures. - boolean[] alreadyStruct = new boolean[dataUnits.size()]; - - //iterate through possible export functions. - for (int i=0; i=1) { - list.add(mlStructure); - list.add(new MLDouble((mlDataUnitsExport.get(i).getName()+"_sR"), new double[] {sampleRate}, 1)); - } - - - } - - - - //now ready to save. - return list; - - } - - -} diff --git a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLRawExport.java b/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLRawExport.java deleted file mode 100644 index 9d65aa98..00000000 --- a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLRawExport.java +++ /dev/null @@ -1,70 +0,0 @@ -package dataPlotsFX.overlaymark.menuOptions.MLExport; - -import com.jmatio.types.MLDouble; -import com.jmatio.types.MLInt32; -import com.jmatio.types.MLInt64; -import com.jmatio.types.MLStructure; - -import PamUtils.PamUtils; -import PamguardMVC.PamDataUnit; -import PamguardMVC.RawDataHolder; - -/** - * Export data for any data unit which implements raw data holder. - * @author au671271 - * - */ -public class MLRawExport extends MLDataUnitExport{ - - @Override - public MLStructure addDetectionSpecificFields(MLStructure mlStruct, PamDataUnit dataUnit, int index) { - - RawDataHolder rawDataHolder = (RawDataHolder) dataUnit; - - //the waveform - MLDouble wave = new MLDouble(null, rawDataHolder.getWaveData()); - - - //the number of channels - MLInt32 nChan = new MLInt32(null, new Integer[]{PamUtils.getNumChannels(dataUnit.getChannelBitmap())}, 1); - - //the duration - repeat of duration in main data unit. Keeping here so strcut is the same as the struct from binary files - MLInt64 duration = new MLInt64(null, new Long[]{dataUnit.getSampleDuration()}, 1); - - mlStruct.setField("nChan", nChan, index); - mlStruct.setField("duration", duration, index); - mlStruct.setField("wave", wave, index); - - MLDouble angles; - MLDouble angleErrors; - if (dataUnit.getLocalisation()!=null) { - //bearing angles - angles = new MLDouble(null, dataUnit.getLocalisation().getAngles(), 1); - //angle errors - angleErrors = new MLDouble(null, dataUnit.getLocalisation().getAngleErrors(), 1); - } - else { - //bearing angles - angles = new MLDouble(null, new double[] {0}, 1); - //angle errors - angleErrors = new MLDouble(null, new double[] {0}, 1); - } - - mlStruct.setField("angles", angles, index); - mlStruct.setField("angleErrors", angleErrors, index); - - - return mlStruct; - } - - @Override - public Class getUnitClass() { - return RawDataHolder.class; - } - - @Override - public String getName() { - return "raw_data_units"; - } - -} diff --git a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLExportOverlayMenu.java b/src/dataPlotsFX/overlaymark/menuOptions/MLExportOverlayMenu.java similarity index 75% rename from src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLExportOverlayMenu.java rename to src/dataPlotsFX/overlaymark/menuOptions/MLExportOverlayMenu.java index f185529f..d0ace300 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLExportOverlayMenu.java +++ b/src/dataPlotsFX/overlaymark/menuOptions/MLExportOverlayMenu.java @@ -1,27 +1,28 @@ -package dataPlotsFX.overlaymark.menuOptions.MLExport; +package dataPlotsFX.overlaymark.menuOptions; import java.io.File; import java.util.ArrayList; import detectiongrouplocaliser.DetectionGroupSummary; +import export.MLExport.MLDetectionsManager; import javafx.scene.control.Labeled; import javafx.scene.control.Tooltip; import javafx.scene.image.ImageView; -import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxGlyphs.PamSVGIcon; import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.format.Mat5File; +import us.hebi.matlab.mat.types.Struct; import java.io.IOException; import javax.swing.filechooser.FileSystemView; -import com.jmatio.io.MatFileWriter; -import com.jmatio.types.MLArray; - import PamUtils.PamCalendar; import PamView.paneloverlay.overlaymark.OverlayMark; import PamguardMVC.PamDataUnit; -import dataPlotsFX.overlaymark.menuOptions.ExportOverlayMenu; /** * Export to MATLAB .mat files menu. @@ -70,9 +71,8 @@ public class MLExportOverlayMenu extends ExportOverlayMenu { private PamButton createButton() { PamButton button = new PamButton(""); - if (svgsprite==null) { - try { - +// if (svgsprite==null) { +// try { // // // load the svg file // InputStream svgFile = @@ -84,20 +84,21 @@ public class MLExportOverlayMenu extends ExportOverlayMenu { // // button.setGraphic(svgImage); - PamSVGIcon svgIconMaker = new PamSVGIcon(); +// PamSVGIcon svgIconMaker = new PamSVGIcon(); +// svgsprite = svgIconMaker.create(getClass().getResource("/Resources/matlab_icon2.svg").toURI().toURL(), Color.WHITE, 1); +//// "-fx-fill: white; -fx-stroke-width: 2;"); +// svgsprite.setFitHeight(standardIconSize+7); +// svgsprite.setFitWidth(standardIconSize+7); + + Text matlabIcon=PamGlyphDude.createPamIcon("file-matlab", standardIconSize+7); - - svgsprite = svgIconMaker.create(getClass().getResource("/Resources/matlab_icon2.svg").toURI().toURL(), Color.WHITE); -// "-fx-fill: white; -fx-stroke-width: 2;"); - svgsprite.setFitHeight(standardIconSize+7); - svgsprite.setFitWidth(standardIconSize+7); - button.setGraphic(svgsprite.getSpriteNode()); + button.setGraphic(matlabIcon); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } +// } catch (Exception e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// } return button; } @@ -119,8 +120,8 @@ public class MLExportOverlayMenu extends ExportOverlayMenu { dataUnits.add(fnDataUnit); } - ArrayList mlData=mlDetectionsManager.dataUnits2MAT(dataUnits); - if (mlData==null || mlData.size()==0){ + Struct mlData=mlDetectionsManager.dataUnits2MAT(dataUnits); + if (mlData==null || mlData.getNumRows()==0){ //do nothing System.out.println("MLExportOverlayMenu: no data units were converted to structs"); } @@ -140,8 +141,8 @@ public class MLExportOverlayMenu extends ExportOverlayMenu { long millisStart=foundDataUnits.getFirstTimeMillis(); String currentPath = PamCalendar.formatFileDateTime(millisStart, false); //add data types to the filename - for (int i=0 ;i(superDets)); } diff --git a/src/dataPlotsFX/overlaymark/popUpMenu/TDMenuPane.java b/src/dataPlotsFX/overlaymark/popUpMenu/TDMenuPane.java index ff2df930..342c04ea 100644 --- a/src/dataPlotsFX/overlaymark/popUpMenu/TDMenuPane.java +++ b/src/dataPlotsFX/overlaymark/popUpMenu/TDMenuPane.java @@ -11,8 +11,8 @@ import dataPlotsFX.layout.TDGraphFX; import dataPlotsFX.layout.TDGraphFX.TDPlotPane; import dataPlotsFX.overlaymark.menuOptions.OverlayMenuItem; import dataPlotsFX.overlaymark.menuOptions.OverlayMenuManager; -import dataPlotsFX.overlaymark.menuOptions.wavExport.WavFileExportManager; import detectiongrouplocaliser.DetectionGroupSummary; +import export.wavExport.WavFileExportManager; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; @@ -149,6 +149,7 @@ public class TDMenuPane extends PamBorderPane { this.setCenter(holder); groupDetectionDisplay = new OverlayGroupDisplay(); + groupDetectionDisplay.addDisplayListener((oldDataUnit, newDataUnit)->{ //listener for changing data units setCurrentDataUnit(newDataUnit); @@ -235,7 +236,6 @@ public class TDMenuPane extends PamBorderPane { */ private void setSummaryText() { - //System.out.println(""); if (currentDataUnit==null || detectionSummary==null) return; String text; @@ -244,11 +244,13 @@ public class TDMenuPane extends PamBorderPane { text=currentDataUnit.getParentDataBlock(). getHoverText(tdGraphFX.getGraphProjector(), currentDataUnit, 0); + if (text==null) return; //do not clear as usually this is because a super unit has been set } else { //selected an area with no data units. text= new String("No data units selected"); } + this.infoTextLabel.setText(PamUtilsFX.htmlToNormal(text)); } @@ -267,7 +269,7 @@ public class TDMenuPane extends PamBorderPane { private Pane createMenuPane(){ PamVBox menuPane = new PamVBox(); - menuPane.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); + menuPane.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); menuPane.setStyle("-fx-background-color: -fx-darkbackground"); //create info and toggle detection buttons that always sit at the top of the display @@ -491,6 +493,7 @@ public class TDMenuPane extends PamBorderPane { * @param pamDataUnit - the current data unit. */ private void setCurrentDataUnit(PamDataUnit pamDataUnit) { + currentDataUnit = pamDataUnit; setSummaryText(); } @@ -528,10 +531,8 @@ public class TDMenuPane extends PamBorderPane { this.groupDetectionDisplay.clearDisplay(); this.infoTextLabel.setText(""); - this.groupDetectionDisplay.setDetectionGroup(detectionSummary!=null ? detectionSummary.getDataList() : null); - //show the detection display. boolean showDetDisplay = toggle.isSelected(); this.toggle.setDisable(false); @@ -545,12 +546,9 @@ public class TDMenuPane extends PamBorderPane { PamRawDataBlock pamRawBlock = findRawSourceBlock(); // System.out.println("Pam raw data block: " + pamRawBlock); - if (WavFileExportManager.haveRawData(pamRawBlock, (long) overlayMarker.getCurrentMark().getLimits()[0], (long) overlayMarker.getCurrentMark().getLimits()[1])) { - //System.out.println("Overaly Marker start X: " + overlayMarker.getCurrentMark().getLimits()[0] + " end: " + overlayMarker.getCurrentMark().getLimits()[1]); //System.out.println("Overlay Marker start Y: " + overlayMarker.getCurrentMark().getLimits()[2] + " end: " + overlayMarker.getCurrentMark().getLimits()[3]); - groupDetectionDisplay.showRawData(pamRawBlock, overlayMarker.getCurrentMark().getLimits(), overlayMarker.getCurrentMark().getMarkChannels()); } else { diff --git a/src/dataPlotsFX/overlaymark/popUpMenu/TDPopUpMenuAdv.java b/src/dataPlotsFX/overlaymark/popUpMenu/TDPopUpMenuAdv.java index f2e452dc..8e1647e4 100644 --- a/src/dataPlotsFX/overlaymark/popUpMenu/TDPopUpMenuAdv.java +++ b/src/dataPlotsFX/overlaymark/popUpMenu/TDPopUpMenuAdv.java @@ -66,7 +66,8 @@ public class TDPopUpMenuAdv implements ExtPopMenu { } if (PamGUIManager.isFX()) { - popOver.show(tdGraphFX.getScene().getWindow(), e.getScreenX(), e.getScreenY()); //FX + //TODO - need to shift by the distance to arrow- not arbitrary 40 pixels + popOver.show(tdGraphFX.getScene().getWindow(), e.getScreenX(), e.getScreenY()-40); //FX } else { popOver.show(tdGraphFX, e.getScreenX(), e.getScreenY()); //Swing diff --git a/src/dataPlotsFX/rawDataPlotFX/RawSoundPlotDataFX.java b/src/dataPlotsFX/rawDataPlotFX/RawSoundPlotDataFX.java index d8a4b609..04260b9b 100644 --- a/src/dataPlotsFX/rawDataPlotFX/RawSoundPlotDataFX.java +++ b/src/dataPlotsFX/rawDataPlotFX/RawSoundPlotDataFX.java @@ -221,10 +221,10 @@ public class RawSoundPlotDataFX { */ public void newRawData(RawDataUnit rawDataUnit, double binsPerPixel) { - // crashes with a Stack Overflow error processing offline wav files. - if (!JamieDev.isEnabled()) { - return; - } +// // crashes with a Stack Overflow error processing offline wav files. +// if (!JamieDev.isEnabled()) { +// return; +// } if (soundStore.binsPerPixel==binsPerPixel || binsPerPixel==-1){ //add data to the sound data store. @@ -382,7 +382,7 @@ public class RawSoundPlotDataFX { if (soundStore.currentRawDataMillis==0){ if (++timeErrors < 10) { - System.err.println("Raw sound data has no associated millisecond time: "+ soundStore.currentRawDataMillis); + System.err.println("RawSoundPlotData: Raw sound data has no associated millisecond time: "+ soundStore.currentRawDataMillis); } return; } @@ -428,7 +428,7 @@ public class RawSoundPlotDataFX { start=(int) Math.round(startPixel); } - //System.out.println(String.format("secondsBack: %.3f arrayPixBack %.1f arrayPixStart: %d start: %d soundStore.storeSize: %d wrapPix %.2f", secondsBack, arrayPixBack, arrayPixStart,start, soundStore.storeSize, wrapPix)); +// System.out.println(String.format("secondsBack: %.3f arrayPixBack %.1f arrayPixStart: %d start: %d soundStore.storeSize: %d wrapPix %.2f", secondsBack, arrayPixBack, arrayPixStart,start, soundStore.storeSize, wrapPix)); int windowSize = (int) ((orientation==Orientation.HORIZONTAL) ? windowRect.getWidth(): windowRect.getHeight()); diff --git a/src/dataPlotsFX/scroller/TDAcousticScroller.java b/src/dataPlotsFX/scroller/TDAcousticScroller.java index 784d3bec..304bd76e 100644 --- a/src/dataPlotsFX/scroller/TDAcousticScroller.java +++ b/src/dataPlotsFX/scroller/TDAcousticScroller.java @@ -144,10 +144,16 @@ public class TDAcousticScroller extends AcousticScrollerFX implements PamSetting createTimeSpinner(); this.settingsPane.setParams(scrollerColourParams); - + updateDataGramColours(scrollerColourParams); } + + private void setSpinnerValue(long millis) { + spinnerCall = true; + spinner.getValueFactory().setValue(millis); + spinnerCall = false; + } boolean spinnerCall = false; @@ -177,9 +183,7 @@ public class TDAcousticScroller extends AcousticScrollerFX implements PamSetting //add a listener so that the spinner changes of the visible amount chnages super.getScrollBarPane().visibleAmountProperty().addListener((obsVal, oldVal, newVal)->{ - spinnerCall = true; - spinner.getValueFactory().setValue(newVal.longValue()); - spinnerCall = false; + setSpinnerValue(newVal.longValue()); }); //add a listener so the visible amount changes of the spinner changes value. @@ -213,6 +217,13 @@ public class TDAcousticScroller extends AcousticScrollerFX implements PamSetting } }); } + + + @Override + public void setVisibleMillis(long visibleAmount) { + super.setVisibleMillis(visibleAmount); + setSpinnerValue(visibleAmount); + } @@ -624,6 +635,7 @@ public class TDAcousticScroller extends AcousticScrollerFX implements PamSetting } + @Override public String getUnitName() { // TODO Auto-generated method stub diff --git a/src/dataPlotsFX/scroller/TDScrollerSettingsPane.java b/src/dataPlotsFX/scroller/TDScrollerSettingsPane.java index bc904afd..81d40375 100644 --- a/src/dataPlotsFX/scroller/TDScrollerSettingsPane.java +++ b/src/dataPlotsFX/scroller/TDScrollerSettingsPane.java @@ -95,7 +95,7 @@ public class TDScrollerSettingsPane extends DynamicSettingsPane>(Arrays.asList(RawDataUnit.class)); + } } diff --git a/src/decimator/DecimatorProcessW.java b/src/decimator/DecimatorProcessW.java index 67be9e28..e99260bb 100644 --- a/src/decimator/DecimatorProcessW.java +++ b/src/decimator/DecimatorProcessW.java @@ -1,5 +1,8 @@ package decimator; +import java.util.ArrayList; +import java.util.Arrays; + import Filters.FilterBand; import Filters.FilterParams; import Filters.FilterType; @@ -137,5 +140,10 @@ public class DecimatorProcessW extends PamProcess { public PamRawDataBlock getOutputDataBlock() { return outputDataBlock; } + + @Override + public ArrayList getCompatibleDataUnits(){ + return new ArrayList>(Arrays.asList(RawDataUnit.class)); + } } diff --git a/src/decimator/layoutFX/DecimatorSettingsPane.java b/src/decimator/layoutFX/DecimatorSettingsPane.java new file mode 100644 index 00000000..b9b2c325 --- /dev/null +++ b/src/decimator/layoutFX/DecimatorSettingsPane.java @@ -0,0 +1,469 @@ +package decimator.layoutFX; + + +import org.controlsfx.control.PopOver; + +import Acquisition.layoutFX.OfflineDAQPane; +import Filters.FilterBand; +import Filters.FilterParams; +import Filters.FilterType; +import PamController.PamController; +import PamController.SettingsPane; +import PamDetection.RawDataUnit; +import PamView.dialog.warn.WarnOnce; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamRawDataBlock; +import dataMap.filemaps.OfflineFileParameters; +import decimator.DecimatorControl; +import decimator.DecimatorParams; +import javafx.geometry.Insets; +import javafx.scene.Node; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.Region; +import pamViewFX.PamGuiManagerFX; +import pamViewFX.fxGlyphs.PamGlyphDude; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamButton; +import pamViewFX.fxNodes.PamGridPane; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; +import pamViewFX.fxNodes.utilityPanes.FilterPaneFX; +import pamViewFX.fxNodes.utilityPanes.SourcePaneFX; +import pamViewFX.validator.PamValidator; + +/** + * + * Settings for the decimator. + * + * @author Jamie Macaulay + * @author Dougals Gillespie + * + */ +public class DecimatorSettingsPane extends SettingsPane { + + private DecimatorParams decimatorParams; + + private PamBorderPane mainPane; + + /** + * Reference to the decimator control. + */ + private DecimatorControl decimatorControl; + + /** + * Allows users to slect the input source. + */ + private SourcePaneFX sourcePanel; + + /** + * Shows the sample rate of the source + */ + private Label sourceSampleRate; + + private float inputSampleRate; + + private TextField newSampleRate; + + private PamButton filterButton; + + private PamButton defaultFilterButton; + + private Label filterInfo; + + private FilterPaneFX filterPaneFX; + + private ComboBox interpolator; + + private boolean isViewer; + + private OfflineDAQPane offlineDAQPaneFX; + + private PamValidator validator = new PamValidator(); + + public DecimatorSettingsPane(DecimatorControl aquisitionControl) { + super(null); + + mainPane= new PamBorderPane(); + this.decimatorControl = aquisitionControl; + + mainPane.setCenter(createPane() ); + + } + + private Region createPane() { + + PamVBox holder = new PamVBox(); + holder.setSpacing(5); + + // insets = new Insets(2,2,2,2); + + Label srcLabel = new Label("Decimator settings"); + + PamGuiManagerFX.titleFont2style(srcLabel); + holder.getChildren().add(srcLabel); + + sourcePanel = new SourcePaneFX( RawDataUnit.class, true, true); + sourcePanel.addSelectionListener((obsval, oldVal, newVal)->{ + newDataSource(); + //need to validate the interpolate too as this depends on the input sample rate. + //validator.validate(); + }); + PamGuiManagerFX.titleFont2style(sourcePanel.getChannelLabel()); + + // sourcePanel.addSourcePanelMonitor(new SPMonitor()); + holder.getChildren().addAll(sourcePanel); + + PamGridPane decimatorPane = new PamGridPane(); + decimatorPane.setVgap(5); + decimatorPane.setHgap(5); + + //Decimator Settings + Label label = new Label("Decimator settings"); + PamGuiManagerFX.titleFont2style(label); + holder.getChildren().add(label); + + int gridx = 0; + int gridy = 0; + decimatorPane.add(new Label("Source sample rate "), gridx, gridy); + gridx++; + decimatorPane.add(sourceSampleRate = new Label(" - Hz"), gridx, gridy); + sourceSampleRate.setPadding(new Insets(0,0,0,5)); + gridx = 0; + gridy ++; + + decimatorPane.add(new Label("Output sample rate "), gridx, gridy); + gridx ++; + decimatorPane.add(newSampleRate = new TextField(), gridx, gridy); + gridx ++; + decimatorPane.add(new Label(" Hz"), gridx, gridy); + + validator.createCheck() + .dependsOn("new_sample_rate", newSampleRate.textProperty()) + .withMethod(c -> { + try { + String posVal = c.get("new_sample_rate"); + if (posVal.isEmpty() || Double.valueOf(posVal)==null) { + c.error("The input for output sample rate is invalid"); + } + } + catch (Exception e) { + c.error("The input for output sample rate is invalid"); + } + }) + .decorates(newSampleRate).immediate(); + + newSampleRate.textProperty().addListener((obsVal, oldVal, newval)->{ + //need to validate the interpolate too as this depends on the sample rate. + validator.validate(); + }); + + + gridy ++; + gridx = 0; + // gridwidth = 1; + + filterPaneFX = new FilterPaneFX(); + decimatorPane.add(new Label("Anti-aliasing filter"), gridx, gridy); + + gridx++; + decimatorPane.add(filterButton = new PamButton("Settings"), gridx, gridy); + filterButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chart-bell-curve-cumulative")); + + + filterButton.setTooltip(new Tooltip("Set a custom anti-aliasing filter")); + filterButton.setOnAction((action)->{ + selectFilters(filterButton); + }); + + // filterButton.addActionListener(new FilterButton()); + gridx = 2; + // gridwidth = 2; + + + decimatorPane.add(defaultFilterButton = new PamButton(), gridx, gridy); + defaultFilterButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-cog-refresh",PamGuiManagerFX.iconSize)); + defaultFilterButton.setTooltip(new Tooltip("Set the default anti-aliasing filter")); + defaultFilterButton.setOnAction((action)->{ + restoreDefaultSettings(); + }); + + gridx = 1; + gridy++; + decimatorPane.add(filterInfo = new Label(""), gridx, gridy); + PamGridPane.setColumnSpan(filterInfo, 3); + + gridx = 0; + // gridwidth = 1; + gridy++; + + Label interpLabel = new Label("Interpolation"); + + decimatorPane.add(interpLabel, gridx, gridy); + gridx++; + // gridx += gridwidth; + // gridwidth = 2; + decimatorPane.add(interpolator = new ComboBox(), gridx, gridy); + interpolator.getItems().add("None"); + interpolator.getItems().add("Linear"); + interpolator.getItems().add("Quadratic"); + + validator.createCheck() + .dependsOn("new_sample_rate", interpolator.getSelectionModel().selectedIndexProperty()) + .withMethod(c -> { + //the selected index + int selectedIndex = c.get("new_sample_rate"); + try { + float sampleRate = java.lang.Float.valueOf(newSampleRate.getText()); + String warning = decimatorInterpWarning(selectedIndex, sampleRate); + + if (warning!=null) { + c.warn(warning); + } + + } + catch (Exception e) { + return; + } + }) + .decorates(interpolator).immediate(); + + holder.getChildren().add(decimatorPane); + + + //the viewer mode. + isViewer = PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW; + Region mainPane; + if (isViewer) { + TabPane tabbedPane = new TabPane(); + + offlineDAQPaneFX= new OfflineDAQPane(decimatorControl); + + tabbedPane.getTabs().add(new Tab("Offline Files",offlineDAQPaneFX.getContentNode())); + tabbedPane.getTabs().add(new Tab("Decimator Settings", holder)); + mainPane = tabbedPane; + } + else { + mainPane = holder; + } + + // + // setHelpPoint("sound_processing.decimatorHelp.docs.decimator_decimator"); + // filterButton.setToolTipText("Manual adjustment of filter settings"); + // defaultFilterButton.setToolTipText("Set a default filter (6th order Butterworth low pass at Decimator Nyquist frequency)"); + // interpolator.setToolTipText("If Decimation / upsampling is not by an integer value, you should use interpolation to improve waveform reconstruction"); + + return mainPane; + } + + private void selectFilters(PamButton button) { + + float filtSampleRate = Math.max(inputSampleRate, getOutputSampleRate()); + + PopOver popOver = new PopOver(); + + popOver.setContentNode(filterPaneFX.getContentNode()); + + filterPaneFX.setSampleRate(filtSampleRate); + + if (decimatorParams.filterParams==null) { + restoreDefaultSettings(); + } + + popOver.setOnHidden((e)->{ + + FilterParams newFP = filterPaneFX.getParams(decimatorParams.filterParams); + + if (newFP != null) { + decimatorParams.filterParams = newFP.clone(); + } + + sayFilter(); + + }); + + filterPaneFX.setParams(decimatorParams.filterParams); + //show the filter paramters + popOver.show(button); + } + + + /** + * display filter information + */ + private void sayFilter() { + if (decimatorParams == null || decimatorParams.filterParams == null) { + filterInfo.setText("No filter"); + } + else { + filterInfo.setText(decimatorParams.filterParams.toString()); + } + } + + + private float getOutputSampleRate() { + try { + float fs = Float.valueOf(newSampleRate.getText()); + return fs; + } + catch (NumberFormatException e) { + return inputSampleRate; + } + } + + + public void restoreDefaultSettings() { + /* + * does not set the output sample rate, but does set sensible values for the + * filter. + */ + float newFS = 0; + try { + newFS = java.lang.Float.valueOf(newSampleRate.getText()); + } + catch (NumberFormatException e) { + } + PamDataBlock sourceblock = sourcePanel.getSource(); + if (sourceblock.getSampleRate() > 0) { + newFS = Math.min(newFS, sourceblock.getSampleRate()); + } + if (newFS <= 0) { + PamDialogFX.showWarning("Invalid output samplerate : " + newSampleRate.getText()); + return; + } + decimatorParams.filterParams.lowPassFreq = newFS/2; + decimatorParams.filterParams.filterType = FilterType.BUTTERWORTH; + decimatorParams.filterParams.filterOrder = 6; + decimatorParams.filterParams.filterBand = FilterBand.LOWPASS; + + sayFilter(); + } + + + @Override + public DecimatorParams getParams(DecimatorParams currParams) { + try { + // ArrayList rawBlocks = PamController.getInstance().getRawDataBlocks(); + decimatorParams.rawDataSource = sourcePanel.getSource().getDataName(); + decimatorParams.channelMap = sourcePanel.getChannelList(); + decimatorParams.newSampleRate = java.lang.Float.valueOf(newSampleRate.getText()); + } + catch (Exception Ex) { + PamDialogFX.showWarning("There is an unknown error - get in touch with PMAGuard support. "); + return null; + } + + if (decimatorParams.rawDataSource == null) { + PamDialogFX.showWarning("You must select a raw data source"); + return null; + } + + if (decimatorParams.channelMap == 0) { + PamDialogFX.showWarning("You must select at least one channel for decimation"); + return null; + } + + if (offlineDAQPaneFX != null) { + OfflineFileParameters ofp = offlineDAQPaneFX.getParams(); + if (ofp == null) { + return null; + } + decimatorControl.getOfflineFileServer().setOfflineFileParameters(ofp); + } + + decimatorParams.interpolation = interpolator.getSelectionModel().getSelectedIndex(); + + // boolean isInt = decimatorControl.isIntegerDecimation(sourcePanel.getSource().getSampleRate(), decimatorParams.newSampleRate); + // if (isInt && decimatorParams.interpolation > 0) { + // int ans = WarnOnce.showWarning("Decimator", "With in / out sample rate ratio equal to a whole number, there is no need to interpolate", WarnOnce.OK_CANCEL_OPTION); + // if (ans == WarnOnce.CANCEL_OPTION) { + // return null; + // } + // else { + // decimatorParams.interpolation = 0; + // } + // } + // if (!isInt && decimatorParams.interpolation == 0) { + // int ans = WarnOnce.showWarning("Decimator", "With in / out sample rate ratio NOT equal to a whole number, it is recommended that you use linear or quadratic interpolation", + // WarnOnce.OK_CANCEL_OPTION); + // if (ans == WarnOnce.CANCEL_OPTION) { + // return null; + // } + // else { + //// decimatorParams.interpolation = 0; + // } + // } + + return decimatorParams; + } + + + /** + * Check if the selected interpolator should be used. If not, returning a warning string. + * @param interpSelection - the interpolator selection + * @param sampleRate - the sample rate. + * @return a warning if one is needed. Null if not. + */ + private String decimatorInterpWarning(int interpSelection, float sampleRate) { + boolean isInt = decimatorControl.isIntegerDecimation(sourcePanel.getSource().getSampleRate(), sampleRate); + String warningString = null; + if (isInt && interpSelection > 0) { + warningString = "With in / out sample rate ratio equal to a whole number, there is no need to interpolate"; + + } + if (!isInt && interpSelection == 0) { + warningString = "With in / out sample rate ratio NOT equal to a whole number, it is recommended that you use linear or quadratic interpolation"; + + } + return warningString; + } + + @Override + public void setParams(DecimatorParams input) { + this.decimatorParams = input.clone(); + + sourcePanel.excludeDataBlock(decimatorControl.getDecimatorProcess().getOutputDataBlock(0), true); + sourcePanel.setSourceList(); + PamRawDataBlock currentBlock = PamController.getInstance().getRawDataBlock(decimatorParams.rawDataSource); + sourcePanel.setSource(currentBlock); + sourcePanel.setChannelList(decimatorParams.channelMap); + newSampleRate.setText(String.format("%.1f", decimatorParams.newSampleRate)); + newDataSource(); + if (offlineDAQPaneFX != null) { + offlineDAQPaneFX.setParams(decimatorControl.getOfflineFileServer().getOfflineFileParameters()); + } + interpolator.getSelectionModel().select(decimatorParams.interpolation); + sayFilter(); + validator.validate(); + + } + + @Override + public String getName() { + return "Decimator Settings"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + + private void newDataSource() { + PamDataBlock block = sourcePanel.getSource(); + if (block != null) { + sourceSampleRate.setText(String.format("%.1f Hz", + inputSampleRate = block.getSampleRate())); + } + } + +} diff --git a/src/decimator/layoutFX/DecimatorUIFX.java b/src/decimator/layoutFX/DecimatorUIFX.java new file mode 100644 index 00000000..6c83dc17 --- /dev/null +++ b/src/decimator/layoutFX/DecimatorUIFX.java @@ -0,0 +1,57 @@ +package decimator.layoutFX; + + +import PamController.SettingsPane; +import decimator.DecimatorControl; +import decimator.DecimatorParams; +import pamViewFX.PamControlledGUIFX; + +/** + * FX GUI for the SoundAquisition module. + * @author Jamie Macaulay + * + */ +public class DecimatorUIFX extends PamControlledGUIFX { + + /** + * The main settings pane for the aquisition control. + */ + private DecimatorSettingsPane decimatorPane; + + /** + * Reference to the Sound Aquisition control. + */ + private DecimatorControl decimatorControl; + + public DecimatorUIFX(DecimatorControl aquisitionControl) { + this.decimatorControl=aquisitionControl; + } + + @Override + public SettingsPane getSettingsPane(){ + if (decimatorPane==null){ + decimatorPane=new DecimatorSettingsPane(decimatorControl); + } + decimatorPane.setParams(decimatorControl.getDecimatorParams()); + return decimatorPane; + } + + + /** + * This is called whenever a settings pane is closed. If a pamControlledUnit has + * settings pane then this should be used to update settings based on info input + * into settings pane. + */ + @Override + public void updateParams() { + DecimatorParams newParams=decimatorPane.getParams(decimatorControl.getDecimatorParams()); + if (newParams!=null) { + decimatorControl.setDecimatorParams(newParams); + } + //setup the controlled unit. + decimatorControl.setupControlledUnit(); + } + + + +} \ No newline at end of file diff --git a/src/detectionPlotFX/DetectionDisplayControl.java b/src/detectionPlotFX/DetectionDisplayControl.java index d9e78654..9e8fcf7b 100644 --- a/src/detectionPlotFX/DetectionDisplayControl.java +++ b/src/detectionPlotFX/DetectionDisplayControl.java @@ -24,6 +24,7 @@ import userDisplayFX.UserDisplayNodeFX; * @author Jamie Macaulay * */ +@Deprecated public class DetectionDisplayControl extends UserDisplayControlFX { /** @@ -49,7 +50,8 @@ public class DetectionDisplayControl extends UserDisplayControlFX { //create the observer for the parent data block. detectionDataObserver=new DetectionDataObserver(); setMultiParent(false); - + + } /** @@ -83,7 +85,7 @@ public class DetectionDisplayControl extends UserDisplayControlFX { displayToDataModel(this.getUserDisplayProcess().getParentDataBlock()); break; } - this.detectionDisplay.notifyModelChanged(type); + //this.detectionDisplay.notifyModelChanged(type); } /** @@ -119,8 +121,11 @@ public class DetectionDisplayControl extends UserDisplayControlFX { */ public boolean newDataBlockAdded(PamDataBlock pamDataBlock){ + System.out.println("New datablock added: " + pamDataBlock.getDataName()); + //now must find the data provider. //first get the list of dataInfos. + //22/05/206. Need to check we have the correct datablock before doing anything else. The datablock should //be null or of the correct type. If not need to keep the last connection. DDDataProvider newDataProviderFX=DDPlotRegister.getInstance().findDataProvider(pamDataBlock); @@ -147,7 +152,7 @@ public class DetectionDisplayControl extends UserDisplayControlFX { this.detectionDisplay.setDataInfo(newDataProviderFX); //now add the observer to the data block - //System.out.println("DetectionDisplayControl: Adding observer to datablock: "+ pamDataBlock.getDataName()); + //System.out.println("DetectionDisplayControl: Adding observer to data block: "+ pamDataBlock.getDataName()); pamDataBlock.addObserver(this.detectionDataObserver); return true; @@ -156,9 +161,9 @@ public class DetectionDisplayControl extends UserDisplayControlFX { @Override public ArrayList getDisplays(){ if (displays==null){ - detectionDisplay=new DetectionPlotDisplay(this); + detectionDisplay=new DetectionPlotDisplay(); displays=new ArrayList(); - displays.add(detectionDisplay); + //displays.add(detectionDisplay); } return displays; } @@ -181,7 +186,7 @@ public class DetectionDisplayControl extends UserDisplayControlFX { @Override public void addData(PamObservable o, PamDataUnit arg) { - //System.out.println("DetectionDisplay: Incoming data unit: "+arg.getParentDataBlock().getDataName()); + System.out.println("DetectionDisplay: Incoming data unit: "+arg.getParentDataBlock().getDataName()); //send the data unit to the display. //TODO-why? Platform.runLater(()->{ diff --git a/src/detectionPlotFX/DetectionDisplayControl2.java b/src/detectionPlotFX/DetectionDisplayControl2.java new file mode 100644 index 00000000..419f44ea --- /dev/null +++ b/src/detectionPlotFX/DetectionDisplayControl2.java @@ -0,0 +1,166 @@ +package detectionPlotFX; + +import java.util.ArrayList; + +import PamController.PamController; +import PamController.PamControllerInterface; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import PamguardMVC.PamObservable; +import PamguardMVC.PamObserverAdapter; +import pamViewFX.fxNodes.internalNode.PamInternalPane; +import userDisplayFX.UserDisplayControlFX; +import userDisplayFX.UserDisplayNodeFX; + + +/** + * + * Detection plot which can display single or groups of detections. + * + * @author Jamie Macaulay + * + */ +public class DetectionDisplayControl2 extends UserDisplayControlFX { + + + /** + * The display + */ + private DetectionGroupDisplayFX detectionDisplay; + + /** + * A list of displays-> really redundant as there is only ever one display. + */ + private ArrayList displays; + + /** + * The data observer that + */ + private DetectionDataObserver detectionDataObserver; + + + private PamInternalPane internalFrame;; + + + public DetectionDisplayControl2(String unitName) { + super(unitName); + //set which data blocks can connect as parents. + setCompatibleDataUnits(); + //create the observer for the parent data block. + detectionDataObserver=new DetectionDataObserver(); + setMultiParent(false); + } + + + /** + * Set compatible data units in the process for this display. This allows the data model to determine if connections can + * be made to the display. + */ + @SuppressWarnings("unchecked") + private void setCompatibleDataUnits(){ + super.removeCompatibleDataUnits(); + super.addCompatibleUnit(PamDataUnit.class); + //also add data unit + } + + + @Override + public void notifyModelChanged(int type){ + // System.out.println("NOTIFICATION: " + type); + super.notifyModelChanged(type); + switch (type){ + case PamControllerInterface.INITIALIZATION_COMPLETE: + setCompatibleDataUnits(); + break; + case PamControllerInterface.ADD_CONTROLLEDUNIT: + setCompatibleDataUnits(); + break; + case PamControllerInterface.CHANGED_PROCESS_SETTINGS: + //this is were the data block may have been added. Need to add an observer to this data block to say when the thing has + //thing has a new detection. + displayToDataModel(this.getUserDisplayProcess().getParentDataBlock()); + break; + } + if (this.detectionDisplay!=null) this.detectionDisplay.notifyModelChanged(type); + } + + + /** + * Set the display to show detections form the data block set in the data model- + * i.e. show it's parent data block. + */ + protected void displayToDataModel(PamDataBlock parentDataBlock){ + //remove any TDDataInfos which are not present in the data block list + //if the datablock the same do nothing + newDataBlockAdded(parentDataBlock); + } + + + /** + * Called whenever a new data block is added to the display. + * Removes the observer from all other data blocks in the model + * and then the adds the observer to that data block; + * @return true if the parent has successfully been added. + */ + public boolean newDataBlockAdded(PamDataBlock pamDataBlock){ + + //System.out.println("NEW DATA BLOCK DETECTION DISPLAY: " + pamDataBlock); + + //if null then no parent- simply set the DDataInfo to null; + if (pamDataBlock==null) { + this.detectionDisplay.clearDisplay(); + return true; + } + + //now add the observer to the data block + //System.out.println("DetectionDisplayControl: Adding observer to data block: "+ pamDataBlock.getDataName()); + pamDataBlock.addObserver(this.detectionDataObserver); + + return true; + } + + + @Override + public ArrayList getDisplays(){ + if (displays==null){ + detectionDisplay=new DetectionGroupDisplayFX(this); + //set the paramters. + displays=new ArrayList(); + displays.add(detectionDisplay); + //TODO +// detectionDisplay.setEnableScrollBar(false); //make this an option + } + return displays; + } + + + /** + * + * The data observer monitors incoming data from data blocks. + * + * @author Doug Gillespie and Jamie Macaulay + * + */ + private class DetectionDataObserver extends PamObserverAdapter { + + @Override + public long getRequiredDataHistory(PamObservable o, Object arg) { + return 1000; //no data histroy required for this click. + } + + @Override + public void addData(PamObservable o, PamDataUnit arg) { + //System.out.println("DetectionDisplay: INCOMING data unit: "+ arg); + //send the data unit to the display. + detectionDisplay.setDataUnit(arg); + } + + @Override + public String getObserverName() { + return "Detection Display FX"; + } + } + + + +} diff --git a/src/detectionPlotFX/DetectionGroupDisplay.java b/src/detectionPlotFX/DetectionGroupDisplay.java index 38ad7de4..28c416c3 100644 --- a/src/detectionPlotFX/DetectionGroupDisplay.java +++ b/src/detectionPlotFX/DetectionGroupDisplay.java @@ -7,6 +7,7 @@ import java.util.List; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.superdet.SuperDetection; +import atlantafx.base.theme.Styles; import detectionPlotFX.data.DDDataInfo; import detectionPlotFX.data.DDDataProvider; import detectionPlotFX.data.DDPlotRegister; @@ -15,7 +16,11 @@ import detectionPlotFX.layout.DetectionPlotDisplay; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.geometry.Side; +import javafx.scene.Node; import javafx.scene.control.Label; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import javafx.scene.control.TabPane.TabClosingPolicy; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; @@ -24,18 +29,30 @@ import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.PamHBox; +import pamViewFX.fxNodes.PamStackPane; +import pamViewFX.fxNodes.PamTabPane; import pamViewFX.fxNodes.hidingPane.HidingPane; import pamViewFX.fxStyles.PamStylesManagerFX; /** * - * A display which shows a list of data units with arrows allowing the user to - * navigate through the different data units. + * A detection plot display with convenience functions to set any type of data unit. * * @author Jamie Macaulay * */ +@SuppressWarnings("rawtypes") public class DetectionGroupDisplay extends PamBorderPane { + + /** + * Show the settings within hiding panes within the display. + */ + public static final int DISPLAY_COMPACT = 0; + + /** + * Show settings on top and to the right of the display + */ + public static final int DISPLAY_EXTENDED = 1; /** * Index of the current normal unit with the detection summary. @@ -55,7 +72,7 @@ public class DetectionGroupDisplay extends PamBorderPane { /** * Label for the top holder. */ - private Label label; + private Label dataLabel; /** * Holds the arrow pane and hiding pane. @@ -81,7 +98,7 @@ public class DetectionGroupDisplay extends PamBorderPane { /** * Holds the detection display and controls for viewing standard detections. */ - public PamBorderPane detectionDisplayHolder; + public Pane detectionDisplayHolder; /** * The group detection listeners. @@ -97,11 +114,26 @@ public class DetectionGroupDisplay extends PamBorderPane { * Hiding pane for the plot settings. */ private HidingPane hidingPane; - + + + /** + * Constructor for the detection group display. + */ public DetectionGroupDisplay() { //create hash map to map DDDataInfos to datablocks for quick access. dDataInfoHashMap = new HashMap(); - createDetectionDisplay(); + createDetectionDisplay(DISPLAY_EXTENDED); + this.setCenter(detectionDisplayHolder); + } + + /** + * Constructor for the detection group display. + * @param layoutType - the layout of the display - e.g. DetectionGroupDisplay.DISPLAY_COMPACT + */ + public DetectionGroupDisplay(int layoutType) { + //create hash map to map DDDataInfos to datablocks for quick access. + dDataInfoHashMap = new HashMap(); + createDetectionDisplay(DISPLAY_COMPACT); this.setCenter(detectionDisplayHolder); } @@ -109,8 +141,8 @@ public class DetectionGroupDisplay extends PamBorderPane { * Create the detection display. * @return the detection display */ - private Pane createDetectionDisplay(){ - + private Pane createDetectionDisplay(int layoutType){ + detectionDisplay= new DetectionPlotDisplay(); arrowLeft= new PamButton(); @@ -136,43 +168,95 @@ public class DetectionGroupDisplay extends PamBorderPane { arrowPane.getChildren().addAll(arrowLeft, arrowRight); BorderPane.setAlignment(arrowPane, Pos.CENTER_RIGHT); + + //a label to show information of the data unit + dataLabel = new Label(); + + if (layoutType==DISPLAY_EXTENDED) { + //the display has controls above the axis and a hiding pane that increases the width of the display. + + //the holder for the detection display. + PamBorderPane detectionDisplayHolder= new PamBorderPane(); + + //create the hiding pane to show advanced settings. + hidingPane = new HidingPane(Side.RIGHT, detectionDisplay.getSettingsHolder(), detectionDisplayHolder, layoutType==DISPLAY_COMPACT, 0); + + topHolder=new PamBorderPane(); + topHolder.setRight(arrowPane); + + topHolder.setCenter(dataLabel); + topHolder.setLeft(detectionDisplay.getDataTypePane()); + + //whenever the detection plot selection box e.g. from waveform to wigner then check there is a settings pane. If not + //then get rid of the settings button. + detectionDisplay.getDataTypePane().getDetectionPlotBox().valueProperty().addListener((obsVal, oldVal, newVal)->{ + enableControls(); + }); + + detectionDisplayHolder.setTop(topHolder); + detectionDisplayHolder.setCenter(detectionDisplay); + this.detectionDisplayHolder = detectionDisplayHolder; + + arrowPane.getChildren().add(hidingPane.getShowButton()); + + hidingPane.getShowButton().setGraphic(PamGlyphDude.createPamIcon("mdi2c-cog", + PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize)); + this.setRight(hidingPane); //bit of a hack but works. + hidingPane.showHidePane(false); + + //this makes the hide button appear top right which is nicer for closing the pane. + StackPane.setAlignment(hidingPane.getHideButton(), Pos.TOP_RIGHT); + //hidingPane.removeHideButton(); + hidingPane.styleHideButton(hidingPane.getHideButton(), Side.LEFT); + + //make the background dark for settings pane. + detectionDisplay.getSettingsHolder().setStyle("-fx-background-color: -fx-darkbackground"); + + } + else { + //the display is compact with all controls within an internal hiding pane. + + detectionDisplayHolder = new PamStackPane(); + + PamTabPane settingsPane = new PamTabPane(); + settingsPane.setTabMinHeight(60); + settingsPane.setMinHeight(60); +// settingsPane.repackTabs(); + + settingsPane.setAddTabButton(false); +// settingsPane.getStyleClass().add(Styles.TABS_FLOATING); + settingsPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); + //settingsPane.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); + + Tab dataTab = new Tab("Data",detectionDisplay.getDataTypePane()); + Tab settingsTab = new Tab("Settings -BLAH",detectionDisplay.getSettingsHolder()); - topHolder=new PamBorderPane(); - topHolder.setRight(arrowPane); + settingsPane.getTabs().add(dataTab); + settingsPane.getTabs().add(settingsTab); + + hidingPane = new HidingPane(Side.RIGHT, settingsPane, detectionDisplayHolder, layoutType==DISPLAY_COMPACT, 0); + hidingPane.removeHideButton(); + hidingPane.getHideButton().setMinWidth(40); + settingsPane.setTabStartRegion(hidingPane.getHideButton()); + + //now everything to pane. + detectionDisplayHolder.getChildren().add(detectionDisplay); + StackPane.setAlignment(detectionDisplay, Pos.CENTER); + + //settingsPane.setPadding(new Insets(35,0,0,0)); + + Node icon = PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconSize); + detectionDisplay.getPlotPane().setHidePane(new PamBorderPane(settingsPane), icon, Side.RIGHT); + hidingPane.getShowButton().setPrefHeight(30); - topHolder.setCenter(label = new Label()); - topHolder.setLeft(detectionDisplay.getDataTypePane()); + } - //whenever the detection plot selection box e.g. from waveform to wigner then check there is a settings pane. If not - //then get rid of the settings button. - detectionDisplay.getDataTypePane().getDetectionPlotBox().valueProperty().addListener((obsVal, oldVal, newVal)->{ - enableControls(); - }); + //set styles +// detectionDisplay.getSettingsHolder().getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); +// detectionDisplay.getSettingsHolder().setStyle("-fx-background-color: -fx-darkbackground"); + hidingPane.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); - detectionDisplayHolder= new PamBorderPane(); - detectionDisplayHolder.setTop(topHolder); - detectionDisplayHolder.setCenter(detectionDisplay); - - - //create the hiding pane to show advanced settings. - hidingPane = new HidingPane(Side.RIGHT, detectionDisplay.getSettingsHolder(), detectionDisplayHolder, false, 0); - - detectionDisplay.getSettingsHolder().getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); - detectionDisplay.getSettingsHolder().setStyle("-fx-background-color: -fx-darkbackground"); - hidingPane.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); - - arrowPane.getChildren().add(hidingPane.getShowButton()); - - hidingPane.getShowButton().setGraphic(PamGlyphDude.createPamIcon("mdi2c-cog", - PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize)); - this.setRight(hidingPane); //bit of a hack but works. - hidingPane.showHidePane(false); - - //this makes the hide button appear top right which is nicer for closing the pane. - StackPane.setAlignment(hidingPane.getHideButton(), Pos.TOP_RIGHT); - //hidingPane.removeHideButton(); - hidingPane.styleHideButton(hidingPane.getHideButton(), Side.LEFT); - + //set default size detectionDisplayHolder.setPrefSize(500, 400); detectionDisplayHolder.setPadding(new Insets(0,10,0,0)); @@ -234,15 +318,15 @@ public class DetectionGroupDisplay extends PamBorderPane { */ private void setLabelText(){ if (detectionGroup.size()>1){ - label.setText(detectionGroup.get(currentUnitIndex).getParentDataBlock().getDataName() + " UID: " + detectionGroup.get(currentUnitIndex).getUID() + + dataLabel.setText(detectionGroup.get(currentUnitIndex).getParentDataBlock().getDataName() + " UID: " + detectionGroup.get(currentUnitIndex).getUID() + ": " + (currentUnitIndex+1) + " of " + detectionGroup.size()); } else if (detectionGroup.size()==1){ - label.setText(detectionGroup.get(currentUnitIndex).getParentDataBlock().getDataName() + " UID: " + detectionGroup.get(currentUnitIndex).getUID()); + dataLabel.setText(detectionGroup.get(currentUnitIndex).getParentDataBlock().getDataName() + " UID: " + detectionGroup.get(currentUnitIndex).getUID()); } else { //selected area with data units. - label.setText("No data units in area"); + dataLabel.setText("No data units in area"); } //add sub detection count if there are sub detections. @@ -250,7 +334,7 @@ public class DetectionGroupDisplay extends PamBorderPane { SuperDetection superDet = (SuperDetection) detectionGroup.get(currentUnitIndex); int subCount = superDet.getSubDetectionsCount(); if (subCount>0) { - label.setText(label.getText()+ " : " + superDet.getSubDetectionsCount() + " sub detection" + (subCount>1 ? "s":"")); + dataLabel.setText(dataLabel.getText()+ " : " + superDet.getSubDetectionsCount() + " sub detection" + (subCount>1 ? "s":"")); } } } @@ -289,7 +373,7 @@ public class DetectionGroupDisplay extends PamBorderPane { public void clearDisplay() { setDataUnit(null); detectionDisplay.clearPane(); - this.label.setText(""); + this.dataLabel.setText(""); } @@ -297,14 +381,15 @@ public class DetectionGroupDisplay extends PamBorderPane { * Sets the current in the display. * @param pamDataUnit - the current data unit to set. * @param detectionDisplay- the detection display plot to set the data unit for. + * @return true of a new data info has been added - usually means a different type of detection to display compared to the last detection. */ - public void setDataUnit(PamDataUnit dataUnit){ + public boolean setDataUnit(PamDataUnit dataUnit){ detectionDisplay.clearPane(); if (dataUnit==null) { detectionDisplay.removeDataInfo(); - return; + return true; } // TDDataInfoFX dataInfo = this.tdGraphFX.findDataInfo(dataUnit); @@ -336,7 +421,7 @@ public class DetectionGroupDisplay extends PamBorderPane { dDataInfoHashMap.put(dataUnit.getParentDataBlock(), dDataInfo); } - if (dDataInfo==null) return; + if (dDataInfo==null) return true; //only change the dDataInfo if it's different,. boolean newDataInfo = false; @@ -364,6 +449,25 @@ public class DetectionGroupDisplay extends PamBorderPane { // clearSingleType(); //TODO....highlight data unit. + + return newDataInfo; + } + + + /** + * Attempts to set the detectionPlot + * @param plotName + * @return + */ + public boolean setDetectionPlot(String plotName) { + + //set the current detection plot based in the name + boolean setOk = currentDataInfo.setCurrentDetectionPlot(plotName); + + //update the detection settings pane so it shows the correct plot names etc. + detectionDisplay.getDataTypePane().notifyDataChange(); + + return setOk; } @@ -374,7 +478,7 @@ public class DetectionGroupDisplay extends PamBorderPane { */ public void triggerListeners(PamDataUnit oldDataUnit, PamDataUnit newDataUnit) { for (GroupDisplayListener aListener : displayListeners) { - aListener.newDataUnitSlected(oldDataUnit, newDataUnit); + aListener.newDataUnitSelected(oldDataUnit, newDataUnit); } } @@ -426,4 +530,23 @@ public class DetectionGroupDisplay extends PamBorderPane { return detectionGroup.get(currentUnitIndex); } + + /** + * Show the scroll bar which allows the user to chnage time limits. + * @param enableScrollBarPane - true to enable the time scroll bar. + */ + public void setEnableScrollBar(boolean enableScrollBarPane) { + this.detectionDisplay.setEnableScrollBar(enableScrollBarPane); + } + + + +// @Override +// public boolean requestNodeSettingsPane() { +// if (dDPlotPane.getHidePane(Side.RIGHT)!=null) dDPlotPane.getHidePane(Side.RIGHT).showHidePane(true); +// if (dDPlotPane.getHidePane(Side.LEFT)!=null) dDPlotPane.getHidePane(Side.LEFT).showHidePane(true); +// return true; +// } + + } \ No newline at end of file diff --git a/src/detectionPlotFX/DetectionGroupDisplayFX.java b/src/detectionPlotFX/DetectionGroupDisplayFX.java new file mode 100644 index 00000000..a06b520d --- /dev/null +++ b/src/detectionPlotFX/DetectionGroupDisplayFX.java @@ -0,0 +1,242 @@ +package detectionPlotFX; + +import java.io.Serializable; + +import PamController.PamControlledUnitSettings; +import PamController.PamController; +import PamController.PamSettingManager; +import PamController.PamSettings; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import javafx.geometry.Side; +import javafx.scene.layout.Region; +import pamViewFX.fxNodes.internalNode.PamInternalPane; +import userDisplayFX.UserDisplayControlFX; +import userDisplayFX.UserDisplayNodeFX; + +/** + * A group detection display with all the bits added to allow the display to be used in the FX GUI as + * a stand-alone user display. + * + * @author Jamie Macaulay + * + */ +public class DetectionGroupDisplayFX extends DetectionGroupDisplay implements UserDisplayNodeFX, PamSettings{ + + private DetectionPlotParams detectionPlotParams = new DetectionPlotParams(); + + /** + * Reference to the internal frame that migfth hold this graph. + */ + private PamInternalPane internalFrame; + + private DetectionDisplayControl2 displayControl; + + private PamDataUnit currentDetection; + + public DetectionGroupDisplayFX(DetectionDisplayControl2 displayControl){ + super(DetectionGroupDisplay.DISPLAY_COMPACT); + this.displayControl = displayControl; + //register the settings. + PamSettingManager.getInstance().registerSettings(this); + } + + @Override + public String getName() { + return "Detection Dsiplay"; + } + + @Override + public Region getNode() { + // TODO Auto-generated method stub + return this; + } + + @Override + public void openNode() { + // TODO Auto-generated method stub + + } + + @Override + public boolean isStaticDisplay() { + return false; + } + + @Override + public boolean isResizeableDisplay() { + return true; + } + + @Override + public boolean isMinorDisplay() { + return true; + } + + @Override + public void closeNode() {}; + @Override + public DetectionPlotParams getDisplayParams() { + return this.detectionPlotParams; + } + + private void prepareDisplayParams() { + if (displayControl.getUserDisplayProcess().getParentDataBlock()!=null) { + detectionPlotParams.dataSource = displayControl.getUserDisplayProcess().getParentDataBlock().getLongDataName(); + } + else detectionPlotParams.dataSource = null; + + if (this.internalFrame!=null) { + //need to use the parent node because inside an internal pane. + detectionPlotParams.positionX=internalFrame.getInternalRegion().getLayoutX(); + detectionPlotParams.positionY=internalFrame.getInternalRegion().getLayoutY(); + detectionPlotParams.sizeX=internalFrame.getInternalRegion().getWidth(); + detectionPlotParams.sizeY=internalFrame.getInternalRegion().getHeight(); + } + } + + + + @Override + public void setFrameHolder(PamInternalPane internalFrame) { + this.internalFrame=internalFrame; + + } + + @Override + public boolean requestNodeSettingsPane() { + this.showSettingsPane(true); + + return true; + } + + + private void showSettingsPane(boolean b) { + this.detectionDisplay.getHidingPane(Side.RIGHT).showHidePane(b);; + + } + + @Override + public void notifyModelChanged(int changeType) { + + switch (changeType) { + case PamController.INITIALIZATION_COMPLETE: + PamDataBlock dataBlock = PamController.getInstance().getDataBlockByLongName(detectionPlotParams.dataSource); + //set the correct parent data block if on exists + displayControl.getUserDisplayProcess().setParentDataBlock(dataBlock); + displayControl.displayToDataModel(dataBlock); + + break; + } + + } + + /** + * Set the display parameterts. + * @param detectionPlotParams + */ + public void setDisplayParams(DetectionPlotParams detectionPlotParams) { + this.detectionPlotParams = detectionPlotParams; + + } + + @Override + public Serializable getSettingsReference() { + Serializable set = prepareSerialisedSettings(); + return set; + } + + /** + * Prepare settings for saving. + * @return + */ + private Serializable prepareSerialisedSettings() { + if (detectionDisplay==null) return null; + prepareDisplayParams(); + detectionPlotParams = getDisplayParams(); + +// System.out.println("SAVE DETECTION DISPLAY DATA SOURCE: " + detectionPlotParams.dataSource); +// System.out.println("SAVE DETECTION DISPLAY TAB NAME: " + detectionPlotParams.tabName); + + return detectionPlotParams; + } + + + @Override + public long getSettingsVersion() { + return DetectionPlotParams.serialVersionUID; + } + + + @Override + public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { + try{ + return restoreSettings((DetectionPlotParams) pamControlledUnitSettings.getSettings()); + } + catch(Exception e){ + e.printStackTrace(); + return false; + } + } + + + private boolean restoreSettings(DetectionPlotParams settings) { + if (settings == null) { + return false; + } + +// System.out.println("LOAD DETECTION DISPLAY DATA SOURCE: " + settings.dataSource); +// System.out.println("LOAD DETECTION DISPLAY DATA SOURCE: " + settings.tabName); + + this.detectionPlotParams = settings.clone(); + return true; + } + + @Override + public String getUnitName() { + return displayControl.getUnitName(); + } + + @Override + public String getUnitType() { + return displayControl.getUnitType(); + } + + @Override + public UserDisplayControlFX getUserDisplayControl() { + return displayControl; + } + + @Override + public boolean setDataUnit(PamDataUnit dataUnit){ + + /** + * The extra stuff here is to make sure that the plot types for a specific detection are saved. So for example + * if viewing click spectrum then the spectrum plot is selected whenever 1) PAMGuard is opened again or 2) switching from + * one type of detection ot another e.g. whistle to click, then the click does nto revert to shwoing a waveform instead + * of spectrum. + */ + + if (currentDetection!=null) { + //save the current selected detection plot for the particular type of data unit. + String detectionPlotName = this.getDetectionDisplay().getCurrentDataInfo().getCurrentDetectionPlot().getName(); + //System.out.println("SET CURRENT DETECTION PLOT TO USE IS: " + detectionPlotName); + detectionPlotParams.dataAxisMap.put(currentDetection.getParentDataBlock().getLongDataName(), detectionPlotName); + } + + this.currentDetection = dataUnit; + + //setup the new data unit + boolean newDataInfo = super.setDataUnit(dataUnit); + + if (newDataInfo && dataUnit!=null) { + //if there's a new data info we may want to set the detection back to it's most recent selection + String detectionPlotName = detectionPlotParams.dataAxisMap.get(dataUnit.getParentDataBlock().getLongDataName()); +// System.out.println("THE CURRENT DETECTION PLOT TO USE IS: " + detectionPlotName); + setDetectionPlot(detectionPlotName); + } + + return newDataInfo; + } + +} diff --git a/src/detectionPlotFX/DetectionPlotParams.java b/src/detectionPlotFX/DetectionPlotParams.java index 5085be83..f951e1c6 100644 --- a/src/detectionPlotFX/DetectionPlotParams.java +++ b/src/detectionPlotFX/DetectionPlotParams.java @@ -1,5 +1,11 @@ package detectionPlotFX; +import java.util.HashMap; + +import PamModel.parametermanager.ManagedParameters; +import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; +import dataPlotsFX.TDParametersFX; import userDisplayFX.UserDisplayNodeParams; /** @@ -8,11 +14,45 @@ import userDisplayFX.UserDisplayNodeParams; * @author Jamie Macaulay * */ -public class DetectionPlotParams extends UserDisplayNodeParams { +public class DetectionPlotParams extends UserDisplayNodeParams implements Cloneable, ManagedParameters { + /** * */ - private static final long serialVersionUID = 1L; + static final long serialVersionUID = 3L; + + /** + * The data source for the detection plot. + */ + public String dataSource = null; + + + /** + * Saves which data axis is used for which data block. The key is the data block long name and the + * result is the name of the plot e.g. waveform. In this way users can set how they want the data plots to display + * different types of data units and the dispay types are saved on PAMGuard opne and close. + */ + public HashMap dataAxisMap = new HashMap(); + + + /* (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public DetectionPlotParams clone() { + try { + return (DetectionPlotParams) super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public PamParameterSet getParameterSet() { + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); + return ps; + } } diff --git a/src/detectionPlotFX/GeneralDDPlotProvider.java b/src/detectionPlotFX/GeneralDDPlotProvider.java new file mode 100644 index 00000000..75825b57 --- /dev/null +++ b/src/detectionPlotFX/GeneralDDPlotProvider.java @@ -0,0 +1,20 @@ +package detectionPlotFX; + +import PamguardMVC.PamDataBlock; +import detectionPlotFX.data.DDDataInfo; +import detectionPlotFX.data.DDDataProvider; +import detectionPlotFX.layout.DetectionPlotDisplay; + +public class GeneralDDPlotProvider extends DDDataProvider { + + public GeneralDDPlotProvider(PamDataBlock parentDataBlock) { + super(parentDataBlock); + // TODO Auto-generated constructor stub + } + + @Override + public DDDataInfo createDataInfo(DetectionPlotDisplay tdGraph) { + return null; + } + +} diff --git a/src/detectionPlotFX/GroupDisplayListener.java b/src/detectionPlotFX/GroupDisplayListener.java index 2f4f5e55..1f1483c1 100644 --- a/src/detectionPlotFX/GroupDisplayListener.java +++ b/src/detectionPlotFX/GroupDisplayListener.java @@ -16,5 +16,5 @@ public interface GroupDisplayListener { * @param oldDataUnit - the old data unit. * @param newDataUnit - the new selected data unit. */ - public void newDataUnitSlected(PamDataUnit oldDataUnit, PamDataUnit newDataUnit); + public void newDataUnitSelected(PamDataUnit oldDataUnit, PamDataUnit newDataUnit); } diff --git a/src/detectionPlotFX/data/DDDataInfo.java b/src/detectionPlotFX/data/DDDataInfo.java index 5fe4a66f..01c59f79 100644 --- a/src/detectionPlotFX/data/DDDataInfo.java +++ b/src/detectionPlotFX/data/DDDataInfo.java @@ -2,7 +2,6 @@ package detectionPlotFX.data; import java.util.ArrayList; import PamController.PamController; -import PamDetection.PamDetection; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import detectionPlotFX.DDScaleInfo; @@ -12,7 +11,6 @@ import detectionPlotFX.projector.DetectionPlotProjector; import javafx.scene.canvas.GraphicsContext; import javafx.scene.shape.Rectangle; import pamViewFX.fxNodes.PamBorderPane; -import pamViewFX.fxNodes.pamAxis.PamAxisFX; import pamViewFX.fxNodes.pamAxis.PamAxisPane2; /** @@ -23,23 +21,23 @@ import pamViewFX.fxNodes.pamAxis.PamAxisPane2; * */ public abstract class DDDataInfo { - - + + /** * Reference to the detection display plot */ private DetectionPlotDisplay dDPlot; - + /** * Pane whihc holds plot specifc settings. */ private PamBorderPane settingsPane= new PamBorderPane(); - + /** * The data block which the DDDataInfo is associated with */ private PamDataBlock pamDataBlock; - + /** * The scale information. Holds the min and max values for the x and y axis. */ @@ -49,12 +47,12 @@ public abstract class DDDataInfo { * True if in viewer mode. */ private boolean isViewer=false; - + /** * List of detection plots which can display the data unit. */ private ArrayList detectionPlots; - + /* * The index of the current detection plot */ @@ -64,23 +62,23 @@ public abstract class DDDataInfo { * The sample rate */ private float hardSampleRate=-1; - -// /** -// * Create the DDDataInfo -// * @param dDDataProvider - the data provider -// * @param dDPlot - the detection plot -// * @param pamDataBlock - the parent datablock -// */ -// public DDDataInfo(DetectionPlotDisplay dDPlot, PamDataBlock pamDataBlock) { -// super(); -// this.dDPlot=dDPlot; -// this.pamDataBlock = pamDataBlock; -// this.isViewer = (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW); -// detectionPlots=new ArrayList(); -// } - - + + // /** + // * Create the DDDataInfo + // * @param dDDataProvider - the data provider + // * @param dDPlot - the detection plot + // * @param pamDataBlock - the parent datablock + // */ + // public DDDataInfo(DetectionPlotDisplay dDPlot, PamDataBlock pamDataBlock) { + // super(); + // this.dDPlot=dDPlot; + // this.pamDataBlock = pamDataBlock; + // this.isViewer = (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW); + // detectionPlots=new ArrayList(); + // } + + /** * Create the DDDataInfo * @param dDDataProvider - the dataplot provider @@ -94,7 +92,7 @@ public abstract class DDDataInfo { this.isViewer = (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW); detectionPlots=new ArrayList(); } - + /** * Create the DDdata info wothout reference to a provider * @param dDPlot - - the data plot @@ -107,20 +105,20 @@ public abstract class DDDataInfo { this.isViewer = (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW); detectionPlots=new ArrayList(); } - -// /** -// * Create the DDdata info wothout reference to a provider -// * @param dDPlot - - the data plot -// * @param pamDataBlock - the parent datablopck -// */ -// public DDDataInfo(DetectionPlotDisplay dDPlot, float sR) { -// super(); -// this.dDPlot=dDPlot; -// this.hardSampleRate=sR; -// this.isViewer = (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW); -// detectionPlots=new ArrayList(); -// } - + + // /** + // * Create the DDdata info wothout reference to a provider + // * @param dDPlot - - the data plot + // * @param pamDataBlock - the parent datablopck + // */ + // public DDDataInfo(DetectionPlotDisplay dDPlot, float sR) { + // super(); + // this.dDPlot=dDPlot; + // this.hardSampleRate=sR; + // this.isViewer = (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW); + // detectionPlots=new ArrayList(); + // } + /** * Get the data block for this information * @return the datablock. This can be null. @@ -128,8 +126,8 @@ public abstract class DDDataInfo { public PamDataBlock getDataBlock() { return pamDataBlock; } - - + + /** * Get the sample rate. This is acquired form the datablock if one exists. Otherwise a hard * wired sample rate is used. @@ -139,9 +137,9 @@ public abstract class DDDataInfo { if (pamDataBlock!=null) return pamDataBlock.getSampleRate(); else return hardSampleRate; } - - - + + + /** * Set the sample rate. This is only used if the parent data block is null. * @return the sample rate. @@ -149,7 +147,7 @@ public abstract class DDDataInfo { public void setHardSampleRate(float hardSampleRate) { this.hardSampleRate = hardSampleRate; } - + /** * Add a type of data unit to the list. * @param unitType String name of the data unit. @@ -157,7 +155,7 @@ public abstract class DDDataInfo { public void addDetectionPlot(DetectionPlot detectionPlot) { detectionPlots.add(detectionPlot); } - + /** * Get a detection plot * @param the index of the detection plot to get. @@ -165,14 +163,14 @@ public abstract class DDDataInfo { public DetectionPlot getDetectionPlot(int i) { return detectionPlots.get(i); } - + /** * The number of possible detection plots for the data unit. */ public int getDetectionPlotCount(){ return detectionPlots.size(); } - + /** * Get the scale information. This holds the min and max values for the x and y axis along with other * axis related parameters. @@ -188,42 +186,57 @@ public abstract class DDDataInfo { public DetectionPlotDisplay getDetectionPlotDisplay() { return dDPlot; } - + /* * Sets the current detection plot to be shown. * @param index of the plot in the plot list. */ public void setCurrentDetectionPlot(int index) { - this.currentDetectionPlotIndex=index; - if (index>=0 && index=0 && index { if (currentDetectionPlotIndex<0 || currentDetectionPlotIndex>=detectionPlots.size()) return null; return detectionPlots.get(currentDetectionPlotIndex); } - - + + /** * Get the settings pane for this DDDataInfo. This holds plot specific settings. * @return @@ -243,7 +256,7 @@ public abstract class DDDataInfo { public PamBorderPane getSettingsPane(){ return settingsPane; } - + /** * Paint data into the graphics window. @@ -255,17 +268,17 @@ public abstract class DDDataInfo { * @param a flag with extra information - e.g. if this is a scroll bar paint or not. */ public void drawData(GraphicsContext g, Rectangle windowRect, DetectionPlotProjector projector, T pamDataUnit, int flag){ - + if (getCurrentDetectionPlot()==null) return; - + g.clearRect(0, 0, windowRect.getWidth(), windowRect.getHeight()); - + getCurrentDetectionPlot().setupAxis((PamDataUnit) pamDataUnit, this.getHardSampleRate(), projector); - + getCurrentDetectionPlot().paintPlot((PamDataUnit) pamDataUnit, g, windowRect, projector, flag); - + } - + /** * Paint data into the graphics window. @@ -276,8 +289,8 @@ public abstract class DDDataInfo { * @param newDataUnit - the data unit to plot. */ public void drawData(GraphicsContext g, Rectangle windowRect, DetectionPlotProjector projector, T pamDataUnit){ - - drawData( g, windowRect, projector, pamDataUnit, DetectionPlot.STANDARD_DRAW); + + drawData( g, windowRect, projector, pamDataUnit, DetectionPlot.STANDARD_DRAW); } @@ -286,23 +299,23 @@ public abstract class DDDataInfo { getCurrentDetectionPlot().setupAxis((PamDataUnit) pamDataUnit, this.getHardSampleRate(), projector); } -// /** -// * Gets a value for a specific data unit which should be in the -// * same units as the scale information. This will then be -// * converted into a plot position by the TDGraph. -// * @param pamDataUnit -// * @return data value or null if this data point should not be plotted. -// */ -// abstract public Double[] getYValue(PamDataUnit pamDataUnit); -// -// /** -// * Gets a value for a specific data unit which should be in the -// * same units as the scale information. This will then be -// * converted into a plot position by the TDGraph. -// * @param pamDataUnit -// * @return data value or null if this data point should not be plotted. -// */ -// abstract public Double[] getXValue(PamDataUnit pamDataUnit); - + // /** + // * Gets a value for a specific data unit which should be in the + // * same units as the scale information. This will then be + // * converted into a plot position by the TDGraph. + // * @param pamDataUnit + // * @return data value or null if this data point should not be plotted. + // */ + // abstract public Double[] getYValue(PamDataUnit pamDataUnit); + // + // /** + // * Gets a value for a specific data unit which should be in the + // * same units as the scale information. This will then be + // * converted into a plot position by the TDGraph. + // * @param pamDataUnit + // * @return data value or null if this data point should not be plotted. + // */ + // abstract public Double[] getXValue(PamDataUnit pamDataUnit); + } diff --git a/src/detectionPlotFX/layout/DDDataPane2.java b/src/detectionPlotFX/layout/DDDataPane2.java index b754f1cb..240a54f3 100644 --- a/src/detectionPlotFX/layout/DDDataPane2.java +++ b/src/detectionPlotFX/layout/DDDataPane2.java @@ -87,7 +87,6 @@ public class DDDataPane2 extends PamBorderPane { dataPlots.getItems().add(ddDataInfo.getDetectionPlot(i).getName()); - if (ddDataInfo.getCurrentDetectionPlot()==ddDataInfo.getDetectionPlot(i)) { index=i; } diff --git a/src/detectionPlotFX/layout/DetectionPlotDisplay.java b/src/detectionPlotFX/layout/DetectionPlotDisplay.java index 865eecb9..439301e9 100644 --- a/src/detectionPlotFX/layout/DetectionPlotDisplay.java +++ b/src/detectionPlotFX/layout/DetectionPlotDisplay.java @@ -37,7 +37,7 @@ import userDisplayFX.UserDisplayNodeParams; * @author Jamie Macaulay * */ -public class DetectionPlotDisplay extends PamBorderPane implements UserDisplayNodeFX { +public class DetectionPlotDisplay extends PamBorderPane { private static final double PREF_SETTINGS_WIDTH = 250; @@ -73,8 +73,7 @@ public class DetectionPlotDisplay extends PamBorderPane implements UserDisplayNo /** * Pane which sits on the right hand side and */ - private PamBorderPane settingsHolder; - + private PamBorderPane settingsHolder; /** @@ -82,11 +81,7 @@ public class DetectionPlotDisplay extends PamBorderPane implements UserDisplayNo */ private boolean enableSettingsButton = true; - /** - * Convenience reference to the settings css style - */ - String cssSettingsResource=PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS(); - + /** * The pane which allows users to settings specific to the type of display */ @@ -257,56 +252,23 @@ public class DetectionPlotDisplay extends PamBorderPane implements UserDisplayNo return isViewer; } - - @Override - public String getName() { - return "Detection Display"; - } - - @Override - public Region getNode() { - return this; - } - - @Override - public void openNode() { - // TODO Auto-generated method stub - - } - - @Override - public boolean isStaticDisplay() { - return false; - } - - @Override - public boolean isResizeableDisplay() { - return true; - } - - @Override - public void closeNode() { - // TODO Auto-generated method stub - - } - - @Override - public void notifyModelChanged(int changeType) { - switch (changeType){ - case PamControllerInterface.INITIALIZATION_COMPLETE: - - break; - case PamControllerInterface.ADD_CONTROLLEDUNIT: - this.dataSettingsPane.notifyDataChange(); - break; - case PamControllerInterface.CHANGED_PROCESS_SETTINGS: - //this is were the data block may have been added. Need to add an observer to this data block to say when the thing has - //thing has a new detection. - this.dataSettingsPane.notifyDataChange(); - break; - } - - } +// @Override +// public void notifyModelChanged(int changeType) { +// switch (changeType){ +// case PamControllerInterface.INITIALIZATION_COMPLETE: +// +// break; +// case PamControllerInterface.ADD_CONTROLLEDUNIT: +// this.dataSettingsPane.notifyDataChange(); +// break; +// case PamControllerInterface.CHANGED_PROCESS_SETTINGS: +// //this is were the data block may have been added. Need to add an observer to this data block to say when the thing has +// //thing has a new detection. +// this.dataSettingsPane.notifyDataChange(); +// break; +// } +// +// } /** * Set the DataInfo for the display. @@ -503,12 +465,7 @@ public class DetectionPlotDisplay extends PamBorderPane implements UserDisplayNo detectionDisplayControl.dataModelToDisplay(); } - @Override - public boolean requestNodeSettingsPane() { - if (dDPlotPane.getHidePane(Side.RIGHT)!=null) dDPlotPane.getHidePane(Side.RIGHT).showHidePane(true); - if (dDPlotPane.getHidePane(Side.LEFT)!=null) dDPlotPane.getHidePane(Side.LEFT).showHidePane(true); - return true; - } + /** * Get an axis pane @@ -528,11 +485,7 @@ public class DetectionPlotDisplay extends PamBorderPane implements UserDisplayNo return dDPlotPane.getHidePane(side); } - @Override - public boolean isMinorDisplay() { - // these are generally smaller minor displays- only used for automatic resize. - return true; - } + /** * Called whenever a new datablock is added. @@ -613,18 +566,6 @@ public class DetectionPlotDisplay extends PamBorderPane implements UserDisplayNo return detectionPlotProjector; } - - @Override - public UserDisplayNodeParams getDisplayParams() { - return this.detectionPlotParams; - } - - - @Override - public void setFrameHolder(PamInternalPane internalFrame) { - // TODO Auto-generated method stub - } - /** * The pane which holds settings for the the current plot. diff --git a/src/detectionPlotFX/plots/RawDataOrder.java b/src/detectionPlotFX/plots/RawDataOrder.java index 5627e9ca..718bffed 100644 --- a/src/detectionPlotFX/plots/RawDataOrder.java +++ b/src/detectionPlotFX/plots/RawDataOrder.java @@ -186,7 +186,6 @@ public abstract class RawDataOrder { */ public boolean resetForLoad(long dataStart, long dataEnd, float sampleRate, int channel) { - // System.out.println("RESET FOR LOAD: "); count=0; this.dataStart=dataStart; this.dataEnd=dataEnd; @@ -194,6 +193,8 @@ public abstract class RawDataOrder { this.fftSampleRate=sampleRate; int dataFrame=(int) (sampleRate*((dataEnd-dataStart)/1000.)); + +// System.out.println("RESET FOR LOAD: " + dataFrame); if (dataFrame>=maxMb){ System.err.println("The raw data is way too big"); @@ -213,12 +214,13 @@ public abstract class RawDataOrder { } private int currentIndex=0; + /*** * Called whenever new raw data is acquired * @param dataUnit */ private void newRawData(RawDataUnit dataUnit) { - // System.out.println("New raw data " + count); +// System.out.println("New raw data " + count); // try{ if (PamUtils.hasChannel(dataUnit.getChannelBitmap(), channel)){ diff --git a/src/detectionPlotFX/plots/SpectrumPlot.java b/src/detectionPlotFX/plots/SpectrumPlot.java index bd7e8966..752130d7 100644 --- a/src/detectionPlotFX/plots/SpectrumPlot.java +++ b/src/detectionPlotFX/plots/SpectrumPlot.java @@ -1,6 +1,7 @@ package detectionPlotFX.plots; import Layout.PamAxis; +import PamUtils.Coordinate3d; import PamguardMVC.PamDataUnit; import detectionPlotFX.layout.DetectionPlot; import detectionPlotFX.layout.DetectionPlotDisplay; @@ -131,7 +132,7 @@ public abstract class SpectrumPlot implements Detection if (data ==null) return; - this.sR=sR; + this.sR=sR; int[] minmax = getAxisMinMaxSamples(plotProjector); @@ -155,21 +156,25 @@ public abstract class SpectrumPlot implements Detection /** * Get the minimum and maximum samples currently shown in the plot projector - * @param plotProjector - the pot projector. + * @param plotProjector - the plot projector. * @return the minimum and maximum samples */ private int[] getAxisMinMaxSamples(DetectionPlotProjector plotProjector) { int[] minmax = new int[2]; // System.out.println("Plot projector: " + plotProjector.getAxis(Side.TOP).getMinVal() + " " + -// plotProjector.getAxis(Side.TOP).getMaxVal()); - - minmax[0] = (int) ((plotProjector.getAxis(Side.TOP).getMinVal()/1000.)*sR); //this is in milliseconds - minmax[1] = (int) ((plotProjector.getAxis(Side.TOP).getMaxVal()/1000.)*sR); +// plotProjector.getAxis(Side.TOP).getMaxVal() + " sR: " + getSampleRate()); + minmax[0] = (int) ((plotProjector.getAxis(Side.TOP).getMinVal()/1000.)*getSampleRate()); //this is in milliseconds + minmax[1] = (int) ((plotProjector.getAxis(Side.TOP).getMaxVal()/1000.)*getSampleRate()); + return minmax; } + private double getSampleRate() { + return sR; + } + @Override public void paintPlot(D data, GraphicsContext gc, Rectangle rectangle, DetectionPlotProjector projector, int flag) { // this.lastDataUnit=data; @@ -293,13 +298,13 @@ public abstract class SpectrumPlot implements Detection */ private void drawSpectrum(GraphicsContext g2, Rectangle clipRect, double[][] clickLineData, DetectionPlotProjector projector){ - double xScale, yScale; + double yScale; double x0, y0, x1, y1; Rectangle r = clipRect; double scale = 1./(maxVal*1.1); - xScale = r.getWidth() / (clickLineData[0].length - 1); +// xScale = r.getWidth() / (clickLineData[0].length - 1); yScale = r.getHeight() / (maxVal * 1.1); double[] scaledDataX; @@ -315,12 +320,15 @@ public abstract class SpectrumPlot implements Detection scaledDataX = new double[clickLineData[iChan].length+2]; scaledDataY = new double[clickLineData[iChan].length+2]; scaledDataY[0]= r.getHeight() ; + Coordinate3d coord; for (int i = 1; i < clickLineData[iChan].length+1; i++) { - x1 = (i * xScale); - // y1 = r.height - (int) (yScale * clickLineData[iChan][i]); - //System.out.println("Hello Projector: " +projector); - y1 = projector.getCoord3d(0,clickLineData[iChan][i-1]*scale,0).y; +// x1 = (i * xScale); +// System.out.println("Freq: " + ((i-1)*1./clickLineData[iChan].length+1)*(getSampleRate()/2./1000.) + " " + (getSampleRate()/2./1000.) + " " + ((i-1)*1./clickLineData[iChan].length)); + + coord = projector.getCoord3d(((i-1)*1./clickLineData[iChan].length)*(getSampleRate()/2./1000.) ,clickLineData[iChan][i-1]*scale,0); // g2.strokeLine(x0, y0, x1, y1); + x1 = coord.x; + y1=coord.y; scaledDataX[i]=x1; scaledDataY[i]=y1; @@ -400,7 +408,7 @@ public abstract class SpectrumPlot implements Detection } lastCepChoice = spectrumPlotParams.plotCepstrum; //drawLogSpectrum(g,clipRect,clickLineData, eventLineData); - drawLogSpectrum(g,clipRect,clickLineData); + drawLogSpectrum(g,clipRect,clickLineData, projector); } } @@ -412,11 +420,11 @@ public abstract class SpectrumPlot implements Detection * @param eventLineData * @param clickLineData */ - public void drawLogSpectrum(GraphicsContext g2, Rectangle clipRect, double[][] clickLineData){ + public void drawLogSpectrum(GraphicsContext g2, Rectangle clipRect, double[][] clickLineData, DetectionPlotProjector projector){ Rectangle r = clipRect; double xScale, yScale, scaleLim; - int x0, y0, x1, y1; + double x0, y0, x1, y1; Color channelColour; // if (eventLineData!=null && isViewer==true && clickSpectrumParams.showEventInfo==true){ @@ -457,12 +465,16 @@ public abstract class SpectrumPlot implements Detection scaleLim = Math.abs(spectrumPlotParams.logRange); yScale = r.getHeight() / Math.abs(scaleLim); + Coordinate3d coord; for (int iChan = 0; iChan < logSpectrum.length; iChan++) { g2.setStroke(PamColorsFX.getInstance().getChannelColor(iChan)); x0 = 0; y0 = (int) (yScale * (maxLogVal-logSpectrum[iChan][0])); for (int i = 1; i < logSpectrum[iChan].length; i++) { x1 = (int) (i * xScale); + + coord = projector.getCoord3d(((i-1)*1./clickLineData[iChan].length)*(getSampleRate()/2./1000.) ,0,0); + x1 = coord.x; y1 = (int) (yScale * (maxLogVal-logSpectrum[iChan][i])); g2.strokeLine(x0, y0, x1, y1); x0 = x1; diff --git a/src/detectionPlotFX/plots/WaveformPlot.java b/src/detectionPlotFX/plots/WaveformPlot.java index 3b790802..5d0abe90 100644 --- a/src/detectionPlotFX/plots/WaveformPlot.java +++ b/src/detectionPlotFX/plots/WaveformPlot.java @@ -146,6 +146,7 @@ public abstract class WaveformPlot implements DetectionPl public void paintPlot(D pamDetection, GraphicsContext gc, Rectangle rectangle, DetectionPlotProjector plotProjector, int flag){ //if (currentDetection == pamDetection) return; currentDetection = pamDetection; + if (flag == DetectionPlot.SCROLLPANE_DRAW) { //drawing a datagram in the scroll pane. //System.out.println("WaveformPlot: Print scroll datagram: " + pamDetection+ " " + rectangle); @@ -166,6 +167,8 @@ public abstract class WaveformPlot implements DetectionPl private void paintScrollDataGram(D pamDetection, GraphicsContext gc, Rectangle clipRect, DetectionPlotProjector projector) { currentWaveform=getWaveform(pamDetection); + if (currentWaveform==null) return; + paintWaveform(currentWaveform, currentDetection.getSequenceBitmap(), gc, clipRect, 0, currentWaveform[0].length, log2Amplitude, null, true, false); @@ -180,6 +183,9 @@ public abstract class WaveformPlot implements DetectionPl */ private void forcePaintPlot(D pamDetection, GraphicsContext gc, Rectangle rectangle, DetectionPlotProjector projector){ currentWaveform=getWaveform(pamDetection); + + if (currentWaveform==null) return; + waveformPlotParams.invert=false; setYScale(); paintWaveform(currentWaveform, gc, rectangle, projector, null); diff --git a/src/detectionPlotFX/projector/DetectionPlotProjector.java b/src/detectionPlotFX/projector/DetectionPlotProjector.java index 05ac2ab2..40a5c6ff 100644 --- a/src/detectionPlotFX/projector/DetectionPlotProjector.java +++ b/src/detectionPlotFX/projector/DetectionPlotProjector.java @@ -21,13 +21,13 @@ public class DetectionPlotProjector extends GeneralProjector { /** - * The minimum limit of the scroll bar. + * The minimum limit of the scroll bar. In millis */ public double minScrollLimit = 0; /** - * The maximum limit of the scroll bar. i.e. the maximum value that can be scrolled to (e.g. the length of a waveform) + * The maximum limit of the scroll bar. i.e. the maximum value that can be scrolled to (e.g. the length of a waveform) In millis */ public double maxScrollLimit = 1; diff --git a/src/detectionPlotFX/rawDDPlot/RawSpectrumPlot.java b/src/detectionPlotFX/rawDDPlot/RawSpectrumPlot.java index a2d6a7e0..ef98e221 100644 --- a/src/detectionPlotFX/rawDDPlot/RawSpectrumPlot.java +++ b/src/detectionPlotFX/rawDDPlot/RawSpectrumPlot.java @@ -54,30 +54,7 @@ public class RawSpectrumPlot extends SpectrumPlot { plotProjector.setEnableScrollBar(true); } - -// @Override -// public double[][] getPowerSpectrum(PamDataUnit data) { -// return ((RawDataHolder) data).getDataTransforms().getPowerSpectrum(((RawDataHolder) data).getWaveData()[0].length); -// } -// @Override -// public double[][] getCepstrum(PamDataUnit data) { -// return ((RawDataHolder) data).getDataTransforms().getCepstrum(((RawDataHolder) data).getWaveData()[0].length); -// //return data.getRawDataTransforms().getCepstrum(channel, cepLength) -// } - - - -// -// public ClickSpectrumPlot(DetectionPlotDisplay displayPlot) { -// super(displayPlot); -// } -// -// -// @Override -// public String getName() { -// return "Spectrum"; -// } -// + /** * New click added to the display. * @param newClick @@ -85,10 +62,13 @@ public class RawSpectrumPlot extends SpectrumPlot { */ private void newClick(PamDataUnit newClick, int bin1, int bin2, boolean forceRecalc){ - //System.out.println("Hello: RawSpectrumPlot"); - + //System.out.println("Hello: RawSpectrumPlot"); + RawDataHolder rawDataHolder = (RawDataHolder) newClick; + if (rawDataHolder.getDataTransforms()==null) return; + + int nChan = PamUtils.getNumChannels(newClick.getChannelBitmap()); //if we don;t need to recalc the spectrum then don't! @@ -153,7 +133,6 @@ public class RawSpectrumPlot extends SpectrumPlot { // @Override public double[][] getPowerSpectrum(PamDataUnit newClick, int min, int max) { - //System.out.println("Get power spectrum: min: " + min + " max: " + max); newClick(newClick,min, max, true); return spectrum; } @@ -167,13 +146,15 @@ public class RawSpectrumPlot extends SpectrumPlot { @Override public void paintPlot(PamDataUnit data, GraphicsContext gc, Rectangle rectangle, DetectionPlotProjector projector, int flag) { -// this.lastDataUnit=data; - //System.out.println("Paint plot projector: "+ projector); + + if (((RawDataHolder) data).getWaveData()==null) { + gc.clearRect(0, 0, rectangle.getWidth(), rectangle.getHeight()); + return; + } + if (flag== DetectionPlot.SCROLLPANE_DRAW) { - double[][] waveformTemp = ((RawDataHolder) data).getWaveData(); -// System.out.println("Spectrum plot: " + " " + projector.getMinScrollLimit() + " " -// + projector.getMaxScrollLimit() + " " + projector.getAxis(Side.TOP).getMaxVal() + " wvfrm: " + WaveformPlot.getYScale(waveformTemp)); + double[][] waveformTemp = ((RawDataHolder) data).getWaveData(); WaveformPlot.paintWaveform(waveformTemp, data.getSequenceBitmap(), gc, rectangle, 0, waveformTemp[0].length, WaveformPlot.getYScale(waveformTemp), null, true, false); diff --git a/src/detectionPlotFX/rawDDPlot/RawWaveformPlot.java b/src/detectionPlotFX/rawDDPlot/RawWaveformPlot.java index 3318d8ac..4deef0f3 100644 --- a/src/detectionPlotFX/rawDDPlot/RawWaveformPlot.java +++ b/src/detectionPlotFX/rawDDPlot/RawWaveformPlot.java @@ -32,7 +32,6 @@ public class RawWaveformPlot extends WaveformPlot{ return rawDetection.getDataTransforms().getFilteredWaveData(super.getWaveformPlotParams().waveformFilterParams.clone()); } else { -// System.out.println("Get normal waveform. "); return rawDetection.getWaveData(); } } diff --git a/src/detectiongrouplocaliser/DetectionGroupSummary.java b/src/detectiongrouplocaliser/DetectionGroupSummary.java index ff86d16a..db461e89 100644 --- a/src/detectiongrouplocaliser/DetectionGroupSummary.java +++ b/src/detectiongrouplocaliser/DetectionGroupSummary.java @@ -39,7 +39,7 @@ public class DetectionGroupSummary { private OverlayMark overlayMark; /** - * Thye currently selected or focused data unit; + * The currently selected or focused data unit; */ private int focusedUnitIndex=0; diff --git a/src/difar/trackedGroups/TrackedGroupProcess.java b/src/difar/trackedGroups/TrackedGroupProcess.java index 766d82c8..73349ae2 100644 --- a/src/difar/trackedGroups/TrackedGroupProcess.java +++ b/src/difar/trackedGroups/TrackedGroupProcess.java @@ -1,32 +1,22 @@ package difar.trackedGroups; import java.util.ArrayList; -import java.util.ListIterator; - -import generalDatabase.DBControl; import generalDatabase.DBControlUnit; import generalDatabase.PamConnection; -import generalDatabase.SQLLogging; -import difar.DIFARCrossingInfo; -import difar.DIFARTargetMotionInformation; import difar.DifarControl; import difar.DifarDataUnit; import pamScrollSystem.PamScroller; -import pamScrollSystem.ViewLoadObserver; import targetMotionModule.TargetMotionResult; import targetMotionModule.algorithms.Simplex2D; import Array.ArrayManager; import Array.StreamerDataUnit; import PamController.PamController; import PamUtils.LatLong; -import PamUtils.PamCalendar; -import PamUtils.PamUtils; import PamView.symbol.StandardSymbolManager; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.PamObservable; import PamguardMVC.PamProcess; -import autecPhones.AutecGraphics; /** * Subscribe to DifarDataBlocks and assign new bearings to groups of whales diff --git a/src/dataPlotsFX/overlaymark/menuOptions/CSVExport/CSVDataUnitExport.java b/src/export/CSVExport/CSVDataUnitExport.java similarity index 93% rename from src/dataPlotsFX/overlaymark/menuOptions/CSVExport/CSVDataUnitExport.java rename to src/export/CSVExport/CSVDataUnitExport.java index 827a367c..3b247e98 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/CSVExport/CSVDataUnitExport.java +++ b/src/export/CSVExport/CSVDataUnitExport.java @@ -1,4 +1,4 @@ -package dataPlotsFX.overlaymark.menuOptions.CSVExport; +package export.CSVExport; import org.apache.commons.csv.CSVPrinter; diff --git a/src/export/CSVExport/CSVExportManager.java b/src/export/CSVExport/CSVExportManager.java new file mode 100644 index 00000000..a624ebdb --- /dev/null +++ b/src/export/CSVExport/CSVExportManager.java @@ -0,0 +1,5 @@ +package export.CSVExport; + +public class CSVExportManager { + +} diff --git a/src/export/ExportOptions.java b/src/export/ExportOptions.java new file mode 100644 index 00000000..0ce6a1f8 --- /dev/null +++ b/src/export/ExportOptions.java @@ -0,0 +1,113 @@ +package export; + +import java.io.Serializable; +import javax.swing.JFrame; + +import PamController.PamControlledUnitSettings; +import PamController.PamSettingManager; +import PamController.PamSettings; +import PamController.StorageParameters; +import export.layoutFX.ExportParams; +import export.swing.ExportProcessDialog; + +/** + * Manages opening settings dialog and saving settings for both FX and Swing GUI's + * + * @author Jamie Macaulay + * + */ +public class ExportOptions implements PamSettings { + + private static ExportOptions singleInstance; + + /** + * Parameters for the exporter. + */ + private ExportParams storageParameters = new ExportParams(); + + /** + * Swing dialog for exporting data. + */ + private ExportProcessDialog exportProcessDialog; + + /** + * Reference to the export manager. This handles exporting data units. + */ + private PamExporterManager exportManager = new PamExporterManager(); + + private ExportOptions() { + PamSettingManager.getInstance().registerSettings(this); + } + + public static ExportOptions getInstance() { + if (singleInstance == null) { + singleInstance = new ExportOptions(); + } + return singleInstance; + } + + + /** + * Show the swing dialog. + * @param parentFrame - the parent frame. + * @return true if settings are OK on close + */ + public boolean showDialog(JFrame parentFrame) { + + if (exportProcessDialog==null) { + exportProcessDialog= new ExportProcessDialog(exportManager); + } + this.exportProcessDialog.showOfflineDialog(parentFrame, this.storageParameters); + +// ExportParams newParams = StorageOptionsDialog.showDialog(parentFrame, storageParameters); +// if (newParams != null) { +// storageParameters = newParams.clone(); +// return true; +// } +// else { +// return false; +// } + + return true; + } + + @Override + public Serializable getSettingsReference() { + return storageParameters; + } + + @Override + public long getSettingsVersion() { + return StorageParameters.serialVersionUID; + } + + @Override + public String getUnitName() { + return "PAMGUARD Export Options"; + } + + @Override + public String getUnitType() { + return "PAMGUARD Export Options"; + } + + @Override + public boolean restoreSettings( + PamControlledUnitSettings pamControlledUnitSettings) { + storageParameters = ((ExportParams) pamControlledUnitSettings.getSettings()).clone(); + return true; + } + + + public void setStorageParameters(ExportParams storageParameters) { + this.storageParameters = storageParameters; + } + + /** + * Get storage parameters settings. + * @return the storage paramters settings + */ + public ExportParams getStorageParameters() { + return storageParameters; + } +} \ No newline at end of file diff --git a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLAnnotationsManager.java b/src/export/MLExport/MLAnnotationsManager.java similarity index 72% rename from src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLAnnotationsManager.java rename to src/export/MLExport/MLAnnotationsManager.java index 4ca4d900..4b42d476 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLAnnotationsManager.java +++ b/src/export/MLExport/MLAnnotationsManager.java @@ -1,11 +1,8 @@ -package dataPlotsFX.overlaymark.menuOptions.MLExport; - - - -import com.jmatio.types.MLStructure; +package export.MLExport; import PamguardMVC.PamDataUnit; import annotation.DataAnnotationType; +import us.hebi.matlab.mat.types.Struct; /** * Adds annotations to data units. @@ -16,16 +13,16 @@ import annotation.DataAnnotationType; public class MLAnnotationsManager { /** - * Add annotations to a matlab structure. + * Add annotations to a MATLAB structure. * @param mlStruct + * @param index * @param dataUnit */ - public void addAnnotations(MLStructure mlStruct, PamDataUnit dataUnit) { + public void addAnnotations(Struct mlStruct, int index, PamDataUnit dataUnit) { for (int i=0; i getUnitClass() { + return ClickDetection.class; + } + + @Override + public String getName() { + return "clicks"; + } + +} diff --git a/src/export/MLExport/MLDataUnitExport.java b/src/export/MLExport/MLDataUnitExport.java new file mode 100644 index 00000000..b6f2825e --- /dev/null +++ b/src/export/MLExport/MLDataUnitExport.java @@ -0,0 +1,118 @@ +package export.MLExport; + +import org.jamdev.jdl4pam.utils.DLMatFile; + +import PamUtils.PamCalendar; +import PamguardMVC.PamDataUnit; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.types.Matrix; +import us.hebi.matlab.mat.types.Struct; + +/** + * Export a detection to MATLAB + * @author Jamie Macaulay + * + */ +public abstract class MLDataUnitExport> { + + /** + * Annotations manager for nadling adding annotations to data units. + */ + private MLAnnotationsManager mlAnnotationsManager; + + public MLDataUnitExport() { + this.mlAnnotationsManager = new MLAnnotationsManager(); + } + + /** + * Create a MATLAB structure which contains all information for a data unit. + * @param dataUnit - the data unit to convert to a MATLAB structure + * @return detection data MATLAB structure ready to be exported to a .mat file or added to a ArrayList. + */ + public Struct detectionToStruct(Struct mlStruct, T dataUnit, int index){ + + Mat5.newScalar(dataUnit.getTimeMilliseconds()); + + //all the default stuff every data unit has. + Matrix millis = Mat5.newScalar(dataUnit.getTimeMilliseconds()); + + //the channel bit map. + Matrix channelMap = Mat5.newScalar(dataUnit.getChannelBitmap()); + + //the sequence map, or 0 if there is no sequence number + Matrix sequenceMap = Mat5.newScalar(0); + if (dataUnit.getSequenceBitmapObject()!=null) { + sequenceMap = Mat5.newScalar(dataUnit.getSequenceBitmapObject()); + } + + //UID for the detection. + Matrix UID = Mat5.newScalar(dataUnit.getUID()); + + //the start sample. + Matrix startSample = Mat5.newScalar(dataUnit.getStartSample()); + + //the duration of the detection in samples. + Matrix sampleDuration = Mat5.newScalar(dataUnit.getSampleDuration()); + + Matrix freqLimits = DLMatFile.array2Matrix(dataUnit.getBasicData().getFrequency()); + + //the frequency limits. + + //create the structure. + mlStruct.set("millis", index, millis); + mlStruct.set("channelMap", index, channelMap); + mlStruct.set("sequenceMap",index, sequenceMap); + mlStruct.set("UID",index, UID); + mlStruct.set("startSample", index,startSample); + mlStruct.set("sampleDuration",index, sampleDuration); + mlStruct.set("freqLimits", index,freqLimits); + + //there may be no delay info + if (dataUnit.getBasicData().getTimeDelaysSeconds()!=null && dataUnit.getBasicData().getTimeDelaysSeconds().length>=1){ + Matrix numTimeDelays = Mat5.newScalar(dataUnit.getBasicData().getTimeDelaysSeconds().length); + Matrix timeDelays = DLMatFile.array2Matrix(dataUnit.getBasicData().getTimeDelaysSeconds()); + mlStruct.set("numTimeDelays", index, numTimeDelays); + mlStruct.set("timeDelays", index,timeDelays); + } + else { + mlStruct.set("numTimeDelays", index, Mat5.newScalar(0)); + //mlStruct.setField("timeDelays", new MLEmptyArray(), index); //22-02-2018 - this caused an exception when saving... + mlStruct.set("timeDelays", index, Mat5.newScalar(0)); + } + + //MATLAB date number. + double datenumMT = PamCalendar.millistoDateNum(dataUnit.getTimeMilliseconds()); + Matrix date = Mat5.newScalar(datenumMT); + + mlStruct.set("date", date); + + //add detection specific data + mlStruct= addDetectionSpecificFields(mlStruct, index, dataUnit); + + this.mlAnnotationsManager.addAnnotations(mlStruct, index, dataUnit); + + return mlStruct; + } + + /** + *Add detection specific fields to a structure. + *@param structure containing all generic info from PamDataUnit + *@param the data unit. + */ + public abstract Struct addDetectionSpecificFields(Struct mlStruct, int index, T dataUnit); + + /** + * Get the unit class + * @return + */ + public abstract Class getUnitClass(); + + /** + * Get the name of the structure + * @return + */ + public abstract String getName(); + + + +} diff --git a/src/export/MLExport/MLDetectionsManager.java b/src/export/MLExport/MLDetectionsManager.java new file mode 100644 index 00000000..37f20911 --- /dev/null +++ b/src/export/MLExport/MLDetectionsManager.java @@ -0,0 +1,201 @@ +package export.MLExport; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import PamguardMVC.PamDataUnit; +import export.PamDataUnitExporter; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.format.Mat5File; +import us.hebi.matlab.mat.types.Matrix; +import us.hebi.matlab.mat.types.Sink; +import us.hebi.matlab.mat.types.Sinks; +import us.hebi.matlab.mat.types.Struct; +import us.hebi.matlab.mat.util.Casts; + + +/** + * Handles the conversion of data units into MATLAB structures. + * @author Jamie Macaulay + * + */ +public class MLDetectionsManager implements PamDataUnitExporter { + + public static final String extension = "mat"; + + /** + * + * All the possible MLDataUnitExport export classes. + */ + ArrayList mlDataUnitsExport = new ArrayList(); + + + public MLDetectionsManager(){ + mlDataUnitsExport.add(new MLClickExport()); + mlDataUnitsExport.add(new MLWhistleMoanExport()); + mlDataUnitsExport.add(new MLRawExport()); + } + + @Override + public boolean hasCompatibleUnits(Class dataUnitType) { + for (int i=0; i dataUnits, boolean append) { + + + try { + Mat5File matFile = Mat5.newMatFile(); + Sink sink = Sinks.newMappedFile(fileName, Casts.sint32(1000000)); + + matFile.writeTo(sink);//Streams the data into a MAT file? + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + + return false; + } + + /** + * Check whether there are compatible data units to be exported. + * @param dataUnits - the data unit list + * @return true if MATLAB export is possible for the current data units. + */ + public boolean hasCompatibleUnits(List dataUnits) { + //first need to figure out how many data units there are. + for (int j=0; j dataUnits){ + + //ArrayList> struct = new ArrayList>(); + //if there's a mixed bunch of data units then we want separate arrays of structures. So a structure of arrays of structures. + //so, need to sort compatible data units. + + Struct list = Mat5.newStruct(); + + //keep a track of the data units that have been transcribed. This means data units that are multiple types + //(e.g. a raw data holder and click) are not added to two different list of structures. + boolean[] alreadyStruct = new boolean[dataUnits.size()]; + + //iterate through possible export functions. + for (int i=0; i=1) { + list.set(mlDataUnitsExport.get(i).getName(),mlStructure); + list.set(mlDataUnitsExport.get(i).getName()+"_sR", Mat5.newScalar(sampleRate)); + } + } + + //now ready to save. + return list; + + } + + @Override + public String getFileExtension() { + return extension; + } + + @Override + public String getIconString() { + return "file-matlab"; + } + + @Override + public String getName() { + return "MATLAB"; + } + + public static void main(String args[]) { + + String fileName = "/Users/au671271/MATLAB-Drive/MATLAB/PAMGUARD/_test/export_test.mat"; + + try { + Mat5File matFile = Mat5.newMatFile(); + + + Struct mlStruct = Mat5.newStruct(3, 1); + Matrix triggerMap = Mat5.newScalar(Math.random()); + + mlStruct.set("triggerMap", 0, triggerMap); + mlStruct.set("triggerMap", 1, triggerMap); + mlStruct.set("triggerMap", 2, triggerMap); + + matFile.addArray("test_struct", mlStruct); + + //basic method to write to a file + Mat5.writeToFile(matFile, fileName); + + +// Sink sink = Sinks.newMappedFile(new File(fileName), Casts.sint32(1000000)); +// +// matFile.writeTo(sink); +// +// sink.close(); + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + + +} diff --git a/src/export/MLExport/MLRawExport.java b/src/export/MLExport/MLRawExport.java new file mode 100644 index 00000000..eb01a350 --- /dev/null +++ b/src/export/MLExport/MLRawExport.java @@ -0,0 +1,70 @@ +package export.MLExport; + +import org.jamdev.jdl4pam.utils.DLMatFile; + +import PamUtils.PamUtils; +import PamguardMVC.PamDataUnit; +import PamguardMVC.RawDataHolder; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.types.Matrix; +import us.hebi.matlab.mat.types.Struct; + +/** + * Export data for any data unit which implements raw data holder. + * @author Jamie Macaulay + * + */ +public class MLRawExport extends MLDataUnitExport{ + + @Override + public Struct addDetectionSpecificFields(Struct mlStruct, int index, PamDataUnit dataUnit) { + + RawDataHolder rawDataHolder = (RawDataHolder) dataUnit; + + //the waveform + Matrix wave = DLMatFile.array2Matrix(rawDataHolder.getWaveData()); + + + //the number of channels + Matrix nChan = Mat5.newScalar(PamUtils.getNumChannels(dataUnit.getChannelBitmap())); + + //the duration - repeat of duration in main data unit. Keeping here so strcut is the same as the struct from binary files + Matrix duration = Mat5.newScalar(dataUnit.getSampleDuration()); + + mlStruct.set("nChan", index, nChan); + mlStruct.set("duration",index, duration); + mlStruct.set("wave", index, wave); + + Matrix angles; + Matrix angleErrors; + if (dataUnit.getLocalisation()!=null) { + //bearing angles + angles = DLMatFile.array2Matrix(dataUnit.getLocalisation().getAngles()); + //angle errors + angleErrors = DLMatFile.array2Matrix(dataUnit.getLocalisation().getAngleErrors()); + } + else { + //bearing angles + angles = Mat5.newScalar(0.); + //angle errors + angleErrors = Mat5.newScalar(0.); + } + + mlStruct.set("angles", index, angles); + mlStruct.set("angleErrors", index, angleErrors); + + + return mlStruct; + } + + @Override + public Class getUnitClass() { + return RawDataHolder.class; + } + + @Override + public String getName() { + return "raw_data_units"; + } + +} diff --git a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLWhistleMoanExport.java b/src/export/MLExport/MLWhistleMoanExport.java similarity index 57% rename from src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLWhistleMoanExport.java rename to src/export/MLExport/MLWhistleMoanExport.java index 60c5e025..124628ca 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/MLExport/MLWhistleMoanExport.java +++ b/src/export/MLExport/MLWhistleMoanExport.java @@ -1,45 +1,45 @@ -package dataPlotsFX.overlaymark.menuOptions.MLExport; +package export.MLExport; -import com.jmatio.types.MLDouble; -import com.jmatio.types.MLInt32; -import com.jmatio.types.MLInt64; -import com.jmatio.types.MLStructure; +import org.jamdev.jdl4pam.utils.DLMatFile; import PamUtils.PamArrayUtils; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.types.Matrix; +import us.hebi.matlab.mat.types.Struct; import whistlesAndMoans.ConnectedRegionDataUnit; import whistlesAndMoans.SliceData; public class MLWhistleMoanExport extends MLDataUnitExport { @Override - public MLStructure addDetectionSpecificFields(MLStructure mlStruct, ConnectedRegionDataUnit dataUnit, int index) { + public Struct addDetectionSpecificFields(Struct mlStruct, int index, ConnectedRegionDataUnit dataUnit) { //date //MLInt64 date = new MLInt64(null, new Long[]{dataUnit.getTimeMilliseconds()}, 1); //nSlices int - MLInt32 nSlices = new MLInt32(null, new Integer[]{dataUnit.getConnectedRegion().getNumSlices()}, 1); + Matrix nSlices = Mat5.newScalar(dataUnit.getConnectedRegion().getNumSlices()); //list of structures: sliceNumber int, nPeaks int, peakData - MLStructure sliceDataStruct = createSliceStruct(dataUnit); + Struct sliceDataStruct = createSliceStruct(dataUnit); int[][] contourData = calcPeakContourWidths( dataUnit); //contour int[] - MLInt32 contour = new MLInt32(null, new int[][]{contourData[0]}); + Matrix contour = DLMatFile.array2Matrix(new int[][]{contourData[0]}); //contour width double[] - MLInt32 contourWidth = new MLInt32(null, new int[][]{contourData[1]}); + Matrix contourWidth = DLMatFile.array2Matrix(new int[][]{contourData[1]}); //mean width - MLDouble meanWidth = new MLDouble(null, new Double[]{PamArrayUtils.mean(contourData[0])}, 1); + Matrix meanWidth = DLMatFile.array2Matrix(new double[]{PamArrayUtils.mean(contourData[0])}); - mlStruct.setField("nSlices", nSlices, index); - mlStruct.setField("sliceData", sliceDataStruct, index); - mlStruct.setField("contour", contour, index); - mlStruct.setField("contourWidth", contourWidth, index); - mlStruct.setField("meanWidth", meanWidth, index); + mlStruct.set("nSlices", index, nSlices); + mlStruct.set("sliceData", index, sliceDataStruct); + mlStruct.set("contour", index, contour); + mlStruct.set("contourWidth", index, contourWidth); + mlStruct.set("meanWidth", index, meanWidth); return mlStruct; } @@ -78,14 +78,16 @@ public class MLWhistleMoanExport extends MLDataUnitExport dataUnits, boolean append); + + /** + * Get the extension for the output file type + * @return the extension for the file type e.g. "mat" + */ + public String getFileExtension(); + + /** + * Get the ikonli icon string for the exporter. + * @return the ikon string. + */ + public String getIconString(); + + /** + * Get the name of the exporter + * @return + */ + public String getName(); + + +} diff --git a/src/export/PamExporterManager.java b/src/export/PamExporterManager.java new file mode 100644 index 00000000..f81a50f9 --- /dev/null +++ b/src/export/PamExporterManager.java @@ -0,0 +1,131 @@ +package export; + +import java.io.File; +import java.util.ArrayList; + +import PamUtils.PamCalendar; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import export.MLExport.MLDetectionsManager; +import export.RExport.RExportManager; +import export.layoutFX.ExportParams; +import export.wavExport.WavFileExportManager; + +/** + * Exports data to external files. Manages the file sizes and creates data buffers for + * offline processing. + */ +public class PamExporterManager { + + /** + * The number of data units to save before saving. + */ + private static int BUFFER_SIZE = 1000; + + /** + * Keep the file size to around 1GB per file + */ + private static int MAX_FILE_SIZE_MB = 1024; + + + /** + * A buffer of data units so we are not opening a file everytime a new data is passed to the export. + */ + private ArrayList dataUnitBuffer = new ArrayList(); + + /** + * A list of the avilable exporters. + */ + private ArrayList pamExporters; + + /** + * The current file. + */ + private File currentFile; + + /** + * Reference to the current export paramters. + */ + private ExportParams exportParams = new ExportParams(); + + public PamExporterManager() { + pamExporters = new ArrayList(); + + //add the MATLAB export + pamExporters.add(new MLDetectionsManager()); + pamExporters.add(new RExportManager()); + pamExporters.add(new WavFileExportManager()); + } + + /** + * Add a data unit to the export list. + */ + public boolean exportDataUnit(PamDataUnit dataUnit) { + boolean exportOK = true; + //if the data unit is null then save everything to the buffer. + + if (currentFile == null || isFileSizeMax(currentFile)) { + //create a new file - note each exporter is responsible for closing the file after writing + //so previous files should already be closed + String fileName = (exportParams.folder + File.separator + PamCalendar.formatDate2(dataUnit.getTimeMilliseconds(), false) + + "." + pamExporters.get(exportParams.exportChoice).getFileExtension()); + + currentFile = new File(fileName); + } + + dataUnitBuffer.add(dataUnit); + + if (BUFFER_SIZE>=BUFFER_SIZE) { + exportOK = pamExporters.get(exportParams.exportChoice).exportData(currentFile, dataUnitBuffer, true); + dataUnitBuffer.clear(); + } + + return exportOK; + + } + + /** + * Check whether the current file is greater than the maximum allowed file size. + * @param currentFile2 - the current file + * @return true of greater than or equal to the maximum file size. + */ + private boolean isFileSizeMax(File currentFile2) { + return getFileSizeMegaBytes(currentFile2) >= MAX_FILE_SIZE_MB; + } + + /** + * Get the file size in MegaBytes + * @param file + * @return + */ + private static double getFileSizeMegaBytes(File file) { + return (double) file.length() / (1024 * 1024); + } + public boolean canExportDataBlock(PamDataBlock dataBlock) { + for (PamDataUnitExporter exporter:pamExporters) { + if (exporter.hasCompatibleUnits(dataBlock.getUnitClass())) return true; + } + + return false; + } + + /** + * Get the number of data exporters. e.g. MAT files, CSV, Wav etc. + * @return the number of data exporters + */ + public int getNumExporters() { + return pamExporters.size(); + } + + /** + * Get a specific exporter. + * @param i - the index of the exporter. + * @return the exporter. + */ + public PamDataUnitExporter getExporter(int i) { + return pamExporters.get(i); + + } + + +} diff --git a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RClickExport.java b/src/export/RExport/RClickExport.java similarity index 94% rename from src/dataPlotsFX/overlaymark/menuOptions/RExport/RClickExport.java rename to src/export/RExport/RClickExport.java index 866c4edd..68293f54 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RClickExport.java +++ b/src/export/RExport/RClickExport.java @@ -1,4 +1,4 @@ -package dataPlotsFX.overlaymark.menuOptions.RExport; +package export.RExport; import org.renjin.sexp.DoubleArrayVector; import org.renjin.sexp.ListVector.NamedBuilder; diff --git a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RDataUnitExport.java b/src/export/RExport/RDataUnitExport.java similarity index 97% rename from src/dataPlotsFX/overlaymark/menuOptions/RExport/RDataUnitExport.java rename to src/export/RExport/RDataUnitExport.java index ecf6df5f..b57d8167 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RDataUnitExport.java +++ b/src/export/RExport/RDataUnitExport.java @@ -1,4 +1,4 @@ -package dataPlotsFX.overlaymark.menuOptions.RExport; +package export.RExport; import org.renjin.sexp.DoubleArrayVector; import org.renjin.sexp.ListVector; diff --git a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RExportManager.java b/src/export/RExport/RExportManager.java similarity index 72% rename from src/dataPlotsFX/overlaymark/menuOptions/RExport/RExportManager.java rename to src/export/RExport/RExportManager.java index c62a00b4..5ba0381d 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RExportManager.java +++ b/src/export/RExport/RExportManager.java @@ -1,12 +1,14 @@ -package dataPlotsFX.overlaymark.menuOptions.RExport; +package export.RExport; +import java.io.File; import java.util.ArrayList; import java.util.List; import org.renjin.sexp.ListVector; import org.renjin.sexp.PairList; -import com.jmatio.types.MLArray; + import PamguardMVC.PamDataUnit; +import export.PamDataUnitExporter; /** * Handles exporting pam data units into an rdata. @@ -14,7 +16,7 @@ import PamguardMVC.PamDataUnit; * */ @SuppressWarnings("rawtypes") -public class RExportManager { +public class RExportManager implements PamDataUnitExporter { /** * @@ -28,30 +30,34 @@ public class RExportManager { mlDataUnitsExport.add(new RClickExport()); mlDataUnitsExport.add(new RWhistleExport()); mlDataUnitsExport.add(new RRawExport()); //should be last in case raw data holders have specific exporters - } - + /** * Check whether there are compatible data units to be exported. * @param dataUnits - the data unit list - * @return true if MATLAB export is possible for the current data units. + * @return true if r export is possible for the current data units. */ public boolean hasCompatibleUnits(List dataUnits) { - for (int i=0; i dataUnitTypes = new ArrayList(); - ArrayList list = new ArrayList(); //iterate through possible export functions. for (int i=0; i dataUnitTypes; } + + + @Override + public boolean exportData(File fileName, List dataUnits, boolean append) { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getFileExtension() { + return "RData"; + } + + @Override + public String getIconString() { + return "file-r"; + } + + @Override + public String getName() { + return "R data"; + } + } diff --git a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RRawExport.java b/src/export/RExport/RRawExport.java similarity index 97% rename from src/dataPlotsFX/overlaymark/menuOptions/RExport/RRawExport.java rename to src/export/RExport/RRawExport.java index 1689b381..1a7e7858 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RRawExport.java +++ b/src/export/RExport/RRawExport.java @@ -1,4 +1,4 @@ -package dataPlotsFX.overlaymark.menuOptions.RExport; +package export.RExport; import org.renjin.sexp.DoubleArrayVector; import org.renjin.sexp.Vector; diff --git a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RWhistleExport.java b/src/export/RExport/RWhistleExport.java similarity index 96% rename from src/dataPlotsFX/overlaymark/menuOptions/RExport/RWhistleExport.java rename to src/export/RExport/RWhistleExport.java index fd643a59..d378af8d 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/RExport/RWhistleExport.java +++ b/src/export/RExport/RWhistleExport.java @@ -1,4 +1,4 @@ -package dataPlotsFX.overlaymark.menuOptions.RExport; +package export.RExport; import org.renjin.sexp.ListVector.NamedBuilder; diff --git a/src/export/layoutFX/ExportParams.java b/src/export/layoutFX/ExportParams.java new file mode 100644 index 00000000..aa68d5fe --- /dev/null +++ b/src/export/layoutFX/ExportParams.java @@ -0,0 +1,39 @@ +package export.layoutFX; + +import java.io.Serializable; + + +/** + * Parameter for the exporter. + * @author Jamie Macaulay + * + */ +public class ExportParams implements Serializable, Cloneable { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * The index of the export choice. + */ + public int exportChoice = 0; + + /** + * The folder to save to. + */ + public String folder = System.getProperty("user.home"); + + @Override + public ExportParams clone() { + try { + ExportParams newP = (ExportParams) super.clone(); + return newP; + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/src/export/layoutFX/ExporterPane.java b/src/export/layoutFX/ExporterPane.java new file mode 100644 index 00000000..5c923a61 --- /dev/null +++ b/src/export/layoutFX/ExporterPane.java @@ -0,0 +1,53 @@ +package export.layoutFX; + +import PamController.SettingsPane; +import javafx.scene.Node; +import javafx.scene.control.Label; +import pamViewFX.fxNodes.PamBorderPane; + +/** + * Create a pane to export data. + * @author Jamie Macaulay + * + */ +public class ExporterPane extends SettingsPane{ + + PamBorderPane mainPane; + + public ExporterPane(Object ownerWindow) { + super(ownerWindow); + mainPane = new PamBorderPane(); + + mainPane.setTop(new Label("Hello")); + mainPane.setCenter(new Label("Hello")); + } + + @Override + public ExportParams getParams(ExportParams currParams) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setParams(ExportParams input) { + // TODO Auto-generated method stub + + } + + @Override + public String getName() { + return "Export Settings"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/export/swing/ExportProcessDialog.java b/src/export/swing/ExportProcessDialog.java new file mode 100644 index 00000000..c3721a23 --- /dev/null +++ b/src/export/swing/ExportProcessDialog.java @@ -0,0 +1,315 @@ +package export.swing; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Window; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.Icon; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JSpinner.DefaultEditor; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.SpinnerListModel; +import javax.swing.SwingConstants; +import javax.swing.border.TitledBorder; + +import org.kordamp.ikonli.Ikon; +import org.kordamp.ikonli.fileicons.FileIcons; +import org.kordamp.ikonli.materialdesign2.MaterialDesignA; +import org.kordamp.ikonli.materialdesign2.MaterialDesignF; +import org.kordamp.ikonli.swing.FontIcon; + +import PamController.PamController; +import PamUtils.PamFileChooser; +import PamView.dialog.PamButton; +import PamView.dialog.PamGridBagContraints; +import PamView.panel.PamPanel; +import PamguardMVC.PamDataBlock; +import export.PamExporterManager; +import export.layoutFX.ExportParams; +import offlineProcessing.OLProcessDialog; +import offlineProcessing.OfflineTaskGroup; + +/** + * Handles an offline dialog for processing offline data and exporting to bespoke file types. + * + * @author Jamie Macaulay + * + */ +public class ExportProcessDialog { + + + /** + * The offline task group + */ + private OfflineTaskGroup dlOfflineGroup; + + + private ExportOLDialog mtOfflineDialog; + + /** + * Reference to the export manager. + */ + private PamExporterManager exportManager; + + /** + * The current paramters. + */ + private ExportParams currentParams; + + public ExportProcessDialog(PamExporterManager exportManager) { + //create the offline task group. + this.exportManager=exportManager; + dlOfflineGroup = new ExportTaskGroup("Export data"); + } + + + public void createExportGroup() { + + //clear current tasks. + dlOfflineGroup.clearTasks(); + + //go through every data block we have and check if we can export the data units... + ArrayList dataBlocks= PamController.getInstance().getDataBlocks(); + + for (int i=0; i { + System.out.println(actionEvent.getActionCommand() + " Selected"); + //TODO set the buttons to be disabled or enabled. + + }; + + exportButtons = new JToggleButton[exportManager.getNumExporters()]; + for (int i = 0; i < exportManager.getNumExporters(); i++) { + JToggleButton b = new JToggleButton(); + b.setToolTipText("Export to " + exportManager.getExporter(i).getName() + " file (" + exportManager.getExporter(i).getFileExtension() + ")"); + + FontIcon icon = FontIcon.of(getIconFromString(exportManager.getExporter(i).getIconString())); + icon.setIconSize(25); + icon.setIconColor(Color.DARK_GRAY); + + b.setIcon(icon); + + b.addActionListener(listener); + + exportButtons[i]=b; + buttonGroup.add(b); + buttonPanel.add(b); + } + + + PamPanel p = new PamPanel(new GridBagLayout()); + GridBagConstraints c = new PamGridBagContraints(); + c.gridwidth = 3; + c.gridx = 0; + c.gridy = 0; + + addComponent(p, exportTo = new JTextField(), c); + exportTo.setMinimumSize(new Dimension(170, 25)); + exportTo.setPreferredSize(new Dimension(170, 25)); + + c.gridx +=3; + c.gridwidth = 1; + PamButton button = new PamButton("Browse..."); + + fc = new PamFileChooser(); + fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + + button.addActionListener((action)->{ + int returnVal = fc.showSaveDialog(this); + if(returnVal == JFileChooser.APPROVE_OPTION) { + File yourFolder = fc.getSelectedFile(); + exportTo.setText(yourFolder.getAbsolutePath()); + exportTo.setToolTipText(yourFolder.getAbsolutePath()); + } + }); + + addComponent(p, button, c); + + c.gridx = 1; + c.gridy++; + c.gridwidth = 2; + + JLabel label = new JLabel("Maximum file size", SwingConstants.RIGHT); + addComponent(p, label, c); + + c.gridwidth = 1; + c.gridx +=2; + + SpinnerListModel list = new SpinnerListModel(new Double[] {10.,30., 60., 100., 200., 300., 600., 1000.}); + + spinner = new JSpinner(list); + //don't want the user to to able to set values + ((DefaultEditor) spinner.getEditor()).getTextField().setEditable(false); + spinner.setBounds(50, 80, 70, 100); + addComponent(p, spinner, c); + + c.gridx ++; + addComponent(p, new JLabel("MB"), c); + + + + mainPanel.add(p); + mainPanel.add(buttonPanel); + + //add the main panel at a different index. + getMainPanel().add(mainPanel, 1); + } + + + + private Ikon getIconFromString(String iconString) { + + Ikon icon = null; + /** + * This is nasty but we won't have many exporters and this is the only + * good way to get this to work in Swing. + */ + switch (iconString) { + case "file-matlab": + icon=FileIcons.MATLAB; + break; + case "file-r": + icon=FileIcons.R; + break; + case "mdi2f-file-music": + icon=MaterialDesignF.FILE_MUSIC; + break; + } + return icon; + } + + public ExportParams getExportParams() { + + + return currentParams; + } + + @Override + public boolean getParams() { + //make sure we update the current paramters before processing starts. + this.currentParams = getExportParams(); + return super.getParams(); + } + + + public void setParams(ExportParams params) { + if (params ==null) currentParams = new ExportParams(); + currentParams = params.clone(); + + buttonGroup.clearSelection(); + exportButtons[params.exportChoice].setSelected(true); + } + + } + + + class ExportTaskGroup extends OfflineTaskGroup{ + + public ExportTaskGroup(String settingsName) { + super(null, settingsName); + // TODO Auto-generated constructor stub + + } + + @Override + public String getUnitType() { + return "Export Data"; + } + } + + + + + + + + +} \ No newline at end of file diff --git a/src/export/swing/ExportTask.java b/src/export/swing/ExportTask.java new file mode 100644 index 00000000..a3582eb2 --- /dev/null +++ b/src/export/swing/ExportTask.java @@ -0,0 +1,82 @@ +package export.swing; + +import PamController.PamController; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import PamguardMVC.dataSelector.DataSelectDialog; +import PamguardMVC.dataSelector.DataSelector; +import dataMap.OfflineDataMapPoint; +import export.PamExporterManager; +import offlineProcessing.OfflineTask; + +/** + * Export data to a file type. + * + * @author Jamie Macaulay + * + */ +public class ExportTask extends OfflineTask>{ + + /** + * Reference to the data exporter which manages exporting of data. + */ + private PamExporterManager exporter; + + /** + * The data selector for the data block + */ + private DataSelector dataSelector; + + public ExportTask(PamDataBlock> parentDataBlock, PamExporterManager exporter) { + super(parentDataBlock); + this.exporter = exporter; + dataSelector=parentDataBlock.getDataSelectCreator().getDataSelector(this.getUnitName() +"_clicks", false, null); + + } + + @Override + public String getName() { + return this.getDataBlock().getDataName(); + } + + @Override + public boolean processDataUnit(PamDataUnit dataUnit) { + exporter.exportDataUnit(dataUnit); + return true; + } + + @Override + public void newDataLoad(long startTime, long endTime, OfflineDataMapPoint mapPoint) { + // TODO Auto-generated method stub + + } + + @Override + public void loadedDataComplete() { + //force the exporter so save any remaning data units in the buffer + exporter.exportDataUnit(null); + + } + /** + * task has settings which can be called + * @return true or false + */ + public boolean hasSettings() { + return dataSelector!=null; + } + + /** + * Call any task specific settings + * @return true if settings may have changed. + */ + public boolean callSettings() { + + dataSelector.getDialogPanel().setParams(); + + DataSelectDialog dataSelectDialog = new DataSelectDialog(PamController.getMainFrame(), + this.getDataBlock(), dataSelector, null); + return dataSelectDialog.showDialog(); + + } + +} diff --git a/src/dataPlotsFX/overlaymark/menuOptions/wavExport/RawHolderWavExport.java b/src/export/wavExport/RawHolderWavExport.java similarity index 90% rename from src/dataPlotsFX/overlaymark/menuOptions/wavExport/RawHolderWavExport.java rename to src/export/wavExport/RawHolderWavExport.java index 77e3ccf0..181aee3a 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/wavExport/RawHolderWavExport.java +++ b/src/export/wavExport/RawHolderWavExport.java @@ -1,4 +1,4 @@ -package dataPlotsFX.overlaymark.menuOptions.wavExport; +package export.wavExport; import PamguardMVC.PamDataUnit; import PamguardMVC.RawDataHolder; diff --git a/src/dataPlotsFX/overlaymark/menuOptions/wavExport/WavDataUnitExport.java b/src/export/wavExport/WavDataUnitExport.java similarity index 95% rename from src/dataPlotsFX/overlaymark/menuOptions/wavExport/WavDataUnitExport.java rename to src/export/wavExport/WavDataUnitExport.java index 7d03c647..a1f161b8 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/wavExport/WavDataUnitExport.java +++ b/src/export/wavExport/WavDataUnitExport.java @@ -1,4 +1,4 @@ -package dataPlotsFX.overlaymark.menuOptions.wavExport; +package export.wavExport; import PamView.paneloverlay.overlaymark.OverlayMark; import PamguardMVC.PamDataUnit; diff --git a/src/dataPlotsFX/overlaymark/menuOptions/wavExport/WavFileExportManager.java b/src/export/wavExport/WavFileExportManager.java similarity index 92% rename from src/dataPlotsFX/overlaymark/menuOptions/wavExport/WavFileExportManager.java rename to src/export/wavExport/WavFileExportManager.java index 52636f3d..407560a8 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/wavExport/WavFileExportManager.java +++ b/src/export/wavExport/WavFileExportManager.java @@ -1,4 +1,4 @@ -package dataPlotsFX.overlaymark.menuOptions.wavExport; +package export.wavExport; import java.io.File; import java.util.ArrayList; @@ -22,6 +22,7 @@ import PamguardMVC.PamRawDataBlock; import PamguardMVC.dataOffline.OfflineDataLoading; import dataMap.OfflineDataMapPoint; import detectiongrouplocaliser.DetectionGroupSummary; +import export.PamDataUnitExporter; import wavFiles.Wav16AudioFormat; import wavFiles.WavFileWriter; @@ -30,7 +31,7 @@ import wavFiles.WavFileWriter; * @author Jamie Macaulay * */ -public class WavFileExportManager { +public class WavFileExportManager implements PamDataUnitExporter { /** * Successful writing of .wav file. @@ -78,7 +79,7 @@ public class WavFileExportManager { /** - * Get a list of ecporters that can export wav files from a data unit. + * Get a list of exporters that can export wav files from a data unit. * @return the wav file exporters. */ public ArrayList getWavDataUnitExporters() { @@ -145,7 +146,7 @@ public class WavFileExportManager { //add correct file type. currentPath = currentPath + ".wav"; - currentPath = currentFolder+"/"+currentPath; + currentPath = currentFolder + File.pathSeparator + currentPath; return currentPath; @@ -196,7 +197,7 @@ public class WavFileExportManager { if (foundDataUnits!=null) { //check whether the wav file has all data raw data units. int n = getNWavDataUnits(foundDataUnits); - hasAllWavClips = n == foundDataUnits.getNumDataUnits(); + hasAllWavClips = (n == foundDataUnits.getNumDataUnits() && n!=0); //make sure to do a zero check here or raw wav data won't save System.out.println("N raw data units: " + n + " N found data units: " + foundDataUnits.getNumDataUnits()); } @@ -233,7 +234,6 @@ public class WavFileExportManager { rawDataBlock.orderOfflineData(new RawObserver(wavWrite, rawDataBlock.getChannelMap()), new RawLoadObserver(wavWrite), start, end, 1, OfflineDataLoading.OFFLINE_DATA_INTERRUPT); - return 0; } @@ -291,7 +291,7 @@ public class WavFileExportManager { * @param dataUnit */ private void newRawData(RawDataUnit dataUnit) { - // System.out.println(" Time millis: "+ dataUnit.getTimeMilliseconds()+ " channel: " + PamUtils.getSingleChannel(dataUnit.getChannelBitmap()) ); +// System.out.println("Write wav data: Time millis: "+ dataUnit.getTimeMilliseconds()+ " channel: " + PamUtils.getSingleChannel(dataUnit.getChannelBitmap()) ); if (currentTimeMillis!=dataUnit.getTimeMilliseconds()) { currentTimeMillis=dataUnit.getTimeMilliseconds(); if (wavArray!=null) { @@ -420,6 +420,45 @@ public class WavFileExportManager { + @Override + public boolean hasCompatibleUnits(Class dataUnitType) { + // TODO Auto-generated method stub + return false; + } + + + + @Override + public boolean exportData(File fileName, + List dataUnits, boolean append) { + // TODO Auto-generated method stub + return false; + } + + + + @Override + public String getFileExtension() { + return "wav"; + } + + + + @Override + public String getIconString() { + // TODO Auto-generated method stub + return "mdi2f-file-music"; + } + + + + @Override + public String getName() { + return "raw sound"; + } + + + // hello(){ diff --git a/src/dataPlotsFX/overlaymark/menuOptions/wavExport/WavSaveCallback.java b/src/export/wavExport/WavSaveCallback.java similarity index 79% rename from src/dataPlotsFX/overlaymark/menuOptions/wavExport/WavSaveCallback.java rename to src/export/wavExport/WavSaveCallback.java index 4b374c4a..f2a0ed2f 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/wavExport/WavSaveCallback.java +++ b/src/export/wavExport/WavSaveCallback.java @@ -1,4 +1,4 @@ -package dataPlotsFX.overlaymark.menuOptions.wavExport; +package export.wavExport; public interface WavSaveCallback { diff --git a/src/generalDatabase/LogSettings.java b/src/generalDatabase/LogSettings.java index 23b713be..c7072e30 100644 --- a/src/generalDatabase/LogSettings.java +++ b/src/generalDatabase/LogSettings.java @@ -109,9 +109,9 @@ public class LogSettings extends DbSpecial { * data that will fit into the database. */ try { - if (pamSettings.getUnitName().equals("User TDDisplay")) { - System.out.println("Saving user display data"); - } +// if (pamSettings.getUnitName().equals("User TDDisplay")) { +// System.out.println("Saving user display data"); +// } } catch (NullPointerException e) { e.printStackTrace(); diff --git a/src/group3dlocaliser/Group3DLocaliserControl.java b/src/group3dlocaliser/Group3DLocaliserControl.java index 1e2baa04..c2fe8c8a 100644 --- a/src/group3dlocaliser/Group3DLocaliserControl.java +++ b/src/group3dlocaliser/Group3DLocaliserControl.java @@ -18,6 +18,8 @@ import group3dlocaliser.algorithm.LocaliserAlgorithmParams; import group3dlocaliser.algorithm.crossedbearing.CrossedBearingGroupLocaliser; import group3dlocaliser.algorithm.gridsearch.TOADGridSearch; import group3dlocaliser.algorithm.hyperbolic.HyperbolicLocaliser; +import group3dlocaliser.algorithm.toadmcmc.ToadMCMCLocaliser; +import group3dlocaliser.algorithm.toadmimplex.ToadMimplexLocaliser; import group3dlocaliser.algorithm.toadsimplex.ToadSimplexLocaliser; import group3dlocaliser.dialog.GroupLocSettingPaneFX; import group3dlocaliser.offline.Group3DOfflineTask; @@ -42,10 +44,13 @@ public class Group3DLocaliserControl extends PamControlledUnit implements PamSet public Group3DLocaliserControl(String unitName) { super(unitType, unitName); algorithms3D = new ArrayList<>(); - algorithms3D.add(new CrossedBearingGroupLocaliser()); + algorithms3D.add(new CrossedBearingGroupLocaliser(this)); algorithms3D.add(new HyperbolicLocaliser(this)); algorithms3D.add(new ToadSimplexLocaliser(this, 2)); algorithms3D.add(new ToadSimplexLocaliser(this, 3)); + algorithms3D.add(new ToadMCMCLocaliser(this)); + algorithms3D.add(new ToadMimplexLocaliser(this)); + // algorithms3D.add(new TOADGridSearch(this)); group3dProcess = new Group3DProcess(this); diff --git a/src/group3dlocaliser/Group3DProcess.java b/src/group3dlocaliser/Group3DProcess.java index 7822e346..6629b05f 100644 --- a/src/group3dlocaliser/Group3DProcess.java +++ b/src/group3dlocaliser/Group3DProcess.java @@ -53,7 +53,7 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor public Group3DProcess(Group3DLocaliserControl group3DControl) { super(group3DControl, null); this.group3DControl = group3DControl; - localiserAlgorithm3D = new CrossedBearingGroupLocaliser(); + localiserAlgorithm3D = group3DControl.getAlgorithmProviders().get(0); detectionGrouper = new DetectionGrouper(this); group3dDataBlock = new Group3DDataBlock(group3DControl.getUnitName() + " Localisations", this, 0, group3DControl); @@ -62,7 +62,7 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor group3dDataBlock.setPamSymbolManager(new Group3DSymbolManager(group3DControl, group3dDataBlock, Group3DOverlayDraw.defaultSymbol.getSymbolData())); group3dLogging = new Group3DLogging(group3DControl, group3dDataBlock); - group3dDataBlock.SetLogging(group3dLogging); + group3dDataBlock.SetLogging(group3dLogging); } /* @@ -118,6 +118,9 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor @Override public void pamStop() { + + ///need this here to close the group + this.detectionGrouper.closeMotherGroup(); String prf = String.format("%s %s ", group3DControl.getUnitName(), localiserAlgorithm3D.getName()); System.out.println(cpuMonitor.getSummary(prf)); } @@ -180,7 +183,10 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor } @Override - public void newGroupedDataSet(DetectionGroupedSet detectionGroupedSet) { + public void newGroupedDataSet(DetectionGroupedSet detectionGroupedSet1) { + + DetectionGroupedSet detectionGroupedSet = this.localiserAlgorithm3D.preFilterLoc(detectionGroupedSet1); + int nGroups = detectionGroupedSet.getNumGroups(); AbstractLocalisation abstractLocalisation; GroupLocalisation groupLocalisation; @@ -223,14 +229,17 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor logViewerData(newDataUnit); } } -// System.out.println("Ran localisation " + localiserAlgorithm3D.getName() + " got: " + abstractLocalisation); + + System.out.println("Ran localisation " + i + " " + localiserAlgorithm3D.getName() + " got: " + abstractLocalisation.getLatLong(0) + " " + abstractLocalisation.getHeight(0) + " Error: " + abstractLocalisation.getLocError(0)); if (abstractLocalisation instanceof GroupLocalisation) { groupLocalisation = (GroupLocalisation) abstractLocalisation; + cpuMonitor.stop(); if (groupLocalisation != null) { groupLocalisation.sortLocResults(); GroupLocResult locResult = groupLocalisation.getGroupLocaResult(0); + if (locResult == null) { continue; } @@ -257,10 +266,15 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor // bestSet = i; // } } - } else { + } else if (bestLocalisation==null) { + //note it is important here to make sure bestLoclaisation is null. If we have an array which has a linear + //compenent than a set of time delays of only the linear system may return a linear loclaisation in which case, without a + // null check, this will always override a group loclaisation. bestLocalisation = abstractLocalisation; bestSet = i; } + + } if (bestLocalisation != null && logAll == false) { // best make and output a data unit ! @@ -272,6 +286,8 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor // } Group3DDataUnit newDataUnit = group3dDataUnits[bestSet]; newDataUnit.setLocalisation(bestLocalisation); + + System.out.println("Set click localisation: " + bestSet + " " + bestLocalisation.getRange(0) + " " + bestLocalisation.getLatLong(0)); group3dDataBlock.addPamData(newDataUnit); if (group3DControl.isViewer()) { // call explicityly since it won't happen in normal mode. @@ -288,7 +304,7 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor * @param group2 * @param result1 * @param result2 - * @return + * @return true if group 1 is better than group 2. */ private boolean isBetter(List group1, List group2, GroupLocResult result1, GroupLocResult result2) { @@ -298,6 +314,17 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor double chi2 = result2.getChi2() / nch2; Double errMag1 = null, errMag2 = null; LocaliserError err = result1.getLocError(); + + //if one has location information and the other does not, choose the one with the + System.out.println("LAT LONG TEST: " + result1.getLatLong() + " " + result2.getLatLong()); + if (result1.getLatLong()==null && result2.getLatLong()!=null) { + return false; + } + + if (result2.getLatLong() == null && result1.getLatLong() != null) { + return true; + } + if (err != null) { errMag1 = err.getErrorMagnitude(); if (errMag1.isInfinite()) { diff --git a/src/group3dlocaliser/ToadManagedSettingsPane.java b/src/group3dlocaliser/ToadManagedSettingsPane.java new file mode 100644 index 00000000..53f39b14 --- /dev/null +++ b/src/group3dlocaliser/ToadManagedSettingsPane.java @@ -0,0 +1,23 @@ +package group3dlocaliser; + +import javafx.scene.control.TabPane; +import pamViewFX.fxNodes.pamDialogFX.ManagedSettingsPane; + +public abstract class ToadManagedSettingsPane extends ManagedSettingsPane{ + + /** + * Get the main tab pane. + * @return the main tab pane. + */ + public abstract TabPane getTabPane(); + + /** + * Set whether the pane should show errors. Because there are multiple algorithms that + * will use this pane we do not want to warn for every algorithm only the user selected one. + * @param warn = true to warn errors e.g. show an dialog - false and there are no error warnings. + */ + public abstract void setErrorWarn(boolean warn); + + + +} diff --git a/src/group3dlocaliser/algorithm/LocaliserAlgorithm3D.java b/src/group3dlocaliser/algorithm/LocaliserAlgorithm3D.java index e7a055b3..42e1afe9 100644 --- a/src/group3dlocaliser/algorithm/LocaliserAlgorithm3D.java +++ b/src/group3dlocaliser/algorithm/LocaliserAlgorithm3D.java @@ -1,14 +1,18 @@ package group3dlocaliser.algorithm; import java.awt.Window; +import java.io.Serializable; import Array.ArrayManager; import Localiser.LocaliserModel; +import Localiser.LocaliserPane; import PamController.SettingsPane; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.superdet.SuperDetection; import generalDatabase.SQLLoggingAddon; +import group3dlocaliser.ToadManagedSettingsPane; +import group3dlocaliser.grouper.DetectionGroupedSet; import pamViewFX.fxNodes.pamDialogFX.ManagedSettingsPane; abstract public class LocaliserAlgorithm3D implements LocaliserModel { @@ -31,19 +35,51 @@ abstract public class LocaliserAlgorithm3D implements LocaliserModelThis may be Swing or FX for compatibility with old code. - * @param parent parent frame / window, etc. - * @param currentParams current algorithm params. These may be null - * the first time this gets called, so prepare for this and create new as - * required - * @return null if the dialog cancelled, a new params object otherwise. + * Option to pre-filter the localisation results. This can be useful when using algorithms that + * internally handle detection match uncertainty. + * @param - the initial set of detection matches to filter. */ - public LocaliserAlgorithmParams showAlgorithmDialog(Window parent, LocaliserAlgorithmParams currentParams) { - return null; + public DetectionGroupedSet preFilterLoc(DetectionGroupedSet preGroups) { + return preGroups; } + + /** + * The loclaiser params. The + *

+ * + * Note: the localiser params for each model are stored in + * @param localiserParams + */ + public void setLocaliserParams(LocaliserAlgorithmParams localiserParams){ + } + + +// /** +// * Configure parameters for this algorithm. Show a dialog with +// * anything you want ... +// *
This may be Swing or FX for compatibility with old code. +// * @param parent parent frame / window, etc. +// * @param currentParams current algorithm params. These may be null +// * the first time this gets called, so prepare for this and create new as +// * required +// * @return null if the dialog cancelled, a new params object otherwise. +// */ +// public LocaliserAlgorithmParams showAlgorithmDialog(Window parent, LocaliserAlgorithmParams currentParams) { +// return null; +// } + + + /** + * Get an algorithm specific settings pane. This holds settings which are specific to the + * localisation algorithm. + */ + @Override + public LocaliserPane getAlgorithmSettingsPane(){ + return null; + } + + /** * Get a source settings pane. This will be inserted into the dialog beneath where the * detection source and algorithm have been selected. @@ -52,7 +88,7 @@ abstract public class LocaliserAlgorithm3D implements LocaliserModel getSourceSettingsPane(Window parent, PamDataBlock detectionSource) { + public ToadManagedSettingsPane getSourceSettingsPane(Window parent, PamDataBlock detectionSource) { return null; } diff --git a/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingGroupLocaliser.java b/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingGroupLocaliser.java index 2874c9b3..81b13b1a 100644 --- a/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingGroupLocaliser.java +++ b/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingGroupLocaliser.java @@ -1,12 +1,11 @@ package group3dlocaliser.algorithm.crossedbearing; import java.awt.Window; -import java.util.List; +import java.io.Serializable; import Array.ArrayManager; import Localiser.LocaliserPane; import Localiser.detectionGroupLocaliser.DetectionGroupOptions; -import Localiser.detectionGroupLocaliser.GroupLocalisation; import PamDetection.AbstractLocalisation; import PamDetection.LocContents; import PamDetection.LocalisationInfo; @@ -17,11 +16,21 @@ import annotation.localise.targetmotion.TMAnnotation; import annotation.localise.targetmotion.TMAnnotationOptions; import annotation.localise.targetmotion.TMAnnotationType; import generalDatabase.SQLLoggingAddon; -import group3dlocaliser.GroupLocDataUnit; +import group3dlocaliser.Group3DLocaliserControl; import group3dlocaliser.algorithm.LocaliserAlgorithm3D; import group3dlocaliser.algorithm.LocaliserAlgorithmParams; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import pamViewFX.PamGuiManagerFX; +import pamViewFX.fxGlyphs.PamGlyphDude; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamButton; +import pamViewFX.fxNodes.PamHBox; -public class CrossedBearingGroupLocaliser extends LocaliserAlgorithm3D{ +public class CrossedBearingGroupLocaliser extends LocaliserAlgorithm3D { private double sampleRate; @@ -29,7 +38,12 @@ public class CrossedBearingGroupLocaliser extends LocaliserAlgorithm3D{ private LocContents locContents = new LocContents(LocContents.HAS_BEARING); - public CrossedBearingGroupLocaliser() { + private CrossedBearingPane crossedBearingPane; + + private Group3DLocaliserControl group3dLocaliserControl; + + public CrossedBearingGroupLocaliser(Group3DLocaliserControl group3dLocaliserControl) { + this.group3dLocaliserControl = group3dLocaliserControl; tmAnnotationType = new TMAnnotationType(); TMAnnotationOptions tmAnnotationOptions = new TMAnnotationOptions("CrossedBearingGroupLocaliser"); tmAnnotationOptions.getLocalisationParams().setIsSelected(0, false); @@ -124,9 +138,11 @@ public class CrossedBearingGroupLocaliser extends LocaliserAlgorithm3D{ * @see Localiser.LocaliserModel#getSettingsPane() */ @Override - public LocaliserPane getSettingsPane() { - // TODO Auto-generated method stub - return null; + public LocaliserPane getAlgorithmSettingsPane() { + if (crossedBearingPane == null) { + crossedBearingPane = new CrossedBearingPane(); + } + return crossedBearingPane; } /* (non-Javadoc) @@ -145,28 +161,121 @@ public class CrossedBearingGroupLocaliser extends LocaliserAlgorithm3D{ // TODO Auto-generated method stub } - - /* (non-Javadoc) - * @see group3dlocaliser.algorithm.LocaliserAlgorithmProvider#showAlgorithmDialog(java.awt.Window, group3dlocaliser.algorithm.LocaliserAlgorithmParams) + + //TODO - make this full FX + /** + * Settings pane for the crossed bearing loclaiser. Note that this just shows a settings button which open a swing dialog.. + * @author Jamie Macaulay + * */ - @Override - public LocaliserAlgorithmParams showAlgorithmDialog(Window parent, LocaliserAlgorithmParams currentParams) { + public class CrossedBearingPane extends LocaliserPane { + + private PamBorderPane mainPane; - if (currentParams != null && currentParams.getAlgorithmParameters() instanceof TMAnnotationOptions) { - TMAnnotationOptions p = (TMAnnotationOptions) currentParams.getAlgorithmParameters(); - this.setTmAnnotationOptions(p); + private LocaliserAlgorithmParams algorithmPaams; + + public CrossedBearingPane() { + PamHBox hBox = new PamHBox(); + hBox.setSpacing(5); + hBox.setAlignment(Pos.CENTER); + hBox.setPadding(new Insets(5,5,5,5)); + + PamButton algoOptsButton = new PamButton("",PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconSize)); + algoOptsButton.setOnAction((action)->{ + moreAlgorithmOptions() ; + }); + algoOptsButton.setTooltip(new Tooltip("More Algorithm Options ...")); + + hBox.getChildren().addAll(new Label(getName() + " settings"), algoOptsButton); + + + mainPane = new PamBorderPane(); + mainPane.setCenter(hBox); } -// AnnotationSettingsPanel settingsPanel = cbLocaliser.getTmAnnotationType().getSettingsPanel(); - boolean asd = AnnotationSettingsDialog.showDialog(parent, this.getTmAnnotationType()); - if (asd) { - return new LocaliserAlgorithmParams(this.getTmAnnotationOptions()); + /** + * Handle algorithm options ... + */ + protected void moreAlgorithmOptions() { + String algoName = getName(); + if (algoName == null) { + return; + } + LocaliserAlgorithm3D localiserAlgorithm = group3dLocaliserControl.findAlgorithm(algoName); + if (localiserAlgorithm == null) { + return; + } + if (localiserAlgorithm.hasParams() == false) { + return; + } + + LocaliserAlgorithmParams algorithmPaams = group3dLocaliserControl.getLocaliserAlgorithmParams(localiserAlgorithm); + + + this.algorithmPaams = showAlgorithmDialog(getAWTWindow(), algorithmPaams); + + +// if (algorithmPaams != null) { +// group3dLocaliserControl.setAlgorithmParams(localiserAlgorithm, algorithmPaams); +// } } - else { - return null; + + + /* (non-Javadoc) + * @see group3dlocaliser.algorithm.LocaliserAlgorithmProvider#showAlgorithmDialog(java.awt.Window, group3dlocaliser.algorithm.LocaliserAlgorithmParams) + */ + public LocaliserAlgorithmParams showAlgorithmDialog(Window parent, LocaliserAlgorithmParams currentParams) { + + if (currentParams != null && currentParams.getAlgorithmParameters() instanceof TMAnnotationOptions) { + TMAnnotationOptions p = (TMAnnotationOptions) currentParams.getAlgorithmParameters(); + setTmAnnotationOptions(p); + } + +// AnnotationSettingsPanel settingsPanel = cbLocaliser.getTmAnnotationType().getSettingsPanel(); + boolean asd = AnnotationSettingsDialog.showDialog(parent, getTmAnnotationType()); + + + if (asd) { + return new LocaliserAlgorithmParams(getTmAnnotationOptions()); + } + else { + return null; + } } + + @Override + public Serializable getParams(Serializable currParams) { + if (algorithmPaams!=null) { + return this.algorithmPaams ; + } + return currParams; + } + + @Override + public void setParams(Serializable input) { + this.algorithmPaams = (LocaliserAlgorithmParams) input; + } + + @Override + public String getName() { + return "Crossed Bearing Settings"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + } + + @Override public boolean canLocalise(PamDataBlock pamDataBlock) { if (pamDataBlock == null) { diff --git a/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingProvider.java b/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingProvider.java index 7d863701..b6e20ab6 100644 --- a/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingProvider.java +++ b/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingProvider.java @@ -19,7 +19,7 @@ public class CrossedBearingProvider extends LocaliserAlgorithmProvider { */ private synchronized CrossedBearingGroupLocaliser getInstance() { if (crossedBearingLocaliser == null) { - crossedBearingLocaliser = new CrossedBearingGroupLocaliser(); + crossedBearingLocaliser = new CrossedBearingGroupLocaliser(null); } return crossedBearingLocaliser; } diff --git a/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingSQLAddon.java b/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingSQLAddon.java index dbcb6ff2..15a0b458 100644 --- a/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingSQLAddon.java +++ b/src/group3dlocaliser/algorithm/crossedbearing/CrossedBearingSQLAddon.java @@ -5,7 +5,7 @@ import targetMotionOld.TargetMotionSQLLogging; public class CrossedBearingSQLAddon extends TargetMotionSQLLogging { - private targetMotionModule.TargetMotionSQLLogging tml; +// private targetMotionModule.TargetMotionSQLLogging tml; public CrossedBearingSQLAddon(int nResults) { super(nResults, ""); // hideColumns(getBeamLatitude()); diff --git a/src/group3dlocaliser/algorithm/gridsearch/TOADGridSearch.java b/src/group3dlocaliser/algorithm/gridsearch/TOADGridSearch.java index 3c5130b1..166cb4d5 100644 --- a/src/group3dlocaliser/algorithm/gridsearch/TOADGridSearch.java +++ b/src/group3dlocaliser/algorithm/gridsearch/TOADGridSearch.java @@ -1,5 +1,7 @@ package group3dlocaliser.algorithm.gridsearch; +import java.io.Serializable; + import org.apache.commons.math.MathException; import org.apache.commons.math.distribution.ChiSquaredDistributionImpl; @@ -15,6 +17,7 @@ import PamguardMVC.PamDataUnit; import generalDatabase.SQLLoggingAddon; import group3dlocaliser.Group3DLocaliserControl; import group3dlocaliser.algorithm.Chi2Data; +import group3dlocaliser.algorithm.LocaliserAlgorithmParams; import group3dlocaliser.algorithm.toadbase.TOADBaseAlgorithm; import group3dlocaliser.algorithm.toadbase.TOADInformation; import group3dlocaliser.grids.Grid3D; @@ -50,11 +53,6 @@ public class TOADGridSearch extends TOADBaseAlgorithm { return null; } - @Override - public LocaliserPane getSettingsPane() { - // TODO Auto-generated method stub - return null; - } @Override public boolean hasParams() { diff --git a/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicGroupLocalisation.java b/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicGroupLocalisation.java new file mode 100644 index 00000000..6df47138 --- /dev/null +++ b/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicGroupLocalisation.java @@ -0,0 +1,33 @@ +package group3dlocaliser.algorithm.hyperbolic; + +import Localiser.detectionGroupLocaliser.GroupLocResult; +import Localiser.detectionGroupLocaliser.GroupLocalisation; +import PamguardMVC.PamDataUnit; +import pamMaths.PamVector; + +/** + * Keeps a copy of the raw position vector for hyperbolic localisation. + * @author Jamie Macaulay + * + */ +public class HyperbolicGroupLocalisation extends GroupLocalisation { + + /** + * The raw localisation result. + */ + double[] posVec; + + + public HyperbolicGroupLocalisation(PamDataUnit pamDataUnit, GroupLocResult targetMotionResult) { + super(pamDataUnit, targetMotionResult); + // TODO Auto-generated constructor stub + } + + public double[] getPosVec() { + return posVec; + } + + public void setPosVec(double[] posVec) { + this.posVec = posVec; + } +} diff --git a/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicLocaliser.java b/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicLocaliser.java index dc6e5eb4..f9483bad 100644 --- a/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicLocaliser.java +++ b/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicLocaliser.java @@ -1,6 +1,8 @@ package group3dlocaliser.algorithm.hyperbolic; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Random; import org.apache.commons.math3.distribution.ChiSquaredDistribution; @@ -9,13 +11,14 @@ import Array.SnapshotGeometry; import GPS.GpsData; import Jama.Matrix; import Localiser.LocaliserPane; +import Localiser.algorithms.locErrors.SimpleError; import Localiser.detectionGroupLocaliser.GroupLocResult; import Localiser.detectionGroupLocaliser.GroupLocalisation; import PamDetection.AbstractLocalisation; import PamDetection.LocContents; import PamUtils.LatLong; +import PamUtils.PamArrayUtils; import PamguardMVC.PamDataUnit; -import PamguardMVC.debug.Debug; import generalDatabase.SQLLoggingAddon; import group3dlocaliser.Group3DLocaliserControl; import group3dlocaliser.algorithm.Chi2Data; @@ -25,15 +28,24 @@ import group3dlocaliser.localisation.LinearLocalisation; import pamMaths.PamVector; /** - * Hyperbolic localisation using methods described in - * Gillette, M. D., and Silverman, H. F. (2008). “A linear closed-form algorithm for source localization from time-differences of arrival,” IEEE Signal Processing Letters, 15, 1–4.

+ * Hyperbolic localisation using methods described in Gillette, M. D., and + * Silverman, H. F. (2008). �A linear closed-form algorithm for source + * localization from time-differences of arrival,� IEEE Signal Processing + * Letters, 15, 1�4. + *

+ * + * Also worth reading Spiesberger, J. L. (2001). �Hyperbolic location errors due + * to insufficient numbers of receivers,� The Journal of the Acoustical Society + * of America, 109, 3076�3079. which gives a clearer explanation of why at least + * 4 recievers are needed for 2D localisation and 5 for 3D localisation. + *

+ * Worth noting that the equations derived in Gillette 2008 are functionally + * identical to those in Spiesberger 2001 and an earlier work by Speisberger and + * Fristrup:
+ * Spiesberger, J. L., and Fristrup, K. M. (1990). �Passive localization of + * calling animals and sensing of their acoustic environment using acoustic + * tomography,� The american naturalist, 135, 107�153. * - * Also worth reading Spiesberger, J. L. (2001). “Hyperbolic location errors due to insufficient numbers of receivers,” The Journal of the Acoustical Society of America, 109, 3076–3079. - * which gives a clearer explanation of why at least 4 recievers are needed for 2D localisation and 5 for 3D localisation.

- * Worth noting that the equations derived in Gillette 2008 are functionally identical to those in Spiesberger 2001 and an earlier work by Speisberger and Fristrup:
- * Spiesberger, J. L., and Fristrup, K. M. (1990). “Passive localization of calling animals and sensing of their acoustic environment using acoustic tomography,” - * The american naturalist, 135, 107–153. - * * @author Doug Gillespie * */ @@ -41,27 +53,53 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm { private double[] lastPosVector; private LocContents locContents = new LocContents(0); + + private HyperbolicParams params = new HyperbolicParams(); + + private HyperbolicSettingsPane hyperbolicLocaliserPane; + public HyperbolicLocaliser(Group3DLocaliserControl group3dLocaliser) { super(group3dLocaliser); - // TODO Auto-generated constructor stub } @Override public AbstractLocalisation processTOADs(PamDataUnit groupDataUnit, SnapshotGeometry geometry, TOADInformation toadInformation) { + + //System.out.println("Process TOADS: Hyperbolic: " + toadInformation.getChannelMap()); + + /** + * This module is a little odd in that it stores paramters for each algorithm in it's own has table without acc + */ + params = (HyperbolicParams) group3dLocaliser.getLocaliserAlgorithmParams(this).getAlgorithmParameters(); + + int shape = geometry.getShape(); + GroupLocalisation loclaisation = null; switch (shape) { case ArrayManager.ARRAY_TYPE_LINE: + //System.out.println("HyperbolicLocaliser: Line"); + return processTOADs2D(groupDataUnit, geometry, toadInformation); case ArrayManager.ARRAY_TYPE_PLANE: - return processTOADsPlane(groupDataUnit, geometry, toadInformation); + //System.out.println("HyperbolicLocaliser: Plane"); + + loclaisation = processTOADsPlane(groupDataUnit, geometry, toadInformation); + calcErrors(loclaisation.getGroupLocaResult(0), groupDataUnit, geometry, toadInformation, params); + return loclaisation; + case ArrayManager.ARRAY_TYPE_VOLUME: - return processTOADs3D(groupDataUnit, geometry, toadInformation); + //System.out.println("HyperbolicLocaliser: Volume"); + + loclaisation = processTOADs3D(groupDataUnit, geometry, toadInformation); + calcErrors(loclaisation.getGroupLocaResult(0), groupDataUnit, geometry, toadInformation, params); + return loclaisation; } - return null; + System.out.println("HyperbolicLocaliser: Shape is null?"); + return loclaisation; //will be null if no array geometry found. } - private GroupLocalisation processTOADsPlane(PamDataUnit groupDataUnit, SnapshotGeometry geometry, + private HyperbolicGroupLocalisation processTOADsPlane(PamDataUnit groupDataUnit, SnapshotGeometry geometry, TOADInformation toadInformation) { PamVector[] arrayAxes = geometry.getArrayAxes(); if (arrayAxes.length < 2) { @@ -322,8 +360,9 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm { GroupLocResult glr = new GroupLocResult(pos, 0, 0); glr.setPerpendicularDistance(0.); // glr.setModel(model); - GroupLocalisation groupLocalisation = new GroupLocalisation(groupDataUnit, glr); - + HyperbolicGroupLocalisation groupLocalisation = new HyperbolicGroupLocalisation(groupDataUnit, glr); + groupLocalisation.setPosVec(posVec); + return groupLocalisation; } @@ -581,7 +620,8 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm { return new Chi2Data(chiTot, nGood-2); } - private GroupLocalisation processTOADs3D(PamDataUnit groupDataUnit, SnapshotGeometry geometry, TOADInformation toadInformation) { + private HyperbolicGroupLocalisation processTOADs3D(PamDataUnit groupDataUnit, SnapshotGeometry geometry, TOADInformation toadInformation) { + // follow formulation in http://ieeexplore.ieee.org/document/4418389/#full-text-section // for (int i = 0; i < delays.length; i++) { @@ -703,6 +743,7 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm { // System.out.println(e.getMessage()); // Matrix m = leftMatrix.transpose(); // m = m.inverse(); + System.out.println("HyperbolicLoclaiser: localisation failed"); return null; } // get a chi2 value for this location. @@ -720,24 +761,25 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm { // System.out.printf("Res %d = %3.5f, ", i, answer.get(i, 0)); // } // System.out.printf(", Chi2 = %3.1f, p=%3.1f, ndf = %d\n", chiData.getChi2(), cumProb, chiData.getNdf()); + - // error estimation - try { - /* - * Error estimation based on - * https://www.math.umd.edu/~petersd/460/linsysterrn.pdf - * - */ - double cond1 = leftMatrix.norm1()*leftInverse.norm1(); - double errorSscale = (rightPlusErrorMatrix.minus(rightMatrix)).norm1(); - errorSscale *= cond1; - errorSscale /= rightMatrix.norm1(); -// System.out.printf("Error scale = %3.3f\n", errorSscale); - - } - catch (Exception e) { - System.out.println("Error in HyperbolicLocaliser.processTOADs3D: " + e.getMessage()); - } +// // error estimation +// try { +// /* +// * Error estimation based on +// * https://www.math.umd.edu/~petersd/460/linsysterrn.pdf +// * +// */ +// double cond1 = leftMatrix.norm1()*leftInverse.norm1(); +// double errorSscale = (rightPlusErrorMatrix.minus(rightMatrix)).norm1(); +// errorSscale *= cond1; +// errorSscale /= rightMatrix.norm1(); +// //System.out.printf("Error scale = %3.3f\n", errorSscale); +// +// } +// catch (Exception e) { +// System.out.println("Error in HyperbolicLocaliser.processTOADs3D: " + e.getMessage()); +// } LatLong pos = geometry.getReferenceGPS().addDistanceMeters(centre.getCoordinate(0)+answer.get(0,0), centre.getCoordinate(1)+answer.get(1, 0), @@ -745,11 +787,102 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm { // TargetMotionResult tmResult = new TargetMotionResult(geometry.getTimeMilliseconds(), null, pos, 0, 0); GroupLocResult glr = new GroupLocResult(pos, 0, 0); glr.setPerpendicularDistance(0.); + glr.setModel(this); + int nToads = countUsableTOADS(toadInformation); + int nDF = nToads-3; + glr.setnDegreesFreedom(nDF); + glr.setDim(3); + glr.setBeamLatLong(geometry.getReferenceGPS()); + glr.setBeamTime(groupDataUnit.getTimeMilliseconds()); + glr.setAic(chiData.getChi2()-6); // glr.setModel(model); - GroupLocalisation groupLocalisation = new GroupLocalisation(groupDataUnit, glr); + HyperbolicGroupLocalisation groupLocalisation = new HyperbolicGroupLocalisation(groupDataUnit, glr); + groupLocalisation.setPosVec(posVec); + + //System.out.println("HyperbolicLoclaiser: Lat long: " + groupLocalisation.getLatLong(0) + " " + groupLocalisation.hasLocContent(LocContents.HAS_LATLONG) ); + return groupLocalisation; } + + /** + * Calculate and add the errors in source position. This is achieved by sampling a random number from the time delay error distributions, localising and looking at the distribution + * in position of localisation results. + * @param loclaisation - the localisation result. + * @param timeDelays - object containing time delay data. + * @param hydrophonePos - the hydrophone positions. + * @param params - hyperbolic params object with settings for error calculations. + * @return the loclaisation object iwth errors added. + */ + public GroupLocResult calcErrors(GroupLocResult loclaisation, PamDataUnit groupDataUnit, SnapshotGeometry geometry, TOADInformation toadInformation, HyperbolicParams params){ + double[] errors = calcErrors( groupDataUnit, geometry, toadInformation , params); + loclaisation.setError(new SimpleError(errors[0], errors[1], errors[2],geometry.getArrayAngles()[0])); + return loclaisation; + } + + + + /** + * Calculate the errors in source position. This is achieved by sampling a random number from the time delay error distributions, localising and looking at the distribution + * in position of localisation results. + * @param timeDelays - object containing time delay data. + * @param hydrophonePos - the hydrophone positions. + * @param params - hyperbolic params object with settings for error calculations. + * @return the errors in meters + */ + public double[] calcErrors(PamDataUnit groupDataUnit, SnapshotGeometry geometry, TOADInformation toadInformation , HyperbolicParams params){ + + Random r = new Random(); + double[][] sourcePositions=new double[params.bootStrapN][3]; + + TOADInformation errToadInformation; + HyperbolicGroupLocalisation errLoc; + for (int i=0; i timeDelaysJitter=new ArrayList(); + double error; + + errToadInformation = (TOADInformation) toadInformation.clone(); + + //generate the new time delay errors. + for (int j=0; j getSettingsPane() { - return null; + public LocaliserPane getAlgorithmSettingsPane() { + if (hyperbolicLocaliserPane == null) { + hyperbolicLocaliserPane= new HyperbolicSettingsPane(); + } + return hyperbolicLocaliserPane; } /* (non-Javadoc) @@ -799,8 +935,7 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm { */ @Override public boolean hasParams() { - // TODO Auto-generated method stub - return false; + return true; } /* (non-Javadoc) @@ -811,5 +946,8 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm { // TODO Auto-generated method stub } + + + } diff --git a/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicParams.java b/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicParams.java new file mode 100644 index 00000000..b257dce9 --- /dev/null +++ b/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicParams.java @@ -0,0 +1,47 @@ +package group3dlocaliser.algorithm.hyperbolic; + +import java.io.Serializable; + +import PamModel.parametermanager.ManagedParameters; +import PamModel.parametermanager.PamParameterSet; + +/** + * Holds settings for hyperbolic loclaiser. + * @author Jamie Macaulay + * + */ +public class HyperbolicParams implements Serializable, Cloneable, ManagedParameters { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Calculates errors from a random distribution of time delay errors + */ + public boolean calcErrors=false; + + /** + * Number of iterations to calculate error. More=more computational time per localisation; + */ + public int bootStrapN=100; + + @Override + public HyperbolicParams clone() { + try { + + return (HyperbolicParams) super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public PamParameterSet getParameterSet() { + PamParameterSet ps = PamParameterSet.autoGenerate(this); + return ps; + } + +} diff --git a/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicSettingsPane.java b/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicSettingsPane.java new file mode 100644 index 00000000..f0a95fe0 --- /dev/null +++ b/src/group3dlocaliser/algorithm/hyperbolic/HyperbolicSettingsPane.java @@ -0,0 +1,88 @@ +package group3dlocaliser.algorithm.hyperbolic; + +import Localiser.LocaliserPane; +import PamController.SettingsPane; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamHBox; +import pamViewFX.fxNodes.PamSpinner; +import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; + +/** + * Hyperbolic settings pane. + * @author Jamie Macaulay + * + */ +public class HyperbolicSettingsPane extends LocaliserPane { + + PamBorderPane mainPane; + + /** + * The toggle switch. + */ + private PamToggleSwitch hyperbolicToggleSwitch; + + private PamSpinner numIterations; + + public HyperbolicSettingsPane() { + mainPane = new PamBorderPane(); + mainPane.setPadding(new Insets(5,5,5,5)); + + hyperbolicToggleSwitch = new PamToggleSwitch("Calculate errors"); + hyperbolicToggleSwitch.selectedProperty().addListener((obsVal, oldVal, newVal)->{ + numIterations.setDisable(!hyperbolicToggleSwitch.isSelected()); + }); + + numIterations = new PamSpinner(0,1000,100,1); + numIterations.setDisable(!hyperbolicToggleSwitch.isSelected()); + numIterations.setEditable(true); + + PamHBox hBox = new PamHBox( ); + hBox.setAlignment(Pos.CENTER_LEFT); + hBox.setSpacing(5); + hBox.getChildren().addAll(hyperbolicToggleSwitch, numIterations, new Label("Iterations")); + + mainPane.setTop(hBox); + + } + + + + @Override + public HyperbolicParams getParams(HyperbolicParams currParams) { + if (currParams ==null) currParams = new HyperbolicParams(); + currParams.bootStrapN = numIterations.getValue(); + currParams.calcErrors = hyperbolicToggleSwitch.isSelected(); + + return currParams; + } + + @Override + public void setParams(HyperbolicParams input) { + if (input==null) input = new HyperbolicParams(); + //System.out.println("Hyperbolic set Params: " + input.bootStrapN); + numIterations.getValueFactory().setValue(input.bootStrapN); + hyperbolicToggleSwitch.setSelected(input.calcErrors); + } + + @Override + public String getName() { + return "Hyperbolic parameters"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/group3dlocaliser/algorithm/toadbase/TOADBaseAlgorithm.java b/src/group3dlocaliser/algorithm/toadbase/TOADBaseAlgorithm.java index 0e98ada5..3bf13104 100644 --- a/src/group3dlocaliser/algorithm/toadbase/TOADBaseAlgorithm.java +++ b/src/group3dlocaliser/algorithm/toadbase/TOADBaseAlgorithm.java @@ -3,7 +3,6 @@ package group3dlocaliser.algorithm.toadbase; import java.awt.Window; import java.io.Serializable; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import Acquisition.AcquisitionControl; @@ -18,11 +17,8 @@ import Localiser.detectionGroupLocaliser.DetectionGroupOptions; import PamController.PamControlledUnitSettings; import PamController.PamSettingManager; import PamController.PamSettings; -import PamController.SettingsNameProvider; -import PamController.SettingsPane; import PamDetection.AbstractLocalisation; import PamUtils.PamUtils; -import PamUtils.complex.ComplexArray; import PamguardMVC.FFTDataHolder; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -32,7 +28,7 @@ import PamguardMVC.toad.GenericTOADCalculator; import PamguardMVC.toad.TOADCalculator; import fftManager.FFTDataUnit; import group3dlocaliser.Group3DLocaliserControl; -import group3dlocaliser.Group3DParams; +import group3dlocaliser.ToadManagedSettingsPane; import group3dlocaliser.algorithm.Chi2Data; import group3dlocaliser.algorithm.FitTestValue; import group3dlocaliser.algorithm.LocaliserAlgorithm3D; @@ -40,7 +36,21 @@ import group3dlocaliser.algorithm.LogLikelihoodData; import pamMaths.PamVector; import pamViewFX.fxNodes.pamDialogFX.ManagedSettingsPane; + abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { + + public enum MultiVariateType { + /** + * Chi2 fucntion + */ + CHI2, + + /** + * Log liklilihood function + */ + LOG_LIKILIHOOD; + } + private double sampleRate; @@ -54,8 +64,10 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { private TOADBaseParams toadBaseParams = new TOADBaseParams(); - private Group3DLocaliserControl group3dLocaliser; - + protected Group3DLocaliserControl group3dLocaliser; + + private TOADSettingsPaneWithChannels tspwc; + private static double halflog2pi = Math.log(2.*Math.PI)/2.; public TOADBaseAlgorithm(Group3DLocaliserControl group3dLocaliser) { @@ -90,6 +102,7 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { } double sampleRate = 0; for (PamDataUnit aDataUnit:subDetections) { + //System.out.println("Channels: " + aDataUnit.getChannelBitmap()); allChannels |= aDataUnit.getChannelBitmap(); sampleRate = aDataUnit.getParentDataBlock().getSampleRate(); } @@ -110,14 +123,21 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { SnapshotGeometry geometry = ArrayManager.getArrayManager().getSnapshotGeometry(hydrophoneMap, groupDataUnit.getTimeMilliseconds()); + + + System.out.println("HYDROPHONE 0 POSITION: "+ groupDataUnit.getTimeMilliseconds() + " " + geometry.getGeometry()[0]); + // if (groupDataUnit.getSubDetection(0).getUID() == 9035003222L) { -// if (superDetection.getSubDetection(0).getUID() == 9035004477L) { -// System.out.println("Found it"); -// } + // if (superDetection.getSubDetection(0).getUID() == 9035004477L) { + // System.out.println("Found it"); + // } TOADInformation toadInformation = toadCalculator.getTOADInformation(superDetection.getSubDetections(), sampleRate, allChannels, geometry); boolean toadOK = checkTOADInformation(toadInformation); + +// System.out.println("ToadOK: " + toadOK + " toadInformation: " + toadInformation + " allChannels: " + allChannels + " geometry: " + geometry); + if (!toadOK) { return null; } @@ -152,7 +172,7 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { public int countUsableTOADS(TOADInformation toadInformation) { return countUsableTOADS(toadInformation, toadBaseParams.getMinCorrelation()); } - + /** * Count the number of TOAD values which have a correlation coefficient or 'score' * >= the minimum. @@ -258,21 +278,24 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { return null; } + /** - * Calculate a Log Likelihood value for the given geometry and set of delays. - * @param geometry - * @param delays - * @return + * Calculates chi2 or log likelihood for a position. + * @param geometry - the geometry of the receivers. + * @param toadInformation - the received time delay information. + * @param position - the position to test. + * @param flag - which value to calculate. + * @return the chi2 or log likilihood value. */ - public LogLikelihoodData calcLogLikelihood(SnapshotGeometry geometry, TOADInformation toadInformation, double[] position) { + private FitTestValue multivariateFunction(SnapshotGeometry geometry, TOADInformation toadInformation, double[] position, MultiVariateType flag) { /** * There is an awful lot of repeated code in this and the Chi2 function. Would be good * to sort out some time ! */ -// if (1>0) { -// Chi2Data chiRes = calcChi2(geometry, toadInformation, position); -// LogLikelihoodData lld = new LogLikelihoodData(-chiRes.getChi2(), chiRes.getDegreesOfFreedom()); -// } + // if (1>0) { + // Chi2Data chiRes = calcChi2(geometry, toadInformation, position); + // LogLikelihoodData lld = new LogLikelihoodData(-chiRes.getChi2(), chiRes.getDegreesOfFreedom()); + // } if (position == null || position.length == 0) { return null; @@ -343,18 +366,140 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { * delay error, but more if you're end on. */ errSq += Math.pow(exp*cError/c, 2.); - /* - * Do the full calc -ln(2pi)/2 - .5ln(sig) - .5(diff^2/errSq). - * So note the 0.5 instead of the which is to take the sqrt(errSq) ! - */ - llVal -= (halflog2pi + 0.5*Math.log(errSq) + Math.pow(val-exp, 2)/errSq/2); -// llVal -= Math.pow(val-exp, 2)/errSq/2.; - nGood++; + + switch (flag) { + + case LOG_LIKILIHOOD: + /* + * Do the full calc -ln(2pi)/2 - .5ln(sig) - .5(diff^2/errSq). + * So note the 0.5 instead of the which is to take the sqrt(errSq) ! + */ + llVal -= (halflog2pi + 0.5*Math.log(errSq) + Math.pow(val-exp, 2)/errSq/2); + // llVal -= Math.pow(val-exp, 2)/errSq/2.; + nGood++; + break; + + case CHI2: + llVal += Math.pow(val-exp, 2)/errSq; + nGood++; + break; + + } } } - return new LogLikelihoodData(llVal, nGood-nDim); - } + + switch (flag) { + case LOG_LIKILIHOOD: + return new LogLikelihoodData(llVal, nGood-nDim); + case CHI2: + return new Chi2Data(llVal, nGood-nDim); + } + //something has gone pretty wrong + return null; + + } + + + /** + * Calculate a Log Likelihood value for the given geometry and set of delays. + * @param geometry + * @param delays + * @return + */ + public LogLikelihoodData calcLogLikelihood(SnapshotGeometry geometry, TOADInformation toadInformation, double[] position) { + + return (LogLikelihoodData) multivariateFunction( geometry, toadInformation, position, MultiVariateType.LOG_LIKILIHOOD); +// /** +// * There is an awful lot of repeated code in this and the Chi2 function. Would be good +// * to sort out some time ! +// */ +// // if (1>0) { +// // Chi2Data chiRes = calcChi2(geometry, toadInformation, position); +// // LogLikelihoodData lld = new LogLikelihoodData(-chiRes.getChi2(), chiRes.getDegreesOfFreedom()); +// // } +// +// if (position == null || position.length == 0) { +// return null; +// } +// double[][] delays = toadInformation.getToadSeconds(); +// double[][] delayErrors = toadInformation.getToadErrorsSeconds(); +// +// double minCorrelationValue = toadBaseParams.getMinCorrelation(); +// +// int nDim = position.length; +// int[] hydrophones = toadInformation.getHydrophoneList(); +// int[] channels = toadInformation.getChannelList(); +// int nChan = channels.length; +// double[] expectedDelays = new double[nChan]; +// double c = geometry.getCurrentArray().getSpeedOfSound(); +// double cError = geometry.getCurrentArray().getSpeedOfSoundError(); +// PamVector centre = geometry.getGeometricCentre(); +// double[] channelErrors = new double[nChan]; +// double[] streamerErrors = new double[nChan]; +// int[] streamerId = new int[nChan]; +// double[][] correlationScores = toadInformation.getToadScores(); +// PamVector positionVec = new PamVector(position).add(centre); +// // calculate the absolute distance (then time) to each hydrophone used in the delay matrix. +// for (int i = 0; i < nChan; i++) { +// double r = 0; +// /* +// * use the hydrophone LUT from TOAD information to make sure we get the right phone +// * since channel groups may not be in a sensible order. +// */ +// PamVector hydrophoneGeom = geometry.getGeometry()[hydrophones[i]]; +// PamVector rv = positionVec.sub(hydrophoneGeom); +// expectedDelays[i] = rv.norm(nDim) / c; +// /** +// * Now work out the expected range error along the unit vector position-hydrophone+centre +// */ +// PamVector rvu = rv.getUnitVector(); +// PamVector hErr = geometry.getHydrophoneErrors()[hydrophones[i]]; //NB. this is not really a vector ! +// channelErrors[i] = Math.pow(rvu.sumComponentsSquared(hErr)/c, 2); // square and convert to time now +// PamVector sErr = geometry.getStreamerErrors()[hydrophones[i]]; // this isn't a vector either +// streamerId[i] = geometry.getCurrentArray().getStreamerForPhone(hydrophones[i]); +// if (sErr != null) { +// streamerErrors[i] = Math.pow(rvu.sumComponentsSquared(sErr)/c, 2); +// } +// } +// double llVal = 0.; +// int nGood = 0; +// for (int i = 0; i < nChan; i++) { +// for (int j = i+1; j < nChan; j++) { +// double val = (delays[i][j]); +// if (Double.isNaN(val)) { +// continue; +// } +// if (correlationScores == null || correlationScores[i][j] < minCorrelationValue) { +// continue; +// } +// double exp = expectedDelays[j]-expectedDelays[i]; +// // now work out the squared error... +// double errSq = Math.pow(delayErrors[i][j], 2)+channelErrors[i]+channelErrors[j]; +// +// // add in the streamer errors if they are in different streamers. +// if (streamerId[i] != streamerId[j]) { +// errSq += streamerErrors[i]+streamerErrors[j]; +// } +// /* +// * Finally there is an error due to uncertainty in c, which is +// * obviously proportional to the expected delay. i.e. if you're +// * perpendicular to a hydrophone pair, then there will be zero +// * delay error, but more if you're end on. +// */ +// errSq += Math.pow(exp*cError/c, 2.); +// /* +// * Do the full calc -ln(2pi)/2 - .5ln(sig) - .5(diff^2/errSq). +// * So note the 0.5 instead of the which is to take the sqrt(errSq) ! +// */ +// llVal -= (halflog2pi + 0.5*Math.log(errSq) + Math.pow(val-exp, 2)/errSq/2); +// // llVal -= Math.pow(val-exp, 2)/errSq/2.; +// nGood++; +// } +// } +// return new LogLikelihoodData(llVal, nGood-nDim); + } + /** * Calculate a chi2 value for the given geometry and set of delays. * @param geometry array geometry. @@ -363,93 +508,104 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { * @return */ public Chi2Data calcChi2(SnapshotGeometry geometry, TOADInformation toadInformation, double[] position) { - if (position == null || position.length == 0) { - return null; - } - double[][] delays = toadInformation.getToadSeconds(); - double[][] delayErrors = toadInformation.getToadErrorsSeconds(); - - double minCorrelationValue = toadBaseParams.getMinCorrelation(); - - int nDim = position.length; - int[] hydrophones = toadInformation.getHydrophoneList(); - int[] channels = toadInformation.getChannelList(); - int nChan = channels.length; - double[] expectedDelays = new double[nChan]; - double c = geometry.getCurrentArray().getSpeedOfSound(); - double cError = geometry.getCurrentArray().getSpeedOfSoundError(); - PamVector centre = geometry.getGeometricCentre(); - double[] channelErrors = new double[nChan]; - double[] streamerErrors = new double[nChan]; - int[] streamerId = new int[nChan]; - double[][] correlationScores = toadInformation.getToadScores(); - PamVector positionVec = new PamVector(position).add(centre); - // calculate the absolute distance (then time) to each hydrophone used in the delay matrix. - for (int i = 0; i < nChan; i++) { - double r = 0; - /* - * use the hydrophone LUT from TOAD information to make sure we get the right phone - * since channel groups may not be in a sensible order. - */ - PamVector hydrophoneGeom = geometry.getGeometry()[hydrophones[i]]; - PamVector rv = positionVec.sub(hydrophoneGeom); - expectedDelays[i] = rv.norm() / c; - /** - * Now work out the expected range error along the unit vector position-hydrophone+centre - * - * These calculations were incorrect since they look at dot of the - * error against the direction to the source, which is wrong ! - * e.g. if error was (1,1,1) it appears as a vector along that bearing, if this - * were dotted with an angle perpendicular to this, then it would give a very - * different answer. - * The correct way to calculate the inter pair error is to add each component of - * the error along the vector joining the two hydrophones. This error should be stored - * as a pair error and then dot producted with the unit vector towards the source. - * Need to rewrite and produce a matrix of channel pair errors! - */ - PamVector rvu = rv.getUnitVector(); - PamVector hErr = geometry.getHydrophoneErrors()[hydrophones[i]]; - // if (hErr != null) { - channelErrors[i] = Math.pow(rvu.sumComponentsSquared(hErr)/c, 2); // square and convert to time now - // } - PamVector sErr = geometry.getStreamerErrors()[hydrophones[i]]; - streamerId[i] = geometry.getCurrentArray().getStreamerForPhone(hydrophones[i]); - if (sErr != null) { - streamerErrors[i] = Math.pow(rvu.sumComponentsSquared(sErr)/c, 2); - } - } - double chiVal = 0.; - int nGood = 0; - for (int i = 0; i < nChan; i++) { - for (int j = i+1; j < nChan; j++) { - double val = (delays[i][j]); - if (Double.isNaN(val)) { - continue; - } - if (correlationScores == null || correlationScores[i][j] < minCorrelationValue) { - continue; - } - double exp = expectedDelays[j]-expectedDelays[i]; - // now work out the squared error... - double errSq = Math.pow(delayErrors[i][j], 2)+channelErrors[i]+channelErrors[j]; - - // add in the streamer errors if they are in different streamers. - if (streamerId[i] != streamerId[j]) { + + return (Chi2Data) multivariateFunction( geometry, toadInformation, position, MultiVariateType.CHI2); +// +// if (position == null || position.length == 0) { +// return null; +// } +// double[][] delays = toadInformation.getToadSeconds(); +// double[][] delayErrors = toadInformation.getToadErrorsSeconds(); +// +// double minCorrelationValue = toadBaseParams.getMinCorrelation(); +// +// int nDim = position.length; +// int[] hydrophones = toadInformation.getHydrophoneList(); +// int[] channels = toadInformation.getChannelList(); +// int nChan = channels.length; +// double[] expectedDelays = new double[nChan]; +// double c = geometry.getCurrentArray().getSpeedOfSound(); +// double cError = geometry.getCurrentArray().getSpeedOfSoundError(); +// PamVector centre = geometry.getGeometricCentre(); +// double[] channelErrors = new double[nChan]; +// double[] streamerErrors = new double[nChan]; +// int[] streamerId = new int[nChan]; +// double[][] correlationScores = toadInformation.getToadScores(); +// PamVector positionVec = new PamVector(position).add(centre); +// // calculate the absolute distance (then time) to each hydrophone used in the delay matrix. +// for (int i = 0; i < nChan; i++) { +// double r = 0; +// /* +// * use the hydrophone LUT from TOAD information to make sure we get the right phone +// * since channel groups may not be in a sensible order. +// */ +// PamVector hydrophoneGeom = geometry.getGeometry()[hydrophones[i]]; +// PamVector rv = positionVec.sub(hydrophoneGeom); +// +// +// expectedDelays[i] = rv.norm() / c; +// /** +// * Now work out the expected range error along the unit vector position-hydrophone+centre +// * +// * These calculations were incorrect since they look at dot of the +// * error against the direction to the source, which is wrong ! +// * e.g. if error was (1,1,1) it appears as a vector along that bearing, if this +// * were dotted with an angle perpendicular to this, then it would give a very +// * different answer. +// * The correct way to calculate the inter pair error is to add each component of +// * the error along the vector joining the two hydrophones. This error should be stored +// * as a pair error and then dot producted with the unit vector towards the source. +// * Need to rewrite and produce a matrix of channel pair errors! +// */ +// PamVector rvu = rv.getUnitVector(); +// PamVector hErr = geometry.getHydrophoneErrors()[hydrophones[i]]; +// // if (hErr != null) { +// channelErrors[i] = Math.pow(rvu.sumComponentsSquared(hErr)/c, 2); // square and convert to time now +// // } +// PamVector sErr = geometry.getStreamerErrors()[hydrophones[i]]; +// streamerId[i] = geometry.getCurrentArray().getStreamerForPhone(hydrophones[i]); +// if (sErr != null) { +// streamerErrors[i] = Math.pow(rvu.sumComponentsSquared(sErr)/c, 2); +// } +// } +// double chiVal = 0.; +// int nGood = 0; +// for (int i = 0; i < nChan; i++) { +// for (int j = i+1; j < nChan; j++) { +// double val = (delays[i][j]); +// if (Double.isNaN(val)) { +// continue; +// } +// if (correlationScores == null || correlationScores[i][j] < minCorrelationValue) { +// continue; +// } +// double exp = expectedDelays[j]-expectedDelays[i]; +// // now work out the squared error... +// double errSq = Math.pow(delayErrors[i][j], 2)+channelErrors[i]+channelErrors[j]; +// +// // add in the streamer errors if they are in different streamers. +// if (streamerId[i] != streamerId[j]) { +// // errSq += streamerErrors[i]+streamerErrors[j]; +// // continue; simulate unsynchronised streamers. +// } +// +// // add in the streamer errors if they are in different streamers. +// if (streamerId[i] != streamerId[j]) { // errSq += streamerErrors[i]+streamerErrors[j]; - // continue; simulate unsynchronised streamers. - } - /* - * Finally there is an error due to uncertainty in c, which is - * obviously proportional to the expected delay. i.e. if you're - * perpendicular to a hydrophone pair, then there will be zero - * delay error, but more if you're end on. - */ - errSq += Math.pow(exp*cError/c, 2.); - chiVal += Math.pow(val-exp, 2)/errSq; - nGood++; - } - } - return new Chi2Data(chiVal, nGood-nDim); +// } +// +// /* +// * Finally there is an error due to uncertainty in c, which is +// * obviously proportional to the expected delay. i.e. if you're +// * perpendicular to a hydrophone pair, then there will be zero +// * delay error, but more if you're end on. +// */ +// errSq += Math.pow(exp*cError/c, 2.); +// chiVal += Math.pow(val-exp, 2)/errSq; +// nGood++; +// } +// } +// return new Chi2Data(chiVal, nGood-nDim); } /** * Estimate an elliptical error with axes aligned with largest error coordinate based on the curvature of the Chi2 / likelihood surface. @@ -459,22 +615,22 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { * @return Elliptical error */ public EllipticalError estimateEllipticalError(SnapshotGeometry geometry, TOADInformation toadInformation, double[] position) { -// Chi2Data centralChi2 = calcChi2(geometry, toadInformation, position); + // Chi2Data centralChi2 = calcChi2(geometry, toadInformation, position); /** * Will take the principle axis as a direct line to the detection from the central point of the array * geometry. */ -// PamVector arrayCentre = geometry.getGeometricCentre(); + // PamVector arrayCentre = geometry.getGeometricCentre(); // position is already relative to the geometric centre of the array. PamVector posVec = new PamVector(position); double[] errors = new double[6]; PamVector[] errVecs = new PamVector[3]; // get a unit vector along the direction from the array centre to the calculated coordinate. errVecs[0] = posVec.getUnitVector(); -// if (posVec.getCoordinate(0) > 6 && posVec.getCoordinate(0) < 8 && Math.abs(posVec.getCoordinate(1)) < 5) { -// System.out.println(posVec); -// } - + // if (posVec.getCoordinate(0) > 6 && posVec.getCoordinate(0) < 8 && Math.abs(posVec.getCoordinate(1)) < 5) { + // System.out.println(posVec); + // } + /* * to get secondary axis we can rotate by 90 degrees in any direction apart from * the principle direction of the vector. @@ -490,7 +646,7 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { errVecs[1] = (errVecs[0].vecProd(PamVector.zAxis)).getUnitVector(); errVecs[2] = errVecs[1].vecProd(errVecs[0]); } - + /* * Error along each principle axis. */ @@ -501,11 +657,11 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { } // now need to convert the error vectors to heading, pitch and roll. double[] angles = PamVector.getHeadingPitchRoll(errVecs); - + EllipticalError elError = new EllipticalError(angles, errors); return elError; } - + /** * Estimate the error in Cartesian coordinates based on the curvature of the Chi2 / likelihood surface. * @param geometry Array geometry @@ -544,7 +700,7 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { return simpleError; } - + /** * Get the error along the principle direction * @param geometry current array geometry @@ -588,9 +744,9 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { */ double nDF = Math.max(centralChi2.getDegreesOfFreedom(), 1); double chiCent = centralChi2.getTestScore(); -// if (chiCent == 0) { -// chiCent = 1; -// } + // if (chiCent == 0) { + // chiCent = 1; + // } double step; double[] sig = new double[2]; double[] shiftedChi = new double[2]; @@ -632,23 +788,24 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D { * @see group3dlocaliser.algorithm.LocaliserAlgorithm3D#getSourceSettingsPane(java.awt.Window) */ @Override - public ManagedSettingsPane getSourceSettingsPane(Window parent, PamDataBlock detectionSource) { + public ToadManagedSettingsPane getSourceSettingsPane(Window parent, PamDataBlock detectionSource) { // return new TOADSourcePane(parent); // return toadCalculator.getSettingsPane(); - if (toadCalculator != null) { + + //System.out.println("Get source settings pane: " +toadCalculator + " " + tspwc); + + if (toadCalculator != null && tspwc == null) { /* * this gets the algorithm specific settings pane. We want to tab * this with a channel panel. */ - ManagedSettingsPane toadPane = toadCalculator.getSettingsPane(parent, detectionSource); - TOADSettingsPaneWithChannels tspwc = new TOADSettingsPaneWithChannels<>(parent, this, toadPane); + ManagedSettingsPane toadPane = toadCalculator.getSettingsPane(parent, detectionSource); + tspwc = new TOADSettingsPaneWithChannels(parent, this, toadPane); tspwc.getChannelPanel().setMultiColumn(true); tspwc.getChannelPanel().setAvailableChannels(detectionSource.getChannelMap()); return tspwc; } - else { - return null; - } + return tspwc; } @Override diff --git a/src/group3dlocaliser/algorithm/toadbase/TOADInformation.java b/src/group3dlocaliser/algorithm/toadbase/TOADInformation.java index 6fcb59e0..4ba34d95 100644 --- a/src/group3dlocaliser/algorithm/toadbase/TOADInformation.java +++ b/src/group3dlocaliser/algorithm/toadbase/TOADInformation.java @@ -1,11 +1,13 @@ package group3dlocaliser.algorithm.toadbase; +import group3dlocaliser.algorithm.hyperbolic.HyperbolicParams; + /** * Class holding information about TOAD delays, channels, etc. * @author dg50 * */ -public class TOADInformation { +public class TOADInformation implements Cloneable { private double[][] toadSeconds; @@ -154,5 +156,17 @@ public class TOADInformation { public void setToadScores(double[][] toadScores) { this.toadScores = toadScores; } + + + @Override + public TOADInformation clone() { + try { + + return (TOADInformation) super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + } } diff --git a/src/group3dlocaliser/algorithm/toadbase/TOADOptionsPane.java b/src/group3dlocaliser/algorithm/toadbase/TOADOptionsPane.java index 8ce684d9..283d0e4e 100644 --- a/src/group3dlocaliser/algorithm/toadbase/TOADOptionsPane.java +++ b/src/group3dlocaliser/algorithm/toadbase/TOADOptionsPane.java @@ -13,7 +13,14 @@ import javafx.scene.layout.GridPane; import pamViewFX.fxNodes.PamGridPane; import pamViewFX.fxNodes.PamLabel; import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; +import pamViewFX.validator.PamValidator; +/** + * The TOAD options pane. + * + * @author Jamie Macaulay + * + */ public class TOADOptionsPane extends SettingsPane{ private BorderPane mainPane; @@ -21,22 +28,89 @@ public class TOADOptionsPane extends SettingsPane{ private TextField minCorrelation; private TextField minTimeDelays, minGroups; + + private PamValidator validator; public TOADOptionsPane(Object ownerWindow) { super(ownerWindow); + + validator = new PamValidator(); + mainPane = new BorderPane(); GridPane gridPane = new PamGridPane(); int x = 0, y = 0; gridPane.add(new PamLabel("Min correlation ", Pos.CENTER_RIGHT), x++, y); - gridPane.add(minCorrelation = new TextField("1234"), x, y); + gridPane.add(minCorrelation = new TextField("0"), x, y); + + + //create check to show at least some check boxes need to be selected. + validator.createCheck() + .dependsOn(("min_corr"), minCorrelation.textProperty()) + .withMethod(c -> { + + try { + double newCorr = Double.valueOf(minCorrelation.getText()); + if (newCorr>=0.9) { + c.warn("It's very unblikely you will get a correlation this high"); + } + if (newCorr>=1.) { + c.error("Correlation values cannot be higher than 1"); + } + + } + catch (Exception e) { + c.error("Input cannot be read as a number"); + } + }) + .decorates(minCorrelation) + .immediate(); + + x = 0; y++; gridPane.add(new PamLabel("Min TOAD measurements ", Pos.CENTER_RIGHT), x++, y); - gridPane.add(minTimeDelays = new TextField("1234"), x, y); + gridPane.add(minTimeDelays = new TextField("3"), x, y); + + validator.createCheck() + .dependsOn(("min_delays"), minTimeDelays.textProperty()) + .withMethod(c -> { + + try { + int minDelays = Integer.valueOf(minTimeDelays.getText()); + + //TODO would be great to add something which catches whether max delays have been reached. + + } + catch (Exception e) { + c.error("Min TOAD measurements cannot be read as a number"); + } + }) + .decorates(minTimeDelays) + .immediate(); + x = 0; y++; gridPane.add(new PamLabel("Min groups ", Pos.CENTER_RIGHT), x++, y); - gridPane.add(minGroups = new TextField("1234"), x, y); + gridPane.add(minGroups = new TextField("0"), x, y); + + + validator.createCheck() + .dependsOn(("min_groups"), minGroups.textProperty()) + .withMethod(c -> { + + try { + int minGroupsVal = Integer.valueOf(minGroups.getText()); + + //TODO would be great to add something which catches whether max delays have been reached. + + } + catch (Exception e) { + c.error("Minb groups input cannot be read as a number"); + } + }) + .decorates(minGroups) + .immediate(); + minCorrelation.setTooltip(new Tooltip("Minimum cross correlation coefficient for each hydrophone pair")); minTimeDelays.setTooltip(new Tooltip("Minimum number of time delays with acceptable cross correlation")); minGroups.setTooltip(new Tooltip("Minimum number of channel groups included with acceptable cross correlations")); diff --git a/src/group3dlocaliser/algorithm/toadbase/TOADSettingsPaneWithChannels.java b/src/group3dlocaliser/algorithm/toadbase/TOADSettingsPaneWithChannels.java index a08dbad0..d3ef1a02 100644 --- a/src/group3dlocaliser/algorithm/toadbase/TOADSettingsPaneWithChannels.java +++ b/src/group3dlocaliser/algorithm/toadbase/TOADSettingsPaneWithChannels.java @@ -2,26 +2,40 @@ package group3dlocaliser.algorithm.toadbase; import PamController.SettingsPane; import PamUtils.PamUtils; +import group3dlocaliser.ToadManagedSettingsPane; import javafx.scene.Node; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.TabPane.TabClosingPolicy; import javafx.scene.layout.BorderPane; +import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamTitledBorderPane; import pamViewFX.fxNodes.pamDialogFX.ManagedSettingsPane; import pamViewFX.fxNodes.pamDialogFX.SwingFXDialogWarning; import pamViewFX.fxNodes.utilityPanes.ChannelPanelFX; -public class TOADSettingsPaneWithChannels extends ManagedSettingsPane { +public class TOADSettingsPaneWithChannels extends ToadManagedSettingsPane { private ManagedSettingsPane toadSettingsPane; private ChSettingsPane tabbedPane; private TabPane tabPane; + + /** + * Get the tab pane for the settings. + * @return the tab pane for the different settings. + */ + @Override + public TabPane getTabPane() { + return tabPane; + } + private ChannelPanelFX channelPanel; private TOADBaseAlgorithm toadBaseAlgorithm; private Object parent; private TOADOptionsPane toadOptionsPane; + private boolean warnError = true; + public TOADSettingsPaneWithChannels(Object parent, TOADBaseAlgorithm toadBaseAlgorithm, ManagedSettingsPane toadSettingsPane) { this.parent = parent; @@ -42,7 +56,11 @@ public class TOADSettingsPaneWithChannels extends ManagedSettingsPane { int chMap = channelPanel.getChannelMap(); toadBaseAlgorithm.getToadBaseParams().setChannelBitmap(chMap); int nSelChannels = PamUtils.getNumChannels(chMap); - if (nSelChannels < 3) { + if (nSelChannels < 3 && warnError) { + /** + * TODO - This is a nasty way of doing this. There should be a validator in this pane which is checked before getParams is called ] + * but that would require a lot of restructuring. + */ return SwingFXDialogWarning.showWarning(parent, "Channel selection", "Not enough channels selected"); } TOADBaseParams ans = toadOptionsPane.getParams(toadBaseAlgorithm.getToadBaseParams()); @@ -51,6 +69,7 @@ public class TOADSettingsPaneWithChannels extends ManagedSettingsPane { @Override public T findParams() { + //System.out.println("CHANNELS: Get channel map: " + toadBaseAlgorithm.getToadBaseParams().getChannelBitmap() + " " + PamUtils.getNumChannels(toadBaseAlgorithm.getToadBaseParams().getChannelBitmap())); channelPanel.setChannelMap(toadBaseAlgorithm.getToadBaseParams().getChannelBitmap()); toadOptionsPane.setParams(toadBaseAlgorithm.getToadBaseParams()); return null; @@ -70,8 +89,17 @@ public class TOADSettingsPaneWithChannels extends ManagedSettingsPane { BorderPane boderPane = new BorderPane(); boderPane.setCenter(new PamTitledBorderPane("Timing options", toadOptionsPane.getContentNode())); boderPane.setBottom(toadSettingsPane.getSettingsPane().getContentNode()); - tabPane.getTabs().add(new Tab("Timing", boderPane)); - tabPane.getTabs().add(new Tab("Channels", new PamTitledBorderPane("Channel Selection", channelPanel.getContentNode()))); + + Tab timingTab = new Tab("Timing", boderPane); + timingTab.setGraphic(PamGlyphDude.createPamIcon("mdi2w-waveform")); + + tabPane.getTabs().add(timingTab ); + + + Tab channelsTab = new Tab("Channels", new PamTitledBorderPane("Channel Selection", channelPanel.getContentNode())); + channelsTab.setGraphic(PamGlyphDude.createPamIcon("mdi2f-format-list-numbered-rtl")); + + tabPane.getTabs().add(channelsTab); } @Override @@ -116,4 +144,10 @@ public class TOADSettingsPaneWithChannels extends ManagedSettingsPane { public ChannelPanelFX getChannelPanel() { return channelPanel; } + + @Override + public void setErrorWarn(boolean warn) { + this.warnError = warn; + + } } diff --git a/src/group3dlocaliser/algorithm/toadmcmc/MCMCEllipticalError.java b/src/group3dlocaliser/algorithm/toadmcmc/MCMCEllipticalError.java new file mode 100644 index 00000000..4b0210a8 --- /dev/null +++ b/src/group3dlocaliser/algorithm/toadmcmc/MCMCEllipticalError.java @@ -0,0 +1,58 @@ +package group3dlocaliser.algorithm.toadmcmc; + +import Localiser.algorithms.locErrors.EllipseLocErrorDraw; +import Localiser.algorithms.locErrors.EllipticalError; +import Localiser.algorithms.locErrors.LocErrorGraphics; +import PamUtils.PamArrayUtils; + +/** + * Elliptical error for MCMC. + * + * @author Jamie Macaulay + * + */ +public class MCMCEllipticalError extends EllipticalError { + + /** + * Class for drawing the error. + */ + private EllipseLocErrorDraw ellipseLocErrorDraw = new MCMCErrorDraw(this); + + + private float[][] points; + + + private double[] meanLoc; + + + public double[] getMeanLoc() { + return meanLoc; + } + + + + public MCMCEllipticalError(double[][] points, double[] meanLoc) { + super(points); + this.points = PamArrayUtils.double2Float(points); + this.meanLoc = meanLoc; + } + + + + public float[][] getPoints() { + return points; + } + + + + public void setPoints(float[][] points) { + this.points = points; + } + + + + @Override + public LocErrorGraphics getErrorDraw() { + return ellipseLocErrorDraw; + } +} diff --git a/src/group3dlocaliser/algorithm/toadmcmc/MCMCErrorDraw.java b/src/group3dlocaliser/algorithm/toadmcmc/MCMCErrorDraw.java new file mode 100644 index 00000000..70ff4f78 --- /dev/null +++ b/src/group3dlocaliser/algorithm/toadmcmc/MCMCErrorDraw.java @@ -0,0 +1,110 @@ +package group3dlocaliser.algorithm.toadmcmc; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Point2D; + +import Localiser.algorithms.locErrors.EllipseLocErrorDraw; +import Localiser.algorithms.locErrors.EllipticalError; +import Localiser.algorithms.locErrors.ErrorEllipse; +import PamUtils.LatLong; +import PamUtils.PamArrayUtils; +import PamView.GeneralProjector; +import PamView.TransformShape; +import PamguardMVC.PamDataUnit; +import pamMaths.PamVector; + + +/** + * Plots some point son the map + * @author Jamie Macaulay + * + */ +public class MCMCErrorDraw extends EllipseLocErrorDraw { + + private MCMCEllipticalError ellipticalErr; + + MCMCErrorDraw(MCMCEllipticalError ellipticalError) { + super(ellipticalError); + this.ellipticalErr = ellipticalError; + } + + @Override + public TransformShape drawOnMap(Graphics g, PamDataUnit pamDetection, LatLong errorOrigin, + GeneralProjector generalProjector, Color ellipseColor) { + if (getEllipticalError() == null) { + return null; + } + + if (ellipticalErr.getPoints()!=null) { + drawMCMCCloudOnMap(g, pamDetection, errorOrigin, generalProjector, ellipseColor); + } + + switch(getDrawType()) { + case DRAW_LINES: + return drawLinesOnMap(g, pamDetection, errorOrigin, generalProjector, ellipseColor); + case DRAW_OVALS: + return drawOvalsOnMap(g, pamDetection, errorOrigin, generalProjector, ellipseColor); + } + + + return null; + } + + + public TransformShape drawMCMCCloudOnMap(Graphics g, PamDataUnit pamDetection, LatLong errorOrigin, + GeneralProjector generalProjector, Color ellipseColor) { + + + //System.out.println("Start MCMCM Plot errors: start " + ellipticalErr.getPoints().length); + Graphics2D g2d = (Graphics2D)g; + + ellipticalErr.getPoints(); + + LatLong point1 = null; + LatLong point2 = null; + Point point1xy = null; + Point point2xy = null; + for (int i=0; i { + + private MCMCPane mainPane; + + public MCMCLocaliserPane() { + mainPane = new MCMCPane(); + ((Pane) mainPane.getContentNode()).setPadding(new Insets(5,5,5,5)); + } + + + @Override + public MCMCParams2 getParams(MCMCParams2 currParams) { + if (currParams==null) return mainPane.getParams(new MCMCParams2()); + return mainPane.getParams(currParams); + } + + @Override + public void setParams(MCMCParams2 input) { + if (input==null) input= new MCMCParams2(); + mainPane.setParams(input); + } + + @Override + public String getName() { + return "MCMC Settings"; + } + + @Override + public Node getContentNode() { + return mainPane.getContentNode(); + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + + +} diff --git a/src/group3dlocaliser/algorithm/toadmcmc/ToadMCMCLocaliser.java b/src/group3dlocaliser/algorithm/toadmcmc/ToadMCMCLocaliser.java new file mode 100644 index 00000000..20546f90 --- /dev/null +++ b/src/group3dlocaliser/algorithm/toadmcmc/ToadMCMCLocaliser.java @@ -0,0 +1,233 @@ +package group3dlocaliser.algorithm.toadmcmc; + +import java.util.ArrayList; + +import org.apache.commons.math.distribution.ChiSquaredDistributionImpl; + +import Array.ArrayManager; +import Array.SnapshotGeometry; +import Localiser.LocaliserPane; +import Localiser.algorithms.genericLocaliser.MinimisationFunction; +import Localiser.algorithms.genericLocaliser.MCMC.MCMC; +import Localiser.algorithms.genericLocaliser.MCMC.MCMCResult; +import Localiser.algorithms.locErrors.EllipticalError; +import Localiser.detectionGroupLocaliser.GroupLocResult; +import Localiser.detectionGroupLocaliser.GroupLocalisation; +import Localiser.algorithms.genericLocaliser.MCMC.MCMCParams2; +import PamDetection.AbstractLocalisation; +import PamDetection.LocContents; +import PamUtils.CPUMonitor; +import PamUtils.LatLong; +import PamUtils.PamArrayUtils; +import PamguardMVC.PamDataUnit; +import generalDatabase.SQLLoggingAddon; +import group3dlocaliser.Group3DLocaliserControl; +import group3dlocaliser.algorithm.Chi2Data; +import group3dlocaliser.algorithm.LocaliserAlgorithmParams; +import group3dlocaliser.algorithm.crossedbearing.CrossedBearingSQLAddon; +import group3dlocaliser.algorithm.hyperbolic.HyperbolicParams; +import group3dlocaliser.algorithm.toadbase.TOADBaseAlgorithm; +import group3dlocaliser.algorithm.toadbase.TOADInformation; +import group3dlocaliser.grouper.DetectionGroupedSet; +import pamMaths.PamVector; + + + +public class ToadMCMCLocaliser extends TOADBaseAlgorithm { + + MCMC mcmc = new MCMC(); + + /** + * CPU monitor + */ + private CPUMonitor cpuMCMC; + + private MCMCLocaliserPane mcmcSettingsPane; + + public ToadMCMCLocaliser(Group3DLocaliserControl group3dLocaliser) { + super(group3dLocaliser); + cpuMCMC = new CPUMonitor(); + + } + + + public DetectionGroupedSet preFilterLoc(DetectionGroupedSet preGroups) { + + + return preGroups; + } + + @Override + public String getName() { + return "MCMC"; + } + + @Override + public String getToolTipText() { + return "Time delay of arrival Markov chain Monte Carlo (MCMC) based localisation Computationaly very slow but less prone to runaway and calculates more accurate error distributions" ; + } + + @Override + public LocContents getLocContents() { + // TODO Auto-generated method stub + return null; + } + + @Override + public LocaliserPane getAlgorithmSettingsPane() { + if (mcmcSettingsPane==null) { + mcmcSettingsPane = new MCMCLocaliserPane(); + } + return mcmcSettingsPane; + } + + @Override + public boolean hasParams() { + return true; + } + + @Override + public void notifyModelProgress(double progress) { + // TODO Auto-generated method stub + } + + + + @Override + public AbstractLocalisation processTOADs(PamDataUnit groupDataUnit, SnapshotGeometry geometry, + TOADInformation toadInformation) { + +// System.out.println("Run MCMC: ------ " + groupDataUnit.getUID()); +// PamArrayUtils.printArray(toadInformation.getToadSeconds()); + + /** + * This module is a little odd in that it stores paramters for each algorithm in it's own has table without acc + */ + MCMCParams2 params = (MCMCParams2) group3dLocaliser.getLocaliserAlgorithmParams(this).getAlgorithmParameters(); + + cpuMCMC.start(); + PamVector centre = geometry.getGeometricCentre(); + + + MCMCChi2Function chi2Func = new MCMCChi2Function(geometry, toadInformation); + mcmc.setChi2(chi2Func); + + //set the parameters. + mcmc.setSettings(params); + + //these are the *best results. + ArrayList mcmcResult = mcmc.runMCMCAlgorithm(); + + + + GroupLocalisation groupLocalisation = null; + for (int i=0; i { + + private MCMCPane mcmcPane; + + private PamVBox mainPane; + + private PamToggleSwitch toggleSwitch; + + public MimplexLocaliserPane() { + + mcmcPane = new MCMCPane(); + + mainPane = new PamVBox(); + mainPane.setSpacing(5); + + mainPane.getChildren().add( createMimplexPane()); + mainPane.getChildren().add(mcmcPane.getContentNode()); + + mainPane.setPadding(new Insets(5,5,5,5)); + + } + + private Pane createMimplexPane() { + + PamVBox holder = new PamVBox(); + + toggleSwitch = new PamToggleSwitch("Always localise first combination"); + + Label label = new Label("Pre-localisation"); + label.setFont(Font.font(null,FontWeight.BOLD, 11)); + + + holder.getChildren().addAll(label, toggleSwitch); + + return holder; + } + + @Override + public MimplexParams getParams(MimplexParams currParams) { + + MimplexParams params; + if (currParams==null) params = new MimplexParams(); + else params = currParams; + + params.useFirstCombination = toggleSwitch.isSelected(); + + + params = (MimplexParams) mcmcPane.getParams(params); + + return params; + } + + + @Override + public void setParams(MimplexParams input) { + if (input==null) input= new MimplexParams(); + + toggleSwitch.setSelected(input.useFirstCombination); + mcmcPane.setParams(input); + } + + @Override + public String getName() { + return "Mimplex Settings"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + + +} diff --git a/src/group3dlocaliser/algorithm/toadmimplex/MimplexParams.java b/src/group3dlocaliser/algorithm/toadmimplex/MimplexParams.java new file mode 100644 index 00000000..83c22067 --- /dev/null +++ b/src/group3dlocaliser/algorithm/toadmimplex/MimplexParams.java @@ -0,0 +1,17 @@ +package group3dlocaliser.algorithm.toadmimplex; + +import Localiser.algorithms.genericLocaliser.MCMC.MCMCParams2; + +public class MimplexParams extends MCMCParams2 { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Always use the first combination + */ + public boolean useFirstCombination = true; + +} diff --git a/src/group3dlocaliser/algorithm/toadmimplex/ToadMimplexLocaliser.java b/src/group3dlocaliser/algorithm/toadmimplex/ToadMimplexLocaliser.java new file mode 100644 index 00000000..797fd542 --- /dev/null +++ b/src/group3dlocaliser/algorithm/toadmimplex/ToadMimplexLocaliser.java @@ -0,0 +1,196 @@ +package group3dlocaliser.algorithm.toadmimplex; + +import java.util.ArrayList; + +import Localiser.LocaliserModel; +import Localiser.LocaliserPane; +import Localiser.algorithms.genericLocaliser.MCMC.MCMCParams2; +import Localiser.detectionGroupLocaliser.GroupLocResult; +import Localiser.detectionGroupLocaliser.GroupLocalisation; +import PamDetection.AbstractLocalisation; +import PamguardMVC.PamDataBlock; +import group3dlocaliser.Group3DDataUnit; +import group3dlocaliser.Group3DLocaliserControl; +import group3dlocaliser.algorithm.hyperbolic.HyperbolicLocaliser; +import group3dlocaliser.algorithm.toadbase.TOADBaseAlgorithm; +import group3dlocaliser.algorithm.toadmcmc.MCMCLocaliserPane; +import group3dlocaliser.algorithm.toadmcmc.ToadMCMCLocaliser; +import group3dlocaliser.algorithm.toadsimplex.ToadSimplexLocaliser; +import group3dlocaliser.grouper.DetectionGroupedSet; + + +/** + * The Mimplex localiser is similar to MCMC but it localises multiple possible combinations using fast algorithms then uses MCMC to localiser the final combination. + * @author Jamie Macaulay + * + */ +public class ToadMimplexLocaliser extends ToadMCMCLocaliser { + + + /** + * The simplex localiser for pre-filtering detections. + */ + ToadSimplexLocaliser simplexLocaliser; + + + /** + * The hyperbolic localiser for pre-filtering detections. + */ + HyperbolicLocaliser hyperbolicLoclaiser; + + ArrayList preLocaliserModels = new ArrayList(); + + /** + * A settings pane for the Mimplex localiser + */ + private MimplexLocaliserPane mimplexSettingsPane; + + + public ToadMimplexLocaliser(Group3DLocaliserControl group3dLocaliser) { + super(group3dLocaliser); + + //note that Mimplex only supports 3D localisations. + simplexLocaliser = new ToadSimplexLocaliser(group3dLocaliser, 3); + + hyperbolicLoclaiser = new HyperbolicLocaliser(group3dLocaliser); + + preLocaliserModels.add(hyperbolicLoclaiser); + preLocaliserModels.add(simplexLocaliser); + + } + + @Override + public String getName() { + return "Mimplex"; + } + + + @Override + public String getToolTipText() { + return "Uses a combination of faster and slower algorithms to localise. Useful if there is match uncertainty between detections"; + } + + @Override + public boolean prepare(PamDataBlock sourceBlock) { + //need to prep our pre-localiser models. + for (TOADBaseAlgorithm model: preLocaliserModels) { + model.prepare(sourceBlock); + + //important to set the toad params here or nothing will work... + model.getToadBaseParams().setChannelBitmap(this.getToadBaseParams().getChannelBitmap()); + model.getToadBaseParams().setMinCorrelatedGroups(this.getToadBaseParams().getMinCorrelatedGroups()); + model.getToadBaseParams().setMinCorrelation(this.getToadBaseParams().getMinCorrelation()); + model.getToadBaseParams().setMinCorrelatedGroups(this.getToadBaseParams().getMinCorrelatedGroups()); + + } + + return super.prepare(sourceBlock); + } + + /** + * Option to pre-filter the localisation results. This can be useful when using algorithms that + * internally handle detection match uncertainty. + * @param - the initial set of detection matches to filter. + */ + public DetectionGroupedSet preFilterLoc(DetectionGroupedSet preGroups) { + + System.out.println("Pre filter groups: " + preGroups.getNumGroups()); + + MimplexParams params = (MimplexParams) group3dLocaliser.getLocaliserAlgorithmParams(this).getAlgorithmParameters(); + + if (params==null) params = new MimplexParams(); + + //no need to do any more processing + if (preGroups.getNumGroups()<=1) { + return preGroups; + } + + //no need to do a y more processing. + if (preGroups.getNumGroups()<=2 && params.useFirstCombination) { + return preGroups; + } + + //localiser using both hyperbolic and the + // will have to make a data unit for each group now... + Group3DDataUnit[] group3dDataUnits = new Group3DDataUnit[preGroups.getNumGroups()]; + + ArrayList preLocalisations = new ArrayList(); + + + for (int i=0; i=0) { + groupedSet.addGroup(preGroups.getGroup(bestLocIndex)); + } + + System.out.println("Number of groups out: " + groupedSet.getNumGroups()); + + return groupedSet; + } + + @Override + public LocaliserPane getAlgorithmSettingsPane() { + if (mimplexSettingsPane==null) { + mimplexSettingsPane = new MimplexLocaliserPane(); + } + return mimplexSettingsPane; + } + + +} diff --git a/src/group3dlocaliser/algorithm/toadsimplex/ToadSimplexLocaliser.java b/src/group3dlocaliser/algorithm/toadsimplex/ToadSimplexLocaliser.java index d362a8ef..1b2e8242 100644 --- a/src/group3dlocaliser/algorithm/toadsimplex/ToadSimplexLocaliser.java +++ b/src/group3dlocaliser/algorithm/toadsimplex/ToadSimplexLocaliser.java @@ -1,6 +1,6 @@ package group3dlocaliser.algorithm.toadsimplex; - +import java.io.Serializable; import java.util.Arrays; import java.util.Random; @@ -13,28 +13,24 @@ import org.apache.commons.math.optimization.OptimizationException; import org.apache.commons.math.optimization.RealPointValuePair; import org.apache.commons.math.optimization.SimpleScalarValueChecker; import org.apache.commons.math.optimization.direct.NelderMead; -import org.apache.commons.math3.distribution.ChiSquaredDistribution; - import Array.ArrayManager; import Array.SnapshotGeometry; import Localiser.LocaliserPane; +import Localiser.algorithms.genericLocaliser.MinimisationFunction; import Localiser.algorithms.locErrors.EllipticalError; -import Localiser.algorithms.locErrors.SimpleError; +import Localiser.algorithms.locErrors.LikilihoodError; import Localiser.detectionGroupLocaliser.GroupLocResult; import Localiser.detectionGroupLocaliser.GroupLocalisation; -import PamDetection.AbstractLocalisation; import PamDetection.LocContents; import PamUtils.CPUMonitor; import PamUtils.LatLong; import PamguardMVC.PamDataUnit; -import PamguardMVC.debug.Debug; import generalDatabase.SQLLoggingAddon; import group3dlocaliser.Group3DLocaliserControl; import group3dlocaliser.algorithm.Chi2Data; +import group3dlocaliser.algorithm.LocaliserAlgorithmParams; import group3dlocaliser.algorithm.LogLikelihoodData; import group3dlocaliser.algorithm.crossedbearing.CrossedBearingSQLAddon; -import group3dlocaliser.algorithm.gridsearch.TOADGridSearch; -import group3dlocaliser.algorithm.hyperbolic.HyperbolicLocaliser; import group3dlocaliser.algorithm.toadbase.TOADBaseAlgorithm; import group3dlocaliser.algorithm.toadbase.TOADInformation; import pamMaths.PamHistogram; @@ -87,7 +83,15 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { public GroupLocalisation processTOADs(PamDataUnit groupDataUnit, SnapshotGeometry geometry, TOADInformation toadInformation) { // get the channel geometry. nCalls++; + + boolean usell = false; + +// ///////////////////////////// +// System.out.println("TOADInformation: " + toadInformation); +// PamArrayUtils.printArray(toadInformation.getToadSeconds()); +// ////////////////////// + PamVector centre = geometry.getGeometricCentre(); double arraySize = geometry.getMaxSeparation(); @@ -101,6 +105,9 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { RealPointValuePair[] results = new RealPointValuePair[nStartPositions]; Chi2Data[] resultChiData = new Chi2Data[nStartPositions]; cpuSimplex.start(); + + MultivariateRealFunction chiFunc = null; + for (int iStart = 0; iStart < nStartPositions; iStart ++) { /* * Start the first iteration in the centre, then randomly jump about guided by the array size. @@ -122,11 +129,10 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { // deal with 2D case start = Arrays.copyOf(start, nDimensions); } - - boolean usell = true; - MultivariateRealFunction chiFunc; + //MultivariateRealFunction chiFunc; GoalType goal; if (usell) { + //This does not seem to work very well! chiFunc = new LogLikelihoodFunction(geometry, toadInformation); goal = GoalType.MAXIMIZE; } @@ -156,9 +162,14 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { // double[] pos = result.getPoint(); // resultVectors[iStart] = new PamVector(pos); // lastGoodDelays = delays; + +// System.out.println("Simplex result " + iStart); +// PamArrayUtils.printArray( result.getPoint()); Chi2Data chiData = calcChi2(geometry, toadInformation, result.getPoint()); resultChiData[iStart] = chiData; + + } cpuSimplex.stop(); @@ -166,8 +177,6 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { if (iBest < 0) { return null; } - - Chi2Data chiData = resultChiData[iBest]; if (chiData.getChi2() / chiData.getDegreesOfFreedom() < 100) { @@ -176,6 +185,12 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { double[] posVec = results[iBest].getPoint(); double r = new PamVector(posVec).norm(nDimensions); + + + // lastGoodDelays = delays; + +// System.out.println("Simplex best " + iBest); +// PamArrayUtils.printArray(posVec); if (chiData.getChi2() / chiData.getDegreesOfFreedom() < 100 && r < 30) { // some diagnostic book keeping for better results. @@ -208,18 +223,39 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { // System.out.printf("Res %d = %3.5f, ", i, posVec[i]); // } // SimpleError cartErr = estimateCartesianError(geometry, toadInformation, posVec); + + //FIXME - this return super weird results EllipticalError ellipErr = estimateEllipticalError(geometry, toadInformation, posVec); +// if (usell) { +// ellipErr = estimateEllipticalError(geometry, toadInformation, posVec); +// } +// else { +// ellipErr = estimateEllipticalError(geometry, toadInformation, posVec); +// +//// SimpleMinimisation simpleMin = new SimpleMinimisation(chiFunc, nDimensions, start, firstStep); +//// ellipErr = new LikilihoodError(simpleMin, posVec); +// } + + // System.out.printf(", Chi2 = %3.1f, p=%3.1f, ndf = %d, Err=%s\n", - // chiData.getChi2(), cumProb, chiData.getNdf(), cartErr.getJsonErrorString()); - + // chiData.getChi2(), cumProb, chiData.getNdf(), cartErr.getJsonErrorString()); + // System.out.println(ellipErr.getJsonErrorString()); /** * Calculated position was relative to array centre, so now need to add the array * centre to the estimated position and then reference this to the ref GPS position. */ LatLong pos = geometry.getReferenceGPS().addDistanceMeters(new PamVector(posVec).add(centre)); + + + + // TargetMotionResult tmResult = new TargetMotionResult(geometry.getTimeMilliseconds(), null, pos, 0, 0); + +// System.out.println("New group localisation: height: " + pos.getHeight() + " ref height: " + +// geometry.getReferenceGPS().getHeight() + " posVec: " + posVec[2]); + GroupLocResult glr = new GroupLocResult(pos, 0, chiData.getChi2()); glr.setError(ellipErr); glr.setPerpendicularDistance(0.); @@ -240,7 +276,7 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { glr.setProbability(null); } GroupLocalisation groupLocalisation = new GroupLocalisation(groupDataUnit, glr); - + return groupLocalisation; } @@ -410,7 +446,7 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { * @see Localiser.LocaliserModel#getSettingsPane() */ @Override - public LocaliserPane getSettingsPane() { + public LocaliserPane getAlgorithmSettingsPane() { // TODO Auto-generated method stub return null; } @@ -447,6 +483,56 @@ public class ToadSimplexLocaliser extends TOADBaseAlgorithm { } return arrayShape >= ArrayManager.ARRAY_TYPE_PLANE; } + + + /** + * Wrapper for converting a MultivariateRealFunction to a MinimisationFunction. + * + * @author Jamie Macaulay + * + */ + class SimpleMinimisation implements MinimisationFunction { + + private MultivariateRealFunction fucntion; + private int nDim; + private double[] start; + private double[] firstStep; + + public SimpleMinimisation(MultivariateRealFunction fucntion, int nDim, double[] start, double[] firstStep) { + this.fucntion=fucntion; + this.nDim= nDim; + this.start= start; + this.firstStep= firstStep; + } + + @Override + public double value(double[] location) { + try { + return fucntion.value(location); + } catch (FunctionEvaluationException | IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return Double.NaN; + + } + } + + @Override + public int getDim() { + return nDim; + } + + @Override + public double[] getStart() { + return start; + } + + @Override + public double[] getFirstStep() { + return firstStep; + } + + } } diff --git a/src/group3dlocaliser/dialog/GroupLocSettingPaneFX.java b/src/group3dlocaliser/dialog/GroupLocSettingPaneFX.java index da7f91c7..af0872a7 100644 --- a/src/group3dlocaliser/dialog/GroupLocSettingPaneFX.java +++ b/src/group3dlocaliser/dialog/GroupLocSettingPaneFX.java @@ -1,47 +1,31 @@ package group3dlocaliser.dialog; +import java.io.Serializable; import java.util.ArrayList; -import javax.swing.JFrame; - import Array.ArrayManager; import PamController.SettingsPane; -import PamDetection.PamDetection; import PamView.GroupedDataSource; -import PamView.symbol.SwingSymbolOptionsPanel; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import group3dlocaliser.Group3DLocaliserControl; import group3dlocaliser.Group3DParams; +import group3dlocaliser.ToadManagedSettingsPane; import group3dlocaliser.algorithm.LocaliserAlgorithm3D; import group3dlocaliser.algorithm.LocaliserAlgorithmParams; -import group3dlocaliser.algorithm.LocaliserAlgorithmProvider; import group3dlocaliser.grouper.DetectionGrouperParams; import group3dlocaliser.grouper.dialog.GrouperSettingsPane; -import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.ActionEvent; import javafx.event.EventHandler; -import javafx.geometry.Pos; import javafx.scene.Node; -import javafx.scene.Parent; -import javafx.scene.control.Button; import javafx.scene.control.ChoiceBox; -import javafx.scene.control.Label; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import pamViewFX.PamGuiManagerFX; import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; -import pamViewFX.fxNodes.PamGridPane; import pamViewFX.fxNodes.PamTabPane; import pamViewFX.fxNodes.PamTitledBorderPane; import pamViewFX.fxNodes.pamDialogFX.ManagedSettingsPane; @@ -65,7 +49,7 @@ public class GroupLocSettingPaneFX extends SettingsPane{ private ManagedSettingsPane algorithmSourcePane; - private Button algoOptsButton; +// private Button algoOptsButton; private PamBorderPane algoSourceHolder; @@ -75,9 +59,10 @@ public class GroupLocSettingPaneFX extends SettingsPane{ public GroupLocSettingPaneFX(Group3DLocaliserControl group3dLocaliserControl, Object ownerWindow) { super(ownerWindow); + this.ownerWindow = ownerWindow; this.group3dLocaliserControl = group3dLocaliserControl; - + PamTabPane tabPane = new PamTabPane(); tabPane.setAddTabButton(false); tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); @@ -89,10 +74,10 @@ public class GroupLocSettingPaneFX extends SettingsPane{ public void changed(ObservableValue observable, PamDataBlock oldValue, PamDataBlock newValue) { newDataBlockSelection(newValue); - +// grouperSettingsPane.newSourceGroup(((GroupedDataSource) newValue).getGroupSourceParameters()); } }); - + grouperSettingsPane = new GrouperSettingsPane(ownerWindow, "Detection matching options"); PamBorderPane gsp = new PamBorderPane(); gsp.setTop(grouperSettingsPane.getContentNode()); @@ -107,21 +92,21 @@ public class GroupLocSettingPaneFX extends SettingsPane{ borderPane.setCenter(gsp); tabPane.getTabs().add(new Tab("Detection source", borderPane)); - - algorithms = new ChoiceBox<>(); setAlgorithmList(); // call here so that box gets correct size. PamBorderPane algoGrid = new PamBorderPane(); + // HBox.setHgrow(algoGrid, Priority.ALWAYS); algoGrid.setCenter(algorithms); algorithms.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); GridPane.setFillWidth(algorithms, true); // algoGrid.add(new Label("Algorithm Options "), 0, 1); // algoOptsButton = new Button("",PamGlyphDude.createPamGlyph(MaterialIcon.SETTINGS,Color.WHITE, PamGuiManagerFX.iconSize)); - algoOptsButton = new Button("",PamGlyphDude.createPamIcon("mdi2c-cog",Color.WHITE, PamGuiManagerFX.iconSize)); - algoGrid.setRight(algoOptsButton); - algoOptsButton.setTooltip(new Tooltip("More Algorithm Options ...")); +// algoOptsButton = new Button("",PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconSize)); +// algoGrid.setRight(algoOptsButton); +// PamBorderPane.setMargin(algoOptsButton, new Insets(0,0,0,5)); +// algoOptsButton.setTooltip(new Tooltip("More Algorithm Options ...")); algoSourceHolder = new PamBorderPane(); // HBox.setHgrow(algoSourceHolder, Priority.ALWAYS); // algoGrid.add(algoSourceHolder, 0, 2, 4, 1); @@ -129,14 +114,15 @@ public class GroupLocSettingPaneFX extends SettingsPane{ PamTitledBorderPane ptb = new PamTitledBorderPane("Select Localisation Algorithm", algoGrid); algoMainPane.setTop(ptb); algoMainPane.setCenter(algoSourceHolder); - tabPane.getTabs().add(new Tab("Algorithm", algoMainPane)); + tabPane.getTabs().add(new Tab("Localisation", algoMainPane)); - algoOptsButton.setOnAction(new EventHandler() { - @Override - public void handle(ActionEvent event) { - moreAlgorithmOptions(); - } - }); + +// algoOptsButton.setOnAction(new EventHandler() { +// @Override +// public void handle(ActionEvent event) { +// moreAlgorithmOptions(); +// } +// }); algorithms.setOnAction(new EventHandler() { @Override @@ -145,14 +131,16 @@ public class GroupLocSettingPaneFX extends SettingsPane{ } }); - mainPane.setCenter(tabPane); + } /** * Called whenever the algorithm selection is changed. */ protected void selectAlgorithm() { + + /** * Algorithm options fall into two separate parts. The first, which primarily involves * timing measurements is included in the bottom half of the main dialog tab, so @@ -165,16 +153,18 @@ public class GroupLocSettingPaneFX extends SettingsPane{ */ String algoName = algorithms.getSelectionModel().getSelectedItem(); if (algoName == null) { - algoOptsButton.setDisable(true); + //algoOptsButton.setDisable(true); return; } LocaliserAlgorithm3D localiserAlgorithm = group3dLocaliserControl.findAlgorithm(algoName); + //System.out.println(" selectAlgorithm: object " + localiserAlgorithm); + if (localiserAlgorithm == null) { - algoOptsButton.setDisable(true); + //algoOptsButton.setDisable(true); return; } // also enable / disable the more options button ... - algoOptsButton.setDisable(localiserAlgorithm.hasParams() == false); + //algoOptsButton.setDisable(localiserAlgorithm.hasParams() == false); /** * Need to immediately tell the algorithm which input we're using so that it can @@ -192,13 +182,14 @@ public class GroupLocSettingPaneFX extends SettingsPane{ * what options to display, i.t TOAD options for clicks, whistles and generic * Time frequency things are NOT the same. */ - ManagedSettingsPane newPane = localiserAlgorithm.getSourceSettingsPane(getAWTWindow(), currSource); - if (newPane == algorithmSourcePane) { - return; - } - if (algorithmSourcePane != null) { - algoSourceHolder.setCenter(null); - } + ToadManagedSettingsPane newPane = localiserAlgorithm.getSourceSettingsPane(getAWTWindow(), currSource); + + //System.out.println(" selectAlgorithm: newPane " + newPane); + + + algoSourceHolder.setCenter(null); + + if (newPane != null) { // PamTitledBorderPane p = new PamTitledBorderPane("Algorithm options", newPane.getSettingsPane().getContentNode()); PamBorderPane borderPane = new PamBorderPane(newPane.getSettingsPane().getContentNode()); @@ -206,10 +197,67 @@ public class GroupLocSettingPaneFX extends SettingsPane{ borderPane.setRightSpace(5); borderPane.setBottomSpace(5); algoSourceHolder.setCenter(borderPane); - LocaliserAlgorithmParams locParams = group3dLocaliserControl.getLocaliserAlgorithmParams(localiserAlgorithm); - newPane.setParams(); +// LocaliserAlgorithmParams locParams = group3dLocaliserControl.getLocaliserAlgorithmParams(localiserAlgorithm); +// newPane.setParams(); + // newPane.setDetectionSource(sourcePanel.getSource()); } + + //System.out.println(" selectAlgorithm: newPane " + localiserAlgorithm.getAlgorithmSettingsPane()); + + /** + * Now set up the advanced settings for the localiser algorothm. If FX add as a tab. If Swing then add a settings button to a new tab. + */ + if (localiserAlgorithm.getAlgorithmSettingsPane()!=null) { + + Node settingsPane = null; + + if (localiserAlgorithm.getAlgorithmSettingsPane()!=null) { + //preferentially select FX pane of one exists. + settingsPane = localiserAlgorithm.getAlgorithmSettingsPane().getContentNode(); + } + //there must be a dialog - otherwise what is the point of a settings pane that does nothing - it should be null. +// else { +// PamHBox hBox = new PamHBox(); +// hBox.setSpacing(5); +// hBox.setAlignment(Pos.CENTER); +// hBox.setPadding(new Insets(5,5,5,5)); +// +// PamButton algoOptsButton = new PamButton("",PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconSize)); +// algoOptsButton.setOnAction((action)->{ +// moreAlgorithmOptions() ; +// }); +// algoOptsButton.setTooltip(new Tooltip("More Algorithm Options ...")); +// +// hBox.getChildren().addAll(new Label(localiserAlgorithm.getName() + " settings"), algoOptsButton); +// +// settingsPane = hBox; +// } +// + + if (newPane==null) { + //System.out.println("Set settings pane: " + settingsPane); + algoSourceHolder.setCenter(settingsPane); + } + else { + //clear the tab pane + for (int i=0; i{ BorderPaneFX2AWT.repackSwingDialog(getContentNode()); } - /** - * Handle algorithm options ... - */ - protected void moreAlgorithmOptions() { - String algoName = algorithms.getSelectionModel().getSelectedItem(); - if (algoName == null) { - return; - } - LocaliserAlgorithm3D localiserAlgorithm = group3dLocaliserControl.findAlgorithm(algoName); - if (localiserAlgorithm == null) { - return; - } - if (localiserAlgorithm.hasParams() == false) { - return; - } - LocaliserAlgorithmParams algorithmPaams = group3dLocaliserControl.getLocaliserAlgorithmParams(localiserAlgorithm); - algorithmPaams = localiserAlgorithm.showAlgorithmDialog(getAWTWindow(), algorithmPaams); - if (algorithmPaams != null) { - group3dLocaliserControl.setAlgorithmParams(localiserAlgorithm, algorithmPaams); - } - } + + @Override public Group3DParams getParams(Group3DParams currParams) { + if (currParams==null) currParams = new Group3DParams(); + PamDataBlock currSource = sourcePanel.getSource(); if (currSource == null) { SwingFXDialogWarning.showWarning(this.getOwnerWindow(), "Invalid DataBlock", "You must select a data source"); @@ -279,34 +310,87 @@ public class GroupLocSettingPaneFX extends SettingsPane{ SwingFXDialogWarning.showWarning(this.getOwnerWindow(), "Localisation algorithm", "You must select a localisation algorithm"); return null; } - currParams.setAlgorithmName(algoName); - LocaliserAlgorithm3D algoProvider = group3dLocaliserControl.findAlgorithm(algoName); - if (algorithmSourcePane != null && currParams != null && algoProvider != null) { - LocaliserAlgorithmParams locParams = currParams.getAlgorithmParams(algoProvider); - if (algorithmSourcePane != null) { - Object ans = algorithmSourcePane.getParams(); - if (ans == null) { - return null; - } -// else { -// locParams.setAlgorithmParams(algoProvider, ans); -// } -// -// if (ans instanceof LocaliserAlgorithmParams) { -// currParams.setAlgorithmParams(algoProvider, (LocaliserAlgorithmParams) ans); -// } + //set the currently select algorithm + currParams.setAlgorithmName(algoName); + + //now iterate through all the algorithms and save both the algorithm source settings and the save settings. If the user has changed multiple + //a settings don't want just the current algorithm settings being saved. + for (int i = 0; i< algorithms.getItems().size(); i++ ) { + + LocaliserAlgorithm3D algoProvider = group3dLocaliserControl.findAlgorithm(algorithms.getItems().get(i)); + + LocaliserAlgorithmParams locAlgorithmParams = currParams.getAlgorithmParams(algoProvider); + + + if (algoProvider.getAlgorithmSettingsPane()!=null) { + + //get the algorithm paramters. + Serializable newAlgorithmParams = algoProvider.getAlgorithmSettingsPane().getParams( + currParams.getAlgorithmParams(algoProvider) == null? null : currParams.getAlgorithmParams(algoProvider).getAlgorithmParameters()); + + if (locAlgorithmParams==null) locAlgorithmParams = new LocaliserAlgorithmParams(); + locAlgorithmParams.setAlgorithmParameters(newAlgorithmParams); + + //FIXME - note that, if a swing dialog has been used the algorithms have already been set....not great - want to + //try and get swing out of this if possible. } -//// LocaliserAlgorithmParams sourceParams = algorithmSourcePane.getParams(locParams); -// if (sourceParams == null) { -//// SwingFXDialogWarning.showWarning(this.getOwnerWindow(), "Localisation algorithm", "Invalid al"); -// return null; -// } -// currParams.setAlgorithmParams(algoProvider, sourceParams); + + if (algoProvider.getSourceSettingsPane(null, currSource)!=null) { + + //set only the selected algorithm to show the user warning sif settings are wrong + if (algorithms.getSelectionModel().getSelectedIndex()==i) { + //this is the currently selected algorithm + algoProvider.getSourceSettingsPane(null, currSource).setErrorWarn(true); + } + else { + algoProvider.getSourceSettingsPane(null, currSource).setErrorWarn(false); + } + + //get the source pane paramters. + Serializable newSourceParams = algoProvider.getSourceSettingsPane(null, currSource).getParams(); + + if (locAlgorithmParams==null) locAlgorithmParams = new LocaliserAlgorithmParams(); + locAlgorithmParams.setXtraSourceParameters(newSourceParams); + } + + + //now set the source paramters. + currParams.setAlgorithmParams(algoProvider, locAlgorithmParams); + + + //System.out.println("Get params" + algoProvider.getName() + " " + currParams.getAlgorithmParams(algoProvider)); + } +// if (algorithmSourcePane != null && currParams != null && algoProvider != null) { +// LocaliserAlgorithmParams locParams = currParams.getAlgorithmParams(algoProvider); +// if (algorithmSourcePane != null) { +// Object ans = algorithmSourcePane.getParams(); +// if (ans == null) { +// return null; +// } +//// else { +//// locParams.setAlgorithmParams(algoProvider, ans); +//// } +//// +//// if (ans instanceof LocaliserAlgorithmParams) { +//// currParams.setAlgorithmParams(algoProvider, (LocaliserAlgorithmParams) ans); +//// } +// } +////// LocaliserAlgorithmParams sourceParams = algorithmSourcePane.getParams(locParams); +//// if (sourceParams == null) { +////// SwingFXDialogWarning.showWarning(this.getOwnerWindow(), "Localisation algorithm", "Invalid al"); +//// return null; +//// } +//// currParams.setAlgorithmParams(algoProvider, sourceParams); +// } + + return currParams; } + + private LocaliserAlgorithm3D getSelectedAlgorithm() { String algoName = algorithms.getSelectionModel().getSelectedItem(); if (algoName == null) { @@ -317,6 +401,7 @@ public class GroupLocSettingPaneFX extends SettingsPane{ @Override public void setParams(Group3DParams input) { + sourcePanel.setSourceList(); sourcePanel.setSource(input.getSourceName()); // sourcePanel.setChannelList(input.getGroupedSourceParams().getChanOrSeqBitmap()); @@ -328,6 +413,7 @@ public class GroupLocSettingPaneFX extends SettingsPane{ // algorithms.getSelectionModel().select(input.getAlgorithmName()); currentParams = input; newDataBlockSelection(sourcePanel.getSource()); + } /** @@ -340,22 +426,32 @@ public class GroupLocSettingPaneFX extends SettingsPane{ PamDataBlock inputDataBlock = sourcePanel.getSource(); algorithms.getItems().clear(); ArrayList algoList = group3dLocaliserControl.getAlgorithmProviders(); + + int shape = 0; + + //Note that, if there is a lot of data in the hydrophone data block this next chunk can take a second or so to prcoess. + //Must keep it outside the for loop or there is a long freeze. + if (inputDataBlock != null) { + int phones = inputDataBlock.getHydrophoneMap(); + shape = ArrayManager.getArrayManager().getArrayType(phones); + } + for (int i = 0; i < algoList.size(); i++) { LocaliserAlgorithm3D algo = algoList.get(i); if (inputDataBlock != null && algo.canLocalise(inputDataBlock) == false) { continue; } + if (inputDataBlock != null) { - int phones = inputDataBlock.getHydrophoneMap(); - int shape = ArrayManager.getArrayManager().getArrayType(phones); - ArrayManager.getArrayManager().getArrayType(phones); if (algo.canArrayShape(shape) == false) { continue; } } + if (algo == currAlgo) { currentIndex = algorithms.getItems().size(); } + algorithms.getItems().add(algo.getName()); } if (currentIndex >= 0) { @@ -373,10 +469,41 @@ public class GroupLocSettingPaneFX extends SettingsPane{ if (algorithmSourcePane == null) { return; } + + PamDataBlock currSource = sourcePanel.getSource(); + + //note that we get the algorithms from the choice box here because these will already have + //been filtered for suitability depending on array geometry. + for (int i=0; i{ } setAlgorithmList(); setAlgorithmSourceParameters(); + grouperSettingsPane.newSourceGroup(((GroupedDataSource) pamDataBlock).getGroupSourceParameters()); } @Override diff --git a/src/group3dlocaliser/grouper/DetectionGrouper.java b/src/group3dlocaliser/grouper/DetectionGrouper.java index c5ce50aa..0774f3e2 100644 --- a/src/group3dlocaliser/grouper/DetectionGrouper.java +++ b/src/group3dlocaliser/grouper/DetectionGrouper.java @@ -242,7 +242,7 @@ public class DetectionGrouper { /** * Close the existing group and make a new one. */ - private synchronized void closeMotherGroup() { + public synchronized void closeMotherGroup() { processFirstGroup(motherGroup); motherGroup = new FirstGrouping(maxInterGroupSamples.length, 0, null); @@ -422,7 +422,7 @@ public class DetectionGrouper { } } if (nAccepted > 0) { -// System.out.printf("Accepted %d of %d data combinations\n", detectionGroupedSet.getNumGroups(), totalCombinations); + System.out.printf("Accepted %d of %d data combinations\n", detectionGroupedSet.getNumGroups(), totalCombinations); detectionGroupMonitor.newGroupedDataSet(detectionGroupedSet); } diff --git a/src/group3dlocaliser/grouper/DetectionGrouperParams.java b/src/group3dlocaliser/grouper/DetectionGrouperParams.java index d789b054..b3e40163 100644 --- a/src/group3dlocaliser/grouper/DetectionGrouperParams.java +++ b/src/group3dlocaliser/grouper/DetectionGrouperParams.java @@ -10,7 +10,14 @@ public class DetectionGrouperParams implements Serializable, Cloneable, ManagedP public static final long serialVersionUID = 1L; + /** + * Use all the possible combinations + */ public static final int GROUPS_ALL_POSSIBLE = 0; + + /** + * Use only the first combination + */ public static final int GROUPS_FIRST_ONLY = 1; public int maxPerSubGroup = 10; @@ -19,6 +26,13 @@ public class DetectionGrouperParams implements Serializable, Cloneable, ManagedP public int minSubGroups = 2; + /** + * The primary detection groups. The slected detection groups must be included in the combination for a loclaisation + * + * null means there is no primary group. + */ + public int[] primaryDetGroup = null; + public int groupingChoice = GROUPS_ALL_POSSIBLE; // data selection options diff --git a/src/group3dlocaliser/grouper/dialog/GrouperSettingsPane.java b/src/group3dlocaliser/grouper/dialog/GrouperSettingsPane.java index edeeb1ac..33bd36ce 100644 --- a/src/group3dlocaliser/grouper/dialog/GrouperSettingsPane.java +++ b/src/group3dlocaliser/grouper/dialog/GrouperSettingsPane.java @@ -1,6 +1,9 @@ package group3dlocaliser.grouper.dialog; + import PamController.SettingsPane; +import PamUtils.PamArrayUtils; +import PamView.GroupedSourceParameters; import PamguardMVC.PamDataBlock; import PamguardMVC.dataSelector.DataSelector; import group3dlocaliser.grouper.DetectionGrouperParams; @@ -14,8 +17,8 @@ import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javafx.scene.control.TextField; import javafx.scene.control.ToggleGroup; +import javafx.scene.control.Tooltip; import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; import pamViewFX.PamGuiManagerFX; import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; @@ -23,10 +26,13 @@ import pamViewFX.fxNodes.PamGridPane; import pamViewFX.fxNodes.PamLabel; import pamViewFX.fxNodes.PamTitledBorderPane; import pamViewFX.fxNodes.pamDialogFX.SwingFXDialogWarning; +import pamViewFX.fxNodes.utilityPanes.GroupedChannelBox; public class GrouperSettingsPane extends SettingsPane{ + private static final double LABELED_WIDTH = 40; + private PamBorderPane mainPane = new PamBorderPane(); private ChoiceBox groupOptions; @@ -40,36 +46,66 @@ public class GrouperSettingsPane extends SettingsPane{ private DataSelector currentDataSelector; private Button dataSelButton; + + private GroupedChannelBox checkGroupBox; + + private DetectionGrouperParams currentParams; public GrouperSettingsPane(Object ownerWindow, String borderTitle) { super(ownerWindow); // PamBorderPane setPane = new PamBorderPane(); groupOptions = new ChoiceBox(); + groupOptions.setMaxWidth(Double.MAX_VALUE); groupOptions.getItems().add("Return all possible combinations"); groupOptions.getItems().add("Return only the first combination"); // setPane.setTop(groupOptions); int x = 0, y = 0; PamGridPane gridPane = new PamGridPane(); + gridPane.setHgap(5); + gridPane.setVgap(5); + gridPane.add(new Label("Min Detections "), x, y); gridPane.add(minDets = new TextField("123"), ++x, y); + minDets.setPrefColumnCount(3); + gridPane.add(new Label("Max Detections "), x=0, ++y); gridPane.add(maxDets = new TextField("123"), ++x, y); - minDets.setPrefColumnCount(3); maxDets.setPrefColumnCount(3); + + minDets.setMaxWidth(LABELED_WIDTH); + maxDets.setMaxWidth(LABELED_WIDTH); + + gridPane.add(new Label("Requires channels "), x=0, ++y); + checkGroupBox = new GroupedChannelBox(); + checkGroupBox.setTooltip(new Tooltip("Select channels that are required to loclaise. \n" + + "No selected channels means any combination of channels can be used.")); + + PamGridPane.setColumnSpan(checkGroupBox, 2); + PamGridPane.setColumnSpan(minDets, 1); + PamGridPane.setColumnSpan(maxDets, 1); + + gridPane.add(checkGroupBox, ++x, y); + // dataSelButton = new Button("",PamGlyphDude.createPamGlyph(MaterialIcon.SETTINGS,Color.WHITE, PamGuiManagerFX.iconSize)); - dataSelButton = new Button("",PamGlyphDude.createPamIcon("mdi2c-cog",Color.WHITE, PamGuiManagerFX.iconSize)); + dataSelButton = new Button("",PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconSize)); + PamGridPane selPane = gridPane; //new PamGridPane(); - selPane.add(new PamLabel("Data selection ", Pos.CENTER_RIGHT), x=0, ++y); + selPane.add(new PamLabel("Data selection ", Pos.CENTER_LEFT), x=0, ++y); selPane.add(dataSelButton, ++x, +y); + dataSelButton.setMaxWidth(LABELED_WIDTH); selPane.add(requireAll = new RadioButton("Require all detections pass data selection"), x=0, ++y, 4, 1); selPane.add(requireSome = new RadioButton("Require minimum of "), x=0, ++y, 2, 1); x+=2; selPane.add(requiredN = new TextField(), x, y); selPane.add(new Label(" pass data selection"), ++x, y); requiredN.setPrefColumnCount(2); + + requireAll.setTooltip(new Tooltip("Select if all detections within a combination must pass the data selector")); + requiredN.setTooltip(new Tooltip("Select if at least N detections within a combination must pass the data selector")); + ToggleGroup group = new ToggleGroup(); requireAll.setToggleGroup(group); requireSome.setToggleGroup(group); @@ -93,7 +129,7 @@ public class GrouperSettingsPane extends SettingsPane{ }); VBox vBox = new VBox(groupOptions, gridPane); - + vBox.setSpacing(5); // setPane.setBottom(gridPane); if (borderTitle == null) { @@ -103,6 +139,8 @@ public class GrouperSettingsPane extends SettingsPane{ mainPane.setCenter(new PamTitledBorderPane(borderTitle, vBox)); } + enableControls(); + } protected void requireOptions() { @@ -126,7 +164,12 @@ public class GrouperSettingsPane extends SettingsPane{ boolean d = dataSelector == null; requireAll.setDisable(d); requireSome.setDisable(d); - requiredN.setDisable(d); + + + //need to disable if no data selector but do not enable unless requireSome selected + if (!d) enableControls(); + else requiredN.setDisable(d); + dataSelButton.setDisable(d); } @@ -165,12 +208,16 @@ public class GrouperSettingsPane extends SettingsPane{ } } + currParams.primaryDetGroup = getGroupParams(); + return currParams; } @Override public void setParams(DetectionGrouperParams params) { + groupOptions.getSelectionModel().select(params.groupingChoice); + minDets.setText(String.format("%d", params.minSubGroups)); maxDets.setText(String.format("%d", params.maxPerGroup)); @@ -178,8 +225,76 @@ public class GrouperSettingsPane extends SettingsPane{ requireSome.setSelected(params.dataSelectOption == DetectionGrouperParams.DATA_SELECT_MIN_N); requiredN.setText(String.format("%d", params.dataSelectMinimum)); + setGroupParams(params.primaryDetGroup); + enableControls(); } + + /** + * Set which primary groups are ticked. + * @param groups - the groups. + */ + private int[] getGroupParams(){ + + //the check box should have already had the correct source params set. + + int n = 0; + for (int i=0; i{ // TODO Auto-generated method stub } + + private void populateChannelBox(GroupedSourceParameters source) { + int[] selected = null; + + //Need to preserve the selection - check whether the groups are equal and if so set the selected back again + if (checkGroupBox.getGroupedParams()!=null && (PamArrayUtils.arrEquals(source.getChannelGroups(), checkGroupBox.getGroupedParams().getChannelGroups()))) { + selected = getGroupParams(); + } + + checkGroupBox.setSource(source); + + setGroupParams(selected); + + } + + /** + * Called whenever there is a new source group. + * @param source + */ + public void newSourceGroup(GroupedSourceParameters source) { + populateChannelBox(source); + } } diff --git a/src/group3dlocaliser/offline/Group3DOfflineTask.java b/src/group3dlocaliser/offline/Group3DOfflineTask.java index 000f022f..ab525a7b 100644 --- a/src/group3dlocaliser/offline/Group3DOfflineTask.java +++ b/src/group3dlocaliser/offline/Group3DOfflineTask.java @@ -2,6 +2,7 @@ package group3dlocaliser.offline; import java.util.ArrayList; +import Array.ArrayManager; import PamController.PamController; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -30,6 +31,12 @@ public class Group3DOfflineTask extends OfflineTask{ if (parentData != null) { this.addRequiredDataBlock(parentData); } + + //here be large bugs in all data and select data processing if not included + this.addRequiredDataBlock(ArrayManager.getArrayManager().getHydrophoneDataBlock()); + this.addRequiredDataBlock(ArrayManager.getArrayManager().getStreamerDatabBlock()); + this.addRequiredDataBlock(ArrayManager.getGPSDataBlock()); + } @Override diff --git a/src/localiserDisplay/LocaliserDisplayControlAWT.java b/src/localiserDisplay/LocaliserDisplayControlAWT.java index c60b7241..cd8c63b8 100644 --- a/src/localiserDisplay/LocaliserDisplayControlAWT.java +++ b/src/localiserDisplay/LocaliserDisplayControlAWT.java @@ -106,7 +106,7 @@ public class LocaliserDisplayControlAWT extends LoclaiserDisplayControl implemen Group root= new Group(); Scene scene = new Scene(root, Color.GRAY); scene.getStylesheets().clear(); - scene.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getGUICSS()); + scene.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getGUICSS()); localiser = new LocaliserDisplayFX(this); root.getChildren().add(localiser); diff --git a/src/matchedTemplateClassifer/ImportTemplateMAT.java b/src/matchedTemplateClassifer/ImportTemplateMAT.java index 6b905042..a1203b32 100644 --- a/src/matchedTemplateClassifer/ImportTemplateMAT.java +++ b/src/matchedTemplateClassifer/ImportTemplateMAT.java @@ -4,9 +4,11 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import com.jmatio.io.MatFileReader; -import com.jmatio.types.MLDouble; -import com.jmatio.types.MLStructure; +import PamUtils.PamArrayUtils; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.format.Mat5File; +import us.hebi.matlab.mat.types.Matrix; +import us.hebi.matlab.mat.types.Struct; /** @@ -22,7 +24,7 @@ public class ImportTemplateMAT implements TemplateImport { /** * The matlab file rader. */ - private MatFileReader mfr; + private Mat5File mfr; @Override public MatchTemplate importTemplate(File filePath){ @@ -31,7 +33,7 @@ public class ImportTemplateMAT implements TemplateImport { //System.out.println("Import MAT file waveform"); //the MATLAB file reader. - mfr = new MatFileReader(filePath); + mfr = Mat5.readFromFile(filePath); MatchTemplate matchTemplate; @@ -72,34 +74,33 @@ public class ImportTemplateMAT implements TemplateImport { * @param mfr - .mat file reader. * @return the match template. */ - private MatchTemplate getTemplateStruct(MatFileReader mfr2) { + private MatchTemplate getTemplateStruct(Mat5File mfr2) { //the MATLAB file reader. - MLStructure clicksStruct = (MLStructure) mfr.getMLArray("clicks"); - MLDouble sampleRateML = (MLDouble) mfr.getMLArray("clicks_sR"); + Struct clicksStruct = mfr.getStruct("clicks"); + Double sampleRateML = getDouble(mfr,"clicks_sR"); - - - if (clicksStruct==null) { - clicksStruct = (MLStructure) mfr.getMLArray("raw_data_units"); - sampleRateML = (MLDouble) mfr.getMLArray("raw_data_units_sR"); - } - - - +// if (clicksStruct==null) { +// clicksStruct = (MLStructure) mfr.getMLArray("raw_data_units"); +// sampleRateML = (MLDouble) mfr.getMLArray("raw_data_units_sR"); +// } if (sampleRateML==null || clicksStruct==null) { return null; } - MLDouble waveML = (MLDouble) clicksStruct.getField("wave", 0); - double[][] waveform = waveML.getArray(); - - - MatchTemplate matchedTemplate = new MatchTemplate(null, waveform[0], (float) sampleRateML.get(0).doubleValue()); + Matrix waveML = clicksStruct.get("wave", 0); + double[][] waveform = PamArrayUtils.matrix2array(waveML); + MatchTemplate matchedTemplate = new MatchTemplate(null, waveform[0], (float) sampleRateML.doubleValue()); return matchedTemplate; - + } + + + private Double getDouble(Mat5File mfr, String field) { + Matrix data = mfr.getMatrix(field); + if (data==null) return null; + return data.getDouble(0); } @@ -108,27 +109,25 @@ public class ImportTemplateMAT implements TemplateImport { * @param mfr - .mat file reader. * @return the match template. */ - private MatchTemplate getTemplateStandard(MatFileReader mfr) { + private MatchTemplate getTemplateStandard(Mat5File mfr) { //System.out.println("Import MAT file waveform"); - - //the MATLAB file reader. - MLDouble sampleRateML = (MLDouble) mfr.getMLArray("sR"); - if (sampleRateML==null) sampleRateML = (MLDouble) mfr.getMLArray("sr"); //try a different name for the sample rate - if (sampleRateML==null) sampleRateML = (MLDouble) mfr.getMLArray("fs"); //try a different name for the sample rate - if (sampleRateML==null) sampleRateML = (MLDouble) mfr.getMLArray("samplerate"); //try a different name for the sample rate - if (sampleRateML==null) sampleRateML = (MLDouble) mfr.getMLArray("sample_rate"); //try a different name for the sample rate - if (sampleRateML==null) sampleRateML = (MLDouble) mfr.getMLArray("sampleRate"); //try a different name for the sample rate - if (sampleRateML==null) sampleRateML = (MLDouble) mfr.getMLArray("clicks_sR"); //try a different name for the sample rate + Double sampleRateML = getDouble(mfr,"sR"); + if (sampleRateML==null) sampleRateML = getDouble(mfr,"sr"); //try a different name for the sample rate + if (sampleRateML==null) sampleRateML = getDouble(mfr,"fs"); //try a different name for the sample rate + if (sampleRateML==null) sampleRateML = getDouble(mfr,"samplerate");//try a different name for the sample rate + if (sampleRateML==null) sampleRateML = getDouble(mfr,"sample_rate"); //try a different name for the sample rate + if (sampleRateML==null) sampleRateML = getDouble(mfr,"sampleRate"); //try a different name for the sample rate + if (sampleRateML==null) sampleRateML = getDouble(mfr,"clicks_sR"); //try a different name for the sample rate //get the waveform or spectrum - MLDouble waveformML = (MLDouble) mfr.getMLArray("waveform"); + Matrix waveformML = mfr.getMatrix("waveform"); - if (waveformML==null) waveformML = (MLDouble) mfr.getMLArray("wave"); //try a different name for the waveform - if (waveformML==null) waveformML = (MLDouble) mfr.getMLArray("spectrum"); //might be a spectrum + if (waveformML==null) waveformML = mfr.getMatrix("wave"); //try a different name for the waveform + if (waveformML==null) waveformML = mfr.getMatrix("spectrum"); //might be a spectrum if (sampleRateML==null || waveformML==null) { @@ -136,7 +135,7 @@ public class ImportTemplateMAT implements TemplateImport { } //import a wave in column or row dimension - int size = Math.max(waveformML.getM(), waveformML.getN()); + int size = Math.max(waveformML.getNumRows(), waveformML.getNumCols()); double[] waveform = new double[size]; if (sizewaveformML.getN() ? waveformML.get(i, 0) : waveformML.get(0, i); + waveform[i]= waveformML.getNumRows()>waveformML.getNumCols() ? waveformML.getDouble(i, 0): waveformML.getDouble(0, i); } - float sR= Float.valueOf((float) sampleRateML.getArray()[0][0]); + float sR= (float) sampleRateML.doubleValue(); //now create waveform diff --git a/src/matchedTemplateClassifer/MTClassifier.java b/src/matchedTemplateClassifer/MTClassifier.java index 9065daac..d111c5c4 100644 --- a/src/matchedTemplateClassifer/MTClassifier.java +++ b/src/matchedTemplateClassifer/MTClassifier.java @@ -4,22 +4,21 @@ import java.io.Serializable; import java.lang.reflect.Field; import org.apache.commons.lang3.ArrayUtils; +import org.jamdev.jdl4pam.utils.DLMatFile; import org.jamdev.jpamutils.wavFiles.WavInterpolator; -import com.jmatio.types.MLArray; -import com.jmatio.types.MLDouble; -import com.jmatio.types.MLStructure; - import Filters.SmoothingFilter; import Localiser.DelayMeasurementParams; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; -import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamArrayUtils; import PamUtils.PamInterp; import PamUtils.complex.ComplexArray; import fftManager.FastFFT; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.types.Matrix; +import us.hebi.matlab.mat.types.Struct; /** * Parameters and useful functions for a single MT classifier. @@ -135,7 +134,6 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters //re-sample the waveform if the sample rate is different this.interpWaveformMatch=interpWaveform(this.waveformMatch, sR); - //System.out.println("interpWaveformMatch: " + interpWaveformMatch.length + " sR " + sR); //normalise //this.interpWaveformMatch=PamArrayUtils.normalise(interpWaveformMatch); @@ -143,8 +141,7 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters //this.inteprWaveformReject=PamArrayUtils.divide(interpWaveformMatch, PamArrayUtils.max(interpWaveformMatch)); this.interpWaveformMatch = normaliseWaveform(interpWaveformMatch, this.normalisation); -// System.out.println("MatchNorm: MATCH"); -// MTClassifierTest.normalizeTest(interpWaveformMatch); + //System.out.println("interpWaveformMatch: " + interpWaveformMatch.length + " sR " + sR + " max: " + PamArrayUtils.max(interpWaveformMatch)); /** * There is an issue here because, if we have a long template waveform, then it @@ -249,13 +246,15 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters * @return */ public static double[] normaliseWaveform(double[] waveform, int normeType) { +// System.out.println("Normalise waveform: " + normeType); double[] newWaveform = null; switch(normeType) { case MatchedTemplateParams.NORMALIZATION_NONE: newWaveform = waveform; break; case MatchedTemplateParams.NORMALIZATION_PEAK: - newWaveform =PamUtils.PamArrayUtils.divide(waveform, PamUtils.PamArrayUtils.max(waveform)); + //important to clone here or the template waveforms are normalised! + newWaveform =PamUtils.PamArrayUtils.divide(waveform.clone(), PamUtils.PamArrayUtils.max(waveform)); break; case MatchedTemplateParams.NORMALIZATION_RMS: newWaveform =PamUtils.PamArrayUtils.normalise(waveform); @@ -305,8 +304,8 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters * @param value - the value * @return */ - private MLDouble mlDouble(double value) { - return new MLDouble(null, new double[]{value}, 1); + private Matrix mlDouble(double value) { + return Mat5.newScalar(value); } @@ -322,10 +321,11 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters * @return an ML Structure with results of all stuff. */ - public MLStructure calcCorrelationMatchTest(ComplexArray click, float sR) { + public Struct calcCorrelationMatchTest(ComplexArray click, float sR) { - MLStructure mlStruct = new MLStructure("matchdata", new int[] {1,1}); - mlStruct.setField("waveform_fft", complexArray2MLArray(click)); +// Struct mlStruct = new MLStructure("matchdata", new int[] {1,1}); + Struct mlStruct = Mat5.newStruct(); + mlStruct.set("waveform_fft", complexArray2MLArray(click)); //set the stored sR @@ -354,14 +354,14 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters } //add data to struct her ebecause some arrays get overwritten - mlStruct.setField("match_template_waveform", new MLDouble(null, new double[][] {this.interpWaveformMatch})); - mlStruct.setField("reject_template_waveform", new MLDouble(null, new double[][] {this.inteprWaveformReject})); + mlStruct.set("match_template_waveform", DLMatFile.array2Matrix(this.interpWaveformMatch)); + mlStruct.set("reject_template_waveform", DLMatFile.array2Matrix(this.inteprWaveformReject)); //add data to struct her ebecause some arrays get overwritten - mlStruct.setField("match_template_fft", complexArray2MLArray(matchTemplate)); - mlStruct.setField("reject_template_fft", complexArray2MLArray(rejectTemplate)); - mlStruct.setField("match_result_corr", complexArray2MLArray(matchResult)); - mlStruct.setField("reject_result_corr", complexArray2MLArray(rejectResult)); + mlStruct.set("match_template_fft", complexArray2MLArray(matchTemplate)); + mlStruct.set("reject_template_fft", complexArray2MLArray(rejectTemplate)); + mlStruct.set("match_result_corr", complexArray2MLArray(matchResult)); + mlStruct.set("reject_result_corr", complexArray2MLArray(rejectResult)); //must use scaling to get the same result as MATLAB if (fft==null) fft= new FastFFT(); @@ -380,10 +380,10 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters double result = 2*(PamArrayUtils.max(matchReal)-PamArrayUtils.max(rejectReal)); - mlStruct.setField("match_result_ifft", complexArray2MLArray(matchResult)); - mlStruct.setField("reject_result_ifft", complexArray2MLArray(rejectResult)); - mlStruct.setField("result", mlDouble(result)); - mlStruct.setField("sR", mlDouble(sR)); + mlStruct.set("match_result_ifft", complexArray2MLArray(matchResult)); + mlStruct.set("reject_result_ifft", complexArray2MLArray(rejectResult)); + mlStruct.set("result", mlDouble(result)); + mlStruct.set("sR", mlDouble(sR)); return mlStruct; @@ -395,13 +395,14 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters * @param complexArray the complex array to export. * @return the ML array. */ - private MLDouble complexArray2MLArray(ComplexArray complexArray) { - MLDouble matchTemplateML = new MLDouble(null, new int[]{complexArray.length(),1}, MLArray.mxDOUBLE_CLASS, MLArray.mtFLAG_COMPLEX ); + private Matrix complexArray2MLArray(ComplexArray complexArray) { + + Matrix matchTemplateML = Mat5.newComplex(complexArray.length(),1); +// MLDouble matchTemplateML = new MLDouble(null, new int[]{complexArray.length(),1}, MLArray.mxDOUBLE_CLASS, MLArray.mtFLAG_COMPLEX ); for (int i=0; i templates) { + private static Struct testClassifier(double[] testWaveform, float sR, ArrayList templates) { //create the classifier object MTClassifier mtclassifier = new MTClassifier(); @@ -63,11 +66,11 @@ public class MTClassifierTest { System.out.println("Click waveform length: " + testWaveform.length + " FFT bins: " + complexArray.length() + " FFT Length: " + fftSize); - MLStructure struct = mtclassifier.calcCorrelationMatchTest(complexArray, sR); + Struct struct = mtclassifier.calcCorrelationMatchTest(complexArray, sR); - struct.setField("waveform", new MLDouble(null, new double[][] {testWaveform})); - - System.out.println("Match: " + ((MLDouble) struct.getField("result")).get(0)); + struct.set("waveform", DLMatFile.array2Matrix(testWaveform)); + + System.out.println("Match: " + struct.getMatrix("result").getDouble(0)); return struct; } @@ -78,8 +81,8 @@ public class MTClassifierTest { * @param sR - the sample rate of the waveform. * @param templates - the match templates to test. */ - private static void testCorrelation(double[] testWaveform, float sR, ArrayList templates) { - testCorrelation(testWaveform, sR, templates, MatchedTemplateParams.NORMALIZATION_RMS); + public static List testCorrelation(double[] testWaveform, float sR, ArrayList templates) { + return testCorrelation(testWaveform, sR, templates, MatchedTemplateParams.NORMALIZATION_RMS); } @@ -91,13 +94,16 @@ public class MTClassifierTest { * @param sR - the sample rate of the waveform. * @param templates - the match templates to test. * @param normalisation - the normalisation type to use e.g. MatchedTemplateParams.NORMALIZATION_RMS + * @return a list of the correlation results. */ - private static void testCorrelation(double[] testWaveform, float sR, ArrayList templates, int normalisation) { + public static List testCorrelation(double[] testWaveform, float sR, ArrayList templates, int normalisation) { + + List matchedTemplateResult = new ArrayList(); //create the classifier object for (int i=0; i importClicks(String filePath, float sR) { - MatFileReader mfr; try { - mfr = new MatFileReader(filePath); - // //get array of a name "my_array" from file - MLStructure mlArrayRetrived = (MLStructure) mfr.getMLArray( "clicks" ); + Mat5File mfr = Mat5.readFromFile(filePath); - int numClicks= mlArrayRetrived.getN(); + // //get array of a name "my_array" from file + Struct mlArrayRetrived = mfr.getStruct( "clicks" ); + + int numClicks= mlArrayRetrived.getNumCols(); ArrayList clicks = new ArrayList(numClicks); for (int i=0; i importTemplates(String filePath) { - MatFileReader mfr; + Mat5File mfr; try { - mfr = new MatFileReader(filePath); + mfr = Mat5.readFromFile(filePath); // //get array of a name "my_array" from file - MLStructure mlArrayMatch = (MLStructure) mfr.getMLArray( "templates" ); - //System.out.println(mlArrayMatch.getType()); + Struct mlArrayMatch = mfr.getStruct( "templates" ); +// System.out.println(mlArrayMatch.getType() + " " + mlArrayMatch.getNumElements()); ArrayList templates = new ArrayList(); - int numTemplates= mlArrayMatch.getN(); + int numTemplates= mlArrayMatch.getNumElements(); for (int i=0; i importTemplate(String filePath) { - MatFileReader mfr; + Mat5File mfr; try { - mfr = new MatFileReader(filePath); + mfr = Mat5.readFromFile(filePath); // //get array of a name "my_array" from file - MLDouble mlArrayMatch = (MLDouble) mfr.getMLArray( "matchtemplate" ); - double[][] mlMatch= mlArrayMatch.getArray(); - MLDouble mlArrayReject = (MLDouble) mfr.getMLArray( "rejectemplate" ); - double[][] mlReject= mlArrayReject.getArray(); + Matrix mlArrayMatch = mfr.getArray( "matchtemplate" ); + double[][] mlMatch= PamArrayUtils.matrix2array(mlArrayMatch); + Matrix mlArrayReject = mfr.getArray( "rejectemplate" ); + double[][] mlReject= PamArrayUtils.matrix2array(mlArrayReject); ArrayList templates = new ArrayList(2); @@ -314,18 +322,19 @@ public class MTClassifierTest { System.out.println(""); System.out.println("Testing the classifier"); - ArrayList mlResults = new ArrayList(); - MLStructure strcut; + Struct mlResults = Mat5.newStruct(); + Struct strcut; int N = 3; // templates.remove(1); // templates.add(clicks.get(0)); for (int i=0; i templates = importTemplates(templteFilePath); @@ -382,7 +391,8 @@ public class MTClassifierTest { public static void main(String args[]) { - testMatchCorrLen(); +// testMatchCorrLen(); + testMatchCorr(); } diff --git a/src/matchedTemplateClassifer/MTProcess.java b/src/matchedTemplateClassifer/MTProcess.java index 92e86a2f..1e9c305f 100644 --- a/src/matchedTemplateClassifer/MTProcess.java +++ b/src/matchedTemplateClassifer/MTProcess.java @@ -421,7 +421,7 @@ public class MTProcess extends PamInstantProcess { ArrayList results = new ArrayList(); - System.out.println("Click waveform max: " + PamArrayUtils.max(clickWaveform) + " sample rate: " + sR); +// System.out.println("Click waveform max: " + PamArrayUtils.max(clickWaveform) + " sample rate: " + sR); //normalisation and picking peak has already been performed diff --git a/src/matchedTemplateClassifer/matched_click_classifer_help.md b/src/matchedTemplateClassifer/matched_click_classifer_help.md index d28e532b..608aed2a 100644 --- a/src/matchedTemplateClassifer/matched_click_classifer_help.md +++ b/src/matchedTemplateClassifer/matched_click_classifer_help.md @@ -14,7 +14,7 @@ The classifier is based on a matched filter i.e. a candidate click detection is The matched click classifier settings are accessed via **Settings-> Matched click classifier**_. The settings are split into the three sections, general settings, click waveform and click templates.

- +

_The settings pane of the matched click classifier_ @@ -38,9 +38,9 @@ _Restrict parameter extraction to XX samples_ sets the maximum length of the wav _Peak threshold_ and _Smoothing_ are both parameters used to find the click center so that a click can be trimmed to the maximum number of samples. The click length is measured by calculating the waveform envelope using a Hilbert Transform. The envelope is smoothed using a moving average filter (the _Smoothing_ parameter defines the size of the averaging window). The click is trimmed as follows. First the peak of the waveform envelope is found. The length of the click is defined as the point at which the click falls _Peak threshold_ dB below the peak. The center of the click is then the middle of this snippet. The click is then trimmed from the center of the click. _Amplitude normalisation_ If there is a very loud click compared to a template it's correlation score will be different to that of a very quiet click of with exactly the same waveform. It is therefore a good idea to normalise the waveform before it is compared the match click classifier. The types of normalisation are -* _norm_ - this is the default - the click is divided by it's RMS amplitude. +* _norm_ - this is the default - the click is divided by it's RMS amplitude. A waveform correalted with itself will return 1 as the maximum correlation value. * _peak to peak_ this can be useful for some types of shorter click e.g. dolphins - the click is divided by it's peak to peak amplitude. -* _none_ no normalisation (not recommended). +* _none_ no normalisation (not recommended). A waveform correlated with itself will return a value dependent on the amplitude and frequency content of the waveform. ### Template settings diff --git a/src/noiseBandMonitor/NoiseBandControl.java b/src/noiseBandMonitor/NoiseBandControl.java index 7776b4e5..c9b0242d 100644 --- a/src/noiseBandMonitor/NoiseBandControl.java +++ b/src/noiseBandMonitor/NoiseBandControl.java @@ -236,6 +236,9 @@ public class NoiseBandControl extends PamControlledUnit implements PamSettings { @Override public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { noiseBandSettings = ((NoiseBandSettings)pamControlledUnitSettings.getSettings()).clone(); + System.out.println("********************************************************"); + System.out.println("NOISE BAND SETTINGS: " + noiseBandSettings.rawDataSource); + System.out.println("********************************************************"); return true; } diff --git a/src/noiseBandMonitor/NoiseBandDialog.java b/src/noiseBandMonitor/NoiseBandDialog.java index 65dfd940..fef945ce 100644 --- a/src/noiseBandMonitor/NoiseBandDialog.java +++ b/src/noiseBandMonitor/NoiseBandDialog.java @@ -231,6 +231,9 @@ public class NoiseBandDialog extends PamDialog { singleInstance = new NoiseBandDialog(parentFrame, noiseBandControl); } singleInstance.setupComplete = false; +// System.out.println("********************************************************"); +// System.out.println("NOISE BAND DIALOG: " + singleInstance.noiseBandSettings.rawDataSource); +// System.out.println("********************************************************"); singleInstance.noiseBandSettings = noiseBandControl.noiseBandSettings.clone(); singleInstance.setParams(); singleInstance.setVisible(true); @@ -287,7 +290,7 @@ public class NoiseBandDialog extends PamDialog { if (dataBlock == null) { return showWarning("You must select a source of raw audio data"); } - noiseBandSettings.rawDataSource = dataBlock.getDataName(); + noiseBandSettings.rawDataSource = dataBlock.getLongDataName(); noiseBandSettings.channelMap = sourcePanel.getChannelList(); if (noiseBandSettings.channelMap == 0) { return showWarning("You must select at least one data channel"); diff --git a/src/noiseBandMonitor/NoiseBandProcess.java b/src/noiseBandMonitor/NoiseBandProcess.java index 744f71b1..0da99e14 100644 --- a/src/noiseBandMonitor/NoiseBandProcess.java +++ b/src/noiseBandMonitor/NoiseBandProcess.java @@ -63,6 +63,10 @@ public class NoiseBandProcess extends PamProcess { public void setupProcess() { super.setupProcess(); PamDataBlock sourceData = noiseBandControl.getPamConfiguration().getDataBlock(RawDataUnit.class, noiseBandControl.noiseBandSettings.rawDataSource); + +// System.out.println("********************************************************"); +// System.out.println("NOISE BAND PROCESS: " + sourceData + " " + noiseBandControl.noiseBandSettings.rawDataSource); +// System.out.println("********************************************************"); if (sourceData == null) { return; } diff --git a/src/offlineProcessing/OLProcessDialog.java b/src/offlineProcessing/OLProcessDialog.java index 9302b10b..bc94e2e1 100644 --- a/src/offlineProcessing/OLProcessDialog.java +++ b/src/offlineProcessing/OLProcessDialog.java @@ -1,6 +1,7 @@ package offlineProcessing; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; @@ -24,6 +25,9 @@ import javax.swing.SwingConstants; import javax.swing.WindowConstants; import javax.swing.border.TitledBorder; +import org.kordamp.ikonli.materialdesign2.MaterialDesignC; +import org.kordamp.ikonli.swing.FontIcon; + import PamUtils.PamCalendar; import PamUtils.TxtFileUtils; import PamView.CancelObserver; @@ -35,6 +39,7 @@ import PamView.panel.PamAlignmentPanel; import PamView.panel.PamPanel; import PamView.panel.PamProgressBar; import PamguardMVC.PamDataBlock; +import export.layoutFX.ExportParams; import offlineProcessing.logging.OldTaskData; import offlineProcessing.logging.TaskLogging; import offlineProcessing.superdet.OfflineSuperDetFilter; @@ -89,17 +94,24 @@ public class OLProcessDialog extends PamDialog { private PamPanel timeChunkDataPanel; - public static ImageIcon settings = new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png")); +// public static ImageIcon settings = new ImageIcon(ClassLoader.getSystemResource("Resources/SettingsButtonSmall2.png")); + public static FontIcon settings = FontIcon.of(MaterialDesignC.COG, 20, Color.DARK_GRAY); + TaskStatus currentStatus = TaskStatus.IDLE; + /** + * Reference to the main panel + */ + private JPanel mainPanel; + public OLProcessDialog(Window parentFrame, OfflineTaskGroup taskGroup, String title) { super(parentFrame, title, false); this.taskGroup = taskGroup; taskGroup.setTaskMonitor(new OLMonitor()); - JPanel mainPanel = new JPanel(); + mainPanel = new JPanel(); mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); JPanel dataSelectPanel = new PamAlignmentPanel(BorderLayout.WEST); @@ -207,6 +219,14 @@ public class OLProcessDialog extends PamDialog { setResizable(true); } + + /** + * Get the main panel. This can be used to add additional controls if needed. + * @return the main panel. + */ + public JPanel getMainPanel() { + return mainPanel; + } /** * Get the delete old data check box @@ -823,4 +843,5 @@ public class OLProcessDialog extends PamDialog { return this.taskGroup; } + } diff --git a/src/offlineProcessing/OfflineTaskGroup.java b/src/offlineProcessing/OfflineTaskGroup.java index e99fc61b..c437f3bb 100644 --- a/src/offlineProcessing/OfflineTaskGroup.java +++ b/src/offlineProcessing/OfflineTaskGroup.java @@ -66,6 +66,7 @@ public class OfflineTaskGroup implements PamSettings { private DataTimeLimits dataTimeLimits; private volatile TaskStatus completionStatus = TaskStatus.IDLE; + /** * PamControlledunit required in constructor since some bookkeeping will * be going on in the background which will need the unit type and name. @@ -76,7 +77,7 @@ public class OfflineTaskGroup implements PamSettings { public OfflineTaskGroup(PamControlledUnit pamControlledUnit, String settingsName) { super(); this.pamControlledUnit = pamControlledUnit; - pamControlledUnit.addOfflineTaskGroup(this); + if (pamControlledUnit!=null) pamControlledUnit.addOfflineTaskGroup(this); this.settingsName = settingsName; PamSettingManager.getInstance().registerSettings(this); } @@ -469,6 +470,9 @@ public class OfflineTaskGroup implements PamSettings { OfflineDataMap dataMap = primaryDataBlock.getPrimaryDataMap(); int nMapPoints = dataMap.getNumMapPoints(startTime, endTime); int iMapPoint = 0; + + System.out.println("N MAP POINTS: "+ nMapPoints + " dataMap: " + dataMap.getParentDataBlock() + " start: " + PamCalendar.formatDateTime(startTime) + " " +PamCalendar.formatDateTime(endTime)); + publish(new TaskMonitorData(TaskStatus.RUNNING, TaskActivity.PROCESSING, nMapPoints, 0, "", taskGroupParams.startRedoDataTime)); OfflineDataStore dataSource = dataMap.getOfflineDataSource(); @@ -962,4 +966,14 @@ public class OfflineTaskGroup implements PamSettings { public OfflineSuperDetFilter getSuperDetectionFilter() { return superDetectionFilter; } + + /** + * Clear all task from the task group. This also clears affected and required datablocks. + */ + public void clearTasks() { + requiredDataBlocks.clear(); + affectedDataBlocks.clear(); + offlineTasks.clear(); + + } } diff --git a/src/pamMaths/PamVector.java b/src/pamMaths/PamVector.java index d491f01e..cbea40c9 100644 --- a/src/pamMaths/PamVector.java +++ b/src/pamMaths/PamVector.java @@ -384,6 +384,7 @@ public class PamVector implements Serializable, Cloneable, PamCoordinate, Manage public double norm(int nDim) { return Math.sqrt(normSquared(nDim)); } + /** * * @param vec a PamVector diff --git a/src/pamScrollSystem/LoadQueueProgressData.java b/src/pamScrollSystem/LoadQueueProgressData.java index bebb4a9a..949c4cec 100644 --- a/src/pamScrollSystem/LoadQueueProgressData.java +++ b/src/pamScrollSystem/LoadQueueProgressData.java @@ -34,11 +34,13 @@ public class LoadQueueProgressData extends PamTaskUpdate { this.loadEnd = loadEnd; this.loadCurrent = loadCurrent; this.nLoaded = nLoaded; + setDualProgressUpdate(true); } public LoadQueueProgressData(int status) { super(); this.setStatus(PamTaskUpdate.STATUS_DONE); + setDualProgressUpdate(true); } @@ -119,6 +121,7 @@ public class LoadQueueProgressData extends PamTaskUpdate { @Override public double getProgress() { +// System.out.println( getStreamName() + " progress: " + iStream + " of " + totalStreams); if (getStreamName() != null) { return (double )iStream/ (double) totalStreams; } @@ -127,6 +130,7 @@ public class LoadQueueProgressData extends PamTaskUpdate { @Override public String getProgressString() { + if (getState() == LoadQueueProgressData.STATE_LINKINGSUBTABLE) { return String.format("Linking subtable data %d of %d", getIStream(), getTotalStreams()); @@ -141,13 +145,15 @@ public class LoadQueueProgressData extends PamTaskUpdate { } @Override - public double getProgress2() { + public double getProgress2() { +// System.out.println( getStreamName() + " progress2: " + getLoadStart() ); long interval =getLoadEnd() - getLoadStart(); - if (interval == 0) { + if (interval <= 0) { return ProgressIndicator.INDETERMINATE_PROGRESS; } else { long done =getLoadCurrent() - getLoadStart(); +// System.out.println( getStreamName() + " progress2: " + done + " " + interval + " start " + getLoadStart() + " end " + getLoadEnd()) return ((double) done )/ interval; } } diff --git a/src/pamViewFX/DataLoadingPane.java b/src/pamViewFX/DataLoadingPane.java index 30b15781..4a9b3ef7 100644 --- a/src/pamViewFX/DataLoadingPane.java +++ b/src/pamViewFX/DataLoadingPane.java @@ -57,12 +57,16 @@ public class DataLoadingPane extends PamBorderPane { PamVBox holderPane=new PamVBox(); holderPane.setSpacing(5); + holderPane.getChildren().add(allProgress); + holderPane.getChildren().add(streamProgress); + return holderPane; } public void setData(LoadQueueProgressData progressData) { + streamProgress.setProgress(-1); if (progressData.getStreamName() != null) { allProgress.setProgress(progressData.getTotalStreams()/progressData.getIStream()); // storeName.setText(progressData.getStoreType()); diff --git a/src/pamViewFX/PAMGuiFXSettings.java b/src/pamViewFX/PAMGuiFXSettings.java index 8a5af7a9..fed74564 100644 --- a/src/pamViewFX/PAMGuiFXSettings.java +++ b/src/pamViewFX/PAMGuiFXSettings.java @@ -36,7 +36,7 @@ public class PAMGuiFXSettings implements Serializable, Cloneable { /** * True if PG is in fullscreen on startup */ - public boolean fullscreen = true; + public boolean fullscreen = false; /* (non-Javadoc) diff --git a/src/pamViewFX/PamGuiFX.java b/src/pamViewFX/PamGuiFX.java index f6e87e84..80ee6983 100644 --- a/src/pamViewFX/PamGuiFX.java +++ b/src/pamViewFX/PamGuiFX.java @@ -174,7 +174,7 @@ public class PamGuiFX extends StackPane implements PamViewInterface { // showButtonRight.setGraphic(PamGlyphDude.createPamGlyph(FontAwesomeIcon.CHEVRON_LEFT, PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize)); showButtonRight.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-left", PamGuiManagerFX.iconColor, PamGuiManagerFX.iconSize)); //showLeftButton.setText(PamFontAwesome.ICON_CHEVRON_LEFT); - showButtonRight.getStyleClass().add("close-button-left"); + showButtonRight.getStyleClass().add("close-button-left-trans"); showButtonRight.setStyle("-fx-background-radius: 0 0 0 0;"); //alter animations to remove/add showButton to tab pane. @@ -199,8 +199,8 @@ public class PamGuiFX extends StackPane implements PamViewInterface { }); PamButton closeButtonLeft=hidingSidePane.getHideButton(); + closeButtonLeft.getStyleClass().add("close-button-right-trans"); // closeButtonLeft.setGraphic(PamGlyphDude.createPamGlyph(FontAwesomeIcon.CHEVRON_RIGHT, Color.DARKGRAY.darker(), PamGuiManagerFX.iconSize)); - closeButtonLeft.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-right", Color.DARKGRAY.darker(), PamGuiManagerFX.iconSize)); closeButtonLeft.prefHeightProperty().bind(mainTabPane.getHeaderHeightProperty()); //add hiding pane to main pane. @@ -212,12 +212,12 @@ public class PamGuiFX extends StackPane implements PamViewInterface { hidingPaneLeft=new HidingPane(Side.LEFT, settingsPane, this, false); hidingPaneLeft.showHidePane(false); - hidingPaneLeft.getStylesheets().add(pamGuiManagerFX.getPamSettingsCSS()); + hidingPaneLeft.getStylesheets().addAll(pamGuiManagerFX.getPamSettingsCSS()); PamButton showButtonLeft=hidingPaneLeft.getShowButton(); // showButtonLeft.setGraphic(PamGlyphDude.createPamGlyph(FontAwesomeIcon.BARS, Color.LIGHTGRAY, PamGuiManagerFX.iconSize)); showButtonLeft.setGraphic(PamGlyphDude.createPamIcon("mdi2m-menu", Color.LIGHTGRAY, PamGuiManagerFX.iconSize)); - showButtonLeft.getStyleClass().add("close-button-right"); + showButtonLeft.getStyleClass().add("close-button-right-trans"); showButtonLeft.setStyle(" -fx-background-radius: 0 0 0 0;"); PamButton closeRightButton=hidingPaneLeft.getHideButton(); @@ -244,20 +244,20 @@ public class PamGuiFX extends StackPane implements PamViewInterface { mainTabPane.setTabStartRegion(showButtonLeft); mainTabPane.getAddTabButton().setOnAction((value)->{ - addPamTab(new TabInfo("Display " + this.getNumTabs()+1), null ,true); + addPamTab(new TabInfo("Display " + (this.getNumTabs()+1)), null ,true); mainTabPane.layout(); }); //now have a holder - add the loading pane. /**create left hiding pane**/ loadPane=new PamLoadingPane(this.pamGuiManagerFX); - loadPane.setPrefWidth(250); - hidingLoadPane=new HidingPane(Side.LEFT, loadPane, this, false); + hidingLoadPane=new HidingPane(Side.TOP, loadPane, this, false); + hidingLoadPane.setPrefHeight(110); hidingLoadPane.removeHideButton(); hidingLoadPane.showHidePane(false); PamBorderPane layoutHolder=new PamBorderPane(layout); - layoutHolder.setLeft(hidingLoadPane); + layoutHolder.setTop(hidingLoadPane); return layoutHolder; @@ -306,8 +306,23 @@ public class PamGuiFX extends StackPane implements PamViewInterface { } } - newTab.setDetachable(detachable); + newTab.setOnClosed((action)->{ + //when a tab is closer. + for (int i=0; i allTabs = getAllTabs(); for (int i=0; i settingsDialog=new PamSettingsDialogFX(tabSelectionPane); - settingsDialog.getDialogPane().getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); + settingsDialog.getDialogPane().getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); settingsDialog.initStyle(StageStyle.UNDECORATED); // ChoiceDialog dialog = new ChoiceDialog<>(tabStrings.get(1), tabStrings); @@ -492,7 +507,7 @@ public class PamGuiManagerFX implements PAMControllerGUI, PamSettings { * Get CSS for PAMGUARD setting 'look and feel' for sliding dialogs * @return the CSS for settings feels. */ - public String getPamSettingsCSS() {//return new PrimerDark().getUserAgentStylesheet(); + public ArrayList getPamSettingsCSS() {//return new PrimerDark().getUserAgentStylesheet(); // return getClass().getResource("/Resources/css/pamSettingsCSS.css").toExternalForm(); return PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS(); } @@ -501,7 +516,7 @@ public class PamGuiManagerFX implements PAMControllerGUI, PamSettings { * Get CSS for PAMGUARD GUI standard 'look and feel' * @return the standard CSS fro PAMGUARD. */ - public String getPamCSS() { + public ArrayList getPamCSS() { //return new PrimerLight().getUserAgentStylesheet(); // return getClass().getResource("/Resources/css/pamCSS.css").toExternalForm(); return PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getGUICSS(); @@ -511,7 +526,7 @@ public class PamGuiManagerFX implements PAMControllerGUI, PamSettings { * Get CSS for PAMGUARD GUI standard 'look and feel' for regular dialogs * @return the standard CSS fro PAMGUARD. */ - public String getPamDialogCSS() {//return new PrimerDark().getUserAgentStylesheet(); + public ArrayList getPamDialogCSS() {//return new PrimerDark().getUserAgentStylesheet(); return PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS(); } @@ -577,7 +592,6 @@ public class PamGuiManagerFX implements PAMControllerGUI, PamSettings { public Stage getMainScene() { - // TODO Auto-generated method stub return primaryStage; } @@ -653,7 +667,7 @@ public class PamGuiManagerFX implements PAMControllerGUI, PamSettings { mainPane.getChildren().addAll(title, namePane); dialog.getDialogPane().setContent(mainPane); - dialog.getDialogPane().getStylesheets().add(this.getPamDialogCSS()); + dialog.getDialogPane().getStylesheets().addAll(this.getPamDialogCSS()); //add listener to prevent close request if the dialog final Button btOk = (Button) dialog.getDialogPane().lookupButton(ButtonType.OK); @@ -1022,6 +1036,10 @@ public class PamGuiManagerFX implements PAMControllerGUI, PamSettings { } + public Window getPrimaryStage() { + return this.primaryStage; + } + } diff --git a/src/pamViewFX/PamGuiTabFX.java b/src/pamViewFX/PamGuiTabFX.java index cf03e5d2..c458f21e 100644 --- a/src/pamViewFX/PamGuiTabFX.java +++ b/src/pamViewFX/PamGuiTabFX.java @@ -40,7 +40,7 @@ public class PamGuiTabFX extends PamTabFX { /** * List of internal panes within the tab content pane. */ - ArrayList internalPanes=new ArrayList(); + private ArrayList internalPanes=new ArrayList(); /** * True if panes are editable. @@ -87,34 +87,25 @@ public class PamGuiTabFX extends PamTabFX { /** * Constructor for a new tab - * @param text. Name of tab + * @param tabInfo - info on the tab such as name + * @param pamGui - reference to the PamGuiFX the pane belongs to. */ public PamGuiTabFX(TabInfo tabInfo, PamGuiFX pamGui) { - super(tabInfo.tabName); - this.pamGui=pamGui; - this.tabInfo=tabInfo; - - contentHolder=new PamBorderPane(); - - //needs to be in this order so toolbar pane sits on top of center pane. - holder=new Pane(); - contentHolder.setCenter(holder); - - super.setContent(contentHolder); - setPanesEditable(editable); + this(tabInfo, null, pamGui); } /** * Constructor for a new tab - * @param text - Name of tab - * @param toolbar - pane which sits just below tab - * @param content - content for tab pane. + * @param tabInfo - info on the tab such as name + * @param newContent - content to display + * @param pamGui - reference to the PamGuiFX the pane belongs to. */ public PamGuiTabFX(TabInfo tabInfo, UserDisplayNodeFX newContent, PamGuiFX pamGui) { super(tabInfo.tabName); this.pamGui=pamGui; this.tabInfo=tabInfo; + contentHolder=new PamBorderPane(); //needs to be in this order so toolbar pane sits on top of center pane. @@ -165,7 +156,7 @@ public class PamGuiTabFX extends PamTabFX { public Pane createNewPane(Tab tab, PamTabPane tabPane, Stage newStage){ //create a new GUI frame. PamGuiFX pamGUIFX=new PamGuiFX(tabPane, newStage, pamGui.getPamGuiManagerFX()); - pamGUIFX.getStylesheets().add(pamGui.getPamGuiManagerFX().getPamCSS()); + pamGUIFX.getStylesheets().addAll(pamGui.getPamGuiManagerFX().getPamCSS()); //need to add PamGUIFX to list in PamGUIManagerFX. @@ -210,7 +201,17 @@ public class PamGuiTabFX extends PamTabFX { * @return the internal pane which has been added */ public PamGuiInternalPane addInternalPane(UserDisplayNodeFX userDisplayNodeFX){ + System.out.println("UserDisplayNodeFX: " + userDisplayNodeFX); if (userDisplayNodeFX==null || userDisplayNodeFX.getNode()==null) return null; + + for (PamGuiInternalPane internalPane: this.internalPanes) { + if (userDisplayNodeFX == internalPane.getUserDisplayNode()) { + System.err.println("UserDisplayNodeFX is laready in pane"); + return null; + } + } + + PamGuiInternalPane newInternalPane=new PamGuiInternalPane(userDisplayNodeFX, holder); if (!userDisplayNodeFX.isResizeableDisplay()) newInternalPane.showResizeControls(false); holder.getChildren().add(newInternalPane); @@ -404,7 +405,9 @@ public class PamGuiTabFX extends PamTabFX { * @return name of tabs. */ public String getName() { - return this.getText(); + //note that this should not be getText() because + //this is not set in the parent class constructor. + return this.tabInfo.tabName; } /** @@ -413,7 +416,7 @@ public class PamGuiTabFX extends PamTabFX { * @author Jamie Macaulay * */ - private class PamGuiInternalPane extends PamInternalPane { + class PamGuiInternalPane extends PamInternalPane { private UserDisplayNodeFX mainPane; diff --git a/src/pamViewFX/PamSettingsMenuPane.java b/src/pamViewFX/PamSettingsMenuPane.java index e1cfc2bd..545c8b00 100644 --- a/src/pamViewFX/PamSettingsMenuPane.java +++ b/src/pamViewFX/PamSettingsMenuPane.java @@ -13,24 +13,36 @@ import PamController.PamSettingManager; import PamController.SettingsPane; import PamController.StorageOptions; import PamController.StorageParameters; +import PamController.soundMedium.GlobalMedium; +import PamController.soundMedium.GlobalMedium.SoundMedium; import PamModel.PamModuleInfo; +import PamView.dialog.warn.WarnOnce; import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; import pamViewFX.fxNodes.pamDialogFX.PamSettingsDialogFX; import pamViewFX.fxNodes.utilityPanes.SettingsDialog; import pamViewFX.fxSettingsPanes.StorageOptionsPane; +import pamViewFX.fxStyles.PamStylesManagerFX; import javafx.geometry.Insets; import javafx.geometry.Pos; +import javafx.geometry.Side; import javafx.scene.control.Label; import javafx.scene.control.Labeled; import javafx.scene.control.MenuButton; import javafx.scene.control.MenuItem; +import javafx.scene.control.Separator; import javafx.scene.text.TextAlignment; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.Priority; /** * Pane which holds settings menu. All primary settings are accessed from this pane which sits in a hiding pane to the right * of the main GUI. + * * @author Jamie Macaulay * */ @@ -41,6 +53,12 @@ public class PamSettingsMenuPane extends PamVBox { StorageOptionsPane storageOptionPane; + public String warning = " Changing to between air and water requires a PAMGuard restart\n" + + " for some display changes to take effect. Settings such as\n" + + " sound speed, reciever sensitivity values and data unit amplitudes\n" + + " will be recalculated or set to new default values.\n" + + "

Data processing changes are ready to use immediately."; + public PamSettingsMenuPane(){ this.setSpacing(0); @@ -48,12 +66,14 @@ public class PamSettingsMenuPane extends PamVBox { this.setPrefWidth(250); this.setStyle("-fx-background-color: -fx-darkbackground"); + + this.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); Label settingsLabel=new Label("Settings"); settingsLabel.setPadding(new Insets(14,0,10,0)); settingsLabel.setAlignment(Pos.CENTER); settingsLabel.setPrefWidth(Double.MAX_VALUE); - settingsLabel.setTextAlignment(TextAlignment.CENTER); + settingsLabel.setTextAlignment(TextAlignment.LEFT); settingsLabel.setStyle("-fx-font-weight: bold;"); PamButton saveConfig=new PamButton("Save"); @@ -68,12 +88,57 @@ public class PamSettingsMenuPane extends PamVBox { }); styleButton(saveConfigAs); + + //Air or water mode + ToggleButton toggleButton1 = new ToggleButton("Water"); + toggleButton1.setPrefWidth(60); + toggleButton1.setTooltip(new Tooltip(GlobalMedium.getToolTip(SoundMedium.Water))); + toggleButton1.setOnAction((action)->{ + if (PamController.getInstance().getGlobalMediumManager().getGlobalMediumParameters().currentMedium==SoundMedium.Water) return; //do nothing. + PamController.getInstance().getGlobalMediumManager().setCurrentMedium(SoundMedium.Water); + }); + + ToggleButton toggleButton2 = new ToggleButton("Air"); + toggleButton2.setPrefWidth(60); + toggleButton2.setTooltip(new Tooltip(GlobalMedium.getToolTip(SoundMedium.Air))); + toggleButton2.setOnAction((action)->{ + if (PamController.getInstance().getGlobalMediumManager().getGlobalMediumParameters().currentMedium==SoundMedium.Air) return; //do nothing. + PamController.getInstance().getGlobalMediumManager().setCurrentMedium(SoundMedium.Air); + }); + + ToggleGroup toggleGroup = new ToggleGroup(); + toggleButton1.setToggleGroup(toggleGroup); + toggleButton2.setToggleGroup(toggleGroup); + toggleGroup.selectToggle(toggleButton1); + + Label mediumLabel = new Label("Sound Medium"); + mediumLabel.setAlignment(Pos.CENTER_LEFT); + mediumLabel.setPadding(new Insets(0,0,0,15)); + //styleButton(mediumLabel); + + + PamHBox toggleButtonBox = new PamHBox(); + toggleButtonBox.setAlignment(Pos.CENTER_RIGHT); + toggleButtonBox.setSpacing(5); + toggleButtonBox.getChildren().addAll(toggleButton1, toggleButton2); + toggleButtonBox.setMaxWidth(Double.MAX_VALUE); + + PamHBox mediumToggleBox = new PamHBox(mediumLabel,toggleButtonBox); + PamHBox.setHgrow(toggleButtonBox, Priority.ALWAYS); + mediumToggleBox.setAlignment(Pos.CENTER_LEFT); + mediumToggleBox.setSpacing(5); + mediumToggleBox.setPadding(new Insets(0,5,0,0)); + + PamButton generalSettings=new PamButton("General Settings..."); styleButton(generalSettings); MenuButton settings=new MenuButton("Module Settings"); - settings.setStyle("-fx-background-radius: 0;" - + " -fx-border-color: transparent; -fx-padding: 0 0 0 0;"); + settings.setPopupSide(Side.RIGHT); +// settings.setStyle("-fx-background-radius: 0;" +// + " -fx-border-color: transparent; -fx-padding: 0 0 0 0;"); + + styleButton(settings); settings.setAlignment(Pos.CENTER_LEFT); settings.setPrefWidth(Double.MAX_VALUE); settings.setPrefHeight(40); @@ -146,16 +211,20 @@ public class PamSettingsMenuPane extends PamVBox { PamButton about=new PamButton("About..."); styleButton(about); - PamButton tip=new PamButton("Tip of the day..."); - styleButton(tip); +// PamButton tip=new PamButton("Tip of the day..."); +// styleButton(tip); PamButton website=new PamButton("Website"); styleButton(website); - PamButton contact=new PamButton("Contact and Support"); + PamButton contact=new PamButton("Found a bug?"); styleButton(contact); - this.getChildren().addAll(settingsLabel,saveConfig,saveConfigAs, generalSettings, settings, storageManager, database, binaryStorage, help, about, tip, website, contact); + PamButton checkForUpdates=new PamButton("Check for updates"); + styleButton(checkForUpdates); + + this.getChildren().addAll(settingsLabel,saveConfig,saveConfigAs, new Separator(), mediumToggleBox, generalSettings, settings, new Separator(), + storageManager, database, binaryStorage, new Separator(), help, checkForUpdates, website, contact, about); } @@ -168,6 +237,7 @@ public class PamSettingsMenuPane extends PamVBox { public void styleButton(Labeled control){ Insets buttonInsets=new Insets(0,leftInset,0,leftInset); control.setAlignment(Pos.CENTER_LEFT); + control.setStyle("-fx-alignment: center-left;"); control.setPadding(buttonInsets); control.getStyleClass().add("square-button-trans"); control.setPrefWidth(Double.MAX_VALUE); @@ -198,6 +268,4 @@ public class PamSettingsMenuPane extends PamVBox { } } - - } diff --git a/src/pamViewFX/fxGlyphs/PamSVGIcon.java b/src/pamViewFX/fxGlyphs/PamSVGIcon.java index ed82172a..2df67799 100644 --- a/src/pamViewFX/fxGlyphs/PamSVGIcon.java +++ b/src/pamViewFX/fxGlyphs/PamSVGIcon.java @@ -4,6 +4,7 @@ import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.paint.Color; +import javafx.scene.shape.FillRule; import javafx.scene.shape.SVGPath; import pamViewFX.fxNodes.utilsFX.PamUtilsFX; @@ -62,12 +63,18 @@ public class PamSVGIcon { } return instance; } + + @Deprecated + public PamSVGIcon create(URL path, Color color, double lineWidth) throws Exception { + return create(path); + } - public PamSVGIcon create(URL path, Color color) throws Exception { -// System.out.println("Create icon start"); + public PamSVGIcon create(URL path) throws Exception { - String col = PamUtilsFX.toRGBCode(color); + // System.out.println("Create icon start"); + +// String col = PamUtilsFX.toRGBCode(color); // System.out.println("Create icon getDocument()"); @@ -87,13 +94,27 @@ public class PamSVGIcon { for(int i=0; i{ + if (!newVal.booleanValue()) { + //need to set the back button so that listeners are triggered whenever the back + //button is pressed. + this.backButtonResponse.set(NORESPONE_BACK_BUTTON); + } + }); } - + /** * Get the front pane. * @return the front pane. @@ -89,7 +115,7 @@ public class PamFlipPane extends FlipPane { public PamBorderPane getFrontPane() { return frontPane; } - + /** * Get the back pane. * @return the back pane. @@ -105,16 +131,16 @@ public class PamFlipPane extends FlipPane { public PamBorderPane getAdvPane() { return advPane; } - - + + /** * Set the advanced pane content. * @param - the content to set. */ public void setAdvPaneContent(Node content) { - advPane.setCenter(content); + advPane.setCenter(content); } - + /** * Set the front pane content. * @param - the content to set. @@ -128,25 +154,42 @@ public class PamFlipPane extends FlipPane { * Create the advanced settings pane which can be accessed by DAQ panes if needed. */ private PamBorderPane createAdvSettingsPane() { - + backButton = new PamButton(); backButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-left", Color.WHITE, PamGuiManagerFX.iconSize)); -// backButton.setStyle("-fx-background-color: -color-base-6"); + // backButton.setStyle("-fx-background-color: -color-base-6"); //backButton.setStyle("-fx-padding: 0,0,0,0"); - - backButton.setOnAction((action)->{ - // System.out.println("FLIP BACK TO FRONT"); - this.flipToFront(); - }); - + //make the back button blue so users can easily see the button. backButton.setStyle("-fx-background-radius: 0 5 5 0; -fx-border-radius: 0 5 5 0; -fx-background-color: -color-accent-6"); + backButton.setOnAction((action)->{ + System.out.println("FLIP BACK TO FRONT"); + this.backButtonResponse.setValue(OK_BACK_BUTTON); + this.flipToFront(); + }); + + //Allow the user to cancel instead of pressing back button to confirm. + ContextMenu contextMenu = new ContextMenu(); + //Creating the menu Items for the context menu + MenuItem item1 = new MenuItem("Cancel"); + +// item1.setStyle("-fx-highlight-color: red"); + + item1.setOnAction((action)->{ + // System.out.println("FLIP BACK TO FRONT"); + this.backButtonResponse.setValue(CANCEL_BACK_BUTTON); + this.flipToFront(); + }); + contextMenu.getItems().addAll(item1); + + backButton.setContextMenu(contextMenu); + + //create a title that can be set. //backButton.setPrefWidth(150); PamBorderPane advPane = new PamBorderPane(); //advPane.setPadding(new Insets(5,5,5,5)); - - + // holds the title of the advanced pane. This consists of a label for a graphic, // an editable text field and a label after the editable settings field PamHBox titleHolder = new PamHBox(); @@ -158,27 +201,27 @@ public class PamFlipPane extends FlipPane { postLabel.setAlignment(Pos.CENTER_LEFT); advLabel.setAlignment(Pos.CENTER); -// advLabel.prefColumnCountProperty().bind(advLabel.textProperty().length().subtract(3)); + // advLabel.prefColumnCountProperty().bind(advLabel.textProperty().length().subtract(3)); // Set Max and Min Width to PREF_SIZE so that the TextField is always PREF advLabel.setMinWidth(Region.USE_PREF_SIZE); advLabel.setMaxWidth(Region.USE_PREF_SIZE); - + //pretty complicated to make sure the text field is the same size as the text that is being typed. advLabel.textProperty().addListener((ov, prevText, currText) -> { - // Do this in a Platform.runLater because of Textfield has no padding at first time and so on - Platform.runLater(() -> { - Text text = new Text(currText); - text.setFont(advLabel.getFont()); // Set the same font, so the size is the same - double width = text.getLayoutBounds().getWidth() // This big is the Text in the TextField - + advLabel.getPadding().getLeft() + advLabel.getPadding().getRight() // Add the padding of the TextField - + 2d; // Add some spacing - advLabel.setPrefWidth(width); // Set the width - advLabel.positionCaret(advLabel.getCaretPosition()); // If you remove this line, it flashes a little bit - }); + // Do this in a Platform.runLater because of Textfield has no padding at first time and so on + Platform.runLater(() -> { + Text text = new Text(currText); + text.setFont(advLabel.getFont()); // Set the same font, so the size is the same + double width = text.getLayoutBounds().getWidth() // This big is the Text in the TextField + + advLabel.getPadding().getLeft() + advLabel.getPadding().getRight() // Add the padding of the TextField + + 2d; // Add some spacing + advLabel.setPrefWidth(width); // Set the width + advLabel.positionCaret(advLabel.getCaretPosition()); // If you remove this line, it flashes a little bit + }); }); advLabel.setId("label-title2"); advLabel.setStyle("-fx-background-color: transparent"); - + titleHolder.setMaxWidth(Double.MAX_VALUE); //need to make sure label is in center. //holds the back button and the title pane. @@ -190,31 +233,31 @@ public class PamFlipPane extends FlipPane { advLabel.setAlignment(Pos.CENTER); advLabel.setMaxWidth(Double.MAX_VALUE); //need to make sure label is in center. -// PamGuiManagerFX.titleFont2style(advLabel); + // PamGuiManagerFX.titleFont2style(advLabel); advLabel.setAlignment(Pos.CENTER); HBox.setHgrow(titleHolder, Priority.ALWAYS); - + advPane.setTop(buttonHolder); - + return advPane; - + } - + public TextField getAdvLabel() { return advLabel; } -// public void setAdvLabel(Label advLabel) { -// this.advLabel = advLabel; -// } + // public void setAdvLabel(Label advLabel) { + // this.advLabel = advLabel; + // } public PamButton getBackButton() { return backButton; } - + /** * Get the label located before the editable label in the title * @return the label before the editable label @@ -222,7 +265,7 @@ public class PamFlipPane extends FlipPane { public Label getPreAdvLabel() { return preLabel; } - + /** * Get the label located after the editable label in the title * @return the label after the editable label @@ -240,7 +283,14 @@ public class PamFlipPane extends FlipPane { public void setAdvLabelEditable(boolean b) { this.advLabel.setEditable(b); - + } + + /** + * The back button property. Called whenever the back button is pressed. + * @return the back button integer proeprty. + */ + public SimpleIntegerProperty backButtonProperty(){ + return this.backButtonResponse; } diff --git a/src/pamViewFX/fxNodes/navigationDrawer/NavigationDrawer.java b/src/pamViewFX/fxNodes/navigationDrawer/NavigationDrawer.java new file mode 100644 index 00000000..0f175c9e --- /dev/null +++ b/src/pamViewFX/fxNodes/navigationDrawer/NavigationDrawer.java @@ -0,0 +1,179 @@ +package pamViewFX.fxNodes.navigationDrawer; + +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener.Change; +import javafx.collections.ObservableList; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Labeled; +import javafx.scene.control.SingleSelectionModel; +import javafx.scene.control.Tab; +import javafx.scene.layout.BorderPane; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamButton; +import pamViewFX.fxNodes.PamVBox; + +/** + * A pane which is somewhat related to a tab pane but where the tabs are on the + * left or right and horizontal instead of vertical. + */ +public class NavigationDrawer extends PamBorderPane { + + private double leftInset=20; + + private ObservableList tabs = FXCollections.observableArrayList(); + + //the currently selected tab is null. + private Tab currentlySelectTab = null; + + PamVBox tabHolder; + + private NavSlectionModel selectionModel; + + /** + *

The tabs to display in this TabPane. Changing this ObservableList will + * immediately result in the NavigationDrawer updating to display the new contents + * of this ObservableList.

+ * + *

If the tabs ObservableList changes, the selected tab will remain the previously + * selected tab, if it remains within this ObservableList. If the previously + * selected tab is no longer in the tabs ObservableList, the selected tab will + * become the first tab in the ObservableList.

+ * @return the list of tabs + */ + public final ObservableList getTabs() { + return tabs; + } + + + public NavigationDrawer() { + + tabHolder = new PamVBox(); + tabHolder.setSpacing(0); + + tabHolder.setPrefWidth(200); + + BorderPane.setMargin(tabHolder, new Insets(0,0,0,0)); + +// tabHolder.setStyle("-fx-background-color: -fx-darkbackground"); +// tabHolder.setStyle("-fx-background-color: red; -fx-padding: 0px 0px 0px 0px;"); + + this.setLeft(tabHolder); + + this.tabs.addListener((Change c) -> { + layoutNavigationDrawer(); + }); + +// this.setStyle("-fx-background-color: orange"); + + //this is required for some styles ot make sure there are no insets. + this.setStyle("-fx-padding: 0px 0px 0px 0px"); + + + selectionModel = new NavSlectionModel(); + + + } + + /** + * Layout the navigation drawer. + */ + private void layoutNavigationDrawer() { + +// System.out.println("LAYOUT NAVIGATION DRAWER:"); + + tabHolder.getChildren().clear(); + for (int i = 0; i{ + setNavigationTab(ii); + }); + tabHolder.getChildren().add(button); + } + + //make sure previous tab is selected. + if (currentlySelectTab!=null) { + int ind = tabs.indexOf(currentlySelectTab); + if (ind>=0) { + setNavigationTab(ind); + } + else if (tabs.size()>0) { + setNavigationTab(0); + } + else { + this.setCenter(null); + } + } + else if (tabs.size()>0) { + setNavigationTab(0); + } + else { + this.setCenter(null); + } + } + + public void setNavigationTab(int ii){ + //set all tabs to default style. + for (int i=0; i{ + + @Override + protected Tab getModelItem(int index) { + return tabs.get(index); + } + + @Override + protected int getItemCount() { + return tabs.size(); + } + + } + + + + + +} + + diff --git a/src/pamViewFX/fxNodes/pamAxis/PamDateAxis.java b/src/pamViewFX/fxNodes/pamAxis/PamDateAxis.java new file mode 100644 index 00000000..e877beec --- /dev/null +++ b/src/pamViewFX/fxNodes/pamAxis/PamDateAxis.java @@ -0,0 +1,827 @@ +package pamViewFX.fxNodes.pamAxis; + +import com.sun.javafx.charts.ChartLayoutAnimator; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.beans.property.*; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.css.CssMetaData; +import javafx.css.Styleable; +import javafx.css.StyleableDoubleProperty; +import javafx.css.StyleableProperty; +import javafx.css.converter.SizeConverter; +import javafx.geometry.Dimension2D; +import javafx.geometry.Side; +import javafx.scene.chart.ValueAxis; +import javafx.util.Duration; +import javafx.util.StringConverter; +import javafx.util.converter.TimeStringConverter; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + +/* +* Created with IntelliJ IDEA. +* User: Pedro Duque Vieira +* Date: 15-08-2013 +* Time: 18:33 +* To change this template use File | Settings | File Templates. +*/ +public class PamDateAxis extends ValueAxis { + + /** We use these for auto ranging to pick a user friendly tick unit. (must be increasingly bigger)*/ + private static final double[] TICK_UNIT_DEFAULTS = { + 86400000, // 1 day + 172800000, // 2 das + 259200000, // 3 days + 345600000, // 4 days + 432000000, // 5 days + 518400000, // 6 days + 604800000, // 7 days + 691200000, // 8 days + 777600000, // 9 days + 864000000, // 10 days + 216000000E1, // 15 days + 388800000E1, // 20 days + 604800000E1, // 25 days + 872640000E1, // 31 days ~ 1 month + 1226880000E1, // 41 days + 1667520000E1, // 51 days + 2203200000E1, // 62 days ~ 2 months + 2868480000E1, // 77 days + 3672000000E1, // 93 days ~ 3 months + 4605120000E1, // 108 days + 5676480000E1, // 124 days ~ 4 months + 6877440000E1, // 139 days + 8216640000E1, // 155 days ~ 5 months + 9685440000E1, // 170 days + 1129248000E2, // 186 days ~ 6 months + 1445472000E2 // 366 days ~ 1 year + }; + + /** These are matching date formatter strings */ + private static final String[] TICK_UNIT_FORMATTER_DEFAULTS = { + "MM/dd/yy", // 1 day + "MM/dd/yy", // 2 das + "MM/dd/yy", // 3 days + "MM/dd/yy", // 4 days + "MM/dd/yy", // 5 days + "MM/dd/yy", // 6 days + "MM/dd/yy", // 7 days + "MM/dd/yy", // 8 days + "MM/dd/yy", // 9 days + "MM/dd/yy", // 10 days + "MM/dd/yy", // 15 days + "MM/dd/yy", // 20 days + "MM/dd/yy", // 25 days + "MMM-yyyy", // 31 days ~ 1 month + "MMM-yyyy", // 41 days + "MMM-yyyy", // 51 days + "MMM-yyyy", // 62 days ~ 2 months + "MMM-yyyy", // 77 days + "MMM-yyyy", // 93 days ~ 3 months + "MMM-yyyy", // 108 days + "MMM-yyyy", // 124 days ~ 4 months + "MMM-yyyy", // 139 days + "MMM-yyyy", // 155 days ~ 5 months + "MMM-yyyy", // 170 days + "MMM-yyyy", // 186 days ~ 6 months + "yyyy" // 366 days ~ 1 year + }; + + + private Object currentAnimationID; + private final ChartLayoutAnimator animator = new ChartLayoutAnimator(this); + private IntegerProperty currentRangeIndexProperty = new SimpleIntegerProperty(this, "currentRangeIndex", -1); + private DefaultFormatter defaultFormatter = new DefaultFormatter(this); + + // -------------- PUBLIC PROPERTIES -------------------------------------------------------------------------------- + + /** When true zero is always included in the visible range. This only has effect if auto-ranging is on. */ + private BooleanProperty forceZeroInRange = new BooleanPropertyBase(true) { + @Override protected void invalidated() { + // This will effect layout if we are auto ranging + if(isAutoRanging()) requestAxisLayout(); + } + + @Override + public Object getBean() { + return PamDateAxis.this; + } + + @Override + public String getName() { + return "forceZeroInRange"; + } + }; + public final boolean isForceZeroInRange() { return forceZeroInRange.getValue(); } + public final void setForceZeroInRange(boolean value) { forceZeroInRange.setValue(value); } + public final BooleanProperty forceZeroInRangeProperty() { return forceZeroInRange; } + + /** The value between each major tick mark in data units. This is automatically set if we are auto-ranging. */ + private DoubleProperty tickUnit = new StyleableDoubleProperty(5) { + @Override protected void invalidated() { + if(!isAutoRanging()) { + invalidateRange(); + requestAxisLayout(); + } + } + + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.TICK_UNIT; + } + + @Override + public Object getBean() { + return PamDateAxis.this; + } + + @Override + public String getName() { + return "tickUnit"; + } + }; + public final double getTickUnit() { return tickUnit.get(); } + public final void setTickUnit(double value) { tickUnit.set(value); } + public final DoubleProperty tickUnitProperty() { return tickUnit; } + + // -------------- CONSTRUCTORS ------------------------------------------------------------------------------------- + + /** + * Create a auto-ranging DateAxis + */ + public PamDateAxis() { + forceZeroInRange.set(false); + } + + /** + * Create a non-auto-ranging DateAxis with the given upper bound, lower bound and tick unit + * + * @param lowerBound The lower bound for this axis, ie min plottable value + * @param upperBound The upper bound for this axis, ie max plottable value + * @param tickUnit The tick unit, ie space between tickmarks + */ + public PamDateAxis(double lowerBound, double upperBound, double tickUnit) { + super(lowerBound, upperBound); + setTickUnit(tickUnit); + } + + /** + * Create a non-auto-ranging DateAxis with the given upper bound, lower bound and tick unit + * + * @param axisLabel The name to display for this axis + * @param lowerBound The lower bound for this axis, ie min plottable value + * @param upperBound The upper bound for this axis, ie max plottable value + * @param tickUnit The tick unit, ie space between tickmarks + */ + public PamDateAxis(String axisLabel, double lowerBound, double upperBound, double tickUnit) { + super(lowerBound, upperBound); + setTickUnit(tickUnit); + setLabel(axisLabel); + } + + // -------------- PROTECTED METHODS -------------------------------------------------------------------------------- + + /** + * Get the string label name for a tick mark with the given value + * + * @param value The value to format into a tick label string + * @return A formatted string for the given value + */ + @Override protected String getTickMarkLabel(Long value) { + StringConverter formatter = getTickLabelFormatter(); + if (formatter == null) formatter = defaultFormatter; + return formatter.toString(value); + } + + /** + * Called to get the current axis range. + * + * @return A range object that can be passed to setRange() and calculateTickValues() + */ + @Override protected Object getRange() { + double[] newParams = recalculateTicks(); + double newMin = newParams[0]; + double newMax = newParams[1]; + double newIndex = newParams[2]; + double newTickUnit = newParams[3]; + return new double[]{ + newMin, + newMax, + newTickUnit, + getScale(), + newIndex + }; + } + + public double[] recalculateTicks() + { + final Side side = getSide(); + final boolean vertical = Side.LEFT.equals(side) || Side.RIGHT.equals(side); + final double length = vertical ? getHeight() : getWidth(); + // guess a sensible starting size for label size, that is approx 2 lines vertically or 2 charts horizontally + double labelSize = getTickLabelFont().getSize() * 2; + + double currentRange = getUpperBound() - getLowerBound(); + + // calculate the number of tick-marks we can fit in the given length + int numOfTickMarks = (int)Math.floor(Math.abs(length)/labelSize); + // can never have less than 2 tick marks one for each end + numOfTickMarks = Math.max(numOfTickMarks, 2); + // calculate tick unit for the number of ticks can have in the given data range + double tickUnit = currentRange/(double)numOfTickMarks; + // search for the best tick unit that fits + double tickUnitRounded = 0; + double minRounded = 0; + double maxRounded = 0; + int count = 0; + double reqLength = Double.MAX_VALUE; + int rangeIndex = 10; + // loop till we find a set of ticks that fit length and result in a total of less than 20 tick marks + while (reqLength > length || count > 20) { + // find a user friendly match from our default tick units to match calculated tick unit + for (int i=0; i tickUnit) { + tickUnitRounded = tickUnitDefault; + rangeIndex = i; + break; + } + } + // move min and max to nearest tick mark + minRounded = Math.floor(getLowerBound() / tickUnitRounded) * tickUnitRounded; + maxRounded = Math.ceil(getUpperBound() / tickUnitRounded) * tickUnitRounded; + // calculate the required length to display the chosen tick marks for real, this will handle if there are + // huge numbers involved etc or special formatting of the tick mark label text + double maxReqTickGap = 0; + double last = 0; + count = 0; + for (double major = minRounded; major <= maxRounded; major += tickUnitRounded, count ++) { + double size = (vertical) ? measureTickMarkSize((long)major, getTickLabelRotation(), rangeIndex).getHeight() : + measureTickMarkSize((long)major, getTickLabelRotation(), rangeIndex).getWidth(); + if (major == minRounded) { // first + last = size/2; + } else { + maxReqTickGap = Math.max(maxReqTickGap, last + 6 + (size/2) ); + } + } + reqLength = (count-1) * maxReqTickGap; + tickUnit = tickUnitRounded; + // check if we already found max tick unit + if (tickUnitRounded == TICK_UNIT_DEFAULTS[TICK_UNIT_DEFAULTS.length-1]) { + // nothing we can do so just have to use this + break; + } + } + return new double[]{minRounded, maxRounded, rangeIndex, tickUnit}; + } + + /** + * Called to set the current axis range to the given range. If isAnimating() is true then this method should + * animate the range to the new range. + * + * @param range A range object returned from autoRange() + * @param animate If true animate the change in range + */ + @Override protected void setRange(Object range, boolean animate) { + final double[] rangeProps = (double[]) range; + final double lowerBound = rangeProps[0]; + final double upperBound = rangeProps[1]; + final double tickUnit = rangeProps[2]; + final double scale = rangeProps[3]; + final double rangeIndex = rangeProps[4]; + currentRangeIndexProperty.set((int)rangeIndex); + final double oldLowerBound = getLowerBound(); + setLowerBound(lowerBound); + setUpperBound(upperBound); + setTickUnit(tickUnit); + + ReadOnlyDoubleWrapper scalePropertyImplValue = (ReadOnlyDoubleWrapper) ReflectionUtils.forceMethodCall(ValueAxis.class, "scalePropertyImpl", this); + + if(animate) { + animator.stop(currentAnimationID); + currentAnimationID = animator.animate( + new KeyFrame(Duration.ZERO, + new KeyValue(currentLowerBound, oldLowerBound), + new KeyValue(scalePropertyImplValue, getScale()) + ), + new KeyFrame(Duration.millis(700), + new KeyValue(currentLowerBound, lowerBound), + new KeyValue(scalePropertyImplValue, scale) + ) + ); + } else { + currentLowerBound.set(lowerBound); + setScale(scale); + } + } + + /** + * Calculate a list of all the data values for each tick mark in range + * + * @param length The length of the axis in display units + * @param range A range object returned from autoRange() + * @return A list of tick marks that fit along the axis if it was the given length + */ + @Override protected List calculateTickValues(double length, Object range) { + final double[] rangeProps = (double[]) range; + final double lowerBound = rangeProps[0]; + final double upperBound = rangeProps[1]; + final double tickUnit = rangeProps[2]; + List tickValues = new ArrayList(); + if (tickUnit <= 0 || lowerBound == upperBound) { + tickValues.add((long)lowerBound); + } else if (getTickUnit() > 0) { + for (double major = lowerBound; major <= upperBound; major += tickUnit) { + tickValues.add((long)major); + if(tickValues.size()>2000) { + // This is a ridiculous amount of major tick marks, something has probably gone wrong + System.err.println("Warning we tried to create more than 2000 major tick marks on a NumberAxis. " + + "Lower Bound=" + lowerBound + ", Upper Bound=" + upperBound + ", Tick Unit=" + tickUnit); + break; + } + } + } + return tickValues; + } + + /** + * Calculate a list of the data values for every minor tick mark + * + * @return List of data values where to draw minor tick marks + */ + protected List calculateMinorTickMarks() { + final List minorTickMarks = new ArrayList(); + final double lowerBound = getLowerBound(); + final double upperBound = getUpperBound(); + final double tickUnit = getTickUnit(); + final double minorUnit = tickUnit/getMinorTickCount(); + if (getTickUnit() > 0) { + for (double major = lowerBound; major < upperBound; major += tickUnit) { + for (double minor=major+minorUnit; minor < (major+tickUnit); minor += minorUnit) { + minorTickMarks.add((long)minor); + if(minorTickMarks.size()>10000) { + // This is a ridiculous amount of major tick marks, something has probably gone wrong + System.err.println("Warning we tried to create more than 10000 minor tick marks on a NumberAxis. " + + "Lower Bound=" + getLowerBound() + ", Upper Bound=" + getUpperBound() + ", Tick Unit=" + tickUnit); + break; + } + } + } + } + return minorTickMarks; + } + + /** + * Measure the size of the label for given tick mark value. This uses the font that is set for the tick marks + * + * @param value tick mark value + * @param range range to use during calculations + * @return size of tick mark label for given value + */ + @Override protected Dimension2D measureTickMarkSize(Long value, Object range) { + final double[] rangeProps = (double[]) range; + final double rangeIndex = rangeProps[4]; + return measureTickMarkSize(value, getTickLabelRotation(), (int)rangeIndex); + } + + /** + * Measure the size of the label for given tick mark value. This uses the font that is set for the tick marks + * + * @param value tick mark value + * @param rotation The text rotation + * @param rangeIndex The index of the tick unit range + * @return size of tick mark label for given value + */ + private Dimension2D measureTickMarkSize(Long value, double rotation, int rangeIndex) { + String labelText; + StringConverter formatter = getTickLabelFormatter(); + if (formatter == null) formatter = defaultFormatter; + if(formatter instanceof DefaultFormatter) { + labelText = ((DefaultFormatter)formatter).toString(value, rangeIndex); + } else { + labelText = formatter.toString(value); + } + return measureTickMarkLabelSize(labelText, rotation); + } + + /** + * Called to set the upper and lower bound and anything else that needs to be auto-ranged + * + * @param minValue The min data value that needs to be plotted on this axis + * @param maxValue The max data value that needs to be plotted on this axis + * @param length The length of the axis in display coordinates + * @param labelSize The approximate average size a label takes along the axis + * @return The calculated range + */ + @Override protected Object autoRange(double minValue, double maxValue, double length, double labelSize) { + final Side side = getSide(); + final boolean vertical = Side.LEFT.equals(side) || Side.RIGHT.equals(side); + // check if we need to force zero into range + if (isForceZeroInRange()) { + if (maxValue < 0) { + maxValue = 0; + } else if (minValue > 0) { + minValue = 0; + } + } + final double range = maxValue-minValue; + +// // pad min and max by 2%, checking if the range is zero + final double paddedRange = (range==0) ? 2 : Math.abs(range)*1.02; + + final double padding = (paddedRange - range) / 2; + // if min and max are not zero then add padding to them + double paddedMin = minValue - padding; + double paddedMax = maxValue + padding; + // check padding has not pushed min or max over zero line + if ((paddedMin < 0 && minValue >= 0) || (paddedMin > 0 && minValue <= 0)) { + // padding pushed min above or below zero so clamp to 0 + paddedMin = 0; + } + if ((paddedMax < 0 && maxValue >= 0) || (paddedMax > 0 && maxValue <= 0)) { + // padding pushed min above or below zero so clamp to 0 + paddedMax = 0; + } + // calculate the number of tick-marks we can fit in the given length + int numOfTickMarks = (int)Math.floor(Math.abs(length)/labelSize); + // can never have less than 2 tick marks one for each end + numOfTickMarks = Math.max(numOfTickMarks, 2); + // calculate tick unit for the number of ticks can have in the given data range + double tickUnit = paddedRange/(double)numOfTickMarks; + // search for the best tick unit that fits + double tickUnitRounded = 0; + double minRounded = 0; + double maxRounded = 0; + int count = 0; + double reqLength = Double.MAX_VALUE; + int rangeIndex = 10; + // loop till we find a set of ticks that fit length and result in a total of less than 20 tick marks + while (reqLength > length || count > 20) { + // find a user friendly match from our default tick units to match calculated tick unit + for (int i=0; i tickUnit) { + tickUnitRounded = tickUnitDefault; + rangeIndex = i; + break; + } + } + // move min and max to nearest tick mark + minRounded = Math.floor(paddedMin / tickUnitRounded) * tickUnitRounded; + maxRounded = Math.ceil(paddedMax / tickUnitRounded) * tickUnitRounded; + // calculate the required length to display the chosen tick marks for real, this will handle if there are + // huge numbers involved etc or special formatting of the tick mark label text + double maxReqTickGap = 0; + double last = 0; + count = 0; + for (double major = minRounded; major <= maxRounded; major += tickUnitRounded, count ++) { + double size = (vertical) ? measureTickMarkSize((long)major, getTickLabelRotation(), rangeIndex).getHeight() : + measureTickMarkSize((long)major, getTickLabelRotation(), rangeIndex).getWidth(); + if (major == minRounded) { // first + last = size/2; + } else { + maxReqTickGap = Math.max(maxReqTickGap, last + 6 + (size/2) ); + } + } + reqLength = (count-1) * maxReqTickGap; + tickUnit = tickUnitRounded; + // check if we already found max tick unit + if (tickUnitRounded == TICK_UNIT_DEFAULTS[TICK_UNIT_DEFAULTS.length-1]) { + // nothing we can do so just have to use this + break; + } + } + // calculate new scale + final double newScale = calculateNewScale(length, minRounded, maxRounded); + // return new range + return new double[]{minRounded, maxRounded, tickUnitRounded, newScale, rangeIndex}; + } + + // -------------- STYLESHEET HANDLING ------------------------------------------------------------------------------ + + /** @treatAsPrivate implementation detail */ + private static class StyleableProperties { + private static final CssMetaData TICK_UNIT = + new CssMetaData("-fx-tick-unit", + SizeConverter.getInstance(), 5.0) { + + @Override + public boolean isSettable(PamDateAxis n) { + return n.tickUnit == null || !n.tickUnit.isBound(); + } + + @Override + public StyleableProperty getStyleableProperty(PamDateAxis n) { + return (StyleableProperty)n.tickUnitProperty(); + } + }; + + private static final List> STYLEABLES; + static { + final List> styleables = + new ArrayList>(ValueAxis.getClassCssMetaData()); + styleables.add(TICK_UNIT); + STYLEABLES = Collections.unmodifiableList(styleables); + } + } + + /** + * @return The CssMetaData associated with this class, which may include the + * CssMetaData of its super classes. + * @since JavaFX 8.0 + */ + public static List> getClassCssMetaData() { + return StyleableProperties.STYLEABLES; + } + + /** + * {@inheritDoc} + * @since JavaFX 8.0 + */ + @Override + public List> getCssMetaData() { + return getClassCssMetaData(); + } + + // -------------- INNER CLASSES ------------------------------------------------------------------------------------ + + /** + * Default number formatter for DateAxis, this stays in sync with auto-ranging and formats values appropriately. + * You can wrap this formatter to add prefixes or suffixes; + * @since JavaFX 2.0 + */ + public static class DefaultFormatter extends StringConverter { + private TimeStringConverter formatter; + private String prefix = null; + private String suffix = null; + + private Date tempDate = new Date(); + + /** used internally */ + private DefaultFormatter() { + formatter = new TimeStringConverter("MM/dd/yy"); + } + + /** + * Construct a DefaultFormatter for the given DateAxis + * + * @param axis The axis to format tick marks for + */ + public DefaultFormatter(final PamDateAxis axis) { + formatter = getFormatter(axis.isAutoRanging()? axis.currentRangeIndexProperty.get() : -1); + final ChangeListener axisListener = new ChangeListener() { + @Override public void changed(ObservableValue observable, Object oldValue, Object newValue) { + formatter = getFormatter(axis.isAutoRanging()? axis.currentRangeIndexProperty.get() : -1); + } + }; + axis.currentRangeIndexProperty.addListener(axisListener); + axis.autoRangingProperty().addListener(axisListener); + } + + /** + * Construct a DefaultFormatter for the given DateAxis with a prefix and/or suffix. + * + * @param axis The axis to format tick marks for + * @param prefix The prefix to append to the start of formatted number, can be null if not needed + * @param suffix The suffix to append to the end of formatted number, can be null if not needed + */ + public DefaultFormatter(PamDateAxis axis, String prefix, String suffix) { + this(axis); + this.prefix = prefix; + this.suffix = suffix; + } + + private static TimeStringConverter getFormatter(int rangeIndex) { + if (rangeIndex < 0) { + return new TimeStringConverter("MM/dd/yy"); + } else if(rangeIndex >= TICK_UNIT_FORMATTER_DEFAULTS.length) { + return new TimeStringConverter(TICK_UNIT_FORMATTER_DEFAULTS[TICK_UNIT_FORMATTER_DEFAULTS.length-1]); + } else { + return new TimeStringConverter(TICK_UNIT_FORMATTER_DEFAULTS[rangeIndex]); + } + } + + /** + * Converts the object provided into its string form. + * Format of the returned string is defined by this converter. + * @return a string representation of the object passed in. + * @see StringConverter#toString + */ + @Override public String toString(Long object) { + return toString(object, formatter); + } + + private String toString(Long object, int rangeIndex) { + return toString(object, getFormatter(rangeIndex)); + } + + private String toString(Long object, TimeStringConverter formatter) { + tempDate.setTime(object); + if (prefix != null && suffix != null) { + return prefix + formatter.toString(tempDate) + suffix; + } else if (prefix != null) { + return prefix + formatter.toString(tempDate); + } else if (suffix != null) { + return formatter.toString(tempDate) + suffix; + } else { + return formatter.toString(tempDate); + } + } + + /** + * Converts the string provided into a Number defined by the this converter. + * Format of the string and type of the resulting object is defined by this converter. + * @return a Number representation of the string passed in. + * @see StringConverter#toString + */ + @Override public Long fromString(String string) { + int prefixLength = (prefix == null)? 0: prefix.length(); + int suffixLength = (suffix == null)? 0: suffix.length(); + return formatter.fromString(string.substring(prefixLength, string.length() - suffixLength)).getTime(); + } + } + + + public static void main (String [] args) + { + // Date construction test + GregorianCalendar calendar = new GregorianCalendar(1900, 0, 1); // year, month, day + Date date = calendar.getTime(); + TimeStringConverter timeConverter = new TimeStringConverter("MM/dd/yyyy"); + System.out.println("This is the date toString = " + timeConverter.toString(date)); + + // What is 1 day converted to long + calendar = new GregorianCalendar(1900, 0, 1); + date = calendar.getTime(); + calendar.add(Calendar.DAY_OF_MONTH, 1); + Date secondDate = calendar.getTime(); + long firstDateValue = date.getTime(); + long secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (1 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 2 days converted to long + calendar = new GregorianCalendar(1900, 0, 1); + calendar.add(Calendar.DAY_OF_MONTH, 2); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (2 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 3 days converted to long + calendar = new GregorianCalendar(1900, 0, 1); + calendar.add(Calendar.DAY_OF_MONTH, 3); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (3 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 4 days converted to long + calendar = new GregorianCalendar(1900, 0, 1); + calendar.add(Calendar.DAY_OF_MONTH, 4); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (4 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 5 days converted to long + calendar = new GregorianCalendar(1900, 0, 1); + calendar.add(Calendar.DAY_OF_MONTH, 5); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (5 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 6 days converted to long + calendar = new GregorianCalendar(1900, 0, 1); + calendar.add(Calendar.DAY_OF_MONTH, 6); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (6 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 7 days converted to long + calendar = new GregorianCalendar(1900, 0, 1); + calendar.add(Calendar.DAY_OF_MONTH, 7); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (7 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 8 days converted to long + calendar = new GregorianCalendar(1900, 0, 1); + calendar.add(Calendar.DAY_OF_MONTH, 8); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (8 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 9 days converted to long + calendar = new GregorianCalendar(1900, 0, 1); + calendar.add(Calendar.DAY_OF_MONTH, 9); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (9 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 10 days converted to long + calendar = new GregorianCalendar(1900, 0, 1); + calendar.add(Calendar.DAY_OF_MONTH, 10); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (10 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 15 days? With a long type + calendar.add(Calendar.DAY_OF_MONTH, 15); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (15 days) - \t" + (secondDateValue - firstDateValue)); + + // What is 20 days? With a long type + calendar.add(Calendar.DAY_OF_MONTH, 20); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (20 days) - \t" + (secondDateValue - firstDateValue)); + + // What is 25 days? With a long type + calendar.add(Calendar.DAY_OF_MONTH, 25); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (25 days) - \t" + (secondDateValue - firstDateValue)); + + + // What is 1 mont converted to long + calendar.add(Calendar.DAY_OF_MONTH, 31); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (31 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 41 days + calendar.add(Calendar.DAY_OF_MONTH, 41); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (41 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 51 days + calendar.add(Calendar.DAY_OF_MONTH, 51); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (51 day) - \t" + (secondDateValue - firstDateValue)); + + // What is 62 days ( 2 monhts + calendar.add(Calendar.DAY_OF_MONTH, 62); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (62 days - 2 months) - \t" + (secondDateValue - firstDateValue)); + + // What is 77 days ( 2 monhts and a half + calendar.add(Calendar.DAY_OF_MONTH, 77); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (77 days - 2.5 month) - \t" + (secondDateValue - firstDateValue)); + + // What is 93 days ( 3 monhts + calendar.add(Calendar.DAY_OF_MONTH, 93); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (93 days - 3 month) - \t" + (secondDateValue - firstDateValue)); + + // What is 108 ( 3 monhts and a half + calendar.add(Calendar.DAY_OF_MONTH, 108); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (108 days - 3.5 month) - \t" + (secondDateValue - firstDateValue)); + + // What is 124 ( 4 monhts + calendar.add(Calendar.DAY_OF_MONTH, 124); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (124 days - 4 month) - \t" + (secondDateValue - firstDateValue)); + + // What is 139 ( 4.5 monhts + calendar.add(Calendar.DAY_OF_MONTH, 139); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (139 days - 4.5 month) - \t" + (secondDateValue - firstDateValue)); + + // What is 139 ( 5 monhts + calendar.add(Calendar.DAY_OF_MONTH, 155); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (155 days - 5 month) - \t" + (secondDateValue - firstDateValue)); + + // What is 139 ( 5.5 monhts + calendar.add(Calendar.DAY_OF_MONTH, 170); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (170 days - 5.5 month) - \t" + (secondDateValue - firstDateValue)); + + // What is 139 ( 6 monhts + calendar.add(Calendar.DAY_OF_MONTH, 186); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (186 days - 6 month) - \t" + (secondDateValue - firstDateValue)); + + // What is 366 ( 1 year) + calendar.add(Calendar.DAY_OF_MONTH, 366); + secondDate = calendar.getTime(); + secondDateValue = secondDate.getTime(); + System.out.println("This is the difference of value between the first and second date (366 days - 1 year) - \t" + (secondDateValue - firstDateValue)); + } + +} \ No newline at end of file diff --git a/src/pamViewFX/fxNodes/pamAxis/ReflectionUtils.java b/src/pamViewFX/fxNodes/pamAxis/ReflectionUtils.java new file mode 100644 index 00000000..ff42894a --- /dev/null +++ b/src/pamViewFX/fxNodes/pamAxis/ReflectionUtils.java @@ -0,0 +1,58 @@ +package pamViewFX.fxNodes.pamAxis; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * User: Pedro + * Date: 11-09-2013 + * Time: 23:21 + * To change this template use File | Settings | File Templates. + */ +public class ReflectionUtils { + + public static Object forceMethodCall(Class classInstance, String methodName, Object source, Class[] paramTypes, Object[] params) { + Object returnedObject = null; + try { + Method method = classInstance.getDeclaredMethod(methodName, paramTypes); + method.setAccessible(true); + returnedObject = method.invoke(source, params); + } catch (Exception e) { + e.printStackTrace(); + } + return returnedObject; + } + + public static Object forceMethodCall(Class classInstance, String methodName, Object source, Object... params) { + Class[] paramTypes = new Class[]{}; + if (params == null) { + params = new Object[]{}; + } + List derivedTypes = new ArrayList<>(); + for (Object p : params) { + derivedTypes.add(p.getClass()); + } + if (derivedTypes.size() > 0) { + paramTypes = derivedTypes.toArray(new Class[derivedTypes.size()]); + } + return forceMethodCall(classInstance, methodName, source, paramTypes, params); + } + + public static Object forceFieldCall(Class classInstance, String fieldName, Object source) { + Object returnedObject = null; + try { + Field field = classInstance.getDeclaredField(fieldName); + field.setAccessible(true); + returnedObject = field.get(source); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + + return returnedObject; + } +} \ No newline at end of file diff --git a/src/pamViewFX/fxNodes/pamDialogFX/PamDialogFX.java b/src/pamViewFX/fxNodes/pamDialogFX/PamDialogFX.java index 62192f14..d83b0dd7 100644 --- a/src/pamViewFX/fxNodes/pamDialogFX/PamDialogFX.java +++ b/src/pamViewFX/fxNodes/pamDialogFX/PamDialogFX.java @@ -20,6 +20,7 @@ import javafx.stage.Window; /** * Creates a dialog with some useful PAMGUARD customisation. + * * @author Jamie Macaulay * */ @@ -253,6 +254,14 @@ public abstract class PamDialogFX extends Dialog { public static boolean showError(String content) { return showMessageDialog(null, "Error", content, AlertType.ERROR); } + + /** + * Show error dialog with default OK and CANCEL. + * @param content - the error message. + */ + public static boolean showError(String title, String content) { + return showMessageDialog(null, title, content, AlertType.ERROR); + } public static boolean showMessageDialog(Window owner, String string, String string2, ButtonType yes, ButtonType cancel) { diff --git a/src/pamViewFX/fxNodes/pamDialogFX/PamInternalDialogFX.java b/src/pamViewFX/fxNodes/pamDialogFX/PamInternalDialogFX.java index e3247eef..d8fa8fec 100644 --- a/src/pamViewFX/fxNodes/pamDialogFX/PamInternalDialogFX.java +++ b/src/pamViewFX/fxNodes/pamDialogFX/PamInternalDialogFX.java @@ -54,7 +54,7 @@ public class PamInternalDialogFX extends StackPane { mainPane.setPadding(new Insets(5,5,5,5)); PamStylesManagerFX stylesManager = PamStylesManagerFX.getPamStylesManagerFX(); - this.getStylesheets().add(stylesManager.getCurStyle().getSlidingDialogCSS()); + this.getStylesheets().addAll(stylesManager.getCurStyle().getSlidingDialogCSS()); //title and drag pane if (title!=null) { diff --git a/src/pamViewFX/fxNodes/pamDialogFX/PamJFXPanel.java b/src/pamViewFX/fxNodes/pamDialogFX/PamJFXPanel.java index 1774e0ce..e64ce8fe 100644 --- a/src/pamViewFX/fxNodes/pamDialogFX/PamJFXPanel.java +++ b/src/pamViewFX/fxNodes/pamDialogFX/PamJFXPanel.java @@ -30,7 +30,7 @@ public class PamJFXPanel extends JFXPanel { this.scene = new Scene(midBorderPane); - scene.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); + scene.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); setScene(scene); diff --git a/src/pamViewFX/fxNodes/pamDialogFX/PamSettingsDialogFX.java b/src/pamViewFX/fxNodes/pamDialogFX/PamSettingsDialogFX.java index 57fc5839..54767d94 100644 --- a/src/pamViewFX/fxNodes/pamDialogFX/PamSettingsDialogFX.java +++ b/src/pamViewFX/fxNodes/pamDialogFX/PamSettingsDialogFX.java @@ -32,7 +32,7 @@ public class PamSettingsDialogFX extends PamDialogFX { // this.getDialogPane().getStylesheets().add(PamController.getInstance().getGuiManagerFX().getPamSettingsCSS()); // } PamStylesManagerFX stylesManager = PamStylesManagerFX.getPamStylesManagerFX(); - this.getDialogPane().getStylesheets().add(stylesManager.getCurStyle().getDialogCSS()); + this.getDialogPane().getStylesheets().addAll(stylesManager.getCurStyle().getDialogCSS()); this.setOnShown((value)->{ settingsPane.paneInitialized(); }); diff --git a/src/pamViewFX/fxNodes/pamScrollers/AbstractPamScrollerFX.java b/src/pamViewFX/fxNodes/pamScrollers/AbstractPamScrollerFX.java index 376f247c..bedb8603 100644 --- a/src/pamViewFX/fxNodes/pamScrollers/AbstractPamScrollerFX.java +++ b/src/pamViewFX/fxNodes/pamScrollers/AbstractPamScrollerFX.java @@ -158,7 +158,7 @@ public abstract class AbstractPamScrollerFX extends AbstractPamScroller implemen navigationdialog.setParams(this.getScrollerData()); PamSettingsDialogFX settingsDialog=new PamSettingsDialogFX(navigationdialog); - settingsDialog.getDialogPane().getStylesheets().add(PamController.getInstance().getGuiManagerFX().getPamSettingsCSS()); + settingsDialog.getDialogPane().getStylesheets().addAll(PamController.getInstance().getGuiManagerFX().getPamSettingsCSS()); Optional newData=settingsDialog.showAndWait(); //PamScrollerData newData=NavigationDialog.showDialog(null, false, StageStyle.UNDECORATED, this); diff --git a/src/pamViewFX/fxNodes/pamScrollers/NavigationDialog.java b/src/pamViewFX/fxNodes/pamScrollers/NavigationDialog.java index 38220dde..92384cf1 100644 --- a/src/pamViewFX/fxNodes/pamScrollers/NavigationDialog.java +++ b/src/pamViewFX/fxNodes/pamScrollers/NavigationDialog.java @@ -101,7 +101,7 @@ public class NavigationDialog extends SettingsPane { //FIXME for some reason when styling with settings CSS the dialog has strange resizing issues. This is due //to padding being set in the CSS- have fixed now but kept commented code to show workaround if ever needed in future. - outerPane.getStylesheets().add(PamController.getInstance().getGuiManagerFX().getPamSettingsCSS()); + outerPane.getStylesheets().addAll(PamController.getInstance().getGuiManagerFX().getPamSettingsCSS()); // this.getDialogPane().getStylesheets().add(PamController.getInstance().getGuiManagerFX().getPamCSS()); // this.getDialogPane().getStyleClass().add("dialog-pane-dark"); // this.getDialogPane().lookupButton(ButtonType.OK).getStyleClass().add("dialog-button-dark"); diff --git a/src/pamViewFX/fxNodes/popOver/PamPopOver.java b/src/pamViewFX/fxNodes/popOver/PamPopOver.java index d6460564..ecf1981d 100644 --- a/src/pamViewFX/fxNodes/popOver/PamPopOver.java +++ b/src/pamViewFX/fxNodes/popOver/PamPopOver.java @@ -33,7 +33,7 @@ public class PamPopOver extends PopOver { public PamPopOver(Pane content) { super(); super.setContentNode(makeResizableContent(content)); - content.setMaxSize(5000, 5000); + content.setMaxSize(2000, 2000); } /** diff --git a/src/pamViewFX/fxNodes/table/TableSettingsPane.java b/src/pamViewFX/fxNodes/table/TableSettingsPane.java index c0c15ee3..55ca656b 100644 --- a/src/pamViewFX/fxNodes/table/TableSettingsPane.java +++ b/src/pamViewFX/fxNodes/table/TableSettingsPane.java @@ -1,6 +1,7 @@ package pamViewFX.fxNodes.table; import pamViewFX.fxNodes.PamBorderPane; +import atlantafx.base.theme.Styles; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.geometry.Orientation; @@ -9,6 +10,8 @@ import javafx.scene.control.TableRow; import javafx.scene.control.TableView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; +import javafx.geometry.Insets; + /** * Pane which holds data within a table and has add, settings and delete buttons. @@ -33,6 +36,7 @@ public abstract class TableSettingsPane extends PamBorderPane { public TableSettingsPane(ObservableList data){ this.data=data; table = new TableView(); + this.setCenter(createPane()); } @@ -70,6 +74,7 @@ public abstract class TableSettingsPane extends PamBorderPane { //create pane holding add, edit and remove controls buttonPane=new TableButtonPane(Orientation.VERTICAL); + buttonPane.getAddButton().setOnAction((event)->{ createNewData(); }); @@ -87,8 +92,8 @@ public abstract class TableSettingsPane extends PamBorderPane { //make sure table resized with pane to stop blank column - getTableView().setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - + getTableView().setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + getButtonPane().getSettingsButton().setDisable(table.getItems().size()<=0); getButtonPane().getDeleteButton().setDisable(table.getItems().size()<=0); @@ -146,6 +151,11 @@ public abstract class TableSettingsPane extends PamBorderPane { public TableButtonPane getButtonPane() { return buttonPane; } + + + public ObservableList getData() { + return data; + } diff --git a/src/pamViewFX/fxNodes/utilityPanes/FilterPaneFX.java b/src/pamViewFX/fxNodes/utilityPanes/FilterPaneFX.java index 33eb11c1..d546f855 100644 --- a/src/pamViewFX/fxNodes/utilityPanes/FilterPaneFX.java +++ b/src/pamViewFX/fxNodes/utilityPanes/FilterPaneFX.java @@ -242,13 +242,17 @@ public class FilterPaneFX extends SettingsPane { // freqList.add(defaultFrequencyVals[i]); // } - highCut=new PamSpinner(10.,500000.,2000.,2000.); + highCut=new PamSpinner(2.,500000.,2000.,2000.); highCut.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); highCut.getValueFactory().valueProperty().addListener((obs, before, after)->{ - if (after>=lowPass.getValue()) highCut.getValueFactory().setValue(Math.max(10,lowPass.getValue()-100)); + + if (after>=lowPass.getValue() && !lowPass.isDisable()) highCut.getValueFactory().setValue(Math.max(10,lowPass.getValue()-100)); if (after>sampleRate/2.) highCut.getValueFactory().setValue(sampleRate/2.); + filterParams.highPassFreq=highCut.getValue().floatValue(); - this.updateBodeGraph(); + + enableControls(); + updateBodeGraph(); }); highCut.setEditable(true); //highCut.setPrefColumnCount(6); @@ -256,15 +260,20 @@ public class FilterPaneFX extends SettingsPane { gridPaneFreq.add(highCut, 1, 0); gridPaneFreq.add(new Label("Hz"), 2, 0); - lowPass=new PamSpinner(10.,500000.,2000.,2000.); + lowPass=new PamSpinner(1.,500000.,2000.,2000.); lowPass.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + lowPass.getValueFactory().valueProperty().addListener((obs, before, after)->{ - if (after<=highCut.getValue()) lowPass.getValueFactory().setValue(Math.min(sampleRate/2.,highCut.getValue()+100)); + + if (after<=highCut.getValue() && !highCut.isDisable()) lowPass.getValueFactory().setValue(Math.min(sampleRate/2.,highCut.getValue()+100)); if (after>sampleRate/2.) lowPass.getValueFactory().setValue(sampleRate/2.); - filterParams.lowPassFreq=lowPass.getValue().floatValue(); + + filterParams.lowPassFreq=after.floatValue(); + enableControls(); - this.updateBodeGraph(); + updateBodeGraph(); }); + lowPass.setEditable(true); //lowCut.setPrefColumnCount(6); gridPaneFreq.add(new Label("Low Pass"), 0, 1); @@ -416,12 +425,15 @@ public class FilterPaneFX extends SettingsPane { logScale = new PamRadioButton("Log Scale"); logScale.setOnAction((action)->{ setGraphLogAxis(logScale.isSelected()); + updateBodeGraph(); }); + logScale.setToggleGroup(group); linScale = new PamRadioButton("Linear Scale"); linScale.setToggleGroup(group); linScale.setOnAction((action)->{ setGraphLogAxis(!linScale.isSelected()); + updateBodeGraph(); }); final PamHBox scalePane=new PamHBox(); @@ -466,7 +478,7 @@ public class FilterPaneFX extends SettingsPane { public void changed(ObservableValue observable, Number oldValue, Number newValue) { if (axisInitialised) { - updateBodeGraph(); + if (filterMethod!=null) updateBodeGraph(); axisInitialised=false; } } @@ -482,7 +494,6 @@ public class FilterPaneFX extends SettingsPane { @Override public void setParams(FilterParams filterParams) { this.filterParams=filterParams.clone(); - setSettings(); } @@ -490,7 +501,7 @@ public class FilterPaneFX extends SettingsPane { * Set controls to input params. */ private void setSettings() { - + if (filterParams == null) { filterParams = new FilterParams(); } @@ -529,11 +540,11 @@ public class FilterPaneFX extends SettingsPane { break; } - // highCut.setText(String.format("%1.1f", filterParams.highPassFreq)); - highCut.getValueFactory().setValue(Double.valueOf(Float.valueOf(filterParams.highPassFreq).doubleValue())); + highCut.getValueFactory().setValue(Double.valueOf(filterParams.highPassFreq)); // lowCut.setText(String.format("%1.1f", filterParams.lowPassFreq)); - lowPass.getValueFactory().setValue(Double.valueOf(Float.valueOf(filterParams.lowPassFreq).doubleValue())); + lowPass.getValueFactory().setValue(Double.valueOf(filterParams.lowPassFreq)); + filterOrder.getValueFactory().setValue(filterParams.filterOrder); setRippleParam(); @@ -689,6 +700,8 @@ public class FilterPaneFX extends SettingsPane { * @return a data series to add to the line chart */ private Series createFilterPoints(ValueAxis axis){ + + if (filterMethod==null) return null; Series series = new Series(); /* diff --git a/src/pamViewFX/fxNodes/utilityPanes/GroupedChannelBox.java b/src/pamViewFX/fxNodes/utilityPanes/GroupedChannelBox.java new file mode 100644 index 00000000..e752f9a8 --- /dev/null +++ b/src/pamViewFX/fxNodes/utilityPanes/GroupedChannelBox.java @@ -0,0 +1,54 @@ +package pamViewFX.fxNodes.utilityPanes; + +import org.controlsfx.control.CheckComboBox; + +import PamUtils.PamArrayUtils; +import PamView.GroupedSourceParameters; + +/** + * A ComboBox which shows a selectable list of channel groups. + * + * @author Jamie Macaulay + * + */ +public class GroupedChannelBox extends CheckComboBox { + + + /** + * The last set grouped parameters. + */ + GroupedSourceParameters params; + + public GroupedSourceParameters getGroupedParams() { + return params; + } + + /** + * Set the channel grouping for the check box. + * @param params - the parameters. + */ + public void setSource(GroupedSourceParameters params) { + + this.params = params; + + this.getItems().clear(); + + for (int i=0; i { if (!isAChannelSelected() ) { c.error("At least one channel needs to be selected for the module to work"); @@ -158,7 +150,7 @@ public class GroupedSourcePaneFX extends SourcePaneFX { for (int i = 0; i < PamConstants.MAX_CHANNELS; i++){ channelBoxes[i] = new CheckBox("Channel " + i); channelValidator.createCheck() - .dependsOn(("channel " + i), channelBoxes[i].selectedProperty()) + .dependsOn(("channel_" + i + "_" + this), channelBoxes[i].selectedProperty()) .withMethod(c -> { if (!isAChannelSelected() ) { c.error("At least one channel needs to be selected for the module to work"); @@ -197,34 +189,6 @@ public class GroupedSourcePaneFX extends SourcePaneFX { } - /** - * Get the validator for the channel. This can identify errors - * in - * @return - */ - public Validator getChannelValidator() { - return channelValidator; - } - - /** - * Check if - * @return - */ - private boolean isAChannelSelected() { - int channels = 0; - PamDataBlock sb = getSource(); - if (sb != null) { - // channels = sb.getChannelMap(); - channels = sb.getSequenceMap(); - } - int n=0; - //remove all channels from vertical box pane. - for (int i = 0; i < Math.min(PamConstants.MAX_CHANNELS, channelBoxes.length); i++) { - if ((channels & 1<{ public LatLongPane(String title) { super(null); - - + + mainPane = new PamVBox(); mainPane.setSpacing(5); mainPane.setAlignment(Pos.CENTER); - + Label titleLabel = new Label(title); titleLabel.maxWidth(Double.MAX_VALUE); titleLabel.setTextAlignment(TextAlignment.LEFT); + titleLabel.setAlignment(Pos.CENTER_LEFT); PamGuiManagerFX.titleFont2style(titleLabel); mainPane.getChildren().add(titleLabel); @@ -109,7 +110,7 @@ public class LatLongPane extends SettingsPane{ cent.getChildren().add(latStrip = new LatLongStrip(true)); cent.getChildren().add(longStrip = new LatLongStrip(false)); - + //bit of a hack that makes sure controls are aligned for the latitude and longitude. latStrip.getTitleLabel().prefWidthProperty().bind(longStrip.getTitleLabel().widthProperty()); @@ -123,46 +124,24 @@ public class LatLongPane extends SettingsPane{ * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(javafx.event.ActionEvent action) { - - - if (action.getSource() == decimalMinutes) { - LatLong.setFormatStyle(LatLong.FORMAT_DECIMALMINUTES); - // if (latStrip != null) { - // latStrip.setDecimalMinutes(true); - // longStrip.setDecimalMinutes(true); - // } - latStrip.showControls(LatLong.FORMAT_DECIMALMINUTES); - longStrip.showControls(LatLong.FORMAT_DECIMALMINUTES); - } - else if (action.getSource() == minutesSeconds){ - LatLong.setFormatStyle(LatLong.FORMAT_MINUTESSECONDS); - // if (latStrip != null) { - // latStrip.setDecimalMinutes(false); - // longStrip.setDecimalMinutes(false); - //// latStrip.showControls(); - //// longStrip.showControls(); - // } - latStrip.showControls(LatLong.FORMAT_MINUTESSECONDS); - longStrip.showControls(LatLong.FORMAT_MINUTESSECONDS); - } - else if (action.getSource() == decimal){ - LatLong.setFormatStyle(LatLong.FORMAT_DECIMAL); - latStrip.showControls(LatLong.FORMAT_DECIMAL); - longStrip.showControls(LatLong.FORMAT_DECIMAL); - } - + + int format = getSelectedLatLongFormat(); + + LatLong.setFormatStyle(format); + latStrip.showControls(format); + longStrip.showControls(format); } private void showLatLong() { - + decimalMinutes .setSelected(LatLong.getFormatStyle() == LatLong.FORMAT_DECIMALMINUTES); minutesSeconds .setSelected(LatLong.getFormatStyle() == LatLong.FORMAT_MINUTESSECONDS); decimal .setSelected(LatLong.getFormatStyle() == LatLong.FORMAT_DECIMAL); - latStrip .showControls(LatLong.getFormatStyle() ); - longStrip .showControls(LatLong.getFormatStyle() ); + latStrip .showControls(LatLong.getFormatStyle()); + longStrip .showControls(LatLong.getFormatStyle()); latStrip .setValue(latLong.getLatitude()); longStrip .setValue(latLong.getLongitude()); } @@ -174,21 +153,11 @@ public class LatLongPane extends SettingsPane{ */ @Override public LatLong getParams(LatLong currentParams) { - - Toggle selectedButton = this.segmentedButton.getToggleGroup().getSelectedToggle(); - - if (selectedButton == decimalMinutes) { - LatLong.setFormatStyle(LatLong.FORMAT_DECIMALMINUTES); - } - else if (selectedButton == minutesSeconds){ - LatLong.setFormatStyle(LatLong.FORMAT_MINUTESSECONDS); - - } - else if (selectedButton == decimal){ - LatLong.setFormatStyle(LatLong.FORMAT_DECIMAL); - } - + int format = getSelectedLatLongFormat(); + + LatLong.setFormatStyle(format); + latLong = new LatLong(latStrip.getValue(), longStrip.getValue()); if (Double.isNaN(latLong.getLatitude()) || Double.isNaN(latLong.getLongitude())) { return null; @@ -196,6 +165,26 @@ public class LatLongPane extends SettingsPane{ return latLong; } + /** + * Get the selected format for showing latitude and longitude values. + * @return the selected format flag or -1 if no format is selected. + */ + private int getSelectedLatLongFormat() { + Toggle selectedButton = this.segmentedButton.getToggleGroup().getSelectedToggle(); + if (selectedButton == decimalMinutes) { + return LatLong.FORMAT_DECIMALMINUTES; + + } + else if (selectedButton == minutesSeconds){ + return LatLong.FORMAT_MINUTESSECONDS; + + } + else if (selectedButton == decimal){ + return LatLong.FORMAT_DECIMAL; + } + return -1; + } + @Override public void setParams(LatLong input) { this.latLong=input; diff --git a/src/pamViewFX/fxNodes/utilityPanes/LatLongStrip.java b/src/pamViewFX/fxNodes/utilityPanes/LatLongStrip.java index 2e80e8f0..915de2d9 100644 --- a/src/pamViewFX/fxNodes/utilityPanes/LatLongStrip.java +++ b/src/pamViewFX/fxNodes/utilityPanes/LatLongStrip.java @@ -12,7 +12,7 @@ import pamViewFX.fxNodes.PamHBox; /** * A pane which shows a latitude or longitude. Allows users to enter data as decimal or minutes/seconds. - * (copied ot FX from Doug Gillespie's LatLongDialogStrip) + * (copied to FX from Doug Gillespie's LatLongDialogStrip) * @author Jamie Macaulay * */ @@ -39,6 +39,7 @@ public class LatLongStrip extends PamBorderPane { * The format type e.g. LatLong.FORMAT_DECIMALMINUTES. */ private int formatType = LatLong.FORMAT_DECIMALMINUTES; + private Label titleLabel; @@ -152,7 +153,7 @@ public class LatLongStrip extends PamBorderPane { this.setCenter(holder); this.setBottom(formattedText); - showControls(formatType); + showControls(formatType, true); } private void newTypedValues(KeyEvent e) { @@ -170,10 +171,25 @@ public class LatLongStrip extends PamBorderPane { // and say the formated version sayFormattedValue(v); } - + + + /** + * Change the current controls for to show to show the current format of Latitude or Longitude. + * @param formatStyle - the style of Latitude or longitude e.g. LatLong.FORMAT_DECIMALMINUTES; + */ public void showControls(int formatStyle) { - if (formatType==formatStyle) { + showControls(formatStyle, false); + } + + /** + * Change the current controls for to show to show the current format of Latitude or Longitude. + * @param force - force a reset of controls even if the format style is the same as the current style. + * @param formatStyle - the style of Latitude or longitude e.g. LatLong.FORMAT_DECIMALMINUTES; + */ + private void showControls(int formatStyle, boolean force) { + + if (formatType==formatStyle && !force) { return; } @@ -223,7 +239,7 @@ public class LatLongStrip extends PamBorderPane { public void setValue(double value, boolean hiddenOnly) { - System.out.println("Set value: " + value); +// System.out.println("Set value: " + value); if (value >= 0) { nsew.getSelectionModel().select(0); } diff --git a/src/pamViewFX/fxNodes/utilityPanes/SettingsDialog.java b/src/pamViewFX/fxNodes/utilityPanes/SettingsDialog.java index 7b8b88ca..96c4fe20 100644 --- a/src/pamViewFX/fxNodes/utilityPanes/SettingsDialog.java +++ b/src/pamViewFX/fxNodes/utilityPanes/SettingsDialog.java @@ -2,8 +2,9 @@ package pamViewFX.fxNodes.utilityPanes; import javafx.stage.Stage; import javafx.stage.StageStyle; +import pamViewFX.PamGuiManagerFX; import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; -import PamController.PamController; +import pamViewFX.fxStyles.PamStylesManagerFX; import PamController.SettingsPane; /** @@ -19,24 +20,23 @@ public class SettingsDialog extends PamDialogFX{ private SettingsPane settingsPane; public SettingsDialog(SettingsPane settingsPane){ - super(null, settingsPane.getName(), StageStyle.DECORATED); + super(PamGuiManagerFX.getInstance().getPrimaryStage() /*TODO - add stage*/, settingsPane.getName(), StageStyle.DECORATED); this.setResizable(true); this.settingsPane=settingsPane; + + this.setContent(settingsPane.getContentNode()); -// this.getDialogPane().getStylesheets().add(PamController.getInstance().getGuiManagerFX().getPamSettingsCSS()); - this.getDialogPane().getStylesheets().add(PamController.getInstance().getGuiManagerFX().getPamDialogCSS()); + Stage stage = (Stage) this.getDialogPane().getScene().getWindow(); stage.toFront(); -// //set results converter -// this.setResultConverter(dialogButton -> { -// if (dialogButton == ButtonType.OK) { -// T params = getParams(); -// if (params!=null) pamControlledUnit.setParams(params); -// } -// return null; -// }); + this.getDialogPane().getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); + +// System.out.println("SettingsPane: DIALOG " + this.getDialogPane().getStylesheets().size()); +// for (int i=0;i{ freqPane.setSampleRate(sampleRate); } + public void addValueChangeListener(Object object) { + // TODO Auto-generated method stub + + } + } diff --git a/src/pamViewFX/fxNodes/utilityPanes/SourcePaneFX.java b/src/pamViewFX/fxNodes/utilityPanes/SourcePaneFX.java index edfece24..f421cc34 100644 --- a/src/pamViewFX/fxNodes/utilityPanes/SourcePaneFX.java +++ b/src/pamViewFX/fxNodes/utilityPanes/SourcePaneFX.java @@ -15,15 +15,15 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.scene.text.Font; import javafx.stage.Window; -import pamViewFX.PamGuiManagerFX; import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxNodes.pamDialogFX.PamDialogFX2AWT; +import pamViewFX.validator.PamValidator; import PamController.PamController; +import PamController.PamGUIManager; import PamDetection.LocalisationInfo; import PamUtils.PamUtils; import PamView.dialog.SourcePanelMonitor; @@ -42,7 +42,6 @@ import PamguardMVC.PamDataBlock; @SuppressWarnings("rawtypes") public class SourcePaneFX extends PamBorderPane { - private PamBorderPane panel; private ArrayList sourceType = new ArrayList<>(); private boolean hasChannels; private String borderTitle; @@ -84,6 +83,12 @@ public class SourcePaneFX extends PamBorderPane { */ private Label titleLabel; + /** + * Validator for channels + */ + protected PamValidator channelValidator; + private Label channelLabel; + /** * Construct a panel with a titles border * @param borderTitle Title to go in border @@ -95,6 +100,7 @@ public class SourcePaneFX extends PamBorderPane { if (sourceType != null) { this.sourceType.add(new SourceSelection(sourceType, includeSubClasses)); } + channelValidator = new PamValidator(); this.setHasChannels(hasChannels); this.setBorderTitle(borderTitle); createPanel(); @@ -109,6 +115,7 @@ public class SourcePaneFX extends PamBorderPane { */ public SourcePaneFX(Class sourceType, boolean hasChannels, boolean includeSubClasses) { this.sourceType.add(new SourceSelection(sourceType, includeSubClasses)); + channelValidator = new PamValidator(); this.setHasChannels(hasChannels); createPanel(); setSourceList(); @@ -147,6 +154,7 @@ public class SourcePaneFX extends PamBorderPane { * Add a listener to the data source drop down list * @param listener listener */ + @SuppressWarnings("unchecked") public void addSelectionListener(ChangeListener listener) { sourceList.valueProperty().addListener(listener); } @@ -158,7 +166,7 @@ public class SourcePaneFX extends PamBorderPane { protected void createPanel() { - + PamVBox comboBoxPane=new PamVBox(); comboBoxPane.setSpacing(5); @@ -183,7 +191,7 @@ public class SourcePaneFX extends PamBorderPane { channelPanel.setSpacing(5); if (isHasChannels()) { - Label channelLabel = new Label("Channel"); + channelLabel = new Label("ChannelS"); //PamGuiManagerFX.titleFont2style(channelLabel); // channelLabel.setFont(PamGuiManagerFX.titleFontSize2); comboBoxPane.getChildren().add(channelLabel); @@ -191,8 +199,11 @@ public class SourcePaneFX extends PamBorderPane { channelPanel.getChildren().add(selectAll =new CheckBox("All")); channelBoxes =new CheckBox[PamConstants.MAX_CHANNELS]; selectAll.setOnAction((action)->{ + System.out.println("Stylesheets: 0: " + getStylesheets().size()); if (selectAll.isSelected()) selectAllChannels(); else selectNoChannels(); + channelValidator.validate(); //makes sure any error signs are removed. + System.out.println("Stylesheets: 1: " + getStylesheets().size()); }); } @@ -204,10 +215,21 @@ public class SourcePaneFX extends PamBorderPane { if (isHasChannels()){ for (int i = 0; i < PamConstants.MAX_CHANNELS; i++){ channelBoxes[i] = new CheckBox("Channel " + i); + channelValidator.createCheck() + .dependsOn(("channel_" + i + "_" + this), channelBoxes[i].selectedProperty()) + .withMethod(c -> { + if (!isAChannelSelected() ) { + c.error("At least one channel needs to be selected for the module to work"); + } + }) + .decorates(channelBoxes[i]) + .immediate(); + //channelPanel.getChildren().add(channelBoxes[i]); final int n=i; channelBoxes[i].setOnAction((action)->{ selectionChanged(n); + channelValidator.validate(); //makes sure any error signs are removed. }); //System.out.println("SourcePanel.java creatPanel"+i); } @@ -220,6 +242,31 @@ public class SourcePaneFX extends PamBorderPane { } + + public Label getChannelLabel() { + return channelLabel; + } + + /** + * Check if a single channel is selected. + * @return true if at least one channel is selected. + */ + public boolean isAChannelSelected() { + int channels = 0; + PamDataBlock sb = getSource(); + if (sb != null) { + // channels = sb.getChannelMap(); + channels = sb.getSequenceMap(); + } + int n=0; + //remove all channels from vertical box pane. + for (int i = 0; i < Math.min(PamConstants.MAX_CHANNELS, channelBoxes.length); i++) { + if ((channels & 1< getDataBlockBox() { return this.sourceList; - + + } + + + /** + * Get the channel validator for the source pane. + * @return the channel validator + */ + public PamValidator getChannelValidator() { + return channelValidator; } } diff --git a/src/pamViewFX/fxPlotPanes/PamHiddenSidePane.java b/src/pamViewFX/fxPlotPanes/PamHiddenSidePane.java index cf76d5a8..bee30dfa 100644 --- a/src/pamViewFX/fxPlotPanes/PamHiddenSidePane.java +++ b/src/pamViewFX/fxPlotPanes/PamHiddenSidePane.java @@ -186,36 +186,37 @@ public class PamHiddenSidePane extends PamStackPane { public HidingPane createHidingPane(Region displayPane, Node icon, Side side){ //create the hiding pane HidingPane hidingPane=new HidingPane(side, displayPane, this, true); - hidingPane.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); + hidingPane.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); hidingPane.getStyleClass().add("pane-trans"); //the stack pane holds all the different settings panes this.getChildren().add(hidingPane); PamButton showButton=hidingPane.getShowButton(); - showButton.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); + showButton.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); + showButton.setPrefHeight(60); + //style show button showButton.setGraphic(icon); switch(side){ case LEFT: StackPane.setAlignment(hidingPane, Pos.TOP_LEFT); StackPane.setAlignment(showButton, Pos.TOP_LEFT); - showButton.getStyleClass().add("close-button-right-trans"); + showButton.getStyleClass().add("close-button-right"); break; case RIGHT: StackPane.setAlignment(hidingPane, Pos.TOP_RIGHT); StackPane.setAlignment(showButton, Pos.TOP_RIGHT); - showButton.getStyleClass().add("close-button-left-trans"); + showButton.getStyleClass().add("close-button-left"); break; default: break; } //make show button same height as hide button - showButton.prefHeightProperty().bind(hidingPane.getHideButton().heightProperty()); +// showButton.prefHeightProperty().bind(hidingPane.getHideButton().heightProperty()); //translate it so it sits slightly below the top of the pane. - showButton.translateYProperty().setValue(yPos); - showButton.prefHeightProperty().bind(hidingPane.getHideButton().heightProperty()); + showButton.translateYProperty().bind(displayPane.heightProperty().divide(2).subtract(showButton.heightProperty().divide(2))); hidingPane.getHideButton().translateYProperty().setValue(yPos); diff --git a/src/pamViewFX/fxPlotPanes/PlotPane.java b/src/pamViewFX/fxPlotPanes/PlotPane.java index da03a046..6f62eb2f 100644 --- a/src/pamViewFX/fxPlotPanes/PlotPane.java +++ b/src/pamViewFX/fxPlotPanes/PlotPane.java @@ -156,7 +156,9 @@ public class PlotPane extends PamBorderPane { //create the plot pane. canvasHolder=new PamBorderPane(); - + canvasHolder.setMaxWidth(4000); + canvasHolder.setMaxHeight(4000); + canvas = new Canvas(50, 50); canvas.heightProperty().bind(canvasHolder.heightProperty()); canvas.widthProperty().bind(canvasHolder.widthProperty()); diff --git a/src/pamViewFX/fxSettingsPanes/SettingsFileDialogFX.java b/src/pamViewFX/fxSettingsPanes/SettingsFileDialogFX.java index fba85899..03ed28b8 100644 --- a/src/pamViewFX/fxSettingsPanes/SettingsFileDialogFX.java +++ b/src/pamViewFX/fxSettingsPanes/SettingsFileDialogFX.java @@ -39,21 +39,27 @@ public class SettingsFileDialogFX { singleInstance = new SettingsFileDialogFX(); singleInstance.settingsDialog=new PamSettingsDialogFX(singleInstance.settingsFilePane); // singleInstance.settingsDialog.getDialogPane().getStylesheets().add(singleInstance.getClass().getResource("/Resources/css/pamSettingsCSS.css").toExternalForm()); - singleInstance.settingsDialog.getDialogPane().getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); + singleInstance.settingsDialog.getDialogPane().getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); } singleInstance.settingsFilePane.setParams(settingsFileData); singleInstance.settingsDialog.setOnShown((value)->{ + +// singleInstance.settingsFilePane.getProgressBar().setVisible(startup); +// singleInstance.settingsFilePane.getProgressBar().setProgress(-1);; + singleInstance.settingsFilePane.paneInitialized(); //fix to make sure the dialog appearsa in pre PG GUI FX insitialisation i.e. when selecting viewer database //on PG start up/. ((Stage) singleInstance.settingsDialog.getDialogPane().getScene().getWindow()).setAlwaysOnTop(true); }); + + Optional result=singleInstance.settingsDialog.showAndWait(); - System.out.println("Hello FX result: "); + //System.out.println("Hello FX result: "); if (result==null || !result.isPresent()) { if (startup) { @@ -62,6 +68,7 @@ public class SettingsFileDialogFX { } else return null; } + SettingsFileData settings=(SettingsFileData) result.get(); return settings; diff --git a/src/pamViewFX/fxSettingsPanes/SettingsFilePane.java b/src/pamViewFX/fxSettingsPanes/SettingsFilePane.java index c9c0d10b..5cd38990 100644 --- a/src/pamViewFX/fxSettingsPanes/SettingsFilePane.java +++ b/src/pamViewFX/fxSettingsPanes/SettingsFilePane.java @@ -6,6 +6,7 @@ import javafx.scene.Node; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; +import javafx.scene.control.ProgressBar; import javafx.scene.paint.Color; import javafx.stage.FileChooser; import javafx.stage.FileChooser.ExtensionFilter; @@ -43,6 +44,8 @@ public class SettingsFilePane extends SettingsPane { private PamBorderPane mainPane = new PamBorderPane(); + private ProgressBar progressBar; + public SettingsFilePane(){ super(null); createSettingsLoadPane(); @@ -99,7 +102,21 @@ public class SettingsFilePane extends SettingsPane { // vBox.getChildren().add(alwaysShow); mainPane.setCenter(vBox); + + progressBar = new ProgressBar(); + progressBar.setVisible(false); + + mainPane.setBottom(progressBar); + + } + + /** + * Get the progress bar. This is default set to not visible and can be used to show loading settings. + * @return the progress bar. + */ + public ProgressBar getProgressBar() { + return progressBar; } /** diff --git a/src/pamViewFX/fxStyles/PamAtlantaStyle.java b/src/pamViewFX/fxStyles/PamAtlantaStyle.java index d0c7182a..892558d9 100644 --- a/src/pamViewFX/fxStyles/PamAtlantaStyle.java +++ b/src/pamViewFX/fxStyles/PamAtlantaStyle.java @@ -1,5 +1,12 @@ package pamViewFX.fxStyles; +import java.util.ArrayList; + +import PamView.ColourScheme; +import PamView.PamColors; +import atlantafx.base.theme.PrimerDark; +import atlantafx.base.theme.PrimerLight; + /* * PAMGUARD - Passive Acoustic Monitoring GUARDianship. * To assist in the Detection Classification and Localisation @@ -23,46 +30,95 @@ package pamViewFX.fxStyles; */ /** - * Class defining the default CSS Style sheets to use for JavaFX displays. This class can be extended and one or more methods overridden to - * specify new CSS styles. Style sheets can be specified for 3 different categories: sliding dialogs, regular dialogs, and all other components - * (incl. displays, etc). In addition, each category can have a style sheet to use for daytime mode and one to use for nighttime mode. The - * relative URI paths to the individual style sheets are specified as private fields, and accessed through public methods. The day/night switch - * is based on the name of the current PamColors colour scheme being used. + * Class defining the default CSS Style sheets to use for JavaFX displays. This + * class can be extended and one or more methods overridden to specify new CSS + * styles. Style sheets can be specified for 3 different categories: sliding + * dialogs, regular dialogs, and all other components (incl. displays, etc). In + * addition, each category can have a style sheet to use for daytime mode and + * one to use for nighttime mode. The relative URI paths to the individual style + * sheets are specified as private fields, and accessed through public methods. + * The day/night switch is based on the name of the current PamColors colour + * scheme being used. * * @author Jamie Macaulay * */ public class PamAtlantaStyle extends PamDefaultStyle { - + /** - * Relative location of the CSS style sheet to be used for the Pamguard GUI (but not dialogs) + * Relative location of the CSS style sheet to be used for the Pamguard GUI (but + * not dialogs) */ - //private String guiCSS = "/Resources/css/pamCSS.css"; - //private String guiCSS = new NordDark().getUserAgentStylesheet(); - protected String primerGuiCSS = "/Resources/css/primer-light.css"; - - + // private String guiCSS = "/Resources/css/pamCSS.css"; + // private String guiCSS = new NordDark().getUserAgentStylesheet(); +// protected String primerlight = "/Resources/css/primer-light.css"; + protected String primerlight = new PrimerLight().getUserAgentStylesheet(); + /** - * Relative location of the CSS style sheet to be used for the Pamguard standard dialogs + * Relative location of the CSS style sheet to be used for the Pamguard standard + * dialogs */ - //private String dialogCSS = "/Resources/css/pamSettingsCSS.css"; - //private String dialogCSS = new PrimerDark().getUserAgentStylesheet(); - protected String primerDialogCSS = "/Resources/css/primer-dark.css"; + // private String dialogCSS = "/Resources/css/pamSettingsCSS.css"; + // private String dialogCSS = new PrimerDark().getUserAgentStylesheet(); +// protected String primerdark = "/Resources/css/primer-dark.css"; + protected String primerdark = new PrimerDark().getUserAgentStylesheet(); /** - * Relative location of the CSS style sheet to be used for the Pamguard sliding dialogs + * PAMGuard specific additions to the Primer CSS style including hifing pane, pop over, etc. */ - //private String slidingDialogCSS = "/Resources/css/pamCSS.css"; - //private String slidingDialogCSS = new PrimerDark().getUserAgentStylesheet(); - protected String primerSlidingDialogCSS = "/Resources/css/primer-pamguard.css"; + protected String primerPAMGuard = "/Resources/css/primer-pamguard.css"; + /** + * Changes the colours in primerPAMGuard to dark style + */ + protected String primerPAMGuardDark = "/Resources/css/primer-pamguard-dark.css"; + public PamAtlantaStyle() { - super.guiCSS = primerGuiCSS; - super.dialogCSS = primerDialogCSS; - super.slidingDialogCSS = primerSlidingDialogCSS; - + super.guiCSS = primerlight; + super.dialogCSS = primerdark; + super.slidingDialogCSS = primerPAMGuard; } + @Override + public ArrayList getGUICSS() { + ArrayList cssStyles = new ArrayList(); + if (PamColors.getInstance().getColourScheme().getName() == ColourScheme.NIGHTSCHEME + && guiCSSNightMode != null) { + cssStyles.add(getClass().getResource(primerdark).toExternalForm()); + } else { + cssStyles.add(getClass().getResource(primerlight).toExternalForm()); + } + cssStyles.add(getClass().getResource(primerPAMGuard).toExternalForm()); + return cssStyles; + } + + + @Override + public ArrayList getDialogCSS() { + ArrayList cssStyles = new ArrayList(); + if (PamColors.getInstance().getColourScheme().getName() == ColourScheme.NIGHTSCHEME + && dialogCSSNightMode != null) { + cssStyles.add(getClass().getResource(primerdark).toExternalForm()); + } else { + cssStyles.add(getClass().getResource(primerdark).toExternalForm()); + } + cssStyles.add(getClass().getResource(primerPAMGuard).toExternalForm()); + cssStyles.add(getClass().getResource(primerPAMGuardDark).toExternalForm()); + return cssStyles; + } + + @Override + public ArrayList getSlidingDialogCSS() { + ArrayList cssStyles = new ArrayList(); + if (PamColors.getInstance().getColourScheme().getName() == ColourScheme.NIGHTSCHEME + && slidingDialogCSSNightMode != null) { + cssStyles.add(getClass().getResource(primerPAMGuardDark).toExternalForm()); + } else { + cssStyles.add(getClass().getResource(primerPAMGuardDark).toExternalForm()); + } + return cssStyles; + } + } diff --git a/src/pamViewFX/fxStyles/PamDefaultStyle.java b/src/pamViewFX/fxStyles/PamDefaultStyle.java index d05fe023..279a870f 100644 --- a/src/pamViewFX/fxStyles/PamDefaultStyle.java +++ b/src/pamViewFX/fxStyles/PamDefaultStyle.java @@ -20,101 +20,137 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - - package pamViewFX.fxStyles; +import java.util.ArrayList; + import PamView.ColourScheme; import PamView.PamColors; /** - * Class defining the default CSS Style sheets to use for JavaFX displays. This class can be extended and one or more methods overridden to - * specify new CSS styles. Style sheets can be specified for 3 different categories: sliding dialogs, regular dialogs, and all other components - * (incl. displays, etc). In addition, each category can have a style sheet to use for daytime mode and one to use for nighttime mode. The - * relative URI paths to the individual style sheets are specified as private fields, and accessed through public methods. The day/night switch - * is based on the name of the current PamColors colour scheme being used. + * Class defining the default CSS Style sheets to use for JavaFX displays. This + * class can be extended and one or more methods overridden to specify new CSS + * styles. Style sheets can be specified for 3 different categories: sliding + * dialogs, regular dialogs, and all other components (incl. displays, etc). In + * addition, each category can have a style sheet to use for daytime mode and + * one to use for nighttime mode. The relative URI paths to the individual style + * sheets are specified as private fields, and accessed through public methods. + * The day/night switch is based on the name of the current PamColors colour + * scheme being used. * * @author mo55 + * @author Jamie Macaulay * */ public class PamDefaultStyle { - + /** - * Relative location of the CSS style sheet to be used for the Pamguard GUI (but not dialogs) + * Relative location of the CSS style sheet to be used for the Pamguard GUI (but + * not dialogs) */ protected String guiCSS = "/Resources/css/pamCSS.css"; - + /** - * Relative location of the CSS style sheet to be used for the Pamguard GUI when in night mode. If there - * is not a style sheet specific to night mode, set it to null or point back to the guiCSS field. + * Relative location of the CSS style sheet to be used for the Pamguard GUI when + * in night mode. If there is not a style sheet specific to night mode, set it + * to null or point back to the guiCSS field. */ protected String guiCSSNightMode = guiCSS; - + /** - * Relative location of the CSS style sheet to be used for the Pamguard standard dialogs + * Relative location of the CSS style sheet to be used for the Pamguard standard + * dialogs */ protected String dialogCSS = "/Resources/css/pamDefaultDialogCSS.css"; - + /** - * Relative location of the CSS style sheet to be used for the Pamguard std dialogs when in night mode. If there - * is not a style sheet specific to night mode, set it to null or point back to the dialogCSS field. + * Relative location of the CSS style sheet to be used for the Pamguard std + * dialogs when in night mode. If there is not a style sheet specific to night + * mode, set it to null or point back to the dialogCSS field. */ protected String dialogCSSNightMode = dialogCSS; - + /** - * Relative location of the CSS style sheet to be used for the Pamguard sliding dialogs + * Relative location of the CSS style sheet to be used for the Pamguard sliding + * dialogs */ protected String slidingDialogCSS = "/Resources/css/pamSettingsCSS.css"; - + /** - * Relative location of the CSS style sheet to be used for the Pamguard sliding dialogs when in night mode. If there - * is not a style sheet specific to night mode, set it to null or point back to the slidingDialogCSS field. + * Relative location of the CSS style sheet to be used for the Pamguard sliding + * dialogs when in night mode. If there is not a style sheet specific to night + * mode, set it to null or point back to the slidingDialogCSS field. */ protected String slidingDialogCSSNightMode = slidingDialogCSS; - + /** - *

Return the CSS Style sheet to be used for the Pamguard GUI (displays and such) but not the dialogs.

- *

If overriding this method, do not simply return a URI String. In order for the String to be in the - * proper format, the getClass.getResource... method should be used. + *

+ * Return the CSS Style sheet to be used for the Pamguard GUI (displays and + * such) but not the dialogs. + *

+ *

+ * If overriding this method, do not simply return a URI String. In order for + * the String to be in the proper format, the getClass.getResource... method + * should be used. * - * @return a String of the class containing the URI of the CSS style sheet to use + * @return an array of Strings of the class containing the URI of the CSS style + * sheet to use */ - public String getGUICSS() { - if (PamColors.getInstance().getColourScheme().getName()==ColourScheme.NIGHTSCHEME && guiCSSNightMode!=null) { - return getClass().getResource(guiCSSNightMode).toExternalForm(); + public ArrayList getGUICSS() { + ArrayList cssStyles = new ArrayList(); + if (PamColors.getInstance().getColourScheme().getName() == ColourScheme.NIGHTSCHEME + && guiCSSNightMode != null) { + cssStyles.add(getClass().getResource(guiCSSNightMode).toExternalForm()); } else { - return getClass().getResource(guiCSS).toExternalForm(); + cssStyles.add(getClass().getResource(guiCSS).toExternalForm()); } + return cssStyles; } - + /** - *

Return the CSS Style sheet to be used for the Pamguard settings dialogs.

- *

If overriding this method, do not simply return a URI String. In order for the String to be in the - * proper format, the getClass.getResource... method should be used. + *

+ * Return the CSS Style sheet to be used for the Pamguard settings dialogs. + *

+ *

+ * If overriding this method, do not simply return a URI String. In order for + * the String to be in the proper format, the getClass.getResource... method + * should be used. * - * @return a String of the class containing the URI of the CSS style sheet to use + * @return an array of Strings of the class containing the URI of the CSS style + * sheet to use */ - public String getDialogCSS() { - if (PamColors.getInstance().getColourScheme().getName()==ColourScheme.NIGHTSCHEME && dialogCSSNightMode!=null) { - return getClass().getResource(dialogCSSNightMode).toExternalForm(); + public ArrayList getDialogCSS() { + ArrayList cssStyles = new ArrayList(); + if (PamColors.getInstance().getColourScheme().getName() == ColourScheme.NIGHTSCHEME + && dialogCSSNightMode != null) { + cssStyles.add(getClass().getResource(dialogCSSNightMode).toExternalForm()); } else { - return getClass().getResource(dialogCSS).toExternalForm(); + cssStyles.add(getClass().getResource(dialogCSS).toExternalForm()); } + return cssStyles; } - + /** - *

Return the CSS Style sheet to be used for the Pamguard sliding dialogs.

- *

If overriding this method, do not simply return a URI String. In order for the String to be in the - * proper format, the getClass.getResource... method should be used. + *

+ * Return the CSS Style sheet to be used for the Pamguard sliding dialogs. + *

+ *

+ * If overriding this method, do not simply return a URI String. In order for + * the String to be in the proper format, the getClass.getResource... method + * should be used. * - * @return a String of the class containing the URI of the CSS style sheet to use + * @return an array of Strings of the class containing the URI of the CSS style + * sheet to use */ - public String getSlidingDialogCSS() { - if (PamColors.getInstance().getColourScheme().getName()==ColourScheme.NIGHTSCHEME && slidingDialogCSSNightMode!=null) { - return getClass().getResource(slidingDialogCSSNightMode).toExternalForm(); + public ArrayList getSlidingDialogCSS() { + ArrayList cssStyles = new ArrayList(); + if (PamColors.getInstance().getColourScheme().getName() == ColourScheme.NIGHTSCHEME + && slidingDialogCSSNightMode != null) { + cssStyles.add(getClass().getResource(slidingDialogCSSNightMode).toExternalForm()); } else { - return getClass().getResource(slidingDialogCSS).toExternalForm(); + cssStyles.add(getClass().getResource(slidingDialogCSS).toExternalForm()); } + return cssStyles; } } diff --git a/src/pamViewFX/pamTask/PamLoadingPane.java b/src/pamViewFX/pamTask/PamLoadingPane.java index 40860869..7b4badc2 100644 --- a/src/pamViewFX/pamTask/PamLoadingPane.java +++ b/src/pamViewFX/pamTask/PamLoadingPane.java @@ -51,7 +51,7 @@ public class PamLoadingPane extends PamVBox { /** * Time for loading before load pane showsn. */ - private long waitBeforeShiow=3000; // wait three seconds before a load + private long waitBeforeShiow=1000; // wait one seconds before a load public PamLoadingPane(PamGuiManagerFX pamGuiManager){ @@ -66,7 +66,7 @@ public class PamLoadingPane extends PamVBox { */ public void updateLoadPane(PamTaskUpdate taskUpdate){ -// System.out.println("New PAMTask Update: " + taskUpdate.getName() + " Progress: " + taskUpdate.getProgress() + " Status: "+ taskUpdate.getStatus()); +// System.out.println("New PAMTask Update: " + taskUpdate.getName() + " Progress: " + taskUpdate.getProgress() + " Status: "+ taskUpdate.getStatus() + " Progress: " + taskUpdate.getProgressString()); //first, figure out if there there is a PamTaskPane for the update; PamTaskPane pamTaskPane = null; diff --git a/src/pamguard/PamguardFX.java b/src/pamguard/PamguardFX.java index a2df26bb..4ae7dbe5 100644 --- a/src/pamguard/PamguardFX.java +++ b/src/pamguard/PamguardFX.java @@ -61,11 +61,14 @@ public class PamguardFX extends Application { + private static int runMode; + @Override public void start(Stage primaryStage) throws Exception { //this is called from launch(args) try { - PamController.create(PamController.RUN_NORMAL, primaryStage); + System.out.println("javafx.runtime.version: " + System.getProperty("javafx.runtime.version")); + PamController.create(runMode, primaryStage); } catch (Exception e) { e.printStackTrace(); @@ -89,7 +92,7 @@ public class PamguardFX extends Application { Debug.setPrintDebug(false); // make sure the class instantiates static members. - int runMode = PamController.RUN_NORMAL; + runMode = PamController.RUN_NORMAL; String InputPsf = "NULL"; @@ -273,6 +276,7 @@ public class PamguardFX extends Application { //that kicked off from with the EDT CJB 2009-06-16 PamGUIManager.setType(PamGUIManager.FX); + launch(args); } diff --git a/src/rawDeepLearningClassifier/DLControl.java b/src/rawDeepLearningClassifier/DLControl.java index 94bb056c..bb9dcc62 100644 --- a/src/rawDeepLearningClassifier/DLControl.java +++ b/src/rawDeepLearningClassifier/DLControl.java @@ -27,17 +27,22 @@ import pamViewFX.fxNodes.pamDialogFX.PamDialogFX2AWT; import rawDeepLearningClassifier.dataPlotFX.DLDetectionPlotProvider; import rawDeepLearningClassifier.dataPlotFX.DLPredictionProvider; import rawDeepLearningClassifier.ddPlotFX.RawDLDDPlotProvider; +import rawDeepLearningClassifier.defaultModels.DLDefaultModelManager; import rawDeepLearningClassifier.dlClassification.DLClassName; import rawDeepLearningClassifier.dlClassification.DLClassNameManager; import rawDeepLearningClassifier.dlClassification.DLClassiferModel; +import rawDeepLearningClassifier.dlClassification.DLClassifierChooser; import rawDeepLearningClassifier.dlClassification.DLClassifyProcess; import rawDeepLearningClassifier.dlClassification.animalSpot.SoundSpotClassifier; +import rawDeepLearningClassifier.dlClassification.archiveModel.PamZipModelClassifier; +import rawDeepLearningClassifier.dlClassification.delphinID.DelphinIDClassifier; import rawDeepLearningClassifier.dlClassification.genericModel.GenericDLClassifier; -import rawDeepLearningClassifier.dlClassification.ketos.KetosClassifier; +import rawDeepLearningClassifier.dlClassification.ketos.KetosClassifier2; +import rawDeepLearningClassifier.dlClassification.koogu.KooguClassifier; import rawDeepLearningClassifier.layoutFX.DLSidePanelSwing; import rawDeepLearningClassifier.layoutFX.DLSymbolManager; import rawDeepLearningClassifier.layoutFX.PredictionSymbolManager; -import rawDeepLearningClassifier.layoutFX.RawDLSettingsPane; +import rawDeepLearningClassifier.layoutFX.DLSettingsPane; import rawDeepLearningClassifier.logging.DLAnnotationType; import rawDeepLearningClassifier.logging.DLDataUnitDatagram; import rawDeepLearningClassifier.logging.DLDetectionBinarySource; @@ -76,7 +81,7 @@ import rawDeepLearningClassifier.segmenter.SegmenterProcess; * AnimalSpot is a framework for training acoustic deep learning * models using Pytorch. Users can load a .py model which contains embedded * metadata so that PMAGuard knows the exact transforms required for the model - * input. This makes deployin models in PAMGuard very easy - users require little + * input. This makes deploying models in PAMGuard very easy - users require little * or no experience to get this working. *

*

  • Ketos
  • @@ -117,11 +122,12 @@ public class DLControl extends PamControlledUnit implements PamSettings { * List of different deep learning models that are available. */ private ArrayList dlModels = new ArrayList(); + /** * The settings pane. */ - private RawDLSettingsPane settingsPane; + private DLSettingsPane settingsPane; /** * The settings dialog @@ -179,7 +185,24 @@ public class DLControl extends PamControlledUnit implements PamSettings { * The current data selector. */ private DataSelector dataSelector; + + /** + * Figure out which model type has been imported. + */ + private DLClassifierChooser dlClassifierChooser; + /** + * Handles downloading models from the internet + */ + private DLDownloadManager modelDownloadManager; + + + /** + * Handles downloading models from the internet + */ + private DLDefaultModelManager defaultModelManager; + + /** * Constructor for the DL Control. * @@ -191,6 +214,7 @@ public class DLControl extends PamControlledUnit implements PamSettings { PamRawDataBlock rawDataBlock = PamController.getInstance() .getRawDataBlock(rawDLParmas.groupedSourceParams.getDataSource()); + /** * In the latest release of djl (0.11.0) there is a bug with the dll's of tensorflow and * pytorch. If tensorflow is loaded before pytorch there is a conglict in dll's and @@ -205,7 +229,14 @@ public class DLControl extends PamControlledUnit implements PamSettings { // classify the raw data segments. addPamProcess(dlClassifyProcess = new DLClassifyProcess(this, segmenterProcess.getSegmenterDataBlock())); + //manages the names assigned to different output classes. dlClassNameManager = new DLClassNameManager(this); + + //manages default models + defaultModelManager = new DLDefaultModelManager(this); + + //manages downloading models + modelDownloadManager = new DLDownloadManager(); // add storage options etc. dlBinaryDataSource = new DLResultBinarySource(dlClassifyProcess); @@ -223,9 +254,16 @@ public class DLControl extends PamControlledUnit implements PamSettings { /***** Add new deep learning models here ****/ - dlModels.add(new GenericDLClassifier(this)); dlModels.add(new SoundSpotClassifier(this)); - dlModels.add(new KetosClassifier(this)); + dlModels.add(new KetosClassifier2(this)); + dlModels.add(new KooguClassifier(this)); + dlModels.add(new PamZipModelClassifier(this)); + dlModels.add(new DelphinIDClassifier(this)); + + //it is important the Generic Model is last because we need to check + //for PG metadata in all other models before resorting to manually + //setting up a model. + dlModels.add(new GenericDLClassifier(this)); // dlModels.add(new DummyClassifier()); // dlModels.add(new OrcaSpotClassifier(this)); //removed soon. @@ -239,7 +277,7 @@ public class DLControl extends PamControlledUnit implements PamSettings { TDDataProviderRegisterFX.getInstance() .registerDataInfo(new DLDetectionPlotProvider(this, dlClassifyProcess.getDLDetectionDatablock())); TDDataProviderRegisterFX.getInstance() - .registerDataInfo(new DLPredictionProvider(this, dlClassifyProcess.getDLDetectionDatablock())); + .registerDataInfo(new DLPredictionProvider(this, dlClassifyProcess.getDLPredictionDataBlock())); // register the DD display DDPlotRegister.getInstance() @@ -251,7 +289,10 @@ public class DLControl extends PamControlledUnit implements PamSettings { // serialized if (rawDLParmas.classNameMap == null) rawDLParmas.classNameMap = new ArrayList(); - + + //create the classiifer chooser. + dlClassifierChooser = new DLClassifierChooser(this); + // ensure everything is updated. updateParams(rawDLParmas); } @@ -264,6 +305,22 @@ public class DLControl extends PamControlledUnit implements PamSettings { public ArrayList getDLModels() { return dlModels; } + + + /** + * Get a model by it's name. + * @param the name the model. + * @return the corresponding model object or null if no model with the name exists. + */ + public DLClassiferModel getDLModel(String string) { + for (int i=0; i< this.dlModels.size(); i++) { + if (dlModels.get(i).getName().equals(string)) { + return dlModels.get(i); + } + } + return null; + } + /** * Get the current deep learning model. @@ -271,7 +328,12 @@ public class DLControl extends PamControlledUnit implements PamSettings { * @return the current deep learning model. */ public DLClassiferModel getDLModel() { - return dlModels.get(rawDLParmas.modelSelection); + if (this.rawDLParmas.modelSelection<0 || this.rawDLParmas.modelSelection>=dlModels.size()) { + return null; + } + else { + return dlModels.get(this.rawDLParmas.modelSelection); + } } /** @@ -342,11 +404,12 @@ public class DLControl extends PamControlledUnit implements PamSettings { * * @return the settings pane. */ - public RawDLSettingsPane getSettingsPane() { + public DLSettingsPane getSettingsPane() { if (this.settingsPane == null) { - settingsPane = new RawDLSettingsPane(this); + settingsPane = new DLSettingsPane(this); } + return settingsPane; } @@ -526,6 +589,31 @@ public class DLControl extends PamControlledUnit implements PamSettings { } } } + + /** + * Get the classifier chooser. + * @return the classifier chooser.Take it + */ + public DLClassifierChooser getDlClassifierChooser() { + return dlClassifierChooser; + } + + /** + * Get the download manager for downloading models offline. + * @return the download manager. + */ + public DLDownloadManager getDownloadManager() { + return modelDownloadManager; + } + + /** + * Get the default model manager. This handles the default models that can be downloaded. + * @return the default model manager. + */ + public DLDefaultModelManager getDefaultModelManager() { + return this.defaultModelManager; + } + } diff --git a/src/rawDeepLearningClassifier/DLControlGUI.java b/src/rawDeepLearningClassifier/DLControlGUI.java index 4b4f3145..fdb36c58 100644 --- a/src/rawDeepLearningClassifier/DLControlGUI.java +++ b/src/rawDeepLearningClassifier/DLControlGUI.java @@ -12,6 +12,8 @@ import pamViewFX.PamControlledGUIFX; */ public class DLControlGUI extends PamControlledGUIFX { + + /** * The dl control. */ diff --git a/src/rawDeepLearningClassifier/DLDownloadListener.java b/src/rawDeepLearningClassifier/DLDownloadListener.java new file mode 100644 index 00000000..3f3ffdf1 --- /dev/null +++ b/src/rawDeepLearningClassifier/DLDownloadListener.java @@ -0,0 +1,15 @@ +package rawDeepLearningClassifier; + +/** + * Listener for downloading files. + */ +public interface DLDownloadListener { + + + /** + * Updates the number of bytes downloaded + * @param bytesDownlaoded - the number of bytes downloaded so far. + */ + public void update(DLStatus status, long bytesDownlaoded); + +} diff --git a/src/rawDeepLearningClassifier/DLDownloadManager.java b/src/rawDeepLearningClassifier/DLDownloadManager.java new file mode 100644 index 00000000..507fd8f4 --- /dev/null +++ b/src/rawDeepLearningClassifier/DLDownloadManager.java @@ -0,0 +1,434 @@ +package rawDeepLearningClassifier; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.io.FilenameUtils; + +import ai.djl.util.ZipUtils; +import rawDeepLearningClassifier.defaultModels.RightWhaleModel1; + +/** + * Manages the downloading and unzipping of models. + */ +public class DLDownloadManager { + + public static String[] defaultModels = new String[] {"saved_model.pb"}; + + /** + * Listens for downlaods. + */ + private ArrayList downLoadListeners = new ArrayList(); + + + + + public DLDownloadManager() { + + } + + /** + * Notify the download listeners of a change. + * @param status - notify the listeners. + * @param downloadedBytes - the number of downloaded bytes. + */ + private void notifyDownLoadListeners(DLStatus status, long downloadedBytes) { + for (DLDownloadListener listener : downLoadListeners) { + listener.update(status, downloadedBytes); + } + } + + /** + * Check whether has been downloaded already to a default local folder? + * @param modelURI - the URL of the model + * @return + * @return true if the model has been downloaded. + */ + public boolean isModelDownloaded(URL modelURL) { + return isModelDownloaded(modelURL, getModelName(modelURL)); + } + + + /** + * Check whether has been downloaded already? + * @param modelURI - the URL of the model + * @param modelName - the model name that has been used to name the temporary folder. + * @return true if the model has been downloaded. + */ + public boolean isModelDownloaded(URL modelURL, String modelName) { + return getModelDownloadedFile( modelURL, modelName).exists(); + } + + /** + * Get the path to the model if it has been downloaded. + * @param modelURL - the URL to the model + * @param modelName - the model name. Use getModelName(modelURL) to use the default model name. + * @return a file object - may not exist. + */ + public File getModelDownloadedFile(URL modelURL, String modelName) { + //get the name of the file + String fileName = FilenameUtils.getName(modelURL.toString()); + + return new File(getModelFolder(modelName) + File.separator + fileName); + } + + + /** + * Get the path to a model. If the URI is a URL then the model is download to a local folder and the path to + * the local folder is returned. + * @param model - the model to load. + * @param modelName - the name of the model - this is used to create the local folder name if the model is downloaded. + * @return the path to the model The model might be a zip file, py file, koogu file. + */ + public URI downloadModel(URI modelURI) { + String modelName = getModelName(modelURI); + return downloadModel( modelURI, modelName); + } + + /** + * Get a model name based on it's filename + * @param modelURI - URI to the model + * @return the model name. + */ + private String getModelName(URI modelURI) { + return FilenameUtils.getBaseName(modelURI.getPath()); + } + + /** + * Get a model name based on it's filename + * @param modelURL - URL to the model + * @return the model name. + */ + private String getModelName(URL modelURI) { + return FilenameUtils.getBaseName(modelURI.getPath()); + } + + + /** + * Get the path to a model. If the URI is a URL then the model is download to a local folder and the path to + * the local folder is returned. + * @param model - the model to load. + * @param modelName - the name of the model - this is used to create the local folder name if the model is downloaded. + * @return the path to the model The model might be a zip file, py file, .kgu file. + */ + public URI downloadModel(URI modelURI, String modelName) { + + if ("file".equalsIgnoreCase(modelURI.getScheme())) { + // It's a file path + return modelURI; //the model is already a path. + + } else { + // It's a URL + try { + System.out.println("Download: " + modelURI.toURL().getPath()); + String folder = getModelFolder(modelName); + if (folder==null) { + System.err.println("DLDefaultModelManager.loadModel: Unable to make model folder: "); + } + try { + + HttpURLConnection huc = (HttpURLConnection) modelURI.toURL().openConnection(); + + int responseCode = huc.getResponseCode(); + + if (HttpURLConnection.HTTP_OK == responseCode) { + notifyDownLoadListeners(DLStatus.CONNECTION_TO_URL, -1); + File file = downLoadModel(modelURI.toURL(), getModelFolder(modelName) ); + + System.out.println("DOWNLOADED MODEL: " + file.getPath() ); + + notifyDownLoadListeners(DLStatus.DECOMPRESSING_MODEL, -1); + + file = decompressFile(file); + + if (file.isDirectory()) { + //the file has been decompressed and need to search for model path within this... + file = findModelFile(file, defaultModels); + + System.out.println("DECOMPRESSED MODEL: " + file ); + + if (file == null) return null; + } + + System.out.println("FINAL MODEL: " + file ); + + + return file.toURI(); + } + else { + notifyDownLoadListeners(DLStatus.NO_CONNECTION_TO_URL, -1); + return null; + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + } + + + } + + private static boolean isArchive(File f) { + int fileSignature = 0; + try (RandomAccessFile raf = new RandomAccessFile(f, "r")) { + fileSignature = raf.readInt(); + } catch (IOException e) { + // handle if you like + } + return fileSignature == 0x504B0304 || fileSignature == 0x504B0506 || fileSignature == 0x504B0708; + } + + + /** + * Find the model file within + * @param folder + * @return + */ + private File findModelFile(File folder, String[] defaultModelNames) { + + + Path startDir = folder.toPath(); // replace with your directory path + + try { + List filesList = Files.walk(startDir) + .filter(Files::isRegularFile) + .collect(Collectors.toList()); + + filesList.forEach(System.out::println); + + for (String modelName:defaultModelNames) { + String name; + //now search through the list + for (Path modelFile:filesList) { + name = modelFile.getFileName().toString(); + if (name.equals(modelName)) { + return modelFile.toFile(); + } + + } + } + return null; + + } catch (IOException e) { + e.printStackTrace(); + } + return null; + + } + + /** + * Once a file has been downloaded, it may need to be decompressed. + * @param file - the file. + * @return the path to the decompressed folder or the path to the original file if not a zip file. + */ + private File decompressFile(File file) { + + if (FilenameUtils.getExtension(file.getPath()).equals("zip")) { + + FileInputStream fileInputStream; + try { + fileInputStream = new FileInputStream(file); + + String fileNameWithOutExt = FilenameUtils.removeExtension(file.getPath()); + + File zipFolder = new File(fileNameWithOutExt); + + //Creating the directory + boolean bool = zipFolder.mkdir(); + + //unzip the model into the temporary directory.... + + ZipUtils.unzip(fileInputStream, Paths.get(zipFolder.toURI())); + + //now that file has been unzipped search for a valid model file. + + return zipFolder; + + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + } + else { + return file; + } + + } + + + private static boolean isRedirected( Map> header ) { + for( String hv : header.get( null )) { + if( hv.contains( " 301 " ) + || hv.contains( " 302 " )) return true; + } + return false; + } + + + /** + * Get the model folder - create it if it doesn't exist. + * @return file object for new folder + */ + static public String getModelFolder() { + String settingsFolder = System.getProperty("user.home"); + settingsFolder += File.separator + "Pamguard_deep_learning"; + // now check that folder exists + File folder = makeFolder( settingsFolder); + if (folder==null) return null; + return folder.getPath(); + } + + /** + * Get the model folder - create it if it doesn't exist. + * @param modelname - the name of the model - this is used to create the local folder name. + * @return file object for new folder + */ + static public String getModelFolder(String modelname) { + String folder = getModelFolder(); + if (folder == null) return null; + // now check that folder exists + File dlFolder = makeFolder( folder + File.separator + modelname); + if (dlFolder==null) return null; + return dlFolder.getPath(); + } + + /** + * Make a settings folder. + * @param settingsFolder + * @return + */ + private static File makeFolder(String settingsFolder) { + File folder = new File(settingsFolder); + if (folder.exists() == false) { + folder.mkdirs(); + if (folder.exists() == false) { + return null; + } + } + return folder; + } + + + + + /** + * Download a model to the default file location. + * @param link - the link to download from + * @param outFolder - the folder in which to save the file. + * @return the model file output. + * @throws IOException - exception if something goes wrong. + */ + private File downLoadModel(URL url, String outFolder) throws IOException { + + String link = url.toString(); + + //get the name of the file + String fileName = FilenameUtils.getName(link); + + File outFile = new File( outFolder + File.separator + fileName); + + HttpURLConnection http = (HttpURLConnection)url.openConnection(); + Map< String, List< String >> header = http.getHeaderFields(); + + //handle any redorects - e.g. from GitHub + while( isRedirected( header )) { + link = header.get( "Location" ).get( 0 ); + url = new URL( link ); + http = (HttpURLConnection)url.openConnection(); + header = http.getHeaderFields(); + } + + + notifyDownLoadListeners(DLStatus.DOWNLOAD_STARTING, 0); + + InputStream input = http.getInputStream(); + byte[] buffer = new byte[512]; //download in 4kB chunks + int n = -1; + OutputStream output = new FileOutputStream(outFile); + long count = 0; + + + //Files.copy(input, outFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + + while ((n = input.read(buffer)) != -1) { +// System.out.println("Chunk: " + count); + count=count+n; //total bytes + notifyDownLoadListeners(DLStatus.DOWNLOADING, count); + output.write( buffer, 0, n ); + } + +// System.out.println("Chunk: " + n); + + output.close(); + + notifyDownLoadListeners(DLStatus.DOWNLOAD_FINISHED, count); + + + return outFile; + } + + /** + * Get the number of download listeners. + * @return the number of download listeners. + */ + public int getNumDownloadListeners() { + return downLoadListeners.size(); + } + + /** + * Add a download listener to the array manager. + * @param e - the listener to add. + * @return true if the listener was added successfully. + */ + public boolean addDownloadListener(DLDownloadListener e) { + return downLoadListeners.add(e); + } + + /** + * Remove a download listener to the array manager. + * @param e - the listener to remove. + * @return true if the listener was removed successfully. + */ + public boolean removeDownladListener(Object o) { + return downLoadListeners.remove(o); + } + + + public static void main(String[] args) { + DLDownloadManager dlDefaultModelManager = new DLDownloadManager(); + System.out.println("Test downloading a model: "); + + URI path = dlDefaultModelManager.downloadModel(new RightWhaleModel1().getModelURI(), new RightWhaleModel1().getModelName()); + } + + public void clearDownloadListeners() { + this.downLoadListeners.clear(); + + } + +} diff --git a/src/rawDeepLearningClassifier/DLStatus.java b/src/rawDeepLearningClassifier/DLStatus.java new file mode 100644 index 00000000..4ab61429 --- /dev/null +++ b/src/rawDeepLearningClassifier/DLStatus.java @@ -0,0 +1,119 @@ +package rawDeepLearningClassifier; + +/** + * Status reporting for the deep learning module. + */ +public enum DLStatus { + + FILE_NULL("The input file is null", + "The loaded file was null. If the file was download it may not have downloaded properly.", ErrorLevel.ERROR), + + MODEL_LOAD_FAILED("The model failed to load", + " The model failed to load - this could be because it is incompatible with PAMGuard or an uknown file format.", + ErrorLevel.ERROR), + + MODEL_LOAD_SUCCESS("The model loaded", " The model successfully loaded", ErrorLevel.NO_ERROR), + + DOWNLOAD_STARTING("Download starting", "The model is downloading", ErrorLevel.NO_ERROR), + + DOWNLOAD_FINISHED("Download finished", " The model successfully downloaded", ErrorLevel.NO_ERROR), + + DOWNLOADING("Downloading", "The model is currently downloading", ErrorLevel.NO_ERROR), + + NO_CONNECTION_TO_URL("Could not connect to the URL", + " Could connect to the URL - the URL may be wrong or there may be no internet connection", ErrorLevel.ERROR), + + CONNECTION_TO_URL("Connected to URL", "The connection to the URL was successful", ErrorLevel.NO_ERROR), + + MODEL_DOWNLOAD_FAILED("The model was not downloaded", + " The model download failed. Check internet connection and try again", ErrorLevel.ERROR), + + NO_MODEL_LOADED("There is no loaded model", + "There is no loaded model. A model my have failed to load or the path/URL to the model is incorrect", ErrorLevel.WARNING), + + //this is not a show stopper because predictions are saved there's just no binary classification. + NO_BINARY_CLASSIFICATION("No binary classification", + " There are no prediction classes selected for classification. Predicitons for each segment will be saved but there will be no detections generated", + ErrorLevel.WARNING), + + DECOMPRESSING_MODEL("Decompressing model", "Decompressing the model file", ErrorLevel.NO_ERROR), + + INCOMPATIBLE_ZIP("Incorrect Zip format", "The zip format is incorrect. The zip file should have a *.pgdl file in the parent directory along with either a Tensorflow or PyTorch model.", ErrorLevel.ERROR), ; + + + /** + * True of the message is an error message. + */ + private int isError = ErrorLevel.NO_ERROR; + + /** + * Check whether the message is an error message. + * + * @return true if an error message. + */ + public boolean isError() { + return isError==ErrorLevel.ERROR; + } + + /** + * The name of the status + */ + private String string; + + /** + * Description of the status + */ + private String expanded; + + /** + * Status report for the leep learning module. + * + * @param string - status + * @param expanded - description of the status. + */ + DLStatus(String string, String expande, int isError) { + this.string = string; + this.expanded = expande; + this.isError = isError; + } + + /** + * Get a brief description of the status. + * + * @return a brief description of the status. + */ + public String getName() { + return string; + } + + /** + * Longer description of the status. + * + * @return longer descrption of the status. + */ + public String getDescription() { + return expanded; + } + + private static class ErrorLevel { + public static final int NO_ERROR = 0; + + public static final int WARNING = 1; + + public static final int ERROR = 2; + } + + /** + * Get the error flag. e.g. ErrorLevel.NO_ERROR. The error flag describes + * whether the status is an error, warning or not an error. + * @return the error flag. + */ + int getErrorFlag() { + return isError; + } + + public boolean isWarning() { + return isError==ErrorLevel.WARNING; + } + +} diff --git a/src/rawDeepLearningClassifier/DLZipUtils.java b/src/rawDeepLearningClassifier/DLZipUtils.java new file mode 100644 index 00000000..808ab663 --- /dev/null +++ b/src/rawDeepLearningClassifier/DLZipUtils.java @@ -0,0 +1,106 @@ +package rawDeepLearningClassifier; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; + + +public class DLZipUtils { + + /** + * Find the first file within a zip folder that matches a pattern. This peaks into the zip file instead of decompressing it. + * @param zipUri - uri to the zip file + * @param filePattern - the file pattern to match - the file must contain this string. + * @return null if no file found and the file pqth if the file is founf + * @throws ZipException + * @throws IOException + */ + public static String getZipFilePath(URI zipUri, String filePattern) throws ZipException, IOException { + return getZipFilePath(new File(zipUri), filePattern); + } + + + /** + * Extract a single file from a zip folder. + * @param zipPackage - URI to the the zip file + * @param fileToBeExtracted - the name of the file to be extracted + * @param - the folder path to extract the file to. + * @return - return the File that has been extracted. + * @throws IOException + * @throws URISyntaxException + */ + public static File extractFile(String zipPackage, String fileToBeExtracted, String outFolder) throws IOException, URISyntaxException { + return extractFile( new URI(zipPackage), fileToBeExtracted, outFolder) ; + } + + + /** + * Extract a single file from a zip folder. + * @param zipPackage - path the the zip file + * @param fileToBeExtracted - the name of the file to be extracted + * @param - the folder path to extract the file to. + * @return - return the File that has been extracted. + * @throws IOException + */ + public static File extractFile(URI zipPackage, String fileToBeExtracted, String outFolder) throws IOException { + File fileOut = new File(outFolder, fileToBeExtracted); + OutputStream out = new FileOutputStream(fileOut); + FileInputStream fileInputStream = new FileInputStream(new File(zipPackage)); + BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream ); + ZipInputStream zin = new ZipInputStream(bufferedInputStream); + ZipEntry ze = null; + while ((ze = zin.getNextEntry()) != null) { + if (ze.getName().equals(fileToBeExtracted)) { + byte[] buffer = new byte[9000]; + int len; + while ((len = zin.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + out.close(); + break; + } + } + zin.close(); + + return fileOut; + } + + + /** + * Find the first file within a zip folder that matches a pattern. + * @param zipFile - uri to the zip file + * @param filePattern - the file pattern to match - the file must contain this string. + * @return null if no file found and the file pqth if the file is founf + * @throws ZipException + * @throws IOException + */ + public static String getZipFilePath(File zipFileIn, String filePattern) throws ZipException, IOException { + + try (ZipFile zipFile = new ZipFile(zipFileIn)) { + Enumeration entries = zipFile.entries(); + //this iterates through all files, including in sub folders. + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + // Check if entry is a directory + if (!entry.isDirectory()) { + //System.out.println(entry); + if (entry.getName().contains(filePattern)) { + return entry.getName(); + } + } + } + } + return null; + } + +} diff --git a/src/rawDeepLearningClassifier/RawDLParams.java b/src/rawDeepLearningClassifier/RawDLParams.java index 4f84ad52..17e8dc7c 100644 --- a/src/rawDeepLearningClassifier/RawDLParams.java +++ b/src/rawDeepLearningClassifier/RawDLParams.java @@ -1,6 +1,7 @@ package rawDeepLearningClassifier; import java.io.Serializable; +import java.net.URI; import java.util.ArrayList; import PamView.GroupedSourceParameters; @@ -21,8 +22,16 @@ public class RawDLParams implements Serializable, Cloneable { /** * The currently selected Deep Learning model. + * (Models are now automatically selected). + * -1 means no model selected. */ - public int modelSelection = 0; + public int modelSelection = -1; + + /** + * The current model URI. The deep learning model must have some sort of external file to run. + * This might be a model, a .exe file etc. + */ + public URI modelURI; /** * Holds channel and grouping information @@ -76,7 +85,7 @@ public class RawDLParams implements Serializable, Cloneable { * different class names. If we change model then the class names may change. * Previously annotated data will then be messed up. But, in a giant dataset * that may be an issue. Perhaps users wish to run a new model on some chunk of - * data without messing up all the other classified detection which have used + * data without messing up all the other classified detectionS which have used * that module. So store the data in binary files? That is super inefficient as * the same string is stored many times. So instead store a short which * identifies the string that sits in this table. Everytime a new model is added diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLDetectionPlotInfoFX.java b/src/rawDeepLearningClassifier/dataPlotFX/DLDetectionPlotInfoFX.java index b1fef763..a2282812 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLDetectionPlotInfoFX.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLDetectionPlotInfoFX.java @@ -47,7 +47,7 @@ public class DLDetectionPlotInfoFX extends RawClipDataInfo { try { PamSVGIcon iconMaker= new PamSVGIcon(); //PamSVGIcon svgsprite = iconMaker.create(new File(getClass().getResource(resourcePath).toURI()), Color.WHITE); - PamSVGIcon svgsprite = iconMaker.create(getClass().getResource(resourcePath).toURI().toURL(), Color.WHITE); + PamSVGIcon svgsprite = iconMaker.create(getClass().getResource(resourcePath).toURI().toURL(), Color.WHITE,1); // svgsprite.getSpriteNode().setStyle("-fx-text-color: white"); // svgsprite.getSpriteNode().setStyle("-fx-fill: white"); diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPane.java b/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPane.java index 41d26118..c603f08f 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPane.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPane.java @@ -184,7 +184,7 @@ public class DLPredictionPane extends PamBorderPane implements TDSettingsPane { try { PamSVGIcon iconMaker= new PamSVGIcon(); //PamSVGIcon svgsprite = iconMaker.create(new File(getClass().getResource(resourcePath).toURI()), Color.WHITE); - PamSVGIcon svgsprite = iconMaker.create(getClass().getResource(resourcePath).toURI().toURL(), Color.DODGERBLUE); + PamSVGIcon svgsprite = iconMaker.create(getClass().getResource(resourcePath).toURI().toURL(), Color.DODGERBLUE, 1); // svgsprite.getSpriteNode().setStyle("-fx-text-color: white"); // svgsprite.getSpriteNode().setStyle("-fx-fill: white"); diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPlotInfoFX.java b/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPlotInfoFX.java index 945561b5..44fdca6f 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPlotInfoFX.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPlotInfoFX.java @@ -3,12 +3,14 @@ package rawDeepLearningClassifier.dataPlotFX; import java.io.Serializable; +import PamController.PamController; import PamUtils.PamArrayUtils; import PamView.GeneralProjector; import PamView.GeneralProjector.ParameterType; import PamView.GeneralProjector.ParameterUnits; import PamView.symbol.PamSymbolChooser; import PamView.symbol.PamSymbolManager; +import PamguardMVC.PamConstants; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import dataPlotsFX.TDManagedSymbolChooserFX; @@ -18,6 +20,7 @@ import dataPlotsFX.data.generic.GenericLinePlotInfo; import dataPlotsFX.data.generic.GenericScaleInfo; import dataPlotsFX.layout.TDGraphFX; import dataPlotsFX.projector.TDProjectorFX; +import javafx.geometry.Point2D; import javafx.scene.canvas.GraphicsContext; import javafx.scene.paint.Color; import javafx.scene.shape.Polygon; @@ -81,19 +84,31 @@ public class DLPredictionPlotInfoFX extends GenericLinePlotInfo { frequencyInfo = new GenericScaleInfo(0, 1, ParameterType.FREQUENCY, ParameterUnits.HZ); - DLClassName[] classNames = getDlControl().getDLModel().getClassNames(); - - //make sure this is initialised otherwise the plot won't work when first created. - if (dlPredParams.lineInfos==null ) dlPredParams.lineInfos = new LineInfo[classNames.length]; - for (int i=0; i - +

    _A diagram of how the deep learning module works in PAMGuard. An input waveform is segmented into chunks. A series of transforms are applied to each chunk creating the input for the deep learning model. The transformed chunks are sent to the model. The results from the model are saved and can be viewed in real time (e.g. mitigation) or in post processing (e.g. data from SoundTraps)._ ### Generic Model + A generic model allows a user to load any model compatible with the [djl](https://djl.ai/) (PyTorch (JIT), Tenserflow, ONXX) library and then manually set up a series of transforms using PAMGuard's transform library. It is recommended that users use an existing framework instead of a generic model as these models will automatically generate the required transforms. ### AnimalSpot + [ANIMAL-SPOT](https://github.com/ChristianBergler/ANIMAL-SPOT) is a deep learning based framework which was initially designed for [killer whale sound detection]((https://github.com/ChristianBergler/ORCA-SPOT)) in noise heavy underwater recordings (see [Bergler et al. (2019)](https://www.nature.com/articles/s41598-019-47335-w)). It has now been expanded to a be species independent framework for training acoustic deep learning models using [PyTorch]( https://pytorch.org/) and Python. Imported AnimalSpot models will automatically set up their own data transforms and output classes. ### Ketos -[Ketos](https://meridian.cs.dal.ca/2015/04/12/ketos/) is an acoustic deep learning framework based on Tensorflow and developed by [MERIDIAN]( https://meridian.cs.dal.ca/). It has excellent resources and tutorials and Python libraries can be installed easily via pip. Imported Ketos model will automatically set up their own data transforms and output classes. + +[Ketos](https://meridian.cs.dal.ca/2015/04/12/ketos/) is an acoustic deep learning framework based on Tensorflow and developed by [MERIDIAN]( https://meridian.cs.dal.ca/). It has excellent resources and tutorials and Python libraries can be installed easily via pip. Imported Ketos (.ktpb) models will automatically set up their own data transforms and output classes. + +### Koogu + +[Koogu ](https://shyamblast.github.io/Koogu/en/stable/) is a Python package which allows users to train a deep learning model. Koogu helps users by integrating with some frequency used annotation programs and provides tools to train and test classifiers. Imported Koogu models (.kgu) will automatically set up their own data transforms and output classes. + +### PAMGuardZip + +PAMGuard zip models consist of a deep learning model (either a Tensorflow saved_model.pb or PyTorch **.py model) alongside a PAMGuard metdata file (*.pdtf*) within a zip archive. The metadata file contains all the information needed for PAMGaurd to set up the model. PAMGuard will import the zip file, decompress it and search for the relevent deep learning model and metadata file then set up all settings accordingly. This framework allows users to easily share pre-tested PAMGuard compatible models. ## Creating an instance of the module + The module can be added from the _File> Add modules > Classifier > Raw deep learning classifier_ menu or by right clicking in the data model. More than one instance of the module can be added if multiple deep learning models are required. ## Module settings + The module settings are opened by selecting the _Settings > Raw deep learning classifier_ menu. The main settings pane is shown below and is split into three sections, _Raw Sound Data_, _Segmentation_ and _Deep Learning Model_

    @@ -38,7 +52,7 @@ _The main settings pane for the deep learning module with descriptions_ ### Raw Sound Data -The deep learning module accepts any raw data source i.e., any data source that contains raw waveform data. +The deep learning module accepts any raw data source i.e., any data source that contains raw waveform data e.g. clicks, clips and Ishmael detections. Note that the module accepts whislte and moan detections but only if a delphinID classifier has been loaded. If the data is continuous, e.g. from the Sound Acquisition module then deep learning detections are saved to PAMGuard's data management system if they pass a user defined prediction threshold. The raw waveform data for segments which pass prediction threshold is saved and the detection is annotated with the deep prediction results. @@ -50,7 +64,7 @@ Channel grouping controls are used to arrange channels into groups. Channels in The segmentation section defines how the raw data is segmented. Some deep learning models require a specific segment size and others can be run with different segment sizes. The _Window Length_ is the size of the segment in samples. The _Hop Length_ is the overlap (from the start of the segment) in samples. A _Hop Length_ which is the same as the segment length means no overlap. If a prediction passes threshold, then the raw data from segments is saved to PAMGuard binary files. If concurrent segments pass a prediction threshold, then they are saved as one data unit. The _Max. re-merge_ is the maximum number of segments that can form a single data unit before a new data unit is automatically created. -### Deep Learning Model +### Deep Learning Model The deep learning model section is used to select the deep learning model. The drop down menu is used to select the framework the model is from e.g. Generic model. Note that each model type has a unique user interface which appears just below the drop down menu - currently these all look fairly similar. @@ -82,27 +96,42 @@ The _Model Settings_ tab allows the model input shape and output shape/classes t The import and export buttons on the bottom of the advanced settings pane can be used to export and import settings for the generic model. This means that users do not have to manually set up transforms and input and output data whenever settings up a new PAMGuard data model and allow easier sharing of classifiers amongst researchers. -#### AnimalSpot and Ketos models +#### Koogu, Ketos, AnimalSpot, PAMGuard zip and delphinID models -If using an AnimalSpot or Ketos model then all transforms are automatically set up. The transforms can be viewed and altered via the Advanced menu button but in the majority of cases these settings should not be used. It is advisable to select "Use default segment length" to change the _Window length_ to the default for the selected model. Note that this is often necessary for Ketos models but usually not a requirement for AnimalSpot models. +If using a deep learning model from a supported framework then all transforms are automatically set up. The transforms can be viewed and altered via the Advanced menu button but in the majority of cases these settings should not be used. For some models, it is advisable to select "Use default segment length" to change the _Window length_ to the default for the selected model.

    -_An AnimalSpot or Ketos model will automatically create a list of transforms with the appropriate settings. These is no need to use the advanced pane but it is there in case users wish to change transform settings for some reason_ +_An AnimalSpot, Ketos or other supported deep learning model will automatically create a list of transforms with the appropriate settings. These is no need to use the advanced pane but it is there in case users wish to change transform settings for some reason_ + +### Default Models + +Default models are selectable from the menu button in the Deep Learning Pane. Default models are deep learning classifiers which are open source, known to be effective and have have been published in open access academic litrature; they are downloaded directly from a GitHub repository and then all associated settings are automtically applied. The default model selection pane also contains hyperlinks to the papers descirbing each model which will take users directly to the relvent website. + +

    + +

    + +_Default models can be downloaded. Default models are models which are published, open and have been known to work well amongst the bioacoustics community. More will be added to PAMGaurd over time. If you you would like your model to become a defualt model then drop PAMGuard support an email._ ## Running + ### Real time + In real time, the deep learning model runs automatically when processing starts. A warning will appear if there are issues with the model and/or it cannot cope with real time speeds. ### Viewer Mode + The deep learning module can be re-run on _detector_ data (e.g. click or clip detections) in PAMGuard _viewer_ mode. Detections can be reclassified by selecting the _Settings > Raw Deep Learning Classifier > Reclassify detections_. Select the data range in the reprocessing dialog e.g. Loaded Data and select _Start_. Detections without a deep learning annotation will have one added and detections with an existing annotation will have it overwritten. ## Viewing and exporting results + Output from the deep learning module can be viewed in PAMGuard viewer mode, or extracted from binary files using MATLAB or R. ### PAMGuard viewer mode + Detections form continuous raw data are shown in the datagram in the same way as all data streams in PAMGuard. The Time base display FX is best way to view detailed data outputs from the deep learning algorithm. The time base display can display almost all data types in PAMGuard on a large variety of different data axis. For example, click detections can be displayed on an amplitude, bearing, ICI, waveform and/or frequency axis. Deep learning detections (i.e. data units which have been saved from raw data using the deep learning detector) can be displayed on the time base display in the same way as many other detections and in addition, there is a symbol manager options which allows the deep learning detections or other detections which have been classified by the deep learning module to be coloured by prediction class. This means that a manual analyst can quickly navigate to detections with high prediction values for a certain class. Hovering over or right clicking on a data unit in the time display and selecting the information button, will show the data unit’s metadata, including the prediction values for all output classes from the deep learning model. @@ -115,7 +144,8 @@ _An example click detection module output coloured by deep learning annotations. Other displays also show outputs from the deep learning module. Hovering over data units in the click display will, for example, show deep learning prediction values. The spectrogram will also show deep learning detections as translucent blue boxes (these must be selected in the right click menu). -### MATLAB +### MATLAB + The easiest way to export to MATLAB is to select the desired units in the time base display, right click and select the MATLAB icon. Data units will be exported to a .mat file as list of structures which is then saved to the clipboard. This file can be saved and then dragged into MATLAB to open. Where it is necessary to further analyse large datasets produced by PAMGuard, there is a MATLAB-PAMGuard library which can directly import the binary files which store PAMGaurd detection data. The library is simple to use with the primary function being ```loadPAMGuardBinaryFile.m. ``` This will load any binary file type (e.g. clicks, whistles, deep learning detections) and return a list of data structures with the detection data. The structures include annotations where deep learning predictions are stored. @@ -125,53 +155,52 @@ Here is a simple example loading up all the deep learning detections for a right ```matlab % the folder containing PAMGuard binary files folder = '/Users/me/right_whale_project_1/PAMBinary/'; - + %load all the detections in the folder dldetections = loadPamguardBinaryFolder(folder, 'Deep_Learning_Classifier_Raw_Deep_Learning_Classifier_DL_detection_*.pgdf') ``` + The predicitons for each class (in this case the classes are noise and right whale) are easily accessed in the structure via; ```matlab %% access the prediciton form the first detection predicitons = dldetections(1).annotations.dlclassification(j).predictions; - ``` The loaded detections can then be plotted by accessing the waveform data in each structure; ```matlab - % plot all the spectrograms. clf tiledlayout(5,5) for i=1:length(dldetections) - + nexttile - + % generate the data for a spectrgram [s, w, t] = spectrogram(dldetections(i).wave,512, 384,[],sR,'yaxis'); - + % create the time and frequency matrices required to plot a surface [X, Y] = meshgrid(t,w); % plot the surface (divide and multiply by 1000 to show milliseconds and kHz respectively) surf(X*1000, Y/1000, 20*log10(abs(s))-140, 'EdgeColor', 'None') view([0,90]) - + caxis([70, 140]-140) ylim([0,0.5]); xlabel('') ylabel('') - + if (mod(i,5)==0) c = colorbar; c.Label.String = 'Amplitude (dB)'; end - + %x axis only on bottom plots if (i>=20) xlabel('Time (ms)') end - + %y axis only on left most plots if (mod(i-1,5)==0) ylabel('Frequency (kHz)') @@ -185,11 +214,13 @@ for i=1:length(dldetections) _Right whale detections from a deep learning model imported and then plotted in MATLAB_ ### R + In the same way as MATLAB export, the PAMGuard time base display and export selected data units directly to an R struct which can be imported easily into R.. R also has a well supported PAMGuard library with like for like functions compared to the MATLAB library. The PAMBinaries R library can be found [here](https://github.com/TaikiSan21/PamBinaries). ## Common bugs and mistakes + The first time you use the module and/or load a different type of model e.g. a tensorflow or pytorch model, you must be connected to the internet. You must install the correct version of CUDA for hardware acceleration using an Nvidea GPU. See the currently supported CUDA versions on the Pytorch and Tensorflow websites. @@ -199,6 +230,3 @@ You should always have deep learning models in their own folder. Do not have any Pytorch models must be saved using jit to be compatible with PAMGuard. Tensorflow models must be saved as .pb files to be opened in PAMGuard. - - - diff --git a/src/rawDeepLearningClassifier/defaultModels/DLDefaultModelManager.java b/src/rawDeepLearningClassifier/defaultModels/DLDefaultModelManager.java new file mode 100644 index 00000000..6437cada --- /dev/null +++ b/src/rawDeepLearningClassifier/defaultModels/DLDefaultModelManager.java @@ -0,0 +1,50 @@ +package rawDeepLearningClassifier.defaultModels; + +import java.util.ArrayList; +import rawDeepLearningClassifier.DLControl; + +/** + * Manages default models. + */ +public class DLDefaultModelManager { + + /** + * A list of the default models + */ + private ArrayList defaultModels = new ArrayList(); + + /** + * Reference ot the DL control. + */ + private DLControl dlControl; + + /** + * Constructor for the Defulat Model Manager. + * @param dlControl - reference to the controller for this model manager. + */ + public DLDefaultModelManager(DLControl dlControl) { + this.dlControl = dlControl; + defaultModels.add(new RightWhaleModel1()); + defaultModels.add(new HumpbackWhaleGoogle()); + defaultModels.add(new HumpbackWhaleAtlantic()); + } + + + /** + * Get a default model at index i + * @param i - the index of the default model + */ + public DLModel getDefaultModel(int i) { + return defaultModels.get(i); + } + + + /** + * Get the number of default models + * @return the number of default models. + */ + public int getNumDefaultModels() { + return defaultModels.size(); + } + +} diff --git a/src/rawDeepLearningClassifier/defaultModels/DLModel.java b/src/rawDeepLearningClassifier/defaultModels/DLModel.java new file mode 100644 index 00000000..f3aad6d9 --- /dev/null +++ b/src/rawDeepLearningClassifier/defaultModels/DLModel.java @@ -0,0 +1,59 @@ +package rawDeepLearningClassifier.defaultModels; + +import java.io.Serializable; +import java.net.URI; + +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; + +/** + * A default model that can be loaded from a file or a URL. + */ +public interface DLModel { + + /** + * Get a brief description of the model. + * @return a brief description of the model. + */ + public String getDescription(); + + /** + * Get a name for the model + * @return + */ + public String getName(); + + /** + * Get the citation for the model + * @return - the citation. + */ + public String getCitation(); + + /** + * Get the URI to the model file + * @return the model URI. + */ + public URI getModelURI(); + + + /** + * The model name. This is used if, for example, a model is downloaded as a zip file and the model + * file is located somewhere within the saved folder. For Tensorflow models this will often be saved_model.pb + * @return the model name; + */ + public String getModelName(); + + /** + * Get the link to the paper for the model + * @return the citation link. + */ + public URI getCitationLink(); + + /** + * Set the model settings once it has loaded. + * @param dlModelSettings - the model settings. + */ + public void setParams(Serializable dlModelSettings); + + + +} diff --git a/src/rawDeepLearningClassifier/defaultModels/HumpbackWhaleAtlantic.java b/src/rawDeepLearningClassifier/defaultModels/HumpbackWhaleAtlantic.java new file mode 100644 index 00000000..4f7e7071 --- /dev/null +++ b/src/rawDeepLearningClassifier/defaultModels/HumpbackWhaleAtlantic.java @@ -0,0 +1,96 @@ +package rawDeepLearningClassifier.defaultModels; + +import java.io.Serializable; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; + +import org.jamdev.jdl4pam.transforms.DLTransformsFactory; +import org.jamdev.jdl4pam.transforms.DLTransfromParams; +import org.jamdev.jdl4pam.transforms.SimpleTransformParams; +import org.jamdev.jdl4pam.transforms.DLTransform.DLTransformType; + +import rawDeepLearningClassifier.dlClassification.DLClassName; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericModelParams; +import rawDeepLearningClassifier.layoutFX.exampleSounds.ExampleSoundFactory.ExampleSoundType; + +/** + * Atlantic version of Google's humpback whale network. + */ +public class HumpbackWhaleAtlantic implements DLModel { + + @Override + public String getDescription() { + return "Detects Atlantic Humpback whales"; + } + + @Override + public String getName() { + return "Humpback Whale Atlantic"; + } + + @Override + public String getCitation() { + return "Vincent Kather, Fabian Seipel, Benoit Berges, Genevieve Davis, Catherine Gibson, Matt Harvey, Lea-Anne Henry, Andrew Stevenson, Denise Risch; Development of a machine learning detector for North Atlantic humpback whale song. J. Acoust. Soc. Am. 1 March 2024; 155 (3): 2050–2064."; + } + + @Override + public URI getModelURI() { + try { + return new URI("https://github.com/PAMGuard/deeplearningmodels/raw/master/humpback_whale_2/humpback_whale_2.zip"); + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + + @Override + public String getModelName() { + return "saved_model.pb"; + } + + @Override + public URI getCitationLink() { + try { + return new URI("https://doi.org/10.1121/10.0025275"); + } catch (URISyntaxException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void setParams(Serializable dlModelSettings) { + GenericModelParams genericModelParams = (GenericModelParams) dlModelSettings; + + //decimation value + float sr = 2000; + + //create the transforms. + ArrayList dlTransformParamsArr = new ArrayList(); + + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE_SCIPY, sr)); + + genericModelParams.dlTransfromParams = dlTransformParamsArr; + + genericModelParams.defaultSegmentLen = 3.8775*1000; + genericModelParams.binaryClassification = new boolean[] {true}; + genericModelParams.classNames= new DLClassName[] {new DLClassName("Humpback whale", (short) 1)}; + genericModelParams.numClasses = 1; + + genericModelParams.defaultShape= new Long[] {-1L,-1L,-1L,1L}; + genericModelParams.shape= new Long[] {-1L,-1L,-1L,1L}; + + //create the transforms. + genericModelParams.dlTransfroms = DLTransformsFactory.makeDLTransforms((ArrayList)genericModelParams.dlTransfromParams); + + genericModelParams.setExampleSound(ExampleSoundType.HUMPBACK_WHALE); + } + + + +} diff --git a/src/rawDeepLearningClassifier/defaultModels/HumpbackWhaleGoogle.java b/src/rawDeepLearningClassifier/defaultModels/HumpbackWhaleGoogle.java new file mode 100644 index 00000000..9a7ed64a --- /dev/null +++ b/src/rawDeepLearningClassifier/defaultModels/HumpbackWhaleGoogle.java @@ -0,0 +1,91 @@ +package rawDeepLearningClassifier.defaultModels; + +import java.io.Serializable; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; + +import org.jamdev.jdl4pam.transforms.DLTransformsFactory; +import org.jamdev.jdl4pam.transforms.DLTransfromParams; +import org.jamdev.jdl4pam.transforms.SimpleTransformParams; +import org.jamdev.jdl4pam.transforms.DLTransform.DLTransformType; + +import rawDeepLearningClassifier.dlClassification.DLClassName; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericModelParams; +import rawDeepLearningClassifier.layoutFX.exampleSounds.ExampleSoundFactory.ExampleSoundType; + +public class HumpbackWhaleGoogle implements DLModel { + + @Override + public String getDescription() { + return "A model developed by Google to detect Humpback whales (Megaptera novaeangliae) in the Pacific ocean"; + } + + @Override + public String getName() { + return "Humpback Whale Pacific"; + } + + @Override + public String getCitation() { + return "A. N. Allen et al., ‘A Convolutional Neural Network for Automated Detection of Humpback Whale Song in a Diverse, Long-Term Passive Acoustic Dataset’, Front. Mar. Sci., vol. 8, p. 607321, Mar. 2021"; + } + + @Override + public URI getModelURI() { + try { + return new URI("https://github.com/PAMGuard/deeplearningmodels/raw/master/humpback_whale_1/humpback_whale_1.zip"); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return null; + } + + @Override + public String getModelName() { + return "saved_model.pb"; + } + + @Override + public URI getCitationLink() { + try { + return new URI("https://doi.org/10.3389/fmars.2021.607321"); + } catch (URISyntaxException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void setParams(Serializable dlModelSettings) { + GenericModelParams genericModelParams = (GenericModelParams) dlModelSettings; + + //decimation value + float sr = 10000; + + //create the transforms. + ArrayList dlTransformParamsArr = new ArrayList(); + + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE_SCIPY, sr)); + + genericModelParams.dlTransfromParams = dlTransformParamsArr; + + genericModelParams.defaultSegmentLen = 3.92*1000; + genericModelParams.binaryClassification = new boolean[] {true}; + genericModelParams.classNames= new DLClassName[] {new DLClassName("Humpback whale", (short) 1)}; + genericModelParams.numClasses = 1; + + genericModelParams.defaultShape= new Long[] {-1L,-1L,-1L,1L}; + genericModelParams.shape= new Long[] {-1L,-1L,-1L,1L}; + + //create the transforms. + genericModelParams.dlTransfroms = DLTransformsFactory.makeDLTransforms((ArrayList)genericModelParams.dlTransfromParams); + + genericModelParams.setExampleSound(ExampleSoundType.HUMPBACK_WHALE); + } + + +} + diff --git a/src/rawDeepLearningClassifier/defaultModels/RightWhaleModel1.java b/src/rawDeepLearningClassifier/defaultModels/RightWhaleModel1.java new file mode 100644 index 00000000..eeb32c60 --- /dev/null +++ b/src/rawDeepLearningClassifier/defaultModels/RightWhaleModel1.java @@ -0,0 +1,115 @@ +package rawDeepLearningClassifier.defaultModels; + +import java.io.Serializable; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; + +import org.jamdev.jdl4pam.transforms.DLTransform.DLTransformType; +import org.jamdev.jdl4pam.transforms.DLTransformsFactory; +import org.jamdev.jdl4pam.transforms.DLTransfromParams; +import org.jamdev.jdl4pam.transforms.SimpleTransformParams; + +import rawDeepLearningClassifier.dlClassification.DLClassName; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericModelParams; +import rawDeepLearningClassifier.layoutFX.exampleSounds.ExampleSoundFactory.ExampleSoundType; + +/** + * Right whale model from Shiu et al. 2019 + *

    + * *

    + * Shiu, Y., Palmer, K.J., Roch, M.A., Fleishman, E., Liu, X., Nosal, E.-M., Helble, T., Cholewiak, D., Gillespie, D., Klinck, H., 2020. + * Deep neural networks for automated detection of marine mammal species. Scientific Reports 10, 607. https://doi.org/10.1038/s41598-020-57549-y + */ +public class RightWhaleModel1 implements DLModel { + + @Override + public String getDescription() { + return "Detects North Atlantic right whales (Eubalaena glacialis)."; + } + + @Override + public String getName() { + return "North Atlantic right whale"; + } + + @Override + public String getCitation() { + return "Shiu, Y., Palmer, K.J., Roch, M.A. et al. Deep neural networks for automated detection of marine mammal species. Sci Rep 10, 607 (2020)"; + } + + @Override + public URI getModelURI() { + try { + return new URI("https://github.com/PAMGuard/deeplearningmodels/raw/master/right_whale_1/model_lenet_dropout_input_conv_all.zip"); + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + + @Override + /** + * Set the model settings once it has loaded. + * @param dlModelSettings - the model settings. + */ + public void setParams(Serializable dlModelSettings) { + + GenericModelParams genericModelParams = (GenericModelParams) dlModelSettings; + + //decimation value + float sr = 2000; + + //create the transforms. + ArrayList dlTransformParamsArr = new ArrayList(); + + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE, sr)); + // dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.PREEMPHSIS, preemphases)); + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.SPECTROGRAM, 256, 102)); + //in the python code they have an sfft of 129xN where N is the number of chunks. They then + //choose fft data between bin 5 and 45 in the FFT. This roughly between 40 and 350 Hz. + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.SPECCROPINTERP, 47.0, 357.0, 40)); + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.SPECNORMALISEROWSUM)); + + + genericModelParams.dlTransfromParams = dlTransformParamsArr; + + genericModelParams.defaultSegmentLen = 2.0*1000; + genericModelParams.binaryClassification = new boolean[] {false, true}; + + genericModelParams.defaultShape= new Long[] {-1L,40l,401L,1L}; + genericModelParams.shape= new Long[] {-1L,40L,40L,1L}; + + genericModelParams.classNames= new DLClassName[] {new DLClassName("Noise", (short) 0), new DLClassName("Right Whale", (short) 1)}; + genericModelParams.numClasses = 2; + + //create the transforms. + genericModelParams.dlTransfroms = DLTransformsFactory.makeDLTransforms((ArrayList)genericModelParams.dlTransfromParams); + + genericModelParams.setExampleSound(ExampleSoundType.RIGHT_WHALE); + + } + + @Override + public String getModelName() { + return "saved_model.pb"; + } + + @Override + public URI getCitationLink() { + try { + return new URI("https://doi.org/10.1038/s41598-020-57549-y"); + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + } + + + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java b/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java index 465fb255..dd0e85b7 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java @@ -1,11 +1,19 @@ package rawDeepLearningClassifier.dlClassification; import java.io.Serializable; +import java.net.URI; import java.util.ArrayList; +import org.jamdev.jdl4pam.transforms.DLTransform; +import org.jamdev.jdl4pam.transforms.DLTransfromParams; +import org.jamdev.jdl4pam.transforms.SimpleTransform; +import org.jamdev.jdl4pam.transforms.SimpleTransformParams; + +import PamguardMVC.PamDataUnit; +import javafx.scene.Node; import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.DLStatus; import rawDeepLearningClassifier.layoutFX.DLCLassiferModelUI; -import rawDeepLearningClassifier.segmenter.SegmenterProcess.GroupedRawData; import warnings.PamWarning; /** @@ -25,12 +33,26 @@ public interface DLClassiferModel { * * @return the deep learning model. */ - public ArrayList runModel(ArrayList rawDataUnit); + public ArrayList runModel(ArrayList rawDataUnit); /** * Prepare the model. This is called on PAMGuard start up. + * @param file */ public void prepModel(); + + /** + * Called whenever PAMGuard stops. + * @return + */ + public DLStatus setModel(URI model); + + /** + * Check whether a URI is compatible with a classification framework + * @param model - the URI to the model + * @return true if the model is compatible. + */ + public boolean isModelType(URI model); /** * Called whenever PAMGuard stops. @@ -81,14 +103,32 @@ public interface DLClassiferModel { /** * Check whether a model has been selected and can be loaded successfully. */ - public boolean checkModelOK(); + public DLStatus getModelStatus(); + +// /** +// * Get warnings for the classifier model. This is called when the user confirms settings and +// * used to return a warning dialog. +// * @return a list of warnings. If the list is null or size() is zero then settings are OK. +// */ +// public ArrayList checkSettingsOK(); + /** - * Get warnings for the classifier model. This is called when the user confirms settings and - * used to return a warning dialog. - * @return a list of warnings. If the list is null or size() is zero then settings are OK. + * Get the parameters which can be serialized from transforms. + * @param dlTransfroms- the dl transforms. */ - public ArrayList checkSettingsOK(); + public static ArrayList getDLTransformParams(ArrayList dlTransfroms) { + ArrayList dlTransformParams = new ArrayList(); + + if (dlTransfroms==null) return null; + //need to set the generic model params. + for (int i=0; i> queue = Collections.synchronizedList(new ArrayList>()); + private List> queue = Collections.synchronizedList(new ArrayList>()); public DLTaskThread(DLModelWorker soundSpotWorker) { @@ -38,9 +38,9 @@ public abstract class DLTaskThread extends Thread { public void stopTaskThread() { run.set(false); //Clean up daemon. - if (dlModelWorker!=null) { - dlModelWorker.closeModel(); - } +// if (dlModelWorker!=null) { +// dlModelWorker.closeModel(); +// } dlModelWorker = null; } @@ -50,7 +50,7 @@ public abstract class DLTaskThread extends Thread { try { if (queue.size()>0) { System.out.println("DL TASK THREAD: " + "The queue size is " + queue.size()); - ArrayList groupedRawData = queue.remove(0); + ArrayList groupedRawData = queue.remove(0); ArrayList modelResult = dlModelWorker.runModel(groupedRawData, groupedRawData.get(0).getParentDataBlock().getSampleRate(), 0); //TODO channel? @@ -79,17 +79,17 @@ public abstract class DLTaskThread extends Thread { * @param soundSpotResult - the new result. * @param groupedRawData - the grouped data unit. */ - public abstract void newDLResult(GenericPrediction soundSpotResult, GroupedRawData groupedRawData); + public abstract void newDLResult(GenericPrediction soundSpotResult, PamDataUnit groupedRawData); /** * Get the grouped data queue * @return */ - public List> getQueue() { + public List> getQueue() { return queue; } - public void setQueue(List> queue) { + public void setQueue(List> queue) { this.queue = queue; } diff --git a/src/rawDeepLearningClassifier/dlClassification/DefaultModels.java b/src/rawDeepLearningClassifier/dlClassification/DefaultModels.java new file mode 100644 index 00000000..dcbd3cf6 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/DefaultModels.java @@ -0,0 +1,70 @@ +package rawDeepLearningClassifier.dlClassification; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import javafx.stage.FileChooser.ExtensionFilter; +import rawDeepLearningClassifier.DLControl; + +public class DefaultModels { + + /** + * The default model list. + */ + ArrayList defaultModels; + + /** + * The dlControl. + */ + private DLControl dlControl; + + public DefaultModels(DLControl dlControl) { + this.dlControl = dlControl; + defaultModels = new ArrayList(); + + /***/ + + defaultModels.add(new DefualtModel("Right Whale", null)); + + + } + + + public class DefualtModel { + + public DefualtModel(String name, URI filename) { + this.name = name; + this.filename = filename; + } + + /** + * The name of the model. + */ + public String name; + + /** + * The filename of the model + */ + public URI filename; + + /** + * Get the DLClassifier associated with the model. + * Technically this could be overridden to provide a different DLClassifierModel. + * @return the default model classifier. + */ + public DLClassiferModel getDLClassifier() { + return dlControl.getDlClassifierChooser().selectClassiferModel(filename); + } + + } + + /** + * Get the default models. + * @return the default models. + */ + public List getDefaultModels() { + return defaultModels; + } + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java b/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java new file mode 100644 index 00000000..f4368052 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java @@ -0,0 +1,320 @@ +package rawDeepLearningClassifier.dlClassification; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.codehaus.plexus.util.FileUtils; + +import PamController.PamSettings; +import PamDetection.RawDataUnit; +import PamUtils.PamArrayUtils; +import PamUtils.PamCalendar; +import PamguardMVC.PamDataUnit; +import javafx.stage.FileChooser.ExtensionFilter; +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.DLStatus; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericDLClassifier; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; +import rawDeepLearningClassifier.layoutFX.DLSettingsPane; +import rawDeepLearningClassifier.segmenter.GroupedRawData; +import warnings.PamWarning; +import warnings.WarningSystem; + +/** + * A useful abstract class for standard models which are a file or URL that is loaded, have a UI and + * utilise PAMSettings to save settings state. These models only accept raw sound data segments. + */ +public abstract class StandardClassifierModel implements DLClassiferModel, PamSettings { + + + protected DLControl dlControl; + + /** + * True to force the classifier to use a queue - used for simulating real time operation. + */ + private boolean forceQueue = false; + + /** + * The worker thread has a buffer so that Standard models can be run + * in real time without slowing down the rest of PAMGaurd. + */ + private TaskThread workerThread; + + + public StandardClassifierModel(DLControl dlControl) { + this.dlControl=dlControl; + } + + /** + * Sound spot warning. + */ + PamWarning dlClassifierWarning = new PamWarning(getName(), "",2); + + + @Override + @SuppressWarnings("rawtypes") + public ArrayList runModel( ArrayList groupedRawData) { + if (getDLWorker().isModelNull()) return null; + + // System.out.println("SoundSpotClassifier: PamCalendar.isSoundFile(): " + // + PamCalendar.isSoundFile() + " " + (PamCalendar.isSoundFile() && !forceQueue)); + + + + /** + * If a sound file is being analysed then Ketos can go as slow as it wants. if used in real time + * then there is a buffer with a maximum queue size. + */ + if ((PamCalendar.isSoundFile() && !forceQueue) || dlControl.isViewer()) { + //run the model + @SuppressWarnings("unchecked") + ArrayList modelResult = (ArrayList) getDLWorker().runModel(groupedRawData, + groupedRawData.get(0).getParentDataBlock().getSampleRate(), 0); + + if (modelResult==null) { + dlClassifierWarning.setWarningMessage(getName() + " deep learning model returned null"); + WarningSystem.getWarningSystem().addWarning(dlClassifierWarning); + return null; + } + + for (int i =0; iDLModelWorker.MAX_QUEUE_SIZE) { + //we are not doing well - clear the buffer + workerThread.getQueue().clear(); + } + workerThread.getQueue().add(groupedRawData); + } + return null; + } + + + @Override + public void prepModel() { +// System.out.println("STANDARD CLASSIFIER MODEL PREP MODEL! !!!"); +// StandardModelParams oldParams = getDLParams().clone(); + + getDLWorker().prepModel(getDLParams(), dlControl); + + + if (getDLWorker().isModelNull()) { + dlClassifierWarning.setWarningMessage("There is no loaded " + getName() + " classifier model. " + getName() + " disabled."); + WarningSystem.getWarningSystem().addWarning(dlClassifierWarning); + } + + + if ((!PamCalendar.isSoundFile() || forceQueue) && !dlControl.isViewer()) { + //for real time only + if (workerThread!=null) { + workerThread.stopTaskThread(); + } + + workerThread = new TaskThread(getDLWorker()); + workerThread.setPriority(Thread.MAX_PRIORITY); + workerThread.start(); + } + } + + + /** + * Get the sound spot worker. + * @return the sound spot worker. + */ + public abstract DLModelWorker getDLWorker(); + + /** + * Get the sound spot worker. + * @return the sound spot worker. + */ + public abstract StandardModelParams getDLParams(); + + + public DLControl getDLControl() { + return dlControl; + } + + + @Override + public int getNumClasses() { + return this.getDLParams().numClasses; + } + + @Override + public DLClassName[] getClassNames() { + return getDLParams().classNames; + } + + @Override + public DLStatus getModelStatus() { + if (getDLWorker().isModelNull()) { + return DLStatus.MODEL_LOAD_FAILED; + } + + File file = new File(getDLParams().modelPath); + if (getDLParams().modelPath == null || !file.isFile()) { + return DLStatus.NO_MODEL_LOADED; + } + + // if continuous data is selected and all classes are false then this is a + // potential mistake... + if (dlControl.getSettingsPane().getSelectedParentDataBlock().getUnitClass() == RawDataUnit.class + && (getDLParams().binaryClassification==null || PamArrayUtils.isAllFalse(getDLParams().binaryClassification))){ + return DLStatus.NO_BINARY_CLASSIFICATION; +// warnings.add(new PamWarning("Generic classifier", +// "There are no prediction classes selected for classification. " +// + "Predicitons for each segment will be saved but there will be no detections generated", +// 1)); + } + return DLStatus.MODEL_LOAD_SUCCESS; + } + + @Override + public DLStatus setModel(URI uri) { + //will change the params if we do not clone. + StandardModelParams.setModel(uri, this.getDLParams()); + this.prepModel(); + return getModelStatus(); + } + + + + /** + * The task thread. + * @author Jamie Macaulay + * + */ + public class TaskThread extends DLTaskThread { + + public TaskThread(DLModelWorker soundSpotWorker) { + super(soundSpotWorker); + } + + @Override + public void newDLResult(GenericPrediction soundSpotResult, PamDataUnit groupedRawData) { + soundSpotResult.setClassNameID(GenericDLClassifier.getClassNameIDs(getDLParams())); + soundSpotResult.setBinaryClassification(GenericDLClassifier.isBinaryResult(soundSpotResult, getDLParams())); + newResult(soundSpotResult, groupedRawData); + } + + } + + + @Override + public void closeModel() { + getDLWorker().closeModel(); + } + + + /** + * Send a new result form the thread queue to the process. + * @param modelResult - the model result; + * @param groupedRawData - the grouped raw data. + */ + protected void newResult(GenericPrediction modelResult, PamDataUnit groupedRawData) { + if (groupedRawData instanceof GroupedRawData) { + this.dlControl.getDLClassifyProcess().newModelResult(modelResult, (GroupedRawData) groupedRawData); + } + } +// +// @Override +// public ArrayList checkSettingsOK() { +// return checkSettingsOK(getDLParams(), dlControl); +// } + + + /** + * Get the number of samples for microseconds. Based on the sample rate of the parent data block. + */ + public double millis2Samples(double millis) { + //System.out.println("Samplerate: " + this.dlControl.getSegmenter().getSampleRate() ); + return millis*this.dlControl.getSegmenter().getSampleRate()/1000.0; + } + + + /** + * Get raw settings pane + * @return the setting pane. + */ + public DLSettingsPane getRawSettingsPane() { + return this.dlControl.getSettingsPane(); + } + + + public boolean isModelExtensions(URI uri){ +// System.out.println("Check: " + getName() + " extensions"); + String url; + try { + url = uri.toURL().getPath(); + + if (url.contains(".")) { + String extension = FileUtils.getExtension(url); +// System.out.println("Check: " + getName() + " file extension " + extension); + + List pExtensions = this.getModelUI().getModelFileExtensions(); + for (ExtensionFilter extF : pExtensions) { + for (String ext : extF.getExtensions()) { +// System.out.println(getName() + " Extensions: " + ext + " " + extension + " " + ext.equals(extension.substring(2).trim()) + " " + extension.substring(2).trim()); + if (extension.equals(ext.substring(2))) { + return true; + } + } + } + + } + return false; + } catch (MalformedURLException e) { + e.printStackTrace(); + } + return false; + } + + + @Deprecated + public static ArrayList checkSettingsOK(StandardModelParams genericModelParams, DLControl dlControl) { + + // TODO - check if model is null. + // check that classifier is selected if continous files. + // + ArrayList warnings = new ArrayList(); + + File file = new File(genericModelParams.modelPath); + if (genericModelParams.modelPath == null || !file.isFile()) { + warnings.add( + new PamWarning("Generic classifier", "There is no model loaded - the classifier will not run", 2)); + // if no model then this is ionly message needed for the generic classifier. + return warnings; + } + + // if continous data is selected and all classes are false then this is a + // potential mistake... + if (dlControl.getSettingsPane().getSelectedParentDataBlock().getUnitClass() == RawDataUnit.class + && PamArrayUtils.isAllFalse(genericModelParams.binaryClassification)) { + warnings.add(new PamWarning("Generic classifier", + "There are no prediction classes selected for classification. " + + "Predicitons for each segment will be saved but there will be no detections generated", + 1)); + } + + return warnings; + + } + + + + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotClassifier.java b/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotClassifier.java index 17d3d58e..214149a3 100644 --- a/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotClassifier.java +++ b/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotClassifier.java @@ -1,25 +1,22 @@ package rawDeepLearningClassifier.dlClassification.animalSpot; import java.io.Serializable; +import java.net.URI; + import java.util.ArrayList; -import java.util.Collections; -import java.util.List; + +import org.jamdev.jdl4pam.animalSpot.AnimalSpotModel; +import org.jamdev.jdl4pam.transforms.DLTransformsFactory; +import org.jamdev.jdl4pam.transforms.DLTransfromParams; + import PamController.PamControlledUnitSettings; import PamController.PamSettingManager; -import PamController.PamSettings; -import PamUtils.PamCalendar; import rawDeepLearningClassifier.DLControl; -import rawDeepLearningClassifier.dlClassification.DLClassName; import rawDeepLearningClassifier.dlClassification.DLClassiferModel; -import rawDeepLearningClassifier.dlClassification.DLTaskThread; +import rawDeepLearningClassifier.dlClassification.StandardClassifierModel; import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker; -import rawDeepLearningClassifier.dlClassification.genericModel.GenericDLClassifier; import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; import rawDeepLearningClassifier.layoutFX.DLCLassiferModelUI; -import rawDeepLearningClassifier.layoutFX.RawDLSettingsPane; -import rawDeepLearningClassifier.segmenter.SegmenterProcess.GroupedRawData; -import warnings.PamWarning; -import warnings.WarningSystem; /** * A deep learning classifier running models wihc have been created using the @@ -40,12 +37,7 @@ import warnings.WarningSystem; * @author JamieMacaulay * */ -public class SoundSpotClassifier implements DLClassiferModel, PamSettings { - - /** - * Reference to the control. - */ - private DLControl dlControl; +public class SoundSpotClassifier extends StandardClassifierModel { /** * The user interface for sound spot. @@ -61,87 +53,125 @@ public class SoundSpotClassifier implements DLClassiferModel, PamSettings { /** * The deep learning model worker. */ - private SoundSpotWorker soundSpotWorker; + private DLModelWorker soundSpotWorker; - /** - * True to force the classifier to use a queue - used for simulating real time operation. - */ - private boolean forceQueue = false; - - /** - * Sound spot warning. - */ - PamWarning soundSpotWarning = new PamWarning("AnimalSpot_Classifier", "",2); - - /** - * Runs the deep leanring on a different thread with a data buffer. For real time only. - */ - private DLTaskThread workerThread; public SoundSpotClassifier(DLControl dlControl) { - this.dlControl=dlControl; + super(dlControl); this.soundSpotParmas = new StandardModelParams(); this.soundSpotUI= new SoundSpotUI(this); //load the previous settings PamSettingManager.getInstance().registerSettings(this); - - System.out.println("LOADED CLASS NAMES: currParams.classNames: " + soundSpotParmas.classNames); +// System.out.println("LOADED CLASS NAMES: currParams.classNames: " + soundSpotParmas.classNames); + } + + + @Override + public String getName() { + return "AnimalSpot"; + } + + @Override + public DLCLassiferModelUI getModelUI() { + return soundSpotUI; + } + + @Override + public Serializable getDLModelSettings() { + return soundSpotParmas; + } + + @Override + public String getUnitName() { + return dlControl.getUnitName()+"_SoundSpot"; + } + + @Override + public String getUnitType() { + return dlControl.getUnitType()+"_SoundSpot"; + } + + @Override + public Serializable getSettingsReference() { + if (soundSpotParmas==null) { + soundSpotParmas = new StandardModelParams(); + } + + ArrayList dlTransformParams = DLClassiferModel.getDLTransformParams(soundSpotParmas.dlTransfroms); + + soundSpotParmas.dlTransfromParams=dlTransformParams; + + + //System.out.println("SoundSpot have been saved. : " + soundSpotParmas.classNames); + return soundSpotParmas; } @Override - public ArrayList runModel(ArrayList groupedRawData) { + public long getSettingsVersion() { + return StandardModelParams.serialVersionUID; + } - // System.out.println("SoundSpotClassifier: PamCalendar.isSoundFile(): " - // + PamCalendar.isSoundFile() + " " + (PamCalendar.isSoundFile() && !forceQueue)); - /** - * If a sound file is being analysed then SoundSpot can go as slow as it wants. if used in real time - * then there is a buffer with a maximum queue size. - */ - if ((PamCalendar.isSoundFile() && !forceQueue) || dlControl.isViewer()) { - //run the model - ArrayList modelResult = getSoundSpotWorker().runModel(groupedRawData, - groupedRawData.get(0).getParentDataBlock().getSampleRate(), 0); - for (int i =0; i) soundSpotParmas.dlTransfromParams); } - - return modelResult; //returns to the classifier. } - else { - //add to a buffer if in real time. - if (workerThread.getQueue().size()>DLModelWorker.MAX_QUEUE_SIZE) { - //we are not doing well - clear the buffer - workerThread.getQueue().clear(); - } - workerThread.getQueue().add(groupedRawData); - } - return null; + else soundSpotParmas = new StandardModelParams(); + return true; } /** - * Check whether a model passes a binary test... - * @param modelResult - the model results - * @return the model results. + * Get the sound spot parameters. + * @return sound spot parameters. */ - private boolean isBinaryResult(GenericPrediction modelResult) { - for (int i=0; isoundSpotParmas.threshold && soundSpotParmas.binaryClassification[i]) { - //System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]); - return true; - } - } - return false; + public StandardModelParams getSoundSpotParams() { + return soundSpotParmas; } /** - * Get the sound spot worker. - * @return the sound spot worker. + * Set the sound spot parameters. + * @param the params to set */ - SoundSpotWorker getSoundSpotWorker() { + public void setSoundSpotParams(StandardModelParams soundSpotParmas) { + this.soundSpotParmas=soundSpotParmas; + } + + + + @Override + public boolean isModelType(URI uri) { + //TODO need to be more sophisticated here. + if (super.isModelExtensions(uri)) { + //we have a PyTorch model but is it animal spot. + + try { + AnimalSpotModel soundSpotModel = new AnimalSpotModel(uri.getPath()); + if (soundSpotModel!=null && soundSpotModel.getExtraFiles()!=null) { + return true; + } + return false; + } + + catch (Exception e) { + return false; + } + + } + + return false; + } + + + @Override + public DLModelWorker getDLWorker() { if (soundSpotWorker==null) { soundSpotWorker = new SoundSpotWorker(); } @@ -149,27 +179,33 @@ public class SoundSpotClassifier implements DLClassiferModel, PamSettings { } - /** - * The task thread. - * @author Jamie Macaulay - * - */ - public class TaskThread extends DLTaskThread { - - TaskThread(DLModelWorker soundSpotWorker) { - super(soundSpotWorker); - } - - @Override - public void newDLResult(GenericPrediction soundSpotResult, GroupedRawData groupedRawData) { - soundSpotResult.setClassNameID(getClassNameIDs()); - soundSpotResult.setBinaryClassification(isBinaryResult(soundSpotResult)); - newResult(soundSpotResult, groupedRawData); - } + @Override + public StandardModelParams getDLParams() { + return soundSpotParmas; } + +// /** +// * Check whether a model passes a binary test... +// * @param modelResult - the model results +// * @return the model results. +// */ +// private boolean isBinaryResult(GenericPrediction modelResult) { +// for (int i=0; isoundSpotParmas.threshold && soundSpotParmas.binaryClassification[i]) { +// //System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]); +// return true; +// } +// } +// return false; +// } + + + + + // public class TaskThread extends Thread { // // private AtomicBoolean run = new AtomicBoolean(true); @@ -223,171 +259,19 @@ public class SoundSpotClassifier implements DLClassiferModel, PamSettings { // // } - /** - * Get the class name IDs - * @return an array of class name IDs - */ - private short[] getClassNameIDs() { - if (soundSpotParmas.classNames==null || soundSpotParmas.classNames.length<=0) return null; - short[] nameIDs = new short[soundSpotParmas.classNames.length]; - for (int i = 0 ; i checkSettingsOK() { - return GenericDLClassifier.checkSettingsOK(soundSpotParmas, dlControl); - } } diff --git a/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotModelPane.java b/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotModelPane.java index ba61ffea..457a9b0a 100644 --- a/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotModelPane.java +++ b/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotModelPane.java @@ -11,20 +11,14 @@ public class SoundSpotModelPane extends StandardModelPane { * Reference to the currently selected sound spot classifier. */ private SoundSpotClassifier soundSpotClassifier; - - /** - * The extension filter for sound spot models. - */ - private ArrayList extensionFilters; + public SoundSpotModelPane(SoundSpotClassifier soundSpotClassifier) { super(soundSpotClassifier); // TODO Auto-generated constructor stub this.soundSpotClassifier=soundSpotClassifier; - - extensionFilters = new ArrayList (); - extensionFilters.add(new ExtensionFilter("Pytorch Model", "*.pk")); + } @@ -35,7 +29,7 @@ public class SoundSpotModelPane extends StandardModelPane { @Override public void newModelSelected(File file) { - System.out.println("New file model selected:"); + //System.out.println("New file model selected:"); this.setCurrentSelectedFile(file); @@ -46,20 +40,31 @@ public class SoundSpotModelPane extends StandardModelPane { //prep the model with current parameters; StandardModelParams params = getParams(getParamsClone()); - soundSpotClassifier.getSoundSpotWorker().prepModel(params, soundSpotClassifier.getDLControl()); + +// if (params.dlTransfromParams!=null) { +// System.out.println("AnimalSpot: Decimator: " + params.dlTransfromParams.get(0).params[0]); +// } +// else { +// System.out.println("AnimalSpot: params is null" + getParamsClone().dlTransfromParams); +// } + + /** + * Note that the model prep will determine whether new transforms need to be loaded from the + * model or to use the existing transforms in the settings. + */ + soundSpotClassifier.getDLWorker().prepModel(params, soundSpotClassifier.getDLControl()); //get the model tansforms calculated from the model by SoundSpoyWorker and apply them to our temporary params clone. - getParamsClone().dlTransfroms = this.soundSpotClassifier.getSoundSpotWorker().getModelTransforms(); + getParamsClone().dlTransfroms = this.soundSpotClassifier.getDLWorker().getModelTransforms(); +// if (getParamsClone().defaultSegmentLen!=null) { +// usedefaultSeg.setSelected(true); +// } + ///set the advanced pane parameters. getAdvSettingsPane().setParams(getParamsClone()); } - @Override - public ArrayList getExtensionFilters() { - return extensionFilters; - } - } diff --git a/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotUI.java b/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotUI.java index 87ecf231..b13b8feb 100644 --- a/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotUI.java +++ b/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotUI.java @@ -1,8 +1,13 @@ package rawDeepLearningClassifier.dlClassification.animalSpot; +import java.util.ArrayList; +import java.util.List; + import javax.swing.JPanel; import PamController.SettingsPane; +import javafx.scene.Node; +import javafx.stage.FileChooser.ExtensionFilter; import rawDeepLearningClassifier.layoutFX.DLCLassiferModelUI; @@ -24,12 +29,21 @@ public class SoundSpotUI implements DLCLassiferModelUI { */ private SoundSpotClassifier soundSpotClassifier; + + /** + * The extension filter for sound spot models. + */ + private ArrayList extensionFilters; + /** * SondSpot classifier. * @param soundSpotClassifier */ public SoundSpotUI(SoundSpotClassifier soundSpotClassifier) { this.soundSpotClassifier=soundSpotClassifier; + + extensionFilters = new ArrayList (); + extensionFilters.add(new ExtensionFilter("Pytorch Model", "*.pk")); } @Override @@ -59,4 +73,21 @@ public class SoundSpotUI implements DLCLassiferModelUI { return null; } + /** + * Get a list of extension fitlers for the file dialog. + * e.g. + * new ExtensionFilter("Pytorch Model", "*.pk") + * @return a list of extension fitlers for the file dialog. + */ + @Override + public List getModelFileExtensions() { + return extensionFilters; + } + + @Override + public Node getIcon() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotWorker.java b/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotWorker.java index 095fa2d5..b7115630 100644 --- a/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotWorker.java +++ b/src/rawDeepLearningClassifier/dlClassification/animalSpot/SoundSpotWorker.java @@ -1,12 +1,13 @@ package rawDeepLearningClassifier.dlClassification.animalSpot; +import java.nio.file.Paths; + import org.jamdev.jdl4pam.animalSpot.AnimalSpotModel; import org.jamdev.jdl4pam.animalSpot.AnimalSpotParams; -import PamModel.PamModel; -import PamModel.PamModel.PluginClassloader; import rawDeepLearningClassifier.DLControl; import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; /** @@ -18,13 +19,16 @@ import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker; * @author Jamie Macaulay * */ -public class SoundSpotWorker extends DLModelWorker { +public class SoundSpotWorker extends DLModelWorker { /** * Sound spot model. */ - private AnimalSpotModel soundSpotModel; + private AnimalSpotModel soundSpotModel; + + + private String currentPath; /** @@ -39,8 +43,8 @@ public class SoundSpotWorker extends DLModelWorker { */ public void prepModel(StandardModelParams soundSpotParams, DLControl dlControl) { //ClassLoader origCL = Thread.currentThread().getContextClassLoader(); - - System.out.println("prepModel: " + soundSpotParams.useDefaultTransfroms); + + //System.out.println("prepModel: " + soundSpotParams.useDefaultTransfroms); try { @@ -52,9 +56,13 @@ public class SoundSpotWorker extends DLModelWorker { // PluginClassloader newCL = PamModel.getPamModel().getClassLoader(); // Thread.currentThread().setContextClassLoader(newCL); // } + if (soundSpotModel==null || currentPath ==null || !Paths.get(currentPath).equals(Paths.get(soundSpotParams.modelPath))) { + //System.out.println("Sound spot path: " + soundSpotParams.modelPath); + //first open the model and get the correct parameters. + soundSpotModel = new AnimalSpotModel(soundSpotParams.modelPath); + this.currentPath = soundSpotParams.modelPath; - //first open the model and get the correct parameters. - soundSpotModel = new AnimalSpotModel(soundSpotParams.modelPath); + } } catch (Exception e) { e.printStackTrace(); @@ -66,7 +74,7 @@ public class SoundSpotWorker extends DLModelWorker { AnimalSpotParams dlParams = new AnimalSpotParams(soundSpotModel.getTransformsString()); //only load new transforms if defaults are selected - if (getModelTransforms()==null || soundSpotParams.useDefaultTransfroms) { + if (getModelTransforms()==null || soundSpotParams.dlTransfroms==null || soundSpotParams.useDefaultTransfroms) { //only set the transforms if they are null - otherwise handled elsewhere. setModelTransforms(model2DLTransforms(dlParams)); soundSpotParams.useDefaultTransfroms = true; @@ -75,7 +83,7 @@ public class SoundSpotWorker extends DLModelWorker { //use the old transforms. setModelTransforms(soundSpotParams.dlTransfroms); } - + soundSpotParams.defaultSegmentLen = dlParams.seglen; //the segment length in microseconds. soundSpotParams.numClasses = dlParams.classNames.length; @@ -132,7 +140,8 @@ public class SoundSpotWorker extends DLModelWorker { * Destroy the model. */ public void closeModel() { - //TODO + //TODO - need to be able to access model in JPAM API. + this.currentPath = null; } @@ -145,4 +154,10 @@ public class SoundSpotWorker extends DLModelWorker { } + @Override + public boolean isModelNull() { + return soundSpotModel==null; + } + + } diff --git a/src/rawDeepLearningClassifier/dlClassification/animalSpot/StandardAdvModelPane.java b/src/rawDeepLearningClassifier/dlClassification/animalSpot/StandardAdvModelPane.java index 8684bfcb..1842dada 100644 --- a/src/rawDeepLearningClassifier/dlClassification/animalSpot/StandardAdvModelPane.java +++ b/src/rawDeepLearningClassifier/dlClassification/animalSpot/StandardAdvModelPane.java @@ -1,6 +1,10 @@ package rawDeepLearningClassifier.dlClassification.animalSpot; +import java.util.ArrayList; + import org.controlsfx.control.ToggleSwitch; +import org.jamdev.jdl4pam.transforms.DLTransfromParams; + import PamController.SettingsPane; import javafx.geometry.Insets; import javafx.scene.Node; @@ -106,13 +110,18 @@ public class StandardAdvModelPane extends SettingsPane { @Override public StandardModelParams getParams(StandardModelParams currParams) { currParams.dlTransfroms = transfromPane.getDLTransforms(); + + currParams.dlTransfromParams = new ArrayList(); +// for (int i=0; i { BorderPane.setMargin(defaultTogglePane, new Insets(5)); transfromPane.setDisable(params.useDefaultTransfroms); toggleSwitch.setSelected(params.useDefaultTransfroms); + transfromPane.setTransforms(params.dlTransfroms); //set the image mainPane.setBottom(dlImage); diff --git a/src/rawDeepLearningClassifier/dlClassification/animalSpot/StandardModelPane.java b/src/rawDeepLearningClassifier/dlClassification/animalSpot/StandardModelPane.java index ce694ccf..231a6d3e 100644 --- a/src/rawDeepLearningClassifier/dlClassification/animalSpot/StandardModelPane.java +++ b/src/rawDeepLearningClassifier/dlClassification/animalSpot/StandardModelPane.java @@ -1,35 +1,27 @@ package rawDeepLearningClassifier.dlClassification.animalSpot; import java.io.File; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.util.ArrayList; - import org.controlsfx.control.CheckComboBox; +import org.controlsfx.control.IndexedCheckModel; import org.controlsfx.control.PopOver; import org.controlsfx.control.ToggleSwitch; import PamController.SettingsPane; +import PamUtils.PamArrayUtils; import PamView.dialog.warn.WarnOnce; -import ai.djl.Device; -import javafx.application.Platform; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.ListChangeListener.Change; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; -import javafx.scene.control.Labeled; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.Spinner; import javafx.scene.control.Tooltip; import javafx.scene.control.Alert.AlertType; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; -import javafx.stage.FileChooser; -import javafx.stage.FileChooser.ExtensionFilter; import pamViewFX.PamGuiManagerFX; import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; @@ -38,7 +30,7 @@ import pamViewFX.fxNodes.PamGridPane; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamSpinner; import pamViewFX.fxNodes.PamVBox; -import pamViewFX.fxNodes.flipPane.FlipPane; +import pamViewFX.validator.PamValidator; import rawDeepLearningClassifier.dlClassification.DLClassiferModel; /** @@ -54,20 +46,12 @@ public abstract class StandardModelPane extends SettingsPane{ - - fileChooser.getExtensionFilters().clear(); - fileChooser.getExtensionFilters().addAll(getExtensionFilters()); - - - Path path = currentSelectedFile.toPath(); - if(path!=null && Files.exists(path, LinkOption.NOFOLLOW_LINKS)) { - fileChooser.setInitialDirectory(new File(currentSelectedFile.getParent())); - } - else { - fileChooser.setInitialDirectory(new File(System.getProperty("user.home"))); - } - - File file = fileChooser.showOpenDialog(null); - - if (file==null) { - return; - } - - modelLoadIndicator.setVisible(true); - - pathLabel.setText("Loading model..."); - - //whenever a new model is selected then the the paramters should be set to use defualt transforms again - this ensures - //that the new transforms are loaded up - advSettingsPane.getParams(paramsClone); - paramsClone.useDefaultTransfroms=true; - advSettingsPane.setParams(paramsClone); - - - // separate non-FX thread - load the model - //on a separate thread so we can show a moving load - //bar on the FX thread. Otherwise the GUI locks up - //whilst stuff is loaded. - new Thread() { - // runnable for that thread - public void run() { - try { - newModelSelected(file); - Thread.sleep(5000); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - Platform.runLater(new Runnable() { - public void run() { - modelLoadIndicator.setVisible(false); - updatePathLabel(); - setClassNames(paramsClone); - } - }); - } - }.start(); - - - - }); - - PamHBox hBox = new PamHBox(); - hBox.setSpacing(5); - hBox.getChildren().addAll(modelLoadIndicator, pathLabel, pamButton); - hBox.setAlignment(Pos.CENTER_RIGHT); // PamButton advButton = new PamButton("", PamGlyphDude.createPamGlyph(MaterialDesignIcon.SETTINGS, PamGuiManagerFX.iconSize)); PamButton advButton = new PamButton("", PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconSize)); @@ -240,6 +161,10 @@ public abstract class StandardModelPane extends SettingsPane{ defaultSegmentLenChanged(); + //only set the hop if the user physically changes the toggle switch. This is not included in defaultSegmentLenChanged + //becuase defaultSegmentLenChanged can be called from elsewhere + int defaultsamples = getDefaultSamples(); + dlClassifierModel.getDLControl().getSettingsPane().getHopLenSpinner().getValueFactory().setValue((int) defaultsamples/2); }); usedefaultSeg.setPadding(new Insets(0,0,0,0)); //there is an issue with the toggle switch which means that it has dead space to the left if @@ -262,7 +187,9 @@ public abstract class StandardModelPane extends SettingsPane(0.0, 1.0, 0.9, 0.1), 1, 0); - detectionSpinner.setPrefWidth(80); + detectionSpinner.setPrefWidth(70); detectionSpinner.setEditable(true); detectionSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + detectionSpinner.setTooltip(new Tooltip("Set the minimum prediciton value for selected classes. If a prediction exceeds this value " + + "a detection will be saved.")); + gridPane.add(new Label(""), 2, 0); speciesIDBox = new CheckComboBox(); gridPane.add(speciesIDBox, 3, 0); - speciesIDBox.setMaxWidth(100); - speciesIDBox.setPrefWidth(100); + //speciesIDBox.setMaxWidth(100); +// speciesIDBox.setPrefWidth(100); speciesIDBox.prefHeightProperty().bind(detectionSpinner.heightProperty()); + speciesIDBox.setMaxWidth(150); //otherwise expands too much if multiple classes selected + speciesIDBox.setTooltip(new Tooltip("Select output classes to use for binary classification. " + + "If the prediction value of a selected class exceeds the minimum prediction threshold then a detection is saved. ")); + validator = new PamValidator(); + + final SimpleIntegerProperty checkItemsCount = new SimpleIntegerProperty(); + + speciesIDBox.getCheckModel().getCheckedItems().addListener((Change c)->{ + checkItemsCount.set(speciesIDBox.getCheckModel().getCheckedItems().size()); + }); + + + validator.createCheck() + .dependsOn("species_box",checkItemsCount) + .withMethod(c -> { + int nChecked = c.get("species_box"); + + if (nChecked==speciesIDBox.getItems().size() && speciesIDBox.getItems().size()>1) { + c.warn("All output class are checked. If one of these classes is noise then PAMGuard will continually detect all sound data..."); + } + + if (nChecked==0) { + c.warn("No output classes are checked for binary classification. PAMGuard will save all prediction values but no detections will be generated"); + } + }) + .decorates(speciesIDBox) + .immediate(); + ; vBoxHolder = new PamVBox(); vBoxHolder.setSpacing(5); - vBoxHolder.getChildren().addAll(classiferInfoLabel, hBox, advSettings, classiferInfoLabel2, gridPane); + vBoxHolder.getChildren().addAll(classifierIcon, advSettings, classiferInfoLabel2, gridPane); mainPane.setCenter(vBoxHolder); @@ -297,14 +255,6 @@ public abstract class StandardModelPane extends SettingsPane getExtensionFilters(); - /** * The default segment len changed. */ @@ -317,13 +267,13 @@ public abstract class StandardModelPane extends SettingsPanei && currParams.classNames[i]!=null) { @@ -508,7 +445,9 @@ public abstract class StandardModelPane extends SettingsPane dlTransfromParams = null; + /** * The DL custom transforms if the default transforms for the model are not being used. */ @@ -97,11 +101,39 @@ public class StandardModelParams implements Serializable, Cloneable { */ public int exampleSoundIndex = 0; + + /** + * Set the example sound that should be associated with parameters. + * @param soundType - the example sound type. + */ + public void setExampleSound(ExampleSoundType soundType) { + ExampleSoundType[] values = ExampleSoundType.values(); + for (int i=0; i getDLWorker() { + return getModelWorker(); + } + + + @Override + public StandardModelParams getDLParams() { + return standardDLParams; + } + + /** + * Get the parameters for the Ketos classifier. + * @param standardDLParams - the Ketos parameters. + */ + public StandardModelParams getKetosParams() { + return standardDLParams; + } + + + @Override + public Serializable getDLModelSettings() { + return standardDLParams; + } + + /** + * Get the KetosWorker. this handles loading and running the Ketos model. + * @return the Ketos worker. + */ + public ArchiveModelWorker getModelWorker() { + if (archiveModelWorker==null) { + archiveModelWorker= new ArchiveModelWorker(); + } + return archiveModelWorker; + } + + + @Override + public String getUnitName() { + return dlControl.getUnitName()+"_" + getName(); + } + + + @Override + public String getUnitType() { + return dlControl.getUnitType()+"_" + getName(); + } + + + @Override + public Serializable getSettingsReference() { + if (standardDLParams==null) { + standardDLParams = makeParams(); + } + + ArrayList dlTransformParams = DLClassiferModel.getDLTransformParams(standardDLParams.dlTransfroms); + + standardDLParams.dlTransfromParams=dlTransformParams; +// System.out.println("KetosParams have been saved. : " + standardDLParams.dlTransfromParams); + return standardDLParams; + } + + + @Override + public long getSettingsVersion() { + return StandardModelParams.serialVersionUID; + } + + + @Override + public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { + StandardModelParams newParameters = (StandardModelParams) pamControlledUnitSettings.getSettings(); + if (newParameters != null) { + standardDLParams = newParameters.clone(); + System.out.println("KetosParams have been restored. : " + standardDLParams.dlTransfromParams); + if (standardDLParams.dlTransfromParams != null) { + standardDLParams.dlTransfroms = DLTransformsFactory + .makeDLTransforms((ArrayList) standardDLParams.dlTransfromParams); + } + } + else { + standardDLParams = makeParams(); + } + return true; + } + + /** + *Create the parameters class for the model. This can be overridden for bespoke parameters. + *classes. + * @return a new parameters class object. + */ + public StandardModelParams makeParams() { + return new StandardModelParams(); + } + + @Override + public boolean isModelType(URI uri) { + return super.isModelExtensions(uri); + } + + + /** + * Get the file extensions for the model type. + * @return the file extension. + */ + public String[] getFileExtensions() { + return fileExtensions; + } + + + + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/archiveModel/ArchiveModelPane.java b/src/rawDeepLearningClassifier/dlClassification/archiveModel/ArchiveModelPane.java new file mode 100644 index 00000000..1f493e92 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/archiveModel/ArchiveModelPane.java @@ -0,0 +1,62 @@ +package rawDeepLearningClassifier.dlClassification.archiveModel; + +import java.io.File; + +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelPane; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.dlClassification.ketos.KetosDLParams; + +public class ArchiveModelPane extends StandardModelPane { + + private ArchiveModelClassifier archiveModelClassifier; + + public ArchiveModelPane(ArchiveModelClassifier archiveModelClassifier) { + super(archiveModelClassifier); + this.archiveModelClassifier = archiveModelClassifier; + + } + + @Override + public void newModelSelected(File file) { + + //the model has to set some of the parameters for the UI . + + //A ketos model contains information on the transforms, duration and the class names. + this.setCurrentSelectedFile(file); + + if (this.getParamsClone()==null) { + this.setParamsClone(new KetosDLParams()); + } + + + StandardModelParams params = getParamsClone(); + + +// if (params.dlTransfromParams!=null) { +// System.out.println("Ketos: Decimator: " + params.dlTransfromParams.get(0).params[0]); +// } +// else { +// System.out.println("Ketos:dltransform params is null" + getParamsClone().dlTransfromParams); +// } + + //prep the model with current parameters; + + /** + * Note that the model prep will determine whether new transforms need to be loaded from the + * model or to use the existing transforms in the settings. + */ + archiveModelClassifier.getDLWorker().prepModel(params, archiveModelClassifier.getDLControl()); + + //get the model transforms calculated from the model by the worker and apply them to our temporary params clone. + getParamsClone().dlTransfroms = this.archiveModelClassifier.getDLWorker().getModelTransforms(); + +// if (getParamsClone().defaultSegmentLen!=null) { +// usedefaultSeg.setSelected(true); +// } + + ///set the advanced pane parameters. + getAdvSettingsPane().setParams(getParamsClone()); + + } + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/archiveModel/ArchiveModelUI.java b/src/rawDeepLearningClassifier/dlClassification/archiveModel/ArchiveModelUI.java new file mode 100644 index 00000000..27fde898 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/archiveModel/ArchiveModelUI.java @@ -0,0 +1,102 @@ +package rawDeepLearningClassifier.dlClassification.archiveModel; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; + +import PamController.SettingsPane; +import javafx.scene.Node; +import javafx.stage.FileChooser.ExtensionFilter; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelPane; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.layoutFX.DLCLassiferModelUI; + +public class ArchiveModelUI implements DLCLassiferModelUI { + + /** + * Pane containing controls to set up the OrcaSPot classifier. + */ + private StandardModelPane standardSettingsPane; + + /** + * The sound spot classifier. + */ + private ArchiveModelClassifier archiveModel; + + /** + * The extension filter. + */ + private ArrayList extensionFilters; + + + + /** + * SondSpot classifier. + * @param soundSpotClassifier + */ + public ArchiveModelUI(ArchiveModelClassifier archiveClassifier) { + this.archiveModel=archiveClassifier; + +// extensionFilters = new ArrayList(); +// +// String[] fileExt = archiveModel.getFileExtensions(); +// +// extensionFilters.add(new ExtensionFilter(archiveClassifier.getName(), fileExt)); + + } + + @Override + public SettingsPane getSettingsPane() { + if (standardSettingsPane==null) { + standardSettingsPane = new ArchiveModelPane(archiveModel); + } + return standardSettingsPane; + } + + @Override + public void getParams() { + StandardModelParams params = getSettingsPane().getParams(archiveModel.getDLParams()); + archiveModel.setDLParams(params); + } + + + @Override + public void setParams() { + // System.out.println("Set model params: " + genericModelClassifier.getGenericDLParams().dlTransfromParams.size()); + getSettingsPane().setParams(archiveModel.getDLParams()); + } + + + @Override + public JPanel getSidePanel() { + //no side pane for Ketos just now. + return null; + } + + @Override + public List getModelFileExtensions() { + if (extensionFilters == null) { + extensionFilters = new ArrayList(); + extensionFilters.add(new ExtensionFilter(archiveModel.getName(), archiveModel.getFileExtensions())); + } + return extensionFilters; + } + + /** + * Set the extension filters. + * @param extensionFilters + */ + public void setExtensionFilters(ArrayList extensionFilters) { + this.extensionFilters = extensionFilters; + } + + @Override + public Node getIcon() { + // TODO Auto-generated method stub + return null; + } + + + +} \ No newline at end of file diff --git a/src/rawDeepLearningClassifier/dlClassification/archiveModel/ArchiveModelWorker.java b/src/rawDeepLearningClassifier/dlClassification/archiveModel/ArchiveModelWorker.java new file mode 100644 index 00000000..ab78a47b --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/archiveModel/ArchiveModelWorker.java @@ -0,0 +1,261 @@ +package rawDeepLearningClassifier.dlClassification.archiveModel; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.jamdev.jdl4pam.ArchiveModel; +import org.jamdev.jdl4pam.genericmodel.GenericModelParams; +import org.jamdev.jdl4pam.transforms.DLTransform; +import org.jamdev.jdl4pam.transforms.DLTransformsFactory; +import org.jamdev.jdl4pam.transforms.FreqTransform; +import org.jamdev.jdl4pam.transforms.jsonfile.DLTransformParser2; +import org.jamdev.jdl4pam.transforms.jsonfile.DLTransformsParser; +import org.json.JSONObject; + +import PamUtils.PamArrayUtils; +import PamView.dialog.warn.WarnOnce; +import ai.djl.MalformedModelException; +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericModelWorker; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; + +/** + * + * Runs a Ketos deep learning model and performs feature extraction. + *

    + * + * @author Jamie Macaulay + * + */ +public class ArchiveModelWorker extends GenericModelWorker { + + + /** + * The ketos model + */ + private ArchiveModel dlModel; + + /** + * Thelast loaded model path., + */ + private String currentPath; + + + /** + * SoundSpotWorker constructor. + */ + public ArchiveModelWorker() { + this.setEnableSoftMax(false); + } + + /** + * Prepare the model + */ + @Override + public void prepModel(StandardModelParams dlParams, DLControl dlControl) { + //ClassLoader origCL = Thread.currentThread().getContextClassLoader(); + try { + + // get the plugin class loader and set it as the context class loader + // NOTE THAT THIS IS REQUIRED TO MAKE THIS MODULE RUN AS A PLUGIN WHEN THE CLASS FILES + // ARE BUNDLED INTO A FATJAR, HOWEVER THIS WILL STOP THE PLUGIN FROM RUNNING AS A SEPARATE + // PROJECT IN ECLIPSE. So while testing the code and debugging, make sure the + // if (DLControl.PLUGIN_BUILD) { + // PluginClassloader newCL = PamModel.getPamModel().getClassLoader(); + // Thread.currentThread().setContextClassLoader(newCL); + // } + //first open the model and get the correct parameters. + //21/11/2022 - Added a null and filename check here to stop the mdoel reloading everytime PAMGuard hits a new file or + //is stopped or started - this was causing a memory leak. + if (dlModel==null || currentPath ==null || !Paths.get(currentPath).equals(Paths.get(dlParams.modelPath))) { + + + //TODO + // if (ketosModel!=null && ketosModel.getModel()!=null) { + // ketosModel.getModel().close(); + // } + + //System.out.println(Paths.get(genericParams.modelPath)); + this.currentPath = dlParams.modelPath; + dlModel = loadModel(currentPath); + //System.out.println("LOAD A NEW MODEL: "); + //System.out.println(genericModel.getModel().getModelPath().getFileName()); + } + } + catch (Exception e) { + e.printStackTrace(); + //WarnOnce.showWarning(null, "Model Load Error", "There was an error loading the model file.", WarnOnce.OK_OPTION); + } + + try { + + //read the JSON string from the the file. + String jsonString = DLTransformsParser.readJSONString(new File(dlModel.getAudioReprFile())); + + + //convert the JSON string to a parameters object. + GenericModelParams modelParams = makeModelParams( jsonString); + + //important to add this for Ketos models because the JSON string does not necessarily contain and output shape. + //System.out.println("----Default output shape: " + ketosParams.defaultOutputShape + " " + ketosModel.getOutShape()); + if (modelParams.defaultOutputShape==null) { + modelParams.defaultOutputShape = dlModel.getOutShape(); + } + + //HACK there seems to be some sort of bug in ketos where the params input shape is correct but the model input shape is wrong. + if (dlModel.getInputShape()==null || !dlModel.getInputShape().equals(modelParams.defaultInputShape)) { + System.err.println("Model input shape does not match: params: " + modelParams.defaultInputShape + " model: " + dlModel.getInputShape()); + //WarnOnce.showWarning("Model shape", "The model shape does not match the model metadata. \n Metadata shape will be used used.", WarnOnce.OK_OPTION); + dlModel.setInputShape(modelParams.defaultInputShape); + } + + + // ///HACK here for now to fix an issue with dB and Ketos transforms having zero length somehow... + // for (int i=0; i transforms = DLTransformsFactory.makeDLTransforms(modelParams.dlTransforms); + +// ///HACK here for now to fix an issue with dB and Ketos transforms having zero length somehow... +// for (int i=0; i + * The model quickly pre checks zip files to make sure there is a settings file inside. + * This means that non compatible zip files are greyed out in the file importer. + *

    + * The model can accept PyTorch or Tensorflow models. + * + * @author Jamie Macaulay + * + */ +public class PamZipModelClassifier extends ArchiveModelClassifier { + + private static final String MODEL_NAME = "PAMGuard model"; + + /** + * The file extensions + */ + private String[] fileExtensions = new String[] {"*.zip"}; + + public PamZipModelClassifier(DLControl dlControl) { + super(dlControl); + } + + @Override + public String getName() { + return MODEL_NAME; + } + + + @Override + public DLStatus setModel(URI zipUri) { + //will change the params if we do not clone. + //first check whether the zip file has the correct model. +// ZipUtils. + + //check that we have some kind of model that we can load here. Do not attempt to load the model if not. + String model; + try { + model = getZipFilePath( zipUri, ".py"); + + if (model==null) model = getZipFilePath( zipUri, ".pb"); + if (model==null) return DLStatus.INCOMPATIBLE_ZIP; + + String settingsFile = getZipFilePath( zipUri, ".pdtf"); + if (settingsFile==null) return DLStatus.INCOMPATIBLE_ZIP; + } catch (ZipException e) { + e.printStackTrace(); + return DLStatus.INCOMPATIBLE_ZIP; + } catch (IOException e) { + e.printStackTrace(); + return DLStatus.INCOMPATIBLE_ZIP; + } + + return super.setModel(zipUri); + } + + /** + * Find the first file within a zip folder that matches a pattern. This peaks into the zip file instead of decompressing it. + * @param zipUri - uri to the zip file + * @param filePattern - the file pattern to match - the file must contain this string. + * @return null if no file found and the file pqth if the file is founf + * @throws ZipException + * @throws IOException + */ + public static String getZipFilePath(URI zipUri, String filePattern) throws ZipException, IOException { + return DLZipUtils.getZipFilePath(new File(zipUri), filePattern); + } + + /** + * Zip test. + * @param args + */ + public static void main(String[] args) { + + String fileName = "D:/Dropbox/PAMGuard_dev/Deep_Learning/Gibbons/gibbon_model_shared/gibbon_model.zip"; + + URI zipUri = new File(fileName).toURI(); + + try { + String pbFile = getZipFilePath( zipUri, ".pb"); + String transformFile = getZipFilePath( zipUri, ".pdtf"); + + System.out.println("pbFile: " + pbFile + " transformFile: " + transformFile); + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + + +} + + diff --git a/src/rawDeepLearningClassifier/dlClassification/archiveModel/SimpleArchiveModel.java b/src/rawDeepLearningClassifier/dlClassification/archiveModel/SimpleArchiveModel.java new file mode 100644 index 00000000..3ffebe01 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/archiveModel/SimpleArchiveModel.java @@ -0,0 +1,92 @@ +package rawDeepLearningClassifier.dlClassification.archiveModel; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.jamdev.jdl4pam.ArchiveModel; + +import ai.djl.MalformedModelException; + +/** + * A Tensorflow model packaged with a jar file. + * @author Jamie Macaulay + * + */ +public class SimpleArchiveModel extends ArchiveModel { + + + public SimpleArchiveModel(File file) throws MalformedModelException, IOException { + super(file); + } + + @Override + public String getAudioReprRelPath(String zipFolder) { + try { + //System.out.println("SETTINGS PATH: " + getRelFilePath(zipFolder, ".pdtf")); + return getRelFilePath(zipFolder, ".pdtf"); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + } + + @Override + public String getModelRelPath(String zipFolder) { + try { + String model = null; + model = getRelFilePath(zipFolder, ".pb"); + if (model==null) model = getRelFilePath(zipFolder, ".py"); + //System.out.println("MODEL PATH: " +model); + return model; + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + } + + @Override + public String getModelFolderName() { + return "zip_model"; + } + + /** + * Gte the relative path of file within a zip folder. + * @param zipFolder + * @param fileEnd + * @return + * @throws IOException + */ + private static String getRelFilePath(String zipFolder, String fileEnd) throws IOException { + try (Stream walk = Files.walk(Paths.get(zipFolder))) { + List result = walk + .filter(p -> !Files.isDirectory(p)) // not a directory + .map(p -> p.toString()) // convert path to string + .filter(f -> f.endsWith(fileEnd)) // check end with + .collect(Collectors.toList()); // collect all matched to a List + + if (result.size()>0) { + String firstFile = result.get(0); + + //System.out.println("First file: " +firstFile); + + String relative = firstFile.replace(zipFolder, ""); + + return relative; + } + else return null; + } + catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDClassifier.java b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDClassifier.java new file mode 100644 index 00000000..375efb79 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDClassifier.java @@ -0,0 +1,131 @@ +package rawDeepLearningClassifier.dlClassification.delphinID; + +import java.io.Serializable; +import java.net.URI; +import java.util.ArrayList; + +import org.jamdev.jdl4pam.transforms.DLTransformsFactory; +import org.jamdev.jdl4pam.transforms.DLTransfromParams; + +import PamController.PamControlledUnitSettings; +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.dlClassification.DLClassiferModel; +import rawDeepLearningClassifier.dlClassification.StandardClassifierModel; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; +import rawDeepLearningClassifier.layoutFX.DLCLassiferModelUI; + +/** + * A classifier based on the delphinID method which uses whistle contours to predict + * dolphin species. + * + * @author Jamie Macaulay + * + */ +public class DelphinIDClassifier extends StandardClassifierModel { + + + private DelphinIDParams delphinIDParams = new DelphinIDParams(); + + + private DelphinUI delphinUI; + + + private DelphinIDWorker delphinIDWorker; + + + public DelphinIDClassifier(DLControl dlControl) { + super(dlControl); + } + + @Override + public boolean isModelType(URI model) { + return false; + } + + @Override + public String getName() { + return "delphinID"; + } + + @Override + public DLCLassiferModelUI getModelUI() { + if (delphinUI==null) { + delphinUI = new DelphinUI(this); + } + return delphinUI; + } + + @Override + public Serializable getDLModelSettings() { + return delphinIDParams; + } + + @Override + public String getUnitName() { + return dlControl.getUnitName()+"_" + getName(); + } + + @Override + public String getUnitType() { + return dlControl.getUnitType()+"_" + getName(); + } + + @Override + public Serializable getSettingsReference() { + if (delphinIDParams==null) { + delphinIDParams = new DelphinIDParams(); + } + + ArrayList dlTransformParams = DLClassiferModel.getDLTransformParams(delphinIDParams.dlTransfroms); + + delphinIDParams.dlTransfromParams=dlTransformParams; + + //System.out.println("SoundSpot have been saved. : " + soundSpotParmas.classNames); + return delphinIDParams; + + } + + + @Override + public long getSettingsVersion() { + return StandardModelParams.serialVersionUID; + } + + + @Override + public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { + DelphinIDParams newParameters = (DelphinIDParams) pamControlledUnitSettings.getSettings(); + if (newParameters!=null) { + delphinIDParams = (DelphinIDParams) newParameters.clone(); + //System.out.println("SoundSpot have been restored. : " + soundSpotParmas.classNames); + if (delphinIDParams.dlTransfromParams!=null) { + delphinIDParams.dlTransfroms = DLTransformsFactory.makeDLTransforms((ArrayList) delphinIDParams.dlTransfromParams); + } + } + else delphinIDParams = new DelphinIDParams(); + return true; + } + + + + @Override + public DLModelWorker getDLWorker() { + if (delphinIDWorker==null) { + delphinIDWorker = new DelphinIDWorker(); + } + return delphinIDWorker; + } + + @Override + public DelphinIDParams getDLParams() { + return delphinIDParams; + } + + public void setDLParams(DelphinIDParams params) { + this.delphinIDParams=params; + + } + +} \ No newline at end of file diff --git a/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDPane.java b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDPane.java new file mode 100644 index 00000000..749f01db --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDPane.java @@ -0,0 +1,88 @@ +package rawDeepLearningClassifier.dlClassification.delphinID; + +import PamController.SettingsPane; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.Pane; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import pamViewFX.fxNodes.PamVBox; + +/** + * Settings pane for delphin ID. + * + * @author Jamie Macaulay + * + */ +public class DelphinIDPane extends SettingsPane { + + /** + * The main pane. + */ + private Pane mainPane; + + /** + * Reference to the delphinID classifier + */ + private DelphinIDClassifier delphinUIClassifier; + + public DelphinIDPane(DelphinIDClassifier delphinUIClassifier) { + super(null); + this.delphinUIClassifier = delphinUIClassifier; + mainPane = createPane(); + } + + private Pane createPane() { + + + //font to use for title labels. + Font font= Font.font(null, FontWeight.BOLD, 11); + + Node classifierIcon; + classifierIcon = delphinUIClassifier.getModelUI().getIcon(); + + + PamVBox vBox = new PamVBox(); + vBox.setSpacing(5.); + + /**Classification thresholds etc to set.**/ + Label classiferInfoLabel2 = new Label("Decision Threshold"); + classiferInfoLabel2.setTooltip(new Tooltip("Set the minimum prediciton value for selected classes. If a prediction exceeds this value " + + "a detection will be saved.")); + classiferInfoLabel2.setFont(font); + + + vBox.getChildren().addAll(classifierIcon, classiferInfoLabel2); + + return vBox; + } + + @Override + public DelphinIDParams getParams(DelphinIDParams currParams) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setParams(DelphinIDParams input) { + // TODO Auto-generated method stub + } + + @Override + public String getName() { + return "delphinIDParams"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDParams.java b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDParams.java new file mode 100644 index 00000000..8cbe9398 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDParams.java @@ -0,0 +1,12 @@ +package rawDeepLearningClassifier.dlClassification.delphinID; + +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; + +public class DelphinIDParams extends StandardModelParams { + + /** + * + */ + private static final long serialVersionUID = 1L; + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDPrediction.java b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDPrediction.java new file mode 100644 index 00000000..b5140ee4 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDPrediction.java @@ -0,0 +1,12 @@ +package rawDeepLearningClassifier.dlClassification.delphinID; + +import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; + +public class DelphinIDPrediction extends GenericPrediction{ + + public DelphinIDPrediction(float[] prob) { + super(prob); + // TODO Auto-generated constructor stub + } + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDWorker.java b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDWorker.java new file mode 100644 index 00000000..e4f83bd5 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDWorker.java @@ -0,0 +1,74 @@ +package rawDeepLearningClassifier.dlClassification.delphinID; + + +import java.util.ArrayList; + +import org.jamdev.jdl4pam.transforms.DLTransform; +import PamguardMVC.PamDataUnit; +import rawDeepLearningClassifier.dlClassification.archiveModel.ArchiveModelWorker; +import rawDeepLearningClassifier.segmenter.GroupedRawData; + + +public class DelphinIDWorker extends ArchiveModelWorker { + + + @Override + public float[][][] dataUnits2ModelInput(ArrayList dataUnits, float sampleRate, int iChan){ + + ArrayList modelTransforms = getModelTransforms(); + + @SuppressWarnings("unchecked") + ArrayList whistleGroups = (ArrayList) dataUnits; + + //the number of chunks. + int numChunks = whistleGroups.size(); + + //data input into the model - a stack of spectrogram images. + float[][][] transformedDataStack = new float[numChunks][][]; +// +// //generate the spectrogram stack. +// AudioData soundData; +// double[][] transformedData2; //spec data +// double[] transformedData1; //waveform data +// for (int j=0; j extensionFilters; + + public DelphinUI(DelphinIDClassifier delphinUIClassifier) { + this.delphinUIClassifier=delphinUIClassifier; + extensionFilters = new ArrayList(); + //import the settings holder + extensionFilters.add(new ExtensionFilter("Zip Model", "*.zip")); + } + + @Override + public SettingsPane getSettingsPane() { + if (delphinIDSettings==null) { + delphinIDSettings = new DelphinIDPane(delphinUIClassifier); + } + return delphinIDSettings; + } + + @Override + public void getParams() { + DelphinIDParams params = getSettingsPane().getParams(delphinUIClassifier.getDLParams()); + delphinUIClassifier.setDLParams(params); + } + + + @Override + public void setParams() { + // System.out.println("Set model params: " + genericModelClassifier.getGenericDLParams().dlTransfromParams.size()); + getSettingsPane().setParams(delphinUIClassifier.getDLParams()); + } + + @Override + public List getModelFileExtensions() { + //this is a bit redundant because zip files + return extensionFilters; + } + + @Override + public JPanel getSidePanel() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Node getIcon() { + if (icon==null) { + PamSVGIcon iconMaker= new PamSVGIcon(); + PamSVGIcon svgsprite; + try { + svgsprite = iconMaker.create(getClass().getResource("/Resources/delphinid_logo01.svg").toURI().toURL()); + icon = svgsprite.getSpriteNode(); + } catch (MalformedURLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return icon; + } + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/delphinID/Whistles2Image.java b/src/rawDeepLearningClassifier/dlClassification/delphinID/Whistles2Image.java new file mode 100644 index 00000000..a4f39ff3 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/delphinID/Whistles2Image.java @@ -0,0 +1,121 @@ +package rawDeepLearningClassifier.dlClassification.delphinID; + +import org.jamdev.jdl4pam.transforms.FreqTransform; +import org.jamdev.jpamutils.spectrogram.SpecTransform; + +import javafx.scene.canvas.Canvas; +import javafx.scene.image.WritableImage; +import javafx.scene.paint.Color; +import rawDeepLearningClassifier.segmenter.SegmenterDetectionGroup; + +/** + * Transform whistles to an image. + * + * Here we extend the FreqTransform class because it contains lots of image + * transforms that are useful once the whistles have been converted into an + * image. + * + * @author Jamie Macaulay + * + */ +public class Whistles2Image extends FreqTransform { + + /** + * Create an image transform from a whistleGroup. + * @param whistleGroup + * @param params + */ + public Whistles2Image(SegmenterDetectionGroup whistleGroup, Number[] params) { + super(null, params); + double[] freqLimits = new double[] {params[0].doubleValue(), params[1].doubleValue()}; + double[] size = new double[] {params[2].doubleValue(), params[3].doubleValue()}; + + SpecTransform specTransform = whistleGroupToImage( whistleGroup, freqLimits, size); + + this.setSpecTransfrom(specTransform); + this.setFreqlims(freqLimits); + + } + + + /** + * Convert a group of whistles + * @param whistleGroup - the whistle groups + * @param freqLimits - the frequency limits + * @return the spectrogram transform. + */ + private SpecTransform whistleGroupToImage(SegmenterDetectionGroup whistleGroup, double[] freqLimits, double[] size) { + + SpecTransform specTransform = new SpecTransform(); + + /* + * All time-frequency points are saved as a scatterplot with x-axis spanning 0-4 + * seconds in time and y-axis spanning 0-20 kHz in frequency. - Matplotlib was + * used to produce plot (matplotlib.pyplot.scatter) (Point size set at 5 and all + * other values kept at default, including figure size which is saved as 6.4 x + * 4.8 inches as default, axes removed before saving using plt.axes(‘off’)) + **/ + + double[][] points = whistContours2Points(whistleGroup); + + Canvas canvas = makeScatterImage(points, size, new double[]{0, whistleGroup.getDurationInMilliseconds()}, freqLimits, 5.); + + WritableImage image = canvas.getGraphicsContext2D().getCanvas().snapshot(null, null); + + double[][] imaged = new double[(int) size[0]][(int) size[1]]; + + Color color; + for (int i=0; i runModel(ArrayList rawDataUnit) { + public ArrayList runModel(ArrayList rawDataUnit) { ArrayList modelResults = new ArrayList(); for (int i=0; i checkSettingsOK() { + public DLStatus setModel(URI model) { // TODO Auto-generated method stub return null; } diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/DLModelWorker.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/DLModelWorker.java index d8816239..22c87512 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/DLModelWorker.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/DLModelWorker.java @@ -11,17 +11,16 @@ import org.jamdev.jdl4pam.transforms.DLTransformsFactory; import org.jamdev.jdl4pam.utils.DLUtils; import org.jamdev.jpamutils.wavFiles.AudioData; -import PamUtils.PamArrayUtils; +import PamguardMVC.PamDataUnit; import rawDeepLearningClassifier.DLControl; import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; -import rawDeepLearningClassifier.segmenter.SegmenterProcess.GroupedRawData; +import rawDeepLearningClassifier.segmenter.GroupedRawData; /** * * Runs the deep learning model and performs feature extraction. *

    - * * * @author Jamie Macaulay * @@ -42,77 +41,89 @@ public abstract class DLModelWorker { * True to enable normalisation of results using softmax; */ private boolean enableSoftMax = true; + + + /** + * Convert a list of data units to a stack if images. + * @param dataUnits - the data units. + * @param sampleRate - the sample rate + * @param iChan - the channels + * @return a stack of images for input into a deep learning model. + */ + public float[][][] dataUnits2ModelInput(ArrayList dataUnits, float sampleRate, int iChan){ + + @SuppressWarnings("unchecked") + ArrayList rawDataUnits = ( ArrayList) dataUnits; + + //the number of chunks. + int numChunks = rawDataUnits.size(); + + //data input into the model - a stack of spectrogram images. + float[][][] transformedDataStack = new float[numChunks][][]; + + //generate the spectrogram stack. + AudioData soundData; + double[][] transformedData2; //spec data + double[] transformedData1; //waveform data + for (int j=0; j runModel(ArrayList rawDataUnits, float sampleRate, int iChan) { + public synchronized ArrayList runModel(ArrayList dataUnits, float sampleRate, int iChan) { try { - //the number of chunks. - int numChunks = rawDataUnits.size(); - //PamCalendar.isSoundFile(); //create an audio data object from the raw data chunk long timeStart = System.nanoTime(); - - //data input into the model - a stack of spectrogram images. - float[][][] transformedDataStack = new float[numChunks][][]; - - //generate the spectrogram stack. - AudioData soundData; - double[][] transformedData2; //spec data - double[] transformedData1; //waveform data - for (int j=0; j { float[] classOut; for (int i=0; i { } public abstract float[] runModel(float[][][] transformedDataStack); + + /** + * Check whether a model is null or not. + * @return true of the model is null. + */ + public abstract boolean isModelNull(); + public abstract T makeModelResult(float[] prob, double time); diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericAdvPane.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericAdvPane.java index 9b56c877..3d695502 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericAdvPane.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericAdvPane.java @@ -1,6 +1,7 @@ package rawDeepLearningClassifier.dlClassification.genericModel; import PamController.SettingsPane; +import PamUtils.PamArrayUtils; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; @@ -51,10 +52,10 @@ public class GenericAdvPane extends SettingsPane { */ private GridPane classNameHolder; -// /** -// * The text field names. -// */ -// private TextField[] classNameFields; + // /** + // * The text field names. + // */ + // private TextField[] classNameFields; /** * The DL transform pane. @@ -182,14 +183,34 @@ public class GenericAdvPane extends SettingsPane { defualtShapeSwitch = new PamToggleSwitch("Use model default shape"); defualtShapeSwitch.selectedProperty().addListener((obsval, oldval, newval)->{ + + PamArrayUtils.printArray( currentInput.defaultShape); + + if (newval) { //set the correct model shape. if (currentInput!=null && currentInput.defaultShape!=null) { - for (int i=0; i { outShapeSpinners= new Spinner[2]; //set at for now but could be different in future? for (int i=0; i(-1, Integer.MAX_VALUE, 10, 1); outShapeSpinners[i] .setPrefWidth(80); outShapeSpinners[i] .getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); @@ -211,25 +232,25 @@ public class GenericAdvPane extends SettingsPane { defualtOutSwitch = new PamToggleSwitch("Use model default out"); defualtOutSwitch.selectedProperty().addListener((obsval, oldval, newval)->{ - + if (!newval) { PamDialogFX.showWarning("This is generally a very bad idea. If the output shape is not that specified by the model then PAMGuard may through an index out of bounds exception"); } -// System.out.println("DEFAULT OUTPUT" + currentInput); + // System.out.println("DEFAULT OUTPUT" + currentInput); //set the correct model shape. if (currentInput!=null && currentInput.defualtOuput!=null) { - + ///just use the max number? for (int i=0; i { if (setParams) return; genericClassifier.getModelUI().getSettingsPane().setParams(this.getParams(currentInput)); } - + /** * Populate the class name fields. */ @@ -304,8 +325,8 @@ public class GenericAdvPane extends SettingsPane { else { textFields[i].setText(("Class " + i)); } - - + + classNameHolder.add(new Label(("Class " + i)), 0, i); classNameHolder.add(textFields[i], 1, i); } @@ -321,7 +342,7 @@ public class GenericAdvPane extends SettingsPane { @Override public GenericModelParams getParams(GenericModelParams currParams) { -// System.out.println("Generic Adv Pane GET PARAMS:"); + // System.out.println("Generic Adv Pane GET PARAMS:"); if (setParams) return null; @@ -336,11 +357,11 @@ public class GenericAdvPane extends SettingsPane { String[] classNames= new String[textFields.length]; boolean[] binaryClassification = new boolean[currParams.numClasses]; - -// if (currParams.binaryClassification!=null) { -// System.out.println("Current classification: " + currParams.binaryClassification.length -// + " new: " + binaryClassification.length) ; -// } + + // if (currParams.binaryClassification!=null) { + // System.out.println("Current classification: " + currParams.binaryClassification.length + // + " new: " + binaryClassification.length) ; + // } for (int i=0; i { } + int nSpinner=0; + for (int i =0; i { //System.out.println("Input shape: V " + shapeSpinners[i].getValue().intValue()); } - + currParams.outputShape = outShape; currParams.shape = inShape; currParams.classNames = this.getDLControl().getClassNameManager().makeClassNames(classNames); - -// System.out.println("GenericAdvPane: Class names"); -// for (int i=0; i { @Override public void setParams(GenericModelParams input) { - + setParams= true; this.currentInput = input.clone(); //System.out.println("Generic Adv Pane SET PARAMS: " +currentInput ); - -// System.out.println("GenericAdvPane: Class names " + currentInput.numClasses); -// for (int i=0; i { // classNumber.getValueFactory().setValue(currentInput.numClasses); - + //populate the class names field. populateClassNameFields(currentInput.numClasses, input); - + //set the model shape if (currentInput.shape!=null) { for (int i=0; i { if (currentInput.outputShape!=null) { for (int i=0; i { //System.out.println("Set transforms: " + currentInput.dlTransfroms.size()); transfromPane.setExampleSoundIndex(currentInput.exampleSoundIndex); transfromPane.setTransforms(currentInput.dlTransfroms); - -// for (int i=0; i)genericModelParams.dlTransfromParams); - } - - genericModelWorker.setModelTransforms(genericModelParams.dlTransfroms); - - - if (genericModelWorker.getModel()==null) { - genericModelWarning.setWarningMessage("There is no loaded deep learning model. Generic model classifier disabled."); - WarningSystem.getWarningSystem().addWarning(genericModelWarning); - } - - if ((!PamCalendar.isSoundFile() || forceQueue) && !dlControl.isViewer()) { - //for real time only - if (workerThread!=null) { - workerThread.stopTaskThread(); - } - - workerThread = new GenericTaskThread(genericModelWorker); - workerThread.setPriority(Thread.MAX_PRIORITY); - workerThread.start(); - } - } - - @Override - public ArrayList runModel(ArrayList groupedRawData) { - - if (genericModelWorker.getModel()==null) return null; - - // System.out.println("SoundSpotClassifier: PamCalendar.isSoundFile(): " - // + PamCalendar.isSoundFile() + " " + (PamCalendar.isSoundFile() && !forceQueue)); - - - /** - * If a sound file is being analysed then SoundSpot can go as slow as it wants. if used in real time - * then there is a buffer with a maximum queue size. - */ - if ((PamCalendar.isSoundFile() && !forceQueue) || dlControl.isViewer()) { - //run the model - - //System.out.println("GENERICDLCLASSIFIER: Run here!!!"); - ArrayList modelResult = getGenericDLWorker().runModel(groupedRawData, - groupedRawData.get(0).getParentDataBlock().getSampleRate(), 0); - //System.out.println("GENERICDLCLASSIFIER: RESULTS!!!: " + modelResult); - - if (modelResult==null) { - genericModelWarning.setWarningMessage("Generic deep learning model returned null"); - WarningSystem.getWarningSystem().addWarning(genericModelWarning); - return null; - } - - for (int i =0; iDLModelWorker.MAX_QUEUE_SIZE) { - //we are not doing well - clear the buffer - workerThread.getQueue().clear(); - } - workerThread.getQueue().add(groupedRawData); - } - return null; - } - - - @Override - public void closeModel() { - //very important to prevent memory leak for long term processing. - if (genericModelWorker.getModel()!=null && genericModelWorker.getModel().getModel()!=null) { - //System.out.println("CLOSE GENERNIC MODEL"); - genericModelWorker.getModel().getModel().close(); - } - } - @Override public int getNumClasses() { @@ -225,36 +117,20 @@ public class GenericDLClassifier implements DLClassiferModel, PamSettings { genericModelParams = new GenericModelParams(); } - ArrayList dlTransformParams = getDLTransformParams(genericModelParams.dlTransfroms); + ArrayList dlTransformParams = DLClassiferModel.getDLTransformParams(genericModelParams.dlTransfroms); genericModelParams.dlTransfromParams=dlTransformParams; - if (genericModelParams.dlTransfromParams!=null) { - System.out.println("Generic settings have been saved. : " + genericModelParams.dlTransfromParams.size()); - } - else { - System.out.println("Generic settings have been saved. : " + null); - - } +// if (genericModelParams.dlTransfromParams!=null) { +// System.out.println("Generic settings have been saved. : " + genericModelParams.dlTransfromParams.size()); +// } +// else { +// System.out.println("Generic settings have been saved. : " + null); +// } return genericModelParams; } - /** - * Get the parameters which can be serialized from transforms. - * @param dlTransfroms- the dl transforms. - */ - public ArrayList getDLTransformParams(ArrayList dlTransfroms) { - ArrayList dlTransformParams = new ArrayList(); - - if (genericModelParams.dlTransfroms==null) return null; - //need to set the generic model params. - for (int i=0; igenericModelParams.threshold && genericModelParams.binaryClassification[i]) { // System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]); return true; @@ -375,35 +221,28 @@ public class GenericDLClassifier implements DLClassiferModel, PamSettings { } - @Override - public ArrayList checkSettingsOK() { - return checkSettingsOK(genericModelParams, dlControl); - } +// @Override +// public ArrayList checkSettingsOK() { +// return checkSettingsOK(genericModelParams, dlControl); +// } - public static ArrayList checkSettingsOK(StandardModelParams genericModelParams, DLControl dlControl) { - - //TODO - check if model is null. - //check that classifier is selected if continous files. - // - ArrayList warnings = new ArrayList(); - - File file = new File(genericModelParams.modelPath); - if (genericModelParams.modelPath == null || !file.isFile()) { - warnings.add(new PamWarning("Generic classifier", "There is no model loaded - the classifier will not run", 2)); - //if no model then this is ionly message needed for the generic classifier. - return warnings; - } - - //if continous data is selected and all classes are false then this is a potential mistake... - if (dlControl.getSettingsPane().getSelectedParentDataBlock().getUnitClass() == RawDataUnit.class - && PamArrayUtils.isAllFalse(genericModelParams.binaryClassification)) { - warnings.add(new PamWarning("Generic classifier", "There are no prediction classes selected for classification. " - + "Predicitons for each segment will be saved but there will be no detections generated", 1)); - } - - return warnings; - + + @Override + public boolean isModelType(URI uri) { + return super.isModelExtensions(uri); + } + + @Override + public DLModelWorker getDLWorker() { + return this.genericModelWorker; + } + + + + @Override + public StandardModelParams getDLParams() { + return this.genericModelParams; } diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericImportExportPane.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericImportExportPane.java index 538dbbc6..4e2efb16 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericImportExportPane.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericImportExportPane.java @@ -76,9 +76,7 @@ public class GenericImportExportPane extends ImportExportPane { //set the default segment length if available. if (genericModelParams.defaultSegmentLen!=null) { - double sR = genericAdvPane.getDLControl().getSettingsPane().getSelectedParentDataBlock().getSampleRate(); - //automatically set the default segment length. - genericAdvPane.getDLControl().getSettingsPane().getSegmentLenSpinner().getValueFactory().setValue((int) (sR*genericModelParams.defaultSegmentLen/1000.)); + genericAdvPane.getDLControl().getSettingsPane().setSegmentLength(genericModelParams.defaultSegmentLen); } if (genericModelParams==null) { diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelPane.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelPane.java index f26f1d72..70dc24e2 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelPane.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelPane.java @@ -1,9 +1,6 @@ package rawDeepLearningClassifier.dlClassification.genericModel; import java.io.File; -import java.util.ArrayList; - -import javafx.stage.FileChooser.ExtensionFilter; import pamViewFX.fxNodes.PamButton; import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelPane; import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; @@ -17,10 +14,6 @@ import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams */ public class GenericModelPane extends StandardModelPane { - /** - * The extension filter for sound spot models. - */ - private ArrayList extensionFilters; private GenericAdvPane advPane; @@ -35,13 +28,6 @@ public class GenericModelPane extends StandardModelPane { this.genericDLClassifier = genericDLClassifier; - //must add an additional import settings button. - extensionFilters = new ArrayList(); - - //import the settings holder - extensionFilters.add(new ExtensionFilter("TensorFlow Model", "*.pb")); - extensionFilters.add(new ExtensionFilter("Pytorch Model", "*.pk")); - //this.getVBoxHolder().getChildren().add(2, new Label("Classifier Settings")); usedefaultSeg.setDisable(true); defaultSegBox.setVisible(false); @@ -85,30 +71,36 @@ public class GenericModelPane extends StandardModelPane { @Override public void setParams(StandardModelParams currParams) { +// System.out.println("SET PARAMS GENERIC PANE: " + currParams); + super.setParams(currParams); } + + @Override + public StandardModelParams getParams(StandardModelParams currParams) { +// System.out.println("GET GENERIC PARAMS: " + currParams); + + return super.getParams(currParams); + } @Override public void newModelSelected(File file) { this.setCurrentSelectedFile(file); - this.genericDLClassifier.newModelSelected(file); - + //this.setParamsClone(new GenericModelParams()); + //prep the model with current parameters; - genericDLClassifier.getGenericDLWorker().prepModel(getParams(getParamsClone()), genericDLClassifier.getDLControl()); - //get the model transforms calculated from the model by SoundSpoyWorker and apply them to our temporary paramters clone. - //getParamsClone().dlTransfroms = this.genericDLClassifier.getGenericDLWorker().getModelTransforms(); - ///set the advanced pane parameters. + +// genericDLClassifier.getGenericDLWorker().prepModel(getParams(getParamsClone()), genericDLClassifier.getDLControl()); + //do not have getParam here as it resets some of the setting before set params has been called. + genericDLClassifier.getGenericDLWorker().prepModel(getParamsClone(), genericDLClassifier.getDLControl()); + //now new parameters have been set in the prepModel functions so need to set new params now. getAdvSettingsPane().setParams(getParamsClone()); } - @Override - public ArrayList getExtensionFilters() { - return extensionFilters; - } } diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelParams.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelParams.java index ff15ebf0..6d87bb16 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelParams.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelParams.java @@ -5,7 +5,9 @@ import java.util.ArrayList; import org.jamdev.jdl4pam.transforms.DLTransform.DLTransformType; import org.json.JSONArray; +import PamUtils.PamArrayUtils; import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.layoutFX.exampleSounds.ExampleSoundFactory.ExampleSoundType; import org.jamdev.jdl4pam.transforms.DLTransformsFactory; import org.jamdev.jdl4pam.transforms.DLTransfromParams; @@ -28,7 +30,7 @@ public class GenericModelParams extends StandardModelParams implements Cloneable ArrayList dlTransformParamsArr = new ArrayList(); //waveform transforms. - dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE, 96000.0)); + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE, 96000.0)); // dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.PREEMPHSIS, preemphases)); dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.SPECTROGRAM, 256, 100)); //in the python code they have an sfft of 129xN where N is the number of chunks. They then @@ -82,16 +84,68 @@ public class GenericModelParams extends StandardModelParams implements Cloneable // } return newParams; } + + + @Override + public boolean equals(Object o) { + + GenericModelParams params = (GenericModelParams) o; + + + //check the transforms are the same. + if (this.dlTransfromParams!=null && params.dlTransfromParams == null) { + return false; + } + if (params.dlTransfromParams!=null && this.dlTransfromParams == null) { + return false; + } + + //iterate through the transofrms and check each one is equal. + if (this.dlTransfromParams!=null) { + + if (this.dlTransfromParams.size()!=dlTransfromParams.size()) { + return false; + } + + for (int i=0; i) params.dlTransfromParams; + + if (params.shape!=null) { + jpamParams.defaultInputShape = new Shape(PamArrayUtils.object2Primitve(params.shape)); + } + if (params.outputShape!=null) { + jpamParams.defaultOutputShape = new Shape(PamArrayUtils.object2Primitve(params.outputShape)); + } + + jpamParams.segLen = params.defaultSegmentLen; + + return jpamParams; + } + + + private static String[] dlClassNames2String(DLClassName[] classNames) { + if (classNames==null) return null; + + String[] classNamesS = new String[classNames.length]; + + int n =0; + for (DLClassName dlName : classNames) { + classNamesS[n] = dlName.className; + n++; + } + + return classNamesS; + } + /** * Write a JSON string to a JSON file. * @param file - the file to write to. @@ -98,6 +186,7 @@ public class GenericModelParser { * @param params - the parameters object. * @return the JSONObject. */ + @Deprecated public static JSONObject getJSonParamsObject(GenericModelParams params) { return getJSonParamsObject( params, new JSONObject()); } @@ -111,6 +200,7 @@ public class GenericModelParser { * @param paramsobject - jsonObject to add params to. * @return the JSONObject. */ + @Deprecated public static JSONObject getJSonParamsObject(GenericModelParams params, JSONObject paramsObject) { //set the class names. JSONObject classInfo = new JSONObject(); @@ -175,18 +265,34 @@ public class GenericModelParser { fileReader.close(); System.out.println(jsonData); - - //DL transforms - ArrayList dlTransforms = DLTransformsParser.parseTransfromParams(jsonData); - //System.out.println("No. parsed transforms: " + dlTransforms.size()); - - //DL transforms - params.dlTransfromParams = dlTransforms; - - //parse the data - params = parseJSOString(jsonData, params, classNameManager); + + //Need to figure out if this is the new or old format. + JSONObject jsonObject = new JSONObject(jsonData); + + + boolean isParamsV2 =false; + + //does the object have a version + if (jsonObject.has("version_info") ) { + JSONObject versionObject = jsonObject.getJSONObject("version_info"); + + //lots of JSON metadata might have a version number so to be absolutely sure... + if (versionObject.has("version")) { + isParamsV2=true; + } + } + if (isParamsV2) { + params = parseJSONObject( jsonObject, params, + classNameManager); + } + else { + params = parseJSONObject_legacy( jsonObject, params, + classNameManager); + } + + return params; } catch (FileNotFoundException e) { @@ -211,6 +317,49 @@ public class GenericModelParser { return null; } + + + /** + * Parse legacy JSON format. + * @param jsonObject - the JSONObject holding data + * @param paramsClone - parameters object to set fields to to set. + * @param classNameManager - the class names manager associated with the parameters. + * @return paramsClone with new settings. + */ + public static GenericModelParams parseJSONObject_legacy(JSONObject jsonObject, GenericModelParams paramsClone, + DLClassNameManager classNameManager) { + + //DL transforms + ArrayList dlTransforms = DLTransformsParser.parseTransfromParams(jsonObject.toString()); + + + //DL transforms + paramsClone.dlTransfromParams = dlTransforms; + + parseJSONMetaData_legacy( jsonObject, paramsClone, classNameManager); + + //System.out.println("No. parsed transforms: " + dlTransforms.size()); + + return paramsClone; + } + + /** + * Parse JSON data containing transforms and model metadata. + * @param jsonObject - the JSONObject holding data + * @param paramsClone - parameters object to set fields to to set. + * @param classNameManager - the class names manager associated with the parameters. + * @return paramsClone with new settings. + */ + public static GenericModelParams parseJSONObject(JSONObject jsonObject, GenericModelParams paramsClone, + DLClassNameManager classNameManager) { + + org.jamdev.jdl4pam.genericmodel.GenericModelParams params = DLTransformParser2.readJSONParams(jsonObject); + + GenericModelParams pgParams = jpamParams2PGParmas(params, classNameManager); + + return pgParams; + + } /** @@ -219,13 +368,9 @@ public class GenericModelParser { * @param params - the parameters. * @return the parameters class with new settings set. */ - public static GenericModelParams parseJSOString(String jsonParamsString, GenericModelParams paramsClone, + public static GenericModelParams parseJSONMetaData_legacy(JSONObject jsonObject, GenericModelParams paramsClone, DLClassNameManager classNameManager) { - JSONObject jsonObject = new JSONObject(jsonParamsString); - - //the segment length information - System.out.println(jsonParamsString); //System.out.println("SEG SIZE: " + jsonObject.getString(SEG_SIZE_STRING) + " " + jsonObject.isNull("size_ms")); diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelUI.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelUI.java index b10e3592..c4f15e4f 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelUI.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelUI.java @@ -1,12 +1,17 @@ package rawDeepLearningClassifier.dlClassification.genericModel; +import java.util.ArrayList; +import java.util.List; + import javax.swing.JPanel; import PamController.SettingsPane; import PamView.dialog.warn.WarnOnce; import PamView.dialog.warn.WarnOnceDialog; import javafx.application.Platform; +import javafx.scene.Node; import javafx.scene.control.Alert.AlertType; +import javafx.stage.FileChooser.ExtensionFilter; import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; import rawDeepLearningClassifier.layoutFX.DLCLassiferModelUI; @@ -28,6 +33,11 @@ public class GenericModelUI implements DLCLassiferModelUI { * The sound spot classifier. */ private GenericDLClassifier genericModelClassifier; + + /** + * The extension filter for sound spot models. + */ + private ArrayList extensionFilters; /** * SondSpot classifier. @@ -35,6 +45,13 @@ public class GenericModelUI implements DLCLassiferModelUI { */ public GenericModelUI(GenericDLClassifier soundSpotClassifier) { this.genericModelClassifier=soundSpotClassifier; + + //must add an additional import settings button. + extensionFilters = new ArrayList(); + + //import the settings holder + extensionFilters.add(new ExtensionFilter("TensorFlow Model", "*.pb")); + extensionFilters.add(new ExtensionFilter("Pytorch Model", "*.pk")); } @Override @@ -56,7 +73,7 @@ public class GenericModelUI implements DLCLassiferModelUI { @Override public void setParams() { - // System.out.println("Set model params: " + genericModelClassifier.getGenericDLParams().dlTransfromParams.size()); +// System.out.println("SE MODEL UI PARAMS: " + genericModelClassifier.getGenericDLParams().dlTransfromParams.size()); getSettingsPane().setParams(genericModelClassifier.getGenericDLParams()); } @@ -68,4 +85,14 @@ public class GenericModelUI implements DLCLassiferModelUI { } + @Override + public List getModelFileExtensions() { + return extensionFilters; + } + + @Override + public Node getIcon() { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelWorker.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelWorker.java index 2742054c..fa7ecdd7 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelWorker.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelWorker.java @@ -23,7 +23,7 @@ public class GenericModelWorker extends DLModelWorker { /** * The generic model */ - private PamGenericModel genericModel; + private GenericModel genericModel; /** * Frequency transform. @@ -33,17 +33,19 @@ public class GenericModelWorker extends DLModelWorker { @Override public float[] runModel(float[][][] transformedDataStack) { //System.out.println("RUN GENERIC MODEL: " + transformedDataStack.length + " " + transformedDataStack[0].length + " " + transformedDataStack[0][0].length); - //System.out.println("RUN GENERIC MODEL: " + transformedDataStack[0][0][0]); +// System.out.println("RUN GENERIC MODEL: " + transformedDataStack[0][0][0]); float[] results; if (freqTransform) - results = genericModel.runModel2(transformedDataStack); + results = getModel().runModel(transformedDataStack); else { //run a model if it is waveform info. float[][] waveStack = new float[transformedDataStack.length][]; for (int i=0; i { // System.out.println(Paths.get(genericParams.modelPath)); // System.out.println(Paths.get(genericModel.getModel().getName()).equals(Paths.get(genericParams.modelPath))); // } - + //first open the model and get the correct parameters. - //21/11/2022 - Added a null and filename check here to stop the mdoel reloading everytime PAMGuard hits a new file or + //21/11/2022 - Added a null and filename check here to stop the model reloading everytime PAMGuard hits a new file or //is stopped or started - this was causing a memory leak. if (genericModel==null || !Paths.get(genericModel.getModel().getName()).equals(Paths.get(genericParams.modelPath))) { + + if (genericModel!=null && genericModel.getModel()!=null) { + genericModel.getModel().close(); + } //System.out.println(Paths.get(genericParams.modelPath)); - genericModel = new PamGenericModel(genericParams.modelPath); + + //create a new model. + genericModel = new GenericModel(genericParams.modelPath); //System.out.println("LOAD A NEW MODEL: "); //System.out.println(genericModel.getModel().getModelPath().getFileName()); } + setModelTransforms(genericParams.dlTransfroms); + //is this a waveform or a spectrogram model? - DLTransform transform = genericParams.dlTransfroms.get(genericParams.dlTransfroms.size()-1); - if (transform instanceof FreqTransform) { - freqTransform = true; - } - else { - freqTransform = false; - } + setWaveFreqModel(genericParams); //use softmax or not? String extension = FilenameUtils.getExtension(genericParams.modelPath); @@ -107,10 +111,10 @@ public class GenericModelWorker extends DLModelWorker { this.setEnableSoftMax(true); } - GenericModelParams genericModelParams = new GenericModelParams(); +// GenericModelParams genericModelParams = new GenericModelParams(); - genericModelParams.defaultShape = longArr2Long(genericModel.getInputShape().getShape()); - genericModelParams.defualtOuput = longArr2Long(genericModel.getOutShape().getShape()); + ((GenericModelParams) genericParams).defaultShape = longArr2Long(genericModel.getInputShape().getShape()); + ((GenericModelParams) genericParams).defualtOuput = longArr2Long(genericModel.getOutShape().getShape()); } @@ -123,6 +127,18 @@ public class GenericModelWorker extends DLModelWorker { //Thread.currentThread().setContextClassLoader(origCL); } + + protected void setWaveFreqModel(StandardModelParams genericParams) { + //is this a waveform or a spectrogram model? + DLTransform transform = genericParams.dlTransfroms.get(genericParams.dlTransfroms.size()-1); + if (transform instanceof FreqTransform) { + freqTransform = true; + } + else { + freqTransform = false; + } + } + /** * Convert a long[] array to a Long[] array. * @param inArr - the input long[] array. @@ -140,15 +156,23 @@ public class GenericModelWorker extends DLModelWorker { @Override public void closeModel() { - genericModel.getModel().close(); +// can be very important to prevent memory leak for long term processing. + if (genericModel!=null && genericModel.getModel()!=null) { + genericModel.getModel().close(); + } } /** * Generic model. * @return the generic model. */ - public PamGenericModel getModel() { + public GenericModel getModel() { return genericModel; } + @Override + public boolean isModelNull() { + return genericModel==null; + } + } diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericPrediction.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericPrediction.java index 8e50f89e..57e83218 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericPrediction.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericPrediction.java @@ -39,6 +39,12 @@ public class GenericPrediction implements PredictionResult { public double analysisTime=0; + /** + * Constructor for a typical generic prediciton. + * @param prob - the probability for each class. + * @param classNameID - the ID's of the class names. + * @param isBinary - true if the model result passed a binary test (usually one species above a threshold) + */ public GenericPrediction(float[] prob, short[] classNameID, boolean isBinary) { this.prob=prob; this.classNameID = classNameID; diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/PamGenericModel.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/PamGenericModel.java deleted file mode 100644 index f81da14d..00000000 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/PamGenericModel.java +++ /dev/null @@ -1,216 +0,0 @@ -package rawDeepLearningClassifier.dlClassification.genericModel; - - -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import org.apache.commons.io.FilenameUtils; -import org.jamdev.jdl4pam.genericmodel.SpectrogramTranslator; -import org.jamdev.jdl4pam.genericmodel.WaveformTranslator; - -import ai.djl.MalformedModelException; -import ai.djl.Model; -import ai.djl.engine.Engine; -import ai.djl.inference.Predictor; -import ai.djl.ndarray.types.Shape; -import ai.djl.translate.TranslateException; - - -/** - * The generic model. This is implemented in the JPAM library as Generic model - * but having it here gives a little more control without requiring constant changes - * the Maven dependencies. - * - * @author Jamie Macaulay - * - */ -public class PamGenericModel { - - - /** - * The currently loaded model - */ - private Model model; - - /** - * The predictor for the model. - */ - Predictor specPredictor; - - - /** - * Predictor for the model for waveforms. - */ - Predictor wavePredictor; - - - /** - * The input shape from the loaded model. - */ - private Shape inputShape = null; - - /** - * The output shape from the model. - */ - private Shape outShape = null; - - - private SpectrogramTranslator specTranslator; - - - private WaveformTranslator waveTranslator; - - - - public PamGenericModel(String modelPath) throws MalformedModelException, IOException{ - - //System.out.println("NEW GENERIC MODEL:"); - - File file = new File(modelPath); - - //String modelPath = "/Users/au671271/Google Drive/Aarhus_research/PAMGuard_bats_2020/deep_learning/BAT/models/bats_denmark/BAT_4ms_256ft_8hop_128_NOISEAUG_40000_100000_-100_0_256000_JAMIE.pk"; - - Path modelDir = Paths.get(file.getAbsoluteFile().getParent()); //the directory of the file (in case the file is local this should also return absolute directory) - String modelName = file.getName(); - - String extension = FilenameUtils.getExtension(file.getAbsolutePath()); - - System.out.println("Generic Model: Available engines: " + Engine.getAllEngines()); - - Model model; - switch (extension) { - case "pb": - model = Model.newInstance(modelPath, "TensorFlow"); - model.load(modelDir, modelName); - break; - case "py": - model = Model.newInstance(modelName); - model.load(modelDir, modelName); - break; - default: - //will try to load a model automatically - problematic but let's see. - model = Model.newInstance(modelPath); - break; - } - - if (model == null) { - System.err.println("Generic Model: Could not load model: " + modelPath); - } - else { - if (model!=null && model.describeInput()!=null) { - System.out.println("Generic Model: Input: " + model.describeInput().toString()); - inputShape = model.describeInput().get(0).getValue(); - } - if (model!=null && model.describeOutput()!=null) { - System.out.println("Generic Model: Output: " + model.describeOutput().toString()); - outShape = model.describeOutput().get(0).getValue(); - } - - this.model=model; - - specTranslator = new SpectrogramTranslator(inputShape); - - waveTranslator = new WaveformTranslator(model.describeInput()); - - //predictor for the model if using images as input - specPredictor = model.newPredictor(specTranslator); - - //predictor for the model if using - wavePredictor = model.newPredictor(waveTranslator); - } - - } - - /** - * Get the predictor for spectrogram images. - * @return - */ - public Predictor getSpecPredictor() { - return specPredictor; - } - - /*** - * Get the predictor for the waveform input. - * @return the predictor for waveforms. - */ - public Predictor getWavePredictor() { - return wavePredictor; - } - - /** - * Get the model shape for the input. - * @return the input shape. - */ - public Shape getInputShape() { - return inputShape; - } - - /** - * Set the input shape. - * @param inputShape - the input shape. - */ - public void setInputShape(Shape inputShape) { - this.inputShape = inputShape; - specTranslator.setShape(inputShape); - } - - /** - * Get the output shape. The shape is null if the model does not specify shape. - * @return the output shape. - */ - public Shape getOutShape() { - return outShape; - } - - - - /** - * Run the model on spectrogram images - * @param specImage - the spectrogram image [no. batches][image x][image y] - * @return the results - */ - public float[] runModel2(float[][][] specImage) { - try { - float[] results = specPredictor.predict(specImage); - //DLUtils.printArray(results); - return results; - } catch (TranslateException e) { - System.out.println("Error on model: "); - e.printStackTrace(); - } - return null; - } - - - /** - * Run the model on a raw waveform data - * @param specImage - waveform data [no. batches][samples] - * @return the results - */ - public float[] runModel1(float[][] waveform) { - try { - float[] results = wavePredictor.predict(waveform); - //DLUtils.printArray(results); - return results; - } catch (TranslateException e) { - System.out.println("Error on model: "); - e.printStackTrace(); - } - return null; - } - - - public Model getModel() { - return model; - } - - public void setModel(Model model) { - this.model = model; - } - - - - - -} diff --git a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosClassifier.java b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosClassifier.java index 49ed2260..437f8250 100644 --- a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosClassifier.java +++ b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosClassifier.java @@ -1,24 +1,21 @@ package rawDeepLearningClassifier.dlClassification.ketos; import java.io.Serializable; +import java.net.URI; import java.util.ArrayList; +import org.jamdev.jdl4pam.transforms.DLTransformsFactory; +import org.jamdev.jdl4pam.transforms.DLTransfromParams; + import PamController.PamControlledUnitSettings; import PamController.PamSettingManager; -import PamController.PamSettings; -import PamUtils.PamCalendar; import rawDeepLearningClassifier.DLControl; -import rawDeepLearningClassifier.dlClassification.DLClassName; import rawDeepLearningClassifier.dlClassification.DLClassiferModel; -import rawDeepLearningClassifier.dlClassification.DLTaskThread; -import rawDeepLearningClassifier.dlClassification.PredictionResult; +import rawDeepLearningClassifier.dlClassification.StandardClassifierModel; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker; -import rawDeepLearningClassifier.dlClassification.genericModel.GenericDLClassifier; import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; import rawDeepLearningClassifier.layoutFX.DLCLassiferModelUI; -import rawDeepLearningClassifier.segmenter.SegmenterProcess.GroupedRawData; -import warnings.PamWarning; -import warnings.WarningSystem; /** * Classifier which uses deep learning models from Meridian's Ketos framework. @@ -32,12 +29,11 @@ import warnings.WarningSystem; * @author Jamie Macaulay * */ -public class KetosClassifier implements DLClassiferModel, PamSettings { +public class KetosClassifier extends StandardClassifierModel { + + public static String MODEL_NAME = "Ketos"; + - /** - * Reference to the DL contro.. - */ - private DLControl dlControl; /** * Paramters for a Ketos classifier. @@ -55,27 +51,12 @@ public class KetosClassifier implements DLClassiferModel, PamSettings { */ private KetosWorker ketosWorker; - /** - * True to force the classifier to use a queue - can be used for simulating real time operation. - */ - private boolean forceQueue = false; - - /** - * Sound spot warning. - */ - PamWarning ketosWarning = new PamWarning("Ketos_Classifier", "",2); - - /** - * The Ketos worker thread has a buffer so that Ketos models can be run - * in real time without dslowing down the rest of PAMGaurd. - */ - private KetosThread workerThread; - /** * The ketos classifier. */ public KetosClassifier(DLControl dlControl) { + super(dlControl); this.dlControl=dlControl; this.ketosDLParams = new KetosDLParams(); this.ketosUI= new KetosUI(this); @@ -84,151 +65,28 @@ public class KetosClassifier implements DLClassiferModel, PamSettings { } - @Override - public ArrayList runModel(ArrayList groupedRawData) { - if (getKetosWorker().getModel()==null) return null; - - // System.out.println("SoundSpotClassifier: PamCalendar.isSoundFile(): " - // + PamCalendar.isSoundFile() + " " + (PamCalendar.isSoundFile() && !forceQueue)); - - /** - * If a sound file is being analysed then Ketos can go as slow as it wants. if used in real time - * then there is a buffer with a maximum queue size. - */ - if ((PamCalendar.isSoundFile() && !forceQueue) || dlControl.isViewer()) { - //run the model - ArrayList modelResult = getKetosWorker().runModel(groupedRawData, - groupedRawData.get(0).getParentDataBlock().getSampleRate(), 0); - - if (modelResult==null) { - ketosWarning.setWarningMessage("Generic deep learning model returned null"); - WarningSystem.getWarningSystem().addWarning(ketosWarning); - return null; - } - - for (int i =0; iDLModelWorker.MAX_QUEUE_SIZE) { - //we are not doing well - clear the buffer - workerThread.getQueue().clear(); - } - workerThread.getQueue().add(groupedRawData); - } - return null; - } - - - @Override - public void prepModel() { - //System.out.println("PrepModel! !!!"); - getKetosWorker().prepModel(ketosDLParams, dlControl); - - if (!ketosDLParams.useDefaultTransfroms) { - //set custom transforms in the model. - getKetosWorker().setModelTransforms(ketosDLParams.dlTransfroms); - } - - if (getKetosWorker().getModel()==null) { - ketosWarning.setWarningMessage("There is no loaded classifier model. Ketos disabled."); - WarningSystem.getWarningSystem().addWarning(ketosWarning); - } - - - if ((!PamCalendar.isSoundFile() || forceQueue) && !dlControl.isViewer()) { - //for real time only - if (workerThread!=null) { - workerThread.stopTaskThread(); - } - - workerThread = new KetosThread(getKetosWorker()); - workerThread.setPriority(Thread.MAX_PRIORITY); - workerThread.start(); - } - - } - - /** - * The task thread to run Ketos classifier in real time. - * - * @author Jamie Macaulay - * - */ - public class KetosThread extends DLTaskThread { - - KetosThread(DLModelWorker soundSpotWorker) { - super(soundSpotWorker); - } - - @Override - public void newDLResult(GenericPrediction soundSpotResult, GroupedRawData groupedRawData) { - soundSpotResult.setClassNameID(GenericDLClassifier.getClassNameIDs(ketosDLParams)); - soundSpotResult.setBinaryClassification(GenericDLClassifier.isBinaryResult(soundSpotResult, ketosDLParams)); - newResult(soundSpotResult, groupedRawData); - } - - } - - /** - * Send a new result form the thread queue to the process. - * @param modelResult - the model result; - * @param groupedRawData - the grouped raw data. - */ - protected void newResult(GenericPrediction modelResult, GroupedRawData groupedRawData) { - this.dlControl.getDLClassifyProcess().newModelResult(modelResult, groupedRawData); - } - - - @Override - public void closeModel() { - // TODO Auto-generated method stub - - } - @Override public String getName() { - return "Ketos"; + return MODEL_NAME; } @Override public DLCLassiferModelUI getModelUI() { return this.ketosUI; } + @Override - public Serializable getDLModelSettings() { + public DLModelWorker getDLWorker() { + return getKetosWorker(); + } + + + @Override + public StandardModelParams getDLParams() { return ketosDLParams; } - - @Override - public int getNumClasses() { - return ketosDLParams.numClasses; - } - - @Override - public DLClassName[] getClassNames() { - //System.out.println("Ketos Model: " + ketosDLParams.numClasses); - return ketosDLParams.classNames; - } - - @Override - public DLControl getDLControl() { - return dlControl; - } - - @Override - public boolean checkModelOK() { - return getKetosWorker().getModel()!=null; - } - + /** * Get the parameters for the Ketos classifier. * @param ketosDLParams - the Ketos parameters. @@ -245,6 +103,11 @@ public class KetosClassifier implements DLClassiferModel, PamSettings { this.ketosDLParams = ketosDLParams; } + @Override + public Serializable getDLModelSettings() { + return ketosDLParams; + } + /** * Get the KetosWorker. this handles loading and running the Ketos model. * @return the Ketos worker. @@ -259,13 +122,13 @@ public class KetosClassifier implements DLClassiferModel, PamSettings { @Override public String getUnitName() { - return dlControl.getUnitName()+"_Ketos"; + return dlControl.getUnitName()+"_" + MODEL_NAME; } @Override public String getUnitType() { - return dlControl.getUnitType()+"_Ketos"; + return dlControl.getUnitType()+"_" + MODEL_NAME; } @@ -275,7 +138,13 @@ public class KetosClassifier implements DLClassiferModel, PamSettings { if (ketosDLParams==null) { ketosDLParams = new KetosDLParams(); } - //System.out.println("SoundSpot have been saved. : " + soundSpotParmas.modelPath); + + ArrayList dlTransformParams = DLClassiferModel.getDLTransformParams(ketosDLParams.dlTransfroms); + + ketosDLParams.dlTransfromParams=dlTransformParams; + + System.out.println("KetosParams have been saved. : " + ketosDLParams.dlTransfromParams); + return ketosDLParams; } @@ -291,15 +160,20 @@ public class KetosClassifier implements DLClassiferModel, PamSettings { KetosDLParams newParameters = (KetosDLParams) pamControlledUnitSettings.getSettings(); if (newParameters!=null) { ketosDLParams = newParameters.clone(); - //System.out.println("SoundSpot have been restored. : " + soundSpotParmas.modelPath); + System.out.println("KetosParams have been restored. : " + ketosDLParams.dlTransfromParams); + if (ketosDLParams.dlTransfromParams!=null) { + ketosDLParams.dlTransfroms = DLTransformsFactory.makeDLTransforms((ArrayList) ketosDLParams.dlTransfromParams); + } } else ketosDLParams = new KetosDLParams(); return true; } - + + @Override - public ArrayList checkSettingsOK() { - return GenericDLClassifier.checkSettingsOK(ketosDLParams, dlControl); + public boolean isModelType(URI uri) { + //Ketos is easy because there are not many files with a .ktpb extension. + return super.isModelExtensions(uri); } } diff --git a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosClassifier2.java b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosClassifier2.java new file mode 100644 index 00000000..70269a3f --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosClassifier2.java @@ -0,0 +1,66 @@ +package rawDeepLearningClassifier.dlClassification.ketos; + +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.dlClassification.archiveModel.ArchiveModelClassifier; +import rawDeepLearningClassifier.dlClassification.archiveModel.ArchiveModelWorker; + +/** + * Classifier which uses deep learning models from Meridian's Ketos framework. + *

    + * Ketos uses TensorFlow models and packages them inside a zipped .ktpb file + * which contains a JSON file for the transforms and a .pb model. Users can + * select a .ktpb file - PAMGaurd will decompress it, find the JSON file, set up + * the transforms and load the model. + *

    + * Details on Meridians framework can be found at https://meridian.cs.dal.ca/2015/04/12/ketos/ + *

    + * KetosClassifier2 is a more abstracted version of KetosClassifer which inherits most functionality from ArchiveModel + * @author Jamie Macaulay + * + */ +public class KetosClassifier2 extends ArchiveModelClassifier { + + public static String MODEL_NAME = "Ketos"; + + /** + * The file extensions + */ + private String[] fileExtensions = new String[] {"*.ktpb"}; + + + private KetosWorker2 ketosWorker; + + public KetosClassifier2(DLControl dlControl) { + super(dlControl); + } + + @Override + public String[] getFileExtensions() { + return fileExtensions; + } + + @Override + public String getName() { + return MODEL_NAME; + } + + @Override + public ArchiveModelWorker getModelWorker() { + if (ketosWorker==null) { + ketosWorker= new KetosWorker2(); + } + return ketosWorker; + } + + /** + * Create the parameters class for the model. This can be overridden for bespoke parameters. + *classes. + * @return a new parameters class object. + */ + public StandardModelParams makeParams() { + return new KetosDLParams(); + } + + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosModelPane.java b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosModelPane.java index 23d471b7..bd0b8f2b 100644 --- a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosModelPane.java +++ b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosModelPane.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import javafx.stage.FileChooser.ExtensionFilter; import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelPane; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; /** @@ -14,7 +15,7 @@ import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelPane; */ public class KetosModelPane extends StandardModelPane { - private ArrayList extensionFilters; +// private ArrayList extensionFilters; private KetosClassifier ketosClassifier; @@ -23,41 +24,52 @@ public class KetosModelPane extends StandardModelPane { super(soundSpotClassifier); this.ketosClassifier = soundSpotClassifier; - extensionFilters = new ArrayList(); - //import the settings holder - extensionFilters.add(new ExtensionFilter("Ketos Model", "*.ktpb")); } - @Override - public ArrayList getExtensionFilters() { - return extensionFilters; - } + @Override public void newModelSelected(File file) { - + //A ketos model contains information on the transforms, duration and the class names. - this.setCurrentSelectedFile(file); - this.setParamsClone(new KetosDLParams()); + if (this.getParamsClone()==null) { + this.setParamsClone(new KetosDLParams()); + } + + //set the current paramters for the clone + StandardModelParams params = getParams(getParamsClone()); + + +// if (params.dlTransfromParams!=null) { +// System.out.println("Ketos: Decimator: " + params.dlTransfromParams.get(0).params[0]); +// } +// else { +// System.out.println("Ketos:dltransform params is null" + getParamsClone().dlTransfromParams); +// } //prep the model with current parameters; - ketosClassifier.getKetosWorker().prepModel(getParams(getParamsClone()), ketosClassifier.getDLControl()); + + /** + * Note that the model prep will determine whether new transforms need to be loaded from the + * model or to use the existing transforms in the settings. + */ + ketosClassifier.getKetosWorker().prepModel(params, ketosClassifier.getDLControl()); //get the model transforms calculated from the model by SoundSpoyWorker and apply them to our temporary params clone. // System.out.println("Ketos transforms 1: " + this.ketosClassifier.getKetosWorker().getModelTransforms()); getParamsClone().dlTransfroms = this.ketosClassifier.getKetosWorker().getModelTransforms(); - - if (getParamsClone().defaultSegmentLen!=null) { - usedefaultSeg.setSelected(true); - } - //System.out.println("Ketos: " + getParamsClone().dlTransfroms.size()); +// if (getParamsClone().defaultSegmentLen!=null) { +// usedefaultSeg.setSelected(true); +// } + +// System.out.println("---------------------------- "); +// System.out.println("KETOS: new model selected " +getParamsClone().dlTransfromParams.size() + " \n " + getParamsClone()); + ///set the advanced pane parameters. getAdvSettingsPane().setParams(getParamsClone()); - - } } diff --git a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosModelTest.java b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosModelTest.java index 5123df1e..60731992 100644 --- a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosModelTest.java +++ b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosModelTest.java @@ -30,20 +30,26 @@ public class KetosModelTest { public static void main(String[] args) { //test on a right whale. - //File file = new File("/Volumes/GoogleDrive/My Drive/PAMGuard_dev/Deep_Learning/Meridian/right_whales/for_pamguard/narw.ktpb"); +// File file = new File("/Users/au671271/Library/CloudStorage/GoogleDrive-macster110@gmail.com/My Drive/PAMGuard_dev/Deep_Learning/Ketos/right_whales/for_pamguard/narw.ktpb"); // File file = new File("/Volumes/GoogleDrive-108005893101854397430/My Drive/PAMGuard_dev/Deep_Learning/Meridian/humpback_whales/SOCAL_Mn_Network.ktpb"); //File file = new File("/Volumes/GoogleDrive-108005893101854397430/My Drive/PAMGuard_dev/Deep_Learning/Meridian/orca/kw_detector_v11_5s.ktpb"); //the wav file to test. - //String wavFilePath = "/Volumes/GoogleDrive/My Drive/PAMGuard_dev/Deep_Learning/Meridian/right_whales/for_pamguard/input.wav"; +// String wavFilePath = "/Users/au671271/Library/CloudStorage/GoogleDrive-macster110@gmail.com/My Drive/PAMGuard_dev/Deep_Learning/Ketos/right_whales/for_pamguard/input.wav"; // String wavFilePath = "/Volumes/GoogleDrive-108005893101854397430/My Drive/PAMGuard_dev/Deep_Learning/Meridian/humpback_whales/wav/5353.210403161502.wav"; -// double windowSize = 3.52; +// double[] window = new double[]{0., 3.0}; +// //Minke model +// File file = new File("/Users/au671271/Desktop/Minke_test/Minke_Network_12s.ktpb"); +// String wavFilePath = "/Users/au671271/Desktop/Minke_test/1705_FLAC_1705_20171106_185953_253.wav"; +// double windowSize = 12; - //Minke model - File file = new File("/Users/au671271/Desktop/Minke_test/Minke_Network_12s.ktpb"); - String wavFilePath = "/Users/au671271/Desktop/Minke_test/1705_FLAC_1705_20171106_185953_253.wav"; - double windowSize = 12; +// + File file = new File("/Users/au671271/Library/CloudStorage/GoogleDrive-macster110@gmail.com/My Drive/PAMGuard_dev/Deep_Learning/Ketos/narw_2/hallo-kw-det_v1_test/hallo-kw-det_v1.ktpb"); + String wavFilePath = "/Users/au671271/Library/CloudStorage/GoogleDrive-macster110@gmail.com/My Drive/PAMGuard_dev/Deep_Learning/Ketos/narw_2/hallo-kw-det_v1_test/audio/jasco_reduced.wav"; +// double[] window = new double[]{10., 15.0176}; + double[] window = new double[]{45, 50.0176}; + try { //the ketos model. @@ -58,22 +64,38 @@ public class KetosModelTest { //System.out.println(ketosParams.toString()); System.out.println("Output shape" + ketosParams.defaultOutputShape); + + System.out.println("Input shape" + ketosParams.defaultInputShape); + + + //28-04-2023 seems like there is a BUG in ketos where the input shape reported by the model is incorrect. + ketosModel.setInputShape(ketosParams.defaultInputShape); + //Open wav files. AudioData soundData = DLUtils.loadWavFile(wavFilePath); - soundData = soundData.trim(0, (int) (soundData.getSampleRate()*windowSize)); - + soundData = soundData.trim((int) (soundData.getSampleRate()*window[0]), (int) (soundData.getSampleRate()*window[1])); + System.out.println("Input sample rate is " + soundData.getSampleRate()); + + //generate the transforms. ArrayList transforms = DLTransformsFactory.makeDLTransforms(ketosParams.dlTransforms); - - + + ((WaveTransform) transforms.get(0)).setWaveData(soundData); - DLTransform transform = transforms.get(0); - for (int i=0; i extensionFilters; /** * SondSpot classifier. @@ -30,6 +37,10 @@ public class KetosUI implements DLCLassiferModelUI { */ public KetosUI(KetosClassifier ketosClassifier) { this.ketosClassifier=ketosClassifier; + + extensionFilters = new ArrayList(); + //import the settings holder + extensionFilters.add(new ExtensionFilter("Ketos Model", "*.ktpb")); } @Override @@ -43,8 +54,8 @@ public class KetosUI implements DLCLassiferModelUI { @Override public void getParams() { - KetosDLParams genericParams = (KetosDLParams) getSettingsPane().getParams(ketosClassifier.getKetosParams()); - ketosClassifier.setKetosParams(genericParams); + KetosDLParams ketosParams = (KetosDLParams) getSettingsPane().getParams(ketosClassifier.getKetosParams()); + ketosClassifier.setKetosParams(ketosParams); } @@ -61,4 +72,17 @@ public class KetosUI implements DLCLassiferModelUI { return null; } + @Override + public List getModelFileExtensions() { + return extensionFilters; + } + + @Override + public Node getIcon() { + // TODO Auto-generated method stub + return null; + } + + + } \ No newline at end of file diff --git a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosWorker.java b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosWorker.java index d39c40ce..3a937c1b 100644 --- a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosWorker.java +++ b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosWorker.java @@ -11,12 +11,11 @@ import org.jamdev.jdl4pam.transforms.DLTransform.DLTransformType; import org.jamdev.jdl4pam.transforms.DLTransformsFactory; import org.jamdev.jdl4pam.transforms.jsonfile.DLTransformsParser; -import PamModel.PamModel; -import PamModel.PamModel.PluginClassloader; +import PamView.dialog.warn.WarnOnce; import rawDeepLearningClassifier.DLControl; import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker; -import rawDeepLearningClassifier.dlClassification.genericModel.PamGenericModel; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; /** * @@ -26,7 +25,7 @@ import rawDeepLearningClassifier.dlClassification.genericModel.PamGenericModel; * @author Jamie Macaulay * */ -public class KetosWorker extends DLModelWorker { +public class KetosWorker extends DLModelWorker { /** @@ -51,21 +50,28 @@ public class KetosWorker extends DLModelWorker { * Prepare the model */ public void prepModel(StandardModelParams ketosDLParams, DLControl dlControl) { - ClassLoader origCL = Thread.currentThread().getContextClassLoader(); + //ClassLoader origCL = Thread.currentThread().getContextClassLoader(); try { // get the plugin class loader and set it as the context class loader // NOTE THAT THIS IS REQUIRED TO MAKE THIS MODULE RUN AS A PLUGIN WHEN THE CLASS FILES // ARE BUNDLED INTO A FATJAR, HOWEVER THIS WILL STOP THE PLUGIN FROM RUNNING AS A SEPARATE // PROJECT IN ECLIPSE. So while testing the code and debugging, make sure the - if (DLControl.PLUGIN_BUILD) { - PluginClassloader newCL = PamModel.getPamModel().getClassLoader(); - Thread.currentThread().setContextClassLoader(newCL); - } +// if (DLControl.PLUGIN_BUILD) { +// PluginClassloader newCL = PamModel.getPamModel().getClassLoader(); +// Thread.currentThread().setContextClassLoader(newCL); +// } //first open the model and get the correct parameters. //21/11/2022 - Added a null and filename check here to stop the mdoel reloading everytime PAMGuard hits a new file or //is stopped or started - this was causing a memory leak. if (ketosModel==null || currentPath ==null || !Paths.get(currentPath).equals(Paths.get(ketosDLParams.modelPath))) { + + + //TODO +// if (ketosModel!=null && ketosModel.getModel()!=null) { +// ketosModel.getModel().close(); +// } + //System.out.println(Paths.get(genericParams.modelPath)); this.currentPath = ketosDLParams.modelPath; ketosModel = new KetosModel(new File(ketosDLParams.modelPath)); @@ -84,15 +90,21 @@ public class KetosWorker extends DLModelWorker { String jsonString = DLTransformsParser.readJSONString(new File(ketosModel.getAudioReprFile())); //convert the JSON string to a parameters object. - KetosParams ketosParams = new KetosParams(jsonString); + KetosParams ketosParams = new KetosParams(jsonString); + //important to add this for Ketos models because the JSON string does not necessarily contain and output shape. //System.out.println("----Default output shape: " + ketosParams.defaultOutputShape + " " + ketosModel.getOutShape()); if (ketosParams.defaultOutputShape==null) { ketosParams.defaultOutputShape = ketosModel.getOutShape(); } - - + //HACK there seems to be some sort of bug in ketos where the params input shape is correct but the model input shape is wrong. + if (ketosModel.getInputShape()==null || !ketosModel.getInputShape().equals(ketosParams.defaultInputShape)) { + WarnOnce.showWarning("Model shape", "The model shape does not match the model metadata. \n Metadata shape will be used used.", WarnOnce.OK_OPTION); + ketosModel.setInputShape(ketosParams.defaultInputShape); + } + + ///HACK here for now to fix an issue with dB and Ketos transforms having zero length somehow... for (int i=0; i { } } - //generate the transforms from the KetosParams objectts. + //generate the transforms from the KetosParams objects. ArrayList transforms = DLTransformsFactory.makeDLTransforms(ketosParams.dlTransforms); ///HACK here for now to fix an issue with dB and Ketos transforms having zero length somehow... for (int i=0; i { if (ketosParams.classNames!=null) { ketosDLParams.classNames = dlControl.getClassNameManager().makeClassNames(ketosParams.classNames); + ketosDLParams.numClasses = ketosDLParams.classNames.length; } + else { + //set the number of class names from the default output shape + ketosDLParams.numClasses = (int) ketosParams.defaultOutputShape.get(1); + } + // if (dlParams.classNames!=null) { // for (int i = 0; i { e.printStackTrace(); //WarnOnce.showWarning(null, "Model Metadata Error", "There was an error extracting the metadata from the model.", WarnOnce.OK_OPTION); } - Thread.currentThread().setContextClassLoader(origCL); + //Thread.currentThread().setContextClassLoader(origCL); } @Override public float[] runModel(float[][][] transformedDataStack) { + System.out.println("Model input: " + transformedDataStack.length + " " + transformedDataStack[0].length + " " + transformedDataStack[0][0].length); return ketosModel.runModel(transformedDataStack); } @@ -175,6 +203,7 @@ public class KetosWorker extends DLModelWorker { */ public void closeModel() { //TODO + this.currentPath = null; } @@ -186,5 +215,10 @@ public class KetosWorker extends DLModelWorker { return ketosModel; } + @Override + public boolean isModelNull() { + return ketosModel==null; + } + } diff --git a/src/rawDeepLearningClassifier/dlClassification/ketos/KetosWorker2.java b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosWorker2.java new file mode 100644 index 00000000..a77c1abd --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/ketos/KetosWorker2.java @@ -0,0 +1,39 @@ +package rawDeepLearningClassifier.dlClassification.ketos; + +import java.io.File; +import java.io.IOException; + +import org.jamdev.jdl4pam.ArchiveModel; +import org.jamdev.jdl4pam.genericmodel.GenericModelParams; +import org.jamdev.jdl4pam.ketos.KetosModel; +import org.jamdev.jdl4pam.ketos.KetosParams; + +import ai.djl.MalformedModelException; +import rawDeepLearningClassifier.dlClassification.archiveModel.ArchiveModelWorker; + +public class KetosWorker2 extends ArchiveModelWorker { + + /** + * Load a model from a file + * @param currentPath- the path to the model. + * @return- the loaded model object. + * @throws MalformedModelException + * @throws IOException + */ + public ArchiveModel loadModel(String currentPath2) throws MalformedModelException, IOException { + return new KetosModel(new File(currentPath2)); + } + + /** + * Create the parameters from a JSON string. + * @param jsonString - the json string. + * @return the paramters. + */ + public GenericModelParams makeModelParams(String jsonString) { + //ketos parameters are non standard and need a bit of extra work to get right. + //This also deal with legacy paramters. + KetosParams params = new KetosParams(jsonString); +// System.out.println(params); + return params; + } +} diff --git a/src/rawDeepLearningClassifier/dlClassification/koogu/KooguClassifier.java b/src/rawDeepLearningClassifier/dlClassification/koogu/KooguClassifier.java new file mode 100644 index 00000000..411d6551 --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/koogu/KooguClassifier.java @@ -0,0 +1,58 @@ +package rawDeepLearningClassifier.dlClassification.koogu; + +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.dlClassification.archiveModel.ArchiveModelClassifier; +import rawDeepLearningClassifier.dlClassification.archiveModel.ArchiveModelWorker; +import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; + +/** + * Classifier which uses deep learning models from Koogus' framework. + *

    + * Koogu uses TensorFlow models and packages them inside a zipped .kgu file + * which contains a standard JSON file for the transforms and a .pb model. Users can + * select a .kgu file - PAMGaurd will decompress it, find the JSON file, set up + * the transforms and load the model. + *

    + * Details on Koogu framework can be found athttps://github.com/shyamblast/Koogu?tab=readme-ov-file + * @author Jamie Macaulay + * + */ +public class KooguClassifier extends ArchiveModelClassifier { + + + public static String MODEL_NAME = "Koogu"; + + /** + * The file extensions + */ + private String[] fileExtensions = new String[] {"*.kgu"}; + + private KooguModelWorker kooguWorker; + + + public KooguClassifier(DLControl dlControl) { + super(dlControl); + } + + @Override + public String[] getFileExtensions() { + return fileExtensions; + } + + @Override + public String getName() { + return MODEL_NAME; + } + + @Override + public ArchiveModelWorker getModelWorker() { + if (kooguWorker==null) { + kooguWorker= new KooguModelWorker(); + } + return kooguWorker; + } + + + +} \ No newline at end of file diff --git a/src/rawDeepLearningClassifier/dlClassification/koogu/KooguModelWorker.java b/src/rawDeepLearningClassifier/dlClassification/koogu/KooguModelWorker.java new file mode 100644 index 00000000..4026575a --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/koogu/KooguModelWorker.java @@ -0,0 +1,31 @@ +package rawDeepLearningClassifier.dlClassification.koogu; + +import java.io.File; +import java.io.IOException; + +import org.jamdev.jdl4pam.ArchiveModel; +import org.jamdev.jdl4pam.koogu.KooguModel; + +import ai.djl.MalformedModelException; +import rawDeepLearningClassifier.dlClassification.archiveModel.ArchiveModelWorker; + +/** + * The Koogu model worker. + * + * @author Jamie Macaulay + * + */ +public class KooguModelWorker extends ArchiveModelWorker { + + + /** + * Load a model from a file + * @param currentPath- the path to the model. + * @return- the loaded model object. + * @throws MalformedModelException + * @throws IOException + */ + public ArchiveModel loadModel(String currentPath2) throws MalformedModelException, IOException { + return new KooguModel(new File(currentPath2)); + } +} diff --git a/src/rawDeepLearningClassifier/dlClassification/orcaSpot/OrcaSpotClassifier.java b/src/rawDeepLearningClassifier/dlClassification/orcaSpot/OrcaSpotClassifier.java index 74f70941..68fb845a 100644 --- a/src/rawDeepLearningClassifier/dlClassification/orcaSpot/OrcaSpotClassifier.java +++ b/src/rawDeepLearningClassifier/dlClassification/orcaSpot/OrcaSpotClassifier.java @@ -1,6 +1,7 @@ package rawDeepLearningClassifier.dlClassification.orcaSpot; import java.io.Serializable; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -9,14 +10,16 @@ import java.util.concurrent.atomic.AtomicBoolean; import PamController.PamControlledUnitSettings; import PamController.PamSettingManager; import PamController.PamSettings; +import PamguardMVC.PamDataUnit; import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.DLStatus; import rawDeepLearningClassifier.dlClassification.DLClassName; import rawDeepLearningClassifier.dlClassification.DLClassiferModel; import rawDeepLearningClassifier.dlClassification.DLDataUnit; import rawDeepLearningClassifier.dlClassification.DLDetection; import rawDeepLearningClassifier.dlClassification.PredictionResult; import rawDeepLearningClassifier.layoutFX.DLCLassiferModelUI; -import rawDeepLearningClassifier.segmenter.SegmenterProcess.GroupedRawData; +import rawDeepLearningClassifier.segmenter.GroupedRawData; import warnings.PamWarning; /** @@ -97,18 +100,18 @@ public class OrcaSpotClassifier implements DLClassiferModel, PamSettings { } @Override - public ArrayList runModel(ArrayList rawDataUnits) { + public ArrayList runModel(ArrayList rawDataUnits) { - for (GroupedRawData groupedRawData: rawDataUnits){ - if (queue.size()>MAX_QUEUE_SIZE) { - //we are not doing well - clear the buffer - queue.clear(); - } - queue.add(groupedRawData); - - - } - this.orcaSpotUI.notifyUpdate(-1); +// for (PamDataUnit groupedRawData: rawDataUnits){ +// if (queue.size()>MAX_QUEUE_SIZE) { +// //we are not doing well - clear the buffer +// queue.clear(); +// } +// queue.add(groupedRawData); +// +// +// } +// this.orcaSpotUI.notifyUpdate(-1); return null; } @@ -339,14 +342,28 @@ public class OrcaSpotClassifier implements DLClassiferModel, PamSettings { } @Override - public boolean checkModelOK() { - return true; + public DLStatus getModelStatus() { + return null; + } + +// @Override +// public ArrayList checkSettingsOK() { +// // TODO Auto-generated method stub +// return null; +// } + + @Override + public boolean isModelType(URI uri) { + // TODO Auto-generated method stub + return false; } @Override - public ArrayList checkSettingsOK() { + public DLStatus setModel(URI model) { // TODO Auto-generated method stub return null; } + + } diff --git a/src/rawDeepLearningClassifier/dlClassification/orcaSpot/OrcaSpotClassifierUI.java b/src/rawDeepLearningClassifier/dlClassification/orcaSpot/OrcaSpotClassifierUI.java index 6917d124..fae4e0b9 100644 --- a/src/rawDeepLearningClassifier/dlClassification/orcaSpot/OrcaSpotClassifierUI.java +++ b/src/rawDeepLearningClassifier/dlClassification/orcaSpot/OrcaSpotClassifierUI.java @@ -1,8 +1,12 @@ package rawDeepLearningClassifier.dlClassification.orcaSpot; +import java.util.List; + import javax.swing.SwingUtilities; import PamController.SettingsPane; +import javafx.scene.Node; +import javafx.stage.FileChooser.ExtensionFilter; import rawDeepLearningClassifier.layoutFX.DLCLassiferModelUI; /** @@ -70,4 +74,16 @@ public class OrcaSpotClassifierUI implements DLCLassiferModelUI { return orcaSpotSidePanel; } + @Override + public List getModelFileExtensions() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Node getIcon() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/src/rawDeepLearningClassifier/layoutFX/DLCLassiferModelUI.java b/src/rawDeepLearningClassifier/layoutFX/DLCLassiferModelUI.java index a1845d78..30a5e4ef 100644 --- a/src/rawDeepLearningClassifier/layoutFX/DLCLassiferModelUI.java +++ b/src/rawDeepLearningClassifier/layoutFX/DLCLassiferModelUI.java @@ -1,7 +1,11 @@ package rawDeepLearningClassifier.layoutFX; +import java.util.List; + import javax.swing.JPanel; +import javafx.scene.Node; +import javafx.stage.FileChooser.ExtensionFilter; import PamController.SettingsPane; /** @@ -27,11 +31,25 @@ public interface DLCLassiferModelUI { */ public void setParams(); + + /** + * If using a file dialog to search for + * @return the file extensions (if any) for this type of classifier + */ + public List getModelFileExtensions(); + /** * Get a side panel specific to the classifier. * @return the side panel. */ public JPanel getSidePanel(); + /** + * Get an icon for the model. Note that this can be null in + * which case the icon in the UI will be a label with the mdoel name. + * @return teh model icon. + */ + public Node getIcon(); + } diff --git a/src/rawDeepLearningClassifier/layoutFX/DLModelSelectPane.java b/src/rawDeepLearningClassifier/layoutFX/DLModelSelectPane.java new file mode 100644 index 00000000..c69bd5cb --- /dev/null +++ b/src/rawDeepLearningClassifier/layoutFX/DLModelSelectPane.java @@ -0,0 +1,614 @@ +package rawDeepLearningClassifier.layoutFX; + +import java.io.File; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; + +import org.controlsfx.control.PopOver; + +import ai.djl.Device; +import javafx.concurrent.Task; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; + +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; +import javafx.geometry.Insets; +import pamViewFX.PamGuiManagerFX; +import pamViewFX.fxGlyphs.PamGlyphDude; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamButton; +import pamViewFX.fxNodes.PamHBox; +import pamViewFX.fxNodes.PamTextField; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.DLStatus; +import rawDeepLearningClassifier.dlClassification.DLClassiferModel; +import rawDeepLearningClassifier.dlClassification.DefaultModels; +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.layoutFX.defaultModels.DefaultModelPane; + + +/** + * A pane which allows users to select a model and then loads the model on + * on a different thread, showing a progress indicator. + * + * Models could be potentially selected from + * 1) A file (implemented) + * 2) A URL (not implemented) + * 3) A default list of models. + * + * + * @author Jamie Macaulay + * + */ +public class DLModelSelectPane extends PamBorderPane { + + /** + * The directory chooser. + */ + private FileChooser fileChooser; + + /** + * Currently selected file. + */ + protected URI currentSelectedFile = new File(System.getProperty("user.home")).toURI(); + + /** + * The label showing the path to the file. + */ + private Label pathLabel; + + + /** + * The label showing the path to file. + */ + private ProgressIndicator modelLoadIndicator; + + /** + * The type of classifier selected + */ + private Label classiferInfoLabel; + + /** + * The DL control. + */ + private DLControl dlControl; + + /** + * The current classifier model. + */ + DLClassiferModel currentClassifierModel; + + /** + * The default models. + */ + private DefaultModels defaultModels; + + /** + * Pop over + */ + private PopOver urlPopOver; + + private TextField uriTextField; + + private DLSettingsPane rawDLSettingsPane; + + private DefaultModelPane defaultModelPane; + + + public DLModelSelectPane(DLSettingsPane rawDLSettingsPane) { + this.rawDLSettingsPane=rawDLSettingsPane; + this.dlControl=rawDLSettingsPane.getDLControl(); + this.setCenter(createDLSelectPane()); + //the directory chooser. + fileChooser = new FileChooser(); + fileChooser.setTitle("Classifier Model Location"); + } + + + public Pane createDLSelectPane() { + + + classiferInfoLabel = new Label(" Classifier"); + //PamGuiManagerFX.titleFont2style(classiferInfoLabel); + Font font= Font.font(null, FontWeight.BOLD, 11); + classiferInfoLabel.setFont(font); + + /**Basic classifier info**/ + pathLabel = new Label("No classifier file selected"); + // PamButton pamButton = new PamButton("", PamGlyphDude.createPamGlyph(MaterialDesignIcon.FILE, PamGuiManagerFX.iconSize)); + PamButton pamButton = new PamButton("", PamGlyphDude.createPamIcon("mdi2f-file", PamGuiManagerFX.iconSize)); + pathLabel.setMinWidth(100); + + modelLoadIndicator = new ProgressIndicator(-1); + modelLoadIndicator.setVisible(false); + modelLoadIndicator.prefHeightProperty().bind(pamButton.heightProperty().subtract(3)); + + pamButton.setMinWidth(30); + pamButton.setTooltip(new Tooltip("Load a model from a file")); + + + pamButton.setOnAction((action)->{ + + fileChooser.getExtensionFilters().clear(); + fileChooser.getExtensionFilters().addAll(getExtensionFilters()); + + + Path path = Paths.get(currentSelectedFile); + if(path!=null && Files.exists(path, LinkOption.NOFOLLOW_LINKS)) { + fileChooser.setInitialDirectory(new File(new File(currentSelectedFile).getParent())); + } + else { + fileChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + } + + File file = fileChooser.showOpenDialog(null); + + if (file==null) { + return; + } + + loadNewModel(file.toURI()); + + }); + + + PamHBox urlBox = new PamHBox(); + urlBox.setSpacing(5); + + PamButton download = new PamButton(); + download.setTooltip(new Tooltip("Download the model from set URL")); + download.setGraphic(PamGlyphDude.createPamIcon("mdi2d-download", PamGuiManagerFX.iconSize)); + download.setDisable(true); + + download.setOnAction((action)->{ + + if (uriTextField.getText().isEmpty()) return; + + try { + URI uri = new URI(uriTextField.getText()); + + loadNewModel(uri); + + } catch (Exception e) { + PamDialogFX.showWarning("A valid URL could not be created"); + e.printStackTrace(); + } + + }); + + final String urlHelpText = "Enter the internet address (URL) for the model"; + + uriTextField = new PamTextField(); + uriTextField.prefHeightProperty().bind(download.heightProperty()); + uriTextField.setPrefWidth(300); + uriTextField.setText("Enter the internet address (URL) for the model"); + uriTextField.textProperty().addListener((obsVal, oldVal, newVal)->{ + if (uriTextField.textProperty().get().isBlank() || uriTextField.textProperty().get().equals(urlHelpText)) { + download.setDisable(true); + } + else { + download.setDisable(false); + } + }); + + urlBox.getChildren().addAll(uriTextField, download); + + PamVBox urlHolder = new PamVBox(); + urlHolder.setPadding(new Insets(5,5,5,5)); + urlHolder.setSpacing(5); + urlHolder.getChildren().addAll(new Label("Download Model"), urlBox); + + urlPopOver = new PopOver(); + urlPopOver.setContentNode(urlHolder); + + PamButton urlButton = new PamButton("", PamGlyphDude.createPamIcon("mdi2l-link-variant", PamGuiManagerFX.iconSize)); + urlButton.setTooltip(new Tooltip("Load a model from a URL")); + urlButton.setOnAction((action)->{ + urlPopOver.show(urlButton); + }); + + + PamButton defaults = new PamButton(); + defaults.setTooltip(new Tooltip("Default models")); + defaults.setGraphic(PamGlyphDude.createPamIcon("mdi2d-dots-vertical", PamGuiManagerFX.iconSize)); + defaults.setTooltip(new Tooltip("Load a default model")); + +// for (DefualtModel defaultmodel: defaultModels.getDefaultModels()) { +// defaults.getItems().add(new MenuItem(defaultmodel.name)); +// } +// defaults.prefHeightProperty().bind(urlButton.heightProperty()); + + defaultModelPane = new DefaultModelPane(this.dlControl.getDefaultModelManager()); + defaultModelPane.setPadding(new Insets(5,5,5,5)); + defaultModelPane.defaultModelProperty().addListener((obsVal, oldVal, newVal)->{ + if (newVal!=oldVal) { + //a default model needs to be loaded. + Task task = loadNewModel(newVal.getModelURI()); + + task.setOnSucceeded((val)->{ + if (currentClassifierModel!=null) { + + //set the parameters from the classifer model to have correct + //transfroms etc. + newVal.setParams(currentClassifierModel.getDLModelSettings()); + + //set the correct classifier pane. + rawDLSettingsPane.setClassifierPane(); + + //need to make sure the classifier pane is updated + currentClassifierModel.getModelUI().setParams(); + + //need to update the segment length... + if (currentClassifierModel.getDLModelSettings() instanceof StandardModelParams) { + double segLen = ((StandardModelParams) currentClassifierModel.getDLModelSettings()).defaultSegmentLen; + this.rawDLSettingsPane.setSegmentLength(segLen); + //set to half the hop size too + this.rawDLSettingsPane.setHopLength(segLen/2); + } + + } + else { + //this should never happen unless there is no internet + System.err.println("Default model failed ot load: "+ newVal); + } + }); + } + }); + + PopOver defualtModelPopOver = new PopOver(); + defualtModelPopOver.setContentNode(defaultModelPane); + + defaults.setOnAction((action)->{ + defualtModelPopOver.show(defaults); + }); + + PamHBox hBox = new PamHBox(); + hBox.setSpacing(5); + hBox.getChildren().addAll(modelLoadIndicator, pathLabel, urlButton, pamButton, defaults); + hBox.setAlignment(Pos.CENTER_RIGHT); + PamHBox.setHgrow(pathLabel, Priority.ALWAYS); + + return hBox; + } + + /** + * Load a new model on a seperate thread. + * @param uri - the uri to the model. + */ + public Task loadNewModel(URI uri) { + // separate non-FX thread - load the model + //on a separate thread so we can show a moving load + //bar on the FX thread. Otherwise the GUI locks up + //whilst stuff is loaded. + if (uri==null) return null; + +// pathLabel.setText("Loading model..."); + modelLoadIndicator.setVisible(true); + + Task task = new LoadTask(uri); + + modelLoadIndicator.progressProperty().bind(task.progressProperty()); + + pathLabel.setGraphic(null); //remove error icon if there is one. + pathLabel.textProperty().bind(task.messageProperty()); + + + //start the load taks. + Thread th = new Thread(task); + th.setDaemon(true); + th.start(); + + return task; + + + + // Thread th = new Thread(task); + // th.setDaemon(true); + // th.start(); + // + // new Thread() { + // // runnable for that thread + // public void run() { + // try { + // + // //either downloads the model or instantly returns the model path. + // URI modelPath = dlControl.getDownloadManager().downloadModel(uri); + // + // newModelSelected(modelPath); + // currentSelectedFile = modelPath; + // Thread.sleep(1000); //just show the user something happened if model loading is rapid. + // } catch (InterruptedException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + // + // Platform.runLater(new Runnable() { + // + // public void run() { + // try { + // rawDLSettingsPane.setClassifierPane(); + // modelLoadIndicator.setVisible(false); + // updatePathLabel(); + // } + // catch (Exception e) { + // e.printStackTrace(); + // } + // } + // + // }); + // } + // }.start(); + } + + + /** + * A new model has been selected + * @param the load status of the model. + */ + private DLStatus newModelSelected(URI file) { + + if (file == null) { + currentClassifierModel=null; + return DLStatus.FILE_NULL; + } + + this.currentClassifierModel = this.dlControl.getDlClassifierChooser().selectClassiferModel(file); + + System.out.println("New classifier model selected!: " + currentClassifierModel); + if (currentClassifierModel!=null) { + + try { + //we are loading model from a file - anything can happen so put in a try catch. + DLStatus status = currentClassifierModel.setModel(file); + + + if (status.isError()) { + System.err.println("Model load failed: " + currentClassifierModel.getModelStatus()); + currentClassifierModel=null; + return status; + } + + return DLStatus.MODEL_LOAD_SUCCESS; + } + catch (Exception e) { + e.printStackTrace(); + currentClassifierModel=null; + return DLStatus.MODEL_LOAD_FAILED; + } + } + else { + currentClassifierModel=null; + return DLStatus.MODEL_LOAD_FAILED; + } + } + + private void showWarningDialog(DLStatus status) { + this.rawDLSettingsPane.showWarning(status); +// PamDialogFX.showError(status.getName(), status.getDescription()); + } + + + /** + * Create an error icon based on the status message. + * @param status - the status + * @return error icon or null of the status is not an error. + */ + private Node createErrorIcon(DLStatus status) { + Node decoration = null; + if (status.isError()){ + decoration = PamGlyphDude.createPamIcon("mdi2c-close-circle-outline", Color.RED, 10); + } + else if (status.isWarning()) { + decoration = PamGlyphDude.createPamIcon("mdi2c-close-circle-outline", Color.ORANGE, 10); + } + return decoration; + } + + /** + * Update the path label and tool tip text + */ + protected void updatePathLabel(DLStatus status) { + + /** + * This a bit complicated. We want the label to show errors if they have occured and warning to prompt the + * user to do things. The sequence of this is quite important to get right. + */ + + pathLabel.setGraphic(null); + + if (currentClassifierModel == null) { + //no frameowrk could be selected for the model. + pathLabel.setGraphic(createErrorIcon(DLStatus.NO_MODEL_LOADED)); + pathLabel.setText("No classifier model loaded: Select model"); + pathLabel.setTooltip(new Tooltip("Use the browse button/ URI botton to select a model or select a default model")); + } + else if (currentClassifierModel.getModelStatus().isError()) { + pathLabel.setGraphic(createErrorIcon(currentClassifierModel.getModelStatus())); + pathLabel.setText(currentClassifierModel.getModelStatus().getName()); + pathLabel.setTooltip(new Tooltip(currentClassifierModel.getModelStatus().getDescription())); + } + else if (status.isError()) { + pathLabel.setGraphic(createErrorIcon(status)); + pathLabel.setText(status.getName()); + pathLabel.setTooltip(new Tooltip(status.getDescription())); + } + else { + pathLabel .setText(new File(this.currentSelectedFile).getName()); + //show a warning icon if needed. + String tooltip = ""; + try { + tooltip += (this.currentSelectedFile.getPath() + + "\n" +" Processor CPU " + Device.cpu() + " " + Device.gpu()); + tooltip+="\n"; + } + catch (Exception e) { + //sometimes get an error here for some reason + //does not make a difference other than tooltip. + System.err.println("StandardModelPane: Error getting the default device!"); + } + if (status.isWarning()) { + tooltip+="Warning: " + status.getDescription(); + } + pathLabel.setTooltip(new Tooltip(tooltip)); + } + + } + + /** + * Get the extension filters for the file selection dialog. + * @return the extension filters for the model file dialog. + */ + + public ArrayList getExtensionFilters() { + + + ArrayList extensionFilters = new ArrayList(); + + for (DLClassiferModel dlModel: dlControl.getDLModels()) { + //System.out.println("Model: " + dlModel.getModelUI()); + + if (dlModel.getModelUI()!=null) { + for (ExtensionFilter extFilter: dlModel.getModelUI().getModelFileExtensions()){ + //System.out.println("Extensions: " + extFilter.getExtensions()); + extensionFilters.addAll(extFilter.getExtensions()); + } + } + } + + //Now we don't really want lots of extension filters + ArrayList dlExtFilter = new ArrayList(); + dlExtFilter.add(new ExtensionFilter("Deep Learning Models", extensionFilters)); + + return dlExtFilter ; + } + + /** + * Task for loading and/or downloading deep learning models. + */ + class LoadTask extends Task { + + private URI uri; + + + + public LoadTask(URI uri) { + this.uri=uri; + + + //TODO - not the best as some other part of the program could be using download listeners... + dlControl.getDownloadManager().clearDownloadListeners(); + + dlControl.getDownloadManager().addDownloadListener((status, bytesDownLoaded)->{ + updateMessage( status, bytesDownLoaded); + }); + + } + + private void updateMessage(DLStatus status, long bytesDownLoaded) { + //the updates have their own messages but let's take some more control here. + this.updateProgress(-1, 1); //set to intermediate + //System.out.println("Status: " + status); + switch (status) { + case CONNECTION_TO_URL: + this.updateMessage("Checking URL"); + break; + case DOWNLOADING: + this.updateMessage(String.format("Download %.2f MB", ((double) bytesDownLoaded)/1024./1024.)); + break; + case DOWNLOAD_FINISHED: + this.updateMessage("Download complete"); + break; + case DOWNLOAD_STARTING: + this.updateMessage("Download starting"); + break; + default: + this.updateMessage(status.getDescription()); + break; + } + } + + + @Override + public DLStatus call() throws Exception { + try { + this.updateMessage("Loading model..."); + + //either downloads the model or instantly returns the model path if it's a file. + //The download listener will update progress if this starts downloading a file. + URI modelPath = dlControl.getDownloadManager().downloadModel(uri); + + + if (modelPath==null) { + return DLStatus.MODEL_DOWNLOAD_FAILED; + } + + this.updateMessage("Loading model..."); + + DLStatus result = newModelSelected(modelPath); + + currentSelectedFile = modelPath; + Thread.sleep(1000); //just show the user something happened if model loading is rapid. + + return result; + + } catch (Exception e) { + System.out.println("UNABLE TO LOAD MODEL"); + currentClassifierModel=null; //this will reset the pane + e.printStackTrace(); + return DLStatus.MODEL_LOAD_FAILED; + } + } + + private void finishedLoading() { + + if (this.getValue().isError()) { + showWarningDialog(this.getValue()); + } + + rawDLSettingsPane.setClassifierPane(); + modelLoadIndicator.setVisible(false); + + //important to stop set a bound property exception. + pathLabel.textProperty().unbind(); + modelLoadIndicator.progressProperty().unbind(); + + updatePathLabel(this.getValue()); + } + + @Override protected void succeeded() { + super.succeeded(); + finishedLoading(); + updateMessage("Done!"); + } + + @Override protected void cancelled() { + super.cancelled(); + finishedLoading(); + updateMessage("Cancelled!"); + } + + @Override protected void failed() { + super.failed(); + finishedLoading(); + updateMessage("Failed!"); + } + }; + + +} diff --git a/src/rawDeepLearningClassifier/layoutFX/RawDLSettingsPane.java b/src/rawDeepLearningClassifier/layoutFX/DLSettingsPane.java similarity index 60% rename from src/rawDeepLearningClassifier/layoutFX/RawDLSettingsPane.java rename to src/rawDeepLearningClassifier/layoutFX/DLSettingsPane.java index c867654d..b2c8c0e4 100644 --- a/src/rawDeepLearningClassifier/layoutFX/RawDLSettingsPane.java +++ b/src/rawDeepLearningClassifier/layoutFX/DLSettingsPane.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import org.controlsfx.control.PopOver; -import PamController.FlipSettingsPane; +import PamController.PamGUIManager; import PamController.SettingsPane; import PamDetection.RawDataUnit; import PamView.dialog.warn.WarnOnce; @@ -16,15 +16,14 @@ import javafx.application.Platform; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; import javafx.scene.control.Label; -import javafx.scene.control.PopupControl; import javafx.scene.control.Spinner; import javafx.scene.control.Tooltip; import javafx.scene.control.Alert.AlertType; +import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; -import javafx.stage.Stage; +import javafx.scene.layout.Priority; import pamViewFX.PamGuiManagerFX; import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; @@ -37,9 +36,10 @@ import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; import pamViewFX.fxNodes.utilityPanes.GroupedSourcePaneFX; import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.DLStatus; import rawDeepLearningClassifier.RawDLParams; -import rawDeepLearningClassifier.dlClassification.DLClassiferModel; import warnings.PamWarning; +import whistlesAndMoans.ConnectedRegionDataUnit; /** * The settings pane. @@ -47,11 +47,11 @@ import warnings.PamWarning; * @author Jamie Macaulay * */ -public class RawDLSettingsPane extends SettingsPane{ +public class DLSettingsPane extends SettingsPane{ public static double MAX_WIDTH = 270; - + /** * The source for the FFT data source. */ @@ -67,10 +67,10 @@ public class RawDLSettingsPane extends SettingsPane{ */ private PamBorderPane mainPane; - /** - * Combo box which allows users to select model. - */ - private ComboBox dlModelBox; + // /** + // * Combo box which allows users to select model. + // */ + // private ComboBox dlModelBox; /** * The window length spinner for the segmenter process @@ -114,11 +114,11 @@ public class RawDLSettingsPane extends SettingsPane{ private Label infoLabel; - private Object flipPane; + private DLModelSelectPane modelSelectPane; - private PopupControl advLabel; - public RawDLSettingsPane(DLControl dlControl){ + + public DLSettingsPane(DLControl dlControl){ super(null); this.dlControl=dlControl; // Button newButton=new Button("Test"); @@ -129,16 +129,19 @@ public class RawDLSettingsPane extends SettingsPane{ // stage.sizeToScene(); // }); // this.setTop(newButton); - - + + mainPane=new PamBorderPane(); mainPane.setCenter(createDLPane()); mainPane.setPadding(new Insets(5,5,5,5)); - mainPane.setMinHeight(400); - mainPane.setMaxWidth(MAX_WIDTH); - mainPane.setPrefWidth(MAX_WIDTH); + + if (!PamGUIManager.isFX()){ + mainPane.setMinHeight(430); + mainPane.setMaxWidth(MAX_WIDTH); + mainPane.setPrefWidth(MAX_WIDTH); + } //this.getAdvPane().setMaxWidth(MAX_WIDTH); - + //mainPane.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); @@ -157,19 +160,20 @@ public class RawDLSettingsPane extends SettingsPane{ sourcePane = new GroupedSourcePaneFX("Raw Sound Data", RawDataUnit.class, true, false, true); sourcePane.addSourceType(ClickDetection.class, false); sourcePane.addSourceType(ClipDataUnit.class, false); + sourcePane.addSourceType(ConnectedRegionDataUnit.class, false); vBox.getChildren().add(sourcePane); sourcePane.prefWidthProperty().bind(vBox.widthProperty()); sourcePane.setMaxWidth(Double.MAX_VALUE); - + sourcePane.getDataBlockBox().setOnAction((action)->{ //need to create a data selector if one exists. this.dlControl.createDataSelector(getSelectedParentDataBlock()); //enable the controls to show a data selector or not. enableControls(); }); - + //create the detection vBox.getChildren().add(createDataSelectorPane()); @@ -193,14 +197,14 @@ public class RawDLSettingsPane extends SettingsPane{ hopLength.valueProperty().addListener((obsVal, oldVal, newVal)->{ setSegInfoLabel(); }); - + reMergeSeg = new PamSpinner(0, Integer.MAX_VALUE, 1, 1); reMergeSeg.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); reMergeSeg.setEditable(true); //button to set default hop size Button defaultButton = new Button(); -// defaultButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialDesignIcon.REFRESH, PamGuiManagerFX.iconSize-3)); + // defaultButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialDesignIcon.REFRESH, PamGuiManagerFX.iconSize-3)); defaultButton.setGraphic(PamGlyphDude.createPamIcon("mdi2r-refresh", PamGuiManagerFX.iconSize-3)); defaultButton.setTooltip(new Tooltip("Set default hop size")); defaultButton.setOnAction((action)->{ @@ -208,6 +212,14 @@ public class RawDLSettingsPane extends SettingsPane{ }); PamGridPane segmenterGridPane = new PamGridPane(); + segmenterGridPane.setHgap(5); + + ColumnConstraints col1 = new ColumnConstraints(); + col1.setHgrow( Priority.SOMETIMES ); + ColumnConstraints col2 = new ColumnConstraints(); + col2.setHgrow( Priority.ALWAYS ); + segmenterGridPane.getColumnConstraints().addAll(col1, col2 ); + segmenterGridPane.add(new Label("Window length"), 0, 0); segmenterGridPane.add(windowLength, 1, 0); segmenterGridPane.add(new Label("samples"), 2, 0); @@ -222,45 +234,50 @@ public class RawDLSettingsPane extends SettingsPane{ segmenterGridPane.add(new Label("segments"), 2, 2); vBox.getChildren().add(segmenterGridPane); - + vBox.getChildren().add(infoLabel = new Label()); Label label2 = new Label("Deep Learning Model"); label2.setPadding(new Insets(5,0,0,0)); PamGuiManagerFX.titleFont2style(label2); - + vBox.getChildren().add(label2); - //add the possible deep learning models. - dlModelBox= new ComboBox(); - for (int i=0; i{ - setClassifierPane(); - if (mainPane!=null) { - if (mainPane.getScene().getWindow() instanceof Stage) { - Stage stage = (Stage) mainPane.getScene().getWindow(); - stage.sizeToScene(); - } - } - //this.dlControl.getAnnotationType().getSymbolModifier(symbolChooser). - }); - - vBox.getChildren().add(dlModelBox); + // //add the possible deep learning models. + // dlModelBox= new ComboBox(); + // for (int i=0; i{ + // setClassifierPane(); + // if (mainPane!=null) { + // if (mainPane.getScene().getWindow() instanceof Stage) { + // Stage stage = (Stage) mainPane.getScene().getWindow(); + // stage.sizeToScene(); + // } + // } + // //this.dlControl.getAnnotationType().getSymbolModifier(symbolChooser). + // }); + // + // vBox.getChildren().add(dlModelBox); classifierPane = new PamBorderPane(); - vBox.getChildren().add(classifierPane); + vBox.getChildren().addAll(modelSelectPane, classifierPane); return vBox; } - + /** * Create the data selector. * @return the data selector. @@ -269,13 +286,13 @@ public class RawDLSettingsPane extends SettingsPane{ dataSelectorPane = new PamHBox(); dataSelectorPane.setSpacing(5); dataSelectorPane.setAlignment(Pos.CENTER_LEFT); - + dataSelectorCheckBox = new PamToggleSwitch("Detection Selector"); dataSelectorCheckBox.selectedProperty().addListener((obsval, oldval, newval)->{ enableControls(); }); dataSelectorButton = new PamButton(); -// dataSelectorButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.SETTINGS, PamGuiManagerFX.iconSize)); + // dataSelectorButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.SETTINGS, PamGuiManagerFX.iconSize)); dataSelectorButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-cog", PamGuiManagerFX.iconSize)); dataSelectorButton.setOnAction((action)->{ showAdvPane(); @@ -283,7 +300,7 @@ public class RawDLSettingsPane extends SettingsPane{ dataSelectorPane.getChildren().addAll(dataSelectorCheckBox, dataSelectorButton); return dataSelectorPane; } - + /** * Set extra information in the info label. */ @@ -301,7 +318,7 @@ public class RawDLSettingsPane extends SettingsPane{ } infoLabel.setText(text); } - + /** * Creates pane allowing the user to change fine scale things such as error limits. * @return the pop over pane. @@ -322,21 +339,23 @@ public class RawDLSettingsPane extends SettingsPane{ popOver.show(dataSelectorButton); } - + /** * Enable the controls. */ private void enableControls() { this.dataSelectorPane.setVisible(true); - //only show the data selector box for detection data. - if (sourcePane.getSource().getDataSelectCreator() instanceof NullDataSelectorCreator) { + //only show the data selector box for detec tion data. + if (sourcePane.getSource() == null) this.dataSelectorPane.setVisible(false); + + else if (sourcePane.getSource().getDataSelectCreator() instanceof NullDataSelectorCreator) { //^bit messy but cannot think of a better way to do it. this.dataSelectorPane.setVisible(false); } - + dataSelectorButton.setDisable(!dataSelectorCheckBox.isSelected()); } - + /** @@ -360,13 +379,21 @@ public class RawDLSettingsPane extends SettingsPane{ /** * Set the classifier pane. */ - private void setClassifierPane() { + protected void setClassifierPane() { + + //set the classifier Pane.class - DLClassiferModel classifierModel = this.dlControl.getDLModels().get(dlModelBox.getSelectionModel().getSelectedIndex()); + if (modelSelectPane.currentClassifierModel!=null && modelSelectPane.currentClassifierModel.getModelUI()!=null) { + + classifierPane.setCenter(modelSelectPane.currentClassifierModel.getModelUI().getSettingsPane().getContentNode()); + + if (modelSelectPane.currentClassifierModel!=null) { + modelSelectPane.currentClassifierModel.getModelUI().setParams(); + } + else { + classifierPane.setCenter(null); + } - if (classifierModel.getModelUI()!=null) { - classifierPane.setCenter(classifierModel.getModelUI().getSettingsPane().getContentNode()); - classifierModel.getModelUI().setParams(); } else { classifierPane.setCenter(null); @@ -379,6 +406,7 @@ public class RawDLSettingsPane extends SettingsPane{ if (currParams==null ) currParams = new RawDLParams(); + @SuppressWarnings("rawtypes") PamDataBlock rawDataBlock = sourcePane.getSource(); if (rawDataBlock == null){ Platform.runLater(()->{ @@ -388,8 +416,8 @@ public class RawDLSettingsPane extends SettingsPane{ } sourcePane.getParams(currParams.groupedSourceParams); - - currParams.modelSelection = dlModelBox.getSelectionModel().getSelectedIndex(); + + // currParams.modelSelection = dlModelBox.getSelectionModel().getSelectedIndex(); if (windowLength.getValue() == 0 || hopLength.getValue()==0){ Platform.runLater(()->{ @@ -403,41 +431,87 @@ public class RawDLSettingsPane extends SettingsPane{ currParams.maxMergeHops = reMergeSeg.getValue(); - //update any changes - if (this.dlControl.getDLModels().get(dlModelBox.getSelectionModel().getSelectedIndex()).getModelUI()!=null){ - this.dlControl.getDLModels().get(dlModelBox.getSelectionModel().getSelectedIndex()).getModelUI().getParams(); - - //display any warnings from the settings. - ArrayList warnings = this.dlControl.getDLModels().get(dlModelBox.getSelectionModel().getSelectedIndex()).checkSettingsOK(); - showWarnings(warnings); - - for (int i=0; i1) { - //Serious error. Do not close dialog. - return null; - } - } + if (modelSelectPane.currentClassifierModel == null) { + currParams.modelSelection = -1; } - + else { + currParams.modelSelection = dlControl.getDLModels().indexOf((modelSelectPane.currentClassifierModel)); + } + + + // //update any changes + // if (this.dlControl.getDLModels().get(dlModelBox.getSelectionModel().getSelectedIndex()).getModelUI()!=null){ + // this.dlControl.getDLModels().get(dlModelBox.getSelectionModel().getSelectedIndex()).getModelUI().getParams(); + // + // //display any warnings from the settings. + // ArrayList warnings = this.dlControl.getDLModels().get(dlModelBox.getSelectionModel().getSelectedIndex()).checkSettingsOK(); + // showWarnings(warnings); + // + // for (int i=0; i1) { + // //Serious error. Do not close dialog. + // return null; + // } + // } + // } + currParams.useDataSelector = dataSelectorCheckBox.isSelected(); + if (dlControl.getDataSelector()!=null) { dlControl.getDataSelector().getDialogPaneFX().getParams(true); } - + + + //need to make sure we call get params for the current model when the oK button is pressed. + if (this.modelSelectPane.currentClassifierModel!=null) { + if (this.modelSelectPane.currentClassifierModel.getModelUI()!=null) { + this.modelSelectPane.currentClassifierModel.getModelUI().getParams(); + } + } + + currParams.modelURI = this.modelSelectPane.currentSelectedFile; return currParams; } + + public static PamWarning statusToWarnings(DLStatus dlStatus) { + PamWarning pamWarning = new PamWarning(dlStatus.getName(), dlStatus.getDescription(), dlStatus.isError() ? 2 : 1); + return pamWarning; + } + + /** + * Show a warning dialog for the status + * @param the status to show + */ + public void showWarning(DLStatus dlWarning) { + ArrayList dlWarnings = new ArrayList(); + dlWarnings.add(statusToWarnings(dlWarning)); + showWarning(dlWarnings); + } + /** * Show a warning dialog. + * @param the warning to show. */ - public void showWarnings(ArrayList dlWarnings) { - + public void showWarning(PamWarning dlWarning) { + ArrayList dlWarnings = new ArrayList(); + dlWarnings.add(dlWarning); + showWarning(dlWarnings); + } + + + /** + * Show a warning dialog. + * @param dlWarnings - list of warnings - the most important will be shown. + */ + public void showWarning(ArrayList dlWarnings) { + if (dlWarnings==null || dlWarnings.size()<1) return; - + String warnings =""; - - + + boolean error = false; for (int i=0; i{ error=true; } } - + final String warningsF = warnings; final boolean errorF = error; Platform.runLater(()->{ WarnOnce.showWarningFX(null, "Deep Learning Settings Warning", warningsF , errorF ? AlertType.ERROR : AlertType.WARNING); + // WarnOnce.showWarning( "Deep Learning Settings Warning", warningsF , WarnOnce.WARNING_MESSAGE); }); - + //user presses OK - these warnings are just a message - they do not prevent running the module. } @@ -460,25 +535,40 @@ public class RawDLSettingsPane extends SettingsPane{ sourcePane.setParams(currParams.groupedSourceParams); sourcePane.sourceChanged(); - + + dlControl.createDataSelector(sourcePane.getSource()); - dlModelBox.getSelectionModel().select(currParams.modelSelection); + //set the classifier model. + if (currParams.modelURI !=null) { + modelSelectPane.currentClassifierModel = dlControl.getDLModel(); + } + // dlModelBox.getSelectionModel().select(currParams.modelSelection); windowLength.getValueFactory().setValue(currParams.rawSampleSize); hopLength.getValueFactory().setValue(currParams.sampleHop); reMergeSeg.getValueFactory().setValue(currParams.maxMergeHops); - + dataSelectorCheckBox.setSelected(currParams.useDataSelector); setClassifierPane(); - + enableControls(); - + setSegInfoLabel(); + // //set up the model and the custom pane if necessary. + this.modelSelectPane.loadNewModel(currParams.modelURI); + //this.modelSelectPane.updatePathLabel(); + this.setClassifierPane(); + + //For some reason, in the FX GUI, this causes a root used in multiple scenes exceptions...not sure why. + Platform.runLater(()->{ + sourcePane.getChannelValidator().validate(); + }); + } @@ -496,17 +586,57 @@ public class RawDLSettingsPane extends SettingsPane{ @Override public void paneInitialized() { - // TODO Auto-generated method stub + //don't know why this is required but causes a root used in multiple scenes exceptions...really not sure why. + Platform.runLater(()->{ + sourcePane.getChannelValidator().validate(); + }); } /** * Get the data block currently selected in the pane. * @return the data block currently selected in the pane. */ + @SuppressWarnings("rawtypes") public PamDataBlock getSelectedParentDataBlock() { return sourcePane.getSource(); } + /** + * Get the DLControl associated with the pane. + * @return a reference to the DLControl. + */ + public DLControl getDLControl() { + return dlControl; + } + + /** + * Convenience class to set the segment length in samples from milliseconds + * @param defaultSegmentLen - the segment length in milliseconds. + */ + public void setSegmentLength(Double defaultSegmentLen) { + if (defaultSegmentLen==null) return; + + + double sR = getDLControl().getSettingsPane().getSelectedParentDataBlock().getSampleRate(); + + System.out.println("Set the segment length: " + defaultSegmentLen + " sR " + sR); + + //automatically set the default segment length. + getDLControl().getSettingsPane().getSegmentLenSpinner().getValueFactory().setValue((int) (sR*defaultSegmentLen/1000.)); + } + + /** + * Convenience class to set the hop length in samples from milliseconds + * @param defaultSegmentLen - the segment length in milliseconds. + */ + public void setHopLength(Double hopLength) { + if (hopLength==null) return; + + double sR = getDLControl().getSettingsPane().getSelectedParentDataBlock().getSampleRate(); + //automatically set the default segment length. + getDLControl().getSettingsPane().getHopLenSpinner().getValueFactory().setValue((int) (sR*hopLength/1000.)); + } + } diff --git a/src/rawDeepLearningClassifier/layoutFX/DLSidePanelSwing.java b/src/rawDeepLearningClassifier/layoutFX/DLSidePanelSwing.java index 1a6bc73e..c1a73b25 100644 --- a/src/rawDeepLearningClassifier/layoutFX/DLSidePanelSwing.java +++ b/src/rawDeepLearningClassifier/layoutFX/DLSidePanelSwing.java @@ -35,7 +35,7 @@ public class DLSidePanelSwing implements PamSidePanel { */ public void setupPanel() { //System.out - if (dlControl.getDLModel().getModelUI()!=null && dlControl.getDLModel().getModelUI().getSidePanel()!=null) { + if (dlControl.getDLModel()!=null && dlControl.getDLModel().getModelUI()!=null && dlControl.getDLModel().getModelUI().getSidePanel()!=null) { mainPanel.add(dlControl.getDLModel().getModelUI().getSidePanel(), BorderLayout.CENTER); // mainPanel.add(new JLabel("Hello"), BorderLayout.WEST); mainPanel.validate(); @@ -44,6 +44,7 @@ public class DLSidePanelSwing implements PamSidePanel { //blank mainPanel.removeAll(); } + } @Override diff --git a/src/rawDeepLearningClassifier/layoutFX/FFTSpinnerBug.java b/src/rawDeepLearningClassifier/layoutFX/FFTSpinnerBug.java new file mode 100644 index 00000000..a4466f84 --- /dev/null +++ b/src/rawDeepLearningClassifier/layoutFX/FFTSpinnerBug.java @@ -0,0 +1,69 @@ +package rawDeepLearningClassifier.layoutFX; + +import javafx.application.Application; +import javafx.geometry.Insets; +import javafx.scene.Scene; +import javafx.scene.control.SpinnerValueFactory.ListSpinnerValueFactory; +import javafx.stage.Stage; +import javafx.util.StringConverter; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamSpinner; +import rawDeepLearningClassifier.layoutFX.dlTransfroms.FFTTransformPane; + +public class FFTSpinnerBug extends Application{ + + protected static final String INITAL_VALUE = "0"; + + public static void main(String[] args) { + Application.launch(args); + } + + @Override + public void start(Stage stage) { + //System.out.println("Create step list: " + createStepList().size()); + PamSpinner spinner = new PamSpinner(FFTTransformPane.createStepList()); + spinner.getValueFactory().setConverter(new NumberConverter()); + spinner.setPrefWidth(80); + + PamBorderPane pane = new PamBorderPane(); + + pane.setCenter(spinner); + + pane.setPadding(new Insets(20,20,20,20)); + + Scene scene = new Scene(pane, 350, 300); + + stage.setTitle("Hello Spinner"); + stage.setScene(scene); + stage.show(); + + + System.out.println("Size before: " + ((ListSpinnerValueFactory) spinner.getValueFactory()).getItems().size()); + spinner.getValueFactory().setValue(1019); + System.out.println("Size after: " + ((ListSpinnerValueFactory) spinner.getValueFactory()).getItems().size()); + + for (int i=0; i<((ListSpinnerValueFactory) spinner.getValueFactory()).getItems().size(); i++) { + System.out.println("List value: " + i + ": " + ((ListSpinnerValueFactory) spinner.getValueFactory()).getItems().get(i)); + } + + spinner.increment(0); + + } + + + class NumberConverter extends StringConverter { + @Override + public String toString(Number object) { + // TODO Auto-generated method stub + return object.toString(); + } + + @Override + public Number fromString(String string) { + return Integer.valueOf(string); + } + + } + + +} diff --git a/src/rawDeepLearningClassifier/layoutFX/FFTSpinnerBugLauncher.java b/src/rawDeepLearningClassifier/layoutFX/FFTSpinnerBugLauncher.java new file mode 100644 index 00000000..f1adec21 --- /dev/null +++ b/src/rawDeepLearningClassifier/layoutFX/FFTSpinnerBugLauncher.java @@ -0,0 +1,11 @@ +package rawDeepLearningClassifier.layoutFX; + + +public class FFTSpinnerBugLauncher { + + public static void main(String[] args) { + FFTSpinnerBug.main(args); + } + + +} diff --git a/src/rawDeepLearningClassifier/layoutFX/defaultModels/DefaultModelPane.java b/src/rawDeepLearningClassifier/layoutFX/defaultModels/DefaultModelPane.java new file mode 100644 index 00000000..755bd5db --- /dev/null +++ b/src/rawDeepLearningClassifier/layoutFX/defaultModels/DefaultModelPane.java @@ -0,0 +1,170 @@ +package rawDeepLearningClassifier.layoutFX.defaultModels; + +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import javafx.application.HostServices; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.geometry.Side; +import javafx.scene.Node; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import pamViewFX.PamGuiManagerFX; +import pamViewFX.fxGlyphs.PamGlyphDude; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamButton; +import pamViewFX.fxNodes.PamStackPane; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.hidingPane.HidingPane; +import rawDeepLearningClassifier.defaultModels.DLDefaultModelManager; +import rawDeepLearningClassifier.defaultModels.DLModel; + +/** + * Pane which allows users to view and select default models. + */ +public class DefaultModelPane extends PamBorderPane{ + + /** + * Reference to the deafult model manager that contains the default models. + */ + private DLDefaultModelManager defaultModelManager; + + + private PamBorderPane hidingPaneContent; + + + private HidingPane hidingPane; + + ObjectProperty defaultModel = new SimpleObjectProperty(); + + + + /** + * Constructor for the DefaultModelPane + * @param defaultModelManager - the default model manager to use. + */ + public DefaultModelPane(DLDefaultModelManager defaultModelManager) { + this.defaultModelManager=defaultModelManager; + this.setCenter(createDefaultModelPane() ); + } + + + + private Pane createDefaultModelPane() { + + PamVBox vBox = new PamVBox(); + +// vBox.setSpacing(5); + + Label label = new Label("Default Models"); + PamGuiManagerFX.titleFont2style(label); + + vBox.getChildren().add(label); +// vBox.setPrefWidth(120); + + hidingPaneContent= new PamBorderPane(); + hidingPaneContent.setPrefWidth(150); + hidingPane = new HidingPane(Side.RIGHT, hidingPaneContent, vBox, true, 0); + + PamButton button; + for (int i=0; i{ + hidingPaneContent.setCenter(createModelPane(defaultModelManager.getDefaultModel(ii))); + hidingPane.showHidePane(true); + }); + + if (i>0 && i{ + try { + Desktop.getDesktop().browse(dlModel.getCitationLink()); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } +// HostServices.getInstance(this).getHostServices().showDocument(yahooURL) + }); + + + PamButton importButton = new PamButton("Import"); + importButton.setGraphic(PamGlyphDude.createPamIcon("mdi2d-download", PamGuiManagerFX.iconSize)); + importButton.setOnAction((action)->{ + defaultModel.set(dlModel); + }); + + PamBorderPane buttonHolder = new PamBorderPane(); + buttonHolder.setRight(importButton); + BorderPane.setAlignment(importButton, Pos.BOTTOM_RIGHT); + VBox.setVgrow(buttonHolder, Priority.ALWAYS); + + + vBox.getChildren().addAll(titleLabel, descriptionLabel, link, buttonHolder); + vBox.setPrefHeight(200); + + return vBox; + } + + + public ObjectProperty defaultModelProperty() { + return defaultModel; + } + + + + +} diff --git a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DLTransformImage.java b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DLTransformImage.java index d1051b9f..594aee48 100644 --- a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DLTransformImage.java +++ b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DLTransformImage.java @@ -4,11 +4,16 @@ import java.util.ArrayList; import java.util.Arrays; import org.controlsfx.control.RangeSlider; +import org.controlsfx.control.decoration.Decorator; +import org.controlsfx.control.decoration.GraphicDecoration; +import org.controlsfx.control.decoration.StyleClassDecoration; import org.jamdev.jdl4pam.transforms.DLTransform; import org.jamdev.jdl4pam.transforms.DLTransform.DLTransformType; import org.jamdev.jdl4pam.transforms.FreqTransform; +import org.jamdev.jdl4pam.transforms.SimpleTransform; import org.jamdev.jdl4pam.transforms.WaveTransform; import org.jamdev.jpamutils.wavFiles.AudioData; +import org.jamdev.jpamutils.wavFiles.FilterParams; import PamUtils.PamArrayUtils; import javafx.collections.FXCollections; @@ -16,16 +21,20 @@ import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.geometry.Side; +import javafx.scene.Node; import javafx.scene.control.ChoiceBox; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; +import javafx.scene.paint.Color; import javafx.util.StringConverter; +import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.sliders.ColourRangeSlider; import pamViewFX.fxNodes.utilsFX.ColourArray; import pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType; import pamViewFX.fxPlotPanes.PlotPane; +import pamViewFX.validator.PamValidator; import rawDeepLearningClassifier.layoutFX.exampleSounds.ExampleSound; import rawDeepLearningClassifier.layoutFX.exampleSounds.ExampleSoundFactory; import rawDeepLearningClassifier.layoutFX.exampleSounds.ExampleSoundFactory.ExampleSoundType; @@ -106,7 +115,12 @@ public abstract class DLTransformImage extends PamBorderPane{ /** * The time label. */ - private Label timeLabel; + private Label timeLabel; + + /** + * The error label. + */ + private Label errorLabel; public DLTransformImage() { @@ -133,8 +147,8 @@ public abstract class DLTransformImage extends PamBorderPane{ transformschoiceBox = new ChoiceBox(); transformschoiceBox.valueProperty().addListener((obsval, oldval, newval)->{ updateTransformImage(); - calcTimeBins(transformsR); - plotPane.repaint(); + // calcTimeBins(transformsR); + // plotPane.repaint(); }); transformschoiceBox.setConverter(new DLTransformConverter()); transformschoiceBox.setPrefWidth(170); @@ -194,11 +208,18 @@ public abstract class DLTransformImage extends PamBorderPane{ timeSlider.maxWidthProperty().bind(plotPane.getPlotCanvas().widthProperty().add(5)); + //create an error label + errorLabel = new Label(); + Node decoration = PamGlyphDude.createPamIcon("mdi2c-close-circle-outline", Color.RED, 10); + errorLabel.setGraphic(decoration); +// Decorator.addDecoration(errorLabel, new GraphicDecoration(decoration, Pos.CENTER_RIGHT)); + BorderPane.setAlignment(timeLabel, Pos.CENTER); this.setBottom(timeLabel); } + /** * Get the species choice box. * @return - the species choice box. @@ -212,7 +233,7 @@ public abstract class DLTransformImage extends PamBorderPane{ * Calculate the sample time bins. */ private void calcTimeBins(float sampleRate) { - + timeBins[0] = (int) (sampleRate*(timeSlider.getLowValue()/1000.0)); timeBins[1] = (int) (sampleRate*(timeSlider.getHighValue()/1000.0)); @@ -221,9 +242,8 @@ public abstract class DLTransformImage extends PamBorderPane{ long[] shape = calcShape(); - double nSamples = this.exampleSound.getSampleRate()*((timeSlider.getHighValue()-timeSlider.getLowValue())/1000.0); - + if (shape!=null && shape.length==2) { int timeShape = (int) (shape[0]*(nSamples/(double) exampleSound.getWave().length)); @@ -238,6 +258,7 @@ public abstract class DLTransformImage extends PamBorderPane{ } } + /** * Calculate the output shape. */ @@ -247,24 +268,30 @@ public abstract class DLTransformImage extends PamBorderPane{ if (this.exampleSound==null) return null; ArrayList transforms = getDLTransforms(); - + if (transforms==null) return null; - + long[] shape; if (transforms.get(transforms.size()-1) instanceof FreqTransform) { FreqTransform freqTransform = ((FreqTransform) transforms.get(transforms.size()-1)); - + if (freqTransform.getSpecTransfrom()==null) return null; - + double[][] data2D = freqTransform.getSpecTransfrom().getTransformedData(); - + shape = new long[] {data2D.length, data2D[0].length}; } else { WaveTransform waveTransform = ((WaveTransform) transforms.get(transforms.size()-1)); - - double[] data = waveTransform.getWaveData().getScaledSampleAmpliudes(); - + + // System.out.println("Get wave data: " + waveTransform.getDLTransformType() + " " + waveTransform.getWaveData()); + + if (waveTransform.getWaveData()==null) { + return null; + } + + double[] data = waveTransform.getWaveData().getScaledSampleAmplitudes(); + shape = new long[] {data.length}; } @@ -301,6 +328,7 @@ public abstract class DLTransformImage extends PamBorderPane{ return this.exampleSound.getSampleRate(); } + /** * Update the example sound. * @param exampleSoundType - the example sound type. @@ -310,6 +338,7 @@ public abstract class DLTransformImage extends PamBorderPane{ updateExampleSound(exampleSound); } + /** * Update the example sound. * @param exampleSound - the new example sound. @@ -332,12 +361,19 @@ public abstract class DLTransformImage extends PamBorderPane{ updateTransformImage(); } + /** * Update the transform image to the latest selected transform and data params. */ - public void updateTransformImage() { + public boolean updateTransformImage() { - if (this.exampleSound==null) return; + setErrorMessage(null); + + + if (this.exampleSound==null) { + System.err.println("DLTRanfromImage.updateTransformImage: the example sound is null"); + return false; + } specImage=null; data1D=null; @@ -347,11 +383,20 @@ public abstract class DLTransformImage extends PamBorderPane{ ((WaveTransform) getDLTransforms().get(0)).setWaveData(soundData); DLTransform currentTransform = getDLTransforms().get(0); - for (int i=0; i0) { plotPane.getAxis(Side.LEFT).setLabel("Amplitude"); colRangeSlider.setDisable(true); - data1D = ((WaveTransform) currentTransform).getWaveData().getScaledSampleAmpliudes(); + data1D = ((WaveTransform) currentTransform).getWaveData().getScaledSampleAmplitudes(); data1DminMax= PamArrayUtils.minmax(data1D); plotPane.getAxis(Side.LEFT).setMinVal(data1DminMax[0]); @@ -408,10 +458,125 @@ public abstract class DLTransformImage extends PamBorderPane{ transformsR = ((WaveTransform) currentTransform).getWaveData().getSampleRate(); //System.out.println("Spec wave data: " + ((WaveTransform) currentTransform).getWaveData().getScaledSampleAmpliudes()[10]); } + else { + setErrorMessage("Error in " + currentTransform.getDLTransformType()); + return false; + } + } calcTimeBins(transformsR); plotPane.repaint(); + return true; + } + + + /** + * Set an error message in the plit + * @param string + */ + public void setErrorMessage(String string) { + if (string==null) { + this.setCenter(plotPane); + return; + } + plotPane.getPlotCanvas().getGraphicsContext2D().clearRect(0, 0, plotPane.getPlotCanvas().getWidth(), + plotPane.getPlotCanvas().getHeight()); + + + errorLabel.setText(string); + + + this.setCenter(errorLabel); + + // getPlotCanvas().getGraphicsContext2D().setStroke(Color.RED); + // getPlotCanvas().getGraphicsContext2D().setFill(Color.RED); + // getPlotCanvas().getGraphicsContext2D().strokeText(string, 10, getPlotCanvas().getHeight()/2); + } + + + /** + * Check for error in a transform and return the transform if true; + * @param dlTransform - the new transform + * @param currentTransform - the data t be transformed + * @return a string error message. + */ + private String checkTransform(DLTransform dlTransform, DLTransform currentTransform) { + + String error = null; + SimpleTransform simpleTransform = ((SimpleTransform) dlTransform); + float sampleRate; + switch (dlTransform.getDLTransformType()) { + case DECIMATE: + case DECIMATE_SCIPY: + //decimation sampleRate + sampleRate = simpleTransform.getParams()[0].floatValue(); + double seconds = ((WaveTransform) currentTransform).getWaveData().getLengthInSeconds(); + + if (seconds < 3/sampleRate) { + //only three samples left + error = "The decimation Nyquist is too low for the sample sound. Use a different example species"; + } + + break; + case ENHANCE: + break; + case FILTER: + FilterParams params = WaveTransform.transform2FilterParams(simpleTransform.getParams()); + sampleRate = ((WaveTransform) currentTransform).getWaveData().getSampleRate(); + + if (params.filterType==AudioData.BANDPASS || params.filterType== AudioData.LOWPASS) { + //filter cutoff must be within sample rate. + if (params.highCut>=sampleRate) { + error = "The upper filter frequency cannnot be greater than or equal to Nyquist"; + } + } + if (params.filterType==AudioData.BANDPASS || params.filterType== AudioData.HIGHPASS) { + + if (params.lowCut>=sampleRate) { + error = "The lower filter frequency cannnot be greater than or equal to Nyquist"; + } + } + break; + case FILTER_ISOLATED_SPOTS: + break; + case GAUSSIAN_FILTER: + break; + case MEDIANFILTER: + break; + case NORMALISE_WAV: + break; + case PREEMPHSIS: + break; + case REDUCETONALNOISE_MEAN: + break; + case REDUCETONALNOISE_MEDIAN: + break; + case SPEC2DB: + break; + case SPECCLAMP: + break; + case SPECCROPINTERP: + break; + case SPECNORMALISE: + break; + case SPECNORMALISEROWSUM: + break; + case SPECNORMALISESTD: + break; + case SPECNORMALISE_MINIMAX: + break; + case SPECTROGRAM: + break; + case SPECTROGRAMKETOS: + break; + case TRIM: + break; + default: + break; + + } + return error; } @@ -452,6 +617,8 @@ public abstract class DLTransformImage extends PamBorderPane{ y1= ((dataTrim[i]- data1DminMax[0])/(dataRange))*getPlotCanvas().getHeight(); y2= ((dataTrim[i+1]- data1DminMax[0])/(dataRange))*getPlotCanvas().getHeight(); + getPlotCanvas().getGraphicsContext2D().setStroke(Color.BLACK); + getPlotCanvas().getGraphicsContext2D().setFill(Color.BLACK); getPlotCanvas().getGraphicsContext2D().strokeLine(x1, y1, x2, y2); } } diff --git a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DLTransformsPane.java b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DLTransformsPane.java index 231aaff3..9ea8a36f 100644 --- a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DLTransformsPane.java +++ b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DLTransformsPane.java @@ -29,7 +29,7 @@ import pamViewFX.fxNodes.orderedList.PamDraggableList; * */ public class DLTransformsPane extends PamBorderPane { - + /** * The order of transforms has changed. */ @@ -73,7 +73,7 @@ public class DLTransformsPane extends PamBorderPane { Label label = new Label("Add Transform"); MenuButton splitButton = new MenuButton(); -// splitButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialDesignIcon.PLUS)); + // splitButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialDesignIcon.PLUS)); splitButton.setGraphic(PamGlyphDude.createPamIcon("mdi2p-plus")); DLTransformType[] dlTransformTypes = DLTransformType.values(); @@ -113,10 +113,10 @@ public class DLTransformsPane extends PamBorderPane { transformPane.addSettingsListener(()->{ newSettings(TRANSFORM_SETTINGS_CHANGE); }); - + draggablelistPane.addDraggablePane(transformPane); } - + /* * Called whenever a transform is remvoed from the pane. */ @@ -138,7 +138,7 @@ public class DLTransformsPane extends PamBorderPane { * @param dlTransforms - the list of transforms (in order) to set */ public void setTransforms(ArrayList dlTransforms) { - + //System.out.println("Set DL transfroms: " + dlTransforms); if (dlTransforms == null) { @@ -146,15 +146,16 @@ public class DLTransformsPane extends PamBorderPane { mainPane.getChildren().add(controlPane); return; } - + ArrayList dlTransformPanes = new ArrayList(); sampleRate=-1; - -// for (int i=0; i{ newSettings(TRANSFORM_SETTINGS_CHANGE); }); + } draggablelistPane = new DLDraggableList(dlTransformPanes); @@ -212,7 +221,7 @@ public class DLTransformsPane extends PamBorderPane { return null; } - + /** * Get the transform panes. * @return the transofrm panes. @@ -225,11 +234,11 @@ public class DLTransformsPane extends PamBorderPane { return null; } } - + class DLDraggableList extends PamDraggableList { - + public DLDraggableList() { super(); } @@ -239,12 +248,12 @@ public class DLTransformsPane extends PamBorderPane { super(panes); // TODO Auto-generated constructor stub } - + @Override public void paneOrderChanged(boolean success) { newSettings(TRANSFORM_ORDER_CHANGE); } - + @Override public void removePane(BorderPane pane) { super.removePane(pane); @@ -255,14 +264,14 @@ public class DLTransformsPane extends PamBorderPane { @Override public boolean canDrop(DLTransformPane source, int sourceIndex, int targetIndex) { // if a wave transform has to be before any spectral transforms. - + //generate the new possible list of nodes. List newPossibleList = getTempSortedList(sourceIndex, targetIndex); - + if (source.getDLTransform() instanceof WaveTransform) { //System.out.println("Target index: " + targetIndex); //uuuurgh this is complicated because it depends on whether the transform itself is in the list or not... - + int i=0; while (i(createStepList())); break; + case SPECTROGRAMKETOS: + + settingsPane = new FFTTransformPane((SimpleTransform) dlTransfrom, new String[]{"FFT Length ", "FFT Hop", "Window_Length"}, new String[]{"", "", "s"}); + + ((FFTTransformPane) settingsPane).setSpinnerMinMaxValues(1, 4, Integer.MAX_VALUE, 4); + + break; + case TRIM: settingsPane = new SimpleTransformPane((SimpleTransform) dlTransfrom, new String[]{"Start", "End"}, new String[]{"samples ", "samples"}); ((SimpleTransformPane) settingsPane).setSpinnerMinMaxValues(0, 0, Integer.MAX_VALUE, 500); @@ -139,6 +158,9 @@ public class DataTransformPaneFactory { break; } + +// System.out.println("Get transform pane for 2: " + settingsPane); + return settingsPane; } diff --git a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/FFTTransformPane.java b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/FFTTransformPane.java index 3fb80d6e..52fb85db 100644 --- a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/FFTTransformPane.java +++ b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/FFTTransformPane.java @@ -6,10 +6,9 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.util.StringConverter; import pamViewFX.fxNodes.PamSpinner; -import javafx.util.StringConverter; /** - * Transform pane for fourier transform + * Transform pane for Fourier transform * @author Jamie Macaulay * */ @@ -29,7 +28,6 @@ public class FFTTransformPane extends SimpleTransformPane { return spinner; } else return super.createSpinner(i); - } @@ -41,13 +39,13 @@ public class FFTTransformPane extends SimpleTransformPane { ObservableList stepSizeListLength=FXCollections.observableArrayList(); for (int i=2; i<15; i++){ stepSizeListLength.add(Integer.valueOf((int) Math.pow(2,i))); - } return stepSizeListLength; } + + class NumberConverter extends StringConverter { - @Override public String toString(Number object) { // TODO Auto-generated method stub @@ -61,4 +59,7 @@ public class FFTTransformPane extends SimpleTransformPane { } + + + } diff --git a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/FilterTransformPane.java b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/FilterTransformPane.java new file mode 100644 index 00000000..2f070e16 --- /dev/null +++ b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/FilterTransformPane.java @@ -0,0 +1,199 @@ +package rawDeepLearningClassifier.layoutFX.dlTransfroms; + +import org.jamdev.jdl4pam.transforms.DLTransform; +import org.jamdev.jdl4pam.transforms.SimpleTransform; +import org.jamdev.jdl4pam.transforms.WaveTransform; +import org.jamdev.jpamutils.wavFiles.AudioData; +import org.jamdev.jpamutils.wavFiles.FilterParams; + +import Filters.FilterBand; +import fftFilter.FFTFilterParams; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.Spinner; +import javafx.scene.control.TitledPane; +import javafx.scene.layout.Pane; +import pamViewFX.fxNodes.PamHBox; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.utilityPanes.SimpleFilterPaneFX; + +/** + * Pane for a waveform filter. + */ +public class FilterTransformPane extends DLTransformPane { + + /** + * The transform associated with the settings pane. + */ + private DLTransform simpleTransfrom; + + /** + * Controls for changing filter settings. + */ + private SimpleFilterPaneFX filterPane; + + /** + * Choice box for changing the filter type - e.g. Butterworth + */ + private ComboBox filterMethodBox; + + /** + * Spinner for changing the filter order. + */ + private Spinner orderSpinner; + + + public FilterTransformPane(DLTransform dlTransfrom) { + super(); + this.simpleTransfrom= dlTransfrom; + this.setCenter(createFilterPane()); +// this.setStyle("-fx-background-color:orangered;"); + + } + + private Node createFilterPane() { + + PamVBox mainPane = new PamVBox(); + mainPane.setSpacing(5); + + filterMethodBox = new ComboBox(); + filterMethodBox.getItems().add(AudioData.BUTTERWORTH, "Butterworth"); + filterMethodBox.getItems().add(AudioData.CHEBYSHEV, "Chebyshev"); + + filterMethodBox.valueProperty().addListener((obsVal, oldVal, newVal)->{ + this.notifySettingsListeners(); + }); + + //spinner for changing filter order. + orderSpinner = new Spinner(1,50,4,1); + orderSpinner.valueProperty().addListener((obsVal, oldVal, newVal)->{ + this.notifySettingsListeners(); + }); + orderSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + + + PamHBox filterTypeHolder = new PamHBox(); + filterTypeHolder.setSpacing(5); + filterTypeHolder.setAlignment(Pos.CENTER_LEFT); + filterTypeHolder.getChildren().addAll(filterMethodBox, new Label("Order"), orderSpinner); + + //create the filter pane - use the standard pG one - a bit of hack but no point in basically replicating + //this whole pane again. + filterPane = new SimpleFilterPaneFX(); + + filterPane.addSettingsListener(()->{ + this.notifySettingsListeners(); + }); + + mainPane.getChildren().addAll(new Label("Filter Type"), filterTypeHolder, filterPane.getContentNode()); + + TitledPane titledPane = new TitledPane(simpleTransfrom.getDLTransformType().toString(), mainPane); + + // PamBorderPane borderPane = new PamBorderPane(); + // borderPane.setTop(new Label(simpleTransfrom.getDLTransformType().toString())); + // borderPane.setCenter(hBox); + + titledPane.setExpanded(false); + + return titledPane; + } + + /** + * Convert parameters array to filter parameters. + * @param params - the parameters to set. + * @return the FFTfilterParams object with params from fileParams + */ + private FFTFilterParams transform2FilterParams(FilterParams filtParams) { + + FFTFilterParams filtParamsPG = new FFTFilterParams(); + + switch (filtParams.filterType) { + case AudioData.LOWPASS: + filtParamsPG.filterBand = FilterBand.LOWPASS; + break; + case AudioData.HIGHPASS: + filtParamsPG.filterBand = FilterBand.HIGHPASS; + break; + case AudioData.BANDPASS: + filtParamsPG.filterBand = FilterBand.BANDPASS; + break; + } + + filtParamsPG.lowPassFreq = filtParams.highCut; + filtParamsPG.highPassFreq = filtParams.lowCut; + + + return filtParamsPG; + + } + + private FilterParams getFFTFilterSettings(FilterParams params) { + + FFTFilterParams paramsFFT = filterPane.getParams(new FFTFilterParams()); + + switch (paramsFFT.filterBand) { + case BANDPASS: + params.filterType = AudioData.BANDPASS; + break; + case BANDSTOP: + break; + case HIGHPASS: + params.filterType = AudioData.HIGHPASS; + break; + case LOWPASS: + params.filterType = AudioData.LOWPASS; + break; + } + + params.highCut = paramsFFT.lowPassFreq; + params.lowCut = paramsFFT.highPassFreq; + + return params; + } + + @Override + public DLTransform getDLTransform() { + return this.getParams(simpleTransfrom) ; + } + + @Override + public DLTransform getParams(DLTransform dlTransform) { + +// System.out.println("GET PARAMS: FILTER"); + + SimpleTransform simpleTransform = (SimpleTransform) dlTransform; + + //create filter params object. + FilterParams filtParams = new FilterParams(); + filtParams = getFFTFilterSettings(filtParams); + + filtParams.filterMethod = filterMethodBox.getSelectionModel().getSelectedIndex(); + filtParams.order = orderSpinner.getValue(); + + simpleTransform.setParams(WaveTransform.filterParams2transform(filtParams)); + + return simpleTransform; + } + + @Override + public void setParams(DLTransform dlTransform) { + +// System.out.println("SET PARAMS: FILTER"); + + SimpleTransform simpleTransform = (SimpleTransform) dlTransform; + + //get filter params as an object to try and make less mistakes! + FilterParams filtParams = WaveTransform.transform2FilterParams(simpleTransform.getParams()); + + //get the selection model. + filterMethodBox.getSelectionModel().select(filtParams.filterMethod); + orderSpinner.getValueFactory().setValue(filtParams.order); + + //bit of a hack but otherwise have to rewrite filter GUI. + filterPane.setParams(transform2FilterParams(filtParams)); + + } + +} diff --git a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/SimpleTransformPane.java b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/SimpleTransformPane.java index 21bcf508..20f99f10 100644 --- a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/SimpleTransformPane.java +++ b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/SimpleTransformPane.java @@ -191,6 +191,7 @@ public class SimpleTransformPane extends DLTransformPane { //Set the new numbers Number[] params = new Number[spinners.size()]; for (int i=0; i>{ @Override public void completeTask() { //dlControl.setNotifyProcesses(false); - this.dlControl.getDLModel().closeModel(); + //this.dlControl.getDLModel().closeModel(); dlControl.update(MTClassifierControl.PROCESSING_END); } diff --git a/src/rawDeepLearningClassifier/resources/advanced_settings_animalspot_1.png b/src/rawDeepLearningClassifier/resources/advanced_settings_animalspot_1.png index 8aa822e3..a406874c 100644 Binary files a/src/rawDeepLearningClassifier/resources/advanced_settings_animalspot_1.png and b/src/rawDeepLearningClassifier/resources/advanced_settings_animalspot_1.png differ diff --git a/src/rawDeepLearningClassifier/resources/advanced_settings_generic_1.png b/src/rawDeepLearningClassifier/resources/advanced_settings_generic_1.png index c70cbffb..96145e72 100644 Binary files a/src/rawDeepLearningClassifier/resources/advanced_settings_generic_1.png and b/src/rawDeepLearningClassifier/resources/advanced_settings_generic_1.png differ diff --git a/src/rawDeepLearningClassifier/resources/advanced_settings_generic_2.png b/src/rawDeepLearningClassifier/resources/advanced_settings_generic_2.png index 70abc303..c70a2d4d 100644 Binary files a/src/rawDeepLearningClassifier/resources/advanced_settings_generic_2.png and b/src/rawDeepLearningClassifier/resources/advanced_settings_generic_2.png differ diff --git a/src/rawDeepLearningClassifier/resources/deep_leanring_module_help.png b/src/rawDeepLearningClassifier/resources/deep_leanring_module_help.png index 250486bc..79fff4d3 100644 Binary files a/src/rawDeepLearningClassifier/resources/deep_leanring_module_help.png and b/src/rawDeepLearningClassifier/resources/deep_leanring_module_help.png differ diff --git a/src/rawDeepLearningClassifier/resources/default_settings_humpback_1.png b/src/rawDeepLearningClassifier/resources/default_settings_humpback_1.png new file mode 100644 index 00000000..eaf71e68 Binary files /dev/null and b/src/rawDeepLearningClassifier/resources/default_settings_humpback_1.png differ diff --git a/src/rawDeepLearningClassifier/segmenter/GroupedRawData.java b/src/rawDeepLearningClassifier/segmenter/GroupedRawData.java new file mode 100644 index 00000000..e969e552 --- /dev/null +++ b/src/rawDeepLearningClassifier/segmenter/GroupedRawData.java @@ -0,0 +1,145 @@ +package rawDeepLearningClassifier.segmenter; + +import java.util.Arrays; + +import PamDetection.PamDetection; +import PamUtils.PamUtils; +import PamguardMVC.PamDataUnit; + +/** + * + * Temporary holder for raw data with a pre defined size. This holds one channel group of raw + * sound data. + * + * @author Jamie Macaulay + * + */ +public class GroupedRawData extends PamDataUnit implements PamDetection, Cloneable { + + + /* + * Raw data holder + */ + protected double[][] rawData; + + + /** + * Current position in the rawData; + */ + protected int[] rawDataPointer; + + /** + * The data unit associated with this raw data chunk. + */ + private PamDataUnit rawDataUnit; + + + /** + * Create a grouped raw data unit. This contains a segment of sound data. + * @param timeMilliseconds - the time in milliseconds. + * @param channelBitmap - the channel bitmap of the raw data. + * @param startSample - the start sample of the raw data. + * @param duration - the duration of the raw data in samples. + * @param samplesize - the total sample size of the raw data unit chunk in samples. + */ + public GroupedRawData(long timeMilliseconds, int channelBitmap, long startSample, long duration, int samplesize) { + super(timeMilliseconds, channelBitmap, startSample, duration); + rawData = new double[PamUtils.getNumChannels(channelBitmap)][]; + rawDataPointer = new int[PamUtils.getNumChannels(channelBitmap)]; + // rawDataStartMillis = new long[PamUtils.getNumChannels(channelBitmap)]; + + for (int i =0; i=rawData[groupChan].length) { + arrayCopyLen=copyLen-(lastPos-rawData[groupChan].length)-1; + dataOverflow = copyLen - arrayCopyLen; + } + else { + arrayCopyLen= copyLen; + } + + arrayCopyLen = Math.max(arrayCopyLen, 0); + + //update the current grouped raw data unit with new raw data. + System.arraycopy(src, srcPos, rawData[groupChan], rawDataPointer[groupChan], arrayCopyLen); + + rawDataPointer[groupChan]=rawDataPointer[groupChan] + arrayCopyLen; + + return dataOverflow; + } + + /** + * Get the raw data grouped by channel. + * @return the raw acoustic data. + */ + public double[][] getRawData() { + return rawData; + } + + /** + * Get the current pointer for rawData. + * @return the data pointer per channel. + */ + public int[] getRawDataPointer() { + return rawDataPointer; + } + + + @Override + protected GroupedRawData clone() { + try { + GroupedRawData groupedRawData = (GroupedRawData) super.clone(); + + //hard clone the acoustic data + groupedRawData.rawData = new double[this.rawData.length][]; + for (int i=0; i { + + /** + * Constructor for a group of detections within a detection. Note that some + * longer detections (e.g. whistles) may have sections outside the segment. + * + * @param timeMilliseconds - this is the start of the SEGMENT - Note that the + * @param channelBitmap - channels of all detections + * @param startSample - the stratSample of the SEGMENT. + * @param duration - the duration of the SEGMENT. + */ + public SegmenterDetectionGroup(long timeMilliseconds, int channelBitmap, long startSample, long duration) { + super(timeMilliseconds, channelBitmap, startSample, duration); + // TODO Auto-generated constructor stub + } + +} diff --git a/src/rawDeepLearningClassifier/segmenter/SegmenterGroupDataBlock.java b/src/rawDeepLearningClassifier/segmenter/SegmenterGroupDataBlock.java new file mode 100644 index 00000000..cbd18e3a --- /dev/null +++ b/src/rawDeepLearningClassifier/segmenter/SegmenterGroupDataBlock.java @@ -0,0 +1,18 @@ +package rawDeepLearningClassifier.segmenter; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamProcess; + +/** + * + * @author Jamie Macaulay + * + */ +public class SegmenterGroupDataBlock extends PamDataBlock { + + public SegmenterGroupDataBlock(String dataName, PamProcess parentProcess, int channelMap) { + super(SegmenterDetectionGroup.class, dataName, parentProcess, channelMap); + // TODO Auto-generated constructor stub + } + +} diff --git a/src/rawDeepLearningClassifier/segmenter/SegmenterProcess.java b/src/rawDeepLearningClassifier/segmenter/SegmenterProcess.java index bc0bb24c..134204ab 100644 --- a/src/rawDeepLearningClassifier/segmenter/SegmenterProcess.java +++ b/src/rawDeepLearningClassifier/segmenter/SegmenterProcess.java @@ -5,9 +5,7 @@ import java.util.ArrayList; import java.util.Arrays; import PamController.PamController; -import PamDetection.PamDetection; import PamDetection.RawDataUnit; -import PamUtils.PamArrayUtils; import PamUtils.PamUtils; import PamView.GroupedSourceParameters; import PamView.PamDetectionOverlayGraphics; @@ -17,11 +15,10 @@ import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.PamObservable; import PamguardMVC.PamProcess; -import PamguardMVC.debug.Debug; import clickDetector.ClickDetection; import clipgenerator.ClipDataUnit; import rawDeepLearningClassifier.DLControl; -import PamUtils.PamCalendar; +import whistlesAndMoans.ConnectedRegionDataUnit; /** @@ -58,7 +55,12 @@ public class SegmenterProcess extends PamProcess { private SegmenterDataBlock segmenterDataBlock; PamSymbol defaultSymbol = new PamSymbol(PamSymbolType.SYMBOL_DIAMOND, 10, 12, false, - Color.CYAN, Color.CYAN); + Color.CYAN, Color.CYAN); + + /** + * Holds groups of data units which are within a defined segment. + */ + private SegmenterGroupDataBlock segmenterGroupDataBlock; public SegmenterProcess(DLControl pamControlledUnit, PamDataBlock parentDataBlock) { @@ -76,7 +78,12 @@ public class SegmenterProcess extends PamProcess { segmenterDataBlock = new SegmenterDataBlock("Segmented Raw Data", this, dlControl.getDLParams().groupedSourceParams.getChanOrSeqBitmap()); + + segmenterGroupDataBlock = new SegmenterGroupDataBlock("Segmented data units", this, + dlControl.getDLParams().groupedSourceParams.getChanOrSeqBitmap()); + addOutputDataBlock(segmenterDataBlock); + addOutputDataBlock(segmenterGroupDataBlock); setProcessName("Segmenter"); @@ -208,11 +215,29 @@ public class SegmenterProcess extends PamProcess { else if (pamRawData instanceof ClipDataUnit) { newClipData(pamRawData); } + else if (pamRawData instanceof ConnectedRegionDataUnit) { + newWhistleData(pamRawData); + } } } + /** + * A new whistle data unit. + * @param dataUnit - the whistle data unit. + */ + private void newWhistleData(PamDataUnit dataUnit) { + ConnectedRegionDataUnit whistle = (ConnectedRegionDataUnit) dataUnit; + + //TODO + //this contains no raw data so we are branching off on a completely different processing path here. + //Whislte data units are saved to a buffer and then fed to the deep learning algorohtm + + + } + + /** * A new raw data unit. * @param obs - the PAM observable @@ -344,7 +369,7 @@ public class SegmenterProcess extends PamProcess { * * @param unit - the data unit which contains relevant metadata on time * etc. - * @param rawDataChunk - the sound chunk to segment extracted form the data + * @param rawDataChunk - the sound chunk to segment extracted from the data * unit. * @param iChan - the channel that is being segmented * @param forceSave - make sure that all data is passed into the buffers and @@ -377,6 +402,7 @@ public class SegmenterProcess extends PamProcess { //create a new data unit - should only be called once after initial start. currentRawChunks[i] = new GroupedRawData(timeMilliseconds, getSourceParams().getGroupChannels(i), startSampleTime, dlControl.getDLParams().rawSampleSize, dlControl.getDLParams().rawSampleSize); + currentRawChunks[i].setParentDataUnit(unit);; } @@ -645,145 +671,6 @@ public class SegmenterProcess extends PamProcess { } - /** - * - * Temporary holder for raw data with a pre defined size. This holds one channel group of raw - * sound data. - * - * @author Jamie Macaulay - * - */ - public class GroupedRawData extends PamDataUnit implements PamDetection, Cloneable { - - - /* - * Raw data holder - */ - protected double[][] rawData; - - - /** - * Current position in the rawData; - */ - protected int[] rawDataPointer; - - /** - * The data unit associated with this raw data chunk. - */ - private PamDataUnit rawDataUnit; - - - /** - * Create a grouped raw data unit. This contains a segment of sound data. - * @param timeMilliseconds - the time in milliseconds. - * @param channelBitmap - the channel bitmap of the raw data. - * @param startSample - the start sample of the raw data. - * @param duration - the duration of the raw data in samples. - * @param samplesize - the total sample size of the raw data unit chunk in samples. - */ - public GroupedRawData(long timeMilliseconds, int channelBitmap, long startSample, long duration, int samplesize) { - super(timeMilliseconds, channelBitmap, startSample, duration); - rawData = new double[PamUtils.getNumChannels(channelBitmap)][]; - rawDataPointer = new int[PamUtils.getNumChannels(channelBitmap)]; - // rawDataStartMillis = new long[PamUtils.getNumChannels(channelBitmap)]; - - for (int i =0; i=rawData[groupChan].length) { - arrayCopyLen=copyLen-(lastPos-rawData[groupChan].length)-1; - dataOverflow = copyLen - arrayCopyLen; - } - else { - arrayCopyLen= copyLen; - } - - arrayCopyLen = Math.max(arrayCopyLen, 0); - - //update the current grouped raw data unit with new raw data. - System.arraycopy(src, srcPos, rawData[groupChan], rawDataPointer[groupChan], arrayCopyLen); - - rawDataPointer[groupChan]=rawDataPointer[groupChan] + arrayCopyLen; - - return dataOverflow; - } - - /** - * Get the raw data grouped by channel. - * @return the raw acoustic data. - */ - public double[][] getRawData() { - return rawData; - } - - /** - * Get the current pointer for rawData. - * @return the data pointer per channel. - */ - public int[] getRawDataPointer() { - return rawDataPointer; - } - - - @Override - protected GroupedRawData clone() { - try { - GroupedRawData groupedRawData = (GroupedRawData) super.clone(); - - //hard clone the acoustic data - groupedRawData.rawData = new double[this.rawData.length][]; - for (int i=0; i implements PamTabPanel public JTextField comment; private PamPanel southPanel; - public ImageIcon settings = new ImageIcon(ClassLoader - .getSystemResource("Resources/SettingsButtonSmall2.png")); +// public ImageIcon settings = new ImageIcon(ClassLoader +// .getSystemResource("Resources/SettingsButtonSmall2.png")); + public static FontIcon settings = FontIcon.of(MaterialDesignC.COG, 20, Color.DARK_GRAY); public TargetMotionMainPanel(TargetMotionLocaliser targetMotionLocaliser) { diff --git a/src/targetMotionOld/algorithms/LeastSquaresNew.java b/src/targetMotionOld/algorithms/LeastSquaresNew.java index 57a7aa08..8cf4a869 100644 --- a/src/targetMotionOld/algorithms/LeastSquaresNew.java +++ b/src/targetMotionOld/algorithms/LeastSquaresNew.java @@ -78,8 +78,8 @@ public class LeastSquaresNew extends AbstractTargetMot } @Override - public LocaliserPane getSettingsPane() { - return detectionGroupLocaliser.getSettingsPane(); + public LocaliserPane getAlgorithmSettingsPane() { + return detectionGroupLocaliser.getAlgorithmSettingsPane(); } @Override diff --git a/src/targetMotionOld/algorithms/Simplex2DNew.java b/src/targetMotionOld/algorithms/Simplex2DNew.java index 95ec65d8..8b7e5298 100644 --- a/src/targetMotionOld/algorithms/Simplex2DNew.java +++ b/src/targetMotionOld/algorithms/Simplex2DNew.java @@ -66,8 +66,8 @@ public class Simplex2DNew extends AbstractTargetMotion } @Override - public LocaliserPane getSettingsPane() { - return detectionGroupLocaliser.getSettingsPane(); + public LocaliserPane getAlgorithmSettingsPane() { + return detectionGroupLocaliser.getAlgorithmSettingsPane(); } @Override diff --git a/src/test/matchedTemplateClassifier/MatchedTemplateClassifierTest.java b/src/test/matchedTemplateClassifier/MatchedTemplateClassifierTest.java new file mode 100644 index 00000000..cb4edc52 --- /dev/null +++ b/src/test/matchedTemplateClassifier/MatchedTemplateClassifierTest.java @@ -0,0 +1,179 @@ +package test.matchedTemplateClassifier; + + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import PamUtils.PamArrayUtils; +import PamUtils.complex.ComplexArray; +import Spectrogram.WindowFunction; +import clickDetector.ClickLength; +import fftManager.FastFFT; +import matchedTemplateClassifer.MTClassifier; +import matchedTemplateClassifer.MTClassifierTest; +import matchedTemplateClassifer.MTProcess; +import matchedTemplateClassifer.MatchTemplate; +import matchedTemplateClassifer.MatchedTemplateParams; +import matchedTemplateClassifer.MatchedTemplateResult; + +/** + * Tests for the matched click classifier. + */ +public class MatchedTemplateClassifierTest { + + + /** + * Test the match correlation algorithm by cross correlating a waveform with itself. Results + * are tested against the matlab xcorr funtion + */ + @Test + public void testMatchCorr() { + + System.out.println("Matched template classifier test: match corr"); + + + /* + * + * Test against MATLAB Xcorr function + * + * load('/Users/au671271/MATLAB-Drive/MATLAB/PAMGUARD/matchedclickclassifer/ + * _test/DS2templates_test.mat') + * + * wave = templates(1).wave; %test correlating waves with themselves using xcorr + * + * r = xcorr( wave , wave); disp(['Max correlation no-norm: ' num2str(max(r))]) + * + * r = xcorr( normalize(wave) , normalize(wave)); disp(['Max correlation rms: ' + * num2str(max(r))]) + * + * r = xcorr( wave/max(wave) , wave/max(wave)); disp(['Max correlation peak: ' + * num2str(max(r))]) + */ + + //Note that the return value of two identical waveforms depends on normalisation. + + String templteFilePath= "./src/test/resources/matchedTemplateClassifier/DS2templates_test.mat"; + //float sR = 288000; //sample rate in samples per second. + Path path = Paths.get(templteFilePath); + String templteFilePathR = path.toAbsolutePath().normalize().toString(); + + ArrayList templates = MTClassifierTest.importTemplates(templteFilePathR); + + List matchedClickResult ; + /** + * Correlate a template waveform with itself and check the result is 1. + */ + matchedClickResult = MTClassifierTest.testCorrelation(templates.get(0).waveform, templates.get(0).sR, templates, MatchedTemplateParams.NORMALIZATION_RMS); + assertEquals(matchedClickResult.get(0).matchCorr, 1.0, 0.01); + + + matchedClickResult = MTClassifierTest.testCorrelation(templates.get(0).waveform, templates.get(0).sR, templates, MatchedTemplateParams.NORMALIZATION_PEAK); + assertEquals(matchedClickResult.get(0).matchCorr, 134.5961, 0.01); + + + matchedClickResult = MTClassifierTest.testCorrelation(templates.get(0).waveform, templates.get(0).sR, templates, MatchedTemplateParams.NORMALIZATION_NONE); + assertEquals(matchedClickResult.get(0).matchCorr, 7.8457, 0.01); + + } + + /** + * Test the match correlation algorithm combined with click length algorithm. Here we awant to test that + * a long waveform + */ + @Test + public void testMatchCorrLen() { + + System.out.println("Matched template classifier test: match corr len"); + + String testClicksPath = "./src/test/resources/matchedTemplateClassifier/DS3clks_test.mat"; + Path path = Paths.get(testClicksPath); + testClicksPath=path.toAbsolutePath().normalize().toString(); + + String templteFilePath= "./src/test/resources/matchedTemplateClassifier/DS3templates_test.mat"; + path = Paths.get(templteFilePath); + templteFilePath=path.toAbsolutePath().normalize().toString(); + + //import some example clicks + float sR = 288000; //sample rate in samples per second. + ArrayList clicks = MTClassifierTest.importClicks(testClicksPath, sR); + + //import some templates + ArrayList templates = MTClassifierTest.importTemplates(templteFilePath); + + int index = 24; //the index of the test clck to use. + //values in MATLAB are9.73577287114938 8.82782814105430 3.51936216182390 +// System.out.println("Number of clicks: " + clicks.size() + " UID " + clicks.get(index).name); + + System.out.println("------Standard Length--------"); + List matchedClickResultLen1 = MTClassifierTest.testCorrelation(clicks.get(index).waveform, sR, templates); + + System.out.println("------Restricted Length--------"); + + int restrictedBins= 2048; + + ClickLength clickLength = new ClickLength(); + int[][] lengthPoints = clickLength.createLengthData(clicks.get(index), sR, 5.5, 3, false, null); + + double[] waveformLen = MTProcess.createRestrictedLenghtWave(clicks.get(index).waveform, lengthPoints[0], + restrictedBins, WindowFunction.hann(restrictedBins)); + + List matchedClickResultLen2 = MTClassifierTest.testCorrelation(waveformLen, sR, templates); + + assertEquals(matchedClickResultLen1.get(0).matchCorr, matchedClickResultLen2.get(0).matchCorr, 0.05); + + + } + + + /** + * Test the FFT method of correlating two identical waveforms. + */ + @Test + public void testFFTCorrelation() { + + System.out.println("Matched template classifier test: xcorr"); + + String templteFilePath= "./src/test/resources/matchedTemplateClassifier/DS2templates_test.mat"; + //float sR = 288000; //sample rate in samples per second. + Path path = Paths.get(templteFilePath); + String templteFilePathR = path.toAbsolutePath().normalize().toString(); + + ArrayList templates = MTClassifierTest.importTemplates(templteFilePathR); + + double[] waveform = templates.get(0).getWaveData()[0]; + waveform = MTClassifier.normaliseWaveform(waveform, MatchedTemplateParams.NORMALIZATION_RMS); + + FastFFT fft = new FastFFT(); + ComplexArray testClick = fft.rfft(waveform, waveform.length); + ComplexArray matchTemplate = fft.rfft(waveform, waveform.length).conj(); + + int fftLength = testClick.length()*2; + ComplexArray matchResult= new ComplexArray(fftLength); + + for (int i=0; i groupedData = new ArrayList(); + + + groupedData.add(groupedRawData); + + ArrayList gwenericPrediciton = genericModelWorker.runModel(groupedData, soundData.sampleRate, 0); + + float[] output = gwenericPrediciton.get(0).getPrediction(); + + System.out.println("Right whale network output: " + output[0] + " " + output[1]); + + //test the predicitons are true. + assertTrue( output[0]<0.01 ); //noise + assertTrue( output[1]>0.98 ); //right whale + + genericModelWorker.closeModel(); + + } catch (IOException | UnsupportedAudioFileException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + assertEquals(false, true); + } + } + + + + /** + * Test Google's humpback whale model. + */ + @Test + public void humpbackWhaleTest() { + + System.out.println("*****Generic DL: Humpback whale test*****"); + + //relative paths to the resource folders. + String relModelPath = "./src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/saved_model.pb"; + String relWavPath = "./src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/SAMOSAS_EL1_humpback.wav"; + String resultsPath = "./src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/SAMOSAS_EL1_humpback_annotations.txt"; + + Path path = Paths.get(relModelPath); + + GenericModelWorker genericModelWorker = new GenericModelWorker(); + + GenericModelParams genericModelParams = new GenericModelParams(); + + genericModelParams.modelPath = path.toAbsolutePath().normalize().toString(); + + path = Paths.get(relWavPath); + String wavFilePath = path.toAbsolutePath().normalize().toString(); + + HumpbackWhaleAtlantic humpbackModel = new HumpbackWhaleAtlantic(); + humpbackModel.setParams(genericModelParams); + + System.out.println(genericModelParams); + + //prep the model + genericModelWorker.prepModel(genericModelParams, null); + + try { + //load audio + AudioData soundData = DLUtils.loadWavFile(wavFilePath); + + + //load true predictions file. + File file = new File(resultsPath); + FileReader fr = new FileReader(file); + BufferedReader br = new BufferedReader(fr); + String line; + int ind=0; + + while((line = br.readLine()) != null){ + if (ind>0) { + //read the data from the text file + String[] data = line.split("\t"); + int chunkID = Integer.valueOf(data[0]); + + double startTimeS = Double.valueOf(data[1]); + double endTimeS = Double.valueOf(data[2]); + double prediction = Double.valueOf(data[5]); + + int startChunk = (int) (soundData.sampleRate*startTimeS); //humpback whale call + int chunkSize = (int) Math.ceil((endTimeS-startTimeS)*soundData.sampleRate); + + chunkSize=7755; + + GroupedRawData groupedRawData = new GroupedRawData(0, 1, 0, chunkSize, chunkSize); + groupedRawData.copyRawData(soundData.getScaledSampleAmplitudes(), startChunk, chunkSize, 0); + + + ArrayList groupedData = new ArrayList(); + + groupedData.add(groupedRawData); + + ArrayList genericPrediction = genericModelWorker.runModel(groupedData, soundData.sampleRate, 0); + + float[] output = genericPrediction.get(0).getPrediction(); + + System.out.println(String.format("Chunk %d %d Predicted output: %.2f true output: %.2f passed: %b", chunkID, startChunk, + output[0], prediction, output[0]>prediction*0.9 && output[0] groupedData = new ArrayList(); + groupedData.add(groupedRawData); + + ArrayList genericPrediciton = ketosWorker2.runModel(groupedData, soundData.sampleRate, 0); + float[] output = genericPrediciton.get(0).getPrediction(); + + boolean testPassed= output[1]> ketosPredicitons[i][2]-0.1 && output[1]< ketosPredicitons[i][2]+0.1; + System.out.println( i+ " : Ketos whale network output: " + output[0] + " " + output[1] + " " + testPassed); + + assertTrue(testPassed); + + } + + ketosWorker2.closeModel(); + + } catch (IOException | UnsupportedAudioFileException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + assertEquals(false, true); + } + } + + + + + +} diff --git a/src/test/rawDeepLearningClassifier/KooguDLClassifierTest.java b/src/test/rawDeepLearningClassifier/KooguDLClassifierTest.java new file mode 100644 index 00000000..6a13ec46 --- /dev/null +++ b/src/test/rawDeepLearningClassifier/KooguDLClassifierTest.java @@ -0,0 +1,135 @@ +package test.rawDeepLearningClassifier; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; + +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.jamdev.jdl4pam.utils.DLMatFile; +import org.jamdev.jdl4pam.utils.DLUtils; +import org.jamdev.jpamutils.wavFiles.AudioData; +import org.junit.jupiter.api.Test; + +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; +import rawDeepLearningClassifier.dlClassification.genericModel.GenericPrediction; +import rawDeepLearningClassifier.dlClassification.koogu.KooguModelWorker; +import rawDeepLearningClassifier.segmenter.GroupedRawData; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.format.Mat5File; +import us.hebi.matlab.mat.types.Matrix; + +public class KooguDLClassifierTest { + + + /** + * Test the koogu classifier and tests are working properly. This tests loading the koogu model and also using + * functions in KooguWorker. + */ + @Test + public void kooguClassifierTest() { + //relative paths to the resource folders. + String relModelPath = "./src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/blue_whale_24.kgu"; + String relWavPath = "./src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/20190527_190000.wav"; + String relMatPath = "./src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/rawScores_20190527_190000.mat"; + + runKooguClassifier( relModelPath, relWavPath, relMatPath); + } + + public static void runKooguClassifier(String relModelPath, String relWavPath, String relMatPath) { + + + Path path = Paths.get(relModelPath); + + KooguModelWorker kooguWorker = new KooguModelWorker(); + + StandardModelParams genericModelParams = new StandardModelParams(); + genericModelParams.modelPath = path.toAbsolutePath().normalize().toString(); + + //prep the model - all setting are included within the model + kooguWorker.prepModel(genericModelParams, null); + System.out.println("seglen: " + genericModelParams.defaultSegmentLen); + + + for (int i=0; i groupedData = new ArrayList(); + groupedData.add(groupedRawData); + + + ArrayList genericPrediciton = kooguWorker.runModel(groupedData, soundData.sampleRate, 0); + float[] output = genericPrediciton.get(0).getPrediction(); + + boolean testPassed= output[1]> kooguPredicitions[i][2]-0.1 && output[1]< kooguPredicitions[i][2]+0.1; + + System.out.println(String.format("Chunk %d %d output[0]: predicted %.5f true %.5f ; output[1]: predicted %.5f true %.5f %b",i, startChunk, + output[0], kooguPredicitions[i][1], output[1], kooguPredicitions[i][2],testPassed)); + + if (testPassed) { + truecount++; + } + } + + //there are occasionaly slight differences between PMAGuard and Python so just make sure most data points are the same. + double percTrue = 100*((double) truecount)/kooguPredicitions.length; + + System.out.println(String.format("Perecentage results true: %.2f count %d", percTrue, truecount)); + + //at least 90% of results must match for the dataset + assertTrue(percTrue>0.9); + + kooguWorker.closeModel(); + + } catch (IOException | UnsupportedAudioFileException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + assertEquals(false, true); + } + } + +} \ No newline at end of file diff --git a/src/test/rawDeepLearningClassifier/PamZipDLClassifierTest.java b/src/test/rawDeepLearningClassifier/PamZipDLClassifierTest.java new file mode 100644 index 00000000..fac851d3 --- /dev/null +++ b/src/test/rawDeepLearningClassifier/PamZipDLClassifierTest.java @@ -0,0 +1,24 @@ +package test.rawDeepLearningClassifier; + +import org.junit.jupiter.api.Test; + +public class PamZipDLClassifierTest { + + /** + * Test the koogu classifier and tests are working properly. This tests loading the koogu model and also using + * functions in KooguWorker. + */ + @Test + public void zipClassifierTest() { + //relative paths to the resource folders. + String relModelPath = "./src/test/resources/rawDeepLearningClassifier/PamZip/blue_whale_24.zip"; + + //the zip classifier is the same as the + String relWavPath = "./src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/20190527_190000.wav"; + String relMatPath = "./src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/rawScores_20190527_190000.mat"; + + //metadata says it should be used with Koogu classifier. + KooguDLClassifierTest.runKooguClassifier( relModelPath, relWavPath, relMatPath); + } + +} diff --git a/src/test/resources/matchedTemplateClassifier/DS2templates_test.mat b/src/test/resources/matchedTemplateClassifier/DS2templates_test.mat new file mode 100644 index 00000000..fa0e893a Binary files /dev/null and b/src/test/resources/matchedTemplateClassifier/DS2templates_test.mat differ diff --git a/src/test/resources/matchedTemplateClassifier/DS3clks_test.mat b/src/test/resources/matchedTemplateClassifier/DS3clks_test.mat new file mode 100644 index 00000000..7e377643 Binary files /dev/null and b/src/test/resources/matchedTemplateClassifier/DS3clks_test.mat differ diff --git a/src/test/resources/matchedTemplateClassifier/DS3templates_test.mat b/src/test/resources/matchedTemplateClassifier/DS3templates_test.mat new file mode 100644 index 00000000..8e668b29 Binary files /dev/null and b/src/test/resources/matchedTemplateClassifier/DS3templates_test.mat differ diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/keras_metadata.pb b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/keras_metadata.pb new file mode 100644 index 00000000..ac53b00b --- /dev/null +++ b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/keras_metadata.pb @@ -0,0 +1,348 @@ + +ß;root"_tf_keras_sequential*º;{"name": "sequential", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": false, "class_name": "Sequential", "config": {"name": "sequential", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 7755]}, "dtype": "float32", "sparse": false, "ragged": false, "name": "input_1"}}, {"class_name": "Lambda", "config": {"name": "lambda", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAAEAAAAUwAAAHMMAAAAdACgAXwAZAGhAlMAqQJO6f////+pAtoCdGba\nC2V4cGFuZF9kaW1zqQHaAXSpAHIIAAAA+isvaG9tZS92aW5jZW50L0NvZGUvTUEvaGJkZXQvaGJk\nZXQvbW9kZWxzLnB52gg8bGFtYmRhPisAAADzAAAAAA==\n", null, null]}, "function_type": "lambda", "module": "hbdet.models", "output_shape": null, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}}, {"class_name": "MelSpectrogram", "config": {"name": "mel_spectrogram", "trainable": true, "dtype": "float32"}}, {"class_name": "PCEN", "config": {"layer was saved without config": true}}, {"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 128, 64]}, "dtype": "float32", "sparse": false, "ragged": false, "name": "input_3"}}, {"class_name": "Lambda", "config": {"name": "make_depth_one", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAAEAAAAUwAAAHMMAAAAdACgAXwAZAGhAlMAKQJO6QMAAAApAtoCdGba\nC2V4cGFuZF9kaW1zKQHaAXSpAHIFAAAAekYvaG9tZS92aW5jZW50L0NvZGUvTUEvaGJkZXQvaGJk\nZXQvaHVtcGJhY2tfbW9kZWxfZGlyL2h1bXBiYWNrX21vZGVsLnB52gg8bGFtYmRhPt8AAADzAAAA\nAA==\n", null, null]}, "function_type": "lambda", "module": "hbdet.humpback_model_dir.humpback_model", "output_shape": null, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}}, {"class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": {"class_name": "__tuple__", "items": [7, 7]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}}, "gamma_initializer": {"class_name": "Ones", "config": {}}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}}, "moving_variance_initializer": {"class_name": "Ones", "config": {}}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}}, {"class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}}, {"class_name": "MaxPooling2D", "config": {"name": "pool_pre_resnet", "trainable": true, "dtype": "float32", "pool_size": {"class_name": "__tuple__", "items": [3, 3]}, "padding": "same", "strides": {"class_name": "__tuple__", "items": [2, 2]}, "data_format": "channels_last"}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "Block", "config": {"layer was saved without config": true}}, {"class_name": "GlobalAveragePooling2D", "config": {"name": "pool", "trainable": true, "dtype": "float32", "data_format": "channels_last", "keepdims": false}}, {"class_name": "Dense", "config": {"name": "dense", "trainable": true, "dtype": "float32", "units": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Activation", "config": {"name": "activation", "trainable": true, "dtype": "float32", "activation": "sigmoid"}}]}, "shared_object_id": 20, "input_spec": [{"class_name": "InputSpec", "config": {"dtype": null, "shape": {"class_name": "__tuple__", "items": [null, 7755]}, "ndim": 2, "max_ndim": null, "min_ndim": null, "axes": {}}}], "build_input_shape": {"class_name": "TensorShape", "items": [null, 7755]}, "is_graph_network": true, "full_save_spec": {"class_name": "__tuple__", "items": [[{"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 7755]}, "float32", "input_1"]}], {}]}, "save_spec": {"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 7755]}, "float32", "input_1"]}, "keras_version": "2.10.0", "backend": "tensorflow", "model_config": {"class_name": "Sequential"}, "training_config": {"loss": {"class_name": "BinaryCrossentropy", "config": {"reduction": "auto", "name": "binary_crossentropy", "from_logits": false, "label_smoothing": 0.0, "axis": -1}, "shared_object_id": 22}, "metrics": [[{"class_name": "BinaryAccuracy", "config": {"name": "binary_accuracy", "dtype": "float32", "threshold": 0.5}, "shared_object_id": 23}, {"class_name": "Precision", "config": {"name": "precision", "dtype": "float32", "thresholds": null, "top_k": null, "class_id": null}, "shared_object_id": 24}, {"class_name": "Recall", "config": {"name": "recall", "dtype": "float32", "thresholds": null, "top_k": null, "class_id": null}, "shared_object_id": 25}, {"class_name": "Addons>FBetaScore", "config": {"name": "fbeta", "dtype": "float32", "num_classes": 1, "average": null, "beta": 0.5, "threshold": 0.5}, "shared_object_id": 26}, {"class_name": "Addons>FBetaScore", "config": {"name": "fbeta1", "dtype": "float32", "num_classes": 1, "average": null, "beta": 1.0, "threshold": 0.5}, "shared_object_id": 27}]], "weighted_metrics": null, "loss_weights": null, "optimizer_config": {"class_name": "Adam", "config": {"name": "Adam", "learning_rate": 0.0005, "decay": 0.0, "beta_1": 0.9, "beta_2": 0.999, "epsilon": 1e-07, "amsgrad": false}}}}2 +ó root.layer-0"_tf_keras_layer*É{"name": "lambda", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Lambda", "config": {"name": "lambda", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAAEAAAAUwAAAHMMAAAAdACgAXwAZAGhAlMAqQJO6f////+pAtoCdGba\nC2V4cGFuZF9kaW1zqQHaAXSpAHIIAAAA+isvaG9tZS92aW5jZW50L0NvZGUvTUEvaGJkZXQvaGJk\nZXQvbW9kZWxzLnB52gg8bGFtYmRhPisAAADzAAAAAA==\n", null, null]}, "function_type": "lambda", "module": "hbdet.models", "output_shape": null, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}, "shared_object_id": 1, "build_input_shape": {"class_name": "TensorShape", "items": [null, 7755]}}2 +‘ root.layer-1"_tf_keras_layer*ç{"name": "mel_spectrogram", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MelSpectrogram", "config": {"name": "mel_spectrogram", "trainable": true, "dtype": "float32"}, "shared_object_id": 2, "__passive_serialization__": true, "build_input_shape": {"class_name": "TensorShape", "items": [null, 7755, 1]}}2 +çroot.layer_with_weights-0"_tf_keras_layer*°{"name": "pcen", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "PCEN", "config": {"layer was saved without config": true}}2 +‚ root.layer-3"_tf_keras_input_layer*Ò{"class_name": "InputLayer", "name": "input_3", "dtype": "float32", "sparse": false, "ragged": false, "batch_input_shape": {"class_name": "__tuple__", "items": [null, 128, 64]}, "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 128, 64]}, "dtype": "float32", "sparse": false, "ragged": false, "name": "input_3"}}2 +ù root.layer-4"_tf_keras_layer*Ï{"name": "make_depth_one", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Lambda", "config": {"name": "make_depth_one", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAAEAAAAUwAAAHMMAAAAdACgAXwAZAGhAlMAKQJO6QMAAAApAtoCdGba\nC2V4cGFuZF9kaW1zKQHaAXSpAHIFAAAAekYvaG9tZS92aW5jZW50L0NvZGUvTUEvaGJkZXQvaGJk\nZXQvaHVtcGJhY2tfbW9kZWxfZGlyL2h1bXBiYWNrX21vZGVsLnB52gg8bGFtYmRhPt8AAADzAAAA\nAA==\n", null, null]}, "function_type": "lambda", "module": "hbdet.humpback_model_dir.humpback_model", "output_shape": null, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}, "shared_object_id": 4}2 +€ +root.layer_with_weights-1"_tf_keras_layer*É {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": {"class_name": "__tuple__", "items": [7, 7]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 5}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 6}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 7, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 1}}, "shared_object_id": 28}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 128, 64, 1]}}2 +Ñ root.layer_with_weights-2"_tf_keras_layer*š {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 8}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 9}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 10}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 11}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 12, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 64}}, "shared_object_id": 29}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 128, 64, 64]}}2 +½ root.layer-7"_tf_keras_layer*“{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 13}2 +î  root.layer-8"_tf_keras_layer*Ä{"name": "pool_pre_resnet", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MaxPooling2D", "config": {"name": "pool_pre_resnet", "trainable": true, "dtype": "float32", "pool_size": {"class_name": "__tuple__", "items": [3, 3]}, "padding": "same", "strides": {"class_name": "__tuple__", "items": [2, 2]}, "data_format": "channels_last"}, "shared_object_id": 14, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {}}, "shared_object_id": 30}}2 +ñ +root.layer_with_weights-3"_tf_keras_layer*º{"name": "block_widen_0", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +í root.layer_with_weights-4"_tf_keras_layer*¶{"name": "block_1_0", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +í root.layer_with_weights-5"_tf_keras_layer*¶{"name": "block_2_0", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +ñ root.layer_with_weights-6"_tf_keras_layer*º{"name": "block_widen_1", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +íroot.layer_with_weights-7"_tf_keras_layer*¶{"name": "block_1_1", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +íroot.layer_with_weights-8"_tf_keras_layer*¶{"name": "block_2_1", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +íroot.layer_with_weights-9"_tf_keras_layer*¶{"name": "block_3_1", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +òroot.layer_with_weights-10"_tf_keras_layer*º{"name": "block_widen_2", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +îroot.layer_with_weights-11"_tf_keras_layer*¶{"name": "block_1_2", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +îroot.layer_with_weights-12"_tf_keras_layer*¶{"name": "block_2_2", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +îroot.layer_with_weights-13"_tf_keras_layer*¶{"name": "block_3_2", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +îroot.layer_with_weights-14"_tf_keras_layer*¶{"name": "block_4_2", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +îroot.layer_with_weights-15"_tf_keras_layer*¶{"name": "block_5_2", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +òroot.layer_with_weights-16"_tf_keras_layer*º{"name": "block_widen_3", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +îroot.layer_with_weights-17"_tf_keras_layer*¶{"name": "block_1_3", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +îroot.layer_with_weights-18"_tf_keras_layer*¶{"name": "block_2_3", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Block", "config": {"layer was saved without config": true}}2 +ï root.layer-25"_tf_keras_layer*Ä{"name": "pool", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "GlobalAveragePooling2D", "config": {"name": "pool", "trainable": true, "dtype": "float32", "data_format": "channels_last", "keepdims": false}, "shared_object_id": 15, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {}}, "shared_object_id": 31}}2 +‰root.layer_with_weights-19"_tf_keras_layer*Ñ{"name": "dense", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Dense", "config": {"name": "dense", "trainable": true, "dtype": "float32", "units": 1, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 16}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 17}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 18, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 2, "axes": {"-1": 2048}}, "shared_object_id": 32}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 2048]}}2 +­ root.layer-27"_tf_keras_layer*‚{"name": "activation", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Activation", "config": {"name": "activation", "trainable": true, "dtype": "float32", "activation": "sigmoid"}, "shared_object_id": 19}2 +“ +2root.layer-1._stft"_tf_keras_layer*ã {"name": "stft", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Lambda", "config": {"name": "stft", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAAFAAAAEwAAAHMiAAAAdABqAWoCdACgA3wAZAGhAogAagRqBYgAagRq\nBmQCjQNTACkDTukCAAAAKQLaDGZyYW1lX2xlbmd0aNoKZnJhbWVfc3RlcCkH2gJ0ZtoGc2lnbmFs\n2gRzdGZ02gdzcXVlZXpl2gZjb25maWfaEXN0ZnRfZnJhbWVfbGVuZ3Ro2g9zdGZ0X2ZyYW1lX3N0\nZXCpAdoBdCkB2gRzZWxmqQD6QS9ob21lL3ZpbmNlbnQvQ29kZS9NQS9oYmRldC9oYmRldC9odW1w\nYmFja19tb2RlbF9kaXIvZnJvbnRfZW5kLnB52gg8bGFtYmRhPkkAAABzCAAAAAYBCgEGAQb9\n", null, {"class_name": "__tuple__", "items": [{"class_name": "MelSpectrogram", "config": {"name": "mel_spectrogram", "trainable": true, "dtype": "float32"}, "shared_object_id": 2, "__passive_serialization__": true}]}]}, "function_type": "lambda", "module": "hbdet.humpback_model_dir.front_end", "output_shape": null, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}, "shared_object_id": 33, "build_input_shape": {"class_name": "TensorShape", "items": [null, 7755, 1]}}2 +þ 3root.layer-1._bin"_tf_keras_layer*Ï {"name": "mel_bins", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Lambda", "config": {"name": "mel_bins", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAANAAAAEwAAAHM+AAAAdACgAXQAoAJ0AKADfAChAXQAagRqBYgBagZq\nB4gAiAFqBmoIiAFqBmoJiAFqBmoKZAFkAo0GZAOhA6EBUwApBE7aBm1hdHJpeCkG2gxudW1fbWVs\nX2JpbnPaFG51bV9zcGVjdHJvZ3JhbV9iaW5z2gtzYW1wbGVfcmF0ZdoQbG93ZXJfZWRnZV9oZXJ0\netoQdXBwZXJfZWRnZV9oZXJ0etoEbmFtZekBAAAAKQvaAnRm2gZzcXVhcmXaCXRlbnNvcmRvdNoD\nYWJz2gZzaWduYWzaG2xpbmVhcl90b19tZWxfd2VpZ2h0X21hdHJpeNoGY29uZmln2glmcmVxX2Jp\nbnNyBAAAANoHbG93ZXJfZtoHdXBwZXJfZqkB2gF0qQJyAwAAANoEc2VsZqkA+kEvaG9tZS92aW5j\nZW50L0NvZGUvTUEvaGJkZXQvaGJkZXQvaHVtcGJhY2tfbW9kZWxfZGlyL2Zyb250X2VuZC5wedoI\nPGxhbWJkYT5RAAAAcxoAAAAEAQQBCAEGAQYBAgEGAQYBBgEC+gQGAvgC/w==\n", null, {"class_name": "__tuple__", "items": [513, {"class_name": "MelSpectrogram", "config": {"name": "mel_spectrogram", "trainable": true, "dtype": "float32"}, "shared_object_id": 2, "__passive_serialization__": true}]}]}, "function_type": "lambda", "module": "hbdet.humpback_model_dir.front_end", "output_shape": null, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}, "shared_object_id": 34, "build_input_shape": {"class_name": "TensorShape", "items": [null, 128, 513]}}2 +› =root.layer_with_weights-0.ema"_tf_keras_rnn_layer*Ü +{"name": "simple_rnn", "trainable": false, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "SimpleRNN", "config": {"name": "simple_rnn", "trainable": false, "dtype": "float32", "return_sequences": true, "return_state": false, "go_backwards": false, "stateful": false, "unroll": false, "time_major": false, "units": 64, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "Identity", "config": {"gain": 0.025}, "shared_object_id": 35}, "recurrent_initializer": {"class_name": "Identity", "config": {"gain": 0.975}, "shared_object_id": 36}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 37}, "kernel_regularizer": null, "recurrent_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "recurrent_constraint": null, "bias_constraint": null, "dropout": 0.0, "recurrent_dropout": 0.0}, "shared_object_id": 39, "input_spec": [{"class_name": "InputSpec", "config": {"dtype": null, "shape": {"class_name": "__tuple__", "items": [null, null, 64]}, "ndim": 3, "max_ndim": null, "min_ndim": null, "axes": {}}, "shared_object_id": 40}], "build_input_shape": {"class_name": "TensorShape", "items": [null, null, 64]}}2 +‡p(root.layer_with_weights-3._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ûq$root.layer_with_weights-3._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +är%root.layer_with_weights-3._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 41}2 +‡z(root.layer_with_weights-4._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +û{$root.layer_with_weights-4._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +ä|%root.layer_with_weights-4._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 42}2 +ˆ„(root.layer_with_weights-5._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ü…$root.layer_with_weights-5._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +å†%root.layer_with_weights-5._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 43}2 +ˆŽ(root.layer_with_weights-6._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ü$root.layer_with_weights-6._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +å%root.layer_with_weights-6._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 44}2 +ˆ˜(root.layer_with_weights-7._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ü™$root.layer_with_weights-7._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +åš%root.layer_with_weights-7._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 45}2 +ˆ¢(root.layer_with_weights-8._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ü£$root.layer_with_weights-8._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +å¤%root.layer_with_weights-8._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 46}2 +ˆ¬(root.layer_with_weights-9._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ü­$root.layer_with_weights-9._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +å®%root.layer_with_weights-9._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 47}2 +‰¶)root.layer_with_weights-10._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ý·%root.layer_with_weights-10._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +æ¸&root.layer_with_weights-10._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 48}2 +‰À)root.layer_with_weights-11._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ýÁ%root.layer_with_weights-11._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +æÂ&root.layer_with_weights-11._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 49}2 +‰Ê)root.layer_with_weights-12._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ýË%root.layer_with_weights-12._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +æÌ&root.layer_with_weights-12._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 50}2 +‰Ô)root.layer_with_weights-13._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ýÕ%root.layer_with_weights-13._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +æÖ&root.layer_with_weights-13._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 51}2 +‰Þ)root.layer_with_weights-14._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ýß%root.layer_with_weights-14._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +æà&root.layer_with_weights-14._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 52}2 +‰è)root.layer_with_weights-15._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ýé%root.layer_with_weights-15._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +æê&root.layer_with_weights-15._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 53}2 +‰ò)root.layer_with_weights-16._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ýó%root.layer_with_weights-16._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +æô&root.layer_with_weights-16._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 54}2 +‰ü)root.layer_with_weights-17._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ýý%root.layer_with_weights-17._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +æþ&root.layer_with_weights-17._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 55}2 +‰†)root.layer_with_weights-18._residual_path"_tf_keras_layer*Á{"name": "residual_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ResidualPath", "config": {"layer was saved without config": true}}2 +ý‡%root.layer_with_weights-18._main_path"_tf_keras_layer*¹{"name": "main_path", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "MainPath", "config": {"layer was saved without config": true}}2 +æˆ&root.layer_with_weights-18._activation"_tf_keras_layer*¡{"name": "relu_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_output", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 56}2 +¨®"root.layer_with_weights-0.ema.cell"_tf_keras_layer*ç{"name": "simple_rnn_cell", "trainable": false, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "SimpleRNNCell", "config": {"name": "simple_rnn_cell", "trainable": false, "dtype": "float32", "units": 64, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "Identity", "config": {"gain": 0.025}, "shared_object_id": 35}, "recurrent_initializer": {"class_name": "Identity", "config": {"gain": 0.975}, "shared_object_id": 36}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 37}, "kernel_regularizer": null, "recurrent_regularizer": null, "bias_regularizer": null, "kernel_constraint": null, "recurrent_constraint": null, "bias_constraint": null, "dropout": 0.0, "recurrent_dropout": 0.0}, "shared_object_id": 38, "build_input_shape": {"class_name": "__tuple__", "items": [null, 64]}}2 +ºíroot.keras_api.metrics.0"_tf_keras_metric*‚{"class_name": "Mean", "name": "loss", "dtype": "float32", "config": {"name": "loss", "dtype": "float32"}, "shared_object_id": 57}2 +ìîroot.keras_api.metrics.1"_tf_keras_metric*´{"class_name": "BinaryAccuracy", "name": "binary_accuracy", "dtype": "float32", "config": {"name": "binary_accuracy", "dtype": "float32", "threshold": 0.5}, "shared_object_id": 23}2 +þïroot.keras_api.metrics.2"_tf_keras_metric*Æ{"class_name": "Precision", "name": "precision", "dtype": "float32", "config": {"name": "precision", "dtype": "float32", "thresholds": null, "top_k": null, "class_id": null}, "shared_object_id": 24}2 +õðroot.keras_api.metrics.3"_tf_keras_metric*½{"class_name": "Recall", "name": "recall", "dtype": "float32", "config": {"name": "recall", "dtype": "float32", "thresholds": null, "top_k": null, "class_id": null}, "shared_object_id": 25}2 +‹ñroot.keras_api.metrics.4"_tf_keras_metric*Ó{"class_name": "Addons>FBetaScore", "name": "fbeta", "dtype": "float32", "config": {"name": "fbeta", "dtype": "float32", "num_classes": 1, "average": null, "beta": 0.5, "threshold": 0.5}, "shared_object_id": 26}2 +òroot.keras_api.metrics.5"_tf_keras_metric*Õ{"class_name": "Addons>FBetaScore", "name": "fbeta1", "dtype": "float32", "config": {"name": "fbeta1", "dtype": "float32", "num_classes": 1, "average": null, "beta": 1.0, "threshold": 0.5}, "shared_object_id": 27}2 +² +” 2root.layer_with_weights-3._residual_path._layers.0"_tf_keras_layer*á {"name": "conv_residual", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_residual", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "valid", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 58}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 59}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 60, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 64}}, "shared_object_id": 61}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +€ +• 2root.layer_with_weights-3._residual_path._layers.1"_tf_keras_layer*¯ {"name": "batch_normalization_residual", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_residual", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 62}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 63}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 64}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 65}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 66, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 67}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 256]}}2 +± +› .root.layer_with_weights-3._main_path._layers.0"_tf_keras_layer*ä {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "valid", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 68}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 69}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 70, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 64}}, "shared_object_id": 71}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +þ œ .root.layer_with_weights-3._main_path._layers.1"_tf_keras_layer*± {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 72}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 73}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 74}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 75}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 76, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 64}}, "shared_object_id": 77}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +ö .root.layer_with_weights-3._main_path._layers.2"_tf_keras_layer*©{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 78}2 +š +ž .root.layer_with_weights-3._main_path._layers.3"_tf_keras_layer*Í {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 79}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 80}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 81, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 64}}, "shared_object_id": 82}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +è Ÿ .root.layer_with_weights-3._main_path._layers.4"_tf_keras_layer*› {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 83}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 84}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 85}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 86}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 87, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 64}}, "shared_object_id": 88}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +à  .root.layer_with_weights-3._main_path._layers.5"_tf_keras_layer*“{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 89}2 +© +¡ .root.layer_with_weights-3._main_path._layers.6"_tf_keras_layer*Ü {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 90}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 91}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 92, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 64}}, "shared_object_id": 93}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +ø ¢ .root.layer_with_weights-3._main_path._layers.7"_tf_keras_layer*« {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 94}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 95}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 96}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 97}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 98, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 99}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 256]}}2 +¶ +² .root.layer_with_weights-4._main_path._layers.0"_tf_keras_layer*é {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 100}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 101}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 102, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 103}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 256]}}2 +„ +³ .root.layer_with_weights-4._main_path._layers.1"_tf_keras_layer*· {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 104}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 105}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 106}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 107}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 108, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 64}}, "shared_object_id": 109}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +÷´ .root.layer_with_weights-4._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 110}2 +ž +µ .root.layer_with_weights-4._main_path._layers.3"_tf_keras_layer*Ñ {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 111}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 112}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 113, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 64}}, "shared_object_id": 114}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +î ¶ .root.layer_with_weights-4._main_path._layers.4"_tf_keras_layer*¡ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 115}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 116}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 117}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 118}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 119, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 64}}, "shared_object_id": 120}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +á· .root.layer_with_weights-4._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 121}2 +­ +¸ .root.layer_with_weights-4._main_path._layers.6"_tf_keras_layer*à {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 122}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 123}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 124, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 64}}, "shared_object_id": 125}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +þ ¹ .root.layer_with_weights-4._main_path._layers.7"_tf_keras_layer*± {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 126}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 127}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 128}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 129}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 130, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 131}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 256]}}2 +¶ +É .root.layer_with_weights-5._main_path._layers.0"_tf_keras_layer*é {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 132}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 133}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 134, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 135}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 256]}}2 +„ +Ê .root.layer_with_weights-5._main_path._layers.1"_tf_keras_layer*· {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 136}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 137}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 138}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 139}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 140, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 64}}, "shared_object_id": 141}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +÷Ë .root.layer_with_weights-5._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 142}2 +ž +Ì .root.layer_with_weights-5._main_path._layers.3"_tf_keras_layer*Ñ {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 64, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 143}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 144}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 145, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 64}}, "shared_object_id": 146}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +î Í .root.layer_with_weights-5._main_path._layers.4"_tf_keras_layer*¡ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 147}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 148}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 149}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 150}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 151, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 64}}, "shared_object_id": 152}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +áÎ .root.layer_with_weights-5._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 153}2 +­ +Ï .root.layer_with_weights-5._main_path._layers.6"_tf_keras_layer*à {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 154}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 155}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 156, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 64}}, "shared_object_id": 157}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 64]}}2 +þ Ð .root.layer_with_weights-5._main_path._layers.7"_tf_keras_layer*± {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 158}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 159}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 160}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 161}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 162, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 163}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 256]}}2 +¸ +Û 2root.layer_with_weights-6._residual_path._layers.0"_tf_keras_layer*ç {"name": "conv_residual", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_residual", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [2, 2]}, "padding": "valid", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 164}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 165}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 166, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 167}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 256]}}2 +† +Ü 2root.layer_with_weights-6._residual_path._layers.1"_tf_keras_layer*µ {"name": "batch_normalization_residual", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_residual", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 168}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 169}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 170}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 171}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 172, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 173}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +¸ +â .root.layer_with_weights-6._main_path._layers.0"_tf_keras_layer*ë {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [2, 2]}, "padding": "valid", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 174}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 175}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 176, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 177}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 64, 32, 256]}}2 +† +ã .root.layer_with_weights-6._main_path._layers.1"_tf_keras_layer*¹ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 178}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 179}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 180}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 181}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 182, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 128}}, "shared_object_id": 183}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +÷ä .root.layer_with_weights-6._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 184}2 +¡ +å .root.layer_with_weights-6._main_path._layers.3"_tf_keras_layer*Ô {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 185}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 186}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 187, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 128}}, "shared_object_id": 188}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +ð æ .root.layer_with_weights-6._main_path._layers.4"_tf_keras_layer*£ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 189}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 190}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 191}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 192}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 193, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 128}}, "shared_object_id": 194}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +áç .root.layer_with_weights-6._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 195}2 +¯ +è .root.layer_with_weights-6._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 196}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 197}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 198, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 128}}, "shared_object_id": 199}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +þ é .root.layer_with_weights-6._main_path._layers.7"_tf_keras_layer*± {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 200}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 201}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 202}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 203}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 204, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 205}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +· +ù .root.layer_with_weights-7._main_path._layers.0"_tf_keras_layer*ê {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 206}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 207}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 208, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 209}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +† +ú .root.layer_with_weights-7._main_path._layers.1"_tf_keras_layer*¹ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 210}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 211}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 212}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 213}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 214, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 128}}, "shared_object_id": 215}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +÷û .root.layer_with_weights-7._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 216}2 +¡ +ü .root.layer_with_weights-7._main_path._layers.3"_tf_keras_layer*Ô {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 217}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 218}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 219, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 128}}, "shared_object_id": 220}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +ð ý .root.layer_with_weights-7._main_path._layers.4"_tf_keras_layer*£ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 221}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 222}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 223}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 224}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 225, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 128}}, "shared_object_id": 226}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +áþ .root.layer_with_weights-7._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 227}2 +¯ +ÿ .root.layer_with_weights-7._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 228}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 229}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 230, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 128}}, "shared_object_id": 231}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +þ € +.root.layer_with_weights-7._main_path._layers.7"_tf_keras_layer*± {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 232}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 233}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 234}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 235}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 236, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 237}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +· + +.root.layer_with_weights-8._main_path._layers.0"_tf_keras_layer*ê {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 238}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 239}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 240, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 241}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +† +‘ +.root.layer_with_weights-8._main_path._layers.1"_tf_keras_layer*¹ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 242}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 243}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 244}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 245}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 246, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 128}}, "shared_object_id": 247}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +÷’ +.root.layer_with_weights-8._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 248}2 +¡ +“ +.root.layer_with_weights-8._main_path._layers.3"_tf_keras_layer*Ô {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 249}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 250}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 251, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 128}}, "shared_object_id": 252}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +ð ” +.root.layer_with_weights-8._main_path._layers.4"_tf_keras_layer*£ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 253}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 254}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 255}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 256}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 257, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 128}}, "shared_object_id": 258}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +á• +.root.layer_with_weights-8._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 259}2 +¯ +– +.root.layer_with_weights-8._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 260}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 261}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 262, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 128}}, "shared_object_id": 263}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +þ — +.root.layer_with_weights-8._main_path._layers.7"_tf_keras_layer*± {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 264}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 265}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 266}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 267}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 268, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 269}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +· +§ +.root.layer_with_weights-9._main_path._layers.0"_tf_keras_layer*ê {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 270}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 271}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 272, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 273}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +† +¨ +.root.layer_with_weights-9._main_path._layers.1"_tf_keras_layer*¹ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 274}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 275}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 276}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 277}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 278, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 128}}, "shared_object_id": 279}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +÷© +.root.layer_with_weights-9._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 280}2 +¡ +ª +.root.layer_with_weights-9._main_path._layers.3"_tf_keras_layer*Ô {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 128, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 281}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 282}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 283, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 128}}, "shared_object_id": 284}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +ð « +.root.layer_with_weights-9._main_path._layers.4"_tf_keras_layer*£ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 285}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 286}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 287}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 288}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 289, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 128}}, "shared_object_id": 290}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +ᬠ+.root.layer_with_weights-9._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 291}2 +¯ +­ +.root.layer_with_weights-9._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 292}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 293}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 294, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 128}}, "shared_object_id": 295}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 128]}}2 +þ ® +.root.layer_with_weights-9._main_path._layers.7"_tf_keras_layer*± {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 296}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 297}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 298}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 299}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 300, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 301}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +º +¹ +3root.layer_with_weights-10._residual_path._layers.0"_tf_keras_layer*è {"name": "conv_residual", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_residual", "trainable": true, "dtype": "float32", "filters": 1024, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [2, 2]}, "padding": "valid", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 302}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 303}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 304, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 305}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +ˆ +º +3root.layer_with_weights-10._residual_path._layers.1"_tf_keras_layer*¶ {"name": "batch_normalization_residual", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_residual", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 306}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 307}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 308}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 309}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 310, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 1024}}, "shared_object_id": 311}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +¹ +À +/root.layer_with_weights-10._main_path._layers.0"_tf_keras_layer*ë {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [2, 2]}, "padding": "valid", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 312}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 313}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 314, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 315}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32, 16, 512]}}2 +† +Á +/root.layer_with_weights-10._main_path._layers.1"_tf_keras_layer*¸ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 316}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 317}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 318}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 319}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 320, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 321}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +ø +/root.layer_with_weights-10._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 322}2 +¡ +à +/root.layer_with_weights-10._main_path._layers.3"_tf_keras_layer*Ó {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 323}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 324}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 325, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 326}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +ð Ä +/root.layer_with_weights-10._main_path._layers.4"_tf_keras_layer*¢ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 327}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 328}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 329}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 330}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 331, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 332}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +âÅ +/root.layer_with_weights-10._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 333}2 +° +Æ +/root.layer_with_weights-10._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 1024, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 334}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 335}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 336, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 337}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +€ +Ç +/root.layer_with_weights-10._main_path._layers.7"_tf_keras_layer*² {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 338}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 339}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 340}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 341}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 342, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 1024}}, "shared_object_id": 343}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +¹ +× +/root.layer_with_weights-11._main_path._layers.0"_tf_keras_layer*ë {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 344}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 345}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 346, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 1024}}, "shared_object_id": 347}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +† +Ø +/root.layer_with_weights-11._main_path._layers.1"_tf_keras_layer*¸ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 348}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 349}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 350}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 351}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 352, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 353}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +øÙ +/root.layer_with_weights-11._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 354}2 +¡ +Ú +/root.layer_with_weights-11._main_path._layers.3"_tf_keras_layer*Ó {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 355}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 356}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 357, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 358}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +ð Û +/root.layer_with_weights-11._main_path._layers.4"_tf_keras_layer*¢ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 359}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 360}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 361}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 362}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 363, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 364}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +âÜ +/root.layer_with_weights-11._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 365}2 +° +Ý +/root.layer_with_weights-11._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 1024, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 366}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 367}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 368, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 369}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +€ +Þ +/root.layer_with_weights-11._main_path._layers.7"_tf_keras_layer*² {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 370}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 371}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 372}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 373}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 374, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 1024}}, "shared_object_id": 375}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +¹ +î +/root.layer_with_weights-12._main_path._layers.0"_tf_keras_layer*ë {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 376}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 377}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 378, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 1024}}, "shared_object_id": 379}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +† +ï +/root.layer_with_weights-12._main_path._layers.1"_tf_keras_layer*¸ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 380}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 381}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 382}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 383}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 384, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 385}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +øð +/root.layer_with_weights-12._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 386}2 +¡ +ñ +/root.layer_with_weights-12._main_path._layers.3"_tf_keras_layer*Ó {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 387}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 388}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 389, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 390}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +ð ò +/root.layer_with_weights-12._main_path._layers.4"_tf_keras_layer*¢ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 391}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 392}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 393}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 394}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 395, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 396}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +âó +/root.layer_with_weights-12._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 397}2 +° +ô +/root.layer_with_weights-12._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 1024, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 398}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 399}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 400, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 401}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +€ +õ +/root.layer_with_weights-12._main_path._layers.7"_tf_keras_layer*² {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 402}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 403}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 404}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 405}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 406, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 1024}}, "shared_object_id": 407}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +¹ +… /root.layer_with_weights-13._main_path._layers.0"_tf_keras_layer*ë {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 408}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 409}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 410, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 1024}}, "shared_object_id": 411}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +† +† /root.layer_with_weights-13._main_path._layers.1"_tf_keras_layer*¸ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 412}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 413}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 414}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 415}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 416, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 417}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +ø‡ /root.layer_with_weights-13._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 418}2 +¡ +ˆ /root.layer_with_weights-13._main_path._layers.3"_tf_keras_layer*Ó {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 419}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 420}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 421, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 422}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +ð ‰ /root.layer_with_weights-13._main_path._layers.4"_tf_keras_layer*¢ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 423}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 424}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 425}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 426}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 427, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 428}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +⊠/root.layer_with_weights-13._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 429}2 +° +‹ /root.layer_with_weights-13._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 1024, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 430}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 431}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 432, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 433}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +€ +Œ /root.layer_with_weights-13._main_path._layers.7"_tf_keras_layer*² {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 434}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 435}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 436}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 437}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 438, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 1024}}, "shared_object_id": 439}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +¹ +œ /root.layer_with_weights-14._main_path._layers.0"_tf_keras_layer*ë {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 440}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 441}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 442, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 1024}}, "shared_object_id": 443}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +† + /root.layer_with_weights-14._main_path._layers.1"_tf_keras_layer*¸ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 444}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 445}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 446}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 447}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 448, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 449}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +øž /root.layer_with_weights-14._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 450}2 +¡ +Ÿ /root.layer_with_weights-14._main_path._layers.3"_tf_keras_layer*Ó {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 451}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 452}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 453, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 454}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +ð   /root.layer_with_weights-14._main_path._layers.4"_tf_keras_layer*¢ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 455}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 456}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 457}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 458}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 459, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 460}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +â¡ /root.layer_with_weights-14._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 461}2 +° +¢ /root.layer_with_weights-14._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 1024, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 462}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 463}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 464, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 465}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +€ +£ /root.layer_with_weights-14._main_path._layers.7"_tf_keras_layer*² {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 466}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 467}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 468}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 469}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 470, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 1024}}, "shared_object_id": 471}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +¹ +³ /root.layer_with_weights-15._main_path._layers.0"_tf_keras_layer*ë {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 472}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 473}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 474, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 1024}}, "shared_object_id": 475}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +† +´ /root.layer_with_weights-15._main_path._layers.1"_tf_keras_layer*¸ {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 476}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 477}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 478}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 479}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 480, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 481}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +øµ /root.layer_with_weights-15._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 482}2 +¡ +¶ /root.layer_with_weights-15._main_path._layers.3"_tf_keras_layer*Ó {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 256, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 483}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 484}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 485, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 486}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +ð · /root.layer_with_weights-15._main_path._layers.4"_tf_keras_layer*¢ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 487}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 488}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 489}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 490}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 491, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 256}}, "shared_object_id": 492}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +⸠/root.layer_with_weights-15._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 493}2 +° +¹ /root.layer_with_weights-15._main_path._layers.6"_tf_keras_layer*â {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 1024, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 494}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 495}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 496, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 256}}, "shared_object_id": 497}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 256]}}2 +€ +º /root.layer_with_weights-15._main_path._layers.7"_tf_keras_layer*² {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 498}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 499}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 500}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 501}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 502, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 1024}}, "shared_object_id": 503}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +» +Å 3root.layer_with_weights-16._residual_path._layers.0"_tf_keras_layer*é {"name": "conv_residual", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_residual", "trainable": true, "dtype": "float32", "filters": 2048, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [2, 2]}, "padding": "valid", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 504}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 505}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 506, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 1024}}, "shared_object_id": 507}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +‡ +Æ 3root.layer_with_weights-16._residual_path._layers.1"_tf_keras_layer*µ {"name": "batch_normalization_residual", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_residual", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 508}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 509}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 510}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 511}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 512, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 2048}}, "shared_object_id": 513}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 2048]}}2 +º +Ì /root.layer_with_weights-16._main_path._layers.0"_tf_keras_layer*ì {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [2, 2]}, "padding": "valid", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 514}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 515}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 516, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 1024}}, "shared_object_id": 517}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 16, 8, 1024]}}2 +… +Í /root.layer_with_weights-16._main_path._layers.1"_tf_keras_layer*· {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 518}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 519}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 520}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 521}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 522, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 523}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +øÎ /root.layer_with_weights-16._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 524}2 +  +Ï /root.layer_with_weights-16._main_path._layers.3"_tf_keras_layer*Ò {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 525}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 526}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 527, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 528}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +ï Ð /root.layer_with_weights-16._main_path._layers.4"_tf_keras_layer*¡ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 529}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 530}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 531}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 532}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 533, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 534}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +âÑ /root.layer_with_weights-16._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 535}2 +¯ +Ò /root.layer_with_weights-16._main_path._layers.6"_tf_keras_layer*á {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 2048, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 536}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 537}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 538, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 539}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +ÿ Ó /root.layer_with_weights-16._main_path._layers.7"_tf_keras_layer*± {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 540}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 541}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 542}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 543}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 544, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 2048}}, "shared_object_id": 545}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 2048]}}2 +¸ +ã /root.layer_with_weights-17._main_path._layers.0"_tf_keras_layer*ê {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 546}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 547}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 548, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 2048}}, "shared_object_id": 549}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 2048]}}2 +… +ä /root.layer_with_weights-17._main_path._layers.1"_tf_keras_layer*· {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 550}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 551}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 552}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 553}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 554, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 555}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +øå /root.layer_with_weights-17._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 556}2 +  +æ /root.layer_with_weights-17._main_path._layers.3"_tf_keras_layer*Ò {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 557}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 558}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 559, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 560}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +ï ç /root.layer_with_weights-17._main_path._layers.4"_tf_keras_layer*¡ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 561}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 562}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 563}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 564}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 565, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 566}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +âè /root.layer_with_weights-17._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 567}2 +¯ +é /root.layer_with_weights-17._main_path._layers.6"_tf_keras_layer*á {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 2048, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 568}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 569}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 570, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 571}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +ÿ ê /root.layer_with_weights-17._main_path._layers.7"_tf_keras_layer*± {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 572}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 573}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 574}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 575}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 576, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 2048}}, "shared_object_id": 577}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 2048]}}2 +¸ +ú /root.layer_with_weights-18._main_path._layers.0"_tf_keras_layer*ê {"name": "conv_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_bottleneck", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 578}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 579}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 580, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 2048}}, "shared_object_id": 581}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 2048]}}2 +… +û /root.layer_with_weights-18._main_path._layers.1"_tf_keras_layer*· {"name": "batch_normalization_bottleneck", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_bottleneck", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 582}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 583}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 584}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 585}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 586, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 587}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +øü /root.layer_with_weights-18._main_path._layers.2"_tf_keras_layer*ª{"name": "relu_bottleneck", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu_bottleneck", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 588}2 +  +ý /root.layer_with_weights-18._main_path._layers.3"_tf_keras_layer*Ò {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 512, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 589}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 590}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 591, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 592}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +ï þ /root.layer_with_weights-18._main_path._layers.4"_tf_keras_layer*¡ {"name": "batch_normalization", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 593}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 594}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 595}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 596}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 597, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 512}}, "shared_object_id": 598}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +âÿ /root.layer_with_weights-18._main_path._layers.5"_tf_keras_layer*”{"name": "relu", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "ReLU", "config": {"name": "relu", "trainable": true, "dtype": "float32", "max_value": null, "negative_slope": 0.0, "threshold": 0.0}, "shared_object_id": 599}2 +¯ +€ /root.layer_with_weights-18._main_path._layers.6"_tf_keras_layer*á {"name": "conv_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "Conv2D", "config": {"name": "conv_output", "trainable": true, "dtype": "float32", "filters": 2048, "kernel_size": {"class_name": "__tuple__", "items": [1, 1]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 600}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 601}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 602, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 512}}, "shared_object_id": 603}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 512]}}2 +ÿ  /root.layer_with_weights-18._main_path._layers.7"_tf_keras_layer*± {"name": "batch_normalization_output", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "preserve_input_structure_in_config": false, "autocast": true, "class_name": "BatchNormalization", "config": {"name": "batch_normalization_output", "trainable": true, "dtype": "float32", "axis": [3], "momentum": 0.9997, "epsilon": 0.0001, "center": true, "scale": false, "beta_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 604}, "gamma_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 605}, "moving_mean_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 606}, "moving_variance_initializer": {"class_name": "Ones", "config": {}, "shared_object_id": 607}, "beta_regularizer": null, "gamma_regularizer": null, "beta_constraint": null, "gamma_constraint": null}, "shared_object_id": 608, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {"3": 2048}}, "shared_object_id": 609}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 8, 4, 2048]}}2 \ No newline at end of file diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/saved_model.pb b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/saved_model.pb new file mode 100644 index 00000000..07ce2663 Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/saved_model.pb differ diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/variables/variables.data-00000-of-00001 b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/variables/variables.data-00000-of-00001 new file mode 100644 index 00000000..3bdd558c Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/variables/variables.data-00000-of-00001 differ diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/variables/variables.index b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/variables/variables.index new file mode 100644 index 00000000..490616b2 Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/FlatHBNA/variables/variables.index differ diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/SAMOSAS_EL1_humpback.wav b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/SAMOSAS_EL1_humpback.wav new file mode 100644 index 00000000..2b19f580 Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/SAMOSAS_EL1_humpback.wav differ diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/SAMOSAS_EL1_humpback_annotations.txt b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/SAMOSAS_EL1_humpback_annotations.txt new file mode 100644 index 00000000..567fb21e --- /dev/null +++ b/src/test/resources/rawDeepLearningClassifier/Generic/humpback_whale_atlantic/SAMOSAS_EL1_humpback_annotations.txt @@ -0,0 +1,216 @@ +Selection Begin Time (s) End Time (s) High Freq (Hz) Low Freq (Hz) Prediction/Comments +1 3.8775 7.755 1000 50 0.99972177 +2 7.755 11.6325 1000 50 0.89199716 +3 11.6325 15.51 1000 50 0.64813995 +4 15.51 19.3875 1000 50 0.9713266 +5 19.3875 23.265 1000 50 0.8342721 +6 23.265 27.142500000000002 1000 50 0.99634343 +7 31.02 34.8975 1000 50 0.9933128 +8 34.8975 38.775 1000 50 0.5325079 +9 42.6525 46.53 1000 50 0.9727334 +10 46.53 50.4075 1000 50 0.7700455 +11 54.285 58.162499999999994 1000 50 0.93229276 +12 58.1625 62.04 1000 50 0.5089565 +13 62.04 65.9175 1000 50 0.53604007 +14 65.9175 69.795 1000 50 0.6665228 +15 69.795 73.6725 1000 50 0.8109671 +16 73.6725 77.55 1000 50 0.9998325 +17 77.55 81.4275 1000 50 0.6296093 +18 81.4275 85.30499999999999 1000 50 0.99747694 +19 93.06 96.9375 1000 50 0.99976456 +20 96.9375 100.815 1000 50 0.53218305 +21 104.6925 108.57 1000 50 0.98100567 +22 112.4475 116.325 1000 50 0.91926867 +23 124.08 127.9575 1000 50 0.9990459 +24 127.9575 131.835 1000 50 0.6861532 +25 135.7125 139.59 1000 50 0.5737106 +26 143.4675 147.345 1000 50 0.9537415 +27 147.345 151.2225 1000 50 0.5592457 +28 151.2225 155.1 1000 50 0.97674406 +29 155.1 158.9775 1000 50 0.55292696 +30 166.7325 170.60999999999999 1000 50 0.86637044 +31 170.61 174.4875 1000 50 0.8887141 +32 174.4875 178.365 1000 50 0.65914917 +33 182.2425 186.12 1000 50 0.78331536 +34 189.9975 193.875 1000 50 0.51390845 +35 193.875 197.7525 1000 50 0.8545577 +36 197.7525 201.63 1000 50 0.99297434 +37 209.385 213.2625 1000 50 0.91762686 +38 217.14 221.01749999999998 1000 50 0.9865262 +39 221.0175 224.895 1000 50 0.9802332 +40 224.895 228.7725 1000 50 0.9038371 +41 228.7725 232.65 1000 50 0.9299754 +42 232.65 236.5275 1000 50 0.99995977 +43 236.5275 240.405 1000 50 0.75877947 +44 240.405 244.2825 1000 50 0.8074757 +45 244.2825 248.16 1000 50 0.57404643 +46 248.16 252.0375 1000 50 0.5844841 +47 252.0375 255.915 1000 50 0.77901065 +48 267.5475 271.425 1000 50 0.9999262 +49 283.0575 286.935 1000 50 0.91496134 +50 286.935 290.8125 1000 50 0.6016941 +51 290.8125 294.69 1000 50 0.5288352 +52 294.69 298.5675 1000 50 0.7803428 +53 298.5675 302.445 1000 50 0.9457351 +54 302.445 306.3225 1000 50 0.8809803 +55 314.0775 317.955 1000 50 0.76862615 +56 317.955 321.8325 1000 50 0.9179431 +57 321.8325 325.71 1000 50 0.64783925 +58 325.71 329.5875 1000 50 0.63720423 +59 333.465 337.3425 1000 50 0.9080041 +60 337.3425 341.21999999999997 1000 50 0.636528 +61 341.22 345.0975 1000 50 0.8285956 +62 348.975 352.8525 1000 50 0.5541101 +63 360.6075 364.485 1000 50 0.9939563 +64 364.485 368.3625 1000 50 0.5179203 +65 368.3625 372.24 1000 50 0.998485 +66 372.24 376.1175 1000 50 0.7255482 +67 379.995 383.8725 1000 50 0.6432554 +68 387.75 391.6275 1000 50 0.85360736 +69 395.505 399.3825 1000 50 0.63949126 +70 403.26 407.1375 1000 50 0.9875199 +71 407.1375 411.015 1000 50 0.63171786 +72 411.015 414.8925 1000 50 0.51271856 +73 414.8925 418.77 1000 50 0.8624972 +74 418.77 422.6475 1000 50 0.9287566 +75 422.6475 426.525 1000 50 0.79819566 +76 442.035 445.9125 1000 50 0.9992767 +77 445.9125 449.79 1000 50 0.85704327 +78 449.79 453.6675 1000 50 0.606248 +79 465.3 469.1775 1000 50 0.6945031 +80 469.1775 473.055 1000 50 0.91730964 +81 476.9325 480.81 1000 50 0.8548426 +82 480.81 484.6875 1000 50 0.8609153 +83 484.6875 488.565 1000 50 0.53578407 +84 492.4425 496.32 1000 50 0.6815063 +85 504.075 507.9525 1000 50 0.5493686 +86 511.83 515.7075 1000 50 0.57242775 +87 515.7075 519.585 1000 50 0.802748 +88 519.585 523.4625000000001 1000 50 0.75667137 +89 527.34 531.2175000000001 1000 50 0.6283601 +90 535.095 538.9725000000001 1000 50 0.58465177 +91 538.9725 542.85 1000 50 0.7337384 +92 546.7275 550.605 1000 50 0.6839575 +93 562.2375 566.115 1000 50 0.6672469 +94 593.2575 597.1350000000001 1000 50 0.5644848 +95 604.89 608.7675 1000 50 0.78537405 +96 624.2775 628.1550000000001 1000 50 0.52095556 +97 632.0325 635.9100000000001 1000 50 0.86063546 +98 635.91 639.7875 1000 50 0.7444124 +99 643.665 647.5425 1000 50 0.59704643 +100 647.5425 651.4200000000001 1000 50 0.9363932 +101 666.93 670.8075 1000 50 0.78785974 +102 678.5625 682.44 1000 50 0.99932384 +103 690.195 694.0725000000001 1000 50 0.7276663 +104 694.0725 697.95 1000 50 0.6033868 +105 697.95 701.8275000000001 1000 50 0.7666113 +106 705.705 709.5825000000001 1000 50 0.9996606 +107 709.5825 713.46 1000 50 0.87555915 +108 717.3375 721.215 1000 50 0.99977213 +109 721.215 725.0925000000001 1000 50 0.6706097 +110 728.97 732.8475000000001 1000 50 0.99964714 +111 736.725 740.6025000000001 1000 50 0.9942172 +112 740.6025 744.48 1000 50 0.99729407 +113 744.48 748.3575000000001 1000 50 0.98387647 +114 748.3575 752.235 1000 50 0.97733885 +115 756.1125 759.99 1000 50 0.9908622 +116 763.8675 767.745 1000 50 0.9963475 +117 775.5 779.3775 1000 50 0.9301784 +118 779.3775 783.2550000000001 1000 50 0.8671598 +119 783.255 787.1325 1000 50 0.8163382 +120 787.1325 791.0100000000001 1000 50 0.67742085 +121 794.8875 798.7650000000001 1000 50 0.9999998 +122 798.765 802.6425 1000 50 0.9092158 +123 802.6425 806.5200000000001 1000 50 0.5064141 +124 806.52 810.3975 1000 50 0.9245316 +125 810.3975 814.2750000000001 1000 50 0.9987917 +126 814.275 818.1525 1000 50 0.98765403 +127 818.1525 822.0300000000001 1000 50 0.7059854 +128 822.03 825.9075 1000 50 0.9687309 +129 825.9075 829.7850000000001 1000 50 0.99980175 +130 829.785 833.6625 1000 50 0.9667981 +131 833.6625 837.5400000000001 1000 50 0.6459364 +132 837.54 841.4175 1000 50 0.99052876 +133 845.295 849.1725 1000 50 0.98032534 +134 849.1725 853.0500000000001 1000 50 0.9769905 +135 864.6825 868.5600000000001 1000 50 0.9989409 +136 868.56 872.4375 1000 50 0.65797764 +137 876.315 880.1925000000001 1000 50 0.8667758 +138 880.1925 884.07 1000 50 0.8123294 +139 884.07 887.9475000000001 1000 50 0.9994879 +140 887.9475 891.825 1000 50 0.6211881 +141 895.7025 899.58 1000 50 0.9882314 +142 899.58 903.4575000000001 1000 50 0.988451 +143 903.4575 907.335 1000 50 0.8560532 +144 907.335 911.2125000000001 1000 50 0.8371353 +145 911.2125 915.09 1000 50 0.9963776 +146 918.9675 922.845 1000 50 0.99993306 +147 930.6 934.4775000000001 1000 50 0.98831177 +148 938.355 942.2325000000001 1000 50 0.6520295 +149 957.7425 961.62 1000 50 0.6918473 +150 961.62 965.4975000000001 1000 50 0.74858236 +151 965.4975 969.375 1000 50 0.97066724 +152 973.2525 977.1300000000001 1000 50 0.6426704 +153 981.0075 984.8850000000001 1000 50 0.8150208 +154 984.885 988.7625 1000 50 0.5683353 +155 988.7625 992.6400000000001 1000 50 0.581548 +156 992.64 996.5175 1000 50 0.77473736 +157 1000.395 1004.2725 1000 50 0.950805 +158 1004.2725 1008.1500000000001 1000 50 0.53004926 +159 1012.0275 1015.9050000000001 1000 50 0.9998928 +160 1015.905 1019.7825 1000 50 0.90716743 +161 1023.66 1027.5375 1000 50 0.9305084 +162 1031.415 1035.2925 1000 50 0.77918565 +163 1039.17 1043.0475000000001 1000 50 0.9984765 +164 1043.0475 1046.925 1000 50 0.5665361 +165 1050.8025 1054.68 1000 50 0.999446 +166 1054.68 1058.5575000000001 1000 50 0.5452138 +167 1062.435 1066.3125 1000 50 0.99469894 +168 1066.3125 1070.19 1000 50 0.5405148 +169 1074.0675 1077.9450000000002 1000 50 0.5739461 +170 1077.945 1081.8225 1000 50 0.5058043 +171 1081.8225 1085.7 1000 50 0.883449 +172 1089.5775 1093.4550000000002 1000 50 0.9999634 +173 1093.455 1097.3325 1000 50 0.6814424 +174 1097.3325 1101.21 1000 50 0.904202 +175 1105.0875 1108.9650000000001 1000 50 0.55364376 +176 1120.5975 1124.4750000000001 1000 50 0.719839 +177 1136.1075 1139.9850000000001 1000 50 0.57873297 +178 1147.74 1151.6175 1000 50 0.7904256 +179 1151.6175 1155.4950000000001 1000 50 0.88318896 +180 1178.76 1182.6375 1000 50 0.88804716 +181 1186.515 1190.3925000000002 1000 50 0.6625152 +182 1198.1475 1202.025 1000 50 0.9660911 +183 1205.9025 1209.78 1000 50 0.91225654 +184 1209.78 1213.6575 1000 50 0.7151032 +185 1221.4125 1225.29 1000 50 0.9564372 +186 1244.6775 1248.555 1000 50 0.722955 +187 1248.555 1252.4325000000001 1000 50 0.7033242 +188 1252.4325 1256.31 1000 50 0.72211057 +189 1256.31 1260.1875 1000 50 0.6124199 +190 1260.1875 1264.065 1000 50 0.7847693 +191 1264.065 1267.9425 1000 50 0.6674457 +192 1267.9425 1271.8200000000002 1000 50 0.76507866 +193 1271.82 1275.6975 1000 50 0.8226003 +194 1279.575 1283.4525 1000 50 0.5882834 +195 1283.4525 1287.3300000000002 1000 50 0.94607145 +196 1287.33 1291.2075 1000 50 0.5691646 +197 1295.085 1298.9625 1000 50 0.9842685 +198 1298.9625 1302.8400000000001 1000 50 0.9851393 +199 1310.595 1314.4725 1000 50 0.5915515 +200 1314.4725 1318.3500000000001 1000 50 0.56246245 +201 1318.35 1322.2275 1000 50 0.996443 +202 1326.105 1329.9825 1000 50 0.8811614 +203 1329.9825 1333.8600000000001 1000 50 0.9826125 +204 1337.7375 1341.615 1000 50 0.9936857 +205 1345.4925 1349.3700000000001 1000 50 0.997924 +206 1357.125 1361.0025 1000 50 0.88610935 +207 1364.88 1368.7575000000002 1000 50 0.5882369 +208 1388.145 1392.0225 1000 50 0.6528943 +209 1395.9 1399.7775000000001 1000 50 0.7434355 +210 1407.5325 1411.41 1000 50 0.53381926 +211 1438.5525 1442.43 1000 50 0.7328098 +212 1446.3075 1450.185 1000 50 0.65970933 +213 1465.695 1469.5725 1000 50 0.539152 +214 1485.0825 1488.96 1000 50 0.6864708 +215 1492.8375 1496.7150000000001 1000 50 0.6845237 diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/legacy_format/right_whale_DL_settings.pdtf b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/legacy_format/right_whale_DL_settings.pdtf new file mode 100644 index 00000000..d8858a7f --- /dev/null +++ b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/legacy_format/right_whale_DL_settings.pdtf @@ -0,0 +1 @@ +{"model_info":"{\"output_shape\":[-1,2],\"input_shape\":[-1,40,40,1]}","class_info":"{\"name_class\":\"Noise,Right Whale\",\"num_class\":2}","seg_size":"{\"size_ms\":2000}","load_audio":"{\"sr\":2000}","spectrogram":"{\"fft\":256,\"hop\":100}","freq_compression":"{\"bins\":40,\"fmin\":47,\"fmax\":357}","norm_row_sum":"{}"} \ No newline at end of file diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/model_lenet_dropout_input_conv_all/saved_model.pb b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/model_lenet_dropout_input_conv_all/saved_model.pb new file mode 100644 index 00000000..9d51248a Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/model_lenet_dropout_input_conv_all/saved_model.pb differ diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/model_lenet_dropout_input_conv_all/variables/variables.data-00000-of-00001 b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/model_lenet_dropout_input_conv_all/variables/variables.data-00000-of-00001 new file mode 100644 index 00000000..cefbba88 Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/model_lenet_dropout_input_conv_all/variables/variables.data-00000-of-00001 differ diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/model_lenet_dropout_input_conv_all/variables/variables.index b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/model_lenet_dropout_input_conv_all/variables/variables.index new file mode 100644 index 00000000..4e56624e Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/model_lenet_dropout_input_conv_all/variables/variables.index differ diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/right_whale_DL_settings.pdtf b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/right_whale_DL_settings.pdtf new file mode 100644 index 00000000..6549ec42 --- /dev/null +++ b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/right_whale_DL_settings.pdtf @@ -0,0 +1,50 @@ +{ + "framework_info": {"framework": "Bespoke"}, + "model_info": { + "output_shape": [ + -1, + 2 + ], + "input_shape": [ + -1, + 40, + 40, + 1 + ] + }, + "class_info": { + "name_class": [ + "Noise", + "Right Whale" + ], + "num_class": 2 + }, + "transforms": [ + { + "name": "norm_row_sum", + "params": {} + }, + { + "name": "spectrogram", + "params": { + "fft": 256, + "hop": 100 + } + }, + { + "name": "load_audio", + "params": {"sr": 2000} + }, + { + "name": "freq_compression", + "params": { + "bins": 40, + "fmin": 47, + "fmax": 357 + } + } + ], + "description": "Metadata for acoustic deep learning", + "version_info": {"version": 1}, + "seg_size": {"size_ms": 2000} +} \ No newline at end of file diff --git a/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/right_whale_example.wav b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/right_whale_example.wav new file mode 100644 index 00000000..4d54da75 Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Generic/right_whale/right_whale_example.wav differ diff --git a/src/test/resources/rawDeepLearningClassifier/Ketos/hallo-kw-det_v1/detections_no_overlap.csv b/src/test/resources/rawDeepLearningClassifier/Ketos/hallo-kw-det_v1/detections_no_overlap.csv new file mode 100644 index 00000000..be0cc920 --- /dev/null +++ b/src/test/resources/rawDeepLearningClassifier/Ketos/hallo-kw-det_v1/detections_no_overlap.csv @@ -0,0 +1,20 @@ +filename,start,duration,score +jasco_reduced.wav,0.0,5.0176,0.1565524 +jasco_reduced.wav,5.0,5.0176,0.99999917 +jasco_reduced.wav,10.0,5.0176,0.99999917 +jasco_reduced.wav,15.0,5.0176,0.97594243 +jasco_reduced.wav,20.0,5.0176,0.8802458 +jasco_reduced.wav,25.0,5.0176,0.9999999 +jasco_reduced.wav,30.0,5.0176,0.9999932 +jasco_reduced.wav,35.0,5.0176,0.9998863 +jasco_reduced.wav,40.0,5.0176,0.99998367 +jasco_reduced.wav,45.0,5.0176,0.21531366 +jasco_reduced.wav,50.0,5.0176,0.9999987 +jasco_reduced.wav,55.0,5.0176,1.0 +jasco_reduced.wav,60.0,5.0176,0.9999989 +jasco_reduced.wav,65.0,5.0176,0.9999993 +jasco_reduced.wav,70.0,5.0176,0.99999845 +jasco_reduced.wav,75.0,5.0176,1.0 +jasco_reduced.wav,80.0,5.0176,0.20126265 +jasco_reduced.wav,85.0,5.0176,0.9797412 +jasco_reduced.wav,90.0,5.0176,1.0 diff --git a/src/test/resources/rawDeepLearningClassifier/Ketos/hallo-kw-det_v1/hallo-kw-det_v1.ktpb b/src/test/resources/rawDeepLearningClassifier/Ketos/hallo-kw-det_v1/hallo-kw-det_v1.ktpb new file mode 100644 index 00000000..a184ba54 Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Ketos/hallo-kw-det_v1/hallo-kw-det_v1.ktpb differ diff --git a/src/test/resources/rawDeepLearningClassifier/Ketos/hallo-kw-det_v1/jasco_reduced.wav b/src/test/resources/rawDeepLearningClassifier/Ketos/hallo-kw-det_v1/jasco_reduced.wav new file mode 100644 index 00000000..a7c4b5c4 Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Ketos/hallo-kw-det_v1/jasco_reduced.wav differ diff --git a/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/20190527_190000.wav b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/20190527_190000.wav new file mode 100644 index 00000000..b4770238 Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/20190527_190000.wav differ diff --git a/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/ReadMe.md.txt b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/ReadMe.md.txt new file mode 100644 index 00000000..4a2f2189 --- /dev/null +++ b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/ReadMe.md.txt @@ -0,0 +1,5 @@ +# Blue whale Koogu models. + +There are two versions of blue whale models BmD_24_tf_model and BmD_23_tf_model in standard Tensorflow saved_.pb format. + +There is also a blue_whale_24.kgu model which is the standard PAMGaurd compatible format for koogu models. This contains settings to set up the model automtically. \ No newline at end of file diff --git a/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/blue_whale_24.kgu b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/blue_whale_24.kgu new file mode 100644 index 00000000..1265875f Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/blue_whale_24.kgu differ diff --git a/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/rawScores_20190527_190000.csv b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/rawScores_20190527_190000.csv new file mode 100644 index 00000000..cfb4724e --- /dev/null +++ b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/rawScores_20190527_190000.csv @@ -0,0 +1,1798 @@ +0.000000000000000000e+00,5.193827673792839050e-02,9.480617642402648926e-01 +5.000000000000000000e+02,3.835381241515278816e-03,9.961646795272827148e-01 +1.000000000000000000e+03,2.028739452362060547e-02,9.797126054763793945e-01 +1.500000000000000000e+03,1.735078287310898304e-03,9.982649683952331543e-01 +2.000000000000000000e+03,1.776782050728797913e-02,9.822322130203247070e-01 +2.500000000000000000e+03,5.662455223500728607e-03,9.943374991416931152e-01 +3.000000000000000000e+03,5.498182028532028198e-03,9.945018291473388672e-01 +3.500000000000000000e+03,8.968043923377990723e-01,1.031956449151039124e-01 +4.000000000000000000e+03,1.809033192694187164e-02,9.819096326828002930e-01 +4.500000000000000000e+03,5.350030260160565376e-04,9.994650483131408691e-01 +5.000000000000000000e+03,1.721951557556167245e-04,9.998277425765991211e-01 +5.500000000000000000e+03,1.795023563317954540e-03,9.982049465179443359e-01 +6.000000000000000000e+03,7.606656436109915376e-05,9.999239444732666016e-01 +6.500000000000000000e+03,6.214079330675303936e-04,9.993785619735717773e-01 +7.000000000000000000e+03,3.204601671313866973e-05,9.999679327011108398e-01 +7.500000000000000000e+03,9.551703114993870258e-04,9.990448355674743652e-01 +8.000000000000000000e+03,1.878460519947111607e-03,9.981215596199035645e-01 +8.500000000000000000e+03,4.259951412677764893e-03,9.957400560379028320e-01 +9.000000000000000000e+03,4.332339740358293056e-04,9.995667338371276855e-01 +9.500000000000000000e+03,9.989676764234900475e-04,9.990010857582092285e-01 +1.000000000000000000e+04,9.432184015167877078e-05,9.999057054519653320e-01 +1.050000000000000000e+04,5.027814768254756927e-04,9.994971752166748047e-01 +1.100000000000000000e+04,7.552343886345624924e-04,9.992448091506958008e-01 +1.150000000000000000e+04,6.083136796951293945e-02,9.391686320304870605e-01 +1.200000000000000000e+04,1.754007098497822881e-04,9.998245835304260254e-01 +1.250000000000000000e+04,5.293995491228997707e-04,9.994706511497497559e-01 +1.300000000000000000e+04,6.138727664947509766e-01,3.861272931098937988e-01 +1.350000000000000000e+04,6.617751903831958771e-03,9.933822154998779297e-01 +1.400000000000000000e+04,5.830759182572364807e-04,9.994169473648071289e-01 +1.450000000000000000e+04,3.248939465265721083e-04,9.996751546859741211e-01 +1.500000000000000000e+04,5.485379602760076523e-03,9.945147037506103516e-01 +1.550000000000000000e+04,2.009915333474054933e-04,9.997990727424621582e-01 +1.600000000000000000e+04,1.963311806321144104e-03,9.980366826057434082e-01 +1.650000000000000000e+04,3.677869448438286781e-03,9.963221549987792969e-01 +1.700000000000000000e+04,2.805884869303554296e-04,9.997194409370422363e-01 +1.750000000000000000e+04,3.867996274493634701e-04,9.996132254600524902e-01 +1.800000000000000000e+04,1.938982307910919189e-02,9.806101322174072266e-01 +1.850000000000000000e+04,2.660461366176605225e-01,7.339538335800170898e-01 +1.900000000000000000e+04,4.061509571329224855e-06,9.999959468841552734e-01 +1.950000000000000000e+04,3.832910151686519384e-04,9.996166229248046875e-01 +2.000000000000000000e+04,9.649378247559070587e-03,9.903505444526672363e-01 +2.050000000000000000e+04,3.297805488109588623e-01,6.702194809913635254e-01 +2.100000000000000000e+04,3.010334670543670654e-01,6.989665031433105469e-01 +2.150000000000000000e+04,3.390581332496367395e-05,9.999661445617675781e-01 +2.200000000000000000e+04,2.993459347635507584e-04,9.997006654739379883e-01 +2.250000000000000000e+04,1.635091751813888550e-02,9.836491346359252930e-01 +2.300000000000000000e+04,1.991654717130586505e-04,9.998008608818054199e-01 +2.350000000000000000e+04,4.963197279721498489e-04,9.995037317276000977e-01 +2.400000000000000000e+04,1.911420113174244761e-04,9.998088479042053223e-01 +2.450000000000000000e+04,9.569693065714091063e-05,9.999042749404907227e-01 +2.500000000000000000e+04,2.452740445733070374e-04,9.997547268867492676e-01 +2.550000000000000000e+04,6.449790816986933351e-05,9.999355077743530273e-01 +2.600000000000000000e+04,9.990211725234985352e-01,9.787887101992964745e-04 +2.650000000000000000e+04,9.999612569808959961e-01,3.880098302033729851e-05 +2.700000000000000000e+04,1.885914476588368416e-03,9.981141090393066406e-01 +2.750000000000000000e+04,9.844623804092407227e-01,1.553760748356580734e-02 +2.800000000000000000e+04,9.978924393653869629e-01,2.107557607814669609e-03 +2.850000000000000000e+04,2.841729903593659401e-03,9.971582889556884766e-01 +2.900000000000000000e+04,1.052950844168663025e-01,8.947048783302307129e-01 +2.950000000000000000e+04,3.529099922161549330e-04,9.996471405029296875e-01 +3.000000000000000000e+04,4.631376214092597365e-06,9.999953508377075195e-01 +3.050000000000000000e+04,3.284719696239335462e-07,9.999996423721313477e-01 +3.100000000000000000e+04,2.709065447561442852e-04,9.997290968894958496e-01 +3.150000000000000000e+04,1.561498851515352726e-03,9.984385371208190918e-01 +3.200000000000000000e+04,2.453411980241071433e-05,9.999754428863525391e-01 +3.250000000000000000e+04,8.419973892159759998e-04,9.991580247879028320e-01 +3.300000000000000000e+04,2.606973566798842512e-06,9.999973773956298828e-01 +3.350000000000000000e+04,2.828014548867940903e-04,9.997171759605407715e-01 +3.400000000000000000e+04,2.027954178629443049e-05,9.999797344207763672e-01 +3.450000000000000000e+04,6.792444619350135326e-04,9.993207454681396484e-01 +3.500000000000000000e+04,5.260556936264038086e-04,9.994739890098571777e-01 +3.550000000000000000e+04,7.813695265213027596e-05,9.999217987060546875e-01 +3.600000000000000000e+04,7.514603785239160061e-04,9.992485642433166504e-01 +3.650000000000000000e+04,1.099482877179980278e-03,9.989005327224731445e-01 +3.700000000000000000e+04,4.218474496155977249e-04,9.995781779289245605e-01 +3.750000000000000000e+04,3.285778802819550037e-05,9.999670982360839844e-01 +3.800000000000000000e+04,1.890823990106582642e-02,9.810917377471923828e-01 +3.850000000000000000e+04,6.573823094367980957e-02,9.342617988586425781e-01 +3.900000000000000000e+04,7.048679253784939647e-05,9.999295473098754883e-01 +3.950000000000000000e+04,9.562879567965865135e-04,9.990436434745788574e-01 +4.000000000000000000e+04,8.081613600552373100e-08,9.999998807907104492e-01 +4.050000000000000000e+04,4.859699693042784929e-04,9.995139837265014648e-01 +4.100000000000000000e+04,3.359363472554832697e-04,9.996640682220458984e-01 +4.150000000000000000e+04,6.333794444799423218e-04,9.993665814399719238e-01 +4.200000000000000000e+04,5.036517541157081723e-05,9.999495744705200195e-01 +4.250000000000000000e+04,6.810786435380578041e-04,9.993189573287963867e-01 +4.300000000000000000e+04,3.196616307832300663e-04,9.996803998947143555e-01 +4.350000000000000000e+04,1.031387597322463989e-03,9.989686012268066406e-01 +4.400000000000000000e+04,3.536416334100067616e-04,9.996463060379028320e-01 +4.450000000000000000e+04,2.714316069614142179e-04,9.997285008430480957e-01 +4.500000000000000000e+04,5.037084338255226612e-05,9.999495744705200195e-01 +4.550000000000000000e+04,2.042202831944450736e-04,9.997958540916442871e-01 +4.600000000000000000e+04,2.222418988822028041e-04,9.997777342796325684e-01 +4.650000000000000000e+04,2.673578856047242880e-04,9.997326731681823730e-01 +4.700000000000000000e+04,4.226464370731264353e-04,9.995773434638977051e-01 +4.750000000000000000e+04,7.799820159561932087e-04,9.992200136184692383e-01 +4.800000000000000000e+04,9.251679875887930393e-04,9.990748167037963867e-01 +4.850000000000000000e+04,7.181509281508624554e-04,9.992818236351013184e-01 +4.900000000000000000e+04,1.184290498495101929e-01,8.815709948539733887e-01 +4.950000000000000000e+04,1.089882352971471846e-04,9.998910427093505859e-01 +5.000000000000000000e+04,9.986404776573181152e-01,1.359469373710453510e-03 +5.050000000000000000e+04,9.999320507049560547e-01,6.800162373110651970e-05 +5.100000000000000000e+04,1.301611587405204773e-02,9.869838953018188477e-01 +5.150000000000000000e+04,1.149401888251304626e-01,8.850598335266113281e-01 +5.200000000000000000e+04,2.088384935632348061e-04,9.997912049293518066e-01 +5.250000000000000000e+04,6.615508027607575059e-05,9.999338388442993164e-01 +5.300000000000000000e+04,2.405644190730527043e-04,9.997594952583312988e-01 +5.350000000000000000e+04,3.161463420838117599e-03,9.968385696411132812e-01 +5.400000000000000000e+04,9.968839287757873535e-01,3.116026055067777634e-03 +5.450000000000000000e+04,9.845912456512451172e-01,1.540880184620618820e-02 +5.500000000000000000e+04,2.414918038994073868e-03,9.975850582122802734e-01 +5.550000000000000000e+04,8.289563469588756561e-03,9.917104840278625488e-01 +5.600000000000000000e+04,3.433938603848218918e-03,9.965661168098449707e-01 +5.650000000000000000e+04,9.210689924657344818e-03,9.907893538475036621e-01 +5.700000000000000000e+04,1.703783753328025341e-03,9.982962012290954590e-01 +5.750000000000000000e+04,4.067900008521974087e-04,9.995931982994079590e-01 +5.800000000000000000e+04,1.280780124943703413e-04,9.998718500137329102e-01 +5.850000000000000000e+04,9.412337094545364380e-02,9.058766365051269531e-01 +5.900000000000000000e+04,7.471109926700592041e-02,9.252889156341552734e-01 +5.950000000000000000e+04,2.750398358330130577e-03,9.972495436668395996e-01 +6.000000000000000000e+04,2.249133735895156860e-01,7.750865817070007324e-01 +6.050000000000000000e+04,1.452418160624802113e-03,9.985476136207580566e-01 +6.100000000000000000e+04,1.537399512017145753e-04,9.998462200164794922e-01 +6.150000000000000000e+04,6.937792204553261399e-05,9.999306201934814453e-01 +6.200000000000000000e+04,3.076585708186030388e-04,9.996923208236694336e-01 +6.250000000000000000e+04,9.970730543136596680e-01,2.926968503743410110e-03 +6.300000000000000000e+04,9.999982118606567383e-01,1.808172328310320154e-06 +6.350000000000000000e+04,2.396436631679534912e-01,7.603563666343688965e-01 +6.400000000000000000e+04,1.381861716508865356e-01,8.618138432502746582e-01 +6.450000000000000000e+04,2.051277086138725281e-02,9.794872403144836426e-01 +6.500000000000000000e+04,2.155594294890761375e-03,9.978443384170532227e-01 +6.550000000000000000e+04,5.884863808751106262e-02,9.411513209342956543e-01 +6.600000000000000000e+04,3.542453050613403320e-03,9.964575171470642090e-01 +6.650000000000000000e+04,7.397509762085974216e-04,9.992602467536926270e-01 +6.700000000000000000e+04,7.889118278399109840e-04,9.992110729217529297e-01 +6.750000000000000000e+04,1.459216000512242317e-03,9.985407590866088867e-01 +6.800000000000000000e+04,8.239042945206165314e-03,9.917609691619873047e-01 +6.850000000000000000e+04,7.470087613910436630e-03,9.925299286842346191e-01 +6.900000000000000000e+04,6.500784365925937891e-05,9.999350309371948242e-01 +6.950000000000000000e+04,1.338618574663996696e-03,9.986613988876342773e-01 +7.000000000000000000e+04,1.320236711762845516e-03,9.986796975135803223e-01 +7.050000000000000000e+04,4.135838826186954975e-04,9.995864033699035645e-01 +7.100000000000000000e+04,1.311136729782447219e-04,9.998688697814941406e-01 +7.150000000000000000e+04,9.863986968994140625e-01,1.360123232007026672e-02 +7.200000000000000000e+04,9.972949624061584473e-01,2.704979619011282921e-03 +7.250000000000000000e+04,1.031971350312232971e-01,8.968029022216796875e-01 +7.300000000000000000e+04,9.484044276177883148e-04,9.990516304969787598e-01 +7.350000000000000000e+04,5.934836864471435547e-01,4.065163731575012207e-01 +7.400000000000000000e+04,9.942224621772766113e-01,5.777487065643072128e-03 +7.450000000000000000e+04,9.875575304031372070e-01,1.244247425347566605e-02 +7.500000000000000000e+04,5.972678661346435547e-01,4.027321040630340576e-01 +7.550000000000000000e+04,3.380014968570321798e-04,9.996620416641235352e-01 +7.600000000000000000e+04,3.810041380347684026e-05,9.999618530273437500e-01 +7.650000000000000000e+04,2.966856991406530142e-04,9.997032284736633301e-01 +7.700000000000000000e+04,1.366305979900062084e-03,9.986336827278137207e-01 +7.750000000000000000e+04,2.198271639645099640e-02,9.780173301696777344e-01 +7.800000000000000000e+04,9.340520203113555908e-02,9.065947532653808594e-01 +7.850000000000000000e+04,7.979068323038518429e-04,9.992020726203918457e-01 +7.900000000000000000e+04,1.012332795653492212e-04,9.998987913131713867e-01 +7.950000000000000000e+04,8.471526205539703369e-03,9.915285110473632812e-01 +8.000000000000000000e+04,9.936608374118804932e-02,9.006339311599731445e-01 +8.050000000000000000e+04,1.330330851487815380e-03,9.986697435379028320e-01 +8.100000000000000000e+04,2.402597665786743164e-02,9.759740233421325684e-01 +8.150000000000000000e+04,2.283616231579799205e-05,9.999771118164062500e-01 +8.200000000000000000e+04,3.158355597406625748e-03,9.968416690826416016e-01 +8.250000000000000000e+04,1.779725571395829320e-04,9.998220801353454590e-01 +8.300000000000000000e+04,1.304299512412399054e-04,9.998695850372314453e-01 +8.350000000000000000e+04,9.861518740653991699e-01,1.384810078889131546e-02 +8.400000000000000000e+04,9.999452829360961914e-01,5.475183570524677634e-05 +8.450000000000000000e+04,6.602609753608703613e-01,3.397389650344848633e-01 +8.500000000000000000e+04,9.999945163726806641e-01,5.465788035508012399e-06 +8.550000000000000000e+04,9.998707771301269531e-01,1.291606022277846932e-04 +8.600000000000000000e+04,2.898480743169784546e-03,9.971014857292175293e-01 +8.650000000000000000e+04,7.230988703668117523e-04,9.992769360542297363e-01 +8.700000000000000000e+04,9.615758657455444336e-01,3.842410072684288025e-02 +8.750000000000000000e+04,9.625777602195739746e-01,3.742222115397453308e-02 +8.800000000000000000e+04,1.214352156966924667e-02,9.878565073013305664e-01 +8.850000000000000000e+04,9.999136924743652344e-01,8.633434481453150511e-05 +8.900000000000000000e+04,9.999949932098388672e-01,5.023072844778653234e-06 +8.950000000000000000e+04,9.762700200080871582e-01,2.373000234365463257e-02 +9.000000000000000000e+04,2.129490603692829609e-04,9.997870326042175293e-01 +9.050000000000000000e+04,9.880379438400268555e-01,1.196211390197277069e-02 +9.100000000000000000e+04,6.643359065055847168e-01,3.356640636920928955e-01 +9.150000000000000000e+04,9.704185128211975098e-01,2.958154492080211639e-02 +9.200000000000000000e+04,9.951884746551513672e-01,4.811542574316263199e-03 +9.250000000000000000e+04,9.938104748725891113e-01,6.189572159200906754e-03 +9.300000000000000000e+04,9.990001320838928223e-01,9.999012108892202377e-04 +9.350000000000000000e+04,1.501314062625169754e-02,9.849868416786193848e-01 +9.400000000000000000e+04,1.224855805048719049e-04,9.998774528503417969e-01 +9.450000000000000000e+04,1.979751395992934704e-04,9.998020529747009277e-01 +9.500000000000000000e+04,9.340546966996043921e-05,9.999065399169921875e-01 +9.550000000000000000e+04,9.894397854804992676e-01,1.056020986288785934e-02 +9.600000000000000000e+04,9.999964237213134766e-01,3.629593720688717440e-06 +9.650000000000000000e+04,9.999953508377075195e-01,4.650708433473482728e-06 +9.700000000000000000e+04,9.916265010833740234e-01,8.373457938432693481e-03 +9.750000000000000000e+04,2.535528270527720451e-03,9.974644184112548828e-01 +9.800000000000000000e+04,3.058022446930408478e-02,9.694197177886962891e-01 +9.850000000000000000e+04,1.048963982611894608e-02,9.895104169845581055e-01 +9.900000000000000000e+04,4.951807204633951187e-03,9.950482249259948730e-01 +9.950000000000000000e+04,7.083467789925634861e-04,9.992916584014892578e-01 +1.000000000000000000e+05,6.073914482840336859e-05,9.999392032623291016e-01 +1.005000000000000000e+05,8.982514739036560059e-01,1.017484888434410095e-01 +1.010000000000000000e+05,8.054814934730529785e-01,1.945185363292694092e-01 +1.015000000000000000e+05,5.675726104527711868e-03,9.943243265151977539e-01 +1.020000000000000000e+05,9.999424219131469727e-01,5.758121551480144262e-05 +1.025000000000000000e+05,9.999991655349731445e-01,7.830465733604796696e-07 +1.030000000000000000e+05,9.979948997497558594e-01,2.005131216719746590e-03 +1.035000000000000000e+05,9.686871431767940521e-03,9.903131723403930664e-01 +1.040000000000000000e+05,1.580704152584075928e-01,8.419295549392700195e-01 +1.045000000000000000e+05,9.999994039535522461e-01,5.958082738288794644e-07 +1.050000000000000000e+05,1.000000000000000000e+00,9.250480381695069809e-09 +1.055000000000000000e+05,9.973427653312683105e-01,2.657212549820542336e-03 +1.060000000000000000e+05,1.468468544771894813e-04,9.998531341552734375e-01 +1.065000000000000000e+05,4.018658946733921766e-04,9.995982050895690918e-01 +1.070000000000000000e+05,8.500309922965243459e-05,9.999150037765502930e-01 +1.075000000000000000e+05,1.852979912655428052e-04,9.998146891593933105e-01 +1.080000000000000000e+05,1.201774575747549534e-03,9.987982511520385742e-01 +1.085000000000000000e+05,1.413681544363498688e-02,9.858631491661071777e-01 +1.090000000000000000e+05,1.953510101884603500e-03,9.980465173721313477e-01 +1.095000000000000000e+05,9.998326301574707031e-01,1.673970109550282359e-04 +1.100000000000000000e+05,1.000000000000000000e+00,1.241484581804286336e-09 +1.105000000000000000e+05,9.965049028396606445e-01,3.495117882266640663e-03 +1.110000000000000000e+05,9.999991655349731445e-01,7.955983960528101306e-07 +1.115000000000000000e+05,9.999996423721313477e-01,3.206867233984667109e-07 +1.120000000000000000e+05,9.879641532897949219e-01,1.203585416078567505e-02 +1.125000000000000000e+05,4.036966129206120968e-04,9.995962977409362793e-01 +1.130000000000000000e+05,8.572885417379438877e-04,9.991426467895507812e-01 +1.135000000000000000e+05,2.131306100636720657e-03,9.978686571121215820e-01 +1.140000000000000000e+05,7.373112021014094353e-04,9.992626309394836426e-01 +1.145000000000000000e+05,6.111404509283602238e-04,9.993888139724731445e-01 +1.150000000000000000e+05,2.117867320775985718e-01,7.882131934165954590e-01 +1.155000000000000000e+05,3.119233436882495880e-02,9.688076376914978027e-01 +1.160000000000000000e+05,9.998724460601806641e-01,1.274945971090346575e-04 +1.165000000000000000e+05,9.995058774948120117e-01,4.941578954458236694e-04 +1.170000000000000000e+05,3.063041018322110176e-03,9.969369173049926758e-01 +1.175000000000000000e+05,1.751106174197047949e-04,9.998249411582946777e-01 +1.180000000000000000e+05,1.117775216698646545e-03,9.988822340965270996e-01 +1.185000000000000000e+05,5.241601229499792680e-06,9.999947547912597656e-01 +1.190000000000000000e+05,8.532915671821683645e-06,9.999914169311523438e-01 +1.195000000000000000e+05,1.845408842200413346e-04,9.998155236244201660e-01 +1.200000000000000000e+05,5.358408088795840740e-04,9.994642138481140137e-01 +1.205000000000000000e+05,3.930488973855972290e-02,9.606950879096984863e-01 +1.210000000000000000e+05,8.027505129575729370e-02,9.197249412536621094e-01 +1.215000000000000000e+05,3.383037168532609940e-03,9.966170191764831543e-01 +1.220000000000000000e+05,9.999978542327880859e-01,2.086825134028913453e-06 +1.225000000000000000e+05,1.000000000000000000e+00,8.594932404681898674e-11 +1.230000000000000000e+05,9.999799728393554688e-01,2.005163878493476659e-05 +1.235000000000000000e+05,9.820980429649353027e-01,1.790202222764492035e-02 +1.240000000000000000e+05,9.196645114570856094e-04,9.990803003311157227e-01 +1.245000000000000000e+05,2.446934922772925347e-05,9.999755620956420898e-01 +1.250000000000000000e+05,9.923347830772399902e-01,7.665141019970178604e-03 +1.255000000000000000e+05,9.999871253967285156e-01,1.287792565562995151e-05 +1.260000000000000000e+05,3.473562002182006836e-02,9.652643799781799316e-01 +1.265000000000000000e+05,9.876000881195068359e-01,1.239996775984764099e-02 +1.270000000000000000e+05,9.999901056289672852e-01,9.893161404761485755e-06 +1.275000000000000000e+05,9.996494054794311523e-01,3.506100329104810953e-04 +1.280000000000000000e+05,9.974570870399475098e-01,2.542970469221472740e-03 +1.285000000000000000e+05,9.954082369804382324e-01,4.591718781739473343e-03 +1.290000000000000000e+05,4.057663900312036276e-04,9.995942711830139160e-01 +1.295000000000000000e+05,9.949475061148405075e-04,9.990050196647644043e-01 +1.300000000000000000e+05,2.994159162044525146e-01,7.005841135978698730e-01 +1.305000000000000000e+05,9.999808073043823242e-01,1.919202986755408347e-05 +1.310000000000000000e+05,9.999998807907104492e-01,1.492692547344631748e-07 +1.315000000000000000e+05,9.890239238739013672e-01,1.097614597529172897e-02 +1.320000000000000000e+05,9.999866485595703125e-01,1.331891871814150363e-05 +1.325000000000000000e+05,9.999998807907104492e-01,1.498889190543195582e-07 +1.330000000000000000e+05,9.984807372093200684e-01,1.519303652457892895e-03 +1.335000000000000000e+05,2.036523222923278809e-01,7.963476777076721191e-01 +1.340000000000000000e+05,1.449721679091453552e-02,9.855028390884399414e-01 +1.345000000000000000e+05,1.157810911536216736e-01,8.842189311981201172e-01 +1.350000000000000000e+05,9.999947547912597656e-01,5.299362783262040466e-06 +1.355000000000000000e+05,1.000000000000000000e+00,7.022221693020469502e-11 +1.360000000000000000e+05,9.999879598617553711e-01,1.202046496473485604e-05 +1.365000000000000000e+05,9.816764593124389648e-01,1.832356117665767670e-02 +1.370000000000000000e+05,2.219074964523315430e-02,9.778092503547668457e-01 +1.375000000000000000e+05,6.334364414215087891e-02,9.366563558578491211e-01 +1.380000000000000000e+05,5.728363757953047752e-04,9.994271993637084961e-01 +1.385000000000000000e+05,2.406235318630933762e-03,9.975937008857727051e-01 +1.390000000000000000e+05,7.280509918928146362e-02,9.271949529647827148e-01 +1.395000000000000000e+05,9.999659061431884766e-01,3.408603879506699741e-05 +1.400000000000000000e+05,9.999347925186157227e-01,6.518841837532818317e-05 +1.405000000000000000e+05,9.384301304817199707e-01,6.156991794705390930e-02 +1.410000000000000000e+05,8.653615950606763363e-04,9.991347193717956543e-01 +1.415000000000000000e+05,9.994366765022277832e-01,5.632754764519631863e-04 +1.420000000000000000e+05,9.995546936988830566e-01,4.453393921721726656e-04 +1.425000000000000000e+05,8.345201015472412109e-01,1.654798537492752075e-01 +1.430000000000000000e+05,1.351582631468772888e-02,9.864842295646667480e-01 +1.435000000000000000e+05,9.720509842736646533e-05,9.999028444290161133e-01 +1.440000000000000000e+05,9.965374469757080078e-01,3.462531603872776031e-03 +1.445000000000000000e+05,9.999995231628417969e-01,4.860782496507454198e-07 +1.450000000000000000e+05,9.999982118606567383e-01,1.833246756177686621e-06 +1.455000000000000000e+05,9.999266862869262695e-01,7.330256630666553974e-05 +1.460000000000000000e+05,6.029192209243774414e-01,3.970807790756225586e-01 +1.465000000000000000e+05,1.419564709067344666e-03,9.985804557800292969e-01 +1.470000000000000000e+05,8.343333378434181213e-03,9.916566610336303711e-01 +1.475000000000000000e+05,2.198146581649780273e-01,7.801853418350219727e-01 +1.480000000000000000e+05,8.691595867276191711e-03,9.913083910942077637e-01 +1.485000000000000000e+05,2.239620126783847809e-02,9.776037931442260742e-01 +1.490000000000000000e+05,2.113621485477779061e-05,9.999788999557495117e-01 +1.495000000000000000e+05,1.857085619121789932e-03,9.981429576873779297e-01 +1.500000000000000000e+05,3.355794469825923443e-04,9.996644258499145508e-01 +1.505000000000000000e+05,4.946722765453159809e-04,9.995052814483642578e-01 +1.510000000000000000e+05,9.997287392616271973e-01,2.713002322707325220e-04 +1.515000000000000000e+05,9.999991655349731445e-01,8.088859999588748906e-07 +1.520000000000000000e+05,9.853377342224121094e-01,1.466225087642669678e-02 +1.525000000000000000e+05,9.998893737792968750e-01,1.106664567487314343e-04 +1.530000000000000000e+05,5.217574238777160645e-01,4.782425463199615479e-01 +1.535000000000000000e+05,1.736067831516265869e-01,8.263932466506958008e-01 +1.540000000000000000e+05,7.063688244670629501e-03,9.929363131523132324e-01 +1.545000000000000000e+05,2.905501285567879677e-03,9.970945119857788086e-01 +1.550000000000000000e+05,9.609861969947814941e-01,3.901381045579910278e-02 +1.555000000000000000e+05,9.998676776885986328e-01,1.322942844126373529e-04 +1.560000000000000000e+05,9.985877275466918945e-01,1.412322279065847397e-03 +1.565000000000000000e+05,9.999996423721313477e-01,3.039268676729989238e-07 +1.570000000000000000e+05,9.999990463256835938e-01,9.874921715891105123e-07 +1.575000000000000000e+05,9.997704625129699707e-01,2.294856967637315392e-04 +1.580000000000000000e+05,9.999911785125732422e-01,8.843859177432022989e-06 +1.585000000000000000e+05,9.979946613311767578e-01,2.005361020565032959e-03 +1.590000000000000000e+05,9.810144901275634766e-01,1.898550428450107574e-02 +1.595000000000000000e+05,3.652338054962456226e-04,9.996347427368164062e-01 +1.600000000000000000e+05,1.101277419365942478e-03,9.988987445831298828e-01 +1.605000000000000000e+05,2.942176361102610826e-04,9.997057318687438965e-01 +1.610000000000000000e+05,1.867295950651168823e-01,8.132704496383666992e-01 +1.615000000000000000e+05,1.997096842387691140e-05,9.999799728393554688e-01 +1.620000000000000000e+05,4.347091889940202236e-04,9.995653033256530762e-01 +1.625000000000000000e+05,4.258494591340422630e-04,9.995741248130798340e-01 +1.630000000000000000e+05,5.786507725715637207e-01,4.213491976261138916e-01 +1.635000000000000000e+05,9.999953508377075195e-01,4.694691597251221538e-06 +1.640000000000000000e+05,9.999998807907104492e-01,6.493858251133133308e-08 +1.645000000000000000e+05,9.994238615036010742e-01,5.761112552136182785e-04 +1.650000000000000000e+05,6.174861788749694824e-01,3.825138509273529053e-01 +1.655000000000000000e+05,1.351442537270486355e-03,9.986485838890075684e-01 +1.660000000000000000e+05,2.979053242597728968e-04,9.997020363807678223e-01 +1.665000000000000000e+05,3.100437223911285400e-01,6.899562478065490723e-01 +1.670000000000000000e+05,8.763132095336914062e-01,1.236867904663085938e-01 +1.675000000000000000e+05,1.035575496644014493e-05,9.999896287918090820e-01 +1.680000000000000000e+05,1.274652895517647266e-03,9.987253546714782715e-01 +1.685000000000000000e+05,1.275995600735768676e-04,9.998723268508911133e-01 +1.690000000000000000e+05,9.950533509254455566e-01,4.946650471538305283e-03 +1.695000000000000000e+05,9.995934367179870605e-01,4.065202083438634872e-04 +1.700000000000000000e+05,1.725058443844318390e-02,9.827493429183959961e-01 +1.705000000000000000e+05,9.999779462814331055e-01,2.207912984886206686e-05 +1.710000000000000000e+05,9.999998807907104492e-01,1.092556232151764561e-07 +1.715000000000000000e+05,9.992747902870178223e-01,7.252380019053816795e-04 +1.720000000000000000e+05,4.556880295276641846e-01,5.443119406700134277e-01 +1.725000000000000000e+05,1.450070762075483799e-03,9.985498785972595215e-01 +1.730000000000000000e+05,1.372448023175820708e-04,9.998627901077270508e-01 +1.735000000000000000e+05,1.883285949588753283e-05,9.999811649322509766e-01 +1.740000000000000000e+05,6.228178972378373146e-04,9.993771910667419434e-01 +1.745000000000000000e+05,1.012817607261240482e-03,9.989871382713317871e-01 +1.750000000000000000e+05,9.110833052545785904e-04,9.990888833999633789e-01 +1.755000000000000000e+05,2.161749216611497104e-05,9.999784231185913086e-01 +1.760000000000000000e+05,1.047956743605027441e-06,9.999989271163940430e-01 +1.765000000000000000e+05,7.697667460888624191e-04,9.992302656173706055e-01 +1.770000000000000000e+05,9.911064058542251587e-02,9.008893966674804688e-01 +1.775000000000000000e+05,8.036985673243179917e-05,9.999196529388427734e-01 +1.780000000000000000e+05,2.779085189104080200e-04,9.997220635414123535e-01 +1.785000000000000000e+05,6.236405461095273495e-04,9.993763566017150879e-01 +1.790000000000000000e+05,1.698997948551550508e-04,9.998301267623901367e-01 +1.795000000000000000e+05,7.342267053900286555e-06,9.999926090240478516e-01 +1.800000000000000000e+05,5.819674115628004074e-04,9.994180202484130859e-01 +1.805000000000000000e+05,1.730947842588648200e-04,9.998269677162170410e-01 +1.810000000000000000e+05,4.520561778917908669e-04,9.995478987693786621e-01 +1.815000000000000000e+05,3.883131779730319977e-03,9.961168766021728516e-01 +1.820000000000000000e+05,4.515092587098479271e-04,9.995484948158264160e-01 +1.825000000000000000e+05,9.867030382156372070e-01,1.329697296023368835e-02 +1.830000000000000000e+05,9.999996423721313477e-01,3.351118778027739609e-07 +1.835000000000000000e+05,9.999473094940185547e-01,5.271693589747883379e-05 +1.840000000000000000e+05,1.874302923679351807e-01,8.125696778297424316e-01 +1.845000000000000000e+05,2.576909027993679047e-02,9.742308855056762695e-01 +1.850000000000000000e+05,3.326825797557830811e-02,9.667317867279052734e-01 +1.855000000000000000e+05,1.048544887453317642e-02,9.895145893096923828e-01 +1.860000000000000000e+05,1.472125062718987465e-03,9.985278844833374023e-01 +1.865000000000000000e+05,2.484502270817756653e-03,9.975155591964721680e-01 +1.870000000000000000e+05,7.763672620058059692e-03,9.922363162040710449e-01 +1.875000000000000000e+05,6.340502295643091202e-04,9.993659853935241699e-01 +1.880000000000000000e+05,6.094135096645914018e-05,9.999390840530395508e-01 +1.885000000000000000e+05,2.796854590997099876e-03,9.972031116485595703e-01 +1.890000000000000000e+05,5.136788822710514069e-03,9.948632121086120605e-01 +1.895000000000000000e+05,1.279757823795080185e-03,9.987202882766723633e-01 +1.900000000000000000e+05,2.917010860983282328e-04,9.997082352638244629e-01 +1.905000000000000000e+05,4.050888528581708670e-04,9.995948672294616699e-01 +1.910000000000000000e+05,7.081071089487522840e-05,9.999291896820068359e-01 +1.915000000000000000e+05,4.096246557310223579e-04,9.995903372764587402e-01 +1.920000000000000000e+05,4.790408129338175058e-04,9.995210170745849609e-01 +1.925000000000000000e+05,7.047785766189917922e-05,9.999295473098754883e-01 +1.930000000000000000e+05,1.192584022646769881e-04,9.998806715011596680e-01 +1.935000000000000000e+05,1.575261703692376614e-03,9.984247684478759766e-01 +1.940000000000000000e+05,9.969437718391418457e-01,3.056222572922706604e-03 +1.945000000000000000e+05,9.997361302375793457e-01,2.638071600813418627e-04 +1.950000000000000000e+05,9.144326448440551758e-01,8.556735515594482422e-02 +1.955000000000000000e+05,1.102217435836791992e-01,8.897782564163208008e-01 +1.960000000000000000e+05,1.866328902542591095e-02,9.813367724418640137e-01 +1.965000000000000000e+05,7.796094287186861038e-03,9.922039508819580078e-01 +1.970000000000000000e+05,9.999806880950927734e-01,1.935213003889657557e-05 +1.975000000000000000e+05,9.991282820701599121e-01,8.716728771105408669e-04 +1.980000000000000000e+05,9.273317456245422363e-01,7.266826182603836060e-02 +1.985000000000000000e+05,2.377928030909970403e-04,9.997622370719909668e-01 +1.990000000000000000e+05,1.277113333344459534e-04,9.998723268508911133e-01 +1.995000000000000000e+05,2.431037602946162224e-03,9.975689053535461426e-01 +2.000000000000000000e+05,6.820268463343381882e-03,9.931797981262207031e-01 +2.005000000000000000e+05,1.161779975518584251e-03,9.988382458686828613e-01 +2.010000000000000000e+05,2.007446484640240669e-03,9.979925155639648438e-01 +2.015000000000000000e+05,1.584369922056794167e-03,9.984155893325805664e-01 +2.020000000000000000e+05,5.816434510052204132e-03,9.941835999488830566e-01 +2.025000000000000000e+05,1.386332958936691284e-01,8.613666892051696777e-01 +2.030000000000000000e+05,5.827385932207107544e-02,9.417260885238647461e-01 +2.035000000000000000e+05,4.329413175582885742e-02,9.567059278488159180e-01 +2.040000000000000000e+05,1.913818268803879619e-04,9.998086094856262207e-01 +2.045000000000000000e+05,7.986491254996508360e-06,9.999920129776000977e-01 +2.050000000000000000e+05,6.253148312680423260e-04,9.993746876716613770e-01 +2.055000000000000000e+05,1.514401083113625646e-04,9.998484849929809570e-01 +2.060000000000000000e+05,3.953779488801956177e-02,9.604622125625610352e-01 +2.065000000000000000e+05,3.060322627425193787e-02,9.693967700004577637e-01 +2.070000000000000000e+05,5.092104765935800970e-05,9.999490976333618164e-01 +2.075000000000000000e+05,3.215990145690739155e-04,9.996783733367919922e-01 +2.080000000000000000e+05,9.750652313232421875e-01,2.493472769856452942e-02 +2.085000000000000000e+05,9.999841451644897461e-01,1.584182427905034274e-05 +2.090000000000000000e+05,9.968166947364807129e-01,3.183305263519287109e-03 +2.095000000000000000e+05,3.658226458355784416e-03,9.963417649269104004e-01 +2.100000000000000000e+05,2.476644003763794899e-03,9.975233674049377441e-01 +2.105000000000000000e+05,6.742317676544189453e-01,3.257682919502258301e-01 +2.110000000000000000e+05,6.359861493110656738e-01,3.640138804912567139e-01 +2.115000000000000000e+05,1.060943905031308532e-04,9.998939037322998047e-01 +2.120000000000000000e+05,1.516762040409957990e-05,9.999848604202270508e-01 +2.125000000000000000e+05,2.873089979402720928e-04,9.997126460075378418e-01 +2.130000000000000000e+05,1.553793670609593391e-03,9.984462857246398926e-01 +2.135000000000000000e+05,5.018242518417537212e-05,9.999498128890991211e-01 +2.140000000000000000e+05,2.928854373749345541e-04,9.997070431709289551e-01 +2.145000000000000000e+05,7.641569827683269978e-04,9.992358684539794922e-01 +2.150000000000000000e+05,1.314996331930160522e-01,8.685003519058227539e-01 +2.155000000000000000e+05,7.702629773120861501e-06,9.999922513961791992e-01 +2.160000000000000000e+05,4.742567543871700764e-04,9.995257854461669922e-01 +2.165000000000000000e+05,1.960577355930581689e-04,9.998039603233337402e-01 +2.170000000000000000e+05,1.727019262034446001e-04,9.998273253440856934e-01 +2.175000000000000000e+05,2.616879646666347980e-05,9.999737739562988281e-01 +2.180000000000000000e+05,2.772122513761132723e-07,9.999997615814208984e-01 +2.185000000000000000e+05,5.205502020544372499e-05,9.999479055404663086e-01 +2.190000000000000000e+05,5.887888744473457336e-02,9.411211609840393066e-01 +2.195000000000000000e+05,1.549651753157377243e-02,9.845035076141357422e-01 +2.200000000000000000e+05,1.820746983867138624e-04,9.998179078102111816e-01 +2.205000000000000000e+05,4.093985538929700851e-03,9.959059953689575195e-01 +2.210000000000000000e+05,3.686403855681419373e-02,9.631359577178955078e-01 +2.215000000000000000e+05,6.918151974678039551e-01,3.081847727298736572e-01 +2.220000000000000000e+05,1.076306030154228210e-02,9.892370104789733887e-01 +2.225000000000000000e+05,1.315355766564607620e-03,9.986845850944519043e-01 +2.230000000000000000e+05,1.321295974776148796e-03,9.986787438392639160e-01 +2.235000000000000000e+05,4.053341399412602186e-04,9.995946288108825684e-01 +2.240000000000000000e+05,1.867210492491722107e-02,9.813279509544372559e-01 +2.245000000000000000e+05,3.735800040885806084e-03,9.962641596794128418e-01 +2.250000000000000000e+05,2.205838391091674566e-04,9.997794032096862793e-01 +2.255000000000000000e+05,8.405421249335631728e-05,9.999159574508666992e-01 +2.260000000000000000e+05,1.135007012635469437e-03,9.988650083541870117e-01 +2.265000000000000000e+05,2.393712848424911499e-01,7.606287598609924316e-01 +2.270000000000000000e+05,2.224475145339965820e-02,9.777552485466003418e-01 +2.275000000000000000e+05,1.828308566473424435e-03,9.981716871261596680e-01 +2.280000000000000000e+05,1.788193942047655582e-03,9.982118606567382812e-01 +2.285000000000000000e+05,4.997711948817595840e-05,9.999500513076782227e-01 +2.290000000000000000e+05,1.928081386722624302e-03,9.980719089508056641e-01 +2.295000000000000000e+05,4.047989787068217993e-04,9.995952248573303223e-01 +2.300000000000000000e+05,3.853885573334991932e-05,9.999614953994750977e-01 +2.305000000000000000e+05,2.268542448291555047e-04,9.997732043266296387e-01 +2.310000000000000000e+05,3.720957040786743164e-01,6.279042959213256836e-01 +2.315000000000000000e+05,1.789928064681589603e-04,9.998210072517395020e-01 +2.320000000000000000e+05,1.862834542989730835e-01,8.137165307998657227e-01 +2.325000000000000000e+05,9.996612071990966797e-01,3.388261247891932726e-04 +2.330000000000000000e+05,5.177778005599975586e-01,4.822221696376800537e-01 +2.335000000000000000e+05,9.331261515617370605e-01,6.687386333942413330e-02 +2.340000000000000000e+05,9.254751801490783691e-01,7.452482730150222778e-02 +2.345000000000000000e+05,2.902173902839422226e-03,9.970978498458862305e-01 +2.350000000000000000e+05,9.999117851257324219e-01,8.821917435852810740e-05 +2.355000000000000000e+05,9.999998807907104492e-01,9.250235422086916515e-08 +2.360000000000000000e+05,9.986792206764221191e-01,1.320782699622213840e-03 +2.365000000000000000e+05,9.939495325088500977e-01,6.050518248230218887e-03 +2.370000000000000000e+05,9.999493360519409180e-01,5.068897735327482224e-05 +2.375000000000000000e+05,9.992782473564147949e-01,7.217302336357533932e-04 +2.380000000000000000e+05,5.250111818313598633e-01,4.749888181686401367e-01 +2.385000000000000000e+05,3.916951827704906464e-03,9.960830211639404297e-01 +2.390000000000000000e+05,1.669086399488151073e-03,9.983308911323547363e-01 +2.395000000000000000e+05,9.997715242207050323e-04,9.990002512931823730e-01 +2.400000000000000000e+05,4.619789542630314827e-04,9.995380640029907227e-01 +2.405000000000000000e+05,8.288820390589535236e-04,9.991711378097534180e-01 +2.410000000000000000e+05,1.728657167404890060e-03,9.982713460922241211e-01 +2.415000000000000000e+05,4.148099943995475769e-02,9.585189223289489746e-01 +2.420000000000000000e+05,2.239372115582227707e-03,9.977606534957885742e-01 +2.425000000000000000e+05,5.548630952835083008e-01,4.451369345188140869e-01 +2.430000000000000000e+05,9.999172687530517578e-01,8.276404696516692638e-05 +2.435000000000000000e+05,8.976599574089050293e-01,1.023400574922561646e-01 +2.440000000000000000e+05,6.595925660803914070e-04,9.993403553962707520e-01 +2.445000000000000000e+05,6.651445478200912476e-02,9.334855079650878906e-01 +2.450000000000000000e+05,9.999513626098632812e-01,4.860738408751785755e-05 +2.455000000000000000e+05,9.999985694885253906e-01,1.425531763743492775e-06 +2.460000000000000000e+05,9.960057139396667480e-01,3.994326572865247726e-03 +2.465000000000000000e+05,1.859697222243994474e-04,9.998140931129455566e-01 +2.470000000000000000e+05,1.349551894236356020e-04,9.998650550842285156e-01 +2.475000000000000000e+05,3.145537993987090886e-05,9.999685287475585938e-01 +2.480000000000000000e+05,4.449774278327822685e-04,9.995550513267517090e-01 +2.485000000000000000e+05,3.654046158771961927e-04,9.996346235275268555e-01 +2.490000000000000000e+05,8.318212167068850249e-06,9.999916553497314453e-01 +2.495000000000000000e+05,8.509228791808709502e-06,9.999915361404418945e-01 +2.500000000000000000e+05,1.161351799964904785e-03,9.988387227058410645e-01 +2.505000000000000000e+05,9.063485860824584961e-01,9.365133941173553467e-02 +2.510000000000000000e+05,9.997079968452453613e-01,2.920336264651268721e-04 +2.515000000000000000e+05,9.970876574516296387e-01,2.912370720878243446e-03 +2.520000000000000000e+05,9.466332197189331055e-01,5.336679890751838684e-02 +2.525000000000000000e+05,1.489267498254776001e-01,8.510732650756835938e-01 +2.530000000000000000e+05,9.955790638923645020e-01,4.420902114361524582e-03 +2.535000000000000000e+05,9.973256587982177734e-01,2.674282295629382133e-03 +2.540000000000000000e+05,4.957732185721397400e-02,9.504226446151733398e-01 +2.545000000000000000e+05,9.990649819374084473e-01,9.350375039502978325e-04 +2.550000000000000000e+05,9.935197234153747559e-01,6.480233278125524521e-03 +2.555000000000000000e+05,1.036236062645912170e-02,9.896376132965087891e-01 +2.560000000000000000e+05,7.830167305655777454e-04,9.992169141769409180e-01 +2.565000000000000000e+05,3.322500269860029221e-03,9.966775178909301758e-01 +2.570000000000000000e+05,1.716356782708317041e-04,9.998283386230468750e-01 +2.575000000000000000e+05,9.915671944618225098e-01,8.432838134467601776e-03 +2.580000000000000000e+05,9.933025836944580078e-01,6.697423290461301804e-03 +2.585000000000000000e+05,9.848207831382751465e-01,1.517917308956384659e-02 +2.590000000000000000e+05,7.348668575286865234e-01,2.651331126689910889e-01 +2.595000000000000000e+05,6.591568235307931900e-04,9.993408322334289551e-01 +2.600000000000000000e+05,8.310410380363464355e-01,1.689589321613311768e-01 +2.605000000000000000e+05,9.856239557266235352e-01,1.437602378427982330e-02 +2.610000000000000000e+05,1.041605646605603397e-04,9.998958110809326172e-01 +2.615000000000000000e+05,4.887846298515796661e-04,9.995112419128417969e-01 +2.620000000000000000e+05,3.470386436674743891e-04,9.996529817581176758e-01 +2.625000000000000000e+05,6.122273392975330353e-03,9.938777089118957520e-01 +2.630000000000000000e+05,6.443914771080017090e-01,3.556085526943206787e-01 +2.635000000000000000e+05,2.209649886935949326e-03,9.977903366088867188e-01 +2.640000000000000000e+05,4.186466336250305176e-03,9.958135485649108887e-01 +2.645000000000000000e+05,1.324385851621627808e-01,8.675614595413208008e-01 +2.650000000000000000e+05,8.796321926638484001e-04,9.991204142570495605e-01 +2.655000000000000000e+05,2.498913789168000221e-03,9.975010752677917480e-01 +2.660000000000000000e+05,3.259941586293280125e-04,9.996739625930786133e-01 +2.665000000000000000e+05,8.210384403355419636e-04,9.991789460182189941e-01 +2.670000000000000000e+05,9.777341485023498535e-01,2.226580493152141571e-02 +2.675000000000000000e+05,9.994078874588012695e-01,5.921117262914776802e-04 +2.680000000000000000e+05,9.992666840553283691e-01,7.333499379456043243e-04 +2.685000000000000000e+05,2.288944870233535767e-01,7.711055278778076172e-01 +2.690000000000000000e+05,5.795959234237670898e-01,4.204041063785552979e-01 +2.695000000000000000e+05,4.621524829417467117e-03,9.953784942626953125e-01 +2.700000000000000000e+05,9.996374845504760742e-01,3.625221434049308300e-04 +2.705000000000000000e+05,9.749571084976196289e-01,2.504287287592887878e-02 +2.710000000000000000e+05,9.588469862937927246e-01,4.115296900272369385e-02 +2.715000000000000000e+05,9.857069849967956543e-01,1.429297402501106262e-02 +2.720000000000000000e+05,3.986805677413940430e-02,9.601318836212158203e-01 +2.725000000000000000e+05,1.967467740178108215e-02,9.803254008293151855e-01 +2.730000000000000000e+05,4.896698985248804092e-03,9.951032400131225586e-01 +2.735000000000000000e+05,1.925051510334014893e-01,8.074948787689208984e-01 +2.740000000000000000e+05,2.891092002391815186e-02,9.710890650749206543e-01 +2.745000000000000000e+05,2.567817922681570053e-03,9.974321722984313965e-01 +2.750000000000000000e+05,9.244133234024047852e-01,7.558669149875640869e-02 +2.755000000000000000e+05,9.997593760490417480e-01,2.406633138889446855e-04 +2.760000000000000000e+05,9.970735311508178711e-01,2.926410408690571785e-03 +2.765000000000000000e+05,5.968493223190307617e-02,9.403150677680969238e-01 +2.770000000000000000e+05,2.548712538555264473e-03,9.974512457847595215e-01 +2.775000000000000000e+05,1.177114062011241913e-02,9.882287979125976562e-01 +2.780000000000000000e+05,4.638493657112121582e-01,5.361506342887878418e-01 +2.785000000000000000e+05,9.985532164573669434e-01,1.446788781322538853e-03 +2.790000000000000000e+05,9.777031540870666504e-01,2.229687012732028961e-02 +2.795000000000000000e+05,2.629852890968322754e-01,7.370146512985229492e-01 +2.800000000000000000e+05,9.591294452548027039e-03,9.904086589813232422e-01 +2.805000000000000000e+05,2.071970142424106598e-03,9.979280233383178711e-01 +2.810000000000000000e+05,7.111493614502251148e-04,9.992887973785400391e-01 +2.815000000000000000e+05,3.058539237827062607e-03,9.969413876533508301e-01 +2.820000000000000000e+05,3.110624675173312426e-05,9.999688863754272461e-01 +2.825000000000000000e+05,1.020798226818442345e-04,9.998979568481445312e-01 +2.830000000000000000e+05,1.665811141720041633e-04,9.998333454132080078e-01 +2.835000000000000000e+05,9.041076302528381348e-01,9.589239209890365601e-02 +2.840000000000000000e+05,9.999938011169433594e-01,6.196700269356369972e-06 +2.845000000000000000e+05,9.999188184738159180e-01,8.120231359498575330e-05 +2.850000000000000000e+05,9.909515380859375000e-01,9.048423729836940765e-03 +2.855000000000000000e+05,9.861450195312500000e-01,1.385500654578208923e-02 +2.860000000000000000e+05,1.932701445184648037e-03,9.980673193931579590e-01 +2.865000000000000000e+05,9.758118987083435059e-01,2.418805286288261414e-02 +2.870000000000000000e+05,1.846267282962799072e-01,8.153733015060424805e-01 +2.875000000000000000e+05,4.964838735759258270e-04,9.995034933090209961e-01 +2.880000000000000000e+05,1.116523519158363342e-03,9.988834261894226074e-01 +2.885000000000000000e+05,5.901401891605928540e-06,9.999940395355224609e-01 +2.890000000000000000e+05,4.112231254111975431e-04,9.995887875556945801e-01 +2.895000000000000000e+05,1.067255288944579661e-04,9.998933076858520508e-01 +2.900000000000000000e+05,1.158929392695426941e-01,8.841071128845214844e-01 +2.905000000000000000e+05,7.487473487854003906e-01,2.512526512145996094e-01 +2.910000000000000000e+05,5.019410309614613652e-05,9.999498128890991211e-01 +2.915000000000000000e+05,5.547795910388231277e-03,9.944521784782409668e-01 +2.920000000000000000e+05,4.416962619870901108e-03,9.955829977989196777e-01 +2.925000000000000000e+05,1.967181917279958725e-03,9.980328679084777832e-01 +2.930000000000000000e+05,1.106756855733692646e-03,9.988933205604553223e-01 +2.935000000000000000e+05,9.871389484032988548e-04,9.990128278732299805e-01 +2.940000000000000000e+05,9.535406570648774505e-05,9.999046325683593750e-01 +2.945000000000000000e+05,7.567036151885986328e-01,2.432963997125625610e-01 +2.950000000000000000e+05,9.999974966049194336e-01,2.527657443351927213e-06 +2.955000000000000000e+05,9.998391866683959961e-01,1.607883314136415720e-04 +2.960000000000000000e+05,8.829446509480476379e-03,9.911705255508422852e-01 +2.965000000000000000e+05,1.214960217475891113e-02,9.878503680229187012e-01 +2.970000000000000000e+05,7.136807441711425781e-01,2.863192558288574219e-01 +2.975000000000000000e+05,6.756019592285156250e-02,9.324398040771484375e-01 +2.980000000000000000e+05,6.079379454604350030e-05,9.999392032623291016e-01 +2.985000000000000000e+05,5.670283571816980839e-04,9.994329810142517090e-01 +2.990000000000000000e+05,7.002919912338256836e-03,9.929971098899841309e-01 +2.995000000000000000e+05,2.610188093967735767e-04,9.997389912605285645e-01 +3.000000000000000000e+05,9.494220614433288574e-01,5.057794600725173950e-02 +3.005000000000000000e+05,9.048762321472167969e-01,9.512381255626678467e-02 +3.010000000000000000e+05,5.684888362884521484e-01,4.315111041069030762e-01 +3.015000000000000000e+05,5.926027521491050720e-02,9.407396912574768066e-01 +3.020000000000000000e+05,8.542725443840026855e-02,9.145727157592773438e-01 +3.025000000000000000e+05,7.064378261566162109e-01,2.935621738433837891e-01 +3.030000000000000000e+05,9.217506051063537598e-01,7.824943959712982178e-02 +3.035000000000000000e+05,9.999992847442626953e-01,6.946789312678447459e-07 +3.040000000000000000e+05,1.000000000000000000e+00,2.768085138882270257e-08 +3.045000000000000000e+05,9.906751513481140137e-01,9.324858896434307098e-03 +3.050000000000000000e+05,9.947199821472167969e-01,5.279998760670423508e-03 +3.055000000000000000e+05,1.054492965340614319e-02,9.894550442695617676e-01 +3.060000000000000000e+05,9.513027907814830542e-05,9.999048709869384766e-01 +3.065000000000000000e+05,4.916996508836746216e-02,9.508301019668579102e-01 +3.070000000000000000e+05,6.108473171480000019e-04,9.993891716003417969e-01 +3.075000000000000000e+05,1.401681103743612766e-03,9.985982775688171387e-01 +3.080000000000000000e+05,4.859429970383644104e-02,9.514057040214538574e-01 +3.085000000000000000e+05,4.294298705644905567e-04,9.995705485343933105e-01 +3.090000000000000000e+05,3.616648958995938301e-03,9.963833093643188477e-01 +3.095000000000000000e+05,2.148359455168247223e-02,9.785163998603820801e-01 +3.100000000000000000e+05,9.932460784912109375e-01,6.753947120159864426e-03 +3.105000000000000000e+05,9.999827146530151367e-01,1.731465999910142273e-05 +3.110000000000000000e+05,9.999601840972900391e-01,3.983465649071149528e-05 +3.115000000000000000e+05,9.990860223770141602e-01,9.140230831690132618e-04 +3.120000000000000000e+05,9.960600733757019043e-01,3.939920105040073395e-03 +3.125000000000000000e+05,7.025424242019653320e-01,2.974576354026794434e-01 +3.130000000000000000e+05,6.216188669204711914e-01,3.783811330795288086e-01 +3.135000000000000000e+05,2.984499558806419373e-02,9.701550006866455078e-01 +3.140000000000000000e+05,9.661584496498107910e-01,3.384153917431831360e-02 +3.145000000000000000e+05,2.613387769088149071e-04,9.997386336326599121e-01 +3.150000000000000000e+05,1.498471572995185852e-02,9.850152730941772461e-01 +3.155000000000000000e+05,1.724139787256717682e-03,9.982758760452270508e-01 +3.160000000000000000e+05,3.687307238578796387e-02,9.631268978118896484e-01 +3.165000000000000000e+05,6.041980304871685803e-05,9.999395608901977539e-01 +3.170000000000000000e+05,3.327446524053812027e-03,9.966725111007690430e-01 +3.175000000000000000e+05,2.241535671055316925e-02,9.775846600532531738e-01 +3.180000000000000000e+05,1.336770132184028625e-02,9.866322875022888184e-01 +3.185000000000000000e+05,2.296359837055206299e-01,7.703640460968017578e-01 +3.190000000000000000e+05,2.925324079114943743e-04,9.997074007987976074e-01 +3.195000000000000000e+05,1.837598538259044290e-04,9.998162388801574707e-01 +3.200000000000000000e+05,1.463668607175350189e-02,9.853633642196655273e-01 +3.205000000000000000e+05,4.492072912398725748e-04,9.995507597923278809e-01 +3.210000000000000000e+05,9.990697503089904785e-01,9.303277474828064442e-04 +3.215000000000000000e+05,9.999995231628417969e-01,4.938581241731299087e-07 +3.220000000000000000e+05,9.999800920486450195e-01,1.990890268643852323e-05 +3.225000000000000000e+05,4.165268540382385254e-01,5.834731459617614746e-01 +3.230000000000000000e+05,6.324098706245422363e-01,3.675901591777801514e-01 +3.235000000000000000e+05,7.977415323257446289e-01,2.022584229707717896e-01 +3.240000000000000000e+05,9.628627896308898926e-01,3.713719174265861511e-02 +3.245000000000000000e+05,9.733672142028808594e-01,2.663275785744190216e-02 +3.250000000000000000e+05,4.934722557663917542e-03,9.950652718544006348e-01 +3.255000000000000000e+05,1.644371077418327332e-03,9.983555674552917480e-01 +3.260000000000000000e+05,2.955658601422328502e-05,9.999704360961914062e-01 +3.265000000000000000e+05,7.856132579036056995e-04,9.992144107818603516e-01 +3.270000000000000000e+05,6.975231226533651352e-03,9.930248260498046875e-01 +3.275000000000000000e+05,2.220384776592254639e-01,7.779614925384521484e-01 +3.280000000000000000e+05,1.462807655334472656e-01,8.537192940711975098e-01 +3.285000000000000000e+05,7.913912995718419552e-04,9.992086291313171387e-01 +3.290000000000000000e+05,5.313437897711992264e-03,9.946866035461425781e-01 +3.295000000000000000e+05,6.109483656473457813e-04,9.993890523910522461e-01 +3.300000000000000000e+05,9.932356476783752441e-01,6.764392834156751633e-03 +3.305000000000000000e+05,9.999778270721435547e-01,2.217176006524823606e-05 +3.310000000000000000e+05,9.747742414474487305e-01,2.522572316229343414e-02 +3.315000000000000000e+05,6.666893023066222668e-04,9.993333220481872559e-01 +3.320000000000000000e+05,1.055631655617617071e-04,9.998943805694580078e-01 +3.325000000000000000e+05,7.278190605575218797e-05,9.999271631240844727e-01 +3.330000000000000000e+05,9.996583461761474609e-01,3.415980900172144175e-04 +3.335000000000000000e+05,9.999828338623046875e-01,1.714857353363186121e-05 +3.340000000000000000e+05,9.956985712051391602e-01,4.301470704376697540e-03 +3.345000000000000000e+05,7.058930117636919022e-03,9.929410219192504883e-01 +3.350000000000000000e+05,3.281513461843132973e-03,9.967184662818908691e-01 +3.355000000000000000e+05,9.932789206504821777e-01,6.721091456711292267e-03 +3.360000000000000000e+05,9.973617196083068848e-01,2.638282952830195427e-03 +3.365000000000000000e+05,8.708162903785705566e-01,1.291837096214294434e-01 +3.370000000000000000e+05,8.280463218688964844e-01,1.719536781311035156e-01 +3.375000000000000000e+05,4.123975634574890137e-01,5.876024365425109863e-01 +3.380000000000000000e+05,3.755281912162899971e-03,9.962447285652160645e-01 +3.385000000000000000e+05,1.161373918876051903e-03,9.988386034965515137e-01 +3.390000000000000000e+05,9.988086223602294922e-01,1.191396964713931084e-03 +3.395000000000000000e+05,9.999948740005493164e-01,5.078793947177473456e-06 +3.400000000000000000e+05,9.985563158988952637e-01,1.443698187358677387e-03 +3.405000000000000000e+05,8.765139617025852203e-03,9.912347793579101562e-01 +3.410000000000000000e+05,2.737431554123759270e-03,9.972625970840454102e-01 +3.415000000000000000e+05,1.745681394822895527e-03,9.982543587684631348e-01 +3.420000000000000000e+05,9.115660795941948891e-04,9.990884065628051758e-01 +3.425000000000000000e+05,2.353266347199678421e-03,9.976467490196228027e-01 +3.430000000000000000e+05,2.561857458204030991e-03,9.974380731582641602e-01 +3.435000000000000000e+05,2.505379088688641787e-04,9.997494816780090332e-01 +3.440000000000000000e+05,4.880836640950292349e-04,9.995119571685791016e-01 +3.445000000000000000e+05,1.976134926080703735e-01,8.023864626884460449e-01 +3.450000000000000000e+05,9.767314195632934570e-01,2.326863445341587067e-02 +3.455000000000000000e+05,9.982719421386718750e-01,1.728064031340181828e-03 +3.460000000000000000e+05,9.868990182876586914e-01,1.310104131698608398e-02 +3.465000000000000000e+05,9.308142662048339844e-01,6.918573379516601562e-02 +3.470000000000000000e+05,9.702982902526855469e-01,2.970167994499206543e-02 +3.475000000000000000e+05,9.690728783607482910e-01,3.092708997428417206e-02 +3.480000000000000000e+05,9.993683695793151855e-01,6.316584185697138309e-04 +3.485000000000000000e+05,9.997563958168029785e-01,2.435588976368308067e-04 +3.490000000000000000e+05,6.852387189865112305e-01,3.147612810134887695e-01 +3.495000000000000000e+05,9.792509078979492188e-01,2.074913494288921356e-02 +3.500000000000000000e+05,2.386648207902908325e-03,9.976133108139038086e-01 +3.505000000000000000e+05,2.972647198475897312e-04,9.997027516365051270e-01 +3.510000000000000000e+05,3.125153779983520508e-01,6.874846220016479492e-01 +3.515000000000000000e+05,2.103220904245972633e-03,9.978967905044555664e-01 +3.520000000000000000e+05,1.271552871912717819e-03,9.987284541130065918e-01 +3.525000000000000000e+05,2.237861007452011108e-01,7.762138843536376953e-01 +3.530000000000000000e+05,1.345452815294265747e-01,8.654546737670898438e-01 +3.535000000000000000e+05,1.117184801842086017e-04,9.998883008956909180e-01 +3.540000000000000000e+05,3.638279158622026443e-04,9.996361732482910156e-01 +3.545000000000000000e+05,1.046474135364405811e-04,9.998953342437744141e-01 +3.550000000000000000e+05,3.056069836020469666e-04,9.996944665908813477e-01 +3.555000000000000000e+05,1.001439057290554047e-03,9.989985823631286621e-01 +3.560000000000000000e+05,9.473083615303039551e-01,5.269164592027664185e-02 +3.565000000000000000e+05,9.999747276306152344e-01,2.532893086026888341e-05 +3.570000000000000000e+05,9.982277750968933105e-01,1.772249815985560417e-03 +3.575000000000000000e+05,5.207396745681762695e-01,4.792603552341461182e-01 +3.580000000000000000e+05,7.757746148854494095e-03,9.922423362731933594e-01 +3.585000000000000000e+05,1.601680298335850239e-03,9.983983635902404785e-01 +3.590000000000000000e+05,7.978550158441066742e-03,9.920215010643005371e-01 +3.595000000000000000e+05,2.655402640812098980e-04,9.997344613075256348e-01 +3.600000000000000000e+05,9.995232820510864258e-01,4.766871570609509945e-04 +3.605000000000000000e+05,9.999645948410034180e-01,3.541077967383898795e-05 +3.610000000000000000e+05,9.671677947044372559e-01,3.283221647143363953e-02 +3.615000000000000000e+05,8.563571609556674957e-03,9.914364218711853027e-01 +3.620000000000000000e+05,8.875119686126708984e-01,1.124880090355873108e-01 +3.625000000000000000e+05,8.440039157867431641e-01,1.559960991144180298e-01 +3.630000000000000000e+05,2.796314889565110207e-03,9.972037076950073242e-01 +3.635000000000000000e+05,2.315477322554215789e-04,9.997684359550476074e-01 +3.640000000000000000e+05,3.204085078323259950e-05,9.999679327011108398e-01 +3.645000000000000000e+05,7.700962014496326447e-04,9.992299079895019531e-01 +3.650000000000000000e+05,4.153288318775594234e-04,9.995847344398498535e-01 +3.655000000000000000e+05,1.491994044044986367e-04,9.998507499694824219e-01 +3.660000000000000000e+05,8.158024284057319164e-04,9.991841912269592285e-01 +3.665000000000000000e+05,3.699107095599174500e-03,9.963008165359497070e-01 +3.670000000000000000e+05,1.186825335025787354e-03,9.988131523132324219e-01 +3.675000000000000000e+05,4.822128452360630035e-03,9.951778650283813477e-01 +3.680000000000000000e+05,7.499494240619242191e-04,9.992499947547912598e-01 +3.685000000000000000e+05,5.543041974306106567e-03,9.944568872451782227e-01 +3.690000000000000000e+05,2.590053901076316833e-02,9.740995168685913086e-01 +3.695000000000000000e+05,7.898442447185516357e-03,9.921016097068786621e-01 +3.700000000000000000e+05,5.193674564361572266e-01,4.806325137615203857e-01 +3.705000000000000000e+05,1.880986541509628296e-01,8.119013905525207520e-01 +3.710000000000000000e+05,2.470332547090947628e-04,9.997529387474060059e-01 +3.715000000000000000e+05,1.602378115057945251e-02,9.839762449264526367e-01 +3.720000000000000000e+05,9.942348599433898926e-01,5.765103735029697418e-03 +3.725000000000000000e+05,9.972828626632690430e-01,2.717095892876386642e-03 +3.730000000000000000e+05,1.448013633489608765e-01,8.551986813545227051e-01 +3.735000000000000000e+05,2.979738346766680479e-04,9.997020363807678223e-01 +3.740000000000000000e+05,1.053837826475501060e-03,9.989461302757263184e-01 +3.745000000000000000e+05,4.543414979707449675e-04,9.995456337928771973e-01 +3.750000000000000000e+05,1.612515625311061740e-04,9.998387098312377930e-01 +3.755000000000000000e+05,1.037930720485746861e-03,9.989620447158813477e-01 +3.760000000000000000e+05,2.424678532406687737e-04,9.997575879096984863e-01 +3.765000000000000000e+05,1.048716949298977852e-03,9.989511966705322266e-01 +3.770000000000000000e+05,3.662998601794242859e-02,9.633700251579284668e-01 +3.775000000000000000e+05,1.134292571805417538e-03,9.988657236099243164e-01 +3.780000000000000000e+05,1.348522258922457695e-03,9.986515641212463379e-01 +3.785000000000000000e+05,8.436195785179734230e-04,9.991563558578491211e-01 +3.790000000000000000e+05,2.308696322143077850e-02,9.769130945205688477e-01 +3.795000000000000000e+05,6.044883630238473415e-04,9.993954896926879883e-01 +3.800000000000000000e+05,5.405845586210489273e-04,9.994594454765319824e-01 +3.805000000000000000e+05,1.064310999936424196e-04,9.998935461044311523e-01 +3.810000000000000000e+05,1.636890578083693981e-03,9.983630776405334473e-01 +3.815000000000000000e+05,7.679540198296308517e-03,9.923204779624938965e-01 +3.820000000000000000e+05,1.803372404538094997e-03,9.981966614723205566e-01 +3.825000000000000000e+05,3.543685423210263252e-03,9.964563250541687012e-01 +3.830000000000000000e+05,5.627053380012512207e-01,4.372946619987487793e-01 +3.835000000000000000e+05,2.531827893108129501e-03,9.974682331085205078e-01 +3.840000000000000000e+05,4.839089160668663681e-05,9.999516010284423828e-01 +3.845000000000000000e+05,5.133048398420214653e-05,9.999486207962036133e-01 +3.850000000000000000e+05,2.195922657847404480e-03,9.978041052818298340e-01 +3.855000000000000000e+05,2.528683980926871300e-03,9.974713325500488281e-01 +3.860000000000000000e+05,7.767136557959020138e-04,9.992232322692871094e-01 +3.865000000000000000e+05,7.540664519183337688e-04,9.992460012435913086e-01 +3.870000000000000000e+05,6.906549970153719187e-05,9.999309778213500977e-01 +3.875000000000000000e+05,1.171614858321845531e-03,9.988283514976501465e-01 +3.880000000000000000e+05,9.952360987663269043e-01,4.763904958963394165e-03 +3.885000000000000000e+05,9.415346980094909668e-01,5.846534296870231628e-02 +3.890000000000000000e+05,1.971502322703599930e-03,9.980284571647644043e-01 +3.895000000000000000e+05,6.027266383171081543e-01,3.972734212875366211e-01 +3.900000000000000000e+05,9.998672008514404297e-01,1.327631325693801045e-04 +3.905000000000000000e+05,9.407376050949096680e-01,5.926235392689704895e-02 +3.910000000000000000e+05,2.937734313309192657e-02,9.706225991249084473e-01 +3.915000000000000000e+05,2.458252944052219391e-02,9.754174947738647461e-01 +3.920000000000000000e+05,1.598083646968007088e-03,9.984019398689270020e-01 +3.925000000000000000e+05,4.601594060659408569e-02,9.539840817451477051e-01 +3.930000000000000000e+05,2.718940086197108030e-04,9.997281432151794434e-01 +3.935000000000000000e+05,5.930499537498690188e-05,9.999406337738037109e-01 +3.940000000000000000e+05,1.256332080811262131e-02,9.874366521835327148e-01 +3.945000000000000000e+05,2.500591427087783813e-02,9.749940633773803711e-01 +3.950000000000000000e+05,1.381052308715879917e-03,9.986189603805541992e-01 +3.955000000000000000e+05,1.188722744700498879e-04,9.998811483383178711e-01 +3.960000000000000000e+05,1.495205651735886931e-04,9.998505115509033203e-01 +3.965000000000000000e+05,6.304105772869661450e-05,9.999369382858276367e-01 +3.970000000000000000e+05,8.281684131361544132e-06,9.999917745590209961e-01 +3.975000000000000000e+05,1.945232070283964276e-04,9.998055100440979004e-01 +3.980000000000000000e+05,9.448813274502754211e-03,9.905512332916259766e-01 +3.985000000000000000e+05,2.813834231346845627e-03,9.971861243247985840e-01 +3.990000000000000000e+05,3.080836322624236345e-04,9.996919631958007812e-01 +3.995000000000000000e+05,1.215328447869978845e-04,9.998784065246582031e-01 +4.000000000000000000e+05,2.427331201033666730e-04,9.997572302818298340e-01 +4.005000000000000000e+05,2.678787313925568014e-05,9.999731779098510742e-01 +4.010000000000000000e+05,1.212331844726577401e-04,9.998787641525268555e-01 +4.015000000000000000e+05,3.993590362370014191e-03,9.960064291954040527e-01 +4.020000000000000000e+05,7.055585592752322555e-05,9.999294281005859375e-01 +4.025000000000000000e+05,1.023214077576994896e-03,9.989768266677856445e-01 +4.030000000000000000e+05,1.196960336528718472e-03,9.988030195236206055e-01 +4.035000000000000000e+05,4.696827232837677002e-01,5.303173065185546875e-01 +4.040000000000000000e+05,8.251830339431762695e-01,1.748169511556625366e-01 +4.045000000000000000e+05,7.588366866111755371e-01,2.411632835865020752e-01 +4.050000000000000000e+05,8.752305060625076294e-02,9.124769568443298340e-01 +4.055000000000000000e+05,5.651621613651514053e-03,9.943483471870422363e-01 +4.060000000000000000e+05,1.921512885019183159e-04,9.998078942298889160e-01 +4.065000000000000000e+05,2.207416400779038668e-04,9.997792840003967285e-01 +4.070000000000000000e+05,3.191744536161422729e-02,9.680826067924499512e-01 +4.075000000000000000e+05,3.858054578304290771e-01,6.141945719718933105e-01 +4.080000000000000000e+05,9.445001184940338135e-02,9.055500030517578125e-01 +4.085000000000000000e+05,2.289596498012542725e-01,7.710403203964233398e-01 +4.090000000000000000e+05,5.553433438763022423e-04,9.994446635246276855e-01 +4.095000000000000000e+05,3.087772638536989689e-04,9.996912479400634766e-01 +4.100000000000000000e+05,6.891191333124879748e-06,9.999930858612060547e-01 +4.105000000000000000e+05,9.959886670112609863e-01,4.011312033981084824e-03 +4.110000000000000000e+05,9.979037046432495117e-01,2.096318639814853668e-03 +4.115000000000000000e+05,2.511488673917483538e-06,9.999974966049194336e-01 +4.120000000000000000e+05,4.084360785782337189e-03,9.959155917167663574e-01 +4.125000000000000000e+05,1.502963304519653320e-01,8.497036695480346680e-01 +4.130000000000000000e+05,1.251251529902219772e-02,9.874874353408813477e-01 +4.135000000000000000e+05,9.977336972951889038e-03,9.900227189064025879e-01 +4.140000000000000000e+05,3.280396776972338557e-05,9.999672174453735352e-01 +4.145000000000000000e+05,6.645514816045761108e-02,9.335448145866394043e-01 +4.150000000000000000e+05,9.997910857200622559e-01,2.088860637741163373e-04 +4.155000000000000000e+05,9.997231364250183105e-01,2.768955891951918602e-04 +4.160000000000000000e+05,9.910324215888977051e-01,8.967545814812183380e-03 +4.165000000000000000e+05,2.993265632539987564e-03,9.970067143440246582e-01 +4.170000000000000000e+05,6.380328559316694736e-04,9.993619322776794434e-01 +4.175000000000000000e+05,9.238344617187976837e-03,9.907616972923278809e-01 +4.180000000000000000e+05,9.856808185577392578e-01,1.431917212903499603e-02 +4.185000000000000000e+05,1.072814370672858786e-06,9.999989271163940430e-01 +4.190000000000000000e+05,6.157071329653263092e-04,9.993842840194702148e-01 +4.195000000000000000e+05,3.001019824296236038e-03,9.969990253448486328e-01 +4.200000000000000000e+05,9.797427058219909668e-01,2.025725878775119781e-02 +4.205000000000000000e+05,9.999909400939941406e-01,9.029189641296397895e-06 +4.210000000000000000e+05,9.999358654022216797e-01,6.412326183635741472e-05 +4.215000000000000000e+05,6.878003478050231934e-02,9.312199354171752930e-01 +4.220000000000000000e+05,2.795737795531749725e-02,9.720426201820373535e-01 +4.225000000000000000e+05,9.998137354850769043e-01,1.862976932898163795e-04 +4.230000000000000000e+05,9.992196559906005859e-01,7.803582702763378620e-04 +4.235000000000000000e+05,7.504967041313648224e-03,9.924950599670410156e-01 +4.240000000000000000e+05,2.829560311511158943e-03,9.971705079078674316e-01 +4.245000000000000000e+05,2.023446140810847282e-03,9.979766011238098145e-01 +4.250000000000000000e+05,5.701795592904090881e-02,9.429820775985717773e-01 +4.255000000000000000e+05,5.645226687192916870e-03,9.943548440933227539e-01 +4.260000000000000000e+05,9.999822378158569336e-01,1.778594560164492577e-05 +4.265000000000000000e+05,9.893424510955810547e-01,1.065753679722547531e-02 +4.270000000000000000e+05,9.930658340454101562e-01,6.934200879186391830e-03 +4.275000000000000000e+05,9.941931366920471191e-01,5.806866101920604706e-03 +4.280000000000000000e+05,3.646996617317199707e-01,6.353003382682800293e-01 +4.285000000000000000e+05,3.232901217415928841e-03,9.967671632766723633e-01 +4.290000000000000000e+05,8.514181375503540039e-01,1.485818624496459961e-01 +4.295000000000000000e+05,9.999587535858154297e-01,4.129127410124056041e-05 +4.300000000000000000e+05,9.999408721923828125e-01,5.915228030062280595e-05 +4.305000000000000000e+05,9.991845488548278809e-01,8.153865928761661053e-04 +4.310000000000000000e+05,9.988903403282165527e-01,1.109635690227150917e-03 +4.315000000000000000e+05,9.936164617538452148e-01,6.383564323186874390e-03 +4.320000000000000000e+05,9.753201603889465332e-01,2.467984333634376526e-02 +4.325000000000000000e+05,2.526309690438210964e-04,9.997473359107971191e-01 +4.330000000000000000e+05,9.988746047019958496e-01,1.125330221839249134e-03 +4.335000000000000000e+05,9.994922876358032227e-01,5.076677771285176277e-04 +4.340000000000000000e+05,8.392111631110310555e-04,9.991607666015625000e-01 +4.345000000000000000e+05,9.990565180778503418e-01,9.434560779482126236e-04 +4.350000000000000000e+05,9.982878565788269043e-01,1.712169498205184937e-03 +4.355000000000000000e+05,9.883311390876770020e-01,1.166880503296852112e-02 +4.360000000000000000e+05,3.777496218681335449e-01,6.222503781318664551e-01 +4.365000000000000000e+05,9.964424967765808105e-01,3.557530930265784264e-03 +4.370000000000000000e+05,9.956047534942626953e-01,4.395229276269674301e-03 +4.375000000000000000e+05,2.039998583495616913e-02,9.796000123023986816e-01 +4.380000000000000000e+05,8.864440023899078369e-04,9.991135001182556152e-01 +4.385000000000000000e+05,9.292414188385009766e-01,7.075855880975723267e-02 +4.390000000000000000e+05,9.559817314147949219e-01,4.401819035410881042e-02 +4.395000000000000000e+05,2.844406664371490479e-01,7.155593633651733398e-01 +4.400000000000000000e+05,9.831051575019955635e-04,9.990168809890747070e-01 +4.405000000000000000e+05,2.940268628299236298e-02,9.705973863601684570e-01 +4.410000000000000000e+05,9.729803800582885742e-01,2.701956592500209808e-02 +4.415000000000000000e+05,9.998121857643127441e-01,1.878828625194728374e-04 +4.420000000000000000e+05,9.997075200080871582e-01,2.924181171692907810e-04 +4.425000000000000000e+05,1.638937294483184814e-01,8.361062407493591309e-01 +4.430000000000000000e+05,9.992075562477111816e-01,7.924326928332448006e-04 +4.435000000000000000e+05,9.985456466674804688e-01,1.454393961466848850e-03 +4.440000000000000000e+05,8.438240289688110352e-01,1.561760157346725464e-01 +4.445000000000000000e+05,9.783051013946533203e-01,2.169487625360488892e-02 +4.450000000000000000e+05,4.654079675674438477e-02,9.534591436386108398e-01 +4.455000000000000000e+05,3.276870772242546082e-02,9.672312736511230469e-01 +4.460000000000000000e+05,1.604923903942108154e-01,8.395076394081115723e-01 +4.465000000000000000e+05,5.799754336476325989e-02,9.420024156570434570e-01 +4.470000000000000000e+05,9.919227361679077148e-01,8.077262900769710541e-03 +4.475000000000000000e+05,9.956440925598144531e-01,4.355867393314838409e-03 +4.480000000000000000e+05,3.417220413684844971e-01,6.582779884338378906e-01 +4.485000000000000000e+05,7.404957711696624756e-02,9.259504079818725586e-01 +4.490000000000000000e+05,1.002423159661702812e-04,9.998997449874877930e-01 +4.495000000000000000e+05,9.180232286453247070e-01,8.197673410177230835e-02 +4.500000000000000000e+05,8.642617103760130703e-06,9.999912977218627930e-01 +4.505000000000000000e+05,7.380011491477489471e-03,9.926200509071350098e-01 +4.510000000000000000e+05,9.998124241828918457e-01,1.875417074188590050e-04 +4.515000000000000000e+05,1.000000000000000000e+00,1.767696211629754544e-08 +4.520000000000000000e+05,9.991810917854309082e-01,8.189397049136459827e-04 +4.525000000000000000e+05,9.999994039535522461e-01,5.541808150155702606e-07 +4.530000000000000000e+05,9.999865293502807617e-01,1.347451507172081620e-05 +4.535000000000000000e+05,9.838581681251525879e-01,1.614183560013771057e-02 +4.540000000000000000e+05,9.599103927612304688e-01,4.008960723876953125e-02 +4.545000000000000000e+05,6.747226871084421873e-05,9.999325275421142578e-01 +4.550000000000000000e+05,4.635984543710947037e-03,9.953639507293701172e-01 +4.555000000000000000e+05,5.976448655128479004e-01,4.023551046848297119e-01 +4.560000000000000000e+05,6.518667936325073242e-01,3.481331765651702881e-01 +4.565000000000000000e+05,2.843650290742516518e-04,9.997156262397766113e-01 +4.570000000000000000e+05,5.937271052971482277e-04,9.994062185287475586e-01 +4.575000000000000000e+05,9.924178123474121094e-01,7.582242134958505630e-03 +4.580000000000000000e+05,9.995054006576538086e-01,4.945466062054038048e-04 +4.585000000000000000e+05,9.992062449455261230e-01,7.937393384054303169e-04 +4.590000000000000000e+05,6.761983782052993774e-02,9.323801398277282715e-01 +4.595000000000000000e+05,5.584298372268676758e-01,4.415701627731323242e-01 +4.600000000000000000e+05,6.887866184115409851e-04,9.993112087249755859e-01 +4.605000000000000000e+05,1.196593418717384338e-02,9.880340695381164551e-01 +4.610000000000000000e+05,9.645355343818664551e-01,3.546442091464996338e-02 +4.615000000000000000e+05,5.479755345731973648e-03,9.945202469825744629e-01 +4.620000000000000000e+05,7.051716092973947525e-04,9.992947578430175781e-01 +4.625000000000000000e+05,9.952656030654907227e-01,4.734348505735397339e-03 +4.630000000000000000e+05,9.999506473541259766e-01,4.930354407406412065e-05 +4.635000000000000000e+05,9.993366599082946777e-01,6.633065058849751949e-04 +4.640000000000000000e+05,7.439553141593933105e-01,2.560447156429290771e-01 +4.645000000000000000e+05,3.064429759979248047e-01,6.935570836067199707e-01 +4.650000000000000000e+05,9.924915432929992676e-01,7.508427370339632034e-03 +4.655000000000000000e+05,9.992388486862182617e-01,7.611648179590702057e-04 +4.660000000000000000e+05,4.626023396849632263e-02,9.537398219108581543e-01 +4.665000000000000000e+05,8.249944949056953192e-05,9.999175071716308594e-01 +4.670000000000000000e+05,1.680959685472771525e-04,9.998319149017333984e-01 +4.675000000000000000e+05,7.824068234185688198e-06,9.999921321868896484e-01 +4.680000000000000000e+05,9.667015671730041504e-01,3.329838067293167114e-02 +4.685000000000000000e+05,9.994478821754455566e-01,5.520772538147866726e-04 +4.690000000000000000e+05,9.931076169013977051e-01,6.892467848956584930e-03 +4.695000000000000000e+05,2.099872678518295288e-01,7.900127172470092773e-01 +4.700000000000000000e+05,9.998917579650878906e-01,1.081870941561646760e-04 +4.705000000000000000e+05,9.995461106300354004e-01,4.538309003692120314e-04 +4.710000000000000000e+05,3.295215964317321777e-01,6.704784035682678223e-01 +4.715000000000000000e+05,1.173905804753303528e-01,8.826094269752502441e-01 +4.720000000000000000e+05,4.532716274261474609e-01,5.467283129692077637e-01 +4.725000000000000000e+05,3.967952355742454529e-02,9.603205323219299316e-01 +4.730000000000000000e+05,7.995547652244567871e-01,2.004452496767044067e-01 +4.735000000000000000e+05,1.003379523754119873e-01,8.996620178222656250e-01 +4.740000000000000000e+05,3.173692675773054361e-04,9.996826648712158203e-01 +4.745000000000000000e+05,9.973927736282348633e-01,2.607167465612292290e-03 +4.750000000000000000e+05,9.889377355575561523e-01,1.106222439557313919e-02 +4.755000000000000000e+05,1.582509430591017008e-04,9.998416900634765625e-01 +4.760000000000000000e+05,4.728042113129049540e-04,9.995272159576416016e-01 +4.765000000000000000e+05,9.248139262199401855e-01,7.518608123064041138e-02 +4.770000000000000000e+05,9.998829364776611328e-01,1.170917312265373766e-04 +4.775000000000000000e+05,9.994889497756958008e-01,5.110113415867090225e-04 +4.780000000000000000e+05,1.097116852179169655e-03,9.989029169082641602e-01 +4.785000000000000000e+05,3.679117362480610609e-04,9.996321201324462891e-01 +4.790000000000000000e+05,6.836442947387695312e-01,3.163557350635528564e-01 +4.795000000000000000e+05,8.206558823585510254e-01,1.793441176414489746e-01 +4.800000000000000000e+05,2.305193990468978882e-02,9.769480228424072266e-01 +4.805000000000000000e+05,9.978796243667602539e-01,2.120391698554158211e-03 +4.810000000000000000e+05,9.466684460639953613e-01,5.333156511187553406e-02 +4.815000000000000000e+05,9.045706689357757568e-02,9.095429182052612305e-01 +4.820000000000000000e+05,4.808668512850999832e-03,9.951913356781005859e-01 +4.825000000000000000e+05,7.470298409461975098e-01,2.529701590538024902e-01 +4.830000000000000000e+05,9.998319149017333984e-01,1.680860441410914063e-04 +4.835000000000000000e+05,9.981990456581115723e-01,1.801027217879891396e-03 +4.840000000000000000e+05,9.979801774024963379e-01,2.019795821979641914e-03 +4.845000000000000000e+05,9.997166991233825684e-01,2.832226164173334837e-04 +4.850000000000000000e+05,9.193766713142395020e-01,8.062329888343811035e-02 +4.855000000000000000e+05,1.184601932764053345e-01,8.815398216247558594e-01 +4.860000000000000000e+05,1.773416437208652496e-02,9.822658300399780273e-01 +4.865000000000000000e+05,9.987351298332214355e-01,1.264935242943465710e-03 +4.870000000000000000e+05,8.883041739463806152e-01,1.116958782076835632e-01 +4.875000000000000000e+05,8.594321012496948242e-01,1.405678987503051758e-01 +4.880000000000000000e+05,9.977699518203735352e-01,2.230108017101883888e-03 +4.885000000000000000e+05,9.958292841911315918e-01,4.170713480561971664e-03 +4.890000000000000000e+05,9.760199189186096191e-01,2.398006245493888855e-02 +4.895000000000000000e+05,1.030018925666809082e-01,8.969981074333190918e-01 +4.900000000000000000e+05,9.999974966049194336e-01,2.481348701621755026e-06 +4.905000000000000000e+05,9.999992847442626953e-01,7.268060926435282454e-07 +4.910000000000000000e+05,9.928938746452331543e-01,7.106063887476921082e-03 +4.915000000000000000e+05,9.964354038238525391e-01,3.564557526260614395e-03 +4.920000000000000000e+05,4.327926635742187500e-01,5.672073960304260254e-01 +4.925000000000000000e+05,2.908074529841542244e-03,9.970918893814086914e-01 +4.930000000000000000e+05,9.947992563247680664e-01,5.200703628361225128e-03 +4.935000000000000000e+05,9.876524209976196289e-01,1.234760973602533340e-02 +4.940000000000000000e+05,1.292804181575775146e-01,8.707195520401000977e-01 +4.945000000000000000e+05,9.709162712097167969e-01,2.908372879028320312e-02 +4.950000000000000000e+05,9.318979382514953613e-01,6.810203939676284790e-02 +4.955000000000000000e+05,2.712220884859561920e-04,9.997287392616271973e-01 +4.960000000000000000e+05,2.145110443234443665e-02,9.785488247871398926e-01 +4.965000000000000000e+05,3.223374858498573303e-02,9.677662849426269531e-01 +4.970000000000000000e+05,7.342828321270644665e-04,9.992657303810119629e-01 +4.975000000000000000e+05,9.776663780212402344e-01,2.233370207250118256e-02 +4.980000000000000000e+05,9.999403953552246094e-01,5.965247328276745975e-05 +4.985000000000000000e+05,9.999401569366455078e-01,5.985681127640418708e-05 +4.990000000000000000e+05,9.986487030982971191e-01,1.351320301182568073e-03 +4.995000000000000000e+05,1.285348273813724518e-02,9.871464967727661133e-01 +5.000000000000000000e+05,9.999698400497436523e-01,3.013704736076761037e-05 +5.005000000000000000e+05,9.999645948410034180e-01,3.546092921169474721e-05 +5.010000000000000000e+05,1.544408053159713745e-01,8.455592393875122070e-01 +5.015000000000000000e+05,4.355895817279815674e-01,5.644104480743408203e-01 +5.020000000000000000e+05,2.866025865077972412e-01,7.133974432945251465e-01 +5.025000000000000000e+05,1.941758580505847931e-02,9.805824160575866699e-01 +5.030000000000000000e+05,9.974421262741088867e-01,2.557915402576327324e-03 +5.035000000000000000e+05,9.999916553497314453e-01,8.379096470889635384e-06 +5.040000000000000000e+05,9.977294802665710449e-01,2.270479453727602959e-03 +5.045000000000000000e+05,1.007722690701484680e-02,9.899228215217590332e-01 +5.050000000000000000e+05,9.862635135650634766e-01,1.373651251196861267e-02 +5.055000000000000000e+05,3.968613222241401672e-03,9.960313439369201660e-01 +5.060000000000000000e+05,9.682349627837538719e-05,9.999032020568847656e-01 +5.065000000000000000e+05,9.559624828398227692e-03,9.904403686523437500e-01 +5.070000000000000000e+05,5.360368266701698303e-02,9.463962912559509277e-01 +5.075000000000000000e+05,1.253148308023810387e-03,9.987468719482421875e-01 +5.080000000000000000e+05,4.198117647320032120e-03,9.958018660545349121e-01 +5.085000000000000000e+05,5.083812400698661804e-02,9.491618275642395020e-01 +5.090000000000000000e+05,9.401184320449829102e-02,9.059881567955017090e-01 +5.095000000000000000e+05,9.018816947937011719e-01,9.811831265687942505e-02 +5.100000000000000000e+05,5.145231261849403381e-03,9.948546886444091797e-01 +5.105000000000000000e+05,2.313679307699203491e-01,7.686321139335632324e-01 +5.110000000000000000e+05,9.977627992630004883e-01,2.237145090475678444e-03 +5.115000000000000000e+05,9.999846220016479492e-01,1.534716830065008253e-05 +5.120000000000000000e+05,9.999992847442626953e-01,7.116249776117911097e-07 +5.125000000000000000e+05,9.877447485923767090e-01,1.225519459694623947e-02 +5.130000000000000000e+05,8.576588630676269531e-01,1.423411518335342407e-01 +5.135000000000000000e+05,4.926418662071228027e-01,5.073580741882324219e-01 +5.140000000000000000e+05,6.136115989647805691e-04,9.993863105773925781e-01 +5.145000000000000000e+05,9.991683959960937500e-01,8.316843886859714985e-04 +5.150000000000000000e+05,9.999961853027343750e-01,3.871168701152782887e-06 +5.155000000000000000e+05,9.950554370880126953e-01,4.944594576954841614e-03 +5.160000000000000000e+05,8.106445074081420898e-01,1.893555074930191040e-01 +5.165000000000000000e+05,8.654049783945083618e-02,9.134594798088073730e-01 +5.170000000000000000e+05,9.974709749221801758e-01,2.529063029214739799e-03 +5.175000000000000000e+05,9.178915061056613922e-03,9.908210635185241699e-01 +5.180000000000000000e+05,9.925824403762817383e-01,7.417608052492141724e-03 +5.185000000000000000e+05,8.321716189384460449e-01,1.678283661603927612e-01 +5.190000000000000000e+05,9.990629553794860840e-01,9.370083571411669254e-04 +5.195000000000000000e+05,9.999964237213134766e-01,3.583204943424789235e-06 +5.200000000000000000e+05,9.940766096115112305e-01,5.923393182456493378e-03 +5.205000000000000000e+05,9.999693632125854492e-01,3.069332888117060065e-05 +5.210000000000000000e+05,9.985476136207580566e-01,1.452405122108757496e-03 +5.215000000000000000e+05,1.208816841244697571e-01,8.791183829307556152e-01 +5.220000000000000000e+05,6.783734541386365891e-03,9.932162165641784668e-01 +5.225000000000000000e+05,8.907739538699388504e-04,9.991092085838317871e-01 +5.230000000000000000e+05,9.803889552131295204e-04,9.990196228027343750e-01 +5.235000000000000000e+05,3.762770676985383034e-03,9.962371587753295898e-01 +5.240000000000000000e+05,9.998761415481567383e-01,1.238770055351778865e-04 +5.245000000000000000e+05,1.000000000000000000e+00,3.402522708029209753e-09 +5.250000000000000000e+05,9.999723434448242188e-01,2.768105150607880205e-05 +5.255000000000000000e+05,2.213733037933707237e-03,9.977862834930419922e-01 +5.260000000000000000e+05,3.661453956738114357e-04,9.996337890625000000e-01 +5.265000000000000000e+05,1.177143491804599762e-02,9.882285594940185547e-01 +5.270000000000000000e+05,3.280977252870798111e-03,9.967190623283386230e-01 +5.275000000000000000e+05,3.917429223656654358e-02,9.608256816864013672e-01 +5.280000000000000000e+05,3.434721846133470535e-03,9.965652823448181152e-01 +5.285000000000000000e+05,2.376115173101425171e-01,7.623885273933410645e-01 +5.290000000000000000e+05,9.658870100975036621e-01,3.411295264959335327e-02 +5.295000000000000000e+05,9.850779771804809570e-01,1.492201443761587143e-02 +5.300000000000000000e+05,8.875037431716918945e-01,1.124962642788887024e-01 +5.305000000000000000e+05,9.993692040443420410e-01,6.308399024419486523e-04 +5.310000000000000000e+05,9.999132156372070312e-01,8.677177538629621267e-05 +5.315000000000000000e+05,9.984702467918395996e-01,1.529716304503381252e-03 +5.320000000000000000e+05,8.534334301948547363e-01,1.465665698051452637e-01 +5.325000000000000000e+05,1.682293415069580078e-01,8.317705988883972168e-01 +5.330000000000000000e+05,1.396597799612209201e-04,9.998602867126464844e-01 +5.335000000000000000e+05,2.641907485667616129e-04,9.997357726097106934e-01 +5.340000000000000000e+05,5.068544996902346611e-04,9.994931221008300781e-01 +5.345000000000000000e+05,3.098754968959838152e-04,9.996901750564575195e-01 +5.350000000000000000e+05,1.470405277359532192e-05,9.999853372573852539e-01 +5.355000000000000000e+05,2.728896834014449269e-05,9.999727010726928711e-01 +5.360000000000000000e+05,5.420322995632886887e-04,9.994580149650573730e-01 +5.365000000000000000e+05,1.444474492018343881e-05,9.999855756759643555e-01 +5.370000000000000000e+05,1.284106075763702393e-02,9.871588945388793945e-01 +5.375000000000000000e+05,7.131284655770286918e-05,9.999287128448486328e-01 +5.380000000000000000e+05,4.432220302987843752e-04,9.995567202568054199e-01 +5.385000000000000000e+05,3.709848679136484861e-04,9.996290206909179688e-01 +5.390000000000000000e+05,7.599494885653257370e-03,9.924005270004272461e-01 +5.395000000000000000e+05,8.504094481468200684e-01,1.495905369520187378e-01 +5.400000000000000000e+05,9.712173342704772949e-01,2.878265269100666046e-02 +5.405000000000000000e+05,8.423482179641723633e-01,1.576518118381500244e-01 +5.410000000000000000e+05,9.873442649841308594e-01,1.265570428222417831e-02 +5.415000000000000000e+05,2.722604840528219938e-04,9.997276663780212402e-01 +5.420000000000000000e+05,1.896148896776139736e-04,9.998103976249694824e-01 +5.425000000000000000e+05,5.027161096222698689e-04,9.994972944259643555e-01 +5.430000000000000000e+05,4.392118155956268311e-01,5.607881546020507812e-01 +5.435000000000000000e+05,3.965277373790740967e-01,6.034722328186035156e-01 +5.440000000000000000e+05,3.638986672740429640e-04,9.996360540390014648e-01 +5.445000000000000000e+05,8.628486990928649902e-01,1.371512711048126221e-01 +5.450000000000000000e+05,9.562169313430786133e-01,4.378312081098556519e-02 +5.455000000000000000e+05,8.843243267619982362e-05,9.999115467071533203e-01 +5.460000000000000000e+05,6.568115204572677612e-04,9.993432164192199707e-01 +5.465000000000000000e+05,6.646537076449021697e-05,9.999334812164306641e-01 +5.470000000000000000e+05,4.740641743410378695e-04,9.995259046554565430e-01 +5.475000000000000000e+05,1.119083844969281927e-05,9.999887943267822266e-01 +5.480000000000000000e+05,6.616308819502592087e-04,9.993383288383483887e-01 +5.485000000000000000e+05,1.320434384979307652e-03,9.986795783042907715e-01 +5.490000000000000000e+05,9.979288578033447266e-01,2.071179915219545364e-03 +5.495000000000000000e+05,9.890418648719787598e-01,1.095816120505332947e-02 +5.500000000000000000e+05,2.477002702653408051e-02,9.752299189567565918e-01 +5.505000000000000000e+05,3.229379281401634216e-02,9.677062630653381348e-01 +5.510000000000000000e+05,1.354307518340647221e-03,9.986457228660583496e-01 +5.515000000000000000e+05,4.664722073357552290e-04,9.995335340499877930e-01 +5.520000000000000000e+05,3.270101547241210938e-02,9.672989249229431152e-01 +5.525000000000000000e+05,9.992062449455261230e-01,7.937899790704250336e-04 +5.530000000000000000e+05,9.962654709815979004e-01,3.734573954716324806e-03 +5.535000000000000000e+05,6.482284516096115112e-03,9.935177564620971680e-01 +5.540000000000000000e+05,1.247348845936357975e-03,9.987525939941406250e-01 +5.545000000000000000e+05,5.854160189628601074e-01,4.145839214324951172e-01 +5.550000000000000000e+05,1.015223562717437744e-01,8.984776735305786133e-01 +5.555000000000000000e+05,2.697295248508453369e-01,7.302704453468322754e-01 +5.560000000000000000e+05,8.369332551956176758e-02,9.163066744804382324e-01 +5.565000000000000000e+05,3.501808969303965569e-03,9.964981079101562500e-01 +5.570000000000000000e+05,4.226579330861568451e-03,9.957733750343322754e-01 +5.575000000000000000e+05,4.401867277920246124e-03,9.955981373786926270e-01 +5.580000000000000000e+05,4.823066294193267822e-03,9.951769113540649414e-01 +5.585000000000000000e+05,9.931502938270568848e-01,6.849746685475111008e-03 +5.590000000000000000e+05,9.691439867019653320e-01,3.085604310035705566e-02 +5.595000000000000000e+05,2.435609139502048492e-02,9.756439328193664551e-01 +5.600000000000000000e+05,1.644343882799148560e-02,9.835565090179443359e-01 +5.605000000000000000e+05,9.200081694871187210e-05,9.999079704284667969e-01 +5.610000000000000000e+05,9.944863915443420410e-01,5.513560026884078979e-03 +5.615000000000000000e+05,9.998249411582946777e-01,1.750832452671602368e-04 +5.620000000000000000e+05,9.843782782554626465e-01,1.562168635427951813e-02 +5.625000000000000000e+05,4.155878559686243534e-04,9.995843768119812012e-01 +5.630000000000000000e+05,1.105425599962472916e-02,9.889457821846008301e-01 +5.635000000000000000e+05,1.387066789902746677e-03,9.986128807067871094e-01 +5.640000000000000000e+05,9.929404258728027344e-01,7.059605792164802551e-03 +5.645000000000000000e+05,9.755747318267822266e-01,2.442533150315284729e-02 +5.650000000000000000e+05,9.139311909675598145e-01,8.606878668069839478e-02 +5.655000000000000000e+05,9.219386265613138676e-04,9.990780353546142578e-01 +5.660000000000000000e+05,9.863672852516174316e-01,1.363275758922100067e-02 +5.665000000000000000e+05,9.853168129920959473e-01,1.468315161764621735e-02 +5.670000000000000000e+05,3.887134138494729996e-03,9.961128234863281250e-01 +5.675000000000000000e+05,5.447618365287780762e-01,4.552381336688995361e-01 +5.680000000000000000e+05,2.152872970327734947e-03,9.978470802307128906e-01 +5.685000000000000000e+05,9.904072880744934082e-01,9.592758491635322571e-03 +5.690000000000000000e+05,9.984209537506103516e-01,1.578999334014952183e-03 +5.695000000000000000e+05,7.525185346603393555e-01,2.474814206361770630e-01 +5.700000000000000000e+05,9.999626874923706055e-01,3.726494833244942129e-05 +5.705000000000000000e+05,9.999974966049194336e-01,2.547943950048647821e-06 +5.710000000000000000e+05,9.999647140502929688e-01,3.527525404933840036e-05 +5.715000000000000000e+05,9.999997615814208984e-01,2.349102743437470053e-07 +5.720000000000000000e+05,9.846128225326538086e-01,1.538716536015272141e-02 +5.725000000000000000e+05,2.368851564824581146e-03,9.976310729980468750e-01 +5.730000000000000000e+05,1.960968220373615623e-04,9.998039603233337402e-01 +5.735000000000000000e+05,2.074312185868620872e-03,9.979256391525268555e-01 +5.740000000000000000e+05,1.421108562499284744e-02,9.857888817787170410e-01 +5.745000000000000000e+05,2.147930208593606949e-03,9.978520870208740234e-01 +5.750000000000000000e+05,9.053351357579231262e-03,9.909466505050659180e-01 +5.755000000000000000e+05,9.218032471835613251e-03,9.907819628715515137e-01 +5.760000000000000000e+05,7.837864686734974384e-04,9.992161989212036133e-01 +5.765000000000000000e+05,8.522645657649263740e-06,9.999915361404418945e-01 +5.770000000000000000e+05,8.991776667244266719e-06,9.999910593032836914e-01 +5.775000000000000000e+05,7.651178748346865177e-05,9.999234676361083984e-01 +5.780000000000000000e+05,4.823556169867515564e-03,9.951764345169067383e-01 +5.785000000000000000e+05,3.116562068462371826e-01,6.883437633514404297e-01 +5.790000000000000000e+05,2.551924809813499451e-02,9.744807481765747070e-01 +5.795000000000000000e+05,7.995649357326328754e-04,9.992004036903381348e-01 +5.800000000000000000e+05,1.596553018316626549e-03,9.984034895896911621e-01 +5.805000000000000000e+05,1.173400887637399137e-04,9.998826980590820312e-01 +5.810000000000000000e+05,6.083513144403696060e-03,9.939164519309997559e-01 +5.815000000000000000e+05,4.506056662648916245e-03,9.954938888549804688e-01 +5.820000000000000000e+05,7.866061059758067131e-04,9.992133378982543945e-01 +5.825000000000000000e+05,3.050438594073057175e-03,9.969496130943298340e-01 +5.830000000000000000e+05,1.787662878632545471e-02,9.821233153343200684e-01 +5.835000000000000000e+05,1.154955825768411160e-03,9.988449811935424805e-01 +5.840000000000000000e+05,3.448518782533938065e-06,9.999965429306030273e-01 +5.845000000000000000e+05,4.443049838300794363e-04,9.995556473731994629e-01 +5.850000000000000000e+05,2.112310176016762853e-04,9.997888207435607910e-01 +5.855000000000000000e+05,7.006400264799594879e-04,9.992992877960205078e-01 +5.860000000000000000e+05,8.931382035370916128e-05,9.999107122421264648e-01 +5.865000000000000000e+05,1.526112464489415288e-04,9.998474121093750000e-01 +5.870000000000000000e+05,4.414147952047642320e-06,9.999955892562866211e-01 +5.875000000000000000e+05,7.449578046798706055e-01,2.550421655178070068e-01 +5.880000000000000000e+05,9.609659314155578613e-01,3.903408721089363098e-02 +5.885000000000000000e+05,7.526258379220962524e-02,9.247373938560485840e-01 +5.890000000000000000e+05,1.357553293928503990e-03,9.986425042152404785e-01 +5.895000000000000000e+05,6.579451728612184525e-04,9.993420243263244629e-01 +5.900000000000000000e+05,3.808766778092831373e-04,9.996191263198852539e-01 +5.905000000000000000e+05,2.679927740246057510e-03,9.973201155662536621e-01 +5.910000000000000000e+05,6.781810661777853966e-04,9.993218183517456055e-01 +5.915000000000000000e+05,2.821605838835239410e-02,9.717839360237121582e-01 +5.920000000000000000e+05,2.561768342275172472e-04,9.997437596321105957e-01 +5.925000000000000000e+05,4.701694124378263950e-04,9.995298385620117188e-01 +5.930000000000000000e+05,1.080311136320233345e-03,9.989197254180908203e-01 +5.935000000000000000e+05,2.400916855549439788e-04,9.997598528861999512e-01 +5.940000000000000000e+05,2.452675253152847290e-04,9.997547268867492676e-01 +5.945000000000000000e+05,1.380260346195427701e-05,9.999861717224121094e-01 +5.950000000000000000e+05,2.267505624331533909e-05,9.999773502349853516e-01 +5.955000000000000000e+05,1.583129924256354570e-04,9.998416900634765625e-01 +5.960000000000000000e+05,4.081143124494701624e-04,9.995918869972229004e-01 +5.965000000000000000e+05,1.131317317485809326e-01,8.868682384490966797e-01 +5.970000000000000000e+05,7.623661309480667114e-02,9.237633347511291504e-01 +5.975000000000000000e+05,7.386151992250233889e-05,9.999260902404785156e-01 +5.980000000000000000e+05,5.125604002387262881e-05,9.999487400054931641e-01 +5.985000000000000000e+05,1.316147972829639912e-03,9.986838698387145996e-01 +5.990000000000000000e+05,6.694569252431392670e-03,9.933053851127624512e-01 +5.995000000000000000e+05,4.666291933972388506e-04,9.995334148406982422e-01 +6.000000000000000000e+05,5.077237263321876526e-03,9.949228167533874512e-01 +6.005000000000000000e+05,6.924982881173491478e-05,9.999307394027709961e-01 +6.010000000000000000e+05,6.845179086667485535e-06,9.999932050704956055e-01 +6.015000000000000000e+05,6.212196312844753265e-04,9.993788003921508789e-01 +6.020000000000000000e+05,2.266565206809900701e-05,9.999773502349853516e-01 +6.025000000000000000e+05,7.727083284407854080e-04,9.992272853851318359e-01 +6.030000000000000000e+05,1.141866319812834263e-03,9.988580942153930664e-01 +6.035000000000000000e+05,5.939229158684611320e-04,9.994060993194580078e-01 +6.040000000000000000e+05,2.222438715398311615e-02,9.777756333351135254e-01 +6.045000000000000000e+05,2.033523742284160107e-05,9.999796152114868164e-01 +6.050000000000000000e+05,8.055323269218206406e-04,9.991944432258605957e-01 +6.055000000000000000e+05,4.133676702622324228e-04,9.995866417884826660e-01 +6.060000000000000000e+05,2.506792545318603516e-03,9.974932670593261719e-01 +6.065000000000000000e+05,1.213810872286558151e-03,9.987861514091491699e-01 +6.070000000000000000e+05,2.505869604647159576e-03,9.974941015243530273e-01 +6.075000000000000000e+05,6.693603354506194592e-04,9.993306398391723633e-01 +6.080000000000000000e+05,1.972201716853305697e-04,9.998027682304382324e-01 +6.085000000000000000e+05,3.022432210855185986e-04,9.996978044509887695e-01 +6.090000000000000000e+05,1.980264787562191486e-04,9.998020529747009277e-01 +6.095000000000000000e+05,1.627314090728759766e-02,9.837268590927124023e-01 +6.100000000000000000e+05,2.556386170908808708e-04,9.997443556785583496e-01 +6.105000000000000000e+05,2.238210290670394897e-04,9.997761845588684082e-01 +6.110000000000000000e+05,1.421342517460288946e-06,9.999985694885253906e-01 +6.115000000000000000e+05,2.028273447649553418e-04,9.997971653938293457e-01 +6.120000000000000000e+05,1.161252293968573213e-04,9.998838901519775391e-01 +6.125000000000000000e+05,5.894977948628365993e-04,9.994105100631713867e-01 +6.130000000000000000e+05,6.991872796788811684e-04,9.993008375167846680e-01 +6.135000000000000000e+05,9.257592319045215845e-05,9.999073743820190430e-01 +6.140000000000000000e+05,1.952795864781364799e-04,9.998047947883605957e-01 +6.145000000000000000e+05,2.024289715336635709e-04,9.997976422309875488e-01 +6.150000000000000000e+05,4.361524479463696480e-04,9.995638728141784668e-01 +6.155000000000000000e+05,3.312353219371289015e-04,9.996687173843383789e-01 +6.160000000000000000e+05,4.201109986752271652e-04,9.995798468589782715e-01 +6.165000000000000000e+05,1.447671093046665192e-02,9.855233430862426758e-01 +6.170000000000000000e+05,7.971958839334547520e-04,9.992027878761291504e-01 +6.175000000000000000e+05,6.886898336233571172e-05,9.999310970306396484e-01 +6.180000000000000000e+05,1.092983176931738853e-03,9.989069700241088867e-01 +6.185000000000000000e+05,9.627114050090312958e-04,9.990372657775878906e-01 +6.190000000000000000e+05,2.000789046287536621e-01,7.999210953712463379e-01 +6.195000000000000000e+05,8.039607200771570206e-05,9.999196529388427734e-01 +6.200000000000000000e+05,2.360813232371583581e-04,9.997639060020446777e-01 +6.205000000000000000e+05,5.279662946122698486e-05,9.999471902847290039e-01 +6.210000000000000000e+05,5.389325669966638088e-04,9.994611144065856934e-01 +6.215000000000000000e+05,1.124508271459490061e-04,9.998875856399536133e-01 +6.220000000000000000e+05,1.486654364271089435e-04,9.998513460159301758e-01 +6.225000000000000000e+05,2.278110041515901685e-04,9.997722506523132324e-01 +6.230000000000000000e+05,4.502784577198326588e-05,9.999549388885498047e-01 +6.235000000000000000e+05,3.238888166379183531e-04,9.996761083602905273e-01 +6.240000000000000000e+05,2.841400215402245522e-03,9.971586465835571289e-01 +6.245000000000000000e+05,1.314017339609563351e-03,9.986860156059265137e-01 +6.250000000000000000e+05,2.747551538050174713e-03,9.972524046897888184e-01 +6.255000000000000000e+05,6.159014301374554634e-04,9.993840456008911133e-01 +6.260000000000000000e+05,6.172860594233497977e-05,9.999382495880126953e-01 +6.265000000000000000e+05,1.830432302085682750e-04,9.998169541358947754e-01 +6.270000000000000000e+05,8.180966251529753208e-04,9.991819262504577637e-01 +6.275000000000000000e+05,2.698432072065770626e-04,9.997301697731018066e-01 +6.280000000000000000e+05,1.034328888636082411e-04,9.998965263366699219e-01 +6.285000000000000000e+05,1.235252566402778029e-04,9.998764991760253906e-01 +6.290000000000000000e+05,9.512885590083897114e-04,9.990486502647399902e-01 +6.295000000000000000e+05,7.537445635534822941e-04,9.992462396621704102e-01 +6.300000000000000000e+05,2.183827891713008285e-04,9.997816681861877441e-01 +6.305000000000000000e+05,3.897831193171441555e-04,9.996102452278137207e-01 +6.310000000000000000e+05,2.632649266161024570e-04,9.997367262840270996e-01 +6.315000000000000000e+05,2.498277574777603149e-01,7.501722574234008789e-01 +6.320000000000000000e+05,4.903686791658401489e-02,9.509630799293518066e-01 +6.325000000000000000e+05,2.015865029534325004e-04,9.997984766960144043e-01 +6.330000000000000000e+05,2.321236068382859230e-03,9.976788163185119629e-01 +6.335000000000000000e+05,9.868765482679009438e-04,9.990130662918090820e-01 +6.340000000000000000e+05,1.135216371039859951e-04,9.998865127563476562e-01 +6.345000000000000000e+05,1.255704660252376925e-06,9.999986886978149414e-01 +6.350000000000000000e+05,1.307262922637164593e-03,9.986928105354309082e-01 +6.355000000000000000e+05,3.117502274108119309e-05,9.999687671661376953e-01 +6.360000000000000000e+05,1.102677633753046393e-04,9.998897314071655273e-01 +6.365000000000000000e+05,9.773422032594680786e-03,9.902265071868896484e-01 +6.370000000000000000e+05,1.025345921516418457e-02,9.897465705871582031e-01 +6.375000000000000000e+05,1.442336360923945904e-03,9.985576272010803223e-01 +6.380000000000000000e+05,2.042818377958610654e-04,9.997957348823547363e-01 +6.385000000000000000e+05,2.169753715861588717e-04,9.997829794883728027e-01 +6.390000000000000000e+05,2.700363053008913994e-03,9.972996115684509277e-01 +6.395000000000000000e+05,1.068696454167366028e-01,8.931303620338439941e-01 +6.400000000000000000e+05,4.035278409719467163e-02,9.596471786499023438e-01 +6.405000000000000000e+05,5.255641008261591196e-05,9.999474287033081055e-01 +6.410000000000000000e+05,2.227897755801677704e-03,9.977720379829406738e-01 +6.415000000000000000e+05,6.929807132109999657e-04,9.993070363998413086e-01 +6.420000000000000000e+05,1.216327678412199020e-02,9.878367781639099121e-01 +6.425000000000000000e+05,1.351299695670604706e-03,9.986487030982971191e-01 +6.430000000000000000e+05,2.325781679246574640e-04,9.997674822807312012e-01 +6.435000000000000000e+05,8.813708089292049408e-03,9.911863207817077637e-01 +6.440000000000000000e+05,3.328985720872879028e-02,9.667101502418518066e-01 +6.445000000000000000e+05,3.264043480157852173e-04,9.996736049652099609e-01 +6.450000000000000000e+05,4.355546552687883377e-03,9.956444501876831055e-01 +6.455000000000000000e+05,1.606901059858500957e-04,9.998393058776855469e-01 +6.460000000000000000e+05,1.024071797728538513e-01,8.975927829742431641e-01 +6.465000000000000000e+05,9.626470506191253662e-02,9.037352800369262695e-01 +6.470000000000000000e+05,2.536918036639690399e-04,9.997462630271911621e-01 +6.475000000000000000e+05,1.203126812470145524e-04,9.998797178268432617e-01 +6.480000000000000000e+05,2.612983109429478645e-03,9.973869919776916504e-01 +6.485000000000000000e+05,1.142749376595020294e-02,9.885724782943725586e-01 +6.490000000000000000e+05,9.970294777303934097e-04,9.990029931068420410e-01 +6.495000000000000000e+05,1.642832066863775253e-03,9.983571171760559082e-01 +6.500000000000000000e+05,6.852838851045817137e-05,9.999314546585083008e-01 +6.505000000000000000e+05,5.901334225200116634e-05,9.999409914016723633e-01 +6.510000000000000000e+05,6.747203413397073746e-03,9.932528138160705566e-01 +6.515000000000000000e+05,1.700991415418684483e-03,9.982990622520446777e-01 +6.520000000000000000e+05,1.918442896567285061e-03,9.980815649032592773e-01 +6.525000000000000000e+05,6.217776332050561905e-03,9.937822222709655762e-01 +6.530000000000000000e+05,1.033973647281527519e-03,9.989659786224365234e-01 +6.535000000000000000e+05,4.055958124808967113e-04,9.995943903923034668e-01 +6.540000000000000000e+05,1.167355570942163467e-03,9.988326430320739746e-01 +6.545000000000000000e+05,9.897391311824321747e-03,9.901026487350463867e-01 +6.550000000000000000e+05,1.458253804594278336e-02,9.854174852371215820e-01 +6.555000000000000000e+05,3.579678479582071304e-03,9.964203834533691406e-01 +6.560000000000000000e+05,5.692658305633813143e-05,9.999430179595947266e-01 +6.565000000000000000e+05,1.775137381628155708e-03,9.982249140739440918e-01 +6.570000000000000000e+05,1.690684002824127674e-03,9.983092546463012695e-01 +6.575000000000000000e+05,7.840183097869157791e-04,9.992159605026245117e-01 +6.580000000000000000e+05,4.885012003796873614e-06,9.999951124191284180e-01 +6.585000000000000000e+05,1.859069988131523132e-02,9.814093112945556641e-01 +6.590000000000000000e+05,9.562557097524404526e-04,9.990437626838684082e-01 +6.595000000000000000e+05,8.730940520763397217e-03,9.912689924240112305e-01 +6.600000000000000000e+05,4.734256363008171320e-04,9.995266199111938477e-01 +6.605000000000000000e+05,2.263627573847770691e-02,9.773637056350708008e-01 +6.610000000000000000e+05,9.993048906326293945e-01,6.950740935280919075e-04 +6.615000000000000000e+05,9.991408586502075195e-01,8.591482182964682579e-04 +6.620000000000000000e+05,9.630151391029357910e-01,3.698485344648361206e-02 +6.625000000000000000e+05,4.809084348380565643e-03,9.951909780502319336e-01 +6.630000000000000000e+05,9.913041591644287109e-01,8.695808239281177521e-03 +6.635000000000000000e+05,9.986206293106079102e-01,1.379393390379846096e-03 +6.640000000000000000e+05,6.353929638862609863e-01,3.646070361137390137e-01 +6.645000000000000000e+05,6.464538723230361938e-02,9.353545904159545898e-01 +6.650000000000000000e+05,4.093083739280700684e-02,9.590691924095153809e-01 +6.655000000000000000e+05,9.737521409988403320e-01,2.624781616032123566e-02 +6.660000000000000000e+05,6.769832968711853027e-01,3.230167031288146973e-01 +6.665000000000000000e+05,9.988526105880737305e-01,1.147360075265169144e-03 +6.670000000000000000e+05,9.993909597396850586e-01,6.090108654461801052e-04 +6.675000000000000000e+05,9.976601600646972656e-01,2.339794067665934563e-03 +6.680000000000000000e+05,3.468113839626312256e-01,6.531885862350463867e-01 +6.685000000000000000e+05,4.327523112297058105e-01,5.672476887702941895e-01 +6.690000000000000000e+05,7.813374395482242107e-04,9.992187023162841797e-01 +6.695000000000000000e+05,5.192323587834835052e-04,9.994807839393615723e-01 +6.700000000000000000e+05,3.952757716178894043e-01,6.047242879867553711e-01 +6.705000000000000000e+05,6.964887492358684540e-03,9.930351972579956055e-01 +6.710000000000000000e+05,4.404389474075287580e-04,9.995595812797546387e-01 +6.715000000000000000e+05,9.585720486938953400e-03,9.904143214225769043e-01 +6.720000000000000000e+05,2.369932532310485840e-01,7.630068063735961914e-01 +6.725000000000000000e+05,2.689971355721354485e-03,9.973100423812866211e-01 +6.730000000000000000e+05,9.306654101237654686e-04,9.990693926811218262e-01 +6.735000000000000000e+05,4.770644754171371460e-02,9.522935152053833008e-01 +6.740000000000000000e+05,9.978524446487426758e-01,2.147581661120057106e-03 +6.745000000000000000e+05,6.918871998786926270e-01,3.081128001213073730e-01 +6.750000000000000000e+05,9.383943080902099609e-01,6.160569936037063599e-02 +6.755000000000000000e+05,9.989241957664489746e-01,1.075793872587382793e-03 +6.760000000000000000e+05,9.999978542327880859e-01,2.102763346556457691e-06 +6.765000000000000000e+05,9.996135830879211426e-01,3.864410391543060541e-04 +6.770000000000000000e+05,8.065032958984375000e-02,9.193496704101562500e-01 +6.775000000000000000e+05,2.209247089922428131e-02,9.779075384140014648e-01 +6.780000000000000000e+05,1.699104905128479004e-02,9.830089211463928223e-01 +6.785000000000000000e+05,9.720202684402465820e-01,2.797974273562431335e-02 +6.790000000000000000e+05,9.567800164222717285e-01,4.321996495127677917e-02 +6.795000000000000000e+05,4.446796476840972900e-01,5.553203225135803223e-01 +6.800000000000000000e+05,2.731832861900329590e-02,9.726816415786743164e-01 +6.805000000000000000e+05,9.147412143647670746e-03,9.908526539802551270e-01 +6.810000000000000000e+05,1.082324539311230183e-03,9.989176988601684570e-01 +6.815000000000000000e+05,9.995535016059875488e-01,4.464938829187303782e-04 +6.820000000000000000e+05,1.000000000000000000e+00,2.751257177635579865e-08 +6.825000000000000000e+05,9.999556541442871094e-01,4.434381844475865364e-05 +6.830000000000000000e+05,2.844902575016021729e-01,7.155097126960754395e-01 +6.835000000000000000e+05,8.191840606741607189e-04,9.991808533668518066e-01 +6.840000000000000000e+05,5.741587756347144023e-06,9.999942779541015625e-01 +6.845000000000000000e+05,5.891448745387606323e-05,9.999411106109619141e-01 +6.850000000000000000e+05,3.379122018814086914e-01,6.620877981185913086e-01 +6.855000000000000000e+05,7.225472927093505859e-01,2.774527370929718018e-01 +6.860000000000000000e+05,1.214984357357025146e-01,8.785015940666198730e-01 +6.865000000000000000e+05,2.154339337721467018e-03,9.978456497192382812e-01 +6.870000000000000000e+05,2.583301393315196037e-03,9.974167346954345703e-01 +6.875000000000000000e+05,1.223162747919559479e-03,9.987768530845642090e-01 +6.880000000000000000e+05,1.483014086261391640e-03,9.985169768333435059e-01 +6.885000000000000000e+05,1.247414294630289078e-02,9.875259399414062500e-01 +6.890000000000000000e+05,4.894713638350367546e-04,9.995105266571044922e-01 +6.895000000000000000e+05,5.527504254132509232e-04,9.994472861289978027e-01 +6.900000000000000000e+05,4.420226905494928360e-04,9.995579123497009277e-01 +6.905000000000000000e+05,4.597684528562240303e-05,9.999539852142333984e-01 +6.910000000000000000e+05,4.558787622954696417e-04,9.995440840721130371e-01 +6.915000000000000000e+05,6.348614115267992020e-04,9.993651509284973145e-01 +6.920000000000000000e+05,7.295478135347366333e-04,9.992704987525939941e-01 +6.925000000000000000e+05,1.574512716615572572e-04,9.998425245285034180e-01 +6.930000000000000000e+05,6.261718226596713066e-04,9.993738532066345215e-01 +6.935000000000000000e+05,1.283571618841961026e-04,9.998716115951538086e-01 +6.940000000000000000e+05,4.355676646810024977e-04,9.995644688606262207e-01 +6.945000000000000000e+05,9.946868419647216797e-01,5.313198547810316086e-03 +6.950000000000000000e+05,9.999895095825195312e-01,1.048708236339734867e-05 +6.955000000000000000e+05,9.987726807594299316e-01,1.227243687026202679e-03 +6.960000000000000000e+05,3.768633306026458740e-01,6.231366991996765137e-01 +6.965000000000000000e+05,9.982277750968933105e-01,1.772187300957739353e-03 +6.970000000000000000e+05,9.830442070960998535e-01,1.695578172802925110e-02 +6.975000000000000000e+05,7.290251255035400391e-01,2.709748148918151855e-01 +6.980000000000000000e+05,8.525780285708606243e-04,9.991474151611328125e-01 +6.985000000000000000e+05,6.639001076109707355e-04,9.993360638618469238e-01 +6.990000000000000000e+05,2.530182013288140297e-03,9.974697828292846680e-01 +6.995000000000000000e+05,9.998247027397155762e-01,1.752764801494777203e-04 +7.000000000000000000e+05,9.997829794883728027e-01,2.170134393963962793e-04 +7.005000000000000000e+05,9.707121253013610840e-01,2.928783372044563293e-02 +7.010000000000000000e+05,1.289960928261280060e-03,9.987100362777709961e-01 +7.015000000000000000e+05,2.545365532569121569e-05,9.999744892120361328e-01 +7.020000000000000000e+05,9.793657955015078187e-05,9.999020099639892578e-01 +7.025000000000000000e+05,5.691436305642127991e-02,9.430856108665466309e-01 +7.030000000000000000e+05,5.461041256785392761e-02,9.453895688056945801e-01 +7.035000000000000000e+05,9.071321692317724228e-04,9.990929365158081055e-01 +7.040000000000000000e+05,3.978048916906118393e-03,9.960219264030456543e-01 +7.045000000000000000e+05,1.943076401948928833e-02,9.805692434310913086e-01 +7.050000000000000000e+05,1.033681182889267802e-04,9.998966455459594727e-01 +7.055000000000000000e+05,9.575457079336047173e-04,9.990424513816833496e-01 +7.060000000000000000e+05,6.206905469298362732e-03,9.937931299209594727e-01 +7.065000000000000000e+05,5.790477152913808823e-04,9.994210004806518555e-01 +7.070000000000000000e+05,5.065231816843152046e-04,9.994934797286987305e-01 +7.075000000000000000e+05,2.711603010538965464e-04,9.997288584709167480e-01 +7.080000000000000000e+05,2.880213105527218431e-05,9.999711513519287109e-01 +7.085000000000000000e+05,2.941113198176026344e-03,9.970588684082031250e-01 +7.090000000000000000e+05,2.823579357936978340e-03,9.971764087677001953e-01 +7.095000000000000000e+05,9.960263967514038086e-01,3.973611630499362946e-03 +7.100000000000000000e+05,9.979819059371948242e-01,2.018120838329195976e-03 +7.105000000000000000e+05,1.473795622587203979e-03,9.985262155532836914e-01 +7.110000000000000000e+05,9.055803418159484863e-01,9.441968798637390137e-02 +7.115000000000000000e+05,9.998301267623901367e-01,1.698796986602246761e-04 +7.120000000000000000e+05,9.071497917175292969e-01,9.285018593072891235e-02 +7.125000000000000000e+05,1.687966473400592804e-02,9.831203222274780273e-01 +7.130000000000000000e+05,1.575920172035694122e-02,9.842408299446105957e-01 +7.135000000000000000e+05,3.247440617997199297e-04,9.996752738952636719e-01 +7.140000000000000000e+05,4.098963108845055103e-04,9.995900988578796387e-01 +7.145000000000000000e+05,8.155128452926874161e-04,9.991844296455383301e-01 +7.150000000000000000e+05,9.765432476997375488e-01,2.345675975084304810e-02 +7.155000000000000000e+05,9.735311269760131836e-01,2.646886371076107025e-02 +7.160000000000000000e+05,3.242249786853790283e-01,6.757749915122985840e-01 +7.165000000000000000e+05,9.890180826187133789e-01,1.098197046667337418e-02 +7.170000000000000000e+05,4.200755953788757324e-01,5.799244046211242676e-01 +7.175000000000000000e+05,2.456898428499698639e-02,9.754309654235839844e-01 +7.180000000000000000e+05,9.957928657531738281e-01,4.207077436149120331e-03 +7.185000000000000000e+05,9.999657869338989258e-01,3.422423833399079740e-05 +7.190000000000000000e+05,4.893600344657897949e-01,5.106400251388549805e-01 +7.195000000000000000e+05,2.954589726869016886e-04,9.997045397758483887e-01 +7.200000000000000000e+05,6.998313963413238525e-02,9.300168752670288086e-01 +7.205000000000000000e+05,9.536085128784179688e-01,4.639146104454994202e-02 +7.210000000000000000e+05,4.405703395605087280e-02,9.559429883956909180e-01 +7.215000000000000000e+05,2.116451505571603775e-03,9.978834986686706543e-01 +7.220000000000000000e+05,1.273652771487832069e-03,9.987263083457946777e-01 +7.225000000000000000e+05,4.316008707974106073e-04,9.995684027671813965e-01 +7.230000000000000000e+05,8.074801880866289139e-04,9.991925358772277832e-01 +7.235000000000000000e+05,4.575636088848114014e-01,5.424364209175109863e-01 +7.240000000000000000e+05,9.999998807907104492e-01,1.363689960953706759e-07 +7.245000000000000000e+05,1.000000000000000000e+00,2.095214002295620048e-09 +7.250000000000000000e+05,9.948799610137939453e-01,5.120045505464076996e-03 +7.255000000000000000e+05,9.675528854131698608e-03,9.903244972229003906e-01 +7.260000000000000000e+05,9.939839243888854980e-01,6.016066297888755798e-03 +7.265000000000000000e+05,9.780615568161010742e-01,2.193848229944705963e-02 +7.270000000000000000e+05,4.339556617196649313e-04,9.995660185813903809e-01 +7.275000000000000000e+05,2.396353520452976227e-03,9.976037144660949707e-01 +7.280000000000000000e+05,4.303586669266223907e-03,9.956964254379272461e-01 +7.285000000000000000e+05,6.630301475524902344e-01,3.369699120521545410e-01 +7.290000000000000000e+05,9.929357171058654785e-01,7.064226083457469940e-03 +7.295000000000000000e+05,9.802787303924560547e-01,1.972124911844730377e-02 +7.300000000000000000e+05,8.523876667022705078e-01,1.476122736930847168e-01 +7.305000000000000000e+05,5.724359303712844849e-03,9.942756295204162598e-01 +7.310000000000000000e+05,9.304774999618530273e-01,6.952257454395294189e-02 +7.315000000000000000e+05,9.535906314849853516e-01,4.640939831733703613e-02 +7.320000000000000000e+05,9.782007932662963867e-01,2.179921418428421021e-02 +7.325000000000000000e+05,9.228014945983886719e-01,7.719854265451431274e-02 +7.330000000000000000e+05,6.912988424301147461e-01,3.087011873722076416e-01 +7.335000000000000000e+05,9.370020707137882710e-04,9.990629553794860840e-01 +7.340000000000000000e+05,5.284074693918228149e-03,9.947158694267272949e-01 +7.345000000000000000e+05,9.716914064483717084e-05,9.999028444290161133e-01 +7.350000000000000000e+05,9.871696233749389648e-01,1.283034216612577438e-02 +7.355000000000000000e+05,9.964092373847961426e-01,3.590801730751991272e-03 +7.360000000000000000e+05,7.595012187957763672e-01,2.404988259077072144e-01 +7.365000000000000000e+05,3.429957723710685968e-04,9.996570348739624023e-01 +7.370000000000000000e+05,1.637136447243392467e-04,9.998362064361572266e-01 +7.375000000000000000e+05,9.916478395462036133e-02,9.008352160453796387e-01 +7.380000000000000000e+05,9.903048276901245117e-01,9.695089422166347504e-03 +7.385000000000000000e+05,9.766601324081420898e-01,2.333987690508365631e-02 +7.390000000000000000e+05,8.948951959609985352e-01,1.051047965884208679e-01 +7.395000000000000000e+05,8.623391985893249512e-01,1.376608163118362427e-01 +7.400000000000000000e+05,9.725801944732666016e-01,2.741977944970130920e-02 +7.405000000000000000e+05,9.982490539550781250e-01,1.750996685586869717e-03 +7.410000000000000000e+05,9.479482173919677734e-01,5.205182731151580811e-02 +7.415000000000000000e+05,1.677613181527704000e-04,9.998321533203125000e-01 +7.420000000000000000e+05,2.476037666201591492e-02,9.752395749092102051e-01 +7.425000000000000000e+05,7.566502690315246582e-02,9.243350028991699219e-01 +7.430000000000000000e+05,4.972738679498434067e-05,9.999502897262573242e-01 +7.435000000000000000e+05,1.302743330597877502e-04,9.998697042465209961e-01 +7.440000000000000000e+05,1.721485459711402655e-04,9.998278617858886719e-01 +7.445000000000000000e+05,1.104532784665934741e-04,9.998894929885864258e-01 +7.450000000000000000e+05,1.801028556656092405e-04,9.998199343681335449e-01 +7.455000000000000000e+05,1.344944350421428680e-03,9.986550807952880859e-01 +7.460000000000000000e+05,3.387999022379517555e-03,9.966120123863220215e-01 +7.465000000000000000e+05,4.250181913375854492e-01,5.749817490577697754e-01 +7.470000000000000000e+05,7.924222350120544434e-01,2.075777649879455566e-01 +7.475000000000000000e+05,8.596427440643310547e-01,1.403572559356689453e-01 +7.480000000000000000e+05,9.682108759880065918e-01,3.178909793496131897e-02 +7.485000000000000000e+05,8.942428827285766602e-01,1.057571545243263245e-01 +7.490000000000000000e+05,9.985992312431335449e-01,1.400777022354304790e-03 +7.495000000000000000e+05,9.119814634323120117e-01,8.801851421594619751e-02 +7.500000000000000000e+05,1.346167549490928650e-02,9.865382909774780273e-01 +7.505000000000000000e+05,9.835211038589477539e-01,1.647884398698806763e-02 +7.510000000000000000e+05,9.959732890129089355e-01,4.026670940220355988e-03 +7.515000000000000000e+05,2.284026741981506348e-01,7.715973258018493652e-01 +7.520000000000000000e+05,8.733345270156860352e-01,1.266654133796691895e-01 +7.525000000000000000e+05,2.104863233398646116e-04,9.997895359992980957e-01 +7.530000000000000000e+05,4.673117771744728088e-03,9.953268766403198242e-01 +7.535000000000000000e+05,7.553661125712096691e-04,9.992446899414062500e-01 +7.540000000000000000e+05,2.553127123974263668e-04,9.997447133064270020e-01 +7.545000000000000000e+05,2.409822940826416016e-01,7.590177655220031738e-01 +7.550000000000000000e+05,2.446904079988598824e-03,9.975531697273254395e-01 +7.555000000000000000e+05,4.314501769840717316e-03,9.956855773925781250e-01 +7.560000000000000000e+05,1.520876120775938034e-02,9.847912192344665527e-01 +7.565000000000000000e+05,6.400450947694480419e-04,9.993599057197570801e-01 +7.570000000000000000e+05,8.367820382118225098e-01,1.632179170846939087e-01 +7.575000000000000000e+05,8.530686497688293457e-01,1.469313353300094604e-01 +7.580000000000000000e+05,1.073991064913570881e-03,9.989259839057922363e-01 +7.585000000000000000e+05,9.586270607542246580e-06,9.999904632568359375e-01 +7.590000000000000000e+05,1.722573651932179928e-03,9.982774257659912109e-01 +7.595000000000000000e+05,3.424334106966853142e-03,9.965756535530090332e-01 +7.600000000000000000e+05,6.992915295995771885e-04,9.993007183074951172e-01 +7.605000000000000000e+05,9.815816879272460938e-01,1.841832697391510010e-02 +7.610000000000000000e+05,9.902310967445373535e-01,9.768900461494922638e-03 +7.615000000000000000e+05,9.922702312469482422e-01,7.729753851890563965e-03 +7.620000000000000000e+05,9.577537775039672852e-01,4.224626719951629639e-02 +7.625000000000000000e+05,7.275474071502685547e-01,2.724525630474090576e-01 +7.630000000000000000e+05,1.361246854066848755e-01,8.638753294944763184e-01 +7.635000000000000000e+05,1.383539056405425072e-04,9.998615980148315430e-01 +7.640000000000000000e+05,5.015735223423689604e-05,9.999498128890991211e-01 +7.645000000000000000e+05,1.921034068800508976e-03,9.980789422988891602e-01 +7.650000000000000000e+05,5.820112419314682484e-04,9.994180202484130859e-01 +7.655000000000000000e+05,1.153068369603715837e-04,9.998847246170043945e-01 +7.660000000000000000e+05,3.581957891583442688e-03,9.964179992675781250e-01 +7.665000000000000000e+05,4.974826704710721970e-03,9.950250983238220215e-01 +7.670000000000000000e+05,9.933578372001647949e-01,6.642210762947797775e-03 +7.675000000000000000e+05,9.967507123947143555e-01,3.249260596930980682e-03 +7.680000000000000000e+05,9.593943953514099121e-01,4.060557484626770020e-02 +7.685000000000000000e+05,2.583442488685250282e-03,9.974164962768554688e-01 +7.690000000000000000e+05,1.126319053582847118e-03,9.988736510276794434e-01 +7.695000000000000000e+05,5.302634555846452713e-03,9.946973323822021484e-01 +7.700000000000000000e+05,1.134781166911125183e-03,9.988652467727661133e-01 +7.705000000000000000e+05,3.087861114181578159e-04,9.996912479400634766e-01 +7.710000000000000000e+05,4.936413373798131943e-03,9.950636029243469238e-01 +7.715000000000000000e+05,9.494499564170837402e-01,5.055002123117446899e-02 +7.720000000000000000e+05,9.781606793403625488e-01,2.183928899466991425e-02 +7.725000000000000000e+05,9.940041899681091309e-01,5.995832383632659912e-03 +7.730000000000000000e+05,3.535045087337493896e-01,6.464954614639282227e-01 +7.735000000000000000e+05,3.291406482458114624e-02,9.670859575271606445e-01 +7.740000000000000000e+05,3.572518180590122938e-04,9.996427297592163086e-01 +7.745000000000000000e+05,2.671262773219496012e-04,9.997329115867614746e-01 +7.750000000000000000e+05,2.549760683905333281e-04,9.997450709342956543e-01 +7.755000000000000000e+05,2.446542493999004364e-02,9.755345582962036133e-01 +7.760000000000000000e+05,2.874657884240150452e-02,9.712533950805664062e-01 +7.765000000000000000e+05,8.698762976564466953e-04,9.991301894187927246e-01 +7.770000000000000000e+05,4.829923156648874283e-03,9.951700568199157715e-01 +7.775000000000000000e+05,6.176737952046096325e-04,9.993822574615478516e-01 +7.780000000000000000e+05,6.177483871579170227e-02,9.382251501083374023e-01 +7.785000000000000000e+05,9.981952309608459473e-01,1.804765081033110619e-03 +7.790000000000000000e+05,9.991973042488098145e-01,8.027067524380981922e-04 +7.795000000000000000e+05,9.784966707229614258e-01,2.150336652994155884e-02 +7.800000000000000000e+05,1.308962237089872360e-03,9.986910223960876465e-01 +7.805000000000000000e+05,9.998751878738403320e-01,1.247503823833540082e-04 +7.810000000000000000e+05,1.000000000000000000e+00,4.890998184237105306e-08 +7.815000000000000000e+05,9.999665021896362305e-01,3.346191806485876441e-05 +7.820000000000000000e+05,1.166636502603068948e-04,9.998832941055297852e-01 +7.825000000000000000e+05,2.135600661858916283e-04,9.997864365577697754e-01 +7.830000000000000000e+05,5.219419836066663265e-05,9.999477863311767578e-01 +7.835000000000000000e+05,1.078608096577227116e-03,9.989213943481445312e-01 +7.840000000000000000e+05,7.274063591466983780e-06,9.999927282333374023e-01 +7.845000000000000000e+05,1.749370421748608351e-04,9.998250603675842285e-01 +7.850000000000000000e+05,3.277143090963363647e-02,9.672285914421081543e-01 +7.855000000000000000e+05,9.985717535018920898e-01,1.428183750249445438e-03 +7.860000000000000000e+05,9.983896017074584961e-01,1.610417151823639870e-03 +7.865000000000000000e+05,9.257189631462097168e-01,7.428102940320968628e-02 +7.870000000000000000e+05,9.999197721481323242e-01,8.022313704714179039e-05 +7.875000000000000000e+05,9.988309741020202637e-01,1.169001217931509018e-03 +7.880000000000000000e+05,2.923470735549926758e-01,7.076528668403625488e-01 +7.885000000000000000e+05,9.987548589706420898e-01,1.245145802386105061e-03 +7.890000000000000000e+05,9.885107278823852539e-01,1.148922275751829147e-02 +7.895000000000000000e+05,9.987022876739501953e-01,1.297721872106194496e-03 +7.900000000000000000e+05,9.879724383354187012e-01,1.202763151377439499e-02 +7.905000000000000000e+05,9.838109612464904785e-01,1.618902012705802917e-02 +7.910000000000000000e+05,9.937841892242431641e-01,6.215817295014858246e-03 +7.915000000000000000e+05,9.941952824592590332e-01,5.804744083434343338e-03 +7.920000000000000000e+05,1.544881425797939301e-02,9.845511317253112793e-01 +7.925000000000000000e+05,8.073337994574103504e-06,9.999918937683105469e-01 +7.930000000000000000e+05,6.657388992607593536e-03,9.933425784111022949e-01 +7.935000000000000000e+05,9.412065744400024414e-01,5.879338458180427551e-02 +7.940000000000000000e+05,9.732262492179870605e-01,2.677368000149726868e-02 +7.945000000000000000e+05,9.946856498718261719e-01,5.314314737915992737e-03 +7.950000000000000000e+05,9.685245156288146973e-01,3.147540986537933350e-02 +7.955000000000000000e+05,4.289637207984924316e-01,5.710363388061523438e-01 +7.960000000000000000e+05,1.129794036387465894e-04,9.998869895935058594e-01 +7.965000000000000000e+05,8.920205291360616684e-04,9.991080164909362793e-01 +7.970000000000000000e+05,9.347380995750427246e-01,6.526190787553787231e-02 +7.975000000000000000e+05,9.999995231628417969e-01,4.746466402139049023e-07 +7.980000000000000000e+05,9.999955892562866211e-01,4.398670625960221514e-06 +7.985000000000000000e+05,9.998919963836669922e-01,1.079378198483027518e-04 +7.990000000000000000e+05,9.987967014312744141e-01,1.203317428007721901e-03 +7.995000000000000000e+05,9.963176250457763672e-01,3.682309994474053383e-03 +8.000000000000000000e+05,9.838988184928894043e-01,1.610125973820686340e-02 +8.005000000000000000e+05,9.495295286178588867e-01,5.047053098678588867e-02 +8.010000000000000000e+05,9.692329168319702148e-01,3.076712973415851593e-02 +8.015000000000000000e+05,6.129258871078491211e-01,3.870740830898284912e-01 +8.020000000000000000e+05,9.886843562126159668e-01,1.131567452102899551e-02 +8.025000000000000000e+05,9.951471686363220215e-01,4.852865356951951981e-03 +8.030000000000000000e+05,1.557528879493474960e-02,9.844247102737426758e-01 +8.035000000000000000e+05,1.151329488493502140e-03,9.988486766815185547e-01 +8.040000000000000000e+05,1.869332918431609869e-04,9.998131394386291504e-01 +8.045000000000000000e+05,4.893057048320770264e-03,9.951069355010986328e-01 +8.050000000000000000e+05,1.004757476039230824e-03,9.989952445030212402e-01 +8.055000000000000000e+05,7.571932655991986394e-05,9.999243021011352539e-01 +8.060000000000000000e+05,8.184969192370772362e-04,9.991814494132995605e-01 +8.065000000000000000e+05,5.741302371025085449e-01,4.258697330951690674e-01 +8.070000000000000000e+05,9.888440370559692383e-01,1.115601137280464172e-02 +8.075000000000000000e+05,9.132331609725952148e-01,8.676683902740478516e-02 +8.080000000000000000e+05,7.085374090820550919e-05,9.999291896820068359e-01 +8.085000000000000000e+05,8.755182352615520358e-05,9.999123811721801758e-01 +8.090000000000000000e+05,2.301862696185708046e-03,9.976981282234191895e-01 +8.095000000000000000e+05,1.060600113123655319e-02,9.893940091133117676e-01 +8.100000000000000000e+05,1.420647837221622467e-03,9.985793828964233398e-01 +8.105000000000000000e+05,1.626771518203895539e-05,9.999837875366210938e-01 +8.110000000000000000e+05,1.422286877641454339e-04,9.998577833175659180e-01 +8.115000000000000000e+05,1.827112282626330853e-03,9.981728792190551758e-01 +8.120000000000000000e+05,3.617248730733990669e-03,9.963827133178710938e-01 +8.125000000000000000e+05,8.758388757705688477e-01,1.241610646247863770e-01 +8.130000000000000000e+05,9.999908208847045898e-01,9.121657967625651509e-06 +8.135000000000000000e+05,9.999510049819946289e-01,4.905194873572327197e-05 +8.140000000000000000e+05,9.999696016311645508e-01,3.041050695173908025e-05 +8.145000000000000000e+05,9.976777434349060059e-01,2.322274260222911835e-03 +8.150000000000000000e+05,2.073625475168228149e-01,7.926374077796936035e-01 +8.155000000000000000e+05,4.449187964200973511e-02,9.555080533027648926e-01 +8.160000000000000000e+05,1.331246457993984222e-02,9.866874814033508301e-01 +8.165000000000000000e+05,9.999171495437622070e-01,8.290188270621001720e-05 +8.170000000000000000e+05,9.999834299087524414e-01,1.653268373047467321e-05 +8.175000000000000000e+05,9.994200468063354492e-01,5.800272920168936253e-04 +8.180000000000000000e+05,7.449164986610412598e-02,9.255083799362182617e-01 +8.185000000000000000e+05,1.193577336380258203e-04,9.998806715011596680e-01 +8.190000000000000000e+05,1.221712008118629456e-01,8.778288364410400391e-01 +8.195000000000000000e+05,1.176953222602605820e-02,9.882304668426513672e-01 +8.200000000000000000e+05,2.006134837865829468e-01,7.993865609169006348e-01 +8.205000000000000000e+05,3.210743367671966553e-01,6.789256334304809570e-01 +8.210000000000000000e+05,2.650691382586956024e-02,9.734930396080017090e-01 +8.215000000000000000e+05,1.866730302572250366e-01,8.133269548416137695e-01 +8.220000000000000000e+05,1.916174590587615967e-02,9.808382391929626465e-01 +8.225000000000000000e+05,1.072690938599407673e-03,9.989272952079772949e-01 +8.230000000000000000e+05,8.975231321528553963e-05,9.999102354049682617e-01 +8.235000000000000000e+05,1.017917093122377992e-04,9.998981952667236328e-01 +8.240000000000000000e+05,1.843816367909312248e-03,9.981561303138732910e-01 +8.245000000000000000e+05,4.248724319040775299e-03,9.957512617111206055e-01 +8.250000000000000000e+05,2.702282276004552841e-03,9.972977042198181152e-01 +8.255000000000000000e+05,6.995526142418384552e-03,9.930045008659362793e-01 +8.260000000000000000e+05,4.016463935840874910e-04,9.995983242988586426e-01 +8.265000000000000000e+05,3.962796836276538670e-05,9.999604225158691406e-01 +8.270000000000000000e+05,1.220887154340744019e-01,8.779112696647644043e-01 +8.275000000000000000e+05,8.663648623041808605e-04,9.991336464881896973e-01 +8.280000000000000000e+05,9.231102885678410530e-04,9.990768432617187500e-01 +8.285000000000000000e+05,9.707853794097900391e-01,2.921462804079055786e-02 +8.290000000000000000e+05,9.999842643737792969e-01,1.572371547808870673e-05 +8.295000000000000000e+05,9.997543692588806152e-01,2.456609217915683985e-04 +8.300000000000000000e+05,4.083146750926971436e-01,5.916852951049804688e-01 +8.305000000000000000e+05,1.218039840459823608e-01,8.781960010528564453e-01 +8.310000000000000000e+05,9.939584732055664062e-01,6.041560787707567215e-03 +8.315000000000000000e+05,9.984247684478759766e-01,1.575300004333257675e-03 +8.320000000000000000e+05,9.293782711029052734e-01,7.062175869941711426e-02 +8.325000000000000000e+05,1.971978927031159401e-03,9.980279803276062012e-01 +8.330000000000000000e+05,3.914125263690948486e-04,9.996085762977600098e-01 +8.335000000000000000e+05,3.198075294494628906e-02,9.680192470550537109e-01 +8.340000000000000000e+05,1.268613268621265888e-04,9.998731613159179688e-01 +8.345000000000000000e+05,9.998554596677422523e-04,9.990001320838928223e-01 +8.350000000000000000e+05,3.140565007925033569e-02,9.685943126678466797e-01 +8.355000000000000000e+05,1.314986380748450756e-03,9.986850619316101074e-01 +8.360000000000000000e+05,1.359369798592524603e-05,9.999864101409912109e-01 +8.365000000000000000e+05,1.124399015679955482e-03,9.988755583763122559e-01 +8.370000000000000000e+05,1.398407272063195705e-03,9.986016154289245605e-01 +8.375000000000000000e+05,5.147673655301332474e-03,9.948523640632629395e-01 +8.380000000000000000e+05,3.717225510627031326e-03,9.962827563285827637e-01 +8.385000000000000000e+05,1.737801067065447569e-04,9.998262524604797363e-01 +8.390000000000000000e+05,1.262710546143352985e-03,9.987372756004333496e-01 +8.395000000000000000e+05,1.069132471457123756e-03,9.989308714866638184e-01 +8.400000000000000000e+05,1.292468397878110409e-03,9.987075328826904297e-01 +8.405000000000000000e+05,5.368291749618947506e-04,9.994631409645080566e-01 +8.410000000000000000e+05,3.966392832808196545e-04,9.996033310890197754e-01 +8.415000000000000000e+05,1.514442992629483342e-04,9.998484849929809570e-01 +8.420000000000000000e+05,9.966003298759460449e-01,3.399701556190848351e-03 +8.425000000000000000e+05,9.989445805549621582e-01,1.055421656928956509e-03 +8.430000000000000000e+05,1.876143738627433777e-02,9.812384843826293945e-01 +8.435000000000000000e+05,5.132867590873502195e-05,9.999486207962036133e-01 +8.440000000000000000e+05,1.836174749769270420e-03,9.981638789176940918e-01 +8.445000000000000000e+05,1.380400732159614563e-03,9.986195564270019531e-01 +8.450000000000000000e+05,3.290953347459435463e-03,9.967089891433715820e-01 +8.455000000000000000e+05,5.153929232619702816e-04,9.994845390319824219e-01 +8.460000000000000000e+05,8.415810298174619675e-04,9.991583824157714844e-01 +8.465000000000000000e+05,1.103178504854440689e-02,9.889681935310363770e-01 +8.470000000000000000e+05,5.663775664288550615e-05,9.999433755874633789e-01 +8.475000000000000000e+05,5.325286765582859516e-04,9.994674324989318848e-01 +8.480000000000000000e+05,2.222179318778216839e-04,9.997777342796325684e-01 +8.485000000000000000e+05,8.237513829953968525e-04,9.991762042045593262e-01 +8.490000000000000000e+05,7.810205817222595215e-01,2.189793735742568970e-01 +8.495000000000000000e+05,9.979609251022338867e-01,2.039037179201841354e-03 +8.500000000000000000e+05,9.097414016723632812e-01,9.025863558053970337e-02 +8.505000000000000000e+05,1.413815625710412860e-04,9.998586177825927734e-01 +8.510000000000000000e+05,1.650862250244244933e-04,9.998348951339721680e-01 +8.515000000000000000e+05,8.262914419174194336e-01,1.737085431814193726e-01 +8.520000000000000000e+05,1.520463451743125916e-02,9.847953319549560547e-01 +8.525000000000000000e+05,3.338232263922691345e-02,9.666177034378051758e-01 +8.530000000000000000e+05,8.126138709485530853e-03,9.918739199638366699e-01 +8.535000000000000000e+05,9.507803770247846842e-05,9.999048709869384766e-01 +8.540000000000000000e+05,5.274462164379656315e-04,9.994725584983825684e-01 +8.545000000000000000e+05,6.668567657470703125e-04,9.993330836296081543e-01 +8.550000000000000000e+05,1.107657095417380333e-03,9.988923668861389160e-01 +8.555000000000000000e+05,9.214763343334197998e-02,9.078522920608520508e-01 +8.560000000000000000e+05,2.163235247135162354e-01,7.836765050888061523e-01 +8.565000000000000000e+05,8.600807632319629192e-04,9.991399049758911133e-01 +8.570000000000000000e+05,1.580945914611220360e-03,9.984190464019775391e-01 +8.575000000000000000e+05,8.147548069246113300e-04,9.991852641105651855e-01 +8.580000000000000000e+05,7.938432972878217697e-04,9.992061257362365723e-01 +8.585000000000000000e+05,2.494659856893122196e-04,9.997505545616149902e-01 +8.590000000000000000e+05,7.420802721753716469e-04,9.992578625679016113e-01 +8.595000000000000000e+05,2.000999171286821365e-03,9.979990124702453613e-01 +8.600000000000000000e+05,1.897594047477468848e-04,9.998102784156799316e-01 +8.605000000000000000e+05,7.875739829614758492e-04,9.992123842239379883e-01 +8.610000000000000000e+05,1.292848173761740327e-04,9.998706579208374023e-01 +8.615000000000000000e+05,2.330916177015751600e-04,9.997668862342834473e-01 +8.620000000000000000e+05,8.254553191363811493e-03,9.917454123497009277e-01 +8.625000000000000000e+05,6.297748186625540257e-04,9.993702769279479980e-01 +8.630000000000000000e+05,1.929711550474166870e-03,9.980702996253967285e-01 +8.635000000000000000e+05,1.109554315917193890e-03,9.988904595375061035e-01 +8.640000000000000000e+05,2.515717060305178165e-04,9.997484087944030762e-01 +8.645000000000000000e+05,3.266741987317800522e-05,9.999673366546630859e-01 +8.650000000000000000e+05,5.615400732494890690e-05,9.999438524246215820e-01 +8.655000000000000000e+05,3.904120996594429016e-03,9.960959553718566895e-01 +8.660000000000000000e+05,4.686778083851095289e-06,9.999953508377075195e-01 +8.665000000000000000e+05,1.226197582582244650e-05,9.999877214431762695e-01 +8.670000000000000000e+05,2.488377736881375313e-03,9.975116252899169922e-01 +8.675000000000000000e+05,9.302327525801956654e-04,9.990697503089904785e-01 +8.680000000000000000e+05,1.012430366245098412e-04,9.998987913131713867e-01 +8.685000000000000000e+05,1.001571654342114925e-03,9.989984631538391113e-01 +8.690000000000000000e+05,8.824561722576618195e-03,9.911754131317138672e-01 +8.695000000000000000e+05,5.700467154383659363e-04,9.994298815727233887e-01 +8.700000000000000000e+05,5.030933767557144165e-02,9.496906399726867676e-01 +8.705000000000000000e+05,9.984017014503479004e-01,1.598296570591628551e-03 +8.710000000000000000e+05,9.944092631340026855e-01,5.590757355093955994e-03 +8.715000000000000000e+05,1.266607199795544147e-03,9.987333416938781738e-01 +8.720000000000000000e+05,9.995828270912170410e-01,4.171787295490503311e-04 +8.725000000000000000e+05,9.992895126342773438e-01,7.105177501216530800e-04 +8.730000000000000000e+05,9.894884228706359863e-01,1.051160413771867752e-02 +8.735000000000000000e+05,9.325066804885864258e-01,6.749334186315536499e-02 +8.740000000000000000e+05,1.870032399892807007e-02,9.812996983528137207e-01 +8.745000000000000000e+05,1.576612889766693115e-01,8.423387408256530762e-01 +8.750000000000000000e+05,3.860903671011328697e-03,9.961391091346740723e-01 +8.755000000000000000e+05,1.475187018513679504e-03,9.985248446464538574e-01 +8.760000000000000000e+05,5.208344664424657822e-03,9.947916269302368164e-01 +8.765000000000000000e+05,1.115849736379459500e-04,9.998884201049804688e-01 +8.770000000000000000e+05,1.641466369619593024e-04,9.998358488082885742e-01 +8.775000000000000000e+05,6.328918039798736572e-02,9.367108345031738281e-01 +8.780000000000000000e+05,6.384631991386413574e-01,3.615367710590362549e-01 +8.785000000000000000e+05,7.528777350671589375e-04,9.992471933364868164e-01 +8.790000000000000000e+05,2.975335228256881237e-04,9.997023940086364746e-01 +8.795000000000000000e+05,9.554067850112915039e-01,4.459324851632118225e-02 +8.800000000000000000e+05,2.009853124618530273e-01,7.990146875381469727e-01 +8.805000000000000000e+05,2.562054432928562164e-04,9.997437596321105957e-01 +8.810000000000000000e+05,2.173957618651911616e-04,9.997826218605041504e-01 +8.815000000000000000e+05,6.964231724850833416e-04,9.993035793304443359e-01 +8.820000000000000000e+05,5.653941771015524864e-04,9.994345307350158691e-01 +8.825000000000000000e+05,8.608993084635585546e-05,9.999139308929443359e-01 +8.830000000000000000e+05,1.402409397996962070e-04,9.998596906661987305e-01 +8.835000000000000000e+05,1.152551631093956530e-04,9.998847246170043945e-01 +8.840000000000000000e+05,3.119569737464189529e-04,9.996880292892456055e-01 +8.845000000000000000e+05,1.276625320315361023e-03,9.987233281135559082e-01 +8.850000000000000000e+05,1.798433135263621807e-03,9.982014894485473633e-01 +8.855000000000000000e+05,8.408503606915473938e-03,9.915914535522460938e-01 +8.860000000000000000e+05,3.305185586214065552e-03,9.966948032379150391e-01 +8.865000000000000000e+05,1.401042682118713856e-03,9.985989928245544434e-01 +8.870000000000000000e+05,4.810103564523160458e-04,9.995189905166625977e-01 +8.875000000000000000e+05,1.687233895063400269e-02,9.831277132034301758e-01 +8.880000000000000000e+05,6.099915504455566406e-02,9.390008449554443359e-01 +8.885000000000000000e+05,9.196587800979614258e-01,8.034122735261917114e-02 +8.890000000000000000e+05,8.867021203041076660e-01,1.132978573441505432e-01 +8.895000000000000000e+05,3.909462597221136093e-03,9.960904717445373535e-01 +8.900000000000000000e+05,2.660480095073580742e-03,9.973395466804504395e-01 +8.905000000000000000e+05,2.229482051916420460e-04,9.997770190238952637e-01 +8.910000000000000000e+05,3.890946100000292063e-04,9.996109604835510254e-01 +8.915000000000000000e+05,4.432254005223512650e-03,9.955677390098571777e-01 +8.920000000000000000e+05,2.555986284278333187e-04,9.997443556785583496e-01 +8.925000000000000000e+05,9.709357400424778461e-04,9.990290403366088867e-01 +8.930000000000000000e+05,7.019673357717692852e-04,9.992979764938354492e-01 +8.935000000000000000e+05,1.186047960072755814e-02,9.881395101547241211e-01 +8.940000000000000000e+05,9.516491889953613281e-01,4.835080355405807495e-02 +8.945000000000000000e+05,2.471807529218494892e-04,9.997528195381164551e-01 +8.950000000000000000e+05,5.173064419068396091e-04,9.994826316833496094e-01 +8.955000000000000000e+05,1.252442318946123123e-02,9.874755740165710449e-01 +8.960000000000000000e+05,2.037291042506694794e-02,9.796270132064819336e-01 +8.965000000000000000e+05,9.914644360542297363e-01,8.535602129995822906e-03 +8.970000000000000000e+05,9.956358075141906738e-01,4.364211577922105789e-03 +8.975000000000000000e+05,1.763730198144912720e-01,8.236269950866699219e-01 +8.980000000000000000e+05,9.615378379821777344e-01,3.846217319369316101e-02 +8.985000000000000000e+05,2.745080888271331787e-01,7.254919409751892090e-01 diff --git a/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/rawScores_20190527_190000.mat b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/rawScores_20190527_190000.mat new file mode 100644 index 00000000..1f28e529 Binary files /dev/null and b/src/test/resources/rawDeepLearningClassifier/Koogu/blue_whale_24/rawScores_20190527_190000.mat differ diff --git a/src/userDisplay/UserDisplayControl.java b/src/userDisplay/UserDisplayControl.java index 15575bf1..2fe0b39d 100644 --- a/src/userDisplay/UserDisplayControl.java +++ b/src/userDisplay/UserDisplayControl.java @@ -21,7 +21,6 @@ package userDisplay; import java.awt.Frame; -import java.awt.MenuItem; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.Serializable; @@ -31,18 +30,14 @@ import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; -import clickDetector.ClickClassifiers.UserTypesPanel; import dataPlots.TDDisplayProvider; -import dataPlotsFX.JamieDev; import dataPlotsFX.TDDisplayProviderFX; -import radardisplay.RadarDisplay; import radardisplay.RadarDisplayProvider; import radardisplay.RadarParameters; import radardisplay.RadarParametersDialog; import radardisplay.RadarProjector; import soundPlayback.PlaybackControl; import fftManager.FFTDataUnit; -import localiserDisplay.LocaliserDisplayProvider; import pamScrollSystem.AbstractScrollManager; import pamScrollSystem.coupling.ScrollerCoupling; //import localiserDisplay.LocaliserDisplayProvider; @@ -55,8 +50,6 @@ import PamController.PamSettings; import PamModel.PamDependency; import PamModel.PamDependent; import PamModel.PamModel; -import PamModel.SMRUEnable; -import PamView.MenuItemEnabler; import PamView.PamGui; import Spectrogram.SpectrogramDiplayProvider; import Spectrogram.SpectrogramParameters; @@ -95,10 +88,10 @@ public class UserDisplayControl extends PamControlledUnit implements TDDisplayProvider.register(); TDDisplayProviderFX.register(); - //register the localiser display. - if (JamieDev.isEnabled()) { - LocaliserDisplayProvider.register(); - } +// //register the localiser display. +// if (JamieDev.isEnabled()) { +// LocaliserDisplayProvider.register(); +// } } diff --git a/src/userDisplayFX/SwingDisplayNodeFX.java b/src/userDisplayFX/SwingDisplayNodeFX.java index 514bf3de..94eb479b 100644 --- a/src/userDisplayFX/SwingDisplayNodeFX.java +++ b/src/userDisplayFX/SwingDisplayNodeFX.java @@ -143,4 +143,10 @@ public class SwingDisplayNodeFX implements UserDisplayNodeFX{ } + @Override + public UserDisplayControlFX getUserDisplayControl() { + // TODO Auto-generated method stub + return null; + } + } \ No newline at end of file diff --git a/src/userDisplayFX/UserDisplayNodeFX.java b/src/userDisplayFX/UserDisplayNodeFX.java index eb3c45aa..68f9bf67 100644 --- a/src/userDisplayFX/UserDisplayNodeFX.java +++ b/src/userDisplayFX/UserDisplayNodeFX.java @@ -10,6 +10,13 @@ import pamViewFX.fxNodes.internalNode.PamInternalPane; */ public interface UserDisplayNodeFX { + /** + * Get a display controller associated with the display. Note that a display does not need a controller + * so this can return null. + * @return the associated control for the display. + */ + public UserDisplayControlFX getUserDisplayControl(); + /** * The name of display. Used for default tabs etc. * @return the name of the display diff --git a/src/videoRangePanel/VRControl.java b/src/videoRangePanel/VRControl.java index a8225eeb..1420e5ea 100644 --- a/src/videoRangePanel/VRControl.java +++ b/src/videoRangePanel/VRControl.java @@ -511,7 +511,7 @@ public class VRControl extends PamControlledUnit implements PamSettings { long time1=System.currentTimeMillis(); if (vrTabPanelControl.openImageFile(file)) { //now set the scroller to move to to the correct imu or angle data; - if (currentImage.getTimeMilliseconds()!=0 && !checkViewLoadTime(currentImage.getTimeMilliseconds())){ + if (currentImage!=null && currentImage.getTimeMilliseconds()!=0 && !checkViewLoadTime(currentImage.getTimeMilliseconds())){ vrScroller.setRangeMillis(currentImage.getTimeMilliseconds()-loadTime, currentImage.getTimeMilliseconds()+loadTime, true); } } diff --git a/src/videoRangePanel/importTideData/CSVTideParser.java b/src/videoRangePanel/importTideData/CSVTideParser.java new file mode 100644 index 00000000..31d68fd7 --- /dev/null +++ b/src/videoRangePanel/importTideData/CSVTideParser.java @@ -0,0 +1,42 @@ +package videoRangePanel.importTideData; + +import java.util.ArrayList; +import java.util.List; + +import PamUtils.LatLong; +import PamUtils.PamCalendar; +import PamUtils.TxtFileUtils; + +public class CSVTideParser implements TideParser { + + @Override + public TideDataUnit parseTideLine(String line, LatLong location) { + + ArrayList txtData= TxtFileUtils.parseTxtLine(line, ","); + + String date = txtData.get(0); + + long timeMillis = PamCalendar.unpackStandardDateTime(date); + + double level = Double.valueOf( txtData.get(1)); + + TideDataUnit tideDataUnit = new TideDataUnit(timeMillis, level, 0, 0, null ); + + System.out.println("Tide data: " + PamCalendar.formatDateTime(timeMillis) + " level: " + level); + + + return tideDataUnit; + } + + @Override + public LatLong getLocation(List txtData) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getName() { + return "CSV Parser"; + } + +} diff --git a/src/videoRangePanel/importTideData/POLPREDParser2.java b/src/videoRangePanel/importTideData/POLPREDParser2.java index f016d9ff..29116753 100644 --- a/src/videoRangePanel/importTideData/POLPREDParser2.java +++ b/src/videoRangePanel/importTideData/POLPREDParser2.java @@ -12,6 +12,7 @@ import PamUtils.TxtFileUtils; /** * Polpred tide file + * * @author Jamie Macaulay * */ diff --git a/src/videoRangePanel/importTideData/TideManager.java b/src/videoRangePanel/importTideData/TideManager.java index 593a4c45..0b989703 100644 --- a/src/videoRangePanel/importTideData/TideManager.java +++ b/src/videoRangePanel/importTideData/TideManager.java @@ -74,6 +74,7 @@ public class TideManager extends DataImport { tideParsers.add(new POLPREDParser1()); tideParsers.add(new POLPREDParser2()); tideParsers.add(new POLPREDParser3()); + tideParsers.add(new CSVTideParser()); } diff --git a/src/videoRangePanel/layoutFX/VRDisplayFX.java b/src/videoRangePanel/layoutFX/VRDisplayFX.java index d43850b6..5c3c7d3e 100644 --- a/src/videoRangePanel/layoutFX/VRDisplayFX.java +++ b/src/videoRangePanel/layoutFX/VRDisplayFX.java @@ -156,9 +156,9 @@ public class VRDisplayFX extends PamBorderPane implements VRPane { this.setCenter(holder); - //need to open image file here because the FX stuff takes a bit of time to load - //on the FX thread. If this is attempted in control constructor the FX GUI has not - //loaded and so a null pointer exception is thrown. +// //need to open image file here because the FX stuff takes a bit of time to load +// //on the FX thread. If this is attempted in control constructor the FX GUI has not +// //loaded and so a null pointer exception is thrown. if (this.vrControl.getVRParams().currentImageFile!=null && vrControl.getVRParams().currentImageFile.exists()) { vrImagePane.openNewFile(this.vrControl.getVRParams().currentImageFile); @@ -414,5 +414,10 @@ public class VRDisplayFX extends PamBorderPane implements VRPane { } + public VRMetaDataPaneFX getInfoPane() { + return getImagePane().getInfoPane(); + } + + } diff --git a/src/videoRangePanel/layoutFX/VRDisplayFX2AWT.java b/src/videoRangePanel/layoutFX/VRDisplayFX2AWT.java index b430cdc4..32f63090 100644 --- a/src/videoRangePanel/layoutFX/VRDisplayFX2AWT.java +++ b/src/videoRangePanel/layoutFX/VRDisplayFX2AWT.java @@ -3,6 +3,7 @@ package videoRangePanel.layoutFX; import java.awt.Point; import java.io.File; +import javafx.application.Platform; import javafx.scene.layout.Pane; import videoRangePanel.VRControl; import videoRangePanel.VRPane; @@ -71,9 +72,11 @@ public class VRDisplayFX2AWT implements VRPane { } public void update(int updateType) { + Platform.runLater(()->{ if (vRDisplayFX!=null) { vRDisplayFX.update(updateType); } + }); } /** diff --git a/src/videoRangePanel/layoutFX/VRImagePane.java b/src/videoRangePanel/layoutFX/VRImagePane.java index 97b3cd61..5b24d8da 100644 --- a/src/videoRangePanel/layoutFX/VRImagePane.java +++ b/src/videoRangePanel/layoutFX/VRImagePane.java @@ -3,6 +3,7 @@ package videoRangePanel.layoutFX; import java.awt.Point; import java.io.File; import java.net.URLConnection; +import java.util.ArrayList; import javafx.animation.KeyFrame; import javafx.animation.Timeline; @@ -205,7 +206,7 @@ public class VRImagePane extends PamBorderPane { scrollPane = new VRZoomScrollPane(stackHolder); scrollPane.setPannable(true); - scrollPane.getStylesheets().add(getDarkStyle() ); + scrollPane.getStylesheets().addAll(getDarkStyle() ); scrollPane.setVbarPolicy(ScrollBarPolicy.ALWAYS); scrollPane.setHbarPolicy(ScrollBarPolicy.ALWAYS); @@ -218,7 +219,7 @@ public class VRImagePane extends PamBorderPane { hidingBottomHolder.setStyle("-fx-background-color: -fx-darkbackground-trans"); //from dark stylesheet //Bit of a hack here. Have set the offset to 15 pixels so that the hiding pane does not cover the scroll bar. hiddenControlPane = new HidingPane(Side.BOTTOM , hidingBottomHolder , mainHolder, true, 15); - hiddenControlPane.getStylesheets().add(getDarkStyle() ); + hiddenControlPane.getStylesheets().addAll(getDarkStyle() ); hiddenControlPane.showHidePane(true); hiddenControlPane.getChildren().remove( hiddenControlPane.getHideButton()); @@ -227,7 +228,7 @@ public class VRImagePane extends PamBorderPane { hidingRightHolder.setCenter(null); hidingRightHolder.setStyle("-fx-background-color: -fx-darkbackground-trans"); //from dark stylesheet hiddenRightPane = new HidingPane(Side.RIGHT , hidingRightHolder , mainHolder, true); - hiddenRightPane.getStylesheets().add(getDarkStyle() ); + hiddenRightPane.getStylesheets().addAll(getDarkStyle() ); //****add main image pane to stack pane****// mainHolder.getChildren().add(scrollPane); @@ -249,7 +250,7 @@ public class VRImagePane extends PamBorderPane { imageControls.setPadding(new Insets(10,10,10,10)); imageControls.setMaxWidth(300); imageControls.setMaxHeight(35); - imageControls.getStylesheets().add(getDarkStyle() ); + imageControls.getStylesheets().addAll(getDarkStyle() ); imageControls.setStyle("-fx-background-color: -fx-darkbackground-trans"); //from dark stylesheet @@ -352,7 +353,7 @@ public class VRImagePane extends PamBorderPane { StackPane.setAlignment(ribbonHolder, Pos.TOP_LEFT); ribbonHolder.setMaxHeight(35); ribbonHolder.maxWidthProperty().bind(stackPane.widthProperty().subtract(imageControls.widthProperty())); - ribbonHolder.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); + ribbonHolder.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); //ribbonHolder.setStyle("-fx-background-color: -fx-darkbackground-trans"); //from dark stylesheet mainHolder.getChildren().add(ribbonHolder); @@ -510,8 +511,9 @@ public class VRImagePane extends PamBorderPane { break; } stackHolder.getChildren().add(vrImage.getNode()); + vrImage.setMedia(currentFile); - + //set the meta data. infoPane.setMetaText(vrImage.getMetaData()); //set the image edit pane. @@ -522,6 +524,8 @@ public class VRImagePane extends PamBorderPane { //set the control pane. hidingBottomHolder.setCenter(vrImage.getControlPane()); + + } /** @@ -754,10 +758,14 @@ public class VRImagePane extends PamBorderPane { * Get dark style CSS. * @return dark style CSS. */ - private String getDarkStyle() { + private ArrayList getDarkStyle() { return PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS(); } + public VRMetaDataPaneFX getInfoPane() { + return infoPane; + } + diff --git a/src/videoRangePanel/layoutFX/VRMediaView.java b/src/videoRangePanel/layoutFX/VRMediaView.java index ea386fa2..6ef0f952 100644 --- a/src/videoRangePanel/layoutFX/VRMediaView.java +++ b/src/videoRangePanel/layoutFX/VRMediaView.java @@ -5,6 +5,7 @@ import java.net.MalformedURLException; import java.util.ArrayList; import PamUtils.PamCalendar; +import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.ObservableMap; @@ -196,27 +197,55 @@ public class VRMediaView extends PamBorderPane implements VRImage { public boolean setMedia(File currentFile) { this.currentFile=currentFile; this.fileTime=currentFile.lastModified(); - + try { media = new Media(currentFile.toURI().toURL().toExternalForm()); + + + //player.getMedia().getMetadata(); + // Media media = new Media("file:///home/paul/MoviePlayer/trailers/sintel.mp4"); + player = new MediaPlayer(media); + + //the media player is loaded asynchronously so must wait. + player.setOnReady(() -> { + //the meta data + this.metaData = getMetaData(media); + setUpPlayer() ; + setImageFreeze(); + + Platform.runLater(()->{ + vrDisplayFX.getInfoPane().setMetaText( getMetaData()); + }); + + }); + // player.setAutoPlay(true); + mediaView.setMediaPlayer(player); + + //wait for the player to be ready.... + //player.getOnReady().wait(); + + //18/09/2023 - really no great but only way I could + Thread.sleep(100); + + + // while (player.getStatus()==MediaPlayer.Status.UNKNOWN) { + + //not great just to put a sleep here but while loop to wait on the + // Thread.sleep(100); + // System.out.println("Status: " + player.getStatus()); + // player.play(); + // } + + return true; + } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); - return false; + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); } - - //the meta data - this.metaData = getMetaData(media); - player.getMedia().getMetadata(); - // Media media = new Media("file:///home/paul/MoviePlayer/trailers/sintel.mp4"); - player = new MediaPlayer(media); - setUpPlayer() ; - mediaView.setMediaPlayer(player); - - setImageFreeze(); - //player.play(); //TEMP - - return true; + return false; } @@ -229,22 +258,24 @@ public class VRMediaView extends PamBorderPane implements VRImage { MetaData metaData = new MetaData(); try { ObservableMap vidMetaData = currentFile.getMetadata(); - + + // System.out.println("File information: " + currentFile.getHeight() + " " + currentFile.getWidth() + " " + currentFile.getMetadata()); + //TODO - need to implement JavaFX metadata here. -// printDetails("Bit-rate", metaData.bitRate=currentFile.getBitRate()); -// printDetails("Frame Rate", metaData.frameRate=vInfo.getFrameRate()); + // printDetails("Bit-rate", metaData.bitRate=currentFile.getBitRate()); + // printDetails("Frame Rate", metaData.frameRate=vInfo.getFrameRate()); printDetails("Height", metaData.height=currentFile.getHeight()); printDetails("Width", metaData.width=currentFile.getHeight()); printDetails("Duration", metaData.duration=(long) currentFile.getDuration().toMillis()); -// printDetails("Format", metaData.format=info.getFormat()); -// -// printDetails("Bit-rate", metaData.bitRate=info.getAudio().getBitRate()); -// printDetails("Channels", metaData.audioChannels=info.getAudio().getChannels()); -// printDetails("Sampling Rate", metaData.audioSamplingRate=info.getAudio().getSamplingRate()); + // printDetails("Format", metaData.format=info.getFormat()); + // + // printDetails("Bit-rate", metaData.bitRate=info.getAudio().getBitRate()); + // printDetails("Channels", metaData.audioChannels=info.getAudio().getChannels()); + // printDetails("Sampling Rate", metaData.audioSamplingRate=info.getAudio().getSamplingRate()); } catch (Exception e) { - System.out.println("Could nto find meta data!"); + System.out.println("Could not find meta data!"); e.printStackTrace(); } return metaData; @@ -252,27 +283,31 @@ public class VRMediaView extends PamBorderPane implements VRImage { boolean wasPausedOnDrag =false; + + /** * Set up the player controls to play, pause and set time of media. This is called whenever a new Media File is * played. */ private void setUpPlayer() { - player.setOnReady(new Runnable() { - @Override - public void run() { + // player.setOnReady(new Runnable() { + // @Override + // public void run() { - int w = player.getMedia().getWidth(); - int h = player.getMedia().getHeight(); + int w = player.getMedia().getWidth(); + int h = player.getMedia().getHeight(); - seekslider.setMin(0.0); - seekslider.setValue(0.0); - seekslider.setMax(player.getTotalDuration().toSeconds()); + seekslider.setMin(0.0); + seekslider.setValue(0.0); + seekslider.setMax(player.getTotalDuration().toSeconds()); - player.setVolume(0.5); + // System.out.println("Get total duration:" + player.getTotalDuration()); - } - }); + player.setVolume(0.5); + + // } + // }); /******Time Slider****/ @@ -289,7 +324,7 @@ public class VRMediaView extends PamBorderPane implements VRImage { } }); - + //need to pause the video if the slider is dragged and than start again when slider is Ok. seekslider.valueChangingProperty().addListener((obsVal, oldVal, newVal)->{ if (newVal && newVal!=oldVal) { @@ -361,12 +396,12 @@ public class VRMediaView extends PamBorderPane implements VRImage { player.setOnPlaying(()->{ -// playButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.PAUSE, controlIconSize)); + // playButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.PAUSE, controlIconSize)); playButton.setGraphic(PamGlyphDude.createPamIcon("mdi2p-pause", controlIconSize)); }); player.setOnPaused(()->{ -// playButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.PLAY_ARROW, controlIconSize)); + // playButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.PLAY_ARROW, controlIconSize)); playButton.setGraphic(PamGlyphDude.createPamIcon("mdi2p-play", controlIconSize)); }); @@ -423,21 +458,21 @@ public class VRMediaView extends PamBorderPane implements VRImage { */ private void setVolumeGraphic(double volume) { if (volume ==0) { -// volumeButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.VOLUME_OFF, controlIconSize)); + // volumeButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.VOLUME_OFF, controlIconSize)); volumeButton.setGraphic(PamGlyphDude.createPamIcon("mdi2v-volume-off", controlIconSize)); } else if (volume>0 && volume<=0.33) { -// volumeButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.VOLUME_MUTE, controlIconSize)); + // volumeButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.VOLUME_MUTE, controlIconSize)); volumeButton.setGraphic(PamGlyphDude.createPamIcon("mdi2v-volume-mute", controlIconSize)); } else if (volume>0.33 && volume<=0.66) { -// volumeButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.VOLUME_DOWN, controlIconSize)); + // volumeButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.VOLUME_DOWN, controlIconSize)); volumeButton.setGraphic(PamGlyphDude.createPamIcon("mdi2v-volume-medium", controlIconSize)); } else if (volume>0.66 && volume<1) { -// volumeButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.VOLUME_UP, controlIconSize)); + // volumeButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.VOLUME_UP, controlIconSize)); volumeButton.setGraphic(PamGlyphDude.createPamIcon("mdi2v-volume-high", controlIconSize)); } @@ -452,10 +487,11 @@ public class VRMediaView extends PamBorderPane implements VRImage { } /** - * Gte the duration of a single frame. - * @return the duration of a single frame. + * Get the duration of a single frame. + * @return the duration of a single frame in milliseconds. */ private long getSingleFrameDuration() { + if ( metaData.frameRate==0.0) return 100; long frameDuration = (long) (Math.ceil(1000.*(1/(double) metaData.frameRate))); return frameDuration; } @@ -471,7 +507,7 @@ public class VRMediaView extends PamBorderPane implements VRImage { //long frameDuration = (long) (Math.ceil(1000.*(1/(double) metaData.frameRate))); -// System.out.println("Frame duration millis: " + millis); + // System.out.println("Frame duration millis: " + millis); if (!forward) millis =-millis; @@ -531,44 +567,44 @@ public class VRMediaView extends PamBorderPane implements VRImage { //the play and pause button playButton = new PamButton(); -// playButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.PLAY_ARROW, controlIconSize)); + // playButton.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.PLAY_ARROW, controlIconSize)); playButton.setGraphic(PamGlyphDude.createPamIcon("mdi2p-play", controlIconSize)); playButton.getStyleClass().add("square-button-trans"); playButton.setTooltip(new Tooltip("Play or pause the media")); leftFrame = new PamButton(); -// leftFrame.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.CHEVRON_LEFT, controlIconSize)); + // leftFrame.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.CHEVRON_LEFT, controlIconSize)); leftFrame.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-left", controlIconSize)); leftFrame.getStyleClass().add("square-button-trans"); leftFrame.setTooltip(new Tooltip("Move to the previous frame")); rightFrame= new PamButton(); -// rightFrame.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.CHEVRON_RIGHT,controlIconSize)); + // rightFrame.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.CHEVRON_RIGHT,controlIconSize)); rightFrame.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-right",controlIconSize)); rightFrame.getStyleClass().add("square-button-trans"); rightFrame.setTooltip(new Tooltip("Move to the next frame")); nextMedia= new PamButton(); -// nextMedia.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.SKIP_PREVIOUS,controlIconSize)); + // nextMedia.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.SKIP_PREVIOUS,controlIconSize)); nextMedia.setGraphic(PamGlyphDude.createPamIcon("mdi2s-skip-previous",controlIconSize)); nextMedia.getStyleClass().add("square-button-trans"); nextMedia.setTooltip(new Tooltip("Move to the next image or video")); prevMedia= new PamButton(); -// prevMedia.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.SKIP_NEXT, controlIconSize)); + // prevMedia.setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.SKIP_NEXT, controlIconSize)); prevMedia.setGraphic(PamGlyphDude.createPamIcon("mdi2s-skip-next", controlIconSize)); prevMedia.getStyleClass().add("square-button-trans"); prevMedia.setTooltip(new Tooltip("Move to the previous image or video")); skipForward= new PamButton(); -// skipForward.setGraphic(PamGlyphDude.createPamGlyph(MaterialDesignIcon.CHEVRON_DOUBLE_RIGHT, controlIconSize)); + // skipForward.setGraphic(PamGlyphDude.createPamGlyph(MaterialDesignIcon.CHEVRON_DOUBLE_RIGHT, controlIconSize)); skipForward.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-double-right", controlIconSize)); skipForward.getStyleClass().add("square-button-trans"); skipForward.setTooltip(new Tooltip("Skip 2 seconds forward")); skipBackward= new PamButton(); -// skipBackward.setGraphic(PamGlyphDude.createPamGlyph(MaterialDesignIcon.CHEVRON_DOUBLE_LEFT, controlIconSize)); + // skipBackward.setGraphic(PamGlyphDude.createPamGlyph(MaterialDesignIcon.CHEVRON_DOUBLE_LEFT, controlIconSize)); skipBackward.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-double-left", controlIconSize)); skipBackward.getStyleClass().add("square-button-trans"); skipBackward.setTooltip(new Tooltip("Skip 2 seconds backward")); @@ -661,7 +697,7 @@ public class VRMediaView extends PamBorderPane implements VRImage { * Size of the video file */ public int height; - + /** * Size of the video file */ @@ -688,6 +724,12 @@ public class VRMediaView extends PamBorderPane implements VRImage { */ public PamImage getImageSnapshot() { + //System.out.println("Fit height: " + mediaView.getFitHeight()); + // + // if (metaData==null || metaData.width==0 || metaData.height == 0 ) { + // return null; + // } + WritableImage image = new WritableImage(metaData.width, metaData.height); mediaView.snapshot(new SnapshotParameters(), image); diff --git a/src/videoRangePanel/layoutFX/VRTabPanelControlFX.java b/src/videoRangePanel/layoutFX/VRTabPanelControlFX.java index 80798d7c..d44c2bb6 100644 --- a/src/videoRangePanel/layoutFX/VRTabPanelControlFX.java +++ b/src/videoRangePanel/layoutFX/VRTabPanelControlFX.java @@ -86,7 +86,7 @@ public class VRTabPanelControlFX extends VRTabPane { Group root= new Group(); Scene scene = new Scene(root, Color.GRAY); scene.getStylesheets().clear(); - scene.getStylesheets().add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getGUICSS()); + scene.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getGUICSS()); vRDisplay = new VRDisplayFX2AWT(vRControl); vRDisplay.createPane(); diff --git a/src/videoRangePanel/vrmethods/landMarkMethod/LandMarkGroupPane.java b/src/videoRangePanel/vrmethods/landMarkMethod/LandMarkGroupPane.java index fa8d90d4..932e1e2b 100644 --- a/src/videoRangePanel/vrmethods/landMarkMethod/LandMarkGroupPane.java +++ b/src/videoRangePanel/vrmethods/landMarkMethod/LandMarkGroupPane.java @@ -158,7 +158,7 @@ public class LandMarkGroupPane extends PamBorderPane { //make pop over dark ((Parent) popOver.getSkin().getNode()).getStylesheets() - .add(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); + .addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); // System.out.println(landMarkTable.getTableView().getSelectionModel().getSelectedItem()); } }); diff --git a/src/whistlesAndMoans/alarm/WMAlarmParameters.java b/src/whistlesAndMoans/alarm/WMAlarmParameters.java index 1978d507..46a480ff 100644 --- a/src/whistlesAndMoans/alarm/WMAlarmParameters.java +++ b/src/whistlesAndMoans/alarm/WMAlarmParameters.java @@ -11,10 +11,11 @@ public class WMAlarmParameters extends DataSelectParams implements Cloneable, Se public static final long serialVersionUID = 1L; - public double minFreq, maxFreq; - public double minAmplitude; - public double minLengthMillis; - public boolean superDetOnly; + public double minFreq = 0.; + public double maxFreq = 30000.; + public double minAmplitude = 90.; + public double minLengthMillis = 0.0; + public boolean superDetOnly = false; @Override public WMAlarmParameters clone() { diff --git a/src/whistlesAndMoans/dataSelector/WMDDataSelector.java b/src/whistlesAndMoans/dataSelector/WMDDataSelector.java index 7838b2d6..34ec0027 100644 --- a/src/whistlesAndMoans/dataSelector/WMDDataSelector.java +++ b/src/whistlesAndMoans/dataSelector/WMDDataSelector.java @@ -1,15 +1,9 @@ package whistlesAndMoans.dataSelector; -import java.io.Serializable; - -import alarm.AlarmParameters; import pamViewFX.fxSettingsPanes.DynamicSettingsPane; import whistlesAndMoans.ConnectedRegionDataUnit; import whistlesAndMoans.WhistleMoanControl; import whistlesAndMoans.alarm.WMAlarmParameters; -import PamController.PamControlledUnitSettings; -import PamController.PamSettingManager; -import PamController.PamSettings; import PamView.dialog.PamDialogPanel; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -24,6 +18,11 @@ public class WMDDataSelector extends DataSelector { private WMAlarmParameters wmAlarmParameters = new WMAlarmParameters(); + /** + * JavaFX pane. + */ + private WMDSelectPaneFX wmdSelectPaneFX; + public WMDDataSelector(WhistleMoanControl wmControl, PamDataBlock pamDataBlock, String selectorName, boolean allowScores) { super(pamDataBlock, selectorName, allowScores); @@ -94,8 +93,10 @@ public class WMDDataSelector extends DataSelector { @Override public DynamicSettingsPane getDialogPaneFX() { - // TODO Auto-generated method stub - return null; + if (wmdSelectPaneFX == null) { + wmdSelectPaneFX = new WMDSelectPaneFX(this); + } + return wmdSelectPaneFX; } } diff --git a/src/whistlesAndMoans/dataSelector/WMDSelectPaneFX.java b/src/whistlesAndMoans/dataSelector/WMDSelectPaneFX.java new file mode 100644 index 00000000..41818234 --- /dev/null +++ b/src/whistlesAndMoans/dataSelector/WMDSelectPaneFX.java @@ -0,0 +1,151 @@ +package whistlesAndMoans.dataSelector; + +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.Spinner; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Pane; +import pamViewFX.fxNodes.PamGridPane; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; +import whistlesAndMoans.alarm.WMAlarmParameters; + +/** + * JavaFX settings pane for the whsitle and moan detector data selector. + * + * @author Jamie Macaulay + * + */ +public class WMDSelectPaneFX extends DynamicSettingsPane { + + private Pane mainPane; + + private WMDDataSelector wmdDataSelector; + + private Spinner minFreq; + + private Spinner maxFreq; + + private Spinner minAmplitude; + + private Spinner minLength; + + private PamToggleSwitch superDetOnly; + + public WMDSelectPaneFX(WMDDataSelector wmdDataSelector) { + super(wmdDataSelector); + this.wmdDataSelector = wmdDataSelector; + // TODO Auto-generated constructor stub + mainPane = createPane(); + } + + + private Pane createPane() { + + PamVBox mainPane = new PamVBox(); + mainPane.setSpacing(5.); + + PamGridPane gridPane = new PamGridPane(); + gridPane.setHgap(5.); + gridPane.setVgap(5.); + + int row = 0; + int column = 0; + + minFreq = new Spinner(0.,Double.MAX_VALUE, 100., 100.); + minFreq.setEditable(true); + + gridPane.add(new Label("Min. frequency"), column, row); + column++; + gridPane.add(minFreq, column, row); + column++; + gridPane.add(new Label("Hz"), column, row); + + maxFreq = new Spinner(0.,Double.MAX_VALUE, 30000., 100.); + maxFreq.setEditable(true); + + row++; + column=0; + gridPane.add(new Label("Max. frequency"), column, row); + gridPane.add(maxFreq, ++column, row); + gridPane.add(new Label("Hz"), ++column, row); + + minAmplitude = new Spinner(0.,1000., 90., 1.); + minAmplitude.setEditable(true); + + row++; + column=0; + gridPane.add(new Label("Min. amplitude"), column, row); + gridPane.add(minAmplitude, ++column, row); + gridPane.add(new Label("dB"), ++column, row); + + minLength = new Spinner(0.,Double.MAX_VALUE, 0., 1.); + minLength.setEditable(true); + + row++; + column=0; + gridPane.add(new Label("Min. length"), column, row); + gridPane.add(minLength, ++column, row); + gridPane.add(new Label("milliseconds"), ++column, row); + + row++; + column=0; + superDetOnly = new PamToggleSwitch("Only whistles with super-detections"); + gridPane.add(superDetOnly, column, row); + GridPane.setColumnSpan(superDetOnly, 3); + + mainPane.getChildren().add(gridPane); + + return mainPane; + } + + @Override + public Boolean getParams(Boolean currParams) { + + WMAlarmParameters wmAlarmParameters = wmdDataSelector.getWmAlarmParameters().clone(); + try { + wmAlarmParameters.minFreq = minFreq.getValue(); + wmAlarmParameters.maxFreq = maxFreq.getValue(); + wmAlarmParameters.minAmplitude = minAmplitude.getValue(); + wmAlarmParameters.minLengthMillis = minLength.getValue(); + wmAlarmParameters.superDetOnly = superDetOnly.isSelected(); + } + catch (NumberFormatException e) { + return false; + } + wmdDataSelector.setWmAlarmParameters(wmAlarmParameters); + return true; + } + + @Override + public void setParams(Boolean input) { + + WMAlarmParameters wmAlarmParameters = wmdDataSelector.getWmAlarmParameters(); + + minFreq.getValueFactory().setValue(wmAlarmParameters.minFreq); + maxFreq.getValueFactory().setValue(wmAlarmParameters.maxFreq); + minAmplitude.getValueFactory().setValue(wmAlarmParameters.minAmplitude); + minLength.getValueFactory().setValue(wmAlarmParameters.minLengthMillis); + + superDetOnly.setSelected(wmAlarmParameters.superDetOnly); + + } + + @Override + public String getName() { + return "WMD Data Selector"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +}