diff --git a/.gitignore b/.gitignore index 7acd763..04012e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.* Makefile Debug/ Build @@ -14,3 +15,4 @@ cmake_uninstall.cmake CTestTestfile.cmake .kdev4 *.kdev4 +*.pyc diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ae13d7..20f3677 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,253 +18,250 @@ else(${CMAKE_VERSION} VERSION_GREATER 2.8.4) include(OldGNUInstallDirs) endif(${CMAKE_VERSION} VERSION_GREATER 2.8.4) -set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually d on windows") -set(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows") -set(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows") -set(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows") +# Warning when build is not an out-of-source build. +string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" InSourceBuild) +if(InSourceBuild) + message(WARNING "Avoid building inside the source tree!") + message(WARNING "Create a separate build directory instead (i.e. 'fgbuild') and call CMake from there: ") + message(WARNING " mkdir ../fgbuild && cd ../fgbuild && cmake ${CMAKE_SOURCE_DIR}") +endif(InSourceBuild) + +# System detection/default settings +include( DetectDistro ) +include( DetectBrowser ) + +set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually d on windows") +set(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows") +set(CMAKE_RELWITHDEBINFO_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows") +set(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows") # read 'version' file into a variable (stripping any newlines or spaces) file(READ version versionFile) -string(STRIP ${versionFile} FLIGHTGEAR_VERSION) - -#packaging -if(EXISTS ${PROJECT_SOURCE_DIR}/.gitignore) - file(READ .gitignore CPACK_SOURCE_IGNORE_FILES) -else() - # clean tar-balls do not contain SCM (.git/.gitignore/...) files. - set(CPACK_SOURCE_IGNORE_FILES - "Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}") +if (NOT versionFile) + message(FATAL_ERROR "Unable to determine FlightGear version. Version file is missing.") endif() +string(STRIP "${versionFile}" FLIGHTGEAR_VERSION) -list (APPEND CPACK_SOURCE_IGNORE_FILES "${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore") - -# split version string into components, note CMAKE_MATCH_0 is the entire regexp match -string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${FLIGHTGEAR_VERSION} ) -set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) -set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2}) -set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) -SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING") -SET(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README") - -set(CPACK_SOURCE_GENERATOR TBZ2) -set(CPACK_SOURCE_PACKAGE_FILE_NAME "flightgear-${FLIGHTGEAR_VERSION}" CACHE INTERNAL "tarball basename") - -include (CPack) +# FlightGear packaging (to build a source tarball) +include( ConfigureCPack ) +# FlightGear base package path if (FG_DATA_DIR) - message(STATUS "Using explicit data-dir: ${FG_DATA_DIR}") + message(STATUS "Using explicit data directory for base package: ${FG_DATA_DIR}") else() set(FG_DATA_DIR "${CMAKE_INSTALL_PREFIX}/lib/FlightGear" CACHE PATH "Default location where data files are located") - message(STATUS "Using default data-dir: ${FG_DATA_DIR}") + message(STATUS "Using default data directory for base package: ${FG_DATA_DIR}") endif() # Change the default build type to something fast if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." - FORCE) + set(CMAKE_BUILD_TYPE Release CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + FORCE) endif(NOT CMAKE_BUILD_TYPE) if(NOT "$ENV{BUILD_ID}" STREQUAL "") - set(HUDSON_BUILD_ID $ENV{BUILD_ID}) - set(HUDSON_BUILD_NUMBER $ENV{BUILD_NUMBER}) - message(STATUS "running under Hudson, build-number is ${HUDSON_BUILD_NUMBER}") + set(HUDSON_BUILD_ID $ENV{BUILD_ID}) + set(HUDSON_BUILD_NUMBER $ENV{BUILD_NUMBER}) + message(STATUS "running under Hudson/Jenkins, build-number is ${HUDSON_BUILD_NUMBER}") else() - set(HUDSON_BUILD_NUMBER 0) - set(HUDSON_BUILD_ID "none") + set(HUDSON_BUILD_NUMBER 0) + set(HUDSON_BUILD_ID "none") +endif() + +##################################################################################### +# Configure library search paths +##################################################################################### + +if(APPLE) + # Custom library directories for Mac, which should have precedence over any other + list(APPEND ADDITIONAL_LIBRARY_PATHS + ~/Library/Frameworks + /Library/Frameworks) +endif(APPLE) + +if(NOT "${CMAKE_LIBRARY_ARCHITECTURE}" STREQUAL "") + # Workaround for Ubuntu/Debian which introduced the "multiarch" library + # directory structure, which is unsupported by CMake < 2.8.10, so we need to + # add paths manually + # see http://www.cmake.org/Bug/view.php?id=12049 and + # http://www.cmake.org/Bug/view.php?id=12037 + list(APPEND ADDITIONAL_LIBRARY_PATHS + /usr/local/lib/${CMAKE_LIBRARY_ARCHITECTURE} + /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} + /lib/${CMAKE_LIBRARY_ARCHITECTURE}) + message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}") endif() +if(NOT MSVC) + # TBD: are these really necessary? Aren't they considered by cmake automatically? + list(APPEND ADDITIONAL_LIBRARY_PATHS + /opt/local + /usr/local + /usr) +endif() +##################################################################################### + IF(APPLE) set(EVENT_INPUT_DEFAULT 1) - + find_library(CORESERVICES_LIBRARY CoreServices) find_library(COCOA_LIBRARY Cocoa) list(APPEND PLATFORM_LIBS ${COCOA_LIBRARY} ${CORESERVICES_LIBRARY}) elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") - find_package(UDev) + find_package(UDev) - if(UDEV_FOUND) - set(EVENT_INPUT_DEFAULT 1) - endif(UDEV_FOUND) + if(UDEV_FOUND) + set(EVENT_INPUT_DEFAULT 1) + endif(UDEV_FOUND) endif() find_package(Git) if (GIT_FOUND) - execute_process(COMMAND git --git-dir ${PROJECT_SOURCE_DIR}/.git rev-parse HEAD - OUTPUT_VARIABLE REVISION - OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Git revision is ${REVISION}") + execute_process(COMMAND git --git-dir ${PROJECT_SOURCE_DIR}/.git rev-parse HEAD + OUTPUT_VARIABLE REVISION + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Git revision is ${REVISION}") else() - set(REVISION "none") + set(REVISION "none") endif() -option(LOGGING "Set to OFF to build FlightGear without logging" ON) - -option(SP_FDMS "Set to ON to build FlightGear with special-purpose FDMs" OFF) +# FlightGear build options +option(SIMGEAR_SHARED "Set to ON when SimGear was built as a shared library" OFF) +option(LOGGING "Set to ON to build FlightGear with logging support (default)" ON) +option(SP_FDMS "Set to ON to build FlightGear with special-purpose FDMs" OFF) option(ENABLE_UIUC_MODEL "Set to ON to build FlightGear with UIUCModel FDM" OFF) -option(ENABLE_LARCSIM "Set to ON to build FlightGear with LaRCsim FDM" OFF) -option(ENABLE_YASIM "Set to ON to build FlightGear with YASIM FDM" ON) -option(ENABLE_JSBSIM "Set to ON to build FlightGear with JSBSim FDM" ON) -option(ENABLE_FGADMIN "Set to ON to build FlightGear with FGADMIN" ON) -option(EVENT_INPUT "Set to ON to build FlightGear with event-based Input support" ${EVENT_INPUT_DEFAULT}) -option(ENABLE_LIBSVN "Set to ON to build FlightGear/terrasync with libsvnclient support" ON) -option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF) -option(WITH_FGPANEL "Set to ON to build the fgpanel application" ON) -option(JPEG_FACTORY "Enable JPEG-factory support" OFF) - -if (MSVC) - GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_SOURCE_DIR} PATH) - if (CMAKE_CL_64) - SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64") - else (CMAKE_CL_64) - SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty") - endif (CMAKE_CL_64) - if (EXISTS ${TEST_3RDPARTY_DIR}) - set(MSVC_3RDPARTY_ROOT ${PARENT_DIR} CACHE PATH "Location where the third-party dependencies are extracted") - else (EXISTS ${TEST_3RDPARTY_DIR}) - set(MSVC_3RDPARTY_ROOT NOT_FOUND CACHE PATH "Location where the third-party dependencies are extracted") - endif (EXISTS ${TEST_3RDPARTY_DIR}) - list(APPEND PLATFORM_LIBS "winmm.lib") -else (MSVC) - set(MSVC_3RDPARTY_ROOT NOT_FOUND CACHE PATH "Location where the third-party dependencies are extracted") -endif (MSVC) +option(ENABLE_LARCSIM "Set to ON to build FlightGear with LaRCsim FDM" OFF) +option(ENABLE_YASIM "Set to ON to build FlightGear with YASIM FDM (default)" ON) +option(ENABLE_JSBSIM "Set to ON to build FlightGear with JSBSim FDM (default)" ON) +option(EVENT_INPUT "Set to ON to build FlightGear with event-based Input support" ${EVENT_INPUT_DEFAULT}) +option(ENABLE_LIBSVN "Set to ON to build FlightGear/terrasync with libsvnclient support (default)" ON) +option(ENABLE_RTI "Set to ON to build FlightGear with RTI support" OFF) +option(ENABLE_PROFILE "Set to ON to build FlightGear with gperftools profiling support" OFF) +option(JPEG_FACTORY "Set to ON to build FlightGear with JPEG-factory support" OFF) +option(SYSTEM_SQLITE "Set to ON to build FlightGear with the system's SQLite3 library" OFF) + +# additional utilities +option(ENABLE_FGADMIN "Set to ON to build the FGADMIN application (default)" ON) +option(WITH_FGPANEL "Set to ON to build the fgpanel application (default)" ON) +option(ENABLE_TESTS "Set to ON to build test applications (default)" ON) if(LOGGING) - # nothing + # nothing else() - set(FG_NDEBUG 1) + set(FG_NDEBUG 1) endif() if(SP_FDMS) - set(ENABLE_SP_FDM 1) + set(ENABLE_SP_FDM 1) endif() -if (MSVC AND MSVC_3RDPARTY_ROOT) - message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}") - set( OSG_MSVC "msvc" ) - if (${MSVC_VERSION} EQUAL 1600) - set( OSG_MSVC ${OSG_MSVC}100 ) - else (${MSVC_VERSION} EQUAL 1600) - set( OSG_MSVC ${OSG_MSVC}90 ) - endif (${MSVC_VERSION} EQUAL 1600) - if (CMAKE_CL_64) - set( OSG_MSVC ${OSG_MSVC}-64 ) - set( MSVC_3RDPARTY_DIR 3rdParty.x64 ) - else (CMAKE_CL_64) - set( MSVC_3RDPARTY_DIR 3rdParty ) - endif (CMAKE_CL_64) - - set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/lib ) - set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/include) - set (BOOST_ROOT ${MSVC_3RDPARTY_ROOT}/boost_1_44_0) - set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include) - set (ALUT_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include) - set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib) -endif (MSVC AND MSVC_3RDPARTY_ROOT) +# Setup MSVC 3rd party directories +include( ConfigureMsvc3rdParty ) if(EVENT_INPUT) - message(STATUS "checking event-based Input") - - IF(APPLE) - - elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") - if(NOT UDEV_FOUND) - message(WARNING "UDev not found, event input is disabled!") - set(EVENT_INPUT 0) - else() - set(EVENT_INPUT_LIBRARIES ${UDEV_LIBRARIES}) - endif() - - else() - message(WARNING "event input is not supported on this platform yet") - endif() + if(APPLE) + elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") + if(NOT UDEV_FOUND) + message(WARNING "UDev not found, event input is disabled!") + set(EVENT_INPUT 0) + else() + add_definitions(-DWITH_EVENTINPUT) + set(EVENT_INPUT_LIBRARIES ${UDEV_LIBRARIES}) + message(STATUS "event-based input enabled. Using ${UDEV_LIBRARIES}") + endif() + else() + message(WARNING "event-based input is not supported on this platform yet") + endif() - # Keep PLIB INPUT enabled as long as EventInput does not replace current joystick configurations. - set(ENABLE_PLIB_JOYSTICK 1) + # Keep PLIB INPUT enabled as long as EventInput does not replace current joystick configurations. + set(ENABLE_PLIB_JOYSTICK 1) else(EVENT_INPUT) - set(ENABLE_PLIB_JOYSTICK 1) + set(ENABLE_PLIB_JOYSTICK 1) endif(EVENT_INPUT) # check required dependencies -find_package(Boost REQUIRED) -find_package(ZLIB REQUIRED) +find_package(Boost REQUIRED) +find_package(ZLIB REQUIRED) find_package(Threads REQUIRED) -find_package(OpenGL REQUIRED) -find_package(OpenAL REQUIRED) -find_package(ALUT REQUIRED) +find_package(OpenGL REQUIRED) +find_package(OpenAL REQUIRED) find_package(OpenSceneGraph 3.0.0 REQUIRED osgText osgSim osgDB osgParticle osgFX osgUtil osgViewer osgGA) if(ENABLE_FGADMIN) - find_package(FLTK) + find_package(FLTK) - if ( FLTK_FOUND ) - if ( X11_Xinerama_FOUND ) - message(STATUS "Found X11_Xinerama...") - list(APPEND FLTK_LIBRARIES ${X11_Xinerama_LIB}) - endif() + if ( FLTK_FOUND ) + if ( X11_Xinerama_FOUND ) + message(STATUS "Found X11_Xinerama...") + list(APPEND FLTK_LIBRARIES ${X11_Xinerama_LIB}) + endif() - if ( X11_Xft_FOUND ) - message(STATUS "Found X11_Xft...") - list(APPEND FLTK_LIBRARIES ${X11_Xft_LIB}) - endif() + if ( X11_Xft_FOUND ) + message(STATUS "Found X11_Xft...") + list(APPEND FLTK_LIBRARIES ${X11_Xft_LIB}) + endif() - set( CMAKE_REQUIRED_INCLUDES ${FLTK_INCLUDE_DIR} ) - set( CMAKE_REQUIRED_LIBRARIES ${FLTK_LIBRARIES} ) - - message(STATUS "Using FLTK_LIBRARIES for fgadmin: ${FLTK_LIBRARIES}") - endif ( FLTK_FOUND ) + message(STATUS "Using FLTK_LIBRARIES for fgadmin: ${FLTK_LIBRARIES}") + endif ( FLTK_FOUND ) endif(ENABLE_FGADMIN) if(ENABLE_LIBSVN) - find_package(SvnClient) - - if(LIBSVN_FOUND) - message(STATUS "libsvn found, enabling in terrasync") - set(HAVE_SVN_CLIENT_H 1) - set(HAVE_LIBSVN_CLIENT_1 1) - endif(LIBSVN_FOUND) + find_package(SvnClient) + + if(LIBSVN_FOUND) + message(STATUS "libsvn found, enabling in terrasync") + set(HAVE_SVN_CLIENT_H 1) + set(HAVE_LIBSVN_CLIENT_1 1) + else() + message(STATUS "libsvn missing. Disabling...") + endif(LIBSVN_FOUND) endif(ENABLE_LIBSVN) +if (SYSTEM_SQLITE) + find_package(SQLite3 REQUIRED) + set(CMAKE_REQUIRED_INCLUDES ${SQLITE3_INCLUDED_DIR}) + message(STATUS "Using system SQLite3 library") +endif (SYSTEM_SQLITE) + +# Sqlite always depends on the threading lib +list(APPEND SQLITE3_LIBRARY ${CMAKE_THREAD_LIBS_INIT}) + find_package(PLIB REQUIRED puaux pu js fnt) -find_package(SimGear 2.6.0 REQUIRED) + +# FlightGear and SimGear versions need to match +find_package(SimGear ${FLIGHTGEAR_VERSION} REQUIRED) if (JPEG_FACTORY) # check simgear was built with JPEG-factory support find_package(JPEG REQUIRED) include_directories(${JPEG_INCLUDE_DIR}) - - set(CMAKE_REQUIRED_INCLUDES - ${SIMGEAR_INCLUDE_DIR} - ${JPEG_INCLUDE_DIR} + + set(CMAKE_REQUIRED_INCLUDES + ${SIMGEAR_INCLUDE_DIR} + ${JPEG_INCLUDE_DIR} ${OPENSCENEGRAPH_INCLUDE_DIRS}) - + check_cxx_source_compiles( "#include int main() { return 0; } " FG_JPEG_SERVER) - + if (NOT FG_JPEG_SERVER) message(STATUS "JPEG server support requested, but SimGear was built without JPEG support") endif() endif() -check_include_file(unistd.h HAVE_UNISTD_H) +check_include_file(unistd.h HAVE_UNISTD_H) check_include_file(sys/time.h HAVE_SYS_TIME_H) -check_include_file(windows.h HAVE_WINDOWS_H) - -# definition depends on OSG version -set(CMAKE_REQUIRED_INCLUDES ${OPENSCENEGRAPH_INCLUDE_DIRS}) +check_include_file(windows.h HAVE_WINDOWS_H) -check_cxx_source_compiles( - "#include - int main() - { - osg::CullSettings::VariablesMask mask = osg::CullSettings::CLEAR_MASK; - return 0; - } - " - HAVE_CULLSETTINGS_CLEAR_MASK) +if(ENABLE_PROFILE) + find_package(GooglePerfTools REQUIRED) + set(FG_HAVE_GPERFTOOLS 1) + message(STATUS "Built-in profiler using gperftools available") +endif() if(ENABLE_RTI) find_package(RTI) @@ -274,9 +271,15 @@ if(ENABLE_RTI) endif(ENABLE_RTI) if(CMAKE_COMPILER_IS_GNUCXX) - set(WARNING_FLAGS -Wall) + set(WARNING_FLAGS_CXX "-Wall") + set(WARNING_FLAGS_C "-Wall") endif(CMAKE_COMPILER_IS_GNUCXX) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual") + set (WARNING_FLAGS_C "-Wall") +endif() + if(WIN32) if(MSVC) @@ -284,28 +287,29 @@ if(WIN32) # foreach(warning 4244 4251 4267 4275 4290 4786 4305 4996) # SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}") # endforeach(warning) - + set(MSVC_FLAGS "-DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS") - if (${MSVC_VERSION} EQUAL 1600) - set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" ) - endif (${MSVC_VERSION} EQUAL 1600) + if (${MSVC_VERSION} GREATER 1599) + set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" ) + endif (${MSVC_VERSION} GREATER 1599) endif(MSVC) set(NOMINMAX 1) -endif(WIN32) +endif(WIN32) + +set (BOOST_CXX_FLAGS "-DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION -DBOOST_BIMAP_DISABLE_SERIALIZATION") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} ${MSVC_FLAGS} -D_REENTRANT") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} ${MSVC_FLAGS} -D_REENTRANT") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS} -D_REENTRANT") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} -D_REENTRANT ${BOOST_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}") include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS} - ${Boost_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIR} - ${ALUT_INCLUDE_DIR} - ${OPENGL_INCLUDE_DIR} - ${OPENAL_INCLUDE_DIR} - ${SIMGEAR_INCLUDE_DIR} - ${PLIB_INCLUDE_DIR} ) + ${Boost_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIR} + ${OPENGL_INCLUDE_DIR} + ${OPENAL_INCLUDE_DIR} + ${SIMGEAR_INCLUDE_DIR} + ${PLIB_INCLUDE_DIR} ) include_directories(${PROJECT_SOURCE_DIR}/src) @@ -321,36 +325,26 @@ check_function_exists(mkfifo HAVE_MKFIFO) # configure a header file to pass some of the CMake settings # to the source code configure_file ( - "${PROJECT_SOURCE_DIR}/src/Include/config_cmake.h.in" - "${PROJECT_BINARY_DIR}/src/Include/config.h" - ) + "${PROJECT_SOURCE_DIR}/src/Include/config_cmake.h.in" + "${PROJECT_BINARY_DIR}/src/Include/config.h" +) #and the same for the version header configure_file ( - "${PROJECT_SOURCE_DIR}/src/Include/version.h.cmake-in" - "${PROJECT_BINARY_DIR}/src/Include/version.h" - ) + "${PROJECT_SOURCE_DIR}/src/Include/version.h.cmake-in" + "${PROJECT_BINARY_DIR}/src/Include/version.h" +) add_subdirectory(src) add_subdirectory(utils) add_subdirectory(man) -set (INSTALL_DOCS - README - README.OpenAL - README.plib - README.OSG - README.SimGear) - -INSTALL(FILES ${INSTALL_DOCS} DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL) - #----------------------------------------------------------------------------- ### uninstall target #----------------------------------------------------------------------------- CONFIGURE_FILE( - "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/cmake_uninstall.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY) + "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) ADD_CUSTOM_TARGET(uninstall - "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") - + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") diff --git a/CMakeModules/ConfigureCPack.cmake b/CMakeModules/ConfigureCPack.cmake new file mode 100644 index 0000000..c21f914 --- /dev/null +++ b/CMakeModules/ConfigureCPack.cmake @@ -0,0 +1,24 @@ +# ConfigureCPack.cmake -- Configure CPack packaging + +if(EXISTS ${PROJECT_SOURCE_DIR}/.gitignore) + file(READ .gitignore CPACK_SOURCE_IGNORE_FILES) +else() + # clean tar-balls do not contain SCM (.git/.gitignore/...) files. + set(CPACK_SOURCE_IGNORE_FILES + "Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}") +endif() + +list (APPEND CPACK_SOURCE_IGNORE_FILES "${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore") + +# split version string into components, note CMAKE_MATCH_0 is the entire regexp match +string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${FLIGHTGEAR_VERSION} ) +set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) +set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2}) +set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) +set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING") +set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README") + +set(CPACK_SOURCE_GENERATOR TBZ2) +set(CPACK_SOURCE_PACKAGE_FILE_NAME "flightgear-${FLIGHTGEAR_VERSION}" CACHE INTERNAL "tarball basename") + +include (CPack) diff --git a/CMakeModules/ConfigureMsvc3rdParty.cmake b/CMakeModules/ConfigureMsvc3rdParty.cmake new file mode 100644 index 0000000..6475dd9 --- /dev/null +++ b/CMakeModules/ConfigureMsvc3rdParty.cmake @@ -0,0 +1,63 @@ +# ConfigureMsvc3rdParty.cmake - Configure 3rd Party Library Paths on Windows + +if (MSVC) + GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_SOURCE_DIR} PATH) + if (CMAKE_CL_64) + SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty.x64") + else (CMAKE_CL_64) + SET(TEST_3RDPARTY_DIR "${PARENT_DIR}/3rdparty") + endif (CMAKE_CL_64) + if (EXISTS ${TEST_3RDPARTY_DIR}) + set(MSVC_3RDPARTY_ROOT ${PARENT_DIR} CACHE PATH "Location where the third-party dependencies are extracted") + else (EXISTS ${TEST_3RDPARTY_DIR}) + set(MSVC_3RDPARTY_ROOT NOT_FOUND CACHE PATH "Location where the third-party dependencies are extracted") + endif (EXISTS ${TEST_3RDPARTY_DIR}) + list(APPEND PLATFORM_LIBS "winmm.lib") +else (MSVC) + set(MSVC_3RDPARTY_ROOT NOT_FOUND CACHE PATH "Location where the third-party dependencies are extracted") +endif (MSVC) + +if (MSVC AND MSVC_3RDPARTY_ROOT) + message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}") + set( OSG_MSVC "msvc" ) + if (${MSVC_VERSION} EQUAL 1700) + set( OSG_MSVC ${OSG_MSVC}110 ) + elseif (${MSVC_VERSION} EQUAL 1600) + set( OSG_MSVC ${OSG_MSVC}100 ) + else (${MSVC_VERSION} EQUAL 1700) + set( OSG_MSVC ${OSG_MSVC}90 ) + endif (${MSVC_VERSION} EQUAL 1700) + if (CMAKE_CL_64) + set( OSG_MSVC ${OSG_MSVC}-64 ) + set( MSVC_3RDPARTY_DIR 3rdParty.x64 ) + set( BOOST_LIB lib64 ) + else (CMAKE_CL_64) + set( MSVC_3RDPARTY_DIR 3rdParty ) + set( BOOST_LIB lib ) + endif (CMAKE_CL_64) + find_path(FLTK_DIR include/FL/Fl.H + ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/fltk + ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/fltk-1.3 + ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR} + ) + list (APPEND CMAKE_PROGRAM_PATH ${FLTK_DIR}/bin) + + set (CMAKE_LIBRARY_PATH ${FLTK_DIR}/lib ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/lib $(BOOST_ROOT)/$(BOOST_LIB) ) + set (CMAKE_INCLUDE_PATH ${FLTK_DIR}/include ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/SimGear/include) + find_path(BOOST_ROOT boost/version.hpp + ${MSVC_3RDPARTY_ROOT}/boost + ${MSVC_3RDPARTY_ROOT}/boost_1_52_0 + ${MSVC_3RDPARTY_ROOT}/boost_1_51_0 + ${MSVC_3RDPARTY_ROOT}/boost_1_50_0 + ${MSVC_3RDPARTY_ROOT}/boost_1_49_0 + ${MSVC_3RDPARTY_ROOT}/boost_1_48_0 + ${MSVC_3RDPARTY_ROOT}/boost_1_47_0 + ${MSVC_3RDPARTY_ROOT}/boost_1_46_1 + ${MSVC_3RDPARTY_ROOT}/boost_1_46_0 + ${MSVC_3RDPARTY_ROOT}/boost_1_45_0 + ${MSVC_3RDPARTY_ROOT}/boost_1_44_0 + ) + message(STATUS "BOOST_ROOT is ${BOOST_ROOT}") + set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include) + set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib) +endif (MSVC AND MSVC_3RDPARTY_ROOT) diff --git a/CMakeModules/DetectBrowser.cmake b/CMakeModules/DetectBrowser.cmake new file mode 100644 index 0000000..6d4be54 --- /dev/null +++ b/CMakeModules/DetectBrowser.cmake @@ -0,0 +1,22 @@ +# DetectBrowser.cmake -- Detect web browser launcher application + +# Set default command to open browser. Override with -DWEB_BROWSER=... +if (APPLE OR MSVC) + # opening the web browser is hardcoded for Mac and Windows, + # so this doesn't really have an effect... + set(WEB_BROWSER "open") +else() + if(CMAKE_SYSTEM_NAME MATCHES "Linux") + # "xdg-open" provides run-time detection of user's preferred browser on (most) Linux. + if (NOT LINUX_DISTRO MATCHES "Debian") + set(WEB_BROWSER "xdg-open" CACHE STRING "Command to open web browser") + else() + # Debian is different: "sensible-browser" provides auto-detection + set(WEB_BROWSER "sensible-browser" CACHE STRING "Command to open web browser") + endif() + else() + # Default for non Linux/non Mac/non Windows platform... + set(WEB_BROWSER "firefox" CACHE STRING "Command to open web browser") + endif() + message(STATUS "Web browser launcher command is: ${WEB_BROWSER}") +endif() diff --git a/CMakeModules/DetectDistro.cmake b/CMakeModules/DetectDistro.cmake new file mode 100644 index 0000000..5f81a4b --- /dev/null +++ b/CMakeModules/DetectDistro.cmake @@ -0,0 +1,13 @@ +# DetectDistro.cmake -- Detect Linux distribution + +message(STATUS "System is: ${CMAKE_SYSTEM_NAME}") + +if(CMAKE_SYSTEM_NAME MATCHES "Linux") + # Detect Linux distribution (if possible) + execute_process(COMMAND "/usr/bin/lsb_release" "-is" + TIMEOUT 4 + OUTPUT_VARIABLE LINUX_DISTRO + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Linux distro is: ${LINUX_DISTRO}") +endif() diff --git a/CMakeModules/FindALUT.cmake b/CMakeModules/FindALUT.cmake deleted file mode 100644 index 08d920b..0000000 --- a/CMakeModules/FindALUT.cmake +++ /dev/null @@ -1,67 +0,0 @@ -# Locate ALUT -# This module defines -# ALUT_LIBRARY -# ALUT_FOUND, if false, do not try to link to ALUT -# ALUT_INCLUDE_DIR, where to find the headers -# -# $ALUTDIR is an environment variable that would -# correspond to the ./configure --prefix=$ALUTDIR -# used in building ALUT. -# -# Created by James Turner. This was influenced by the FindOpenAL.cmake module. - -#============================================================================= -# Copyright 2005-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distributed this file outside of CMake, substitute the full -# License text for the above reference.) - -# Per my request, CMake should search for frameworks first in -# the following order: -# ~/Library/Frameworks/OpenAL.framework/Headers -# /Library/Frameworks/OpenAL.framework/Headers -# /System/Library/Frameworks/OpenAL.framework/Headers -# -# On OS X, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# OPENAL_LIBRARY to override this selection or set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. - -FIND_PATH(ALUT_INCLUDE_DIR alut.h - HINTS - $ENV{ALUTDIR} - PATH_SUFFIXES include/AL include/ALUT include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local - /usr - /opt -) - -FIND_LIBRARY(ALUT_LIBRARY - NAMES ALUT alut - HINTS - $ENV{ALUTDIR} - PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64 - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local - /usr - /opt -) - - -SET(ALUT_FOUND "NO") -IF(ALUT_LIBRARY AND ALUT_INCLUDE_DIR) - SET(ALUT_FOUND "YES") -ENDIF(ALUT_LIBRARY AND ALUT_INCLUDE_DIR) - diff --git a/CMakeModules/FindGooglePerfTools.cmake b/CMakeModules/FindGooglePerfTools.cmake new file mode 100644 index 0000000..c787e87 --- /dev/null +++ b/CMakeModules/FindGooglePerfTools.cmake @@ -0,0 +1,48 @@ +# - Try to find GooglePerfTools headers and libraries +# +# Usage of this module as follows: +# +# find_package(GooglePerfTools) +# +# Variables used by this module, they can change the default behaviour and need +# to be set before calling find_package: +# +# GooglePerfTools_ROOT_DIR Set this variable to the root installation of +# GooglePerfTools if the module has problems finding +# the proper installation path. +# +# Variables defined by this module: +# +# GooglePerfTools_FOUND System has GooglePerfTools libs/headers +# GooglePerfTools_LIBRARIES The GooglePerfTools libraries +# GooglePerfTools_INCLUDE_DIR The location of GooglePerfTools headers + +find_path(GooglePerfTools_ROOT_DIR + NAMES include/google/profiler.h +) + +find_path(GooglePerfTools_INCLUDE_DIR + NAMES google/profiler.h + HINTS ${GooglePerfTools_ROOT_DIR} +) + +find_library(GooglePerfTools_PROFILER_LIBRARY + NAMES profiler + HINTS ${GooglePerfTools_ROOT_DIR} +) + +set(GooglePerfTools_LIBRARIES ${GooglePerfTools_PROFILER_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GooglePerfTools + "Try setting GooglePerfTools_ROOT_DIR to root of your gperftools installation" + GooglePerfTools_LIBRARIES + GooglePerfTools_INCLUDE_DIR +) + +mark_as_advanced( + GooglePerfTools_ROOT_DIR + GooglePerfTools_LIBRARIES + GooglePerfTools_PROFILER_LIBRARY + GooglePerfTools_INCLUDE_DIR +) \ No newline at end of file diff --git a/CMakeModules/FindPLIB.cmake b/CMakeModules/FindPLIB.cmake index 685c54d..b787983 100644 --- a/CMakeModules/FindPLIB.cmake +++ b/CMakeModules/FindPLIB.cmake @@ -23,26 +23,13 @@ # (To distributed this file outside of CMake, substitute the full # License text for the above reference.) -# Per my request, CMake should search for frameworks first in -# the following order: -# ~/Library/Frameworks/OpenAL.framework/Headers -# /Library/Frameworks/OpenAL.framework/Headers -# /System/Library/Frameworks/OpenAL.framework/Headers -# -# On OS X, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# OPENAL_LIBRARY to override this selection or set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. - include(SelectLibraryConfigurations) set(save_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) set(CMAKE_FIND_FRAMEWORK ONLY) FIND_PATH(PLIB_INCLUDE_DIR ul.h PATH_SUFFIXES include/plib include - PATHS - ~/Library/Frameworks - /Library/Frameworks + PATHS ${ADDITIONAL_LIBRARY_PATHS} ) set(CMAKE_FIND_FRAMEWORK ${save_FIND_FRAMEWORK}) @@ -50,10 +37,7 @@ if(NOT PLIB_INCLUDE_DIR) FIND_PATH(PLIB_INCLUDE_DIR plib/ul.h PATH_SUFFIXES include HINTS $ENV{PLIBDIR} - PATHS - /usr/local - /opt/local - /usr + PATHS ${ADDITIONAL_LIBRARY_PATHS} ) endif() @@ -64,9 +48,7 @@ FIND_LIBRARY(PLIB_LIBRARIES NAMES plib PLIB HINTS $ENV{PLIBDIR} - PATHS - ~/Library/Frameworks - /Library/Frameworks + PATHS ${ADDITIONAL_LIBRARY_PATHS} ) if (MSVC) @@ -91,19 +73,13 @@ macro(find_static_component comp libs) NAMES ${compLib}_d HINTS $ENV{PLIBDIR} PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64 - PATHS - /usr/local - /usr - /opt + PATHS ${ADDITIONAL_LIBRARY_PATHS} ) FIND_LIBRARY(${compLibName}_RELEASE NAMES ${compLib} HINTS $ENV{PLIBDIR} PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64 - PATHS - /usr/local - /usr - /opt + PATHS ${ADDITIONAL_LIBRARY_PATHS} ) select_library_configurations( ${compLibBase} ) @@ -125,17 +101,17 @@ if(${PLIB_LIBRARIES} STREQUAL "PLIB_LIBRARIES-NOTFOUND") set(PLIB_LIBRARIES "") # clear value # based on the contents of deps, add other required PLIB -# static library dependencies. Eg PUI requires SSG and FNT +# static library dependencies. Eg PUI requires FNT set(outDeps ${PLIB_FIND_COMPONENTS}) foreach(c ${PLIB_FIND_COMPONENTS}) if (${c} STREQUAL "pu") # handle MSVC confusion over pu/pui naming, by removing # 'pu' and then adding it back - list(REMOVE_ITEM outDeps "pu") - list(APPEND outDeps ${PUNAME} "fnt" "ssg" "sg") + list(REMOVE_ITEM outDeps "pu" "fnt" "sg") + list(APPEND outDeps ${PUNAME} "fnt" "sg") elseif (${c} STREQUAL "puaux") - list(APPEND outDeps ${PUNAME} "fnt" "ssg" "sg") + list(APPEND outDeps ${PUNAME} "fnt" "sg") elseif (${c} STREQUAL "ssg") list(APPEND outDeps "sg") endif() @@ -144,7 +120,6 @@ if(${PLIB_LIBRARIES} STREQUAL "PLIB_LIBRARIES-NOTFOUND") list(APPEND outDeps "ul") # everything needs ul list(REMOVE_DUPLICATES outDeps) # clean up - # look for traditional static libraries foreach(component ${outDeps}) diff --git a/CMakeModules/FindSQLite3.cmake b/CMakeModules/FindSQLite3.cmake new file mode 100644 index 0000000..5210c8b --- /dev/null +++ b/CMakeModules/FindSQLite3.cmake @@ -0,0 +1,36 @@ +# Find Sqlite3 +# ~~~~~~~~~~~~ +# Copyright (c) 2007, Martin Dobias +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +# CMake module to search for Sqlite3 library +# +# If it's found it sets SQLITE3_FOUND to TRUE +# and following variables are set: +# SQLITE3_INCLUDE_DIR +# SQLITE3_LIBRARY + + +# FIND_PATH and FIND_LIBRARY normally search standard locations +# before the specified paths. To search non-standard paths first, +# FIND_* is invoked first with specified paths and NO_DEFAULT_PATH +# and then again with no specified paths to search the default +# locations. When an earlier FIND_* succeeds, subsequent FIND_*s +# searching for the same item do nothing. + +FIND_PATH(SQLITE3_INCLUDE_DIR sqlite3.h + PATH_SUFFIXES include + HINTS $ENV{SQLITE3DIR} + PATHS + ${ADDITIONAL_LIBRARY_PATHS} + ) + +FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3 sqlite3 + HINTS $ENV{SQLITE3DIR} + PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64 + PATHS ${ADDITIONAL_LIBRARY_PATHS} + ) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SQLITE3 DEFAULT_MSG SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR) diff --git a/CMakeModules/FindSimGear.cmake b/CMakeModules/FindSimGear.cmake index 84ee664..47ce3ca 100644 --- a/CMakeModules/FindSimGear.cmake +++ b/CMakeModules/FindSimGear.cmake @@ -29,24 +29,23 @@ include(SelectLibraryConfigurations) macro(find_sg_library libName varName libs) set(libVarName "${varName}_LIBRARY") - + # do not cache the library check + unset(${libVarName}_DEBUG CACHE) + unset(${libVarName}_RELEASE CACHE) + FIND_LIBRARY(${libVarName}_DEBUG NAMES ${libName}${CMAKE_DEBUG_POSTFIX} HINTS $ENV{SIMGEAR_DIR} PATH_SUFFIXES ${CMAKE_INSTALL_LIBDIR} libs64 libs libs/Win32 libs/Win64 PATHS - /usr/local - /usr - /opt + ${ADDITIONAL_LIBRARY_PATHS} ) FIND_LIBRARY(${libVarName}_RELEASE NAMES ${libName}${CMAKE_RELEASE_POSTFIX} HINTS $ENV{SIMGEAR_DIR} PATH_SUFFIXES ${CMAKE_INSTALL_LIBDIR} libs64 libs libs/Win32 libs/Win64 PATHS - /usr/local - /usr - /opt + ${ADDITIONAL_LIBRARY_PATHS} ) # message(STATUS "before: Simgear ${${libVarName}_RELEASE} ") @@ -72,25 +71,49 @@ macro(find_sg_library libName varName libs) endif() endmacro() -macro(find_sg_component comp libs) - set(compLib "sg${comp}") - string(TOUPPER "SIMGEAR_${comp}" libVar) - - find_sg_library(${compLib} ${libVar} ${libs}) -endmacro() - FIND_PATH(SIMGEAR_INCLUDE_DIR simgear/math/SGMath.hxx HINTS $ENV{SIMGEAR_DIR} PATH_SUFFIXES include PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local - /usr - /opt + ${ADDITIONAL_LIBRARY_PATHS} ) -# message(STATUS ${SIMGEAR_INCLUDE_DIR}) +# make sure the simgear include directory exists +if (NOT SIMGEAR_INCLUDE_DIR) + message(FATAL_ERROR "Cannot find SimGear includes! (Forgot 'make install' for SimGear?) " + "Compile & INSTALL SimGear before configuring FlightGear. " + "When using non-standard locations, use 'SIMGEAR_DIR' to configure the SimGear location.") +endif() + +message(STATUS "SimGear include directory: ${SIMGEAR_INCLUDE_DIR}") + +# read the simgear version header file, get the version +file(READ ${SIMGEAR_INCLUDE_DIR}/simgear/version.h SG_VERSION_FILE) + +# make sure the simgear/version.h header exists +if (NOT SG_VERSION_FILE) + message(FATAL_ERROR "Found SimGear, but it does not contain a simgear/version.h include! " + "SimGear installation is incomplete or mismatching.") +endif() + +string(STRIP "${SG_VERSION_FILE}" SIMGEAR_DEFINE) +string(REPLACE "#define SIMGEAR_VERSION " "" SIMGEAR_VERSION "${SIMGEAR_DEFINE}") + +if(NOT SIMGEAR_VERSION) + message(FATAL_ERROR "Unable to find SimGear or simgear/version.h does not exist/is invalid. " + "Make sure you have installed the SimGear ${SimGear_FIND_VERSION} includes. " + "When using non-standard locations, please use 'SIMGEAR_DIR' " + "to select the SimGear library location to be used.") +endif() + +message(STATUS "found SimGear version: ${SIMGEAR_VERSION} (needed ${SimGear_FIND_VERSION})") + +if(NOT "${SIMGEAR_VERSION}" EQUAL "${SimGear_FIND_VERSION}") + message(FATAL_ERROR "You have installed a mismatching SimGear version ${SIMGEAR_VERSION} " + "instead of ${SimGear_FIND_VERSION} as required by FlightGear. " + "When using multiple SimGear installations, please use 'SIMGEAR_DIR' " + "to select the SimGear library location to be used.") +endif() # dependent packages find_package(ZLIB REQUIRED) @@ -101,7 +124,6 @@ if(SIMGEAR_SHARED) find_sg_library(SimGearCore SIMGEAR_CORE SIMGEAR_CORE_LIBRARIES) find_sg_library(SimGearScene SIMGEAR_SCENE SIMGEAR_LIBRARIES) - list(APPEND SIMGEAR_LIBRARIES ${SIMGEAR_CORE_LIBRARIES}) set(SIMGEAR_CORE_LIBRARY_DEPENDENCIES "") @@ -113,63 +135,22 @@ else(SIMGEAR_SHARED) set(SIMGEAR_LIBRARIES "") # clear value set(SIMGEAR_CORE_LIBRARIES "") # clear value - message(STATUS "looking for static Simgear libraries") + message(STATUS "looking for static SimGear libraries") - # note the order here affects the order Simgear libraries are - # linked in, and hence ability to link when using a traditional - # linker such as GNU ld on Linux - set(comps - environment - nasal - bucket - route - timing - io - serial - math - props - structure - xml - misc - threads - debug - magvar - ) - - set(scene_comps - tsync - ephem - sky - material - tgdb - model - screen - bvh - util - sound) - - foreach(component ${comps}) - find_sg_component(${component} SIMGEAR_CORE_LIBRARIES) - endforeach() - - foreach(component ${scene_comps}) - find_sg_component(${component} SIMGEAR_LIBRARIES) - endforeach() + find_sg_library(SimGearCore SIMGEAR_CORE SIMGEAR_CORE_LIBRARIES) + find_sg_library(SimGearScene SIMGEAR_SCENE SIMGEAR_LIBRARIES) - # again link order matters - scene libraries depend on core ones list(APPEND SIMGEAR_LIBRARIES ${SIMGEAR_CORE_LIBRARIES}) - - #message(STATUS "all libs ${SIMGEAR_LIBRARIES}") set(SIMGEAR_CORE_LIBRARY_DEPENDENCIES ${CMAKE_THREAD_LIBS_INIT} - ${ZLIB_LIBRARY}) + ${ZLIB_LIBRARY} + ${LIBSVN_LIBRARIES} + ${WINMM_LIBRARY}) set(SIMGEAR_SCENE_LIBRARY_DEPENDENCIES - ${ALUT_LIBRARY} - ${OPENAL_LIBRARY} - ${LIBSVN_LIBRARIES}) + ${OPENAL_LIBRARY}) if(WIN32) list(APPEND SIMGEAR_CORE_LIBRARY_DEPENDENCIES ws2_32.lib) @@ -177,27 +158,35 @@ else(SIMGEAR_SHARED) if(NOT MSVC) # basic timing routines on non windows systems, may be also cygwin?! - check_function_exists(clock_gettime clock_gettime_in_libc) - if(NOT clock_gettime_in_libc) - check_library_exists(rt clock_gettime "" have_rt) - if(have_rt) - list(APPEND SIMGEAR_CORE_LIBRARY_DEPENDENCIES rt) - endif(have_rt) - endif(NOT clock_gettime_in_libc) + check_library_exists(rt clock_gettime "" have_rt) + if(have_rt) + list(APPEND SIMGEAR_CORE_LIBRARY_DEPENDENCIES rt) + endif(have_rt) endif(NOT MSVC) endif(SIMGEAR_SHARED) -# now we've found SimGear, check its version +if((NOT SIMGEAR_CORE_LIBRARIES)OR(NOT SIMGEAR_LIBRARIES)) + message(FATAL_ERROR "Cannot find SimGear libraries! (Forgot 'make install' for SimGear?) " + "Compile & INSTALL SimGear before configuring FlightGear. " + "When using non-standard locations, use 'SIMGEAR_DIR' to configure the SimGear location.") +else() + message(STATUS "found SimGear libraries") +endif() +# now we've found SimGear, try test-compiling using its includes include(CheckCXXSourceRuns) -message(STATUS "looking for version: ${SimGear_FIND_VERSION}") - SET(CMAKE_REQUIRED_INCLUDES ${SIMGEAR_INCLUDE_DIR}) +# clear cache, run a fresh compile test every time +unset(SIMGEAR_COMPILE_TEST CACHE) + +# disable OSG dependencies for test-compiling +set(CMAKE_REQUIRED_DEFINITIONS "-DNO_OPENSCENEGRAPH_INTERFACE") check_cxx_source_runs( "#include #include \"simgear/version.h\" + #include \"simgear/math/SGMath.hxx\" #define xstr(s) str(s) #define str(s) #s @@ -214,18 +203,24 @@ check_cxx_source_runs( sscanf( xstr(SIMGEAR_VERSION), \"%d.%d.%d\", &major, &minor, µ ); - if ( (major < MIN_MAJOR) || - (major == MIN_MAJOR && minor < MIN_MINOR) || - (major == MIN_MAJOR && minor == MIN_MINOR && micro < MIN_MICRO) ) { + if ( (major != MIN_MAJOR) || + (minor != MIN_MINOR) || + (micro != MIN_MICRO) ) { return -1; } return 0; } " - SIMGEAR_VERSION_OK) + SIMGEAR_COMPILE_TEST) + +if(NOT SIMGEAR_COMPILE_TEST) + message(FATAL_ERROR "Oops, you have installed SimGear includes, however test compiling failed. " + "Try removing 'CMakeCache.txt' and reconfigure with 'cmake'.") +endif() +unset(CMAKE_REQUIRED_DEFINITIONS) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SimGear DEFAULT_MSG - SIMGEAR_LIBRARIES SIMGEAR_INCLUDE_DIR SIMGEAR_VERSION_OK) + SIMGEAR_LIBRARIES SIMGEAR_CORE_LIBRARIES SIMGEAR_INCLUDE_DIR SIMGEAR_COMPILE_TEST) diff --git a/CMakeModules/FindSvnClient.cmake b/CMakeModules/FindSvnClient.cmake index 15f6ba2..724dcff 100644 --- a/CMakeModules/FindSvnClient.cmake +++ b/CMakeModules/FindSvnClient.cmake @@ -16,14 +16,20 @@ macro(find_static_component comp libs) string(TOUPPER "${comp}" compLibBase) set( compLibName ${compLibBase}_LIBRARY ) + # NO_DEFAULT_PATH is important on Mac - we need to ensure subversion + # libraires in dist/ or Macports are picked over the Apple version + # in /usr, since that's what we will ship. + # On other platforms we do need default paths though, i.e. since Linux + # distros may use architecture-specific directories (like + # /usr/lib/x86_64-linux-gnu) which we cannot hardcode/guess here. FIND_LIBRARY(${compLibName} +if(APPLE) + NO_DEFAULT_PATH +endif(APPLE) NAMES ${compLib} - HINTS $ENV{PLIBDIR} + HINTS $ENV{LIBSVN_DIR} ${CMAKE_INSTALL_PREFIX} ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64 - PATHS - /usr/local - /usr - /opt + PATHS ${ADDITIONAL_LIBRARY_PATHS} ) list(APPEND ${libs} ${${compLibName}}) @@ -49,13 +55,11 @@ endif(HAVE_APR_CONFIG) if(HAVE_APR_CONFIG OR MSVC) find_path(LIBSVN_INCLUDE_DIR svn_client.h + NO_DEFAULT_PATH HINTS - $ENV{LIBSVN_DIR} + $ENV{LIBSVN_DIR} ${CMAKE_INSTALL_PREFIX} ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include PATH_SUFFIXES include/subversion-1 - PATHS - /usr/local - /usr - /opt + PATHS ${ADDITIONAL_LIBRARY_PATHS} ) set(LIBSVN_LIBRARIES "") diff --git a/CMakeModules/FindUDev.cmake b/CMakeModules/FindUDev.cmake index dc363ba..f6d2a25 100644 --- a/CMakeModules/FindUDev.cmake +++ b/CMakeModules/FindUDev.cmake @@ -20,9 +20,7 @@ FIND_PATH( FIND_LIBRARY( UDEV_LIBRARIES NAMES udev libudev - PATHS - /usr/${CMAKE_INSTALL_LIBDIR} - /usr/local/${CMAKE_INSTALL_LIBDIR} + PATHS ${ADDITIONAL_LIBRARY_PATHS} ${UDEV_PATH_LIB} ) diff --git a/CMakeModules/FlightGearComponent.cmake b/CMakeModules/FlightGearComponent.cmake index 2b0daa9..0136e44 100644 --- a/CMakeModules/FlightGearComponent.cmake +++ b/CMakeModules/FlightGearComponent.cmake @@ -1,9 +1,19 @@ macro(flightgear_component name sources) + set(fc ${name}) + set(fh ${name}) + foreach(s ${sources}) + set_property(GLOBAL + APPEND PROPERTY FG_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/${s}") + set(fc "${fc}#${CMAKE_CURRENT_SOURCE_DIR}/${s}") + endforeach() - set(libName "fg${name}") - add_library(${libName} STATIC ${sources} ${ARGV2}) + foreach(h ${ARGV2}) + set_property(GLOBAL + APPEND PROPERTY FG_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/${h}") + set(fh "${fh}#${CMAKE_CURRENT_SOURCE_DIR}/${h}") + endforeach() - set_property(GLOBAL APPEND PROPERTY FG_LIBS ${libName}) - + set_property(GLOBAL APPEND PROPERTY FG_GROUPS_C "${fc}@") + set_property(GLOBAL APPEND PROPERTY FG_GROUPS_H "${fh}@") endmacro() diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..df463ea --- /dev/null +++ b/INSTALL @@ -0,0 +1,33 @@ +How to build FlightGear +======================= + +FlightGear uses the CMake build system to generate a platform-specific +build environment. CMake reads the CMakeLists.txt files that you'll +find throughout the source directories, checks for installed +dependencies and then generates the appropriate build system. + +If you don't already have CMake installed on your system you can grab +it from http://www.cmake.org, use version 2.6.4 or later. + +Under unices (i.e. Linux, Solaris, Free-BSD, HP-Ux, OSX) use the cmake +or ccmake command-line utils. Preferably, create an out-of-source +build directory and run cmake or ccmake from there. The advantage to +this approach is that the temporary files created by CMake won't +clutter the source directory, and also makes it possible to have +multiple independent build targets by creating multiple build +directories. In a directory alongside the FlightGear source directory +use: + + mkdir fgbuild + cd fgbuild + cmake ../flightgear -DCMAKE_BUILD_TYPE=Release + make + sudo make install + +For detailed instructions see: +* README.cmake for Linux, OSX, ... +* README.msvc for Windows / Visual Studio + +For further information see: +* http://wiki.flightgear.org/Building_Flightgear + diff --git a/README.OSG b/README.OSG index d547ffe..e165836 100644 --- a/README.OSG +++ b/README.OSG @@ -3,7 +3,7 @@ You *must* have OpenSceneGraph (OSG) installed to build this version of FlightGear. -Notice that FlightGear 2.6.0 requires at least version 3.0.0. +Notice that this version of SimGear/FlightGear requires at least OSG 3.0.0. You can get the latest version of OSG from: diff --git a/README.OpenAL b/README.OpenAL index 7c0cd01..f21c656 100644 --- a/README.OpenAL +++ b/README.OpenAL @@ -18,12 +18,3 @@ ccmake . then press 'g' to generate and exit ] -The alut library is also required, but comes separately in the package -freelut-1.1.0.tar.gz. This package can be downloaded from the same page -(http://connect.creativelabs.com/openal/default.aspx). Download and run: -tar xzvf freelut-1.1.0.tar.gz -cd freealut-1.1.0 -./configure -make -sudo make install - diff --git a/README.cmake b/README.cmake index f03333f..767f9c7 100644 --- a/README.cmake +++ b/README.cmake @@ -1,6 +1,7 @@ Getting started with CMake +========================== -[Windows instructions at the end of this document] +[For Windows build instructions see README.msvc] (These instructions apply to Unix-like systems, including Cygwin and Mac. To build using Visual Studio or some other IDE supported by CMake, most of the @@ -11,7 +12,7 @@ code (eg, from Git) is at /home/curt/projects/flightgear, you might create /home/curt/projects/fgbuild. Change into the new directory, and run cmake ../flightgear - + To generate standard Unix Makefiles in fgbuild. Probably you want to specify an install prefix: @@ -39,7 +40,7 @@ usually simpler. By default, we select a release build. To create a debug build, use cmake ../flightgear -DCMAKE_BUILD_TYPE=Debug - + (or MinSizeRel, or RelWithDbg) Debug builds will automatically use corresponding debug builds of required @@ -64,21 +65,23 @@ configurations, eg To set an optional feature, do - cmake ../flightgear -DFEATURE_NAME=ON + cmake ../flightgear -DFEATURE_NAME=ON (or 'OFF' to disable ) To see the variables that can be configured / are currently defined, you can run one of the GUI front ends, or the following command: - cmake ../flighgear -L + cmake ../flightgear -L Add 'A' to see all the options (including advanced options), or 'H' to see the help for each option (similar to running configure --help under autoconf): cmake ../flightgear -LH + Build Targets +============= For a Unix makefile build, 'make dist', 'make uninstall' and 'make test' are all available and should work as expected. 'make clean' is also as normal, @@ -95,7 +98,7 @@ For target conditional files, you can append to the SOURCES or HEADERS lists inside an if() test, for example: if(APPLE) - list(APPEND SOURCES extraFile1.cxx extraFile2.cxx) + list(APPEND SOURCES extraFile1.cxx extraFile2.cxx) endif() Setting include directories @@ -113,11 +116,11 @@ Use set_target_property(), for example set_target_property(fgfs PROPERTIES COMPILE_DEFINITIONS FOO BAR=1) - + You can set a property on an individual source file: set_property(SOURCE myfile.cxx PROPERTY COMPILE_FLAGS "-Wno-unsigned-compare") - + Detecting Features / Libraries For most standard libraries (Gtk, wxWidget, Python, GDAL, Qt, libXml, Boost), @@ -128,7 +131,7 @@ cmake provides a standard helper. To see the available modules, run: In the root CMakeLists file, use a statement like: find_package(OpenGL REQUIRED) - + Each package helper sets various variables such aaa_FOUND, aaa_INCLUDE_DIR, and aaa_LIBRARY. Depending on the complexity of the package, these variables might have different names (eg, OPENSCENEGRAPH_LIBRARIES). @@ -140,86 +143,19 @@ directory, eg /usr/share/cmake/modules on Unix systems. Note libraries support by pkg-config can be handled directly, with no need to create a custom FindABC helper. - + Adding a new executable target add_executable(myexecutable ${SOURCES} ${HEADERS}) target_link_libraries(myexecutable .... libraries ... ) install(TARGETS myexecutable RUNTIME DESTINATION bin) - + (If the executable should not be installed, omit the final line above) If you add an additional line add_test(testname ${EXECUTABLE_OUTPUT_PATH}/myexecutable) - + Then running 'make test' will run your executable as a unit test. The executable should return either a success or failure result code. - - -SPECIAL INSTRUCTIONS TO BUILD UNDER WINDOWS WITH VISUAL STUDIO -============================================================== - -On Windows, assumptions on the directory structure are made to automate the discovery of dependencies. -This recommended directory structure is described below: - -${MSVC_3RDPARTY_ROOT} / - 3rdParty / ( includes plib, fltk, zlib, libpng, libjpeg, libtiff, freetype, libsvn, gdal, ... - bin / - include / - lib / - 3rdParty.x64 / ( 64 bit version ) - ... - boost_1_44_0 / - boost / - install / - msvc100 / ( for VS2010 32 bits, or msvc90, msvc90-64 or msvc100-64 for VS2008 32, VS2008 64 and VS2010 64 ) - OpenSceneGraph / ( OSG CMake install - bin / - include / - lib / - SimGear / - include / - lib / - -If you do not use the recommended structure you will need to enter paths by hand. Source and build directories can be located anywhere. - -The suggested inputs to cmake are : - MSVC_3RDPARTY_ROOT : location of the above directory structure - CMAKE_INSTALL_PREFIX : ${MSVC_3RDPARTY_ROOT}/install/msvc100/FlightGear (or any variation for the compiler version described above ) - - -1. Set up a work directory as described above. - -2. Open the Cmake gui. - -3. Set "Where is the source code" to wherever you put the FlightGear sources (from the released tarball or the git repository). - -4. Set "Where to build the binaries" to an empty directory. - -5. Press the "Configure" button. The first time that the project is configured, Cmake will bring up a window asking which compiler you wish to use. Normally just accept Cmakes suggestion, and press Finish. Cmake will now do a check on your system and will produce a preliminary build configuration. - -6. Cmake adds new configuration variables in red. Some have a value ending with -NOTFOUND. These variables should receive your attention. Some errors will prevent FlightGear to build and others will simply invalidate some options without provoking build errors. First check the MSVC_3RDPARTY_ROOT variable. If it is not set, chances are that there will be a lot of -NOTFOUND errors. Instead of trying to fix every error individually, set that variable and press the "Configure" button again. - -7. Also check the lines with a checkbox. These are build options and may impact the feature set of the built program. - -8. Change the CMAKE_INSTALL_PREFIX to ${MSVC_3RDPARTY_ROOT}/install/msvc100/FlightGear because C:\Program Files is likely unwritable to ordinary Windows users and will integrate better with the above directory structure (this is mandatory for SimGear if you don't want to solve errors by hand). - -10. Repeat the process until the "Generate" button is enabled. - -11. Press the "Generate" button. - -12. Start Visual Studio 2010 and load the FlightGear solution (FlightGear.sln) located in "Where to build the binaries" (point 4.) - -13. Choose the "Release" build in the VS2010 "Generation" toolbar - -14. Generate the solution. - -15. If there are build errors, return to Cmake, clear remaining errors, "Configure" and "Generate" - -16. When Visual Studio is able to build everything without errors, build the INSTALL project to put the product files in ${CMAKE_INSTALL_PREFIX} - -17. Enjoy! - -PS: When updating the source from git, it is usually unnecessary to restart Cmake as the solution is able to reconfigure itself when Cmake files are changed. Simply rebuild the solution from Visual Studio and accept the reload of updated projects. It also possible to edit CMakeList.txt files directly in Visual Studio as they also appear in the solution, and projects will be reconfigured on the next generation. To change build options or directory path, it is mandatory to use the Cmake Gui. In case of problems, locate the CMakeCache.txt in "Where to build the binaries” directory and delete it to reconfigure from scratch or use the menu item File->Delete Cache. diff --git a/README.msvc b/README.msvc new file mode 100644 index 0000000..34e7398 --- /dev/null +++ b/README.msvc @@ -0,0 +1,114 @@ +WINDOWS / VISUAL STUDIO BUILD INSTRUCTIONS +========================================== + +[For Linux/Unix/OSX/... instructions see README.cmake] + + +Directory Structure +=================== + +On Windows, assumptions on the directory structure are made to automate +the discovery of dependencies. +This recommended directory structure is described below: + +${MSVC_3RDPARTY_ROOT} / + 3rdParty / ( includes plib, fltk, zlib, libpng, + libjpeg, libtiff, freetype, libsvn, + gdal, ... ) + bin / + include / + lib / + 3rdParty.x64 / ( 64 bit version ) + ... + boost_1_44_0 / + boost / + install / + msvc100 / ( for VS2010 32 bits, or msvc90, + msvc90-64 or msvc100-64 for VS2008 32, + VS2008 64 and VS2010 64 ) + OpenSceneGraph / ( OSG CMake install ) + bin / + include / + lib / + SimGear / + include / + lib / + +If you do not use the recommended structure you will need to enter paths +by hand. Source and build directories can be located anywhere. + + +Running CMake GUI +================= + +The suggested inputs to cmake are : + MSVC_3RDPARTY_ROOT : location of the above directory structure + CMAKE_INSTALL_PREFIX : ${MSVC_3RDPARTY_ROOT}/install/msvc100/FlightGear + (or any variation for the compiler version described above) + +1. Set up a work directory as described above. + +2. Open the CMake gui. + +3. Set "Where is the source code" to wherever you put the FlightGear + sources (from the released tarball or the git repository). + +4. Set "Where to build the binaries" to an empty directory. + +5. Press the "Configure" button. The first time that the project is + configured, CMake will bring up a window asking which compiler you wish + to use. Normally just accept Cmakes suggestion, and press Finish. CMake + will now do a check on your system and will produce a preliminary build + configuration. + +6. CMake adds new configuration variables in red. Some have a value ending + with -NOTFOUND. These variables should receive your attention. Some + errors will prevent FlightGear to build and others will simply + invalidate some options without provoking build errors. First check the + MSVC_3RDPARTY_ROOT variable. If it is not set, chances are that there + will be a lot of -NOTFOUND errors. Instead of trying to fix every error + individually, set that variable and press the "Configure" button again. + +7. Also check the lines with a checkbox. These are build options and may + impact the feature set of the built program. + +8. Change the CMAKE_INSTALL_PREFIX to + ${MSVC_3RDPARTY_ROOT}/install/msvc100/FlightGear because + C:\Program Files is likely unwritable to ordinary Windows users and + will integrate better with the above directory structure (this is + mandatory for SimGear if you don't want to solve errors by hand). + +10. Repeat the process until the "Generate" button is enabled. + +11. Press the "Generate" button. + +12. Start Visual Studio 2010 and load the FlightGear solution + (FlightGear.sln) located in "Where to build the binaries" (point 4.) + +13. Choose the "Release" build in the VS2010 "Generation" toolbar + +14. Generate the solution. + +15. If there are build errors, return to CMake, clear remaining errors, + "Configure" and "Generate" + +16. When Visual Studio is able to build everything without errors, build + the INSTALL project to put the product files in ${CMAKE_INSTALL_PREFIX} + +17. Enjoy! + + +Rebuilding Updated Sources +========================== + +When updating the sources from Git, it is usually unnecessary to restart +CMake as the solution is able to reconfigure itself when CMake files are +changed. Simply rebuild the solution from Visual Studio and accept the +reload of updated projects. It also possible to edit CMakeList.txt files +directly in Visual Studio as they also appear in the solution, and projects +will be reconfigured on the next generation. To change build options or +directory path, it is mandatory to use the CMake Gui. In case of problems, +locate the CMakeCache.txt in "Where to build the binaries" directory and +delete it to reconfigure from scratch or use the menu item +File->Delete Cache. + diff --git a/docs-mini/FlightGear-FAQ.html b/docs-mini/FlightGear-FAQ.html index d6b9c2d..6ec3c55 100644 --- a/docs-mini/FlightGear-FAQ.html +++ b/docs-mini/FlightGear-FAQ.html @@ -285,12 +285,12 @@

The latest development code is available for everyone through our - CVS repository. See - http://flightgear.org/cvsResources/ for details. + git repository. See + http://wiki.flightgear.org/FlightGear_and_Git for details.

Otherwise, you can get relatively up-to-date snapshots of the development tree at - ftp://flightgear.sourceforge.net/pub/flightgear/Devel/Snapshots/. + http://flightgear.simpits.org:8080/, which are recompiled at every git commit.

diff --git a/docs-mini/README.autoconf b/docs-mini/README.autoconf deleted file mode 100644 index 1a00b38..0000000 --- a/docs-mini/README.autoconf +++ /dev/null @@ -1,35 +0,0 @@ -Flight Gear uses the Gnu autoconf and automake tools for managing -Makefiles. Key input files for this system are: - - configure.in - Top level directory - Makefile.am - One in each subdirectory - Include/config.in - input file for building config.h - -If you need to modify any of these files, you will need to install the -following packages. If you don't have a prebuilt package for your OS, -you can fetch them from the locations listed below: - - - GNU autoconf (available from ftp://prep.ai.mit.edu/pub/gnu) - - GNU automake (available from ftp://ftp.cygnus.com/pub/tromey) - -After making a change to configure.in you will need to run: - - aclocal ; autoheader ; automake -a ; autoconf - -Then follow the regular build procedure: - - ./configure; make; make install - -For debuging purposes you might want to try something like: - - CFLAGS=-Wall CXXFLAGS=-Wall ./configure; make; make install - -For full optimization using the EGCS compiler on an Intel processor you -could try something like: - - MACH="-mpentium" # -m486 -mpentiumpro etc. - export CC=egcc # for Linux - export CFLAGS="-Wall -O5 -fomit-frame-pointer -ffast-math -funroll-loops $MACH" - export CXXFLAGS="-Wall -O5 -fomit-frame-pointer -ffast-math -funroll-loops $MACH" - ./configure - diff --git a/docs-mini/README.canvas b/docs-mini/README.canvas new file mode 100644 index 0000000..a83865e --- /dev/null +++ b/docs-mini/README.canvas @@ -0,0 +1,169 @@ +Canvas - A 2D Drawing API +========================= + +Author: Thomas Geymayer +Revision: 2012/05/18 + +Introduction +------------ + +With the increasing complexity of (glass) cockpits the need for a simple API to +draw on a 2D surface without modifying the C++ core increased heavily in the +last time. The 2D canvas is an effort to satisfy this needs. It is now possible +to create offscreen rendertargets only by using the property tree and placing +them on any 3D object on the aircraft by using certain filter criteria. + +Currently it is only possible to place text on the canvas but 2d shapes (using +OpenVG) are going to follow. + +Creating a canvas +----------------- + +A new canvas can be instantiated by creating a node /canvas/texture[] +with at least the following children: + + The width of the underlying texture + The height of the underlying texture + + The width of the canvas + The height of the canvas + +The dimensions of the canvas are needed to be able to use textures with +different resolutions but use the same units for rendering to the canvas. +Therefore you can choose any texture size with the same canvas size and always +get the same results (apart from resolution dependent artifacts). + +* Filtering: + + Optionally you can enable mipmapping and/or multisampling (Coverage Sampling + Antialiasing): + + Use mipmapping (default: false) + Coverage Samples (default: 0) + Color Samples (default: 0, always + have to be <= coverage-samples) +Drawing +------- + +Drawing to the canvas is accomplished by creating nodes as childs of the +canvas root node. Every shape has to be a child of a node. Currently +only drawing Text is possible: + +* General: + The following parameters are used by multiple elements: + + Color: + A color can be specified by the following subtree (NAME is replaced by + another name depending on the usage of the color) + + + + + + + + +* Text: + Create a node and configure with the following properties: + + The text to be displayed + The font to be used (Searched in + 1. aircraft-dir/Fonts + 2. aircraft-dir + 3. $FG_DATA/Fonts + 4. Default osg font paths + The font size (default: 32) + Ratio between character height and width + (default: 1) + A 3x3 transformation matrix specified by 6 values + (child elements , ..., which equal to a, + ...,f used in the SVG standard) See + http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined + for details. + You can also use shortcuts and use an alternative to + specifying six values: + - Translation: , (both default to 0) + - Rotation: + - Scale: , (s[0] is required, s[1] + defaults to s[0]) + Text alignment (default: "left-baseline") One of: + + "left-top" + "left-center" + "left-bottom" + + "center-top" + "center-center" + "center-bottom" + + "right-top" + "right-center" + "right-bottom" + + "left-baseline" + "center-baseline" + "right-baseline" + + "left-bottom-baseline" + "center-bottom-baseline" + "right-bottom-baseline" + A bitwise combination of the following values + 1 (Text - default) + 2 (Boundingbox) + 4 (Filled boundingbox) + 8 (Alignment -> Draw a cross at the position + of the text) + Padding between for the boundingbox (default: 0) + Text color + Fill color (for the bounding box) + +Placement +--------- + +To place the canvas into the scene one ore more elements can be +added to the texture node. By setting at least on of the following nodes +the objects where the canvas texture should be placed on are selected: + + Match objects with the given texture assigned + Match objects with the given name + Match objects with a parent matching the given name + (Not necessarily the direct parent) + +Example +------- + + + + 384 + 512 + 768 + 1024 + false + 0 + 0 + + 0 + 0.02 + 0 + 1 + + + + TEST MESSAGE + helvetica_bold.txf + 40 + + + 18 + 50 + + + + + + EICAS.png + HDD 1 + + + diff --git a/docs-mini/README.mingw b/docs-mini/README.mingw index 757cd95..ff48a1a 100644 --- a/docs-mini/README.mingw +++ b/docs-mini/README.mingw @@ -62,44 +62,6 @@ patch header: #ifndef NEED_FTIME #include -GLUT -==== - -use precompiled in order to avoid conflicts with glut32.dll already installed. - -http://www.xmission.com/~nate/glut.html -http://www.xmission.com/~nate/glut/glut-3.7.6-bin.zip - -The header has to be updated with respect to MINGW. - -*** glut.h Tue Dec 12 22:22:52 2000 ---- /local_old/include/GL/glut.h Thu Aug 18 20:41:15 2005 -*************** -*** 20,26 **** - /* XXX This is from Win32's */ - # ifndef APIENTRY - # define GLUT_APIENTRY_DEFINED -! # if (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) || defined(__BORLANDC__) || defined(__LCC__) - # define APIENTRY __stdcall - # else - # define APIENTRY ---- 20,26 ---- - /* XXX This is from Win32's */ - # ifndef APIENTRY - # define GLUT_APIENTRY_DEFINED -! # if (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) || defined(__BORLANDC__) || defined(__LCC__) || defined(__MINGW32__) - # define APIENTRY __stdcall - # else - # define APIENTRY - - -install: -cp glut.h /usr/local/include -cp glut32.dll /usr/local/bin - -reimp glut32.lib -cp libglut32.a /usr/local/lib - OpenAL ====== @@ -114,7 +76,6 @@ install: cd libs reimp OpenAL32.lib cp libopenal32.a /usr/local/lib -cp alut.lib /usr/local/lib/libalut.a cd .. mkdir /usr/local/include/AL cp Include/* /usr/local/include/AL diff --git a/docs-mini/README.protocol b/docs-mini/README.protocol index ef9c39a..80a7163 100644 --- a/docs-mini/README.protocol +++ b/docs-mini/README.protocol @@ -131,6 +131,26 @@ each of which describes the properties of on variable to write/read. an optional offset which can be used for unit conversion. (for example, degrees Celcius to degrees Fahrenheit). +For input chunks there exist some more options: + + optional boolean parameter to enable handling of incoming values + as relative changes (default: false) + (Can be eg. used to realise up/down buttons by just sending 1 or + -1 respectively) + + an optional minimum limit for the value to be clamped to. This + limit is always specified as absolute value, also with relative + changes enabled. (default: 0) + an optional upper limit for the input value to be clamped to. If + equals no limit is applied. (default: 0) + instead of clamping to minimum and maximum limits, wrap values + around. Values will be in [min, max[ (default: false) + (Usefull for eg. heading selector to start again with 1 for + values higher than 360) + +, , and are only used for numeric data types. can +additionally be used with type 'bool', where it toggles the value, if the received +value evaluates to 'true', otherwise the value is left unchanged. Chunks can also consist of a single constant , like in: Data Section @@ -184,7 +204,30 @@ P=3.59 +Control the heading bug by sending relative changes separated by newlines: + + + + + + + newline + + + heading bug + int + /autopilot/settings/heading-bug-deg + true + 1 + 360 + true + + + + + + -- writing data in XML syntax ------------------------------------------------- diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt index 6e97957..66bc113 100644 --- a/man/CMakeLists.txt +++ b/man/CMakeLists.txt @@ -1,3 +1,7 @@ +if (MSVC) + # don't install man pages on Windows +else(MSVC) + if(${CMAKE_VERSION} VERSION_GREATER 2.8.4) # use official include provided by latest CMake include(GNUInstallDirs) @@ -27,3 +31,5 @@ foreach(man ${MANPAGES}) install(FILES ${PROJECT_BINARY_DIR}/man/${man} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT doc) endforeach() + +endif(MSVC) \ No newline at end of file diff --git a/man/fgfs.1.in b/man/fgfs.1.in index 8ae2237..002749d 100644 --- a/man/fgfs.1.in +++ b/man/fgfs.1.in @@ -47,7 +47,7 @@ executable. This is used for UIUC aircraft only. Specify starting postion by airport ID. .TP .BI "--altitude=" "value" -Specify starting altitude (in feet unless --units-meters specified). +Specify starting altitude (in feet unless \-\-units\-meters specified). .TP .BI "--atc610x" Enable atc610x interface. @@ -275,7 +275,7 @@ Specify window geometry (ie. 640x480, 800x600, etc). Specify heading or yaw angle (degrees). .TP .BR "--help" ", " "-h" -Show a brief help message. Use --verbose,-v for a full listing of options. +Show a brief help message. Use \-\-verbose,\-v for a full listing of options. .TP .BI "--httpd=" "port" Start an HTTP server on the specified port. @@ -371,7 +371,7 @@ set property to . Open an IO connection using the PVE (Provision Entertainment) protocol. .TP .B "--random-wind" -Randomize initial wind parameters. See also: --wind. +Randomize initial wind parameters. See also: \-\-wind. .TP .BI "--ray=" "medium" "," "direction" "," "hz" "," "options" "," "..." Open an IO connection to a "RayWoodworth" motion chair. @@ -429,7 +429,7 @@ Specify turbulence from 0.0 (calm) to 1.0 (severe). .TP .BI "--uBody=" "value" Specify velocity along the body X axis (feet per second unless ---units-meters specified). +\-\-units\-meters specified). .TP .B "--units-feet" Use feet instead of meters. Affects other options as well as the @@ -441,7 +441,7 @@ values displayed by the HUD. .TP .BI "--vBody=" "value" Specify velocity along the body Y axis (feet per second unless ---units-meters specified). +\-\-units\-meters specified). .TP .BI "--vc=" "knots" Specify initial airspeed (knots). @@ -465,7 +465,7 @@ Specify starting position relative to a VOR. .TP .BI "--wBody=" "value" Specify velocity along the body Z axis (feet per second unless ---units-meters specified). +\-\-units-meters specified). .TP .BI "--wind=" "DIR@SPEED" Specify wind coming from DIR (degrees) at SPEED (knots). Values may be @@ -509,8 +509,8 @@ $FG_ROOT/mice.xml Mouse bindings. .RE .SH BUGS -Send bug reports to or visit -. +Send bug reports to or visit +. .SH SEE ALSO fgjs(1), fgpanel(1), js_demo(1), terrasync(1) .SH AUTHORS diff --git a/man/fgjs.1.in b/man/fgjs.1.in index 5953cba..38a04a4 100644 --- a/man/fgjs.1.in +++ b/man/fgjs.1.in @@ -26,8 +26,8 @@ is a joystick utility for the FlightGear Flight Simulator. It allows you to generate an appropriate configuration file for your joystick. For more information visit . .SH BUGS -Send bug reports to or visit -. +Send bug reports to or visit +. .SH SEE ALSO fgfs(1), fgpanel(1), js_demo(1), terrasync(1) .SH AUTHORS diff --git a/man/fgpanel.1.in b/man/fgpanel.1.in index c497934..aff7344 100644 --- a/man/fgpanel.1.in +++ b/man/fgpanel.1.in @@ -38,8 +38,8 @@ Specify the panel description file to be displayed. .BI "--prop:" "name=value" set property to . .SH BUGS -Send bug reports to or visit -. +Send bug reports to or visit +. .SH SEE ALSO fgfs(1), fgjs(1), js_demo(1), terrasync(1) .SH AUTHORS diff --git a/man/gl-info.1.in b/man/gl-info.1.in index 9a8a534..65027f6 100644 --- a/man/gl-info.1.in +++ b/man/gl-info.1.in @@ -17,15 +17,15 @@ .\" .TH GL-INFO 1 "January 2002" "@VERSION@" "@PACKAGE@" .SH NAME -gl-info \- OpenGL test program for FlightGear +gl\-info \- OpenGL test program for FlightGear .SH SYNOPSIS .B gl-info .SH DESCRIPTION .B gl-info is an OpenGL test program used to verify a valid OpenGL environment. .SH BUGS -Send bug reports to or visit -. +Send bug reports to or visit +. .SH SEE ALSO fgfs(1), fgjs(1), fgpanel(1), js_demo(1), terrasync(1) .SH AUTHORS diff --git a/man/js_demo.1.in b/man/js_demo.1.in index 6acf8b0..dc61465 100644 --- a/man/js_demo.1.in +++ b/man/js_demo.1.in @@ -25,8 +25,8 @@ js_demo \- joystick test program for FlightGear is a joystick test program for the FlightGear Flight Simulator. For more information visit . .SH BUGS -Send bug reports to or visit -. +Send bug reports to or visit +. .SH SEE ALSO fgfs(1), fgjs(1), fgpanel(1), terrasync(1) .SH AUTHORS diff --git a/man/terrasync.1.in b/man/terrasync.1.in index 985b2d4..67f45ca 100644 --- a/man/terrasync.1.in +++ b/man/terrasync.1.in @@ -37,8 +37,8 @@ Specify the UDP port where terrasync listens for a FlightGear Flight Simulator c .BI "-v" Enable verbose output. .SH BUGS -Send bug reports to or visit -. +Send bug reports to or visit +. .SH SEE ALSO fgfs(1), fgjs(1), js_demo(1), terrasync(1) .SH AUTHORS diff --git a/package/RedHat/README b/package/RedHat/README index a82b070..357149e 100644 --- a/package/RedHat/README +++ b/package/RedHat/README @@ -1,8 +1,7 @@ -Building Flight Gear RPM package for Red Hat +Building FlightGear RPM package for Red Hat -G.Richard Keech - -2003-10-29 +Initial version: G.Richard Keech , 2003-10-29 +Last updated: The FlightGear Project, 2012-06-30 This directory contains the files which, along with the source code tar files, can be used to build @@ -16,20 +15,18 @@ To build flighgear from source do the following: 1. copy flightgear.spec to /usr/src/redhat/SPECS/ -2. copy flightgear.48.png to /usr/src/redhat/SOURCES - -3. copy flightgear.desktop to /usr/src/redhat/SOURCES +2. copy flightgear.desktop to /usr/src/redhat/SOURCES -4. obtain fgfs-base-0.9.3.tar.gz and FlightGear-0.9.3.tar.gz +3. obtain FlightGear-data-2.8.0.tar.bz2 and flightgear-2.8.0.tar.bz2 and copy them into /usr/src/redhat/SOURCES. -5. obtain and install SimGear (>= 0.3.4). +4. obtain and install SimGear (= 2.8.0). -6. look in the BuildRequires section of flightgear.spec +5. look in the BuildRequires section of flightgear.spec and check that all the packages referred to are installed. -7. cd /usr/src/redhat/SPECS +6. cd /usr/src/redhat/SPECS rpmbuild -ba flightgear.spec that's it. diff --git a/package/RedHat/flightgear.48.png b/package/RedHat/flightgear.48.png deleted file mode 100644 index 8e08762..0000000 Binary files a/package/RedHat/flightgear.48.png and /dev/null differ diff --git a/package/RedHat/flightgear.desktop b/package/RedHat/flightgear.desktop index e4e0d46..49b060e 100644 --- a/package/RedHat/flightgear.desktop +++ b/package/RedHat/flightgear.desktop @@ -1,7 +1,8 @@ [Desktop Entry] Encoding=UTF-8 -Name=Flight Gear -Comment=Flight Gear Flight Simulator +Type=Application +Name=FlightGear +Comment=FlightGear Flight Simulator Exec=fgfs -Icon= -Categories=GNOME;Application;Game +Icon=flightgear +Categories=Game;Simulation diff --git a/package/RedHat/flightgear.spec b/package/RedHat/flightgear.spec index ca2b60c..7c03fc1 100644 --- a/package/RedHat/flightgear.spec +++ b/package/RedHat/flightgear.spec @@ -1,40 +1,40 @@ %define name flightgear -%define version 0.9.3 -%define release 1grk +%define version 2.8.0 +%define release 1 Summary: The FlightGear Flight Simulator Name: %{name} Version: %{version} Release: %{release} -License: GPL +License: GPL-2.0 +URL: http://www.flightgear.org Group: Games/Other BuildRoot: %{_tmppath}/%{name}-buildroot -Source: ftp://ftp.flightgear.org/pub/fgfs/Source/FlightGear-%{version}.tar.gz -Source1: ftp://ftp.flightgear.org/pub/fgfs/Shared/fgfs-base-%{version}.tar.gz +Source: http://mirrors.ibiblio.org/pub/mirrors/flightgear/ftp/Source/flightgear-%{version}.tar.bz2 +Source1: http://mirrors.ibiblio.org/pub/mirrors/flightgear/ftp/Shared/FlightGear-data-%{version}.tar.bz2 Source3: flightgear.desktop -Source10: %{name}.48.png -BuildRequires: plib >= 1.6.0, simgear = 0.3.4, XFree86-devel, XFree86-Mesa-libGL, XFree86-Mesa-libGLU, gcc, zlib-devel -Requires: XFree86-devel, XFree86-Mesa-libGL, XFree86-Mesa-libGLU, gcc, zlib-devel -URL: http://www.flightgear.org +BuildRequires: gcc, gcc-c++, cmake +BuildRequires: plib >= 1.8.0, SimGear = %{version} +BuildRequires: XFree86-devel, XFree86-Mesa-libGL, XFree86-Mesa-libGLU, zlib-devel +BuildRequires: OpenSceneGraph >= 3.0.0 Obsoletes: FlightGear Provides: FlightGear = %{version}-%{release} %description -The Flight Gear project is working to create a sophisticated flight simulator +The FlightGear project is working to create a sophisticated flight simulator framework for the development and pursuit of interesting flight simulator ideas. We are developing a solid basic sim that can be expanded and improved upon by anyone interested in contributing. %prep -%setup -q -n FlightGear-%{version} -rm -f docs-mini/*~ +%setup -q -n %{name}-%{version} %build -%configure +cmake -DSIMGEAR_SHARED:BOOL=ON -DENABLE_TESTS:BOOL=OFF -DFG_DATA_DIR:STRING="/usr/share/flightgear" -DJPEG_FACTORY:BOOL=ON -DCMAKE_INSTALL_PREFIX:PATH="/usr" make %install -make DESTDIR=$RPM_BUILD_ROOT install +make install DESTDIR=$RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT%{_libdir} tar xzf %{SOURCE1} -C $RPM_BUILD_ROOT%{_libdir} mv $RPM_BUILD_ROOT%{_libdir}/FlightGear-%{version} $RPM_BUILD_ROOT%{_libdir}/FlightGear @@ -46,8 +46,9 @@ desktop-file-install --vendor flightgear --delete-original \ --dir $RPM_BUILD_ROOT%{_datadir}/applications \ $RPM_BUILD_ROOT%{_datadir}/applications/flightgear.desktop +# install icon mkdir -p $RPM_BUILD_ROOT%{_datadir}/pixmaps -cp %{SOURCE10} $RPM_BUILD_ROOT%{_datadir}/pixmaps/%{name}.png +cp icons/fg-128.png $RPM_BUILD_ROOT%{_datadir}/pixmaps/FlightGear.png %post @@ -66,6 +67,11 @@ rm -rf $RPM_BUILD_ROOT %_mandir/man1/* %changelog +* Thu Jun 28 2012 Thorsten Brehm +- Updated to 2.8.0 +- Converted to CMake +- Use shared SimGear libraries + * Sun Oct 26 2003 Richard Keech - updated for 0.9.3 diff --git a/package/Win-NSIS/flightgear-nightly-vs2010.nsi b/package/Win-NSIS/flightgear-nightly-vs2010.nsi index a6ce5f8..27d6100 100644 --- a/package/Win-NSIS/flightgear-nightly-vs2010.nsi +++ b/package/Win-NSIS/flightgear-nightly-vs2010.nsi @@ -32,8 +32,8 @@ AutoCloseWindow true !define ThirdPartyBinDir "3rdParty\bin" -!define MUI_ICON "flightgear\projects\VC90\flightgear.ico" -!define MUI_UNICON "flightgear\projects\VC90\flightgear.ico" +!define MUI_ICON "flightgear\package\flightgear.ico" +!define MUI_UNICON "flightgear\package\flightgear.ico" !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_RIGHT diff --git a/package/Win-NSIS/flightgear-nightly.nsi b/package/Win-NSIS/flightgear-nightly.nsi index 856d861..8048c36 100644 --- a/package/Win-NSIS/flightgear-nightly.nsi +++ b/package/Win-NSIS/flightgear-nightly.nsi @@ -32,8 +32,8 @@ AutoCloseWindow true !define ThirdPartyBinDir "3rdParty\bin" -!define MUI_ICON "flightgear\projects\VC90\flightgear.ico" -!define MUI_UNICON "flightgear\projects\VC90\flightgear.ico" +!define MUI_ICON "flightgear\package\flightgear.ico" +!define MUI_UNICON "flightgear\package\flightgear.ico" !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_RIGHT diff --git a/package/Win-NSIS/flightgear64-nightly-vs2010.nsi b/package/Win-NSIS/flightgear64-nightly-vs2010.nsi new file mode 100644 index 0000000..f8d03de --- /dev/null +++ b/package/Win-NSIS/flightgear64-nightly-vs2010.nsi @@ -0,0 +1,153 @@ +!include "MUI.nsh" + +!system 'osgversion --so-number > %TEMP%\osg-so-number.txt' +!system 'osgversion --version-number > %TEMP%\osg-version.txt' + +!define /file OSGSoNumber $%TEMP%\osg-so-number.txt +!define /file OSGVersion $%TEMP%\osg-version.txt +!define /file FGVersion flightgear\version + +!echo "osg-so is ${OSGSoNumber}" + +Name "FlightGear Nightly vs2010" +OutFile fgfs_win64_vs2010_nightly_${FGVersion}.exe + +; use LZMA for best compression +SetCompressor /FINAL /SOLID lzma +SetCompressorDictSize 64 + +InstallDir $PROGRAMFILES\FlightGear64-nightly-2010 + +; Request admin privileges for Windows Vista +RequestExecutionLevel highest + +; don't hang around +AutoCloseWindow true + +!define UninstallKey "Software\Microsoft\Windows\CurrentVersion\Uninstall\FlightGear64-nightly-2010" +!define FGBinDir "install\msvc100-64\FlightGear\bin" +!define FGRunDir "install\msvc100-64\fgrun" +!define OSGInstallDir "install\msvc100-64\OpenSceneGraph" +!define OSGPluginsDir "${OSGInstallDir}\bin\osgPlugins-${OSGVersion}" + +!define ThirdPartyBinDir "3rdParty.x64\bin" + +!define MUI_ICON "flightgear\package\flightgear.ico" +!define MUI_UNICON "flightgear\package\flightgear.ico" + +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_RIGHT +!define MUI_HEADERIMAGE_BITMAP "flightgear\package\Win-NSIS\fg-install-header.bmp" ; optional + + + +;!define MUI_WELCOMEFINISHPAGE_BITMAP "welcome.bmp" +;!define MUI_UNWELCOMEFINISHPAGE_BITMAP "welcome.bmp" + +!insertmacro MUI_PAGE_WELCOME +; include GPL license page +!insertmacro MUI_PAGE_LICENSE "flightgear\Copying" +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +!define MUI_FINISHPAGE_RUN $INSTDIR\fgrun.exe +!define MUI_FINISHPAGE_RUN_TEXT "Run FlightGear now" +!insertmacro MUI_PAGE_FINISH + + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_LANGUAGE "English" + +; The stuff to install +Section "" ;No components page, name is not important + + SetShellVarContext all + ; Set output path to the installation directory. + SetOutPath $INSTDIR + + File ${FGBinDir}\fgfs.exe + File ${FGBinDir}\fgjs.exe + File ${FGRunDir}\bin\fgrun.exe + + File ${OSGInstallDir}\bin\osg${OSGSoNumber}-osg.dll + File ${OSGInstallDir}\bin\osg${OSGSoNumber}-osgDB.dll + File ${OSGInstallDir}\bin\osg${OSGSoNumber}-osgGA.dll + File ${OSGInstallDir}\bin\osg${OSGSoNumber}-osgParticle.dll + File ${OSGInstallDir}\bin\osg${OSGSoNumber}-osgText.dll + File ${OSGInstallDir}\bin\osg${OSGSoNumber}-osgUtil.dll + File ${OSGInstallDir}\bin\osg${OSGSoNumber}-osgViewer.dll + File ${OSGInstallDir}\bin\osg${OSGSoNumber}-osgSim.dll + File ${OSGInstallDir}\bin\osg${OSGSoNumber}-osgFX.dll + + File ${OSGInstallDir}\bin\ot12-OpenThreads.dll + + File ${ThirdPartyBinDir}\*.dll + + ; VC runtime redistributables + File "$%VCINSTALLDIR%\redist\x64\Microsoft.VC100.CRT\*.dll" + + File /r ${FGRunDir}\share\locale + + SetOutPath $INSTDIR\osgPlugins-${OSGVersion} + File ${OSGPluginsDir}\osgdb_ac.dll + File ${OSGPluginsDir}\osgdb_osg.dll + File ${OSGPluginsDir}\osgdb_osga.dll + File ${OSGPluginsDir}\osgdb_3ds.dll + File ${OSGPluginsDir}\osgdb_mdl.dll + File ${OSGPluginsDir}\osgdb_jpeg.dll + File ${OSGPluginsDir}\osgdb_rgb.dll + File ${OSGPluginsDir}\osgdb_png.dll + File ${OSGPluginsDir}\osgdb_dds.dll + File ${OSGPluginsDir}\osgdb_txf.dll + File ${OSGPluginsDir}\osgdb_freetype.dll + File ${OSGPluginsDir}\osgdb_serializers_osg.dll + File ${OSGPluginsDir}\osgdb_serializers_osganimation.dll + File ${OSGPluginsDir}\osgdb_serializers_osgfx.dll + File ${OSGPluginsDir}\osgdb_serializers_osgmanipulator.dll + File ${OSGPluginsDir}\osgdb_serializers_osgparticle.dll + File ${OSGPluginsDir}\osgdb_serializers_osgshadow.dll + File ${OSGPluginsDir}\osgdb_serializers_osgsim.dll + File ${OSGPluginsDir}\osgdb_serializers_osgterrain.dll + File ${OSGPluginsDir}\osgdb_serializers_osgtext.dll + File ${OSGPluginsDir}\osgdb_serializers_osgvolume.dll + File ${OSGPluginsDir}\osgdb_deprecated_osg.dll + File ${OSGPluginsDir}\osgdb_deprecated_osgparticle.dll + + + Exec '"$INSTDIR\fgrun.exe" --silent --fg-exe="$INSTDIR\fgfs.exe" ' + + CreateDirectory "$SMPROGRAMS\FlightGear" + CreateShortCut "$SMPROGRAMS\FlightGear\FlightGear64-nightly-2010.lnk" "$INSTDIR\fgrun.exe" + + + WriteUninstaller "$INSTDIR\FlightGear_Uninstall.exe" + + WriteRegStr HKLM ${UninstallKey} "DisplayName" "FlightGear64 Nightly (vs2010 build)" + WriteRegStr HKLM ${UninstallKey} "DisplayVersion" "${FGVersion}" + WriteRegStr HKLM ${UninstallKey} "UninstallString" "$INSTDIR\FlightGear_Uninstall.exe" + WriteRegStr HKLM ${UninstallKey} "UninstallPath" "$INSTDIR\FlightGear_Uninstall.exe" + WriteRegDWORD HKLM ${UninstallKey} "NoModify" 1 + WriteRegDWORD HKLM ${UninstallKey} "NoRepair" 1 + WriteRegStr HKLM ${UninstallKey} "URLInfoAbout" "http://www.flightgear.org/" + +SectionEnd + + + +Section "Uninstall" + + SetShellVarContext all + + + Delete "$SMPROGRAMS\FlightGear\FlightGear64-nightly-2010.lnk" + ; only delete the FlightGear group if it's empty + RMDir "$SMPROGRAMS\FlightGear" + + RMDir /r "$INSTDIR" + + DeleteRegKey HKLM ${UninstallKey} + +SectionEnd + diff --git a/package/flightgear.desktop b/package/flightgear.desktop index 8d793dc..d5f6973 100644 --- a/package/flightgear.desktop +++ b/package/flightgear.desktop @@ -1,7 +1,9 @@ [Desktop Entry] -Encoding=UTF-8 -Name=Flight Gear -Exec=artsdsp /usr/games/fgfs --geometry=1024x768 --enable-game-mode +Name=FlightGear +GenericName=Flight Simulator +Comment=An Open Source Flight Simulator +Exec=fgfs --geometry=1024x768 +Icon=flightgear Terminal=false Type=Application -Icon=flightgear +Categories=Game;Simulation; diff --git a/package/flightgear.ico b/package/flightgear.ico new file mode 100644 index 0000000..e041319 Binary files /dev/null and b/package/flightgear.ico differ diff --git a/package/mac/build-mac-nightly-dmg.rb b/package/mac/build-mac-nightly-dmg.rb index 6161599..a666143 100755 --- a/package/mac/build-mac-nightly-dmg.rb +++ b/package/mac/build-mac-nightly-dmg.rb @@ -3,7 +3,7 @@ require 'ERB' $osgLibs = ['osgFX', 'osgParticle', 'osg', 'osgGA', 'osgText', 'osgUtil', 'osgSim', 'osgViewer', 'osgDB'] -$osgPlugins = ['ac', 'osg', 'freetype', 'qt', 'imageio', 'rgb', 'txf', 'mdl', '3ds'] +$osgPlugins = ['ac', 'osg', 'freetype', 'imageio', 'rgb', 'txf', 'mdl', '3ds', 'dds'] def runOsgVersion(option) env = "export DYLD_LIBRARY_PATH=#{Dir.pwd}/dist/lib" @@ -15,9 +15,14 @@ osgVersion = runOsgVersion('version-number') $osgSoVersion=runOsgVersion('so-number') $openThreadsSoVersion=runOsgVersion('openthreads-soversion-number') +$codeSignIdentity = ENV['FG_CODESIGN_IDENTITY'] +puts "Code signing identity is #{$codeSignIdentity}" + puts "osgVersion=#{osgVersion}, so-number=#{$osgSoVersion}" -$alutSourcePath='/Library/Frameworks/ALUT.framework' +$svnLibs = ['svn_client', 'svn_wc', 'svn_delta', 'svn_diff', 'svn_ra', + 'svn_ra_local', 'svn_repos', 'svn_fs', 'svn_fs_fs', 'svn_fs_util', + 'svn_ra_svn', 'svn_subr', 'svn_ra_neon'] def fix_install_names(object) #puts "fixing install names for #{object}" @@ -31,65 +36,88 @@ def fix_install_names(object) oldName = "libOpenThreads.#{$openThreadsSoVersion}.dylib" newName= "@executable_path/../Frameworks/#{oldName}" `install_name_tool -change #{oldName} #{newName} #{object}` - - alutBundlePath = "@executable_path/../Frameworks/Alut.framework" - alutLib = "Versions/A/ALUT" - `install_name_tool -change #{$alutSourcePath}/#{alutLib} #{alutBundlePath}/#{alutLib} #{object}` end -prefixDir=Dir.pwd + "/dist" +$prefixDir=Dir.pwd + "/dist" dmgDir=Dir.pwd + "/image" srcDir=Dir.pwd + "/flightgear" - puts "Erasing previous image dir" `rm -rf #{dmgDir}` bundle=dmgDir + "/FlightGear.app" contents=bundle + "/Contents" macosDir=contents + "/MacOS" -frameworksDir=contents +"/Frameworks" +$frameworksDir=contents +"/Frameworks" resourcesDir=contents+"/Resources" osgPluginsDir=contents+"/PlugIns/osgPlugins-#{osgVersion}" volName="\"FlightGear Nightly Build\"" +def fix_svn_install_names(object) + $svnLibs.each do |l| + fileName = "lib#{l}-1.0.dylib" + newName = "@executable_path/../Frameworks/#{fileName}" + `install_name_tool -change #{fileName} #{newName} #{object}` + end +end + +def copy_svn_libs() + puts "Copying Subversion client libraries" + $svnLibs.each do |l| + libFile = "lib#{l}-1.0.dylib" + path = "#{$frameworksDir}/#{libFile}" + `cp #{$prefixDir}/lib/#{libFile} #{$frameworksDir}` + fix_svn_install_names(path) + # `install_name_tool -id #{libFile} #{path}` + end +end + +def code_sign(path) + puts "Signing #{path}" + `codesign -s "#{$codeSignIdentity}" #{path}` +end + fgVersion = File.read("#{srcDir}/version").strip dmgPath = Dir.pwd + "/fg_mac_nightly_#{fgVersion}.dmg" puts "Creating directory structure" `mkdir -p #{macosDir}` -`mkdir -p #{frameworksDir}` +`mkdir -p #{$frameworksDir}` `mkdir -p #{resourcesDir}` `mkdir -p #{osgPluginsDir}` puts "Copying binaries" -bins = ['fgfs', 'terrasync', 'fgjs'] +bins = ['fgfs', 'fgjs', 'fgcom'] bins.each do |b| - `cp #{prefixDir}/bin/#{b} #{macosDir}/#{b}` - fix_install_names("#{macosDir}/#{b}") + if !File.exist?("#{$prefixDir}/bin/#{b}") + next + end + + outPath = "#{macosDir}/#{b}" + `cp #{$prefixDir}/bin/#{b} #{outPath}` + fix_install_names(outPath) + fix_svn_install_names(outPath) end puts "copying libraries" $osgLibs.each do |l| libFile = "lib#{l}.#{$osgSoVersion}.dylib" - `cp #{prefixDir}/lib/#{libFile} #{frameworksDir}` - fix_install_names("#{frameworksDir}/#{libFile}") + `cp #{$prefixDir}/lib/#{libFile} #{$frameworksDir}` + fix_install_names("#{$frameworksDir}/#{libFile}") end # and not forgetting OpenThreads libFile = "libOpenThreads.#{$openThreadsSoVersion}.dylib" -`cp #{prefixDir}/lib/#{libFile} #{frameworksDir}` +`cp #{$prefixDir}/lib/#{libFile} #{$frameworksDir}` $osgPlugins.each do |p| pluginFile = "osgdb_#{p}.so" - `cp #{prefixDir}/lib/osgPlugins-#{osgVersion}/#{pluginFile} #{osgPluginsDir}` + `cp #{$prefixDir}/lib/osgPlugins-#{osgVersion}/#{pluginFile} #{osgPluginsDir}` fix_install_names("#{osgPluginsDir}/#{pluginFile}") end -# custom ALUT -# must copy frameworks using ditto -`ditto #{$alutSourcePath} #{frameworksDir}/ALUT.framework` +copy_svn_libs() # Info.plist template = File.read("#{srcDir}/package/mac/nightly.plist.in") @@ -104,11 +132,26 @@ File.open("#{contents}/Info.plist", 'w') { |f| `cp #{srcDir}/COPYING #{dmgDir}` # Macflightgear launcher -puts "Copying Macflightgear launcher files" +if File.exist?("FlightGearOSX") + puts "Copying Macflightgear launcher files" + Dir.chdir "FlightGearOSX" do + `cp FlightGear #{macosDir}` + `rsync -a *.rb *.lproj *.sh *.tiff #{resourcesDir}` + end +end + +if File.exist?("#{$prefixDir}/bin/fgcom-data") + puts "Copying FGCom data files" + `ditto #{$prefixDir}/bin/fgcom-data #{resourcesDir}/fgcom-data` +end -Dir.chdir "macflightgear" do - `cp FlightGear #{macosDir}` - `rsync -a --exclude=\".svn\" *.rb *.lproj *.sh *.tiff #{resourcesDir}` +# code sign all executables in MacOS dir. Do this last since reource +# changes will invalidate the signature! +Dir.foreach(macosDir) do |b| + if b == '.' or b == '..' then + next + end + code_sign("#{macosDir}/#{b}") end puts "Creating DMG" diff --git a/package/mac/hudson_mac_build_launcher.sh b/package/mac/hudson_mac_build_launcher.sh deleted file mode 100755 index dc3bd24..0000000 --- a/package/mac/hudson_mac_build_launcher.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -SDK_PATH="/Developer/SDKs/MacOSX10.6.sdk" -OSX_TARGET="10.6" - -svn co https://macflightgear.svn.sourceforge.net/svnroot/macflightgear/trunk/FlightGearOSX macflightgear - -pushd macflightgear - -# compile the stub executable -gcc -o FlightGear -mmacosx-version-min=$OSX_TARGET -isysroot $SDK_PATH -arch i386 main.m \ - -framework Cocoa -framework RubyCocoa -framework Foundation -framework AppKit - -popd - diff --git a/package/mac/nightly.plist.in b/package/mac/nightly.plist.in index fd9fe18..8ad6f12 100644 --- a/package/mac/nightly.plist.in +++ b/package/mac/nightly.plist.in @@ -11,7 +11,7 @@ CFBundleSignature ???? CFBundleExecutable - fgfs + FlightGear CFBundleIdentifier org.flightgear.FlightGear CFBundleVersion @@ -19,7 +19,11 @@ CFBundleShortVersionString unstable nightly build LSMinimumSystemVersion - 10.5.0 + 10.6.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication diff --git a/package/make-base-package.noarch.sh b/package/make-base-package.noarch.sh index 740f5ad..5367732 100755 --- a/package/make-base-package.noarch.sh +++ b/package/make-base-package.noarch.sh @@ -11,7 +11,7 @@ fi if [ "x$2" != "x" ]; then VERSION="$2" else - VERSION="2.5" + VERSION="2.7" fi echo base dir = $BASE, version = $VERSION @@ -40,6 +40,7 @@ tar \ data/Aircraft/Instruments \ data/Aircraft/Instruments-3d \ data/Aircraft/UIUC \ + data/Aircraft/777 \ data/Aircraft/777-200 \ data/Aircraft/A6M2 \ data/Aircraft/ASK13 \ @@ -72,9 +73,8 @@ tar \ data/joysticks.xml \ data/keyboard.xml \ data/Lighting \ - data/materials.dtd \ - data/materials.xml \ data/mice.xml \ + data/Materials \ data/Models \ data/MP \ data/N* \ diff --git a/package/openSUSE/FlightGear.desktop b/package/openSUSE/FlightGear.desktop new file mode 100644 index 0000000..9fa442f --- /dev/null +++ b/package/openSUSE/FlightGear.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=FlightGear +Comment=FlightGear Flight Simulator +Exec=fgfs +Icon=FlightGear +Categories=Game;Simulation; diff --git a/package/openSUSE/FlightGear.spec b/package/openSUSE/FlightGear.spec new file mode 100644 index 0000000..561e20f --- /dev/null +++ b/package/openSUSE/FlightGear.spec @@ -0,0 +1,77 @@ +Summary: The FlightGear Flight Simulator +Name: FlightGear +Version: 2.8.0 +Release: 1 +License: GPL-2.0 +URL: http://www.flightgear.org +Group: Amusements/Games/3D/Simulation +BuildRoot: %{_tmppath}/%{name}-buildroot +Source0: http://mirrors.ibiblio.org/pub/mirrors/flightgear/ftp/Source/flightgear-%{version}.tar.bz2 +Source1: %{name}.desktop + +BuildRequires: gcc, gcc-c++, cmake +BuildRequires: update-desktop-files +BuildRequires: SimGear = %{version}, SimGear-devel = %{version} +BuildRequires: libOpenSceneGraph-devel >= 3.0 +BuildRequires: subversion-devel, libapr1-devel +BuildRequires: libopenal1-soft, openal-soft +BuildRequires: zlib, zlib-devel +BuildRequires: libfreetype6 +BuildRequires: libjpeg62, libjpeg62-devel +BuildRequires: libudev-devel +BuildRequires: boost-devel >= 1.37 + +Requires: OpenSceneGraph-plugins >= 3.0 +Requires: FlightGear-data = %{version} +Requires: SimGear = %{version} + +%description +The FlightGear project is working to create a sophisticated flight simulator +framework for the development and pursuit of interesting flight simulator +ideas. We are developing a solid basic sim that can be expanded and improved +upon by anyone interested in contributing. + +%prep +%setup -q -n flightgear-%{version} -T -b 0 +rm -f docs-mini/* +# remove unnecessary DATE/TIME dependency +sed -i 's/__DATE__" "__TIME__//' src/FDM/JSBSim/FGJSBBase.cpp + +%build +export CFLAGS="$RPM_OPT_FLAGS" +export CXXFLAGS="$RPM_OPT_FLAGS" +export BUILD_ID=OpenSuSE +export BUILD_NUMBER=0 +cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} -DSIMGEAR_SHARED:BOOL=ON -DENABLE_TESTS:BOOL=OFF -DFG_DATA_DIR:STRING="/usr/share/flightgear" -DJPEG_FACTORY:BOOL=ON +make %{?_smp_mflags} + +%install +make %{?_smp_mflags} install DESTDIR=$RPM_BUILD_ROOT +# install desktop link +mkdir -p $RPM_BUILD_ROOT/%{_datadir}/applications/ +cp %{SOURCE1} $RPM_BUILD_ROOT/%{_datadir}/applications/ +%suse_update_desktop_file -i %{name} +# move docs into doc/packages subfolder +mkdir -p $RPM_BUILD_ROOT/usr/share/doc/packages/FlightGear +cp README $RPM_BUILD_ROOT/usr/share/doc/packages/FlightGear/. +cp COPYING $RPM_BUILD_ROOT/usr/share/doc/packages/FlightGear/. +cp AUTHORS $RPM_BUILD_ROOT/usr/share/doc/packages/FlightGear/. +# install icon +mkdir -p $RPM_BUILD_ROOT%{_datadir}/pixmaps +cp icons/fg-128.png $RPM_BUILD_ROOT%{_datadir}/pixmaps/FlightGear.png +# remove obsolete/test utilities +cd $RPM_BUILD_ROOT/usr/bin && rm -f GPSsmooth MIDGsmooth UGsmooth metar + +%files +%defattr(-, root, root, -) +%dir /usr/share/doc/packages/FlightGear +/usr/share/doc/packages/FlightGear/* +%{_bindir}/* +%_mandir/man1/* +%{_datadir}/pixmaps/FlightGear.png +%{_datadir}/applications/%{name}.desktop + +%changelog +* Thu Jun 30 2012 thorstenb@flightgear.org +- Initial version + diff --git a/package/openSUSE/README b/package/openSUSE/README new file mode 100644 index 0000000..36f3e1e --- /dev/null +++ b/package/openSUSE/README @@ -0,0 +1,28 @@ +Building a FlightGear RPM package for openSUSE + +(Last tested with openSUSE 11.4+12.1) + +This directory contains the files which, along with +the source code tar files, can be used to build +an RPM package targeted at an openSUSE Linux system. + +To build FlightGear from source do the following: + +1. obtain flightgear-2.8.0.tar.bz2 (adapt version if + necessary) and copy it into ~/rpmbuild/SOURCES + +2. copy FlightGear.desktop to ~/rpmbuild/SOURCES + +3. obtain (or build) and install SimGear (exact + match with FlightGear's version required). + +4. look in the BuildRequires section of FlightGear.spec + and check that all the packages referred to are + installed (note, some of these packages may be part + of openSUSE's "games" repository). + +5. run 'rpmbuild -ba FlightGear.spec' and find the RPM + build result in ~/rpmbuild/RPMS + +That's all! + diff --git a/projects/VC100/README b/projects/VC100/README deleted file mode 100644 index aeca7db..0000000 --- a/projects/VC100/README +++ /dev/null @@ -1,2 +0,0 @@ -The handmade VS2010 project files have been replaced by the Cmake build system. -Please use Cmake to build FlightGear with Visual Studio 2010. diff --git a/projects/VC90/.gitignore b/projects/VC90/.gitignore deleted file mode 100644 index 6ecd66b..0000000 --- a/projects/VC90/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -FlightGear.ncb -FlightGear.suo -*.sln.cache -Win32 -x64 -*.user diff --git a/projects/VC90/FlightGear.sln b/projects/VC90/FlightGear.sln deleted file mode 100644 index d214ba2..0000000 --- a/projects/VC90/FlightGear.sln +++ /dev/null @@ -1,184 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fgadmin", "fgadmin\fgadmin.vcproj", "{7004E589-7EA0-4AFD-B432-3D5E00B55049}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fgjs", "fgjs\fgjs.vcproj", "{6749547A-6493-4754-8E0E-49FB3137C4CA}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fgviewer", "fgviewer\fgviewer.vcproj", "{0F13A557-EC52-481D-ADFB-9209C068FCEB}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FlightGear", "FlightGear\FlightGear.vcproj", "{49142EAF-B264-4B9F-B096-F669999EBB2E}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPSsmooth", "GPSsmooth\GPSsmooth.vcproj", "{AE9CE7E4-8F21-4C34-82DD-4D0371C210DA}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "metar", "metar\metar.vcproj", "{FC424099-5D77-4BC2-A93F-2EE59F816B51}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MIDGsmooth", "MIDGsmooth\MIDGsmooth.vcproj", "{92010FAB-17A3-4891-AE6D-507214FEA508}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "terrasync", "terrasync\terrasync.vcproj", "{874D3F55-6048-4068-A7C2-7FA6AF1F30EA}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UGsmooth", "UGsmooth\UGsmooth.vcproj", "{EEEEB798-BFFD-425F-86F9-03C6FE6B8B99}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xmlgrep", "xmlgrep\xmlgrep.vcproj", "{1F40CF41-9836-4488-BAAF-560623665C12}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yasim", "yasim\yasim.vcproj", "{800CB1FF-C398-4B81-B3D6-8BBD9E0897D2}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimGear", "..\..\..\Simgear\projects\VC90\SimGear.vcproj", "{22540CD3-D3CA-4C86-A773-80AEEE3ACDED}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fgpanel", "fgpanel\fgpanel.vcproj", "{FA27B353-179C-4DE8-B3AC-E260F8F790DD}" - ProjectSection(ProjectDependencies) = postProject - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} = {22540CD3-D3CA-4C86-A773-80AEEE3ACDED} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JS_demo", "JS_demo\JS_demo.vcproj", "{7E64A670-D297-468D-AF5D-315A02AA668F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7004E589-7EA0-4AFD-B432-3D5E00B55049}.Debug|Win32.ActiveCfg = Debug|Win32 - {7004E589-7EA0-4AFD-B432-3D5E00B55049}.Debug|Win32.Build.0 = Debug|Win32 - {7004E589-7EA0-4AFD-B432-3D5E00B55049}.Debug|x64.ActiveCfg = Debug|x64 - {7004E589-7EA0-4AFD-B432-3D5E00B55049}.Debug|x64.Build.0 = Debug|x64 - {7004E589-7EA0-4AFD-B432-3D5E00B55049}.Release|Win32.ActiveCfg = Release|Win32 - {7004E589-7EA0-4AFD-B432-3D5E00B55049}.Release|Win32.Build.0 = Release|Win32 - {7004E589-7EA0-4AFD-B432-3D5E00B55049}.Release|x64.ActiveCfg = Release|x64 - {7004E589-7EA0-4AFD-B432-3D5E00B55049}.Release|x64.Build.0 = Release|x64 - {6749547A-6493-4754-8E0E-49FB3137C4CA}.Debug|Win32.ActiveCfg = Debug|Win32 - {6749547A-6493-4754-8E0E-49FB3137C4CA}.Debug|Win32.Build.0 = Debug|Win32 - {6749547A-6493-4754-8E0E-49FB3137C4CA}.Debug|x64.ActiveCfg = Debug|x64 - {6749547A-6493-4754-8E0E-49FB3137C4CA}.Debug|x64.Build.0 = Debug|x64 - {6749547A-6493-4754-8E0E-49FB3137C4CA}.Release|Win32.ActiveCfg = Release|Win32 - {6749547A-6493-4754-8E0E-49FB3137C4CA}.Release|Win32.Build.0 = Release|Win32 - {6749547A-6493-4754-8E0E-49FB3137C4CA}.Release|x64.ActiveCfg = Release|x64 - {6749547A-6493-4754-8E0E-49FB3137C4CA}.Release|x64.Build.0 = Release|x64 - {0F13A557-EC52-481D-ADFB-9209C068FCEB}.Debug|Win32.ActiveCfg = Debug|Win32 - {0F13A557-EC52-481D-ADFB-9209C068FCEB}.Debug|Win32.Build.0 = Debug|Win32 - {0F13A557-EC52-481D-ADFB-9209C068FCEB}.Debug|x64.ActiveCfg = Debug|x64 - {0F13A557-EC52-481D-ADFB-9209C068FCEB}.Debug|x64.Build.0 = Debug|x64 - {0F13A557-EC52-481D-ADFB-9209C068FCEB}.Release|Win32.ActiveCfg = Release|Win32 - {0F13A557-EC52-481D-ADFB-9209C068FCEB}.Release|Win32.Build.0 = Release|Win32 - {0F13A557-EC52-481D-ADFB-9209C068FCEB}.Release|x64.ActiveCfg = Release|x64 - {0F13A557-EC52-481D-ADFB-9209C068FCEB}.Release|x64.Build.0 = Release|x64 - {49142EAF-B264-4B9F-B096-F669999EBB2E}.Debug|Win32.ActiveCfg = Debug|Win32 - {49142EAF-B264-4B9F-B096-F669999EBB2E}.Debug|Win32.Build.0 = Debug|Win32 - {49142EAF-B264-4B9F-B096-F669999EBB2E}.Debug|x64.ActiveCfg = Debug|x64 - {49142EAF-B264-4B9F-B096-F669999EBB2E}.Debug|x64.Build.0 = Debug|x64 - {49142EAF-B264-4B9F-B096-F669999EBB2E}.Release|Win32.ActiveCfg = Release|Win32 - {49142EAF-B264-4B9F-B096-F669999EBB2E}.Release|Win32.Build.0 = Release|Win32 - {49142EAF-B264-4B9F-B096-F669999EBB2E}.Release|x64.ActiveCfg = Release|x64 - {49142EAF-B264-4B9F-B096-F669999EBB2E}.Release|x64.Build.0 = Release|x64 - {AE9CE7E4-8F21-4C34-82DD-4D0371C210DA}.Debug|Win32.ActiveCfg = Debug|Win32 - {AE9CE7E4-8F21-4C34-82DD-4D0371C210DA}.Debug|Win32.Build.0 = Debug|Win32 - {AE9CE7E4-8F21-4C34-82DD-4D0371C210DA}.Debug|x64.ActiveCfg = Debug|x64 - {AE9CE7E4-8F21-4C34-82DD-4D0371C210DA}.Debug|x64.Build.0 = Debug|x64 - {AE9CE7E4-8F21-4C34-82DD-4D0371C210DA}.Release|Win32.ActiveCfg = Release|Win32 - {AE9CE7E4-8F21-4C34-82DD-4D0371C210DA}.Release|Win32.Build.0 = Release|Win32 - {AE9CE7E4-8F21-4C34-82DD-4D0371C210DA}.Release|x64.ActiveCfg = Release|x64 - {AE9CE7E4-8F21-4C34-82DD-4D0371C210DA}.Release|x64.Build.0 = Release|x64 - {FC424099-5D77-4BC2-A93F-2EE59F816B51}.Debug|Win32.ActiveCfg = Debug|Win32 - {FC424099-5D77-4BC2-A93F-2EE59F816B51}.Debug|Win32.Build.0 = Debug|Win32 - {FC424099-5D77-4BC2-A93F-2EE59F816B51}.Debug|x64.ActiveCfg = Debug|x64 - {FC424099-5D77-4BC2-A93F-2EE59F816B51}.Debug|x64.Build.0 = Debug|x64 - {FC424099-5D77-4BC2-A93F-2EE59F816B51}.Release|Win32.ActiveCfg = Release|Win32 - {FC424099-5D77-4BC2-A93F-2EE59F816B51}.Release|Win32.Build.0 = Release|Win32 - {FC424099-5D77-4BC2-A93F-2EE59F816B51}.Release|x64.ActiveCfg = Release|x64 - {FC424099-5D77-4BC2-A93F-2EE59F816B51}.Release|x64.Build.0 = Release|x64 - {92010FAB-17A3-4891-AE6D-507214FEA508}.Debug|Win32.ActiveCfg = Debug|Win32 - {92010FAB-17A3-4891-AE6D-507214FEA508}.Debug|Win32.Build.0 = Debug|Win32 - {92010FAB-17A3-4891-AE6D-507214FEA508}.Debug|x64.ActiveCfg = Debug|x64 - {92010FAB-17A3-4891-AE6D-507214FEA508}.Debug|x64.Build.0 = Debug|x64 - {92010FAB-17A3-4891-AE6D-507214FEA508}.Release|Win32.ActiveCfg = Release|Win32 - {92010FAB-17A3-4891-AE6D-507214FEA508}.Release|Win32.Build.0 = Release|Win32 - {92010FAB-17A3-4891-AE6D-507214FEA508}.Release|x64.ActiveCfg = Release|x64 - {92010FAB-17A3-4891-AE6D-507214FEA508}.Release|x64.Build.0 = Release|x64 - {874D3F55-6048-4068-A7C2-7FA6AF1F30EA}.Debug|Win32.ActiveCfg = Debug|Win32 - {874D3F55-6048-4068-A7C2-7FA6AF1F30EA}.Debug|Win32.Build.0 = Debug|Win32 - {874D3F55-6048-4068-A7C2-7FA6AF1F30EA}.Debug|x64.ActiveCfg = Debug|x64 - {874D3F55-6048-4068-A7C2-7FA6AF1F30EA}.Release|Win32.ActiveCfg = Release|Win32 - {874D3F55-6048-4068-A7C2-7FA6AF1F30EA}.Release|Win32.Build.0 = Release|Win32 - {874D3F55-6048-4068-A7C2-7FA6AF1F30EA}.Release|x64.ActiveCfg = Release|x64 - {EEEEB798-BFFD-425F-86F9-03C6FE6B8B99}.Debug|Win32.ActiveCfg = Debug|Win32 - {EEEEB798-BFFD-425F-86F9-03C6FE6B8B99}.Debug|Win32.Build.0 = Debug|Win32 - {EEEEB798-BFFD-425F-86F9-03C6FE6B8B99}.Debug|x64.ActiveCfg = Debug|x64 - {EEEEB798-BFFD-425F-86F9-03C6FE6B8B99}.Debug|x64.Build.0 = Debug|x64 - {EEEEB798-BFFD-425F-86F9-03C6FE6B8B99}.Release|Win32.ActiveCfg = Release|Win32 - {EEEEB798-BFFD-425F-86F9-03C6FE6B8B99}.Release|Win32.Build.0 = Release|Win32 - {EEEEB798-BFFD-425F-86F9-03C6FE6B8B99}.Release|x64.ActiveCfg = Release|x64 - {EEEEB798-BFFD-425F-86F9-03C6FE6B8B99}.Release|x64.Build.0 = Release|x64 - {1F40CF41-9836-4488-BAAF-560623665C12}.Debug|Win32.ActiveCfg = Debug|Win32 - {1F40CF41-9836-4488-BAAF-560623665C12}.Debug|Win32.Build.0 = Debug|Win32 - {1F40CF41-9836-4488-BAAF-560623665C12}.Debug|x64.ActiveCfg = Debug|x64 - {1F40CF41-9836-4488-BAAF-560623665C12}.Debug|x64.Build.0 = Debug|x64 - {1F40CF41-9836-4488-BAAF-560623665C12}.Release|Win32.ActiveCfg = Release|Win32 - {1F40CF41-9836-4488-BAAF-560623665C12}.Release|Win32.Build.0 = Release|Win32 - {1F40CF41-9836-4488-BAAF-560623665C12}.Release|x64.ActiveCfg = Release|x64 - {1F40CF41-9836-4488-BAAF-560623665C12}.Release|x64.Build.0 = Release|x64 - {800CB1FF-C398-4B81-B3D6-8BBD9E0897D2}.Debug|Win32.ActiveCfg = Debug|Win32 - {800CB1FF-C398-4B81-B3D6-8BBD9E0897D2}.Debug|Win32.Build.0 = Debug|Win32 - {800CB1FF-C398-4B81-B3D6-8BBD9E0897D2}.Debug|x64.ActiveCfg = Debug|x64 - {800CB1FF-C398-4B81-B3D6-8BBD9E0897D2}.Debug|x64.Build.0 = Debug|x64 - {800CB1FF-C398-4B81-B3D6-8BBD9E0897D2}.Release|Win32.ActiveCfg = Release|Win32 - {800CB1FF-C398-4B81-B3D6-8BBD9E0897D2}.Release|Win32.Build.0 = Release|Win32 - {800CB1FF-C398-4B81-B3D6-8BBD9E0897D2}.Release|x64.ActiveCfg = Release|x64 - {800CB1FF-C398-4B81-B3D6-8BBD9E0897D2}.Release|x64.Build.0 = Release|x64 - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED}.Debug|Win32.ActiveCfg = Debug|Win32 - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED}.Debug|Win32.Build.0 = Debug|Win32 - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED}.Debug|x64.ActiveCfg = Debug|x64 - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED}.Debug|x64.Build.0 = Debug|x64 - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED}.Release|Win32.ActiveCfg = Release|Win32 - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED}.Release|Win32.Build.0 = Release|Win32 - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED}.Release|x64.ActiveCfg = Release|x64 - {22540CD3-D3CA-4C86-A773-80AEEE3ACDED}.Release|x64.Build.0 = Release|x64 - {FA27B353-179C-4DE8-B3AC-E260F8F790DD}.Debug|Win32.ActiveCfg = Debug|Win32 - {FA27B353-179C-4DE8-B3AC-E260F8F790DD}.Debug|Win32.Build.0 = Debug|Win32 - {FA27B353-179C-4DE8-B3AC-E260F8F790DD}.Debug|x64.ActiveCfg = Debug|x64 - {FA27B353-179C-4DE8-B3AC-E260F8F790DD}.Release|Win32.ActiveCfg = Release|Win32 - {FA27B353-179C-4DE8-B3AC-E260F8F790DD}.Release|Win32.Build.0 = Release|Win32 - {FA27B353-179C-4DE8-B3AC-E260F8F790DD}.Release|x64.ActiveCfg = Release|x64 - {7E64A670-D297-468D-AF5D-315A02AA668F}.Debug|Win32.ActiveCfg = Debug|Win32 - {7E64A670-D297-468D-AF5D-315A02AA668F}.Debug|Win32.Build.0 = Debug|Win32 - {7E64A670-D297-468D-AF5D-315A02AA668F}.Debug|x64.ActiveCfg = Debug|x64 - {7E64A670-D297-468D-AF5D-315A02AA668F}.Debug|x64.Build.0 = Debug|x64 - {7E64A670-D297-468D-AF5D-315A02AA668F}.Release|Win32.ActiveCfg = Release|Win32 - {7E64A670-D297-468D-AF5D-315A02AA668F}.Release|Win32.Build.0 = Release|Win32 - {7E64A670-D297-468D-AF5D-315A02AA668F}.Release|x64.ActiveCfg = Release|x64 - {7E64A670-D297-468D-AF5D-315A02AA668F}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/projects/VC90/FlightGear/.gitignore b/projects/VC90/FlightGear/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/FlightGear/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/FlightGear/FlightGear.vcproj b/projects/VC90/FlightGear/FlightGear.vcproj deleted file mode 100644 index fd2548b..0000000 --- a/projects/VC90/FlightGear/FlightGear.vcproj +++ /dev/null @@ -1,4584 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/GPSsmooth/.gitignore b/projects/VC90/GPSsmooth/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/GPSsmooth/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/GPSsmooth/GPSsmooth.vcproj b/projects/VC90/GPSsmooth/GPSsmooth.vcproj deleted file mode 100644 index d804c8d..0000000 --- a/projects/VC90/GPSsmooth/GPSsmooth.vcproj +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/JS_demo/JS_demo.vcproj b/projects/VC90/JS_demo/JS_demo.vcproj deleted file mode 100644 index 62c4fb0..0000000 --- a/projects/VC90/JS_demo/JS_demo.vcproj +++ /dev/null @@ -1,427 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/MIDGsmooth/.gitignore b/projects/VC90/MIDGsmooth/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/MIDGsmooth/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/MIDGsmooth/MIDGsmooth.vcproj b/projects/VC90/MIDGsmooth/MIDGsmooth.vcproj deleted file mode 100644 index bb7ac97..0000000 --- a/projects/VC90/MIDGsmooth/MIDGsmooth.vcproj +++ /dev/null @@ -1,376 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/UGsmooth/.gitignore b/projects/VC90/UGsmooth/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/UGsmooth/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/UGsmooth/UGsmooth.vcproj b/projects/VC90/UGsmooth/UGsmooth.vcproj deleted file mode 100644 index b334538..0000000 --- a/projects/VC90/UGsmooth/UGsmooth.vcproj +++ /dev/null @@ -1,396 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/fgadmin.rc b/projects/VC90/fgadmin.rc deleted file mode 100755 index a25aa78..0000000 --- a/projects/VC90/fgadmin.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON "flightgear.ico" diff --git a/projects/VC90/fgadmin/.gitignore b/projects/VC90/fgadmin/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/fgadmin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/fgadmin/fgadmin.vcproj b/projects/VC90/fgadmin/fgadmin.vcproj deleted file mode 100644 index 0cc986c..0000000 --- a/projects/VC90/fgadmin/fgadmin.vcproj +++ /dev/null @@ -1,402 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/fgjs/.gitignore b/projects/VC90/fgjs/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/fgjs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/fgjs/fgjs.vcproj b/projects/VC90/fgjs/fgjs.vcproj deleted file mode 100644 index 4b65db5..0000000 --- a/projects/VC90/fgjs/fgjs.vcproj +++ /dev/null @@ -1,385 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/fgpanel/.gitignore b/projects/VC90/fgpanel/.gitignore deleted file mode 100755 index 8a9d35c..0000000 --- a/projects/VC90/fgpanel/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/fgpanel/fgpanel.vcproj b/projects/VC90/fgpanel/fgpanel.vcproj deleted file mode 100755 index ffa22c3..0000000 --- a/projects/VC90/fgpanel/fgpanel.vcproj +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/fgviewer/.gitignore b/projects/VC90/fgviewer/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/fgviewer/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/fgviewer/fgviewer.vcproj b/projects/VC90/fgviewer/fgviewer.vcproj deleted file mode 100644 index a4a71b7..0000000 --- a/projects/VC90/fgviewer/fgviewer.vcproj +++ /dev/null @@ -1,368 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/flightgear.ico b/projects/VC90/flightgear.ico deleted file mode 100644 index e041319..0000000 Binary files a/projects/VC90/flightgear.ico and /dev/null differ diff --git a/projects/VC90/flightgear.rc b/projects/VC90/flightgear.rc deleted file mode 100644 index 32cb5ae..0000000 --- a/projects/VC90/flightgear.rc +++ /dev/null @@ -1 +0,0 @@ -FLIGHTGEAR ICON "flightgear.ico" diff --git a/projects/VC90/flightgear64.ico b/projects/VC90/flightgear64.ico deleted file mode 100644 index 4f26205..0000000 Binary files a/projects/VC90/flightgear64.ico and /dev/null differ diff --git a/projects/VC90/flightgear64.rc b/projects/VC90/flightgear64.rc deleted file mode 100644 index 0a929eb..0000000 --- a/projects/VC90/flightgear64.rc +++ /dev/null @@ -1 +0,0 @@ -FLIGHTGEAR ICON "flightgear64.ico" diff --git a/projects/VC90/metar/.gitignore b/projects/VC90/metar/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/metar/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/metar/metar.vcproj b/projects/VC90/metar/metar.vcproj deleted file mode 100644 index 85e2c19..0000000 --- a/projects/VC90/metar/metar.vcproj +++ /dev/null @@ -1,364 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/terrasync/.gitignore b/projects/VC90/terrasync/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/terrasync/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/terrasync/terrasync.vcproj b/projects/VC90/terrasync/terrasync.vcproj deleted file mode 100644 index f14254a..0000000 --- a/projects/VC90/terrasync/terrasync.vcproj +++ /dev/null @@ -1,368 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/xmlgrep/.gitignore b/projects/VC90/xmlgrep/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/xmlgrep/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/xmlgrep/xmlgrep.vcproj b/projects/VC90/xmlgrep/xmlgrep.vcproj deleted file mode 100644 index 97007ac..0000000 --- a/projects/VC90/xmlgrep/xmlgrep.vcproj +++ /dev/null @@ -1,369 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/VC90/yasim/.gitignore b/projects/VC90/yasim/.gitignore deleted file mode 100644 index 8a9d35c..0000000 --- a/projects/VC90/yasim/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.user diff --git a/projects/VC90/yasim/yasim.vcproj b/projects/VC90/yasim/yasim.vcproj deleted file mode 100644 index 6cd6689..0000000 --- a/projects/VC90/yasim/yasim.vcproj +++ /dev/null @@ -1,576 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/atis/.gitignore b/scripts/atis/.gitignore new file mode 100644 index 0000000..4a675de --- /dev/null +++ b/scripts/atis/.gitignore @@ -0,0 +1,4 @@ +*.vlist +*.vce +*.wav.gz +snip/* diff --git a/scripts/atis/README b/scripts/atis/README new file mode 100644 index 0000000..e5d484b --- /dev/null +++ b/scripts/atis/README @@ -0,0 +1,50 @@ +The ATIS Voice Generation HowTo +------------------------------- + +Required packages / installation hints by J. Denker +--------------------------------------------------- + cpan Audio::Wav + apt-get install festival mbrola sox festlex-oald + cd \$tars + wget http://tcts.fpms.ac.be/synthesis/mbrola/dba/en1/en1-980910.zip + wget http://www.cstr.ed.ac.uk/downloads/festival/1.95/festvox_en1.tar.gz + cd /usr/share/festival/voices/english + mkdir en1_mbrola + cd en1_mbrola + unzip \$tars/en1-980910.zip + cd /usr/share/festival + mkdir lib + cd lib + ln -s ../voices ./ + cd /usr/share + tar -xpzvf \$tars/festvox_en1.tar.gz + +Generating Voice Files +---------------------- + + 1. Configure paths to fgdata and flightgear sources + export FG_ROOT=/home/whatever/fgdata + export FG_SRC=/home/whatever/flightgear + + 2. Create phraseology word list + ./atis-lex.pl > phraseology.vlist + + 3. Create airport word list + export ATIS_ONLY=yes + ./list-airports.pl | ./words_per_line.sh > airports.vlist + + 4. Check for and fix non-UTF8 encoded airport names + ./find_nonUTF8.pl + + 5. Generate phraseology voice file + ./synth.pl phraseology.vlist phraseology.vce phraseology.wav + + 6. Generate airport voice file + ./synth.pl airports.vlist airports.vce airports.wav + + 7. Install *.vce and *.wav.gz files in fgdata: + cp phraseology.vce $(FG_ROOT)/ATC/voices/default/. + cp phraseology.wav.gz $(FG_ROOT)/ATC/voices/default/. + cp airports.vce $(FG_ROOT)/ATC/voices/default/. + cp airports.wav.gz $(FG_ROOT)/ATC/voices/default/. + diff --git a/scripts/atis/atis-lex.pl b/scripts/atis/atis-lex.pl new file mode 100755 index 0000000..701f1c6 --- /dev/null +++ b/scripts/atis/atis-lex.pl @@ -0,0 +1,80 @@ +#! /usr/bin/perl -w + +sub usage { + print < phraseology.vlist +EoF +} + +use strict; +use Symbol; + + my $fgroot = $ENV{'FG_ROOT'} || '.'; + +main: { + if (@ARGV) { + usage; + exit; + } + my $mapfn = "$ENV{'FG_SRC'}/src/ATCDCL/atis_lexicon.hxx"; + my $mapch = Symbol::gensym; + if (!open($mapch, '<', $mapfn)) { + print STDERR "Could not open abbreviation file '$mapfn'\n"; + print STDERR "Maybe you need to set FG_ROOT\n"; + exit(1); + } + print "/\n"; + while (my $line = <$mapch>) { + chomp $line; + if ($line =~ s/^[ \t]*Q[(]//) { + $line =~ s/[)][ \t]*$//; + print "$line\n"; + } + } + print <\n"; + +while( chomp($content = ) ) { + $length = length($content); + + for( $i = 0; $i < $length; $i++ ) { + + if( ord(substr($content, $i, 1)) > 127 ) + { + print "$content\n"; + last; + } + } +} +close(FILE); + +exit 0 diff --git a/scripts/atis/list-airports.pl b/scripts/atis/list-airports.pl new file mode 100755 index 0000000..347b928 --- /dev/null +++ b/scripts/atis/list-airports.pl @@ -0,0 +1,155 @@ +#! /usr/bin/perl -w + +sub usage { + print < airports.vlist +EoF +} + +use strict; +use Symbol; + + my $noparen = 1; + my $verbose = 0; + my $apt_name = ''; + my $lat; + my $lon; + my $atis; + my $country = ''; + my $elev; + my $tower; + my $bldgs; + my $apt_id; + my $shapefile; + my $namer = 'NAME'; + my $skipping = 0; + my $tot_apts = 0; + + my %states = (); + my %short_country = (); + + + my $fgroot = $ENV{'FG_ROOT'} || '.'; + my $atis_only = $ENV{'ATIS_ONLY'} || 0; + my $mapfn = "$ENV{'FG_SRC'}/src/ATCDCL/atis_remap.hxx"; + +sub process_apt { + if ($atis_only && ! $atis) { + return; + } + my $str .= $apt_name; + + $str =~ s' *$''; ## remove trailing spaces + if ($noparen) { + $str =~ s/[(][^)]*[)]?//g; + } + print "$str\n"; + $tot_apts++; +} + +my %remap = (); + +sub get_remap { + +# Note: in this context, GKI probably stands for Gereja Kristen Indonesia +# I guess the church builds lots of airports. + + my $mapch = Symbol::gensym; + if (!open($mapch, '<', $mapfn)) { + print STDERR "Could not open abbreviation file '$mapfn'\n"; + print STDERR "Maybe you need to set FG_ROOT\n"; + exit(1); + } + while (my $line = <$mapch>) { + chomp $line; + if ($line =~ s/[ \t]*REMAP[(]//) { + $line =~ s/[)][ \t]*$//; + my @stuff = split(',', $line, 2); + my $from = $stuff[0]; + my $to = $stuff[1]; + $to =~ s/^[ \t]*//; + if ($to eq 'NIL') { + $to = ''; + } + $remap{$from} = $to; + } + } +} + +main: { + if (@ARGV) { + usage; + exit; + } + + get_remap; + + my $delim = '-'; + my $fgroot = $ENV{'FG_ROOT'} || 0; + my $incmd = "zcat $fgroot/Airports/apt.dat.gz"; + my $inch = Symbol::gensym; + open ($inch, '-|', $incmd) + || die "Couldn't open pipe from '$incmd'\n"; + + + my $junk = <$inch>; + $junk = <$inch>; + liner: while (my $line = <$inch>) { + chomp $line; + my @stuff = split(' ', $line); + my $type = shift @stuff || 0; +### print "..$type ... $line ...\n"; + + if ($type == 1) { +## Here if new airport. +## +## First, print results of previous work, i.e. airport +## stanzas already seen ... since the apt.dat file +## doesn't have a clear way of signaling the end of a stanza. + if ($apt_name) { + process_apt(); + } + $apt_name = ''; + $atis = ''; + $lat = 0; + $lon = 0; + $country = ''; + + $elev = shift @stuff; + $tower = shift @stuff; + $bldgs = shift @stuff; + $apt_id = shift @stuff; + my $name = join $delim, @stuff; + + for my $from (keys %remap) { + my $to = $remap{$from}; + $name =~ s/\b$from\b/$to/gi; + } + +## option for plain words, not hyphenated phrases + if (1) { + $name =~ s/$delim/ /g; + } + + $apt_name = "$name"; + } + + if ($type == 10) { + $lat = $stuff[0]; + $lon = $stuff[1]; + } + + if ($type == 50) { + $atis = join(' ', @stuff); + } + } + process_apt(); ## flush out the very last one + print STDERR "Total airports: $tot_apts\n"; +} diff --git a/scripts/atis/quiet0.500.wav b/scripts/atis/quiet0.500.wav new file mode 100644 index 0000000..bceb4e6 Binary files /dev/null and b/scripts/atis/quiet0.500.wav differ diff --git a/scripts/atis/synth.pl b/scripts/atis/synth.pl new file mode 100755 index 0000000..86cbf67 --- /dev/null +++ b/scripts/atis/synth.pl @@ -0,0 +1,349 @@ +#! /usr/bin/perl -w + +use strict; +use Symbol; +use Audio::Wav; + +sub usage { + print < 'windh', + 'romeo' => 'Rome E O', + 'xray' => 'X ray', +); + +main: { + + my $skip = 0; + my $fmtcheck = 1; + my $oneword = 0; + my $gripe = 0; + my $out_bits_sample = 8; ## this is what FGFS expects + my @plain_args = (); + argx: while (@ARGV) { + my $arg = shift @ARGV; + $arg =~ s/^--/-/; + if ($arg eq '-help' || $arg eq '-h') { + usage; + exit(0); + } + if ($arg eq '-dump') { + my $ifn = shift @ARGV || 'foo.wav'; + my $wav = new Audio::Wav; + print "About to open '$ifn' ...\n"; + my $waver = $wav -> read( $ifn ); + print "xxxxxxxxxxxxxxxx\n"; + for my $detail (keys %{$waver->details()}) { + printf("%-20s %s\n", $detail, ${$waver->details()}{$detail}); + } + exit(0); + } + if ($arg eq '-skip') { + $skip++; + next argx; + } + if ($arg eq '-gripe') { + $gripe++; + next argx; + } + if ($arg eq '-one' || $arg eq '-1') { + $oneword++; + next argx; + } + if ($arg eq '-nocheck') { + $fmtcheck=0; + next argx; + } + if ($arg =~ '^-') { + die "Unrecognized option '$arg'\n"; + } + push @plain_args, $arg; + } + + my $nargs = @plain_args; + if ($nargs != 3) { + die "Wrong number of arguments ($nargs); for help try:\n $0 -help\n"; + } + my @todo = (); + my ($ifn, $indexfn, $out_wav) = @plain_args; + + my $inch = Symbol::gensym; + open ($inch, '<', $ifn) + || die "Couldn't open input file '$ifn'\n"; + +# Skip some lines from the input list, as requested: + for (my $ii = 0; $ii < $skip; $ii++) { + my $ignore = <$inch>; + } + +# Read the rest of the input file: + while (my $line = <$inch>) { + chomp($line); + if ($oneword) { + my @stuff = split(' ', $line, 2); + doit($stuff[0]); + } else { + for my $word (split(' ', $line)) { + doit($word); + } + } + } + close $inch; + +# Optionally print a list of things that the input file +# requested more than once. + if ($gripe) { + foreach my $thing (sort keys %count) { + if ($count{$thing} > 1) { + printf("%4d %s\n", $count{$thing}, $list{$thing}); + } + } + } + my $nsnip = (keys %list); + + if (0 && $nsnip > 10) { + $nsnip = 10; + } + print STDERR "nsnip: $nsnip\n"; + + my $index = Symbol::gensym; + open ($index, '>', $indexfn) + || die "Couldn't write index file '$indexfn'\n"; + + print $index "$nsnip\n"; + if (! -d 'snip') { + mkdir('snip') + || die "Could not create directory 'snip' : $!\n"; + } + + my $wav = new Audio::Wav; + my $waver = $wav -> read("quiet0.500.wav"); + my $sample_rate = -1; + my $channels = -1; + my $bits_sample = -1; + $sample_rate = ${$waver->details()}{'sample_rate'}; + $channels = ${$waver->details()}{'channels'}; + $bits_sample = ${$waver->details()}{'bits_sample'}; + +############## system "/bin/cp nothing.wav t1.wav"; + my $where = 0; + my $ii = 0; + + snipper: for my $thing (sort keys %list) { + $ii++; + my $iix = sprintf('%05d', $ii); + my $xfn = "./snip/x$iix"; + print( "$xfn\n"); + + my $fraise = lc($thing); + if (exists $fixup{$fraise}) { + #xxxx print "fixing $fraise\n"; + $fraise = $fixup{$fraise}; + } + +## This turns dashes and other funny stuff into spaces +## in the phrase to be processed: + $fraise =~ s%[^a-z']+% %gi; + if ($thing eq '/' || $thing eq '/_') { + system("/bin/cp quiet0.500.wav $xfn.wav"); + } else { + my $script = $proto; + $script =~ s/XXX/$fraise/; + $script =~ s|YYY|$xfn.wav|; + #xxxx print "$fraise ... $script\n"; + + my $cmd = '/usr/bin/festival'; + my $pipe = Symbol::gensym; + open ($pipe, '|-', $cmd) + || die "Couldn't open pipe to '$cmd'\n"; + print $pipe $script; + close $pipe; + if ($? != 0){ + print STDERR "Error in festival script: '$script'\n"; + next snipper; + } + } + } + + $ii = 0; + snipper: for my $thing (sort keys %list) { + $ii++; + my $iix = sprintf('%05d', $ii); + my $xfn = "./snip/x$iix"; + + if ($fmtcheck == 1) { + my $wav = new Audio::Wav; + my $waver = $wav -> read("$xfn.wav"); + if ($sample_rate < 0) { + $sample_rate = ${$waver->details()}{'sample_rate'}; + $channels = ${$waver->details()}{'channels'}; + $bits_sample = ${$waver->details()}{'bits_sample'}; + } else { + $sample_rate == ${$waver->details()}{'sample_rate'} + && $channels == ${$waver->details()}{'channels'} + && $bits_sample == ${$waver->details()}{'bits_sample'} + || die "audio format not the same: $xfn.wav"; + } + } + + my $statcmd = "2>&1 sox $xfn.wav -n stat"; + my $stat = Symbol::gensym; + open ($stat, '-|', $statcmd) + || die "Couldn't open pipe from '$statcmd'\n"; + my $vol = 0; + my $size = 0; + + my $lastline; + while (my $line = <$stat>) { + chomp $line; + $lastline = $line; + my @stuff = split ':', $line; + my $nw = @stuff; + #### print STDERR "$nw +++ $line\n"; + if ($nw == 2) { + if ($stuff[0] eq 'Volume adjustment') { + $vol = 0+$stuff[1]; + } + elsif ($stuff[0] eq 'Samples read') { + $size = 0+$stuff[1]; + } + } + } + my $status = close $stat; + if ($?) { + print STDERR "Stat command failed: $statcmd\n" . ": $lastline \n"; + next snipper; + } + if ($size == 0) { + print STDERR "?Warning! Zero-size audio file for $iix '$thing'\n"; + } + + if ($vol > 20) { + ## unreasonable volume, happens with 'silent' files + $vol = 0; + } + printf("%s %6.3f %6d '%s'\n", $iix, $vol, $size, $thing); + my $subsize = int($size/2); + printf $index ("%-45s %10d %10d\n", $thing, $where, $subsize); + $where += $subsize; + + my $volume_cmd = sprintf("sox -v %6.3f %s.wav %s.raw", + $vol*0.9, $xfn, $xfn); + ########## print "+ $volume_cmd\n"; + if (1) { + my $vol_handle = Symbol::gensym; + open ($vol_handle, '|-', $volume_cmd) + || die "Couldn't open pipe to command '$volume_cmd'\n"; + + close $vol_handle; + if ($?) { + die "Volume command failed: $statcmd\n" . ": $lastline"; + } + } + push @todo, "$xfn.raw"; + } + close $index; + + my $cat_cmd = "cat " . join(' ', @todo) . " > ./snip/everything.raw"; + my $cat_handle = Symbol::gensym; + open ($cat_handle, '|-', $cat_cmd) + || die "Couldn't open pipe to command '$cat_cmd'\n"; + close $cat_handle; + if ($?) { + die "Cat command failed: $cat_cmd"; + } + + ## Convert RAW to WAVE format + my $wav_cmd = "sox --rate $sample_rate --bits $bits_sample" + . " --encoding signed-integer" + . " ./snip/everything.raw --rate 8000 --bits $out_bits_sample $out_wav"; + + my $wav_handle = Symbol::gensym; + open ($wav_handle, '|-', $wav_cmd) + || die "Couldn't open pipe to command '$wav_cmd'\n"; + close $wav_handle; + if ($?) { + die ".wav command failed: $wav_cmd"; + } + + ## Compress WAVE file + my $gz_cmd = "gzip -f $out_wav"; + my $gz_handle = Symbol::gensym; + open ($gz_handle, '|-', $gz_cmd) + || die "Couldn't open pipe to command '$gz_cmd'\n"; + close $gz_handle; + system("rm snip/*; rmdir snip"); +} diff --git a/scripts/atis/voice.pl b/scripts/atis/voice.pl new file mode 100755 index 0000000..f90c2f6 --- /dev/null +++ b/scripts/atis/voice.pl @@ -0,0 +1,102 @@ +#! /usr/bin/perl -w + +use strict; +use Symbol; + +sub usage { + print <> tmp.raw "; + print "$cmd\n"; + system $cmd; + my $end = $start{$arg} + $len{$arg}; + print "$start{$arg} + $len{$arg} = $end\n"; + } else { + print "Can't find '$arg'\n"; + } +} + + +sub setup{ + my $inch = Symbol::gensym(); + my $file = "$dir/voice.vce"; + open($inch, "<$file") || die "Cannot open input file '$file'\n"; + my $header = <$inch>; + chomp $header; + my $ii=1; + liner: while (my $line = <$inch>){ + chomp $line; + my @word = split(" ", $line); + my $nn = @word; + if ($nn != 3) { + next liner; + } + my $id = lc($word[0]); + my $st = $word[1]; + my $ln = $word[2]; + if ($ln =~ s/^x//) { + $ln = $ln - $st; + print "$id $st $ln\n"; + } + $start{$id} = $st; + $len{$id} = $ln; + ##print "$ii $nn '$line'\n"; + $ii++; + } + print "(($header)) --> $ii\n"; +} \ No newline at end of file diff --git a/scripts/atis/words_per_line.sh b/scripts/atis/words_per_line.sh new file mode 100755 index 0000000..ce18d89 --- /dev/null +++ b/scripts/atis/words_per_line.sh @@ -0,0 +1,43 @@ +#! /bin/bash + +## +## + +if test "x$1" = "x-h" ; then + 1>&2 echo "Usage: " + 1>&2 echo " $0 [filename]" + 1>&2 echo "Read words from input, treating all whitespace like," + 1>&2 echo "and write exactly N words per line on output." + 1>&2 echo "Options: " + 1>&2 echo " -n [N] specify N (default: 1)" + 1>&2 echo " filename = '-' or '' ==> read from standard input" + exit 1 +fi + +: ${wordmax:=1} +files="" + + +while test -n "$*" ; do + this=$1 ; shift + case $this in + -n) wordmax=$1 ; shift + ;; + *) files="$files $this" + ;; + esac +done + + +awk '{ + for (ii = 1; ii <=NF; ii++) { + printf ("%s", $ii); + words++; + if (words >= wordmax) { + print ""; + words = 0; + } else { + printf (" "); + } + } +}' wordmax=$wordmax $files diff --git a/scripts/java/FGClient/src/org/flightgear/fgfsclient/Util._java_ b/scripts/java/FGClient/src/org/flightgear/fgfsclient/Util._java_ new file mode 100644 index 0000000..684b762 --- /dev/null +++ b/scripts/java/FGClient/src/org/flightgear/fgfsclient/Util._java_ @@ -0,0 +1,70 @@ +package org.flightgear.fgfsclient; + +import java.io.IOException; + +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; + + +public final class Util +{ + + public static void forceConnection (JFrame frame, final FGFSConnection fgfs) + { + while (!fgfs.isConnected()) { + final JDialog dialog = + new JDialog(frame, "Connect to FlightGear", true); + dialog.getContentPane().setLayout(new GridLayout(3, 2)); + dialog.getContentPane().add(new JLabel("Host:")); + + final JTextField hostField = new JTextField(20); + hostField.setText(fgfs.getHost()); + dialog.getContentPane().add(hostField); + + dialog.getContentPane().add(new JLabel("Port:")); + + final JTextField portField = new JTextField(5); + portField.setText(Integer.toString(fgfs.getPort())); + dialog.getContentPane().add(portField); + + JButton connectButton = new JButton("Connect"); + connectButton.addActionListener(new ActionListener() { + public void actionPerformed (ActionEvent ev) + { + try { + fgfs.open(hostField.getText(), + Integer.parseInt(portField.getText())); + } catch (IOException ex) { + JOptionPane.showMessageDialog(dialog, ex.getMessage(), + "Alert", JOptionPane.ERROR_MESSAGE); + } + dialog.hide(); + } + }); + dialog.getContentPane().add(connectButton); + dialog.getRootPane().setDefaultButton(connectButton); + + JButton quitButton = new JButton("Quit"); + quitButton.addActionListener(new ActionListener() { + public void actionPerformed (ActionEvent ev) + { + System.exit(0); // FIXME + } + }); + dialog.getContentPane().add(quitButton); + + dialog.pack(); + dialog.show(); + + } + } + +} diff --git a/scripts/java/FGClient/src/org/flightgear/fgfsclient/Util.java b/scripts/java/FGClient/src/org/flightgear/fgfsclient/Util.java deleted file mode 100644 index 684b762..0000000 --- a/scripts/java/FGClient/src/org/flightgear/fgfsclient/Util.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.flightgear.fgfsclient; - -import java.io.IOException; - -import java.awt.GridLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JTextField; - - -public final class Util -{ - - public static void forceConnection (JFrame frame, final FGFSConnection fgfs) - { - while (!fgfs.isConnected()) { - final JDialog dialog = - new JDialog(frame, "Connect to FlightGear", true); - dialog.getContentPane().setLayout(new GridLayout(3, 2)); - dialog.getContentPane().add(new JLabel("Host:")); - - final JTextField hostField = new JTextField(20); - hostField.setText(fgfs.getHost()); - dialog.getContentPane().add(hostField); - - dialog.getContentPane().add(new JLabel("Port:")); - - final JTextField portField = new JTextField(5); - portField.setText(Integer.toString(fgfs.getPort())); - dialog.getContentPane().add(portField); - - JButton connectButton = new JButton("Connect"); - connectButton.addActionListener(new ActionListener() { - public void actionPerformed (ActionEvent ev) - { - try { - fgfs.open(hostField.getText(), - Integer.parseInt(portField.getText())); - } catch (IOException ex) { - JOptionPane.showMessageDialog(dialog, ex.getMessage(), - "Alert", JOptionPane.ERROR_MESSAGE); - } - dialog.hide(); - } - }); - dialog.getContentPane().add(connectButton); - dialog.getRootPane().setDefaultButton(connectButton); - - JButton quitButton = new JButton("Quit"); - quitButton.addActionListener(new ActionListener() { - public void actionPerformed (ActionEvent ev) - { - System.exit(0); // FIXME - } - }); - dialog.getContentPane().add(quitButton); - - dialog.pack(); - dialog.show(); - - } - } - -} diff --git a/scripts/python/FlightGear.pyc b/scripts/python/FlightGear.pyc deleted file mode 100644 index c58cba9..0000000 Binary files a/scripts/python/FlightGear.pyc and /dev/null differ diff --git a/scripts/python/nasal_api_doc.py b/scripts/python/nasal_api_doc.py new file mode 100755 index 0000000..9c27310 --- /dev/null +++ b/scripts/python/nasal_api_doc.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2012 Adrian Musceac +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +import os, sys, glob +import io +import re, string + +"""Script which generates an API documentation file for Nasal libraries +located inside $FGROOT/Nasal/ +Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/] +Or configure the local path below, and ommit the path in the console. +The API doc in HTML format is generated in the current working directory""" + +########### Local $FGROOT/Nasal/ path ########## +NASAL_PATH="../fgfs/fgdata/Nasal/" + + +def get_files(nasal_dir): + if nasal_dir[-1]!='/': + nasal_dir+='/' + try: + os.stat(nasal_dir) + except: + print "The path does not exist" + sys.exit() + fgroot_dir = nasal_dir.rstrip('/').replace('Nasal','') + f_version = open(fgroot_dir+'version','rb') + version = f_version.read(256).rstrip('\n') + top_level = [] + modules = [] + top_namespaces = [] + files_list = os.listdir(nasal_dir) + for f in files_list: + if f.find(".nas")!=-1: + top_level.append(f) + continue + if os.path.isdir(nasal_dir + f): + modules.append(f) + top_level.sort() + modules.sort() + if len(top_level) ==0: + print "This does not look like the correct $FGROOT/Nasal path" + sys.exit() + if len(modules)==0: + print "Warning: could not find any submodules" + for f in top_level: + namespace=f.replace(".nas","") + functions=parse_file(nasal_dir + f) + top_namespaces.append([namespace,functions]) + for m in modules: + files=glob.glob(nasal_dir+m+"/*.nas") + for f in files: + functions=parse_file(f) + top_namespaces.append([m,functions]) + + output_text(top_namespaces,modules,version) + + +def output_text(top_namespaces,modules,version): + fw=open('./nasal_api_doc.html','wb') + buf='\ + Nasal API\ + \n\ + ' + + buf+='

\ + Nasal $FGROOT Library
Flightgear version: '+version+'\ +
This file is generated automatically by scripts/python/nasal_api_doc.py\ +

\ +
Nasal documentation  \ + Flightgear Nasal documentation\n
 ' + buf+='

\n' + done=[] + for namespace in top_namespaces: + color='0000cc' + if namespace[0] in modules: + color='cc0000' + if namespace[0] not in done: + buf+=''+namespace[0]+' 
\n' + done.append(namespace[0]) + buf+='

\n' + done2=[] + for namespace in top_namespaces: + if namespace[0] not in done2: + buf+='
\n' + buf += '

'+namespace[0]+'

\n' + done2.append(namespace[0]) + for functions in namespace[1]: + class_func=functions[0].split('.') + if len(class_func)>1: + f_name='' + if class_func[1].find('_')==0: + f_name=''+class_func[1]+'' + else: + f_name=class_func[1] + if class_func[1]!='': + buf+= '

'\ + +namespace[0]+''+ "." + ''+class_func[0]+''+'.'+f_name+''+' ( '+ functions[1]+ ' )' +'

\n' + else: + buf+= '

'\ + +namespace[0]+''+ "." + ''+class_func[0]+'' +'

\n' + else: + if functions[0].find('_')==0: + f_name=''+functions[0]+'' + else: + f_name=functions[0] + buf+= '

'\ + +namespace[0]+''+ "." + ''+f_name+''+ ' ( '+ functions[1]+ ' )' +'

\n' + for comment in functions[2]: + if comment.find('=====')!=-1: + buf+='
' + else: + tempComment = comment.replace('#','').replace('<','<').replace('>','>') + if string.strip(tempComment)!="": + buf+= '
'+tempComment+'

\n' + buf+='
\n' + if namespace[0] not in done2: + buf+='
\n' + buf+='' + fw.write(buf) + fw.close() + +def parse_file(filename): + fr=open(filename,'rb') + content=fr.readlines() + i=0 + retval=[] + classname="" + for line in content: + match=re.search('^var\s+([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line) + if match!=None: + func_name=match.group(1) + comments=[] + param=match.group(2) + if(line.find(')')==-1 and line.find('(')!=-1): + k=i+1 + while(content[k].find(')')==-1): + param+=content[k].rstrip('\n') + k+=1 + param+=content[k].split(')')[0] + j=i-1 + count=0 + while ( j>i-35 and j>-1): + if count>3: + break + if len(content[j])<2: + j-=1 + count+=1 + continue + if re.search('^\s*#',content[j])!=None: + comments.append(content[j].rstrip('\n')) + j-=1 + else: + break + if(len(comments)>1): + comments.reverse() + retval.append((func_name, param,comments)) + i+=1 + continue + + match3=re.search('^var\s*([A-Za-z0-9_-]+)\s*=\s*{\s*(\n|})',line) + if match3!=None: + classname=match3.group(1) + + comments=[] + + j=i-1 + count=0 + while ( j>i-35 and j>-1): + if count>3: + break + if len(content[j])<2: + j-=1 + count+=1 + continue + if re.search('^\s*#',content[j])!=None: + comments.append(content[j].rstrip('\n')) + j-=1 + else: + break + if(len(comments)>1): + comments.reverse() + retval.append((classname+'.', '',comments)) + i+=1 + continue + + match2=re.search('^\s*([A-Za-z0-9_-]+)\s*:\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line) + if match2!=None: + func_name=match2.group(1) + comments=[] + param=match2.group(2) + if(line.find(')')==-1 and line.find('(')!=-1): + k=i+1 + while(content[k].find(')')==-1): + param+=content[k].rstrip('\n') + k+=1 + param+=content[k].split(')')[0] + j=i-1 + count=0 + while ( j>i-35 and j>-1): + if count>3: + break + if len(content[j])<2: + j-=1 + count+=1 + continue + if re.search('^\s*#',content[j])!=None: + comments.append(content[j].rstrip('\n')) + j-=1 + else: + break + if(len(comments)>1): + comments.reverse() + if classname =='': + continue + retval.append((classname+'.'+func_name, param,comments)) + i+=1 + continue + + match4=re.search('^([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=\n.-]*)\)?',line) + if match4!=None: + classname=match4.group(1) + func_name=match4.group(2) + comments=[] + param=match4.group(3) + if(line.find(')')==-1 and line.find('(')!=-1): + k=i+1 + while(content[k].find(')')==-1): + param+=content[k].rstrip('\n') + k+=1 + param+=content[k].split(')')[0] + j=i-1 + count=0 + while ( j>i-35 and j>-1): + if count>3: + break + if len(content[j])<2: + j-=1 + count+=1 + continue + if re.search('^\s*#',content[j])!=None: + comments.append(content[j].rstrip('\n')) + j-=1 + else: + break + if(len(comments)>1): + comments.reverse() + retval.append((classname+'.'+func_name, param,comments)) + i+=1 + continue + + i+=1 + return retval + + + +if __name__ == "__main__": + if len(sys.argv) <2: + print 'Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]' + sys.exit() + else: + if sys.argv[1]=='parse': + if len(sys.argv) <3: + nasal_path=NASAL_PATH + else: + nasal_path=sys.argv[2] + get_files(nasal_path) + else: + print 'Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]' + sys.exit() diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx index 5cd3d62..b5ef241 100644 --- a/src/AIModel/AIAircraft.cxx +++ b/src/AIModel/AIAircraft.cxx @@ -22,14 +22,13 @@ # include #endif -#include #include
#include
-#include
#include #include #include #include +#include
#include #include @@ -42,13 +41,16 @@ # include #endif -using std::string; #include "AIAircraft.hxx" #include "performancedata.hxx" #include "performancedb.hxx" #include +using std::string; +using std::cerr; +using std::endl; + //#include FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : @@ -93,6 +95,9 @@ FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : _performance = 0; //TODO initialize to JET_TRANSPORT from PerformanceDB dt = 0; takeOffStatus = 0; + + trackCache.remainingLength = 0; + trackCache.startWptName = "-"; } @@ -109,7 +114,7 @@ void FGAIAircraft::readFromScenario(SGPropertyNode* scFileNode) { FGAIBase::readFromScenario(scFileNode); - setPerformance(scFileNode->getStringValue("class", "jet_transport")); + setPerformance("", scFileNode->getStringValue("class", "jet_transport")); setFlightPlan(scFileNode->getStringValue("flightplan"), scFileNode->getBoolValue("repeat", false)); setCallSign(scFileNode->getStringValue("callsign")); @@ -119,39 +124,28 @@ void FGAIAircraft::readFromScenario(SGPropertyNode* scFileNode) { void FGAIAircraft::bind() { FGAIBase::bind(); - props->tie("controls/gear/gear-down", - SGRawValueMethods(*this, - &FGAIAircraft::_getGearDown)); - props->tie("transponder-id", - SGRawValueMethods(*this, - &FGAIAircraft::_getTransponderCode)); + tie("transponder-id", + SGRawValueMethods(*this, + &FGAIAircraft::_getTransponderCode)); } - -void FGAIAircraft::unbind() { - FGAIBase::unbind(); - - props->untie("controls/gear/gear-down"); - props->untie("transponder-id"); -} - - void FGAIAircraft::update(double dt) { FGAIBase::update(dt); Run(dt); Transform(); } -void FGAIAircraft::setPerformance(const std::string& acclass) { - static PerformanceDB perfdb; //TODO make it a global service - setPerformance(perfdb.getDataFor(acclass)); - } - +void FGAIAircraft::setPerformance(const std::string& acType, const std::string& acclass) +{ + static PerformanceDB perfdb; //TODO make it a global service + _performance = perfdb.getDataFor(acType, acclass); +} +#if 0 void FGAIAircraft::setPerformance(PerformanceData *ps) { _performance = ps; } - +#endif void FGAIAircraft::Run(double dt) { FGAIAircraft::dt = dt; @@ -169,8 +163,14 @@ void FGAIAircraft::setPerformance(const std::string& acclass) { handleATCRequests(); // ATC also has a word to say updateSecondaryTargetValues(); // target roll, vertical speed, pitch - updateActualState(); - // We currently have one situation in which an AIAircraft object is used that is not attached to the + updateActualState(); +#if 0 + // 25/11/12 - added but disabled, since setting properties isn't + // affecting the AI-model as expected. + updateModelProperties(dt); +#endif + + // We currently have one situation in which an AIAircraft object is used that is not attached to the // AI manager. In this particular case, the AIAircraft is used to shadow the user's aircraft's behavior in the AI world. // Since we perhaps don't want a radar entry of our own aircraft, the following conditional should probably be adequate // enough @@ -182,8 +182,7 @@ void FGAIAircraft::setPerformance(const std::string& acclass) { void FGAIAircraft::checkVisibility() { double visibility_meters = fgGetDouble("/environment/visibility-m"); - FGViewer* vw = globals->get_current_view(); - invisible = (SGGeodesy::distanceM(vw->getPosition(), pos) > visibility_meters); + invisible = (SGGeodesy::distanceM(globals->get_view_position(), pos) > visibility_meters); } @@ -284,6 +283,12 @@ void FGAIAircraft::ProcessFlightPlan( double dt, time_t now ) { curr = fp->getCurrentWaypoint(); next = fp->getNextWaypoint(); } + if (!curr) + { + // Oops! FIXME + return; + } + if (! leadPointReached(curr)) { controlHeading(curr); controlSpeed(curr, next); @@ -448,12 +453,6 @@ void FGAIAircraft::checkTcas(void) void FGAIAircraft::initializeFlightPlan() { } - -bool FGAIAircraft::_getGearDown() const { - return _performance->gearExtensible(this); -} - - const char * FGAIAircraft::_getTransponderCode() const { return transponderCode.c_str(); } @@ -520,10 +519,8 @@ void FGAIAircraft::getGroundElev(double dt) { // Only do the proper hitlist stuff if we are within visible range of the viewer. if (!invisible) { - double visibility_meters = fgGetDouble("/environment/visibility-m"); - FGViewer* vw = globals->get_current_view(); - - if (SGGeodesy::distanceM(vw->getPosition(), pos) > visibility_meters) { + double visibility_meters = fgGetDouble("/environment/visibility-m"); + if (SGGeodesy::distanceM(globals->get_view_position(), pos) > visibility_meters) { return; } @@ -633,7 +630,7 @@ void FGAIAircraft::scheduleForATCTowerDepartureControl(int state) { // Process ATC instructions and report back -void FGAIAircraft::processATC(FGATCInstruction instruction) { +void FGAIAircraft::processATC(const FGATCInstruction& instruction) { if (instruction.getCheckForCircularWait()) { // This is not exactly an elegant solution, // but at least it gives me a chance to check @@ -716,9 +713,9 @@ void FGAIAircraft::handleFirstWaypoint() { setAltitude(prev->getAltitude()); if (prev->getSpeed() > 0.0) - setHeading(fp->getBearing(prev->getLatitude(), prev->getLongitude(), curr)); + setHeading(fp->getBearing(prev, curr)); else - setHeading(fp->getBearing(curr->getLatitude(), curr->getLongitude(), prev)); + setHeading(fp->getBearing(curr, prev)); // If next doesn't exist, as in incrementally created flightplans for // AI/Trafficmanager created plans, @@ -806,7 +803,7 @@ bool FGAIAircraft::leadPointReached(FGAIWaypoint* curr) { // << " Ground target speed " << groundTargetSpeed << endl; double bearing = 0; // don't do bearing calculations for ground traffic - bearing = getBearing(fp->getBearing(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr)); + bearing = getBearing(fp->getBearing(pos, curr)); if (bearing < minBearing) { minBearing = bearing; if (minBearing < 10) { @@ -843,7 +840,7 @@ bool FGAIAircraft::aiTrafficVisible() SGVec3d cartPos = SGVec3d::fromGeod(pos); const double d2 = (TRAFFICTOAIDISTTODIE * SG_NM_TO_METER) * (TRAFFICTOAIDISTTODIE * SG_NM_TO_METER); - return (distSqr(cartPos, globals->get_aircraft_positon_cart()) < d2); + return (distSqr(cartPos, globals->get_aircraft_position_cart()) < d2); } @@ -869,7 +866,8 @@ bool FGAIAircraft::handleAirportEndPoints(FGAIWaypoint* prev, time_t now) { //cerr << trafficRef->getCallSign() << " has passed waypoint " << prev->name << " at speed " << speed << endl; //cerr << "Passing waypoint : " << prev->getName() << endl; if (prev->contains("PushBackPoint")) { - dep->getDynamics()->releaseParking(fp->getGate()); + // clearing the parking assignment will release the gate + fp->setGate(ParkingAssignment()); AccelTo(0.0); //setTaxiClearanceRequest(true); } @@ -920,12 +918,11 @@ bool FGAIAircraft::handleAirportEndPoints(FGAIWaypoint* prev, time_t now) { * @param curr */ void FGAIAircraft::controlHeading(FGAIWaypoint* curr) { - double calc_bearing = fp->getBearing(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr); + double calc_bearing = fp->getBearing(pos, curr); //cerr << "Bearing = " << calc_bearing << endl; if (speed < 0) { calc_bearing +=180; - if (calc_bearing > 360) - calc_bearing -= 360; + SG_NORMALIZE_RANGE(calc_bearing, 0.0, 360.0); } if (finite(calc_bearing)) { @@ -938,9 +935,9 @@ void FGAIAircraft::controlHeading(FGAIWaypoint* curr) { cerr << "calc_bearing is not a finite number : " << "Speed " << speed << "pos : " << pos.getLatitudeDeg() << ", " << pos.getLongitudeDeg() - << "waypoint " << curr->getLatitude() << ", " << curr->getLongitude() << endl; - cerr << "waypoint name " << curr->getName(); - exit(1); // FIXME + << ", waypoint: " << curr->getLatitude() << ", " << curr->getLongitude() << endl; + cerr << "waypoint name: '" << curr->getName() << "'" << endl; + //exit(1); // FIXME } } @@ -1004,7 +1001,7 @@ void FGAIAircraft::updatePrimaryTargetValues(bool& flightplanActive, bool& aiOut // from control properties. These default to the initial // settings in the config file, but can be changed "on the // fly". - string lat_mode = props->getStringValue("controls/flight/lateral-mode"); + const string& lat_mode = props->getStringValue("controls/flight/lateral-mode"); if ( lat_mode == "roll" ) { double angle = props->getDoubleValue("controls/flight/target-roll" ); @@ -1030,19 +1027,6 @@ void FGAIAircraft::updatePrimaryTargetValues(bool& flightplanActive, bool& aiOut } } -void FGAIAircraft::updatePosition() { - // convert speed to degrees per second - double speed_north_deg_sec = cos( hdg * SGD_DEGREES_TO_RADIANS ) - * speed * 1.686 / ft_per_deg_lat; - double speed_east_deg_sec = sin( hdg * SGD_DEGREES_TO_RADIANS ) - * speed * 1.686 / ft_per_deg_lon; - - // set new position - pos.setLatitudeDeg( pos.getLatitudeDeg() + speed_north_deg_sec * dt); - pos.setLongitudeDeg( pos.getLongitudeDeg() + speed_east_deg_sec * dt); -} - - void FGAIAircraft::updateHeading() { // adjust heading based on current bank angle if (roll == 0.0) @@ -1188,13 +1172,9 @@ void FGAIAircraft::updateVerticalSpeedTarget() { // find target vertical speed if (use_perf_vs) { if (altitude_ft < tgt_altitude_ft) { - tgt_vs = tgt_altitude_ft - altitude_ft; - if (tgt_vs > _performance->climbRate()) - tgt_vs = _performance->climbRate(); + tgt_vs = std::min(tgt_altitude_ft - altitude_ft, _performance->climbRate()); } else { - tgt_vs = tgt_altitude_ft - altitude_ft; - if (tgt_vs < (-_performance->descentRate())) - tgt_vs = -_performance->descentRate(); + tgt_vs = std::max(tgt_altitude_ft - altitude_ft, -_performance->descentRate()); } } else { double vert_dist_ft = fp->getCurrentWaypoint()->getCrossat() - altitude_ft; @@ -1234,18 +1214,16 @@ void FGAIAircraft::updatePitchAngleTarget() { } } -string FGAIAircraft::atGate() { - string tmp(""); - if (fp->getLeg() < 3) { - if (trafficRef) { - if (fp->getGate() > 0) { - FGParking *park = - trafficRef->getDepartureAirport()->getDynamics()->getParking(fp->getGate()); - tmp = park->getName(); - } - } +const string& FGAIAircraft::atGate() +{ + if ((fp->getLeg() < 3) && trafficRef) { + if (fp->getParkingGate()) { + return fp->getParkingGate()->ident(); + } } - return tmp; + + static const string empty; + return empty; } void FGAIAircraft::handleATCRequests() { @@ -1272,7 +1250,9 @@ void FGAIAircraft::handleATCRequests() { void FGAIAircraft::updateActualState() { //update current state //TODO have a single tgt_speed and check speed limit on ground on setting tgt_speed - updatePosition(); + double distance = speed * SG_KT_TO_MPS * dt; + pos = SGGeodesy::direct(pos, hdg, distance); + if (onGround()) speed = _performance->actualSpeed(this, groundTargetSpeed, dt, holdPos); @@ -1364,11 +1344,20 @@ double FGAIAircraft::getBearing(double crse) return hdgDiff; } -time_t FGAIAircraft::checkForArrivalTime(string wptName) { +time_t FGAIAircraft::checkForArrivalTime(const string& wptName) { FGAIWaypoint* curr = 0; curr = fp->getCurrentWaypoint(); - double tracklength = fp->checkTrackLength(wptName); + // don't recalculate tracklength unless the start/stop waypoint has changed + if (curr && + ((curr->getName() != trackCache.startWptName)|| + (wptName != trackCache.finalWptName))) + { + trackCache.remainingLength = fp->checkTrackLength(wptName); + trackCache.startWptName = curr->getName(); + trackCache.finalWptName = wptName; + } + double tracklength = trackCache.remainingLength; if (tracklength > 0.1) { tracklength += fp->getDistanceToGo(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr); } else { @@ -1384,3 +1373,44 @@ time_t FGAIAircraft::checkForArrivalTime(string wptName) { } return (ete - secondsToGo); // Positive when we're too slow... } + +double limitRateOfChange(double cur, double target, double maxDeltaSec, double dt) +{ + double delta = target - cur; + double maxDelta = maxDeltaSec * dt; + +// if delta is > maxDelta, use maxDelta, but with the sign of delta. + return (fabs(delta) < maxDelta) ? delta : copysign(maxDelta, delta); +} + +// drive various properties in a semi-realistic fashion. +void FGAIAircraft::updateModelProperties(double dt) +{ + if (!props) { + return; + } + + SGPropertyNode* gear = props->getChild("gear", 0, true); + double targetGearPos = fp->getCurrentWaypoint()->getGear_down() ? 1.0 : 0.0; + if (!gear->hasValue("gear/position-norm")) { + gear->setDoubleValue("gear/position-norm", targetGearPos); + } + + double gearPosNorm = gear->getDoubleValue("gear/position-norm"); + if (gearPosNorm != targetGearPos) { + gearPosNorm += limitRateOfChange(gearPosNorm, targetGearPos, 0.1, dt); + if (gearPosNorm < 0.001) { + gearPosNorm = 0.0; + } else if (gearPosNorm > 0.999) { + gearPosNorm = 1.0; + } + + for (int i=0; i<6; ++i) { + SGPropertyNode* g = gear->getChild("gear", i, true); + g->setDoubleValue("position-norm", gearPosNorm); + } // of gear setting loop + } // of gear in-transit + +// double flapPosNorm = props->getDoubleValue(); +} + diff --git a/src/AIModel/AIAircraft.hxx b/src/AIModel/AIAircraft.hxx index cfadfaf..58fd417 100644 --- a/src/AIModel/AIAircraft.hxx +++ b/src/AIModel/AIAircraft.hxx @@ -42,18 +42,17 @@ public: // virtual bool init(bool search_in_AI_path=false); virtual void bind(); - virtual void unbind(); virtual void update(double dt); - void setPerformance(const std::string& perfString); - void setPerformance(PerformanceData *ps); + void setPerformance(const std::string& acType, const std::string& perfString); + // void setPerformance(PerformanceData *ps); void setFlightPlan(const std::string& fp, bool repat = false); void SetFlightPlan(FGAIFlightPlan *f); void initializeFlightPlan(); FGAIFlightPlan* GetFlightPlan() const { return fp; }; void ProcessFlightPlan( double dt, time_t now ); - time_t checkForArrivalTime(string wptName); + time_t checkForArrivalTime(const string& wptName); void AccelTo(double speed); void PitchTo(double angle); @@ -69,10 +68,12 @@ public: double getBearing(double crse); void setAcType(const std::string& ac) { acType = ac; }; + const std::string& getAcType() const { return acType; } + void setCompany(const std::string& comp) { company = comp;}; void announcePositionToController(); //TODO have to be public? - void processATC(FGATCInstruction instruction); + void processATC(const FGATCInstruction& instruction); void setTaxiClearanceRequest(bool arg) { needsTaxiClearance = arg; }; bool getTaxiClearanceRequest() { return needsTaxiClearance; }; FGAISchedule * getTrafficRef() { return trafficRef; }; @@ -85,7 +86,7 @@ public: virtual const char* getTypeString(void) const { return "aircraft"; } - std::string GetTransponderCode() { return transponderCode; }; + const std::string& GetTransponderCode() { return transponderCode; }; void SetTransponderCode(const std::string& tc) { transponderCode = tc;}; // included as performance data needs them, who else? @@ -98,7 +99,7 @@ public: inline double getVerticalSpeed() const { return vs; }; inline double altitudeAGL() const { return props->getFloatValue("position/altitude-agl-ft");}; inline double airspeed() const { return props->getFloatValue("velocities/airspeed-kt");}; - std::string atGate(); + const std::string& atGate(); int getTakeOffStatus() { return takeOffStatus; }; @@ -147,12 +148,12 @@ private: void updatePrimaryTargetValues(bool& flightplanActive, bool& aiOutOfSight); void updateSecondaryTargetValues(); - void updatePosition(); void updateHeading(); void updateBankAngleTarget(); void updateVerticalSpeedTarget(); void updatePitchAngleTarget(); void updateActualState(); + void updateModelProperties(double dt); void handleATCRequests(); void checkVisibility(); inline bool isStationary() { return ((fabs(speed)<=0.0001)&&(fabs(tgt_speed)<=0.0001));} @@ -171,8 +172,6 @@ private: bool holdPos; - bool _getGearDown() const; - const char * _getTransponderCode() const; bool reachedWaypoint; @@ -184,6 +183,13 @@ private: PerformanceData* _performance; // the performance data for this aircraft void assertSpeed(double speed); + + struct + { + double remainingLength; + std::string startWptName; + std::string finalWptName; + } trackCache; }; diff --git a/src/AIModel/AIBallistic.cxx b/src/AIModel/AIBallistic.cxx index 68d72f9..fc4ea30 100644 --- a/src/AIModel/AIBallistic.cxx +++ b/src/AIModel/AIBallistic.cxx @@ -189,104 +189,72 @@ void FGAIBallistic::reinit() { void FGAIBallistic::bind() { // FGAIBase::bind(); - props->tie("sim/time/elapsed-sec", + _tiedProperties.setRoot(props); + tie("sim/time/elapsed-sec", SGRawValueMethods(*this, &FGAIBallistic::_getTime, &FGAIBallistic::setTime)); - //props->tie("mass-slug", + //tie("mass-slug", // SGRawValueMethods(*this, // &FGAIBallistic::getMass)); - props->tie("material/solid", + tie("material/solid", SGRawValuePointer(&_solid)); - props->tie("altitude-agl-ft", + tie("altitude-agl-ft", SGRawValuePointer(&_ht_agl_ft)); - props->tie("controls/slave-to-ac", + tie("controls/slave-to-ac", SGRawValueMethods (*this, &FGAIBallistic::getSlaved, &FGAIBallistic::setSlaved)); - props->tie("controls/invisible", + tie("controls/invisible", SGRawValuePointer(&invisible)); if(_external_force || _slave_to_ac){ - props->tie("controls/force_stabilized", + tie("controls/force_stabilized", SGRawValuePointer(&_force_stabilised)); - props->tie("position/global-x", + tie("position/global-x", SGRawValueMethods(*this, &FGAIBase::_getCartPosX, 0)); - props->tie("position/global-y", + tie("position/global-y", SGRawValueMethods(*this, &FGAIBase::_getCartPosY, 0)); - props->tie("position/global-z", + tie("position/global-z", SGRawValueMethods(*this, &FGAIBase::_getCartPosZ, 0)); - props->tie("velocities/vertical-speed-fps", + tie("velocities/vertical-speed-fps", SGRawValuePointer(&vs)); - props->tie("velocities/true-airspeed-kt", + tie("velocities/true-airspeed-kt", SGRawValuePointer(&speed)); - props->tie("velocities/horizontal-speed-fps", + tie("velocities/horizontal-speed-fps", SGRawValuePointer(&hs)); - props->tie("position/altitude-ft", + tie("position/altitude-ft", SGRawValueMethods(*this, &FGAIBase::_getElevationFt, &FGAIBase::_setAltitude)); - props->tie("position/latitude-deg", + tie("position/latitude-deg", SGRawValueMethods(*this, &FGAIBase::_getLatitude, &FGAIBase::_setLatitude)); - props->tie("position/longitude-deg", + tie("position/longitude-deg", SGRawValueMethods(*this, &FGAIBase::_getLongitude, &FGAIBase::_setLongitude)); - props->tie("orientation/hdg-deg", + tie("orientation/hdg-deg", SGRawValuePointer(&hdg)); - props->tie("orientation/pitch-deg", + tie("orientation/pitch-deg", SGRawValuePointer(&pitch)); - props->tie("orientation/roll-deg", + tie("orientation/roll-deg", SGRawValuePointer(&roll)); - props->tie("controls/slave-load-to-ac", + tie("controls/slave-load-to-ac", SGRawValueMethods (*this, &FGAIBallistic::getSlavedLoad, &FGAIBallistic::setSlavedLoad)); - props->tie("position/load-offset", + tie("position/load-offset", SGRawValueMethods (*this, &FGAIBallistic::getLoadOffset, &FGAIBallistic::setLoadOffset)); - props->tie("load/distance-to-hitch-ft", + tie("load/distance-to-hitch-ft", SGRawValueMethods (*this, &FGAIBallistic::getDistanceToHitch)); - props->tie("load/elevation-to-hitch-deg", + tie("load/elevation-to-hitch-deg", SGRawValueMethods (*this, &FGAIBallistic::getElevToHitch)); - props->tie("load/bearing-to-hitch-deg", + tie("load/bearing-to-hitch-deg", SGRawValueMethods (*this, &FGAIBallistic::getBearingToHitch)); - props->tie("material/load-resistance", + tie("material/load-resistance", SGRawValuePointer(&_load_resistance)); } } -void FGAIBallistic::unbind() { -// FGAIBase::unbind(); - - props->untie("sim/time/elapsed-sec"); - props->untie("mass-slug"); - props->untie("material/solid"); - props->untie("altitude-agl-ft"); - props->untie("controls/slave-to-ac"); - props->untie("controls/invisible"); - - if(_external_force || _slave_to_ac){ - props->untie("position/global-y"); - props->untie("position/global-x"); - props->untie("position/global-z"); - props->untie("velocities/vertical-speed-fps"); - props->untie("velocities/true-airspeed-kt"); - props->untie("velocities/horizontal-speed-fps"); - props->untie("position/altitude-ft"); - props->untie("position/latitude-deg"); - props->untie("position/longitude-deg"); - props->untie("position/ht-agl-ft"); - props->untie("orientation/hdg-deg"); - props->untie("orientation/pitch-deg"); - props->untie("orientation/roll-deg"); - props->untie("controls/force_stabilized"); - props->untie("position/load-offset"); - props->untie("load/distance-to-hitch-ft"); - props->untie("load/elevation-to-hitch-deg"); - props->untie("load/bearing-to-hitch-deg"); - props->untie("material/load-resistance"); - } -} - void FGAIBallistic::update(double dt) { FGAIBase::update(dt); _setUserPos(); @@ -530,16 +498,17 @@ void FGAIBallistic::setForcePath(const string& p) { } bool FGAIBallistic::getHtAGL(double start){ - + const simgear::BVHMaterial* mat = 0; if (getGroundElevationM(SGGeod::fromGeodM(pos, start), - _elevation_m, &_material)) { + _elevation_m, &mat)) { + const SGMaterial* material = dynamic_cast(mat); _ht_agl_ft = pos.getElevationFt() - _elevation_m * SG_METER_TO_FEET; - if (_material) { - const vector& names = _material->get_names(); - _solid = _material->get_solid(); - _load_resistance = _material->get_load_resistance(); - _frictionFactor =_material->get_friction_factor(); + if (material) { + const vector& names = material->get_names(); + _solid = material->get_solid(); + _load_resistance = material->get_load_resistance(); + _frictionFactor = material->get_friction_factor(); if (!names.empty()) props->setStringValue("material/name", names[0].c_str()); diff --git a/src/AIModel/AIBallistic.hxx b/src/AIModel/AIBallistic.hxx index 70ea502..9b28efd 100644 --- a/src/AIModel/AIBallistic.hxx +++ b/src/AIModel/AIBallistic.hxx @@ -45,7 +45,6 @@ public: bool init(bool search_in_AI_path=false); virtual void bind(); - virtual void unbind(); virtual void reinit(); virtual void update(double dt); @@ -220,8 +219,6 @@ private: string _force_path; string _contents_path; - const SGMaterial* _material; - void handle_collision(); void handle_expiry(); void handle_impact(); diff --git a/src/AIModel/AIBase.cxx b/src/AIModel/AIBase.cxx index 1431cc4..bbc1510 100644 --- a/src/AIModel/AIBase.cxx +++ b/src/AIModel/AIBase.cxx @@ -34,11 +34,9 @@ #include #include -#include #include #include #include -#include #include #include @@ -77,7 +75,6 @@ FGAIBase::FGAIBase(object_type ot, bool enableHot) : _initialized(false), _modeldata(0), _fx(0) - { tgt_heading = hdg = tgt_altitude_ft = tgt_speed = 0.0; tgt_roll = roll = tgt_pitch = tgt_yaw = tgt_vs = vs = pitch = 0.0; @@ -144,13 +141,7 @@ FGAIBase::~FGAIBase() { model_removed->setStringValue(props->getPath()); } - if (_fx && _refID != 0 && _refID != 1) { - SGSoundMgr *smgr = globals->get_soundmgr(); - stringstream name; - name << "aifx:"; - name << _refID; - smgr->remove(name.str()); - } + removeSoundFx(); if (fp) delete fp; @@ -175,7 +166,7 @@ FGAIBase::removeModel() aip.init( 0 ); _model = 0; // pass it on to the pager, to be be deleted in the pager thread - pSceneryManager->getPagerSingleton()->queueDeleteRequest(temp); + pSceneryManager->getPager()->queueDeleteRequest(temp); } else { @@ -240,17 +231,16 @@ void FGAIBase::update(double dt) { // sound initialization if (fgGetBool("/sim/sound/aimodels/enabled",false)) { - string fxpath = _modeldata->get_sound_path(); + const string& fxpath = _modeldata->get_sound_path(); if (fxpath != "") { props->setStringValue("sim/sound/path", fxpath.c_str()); // initialize the sound configuration - SGSoundMgr *smgr = globals->get_soundmgr(); - stringstream name; + std::stringstream name; name << "aifx:"; name << _refID; - _fx = new FGFX(smgr, name.str(), props); + _fx = new FGFX(name.str(), props); _fx->init(); } } @@ -351,11 +341,10 @@ bool FGAIBase::init(bool search_in_AI_path) aip.setVisible(true); invisible = false; globals->get_scenery()->get_scene_graph()->addChild(aip.getSceneGraph()); - - // Get the sound-path tag from the configuration file and store it - // in the property tree. _initialized = true; + SG_LOG(SG_AI, SG_DEBUG, "AIBase: Loaded model " << model_path); + } else if (!model_path.empty()) { SG_LOG(SG_AI, SG_WARN, "AIBase: Could not load model " << model_path); // not properly installed... @@ -394,61 +383,62 @@ bool FGAIBase::isa( object_type otype ) { void FGAIBase::bind() { - props->tie("id", SGRawValueMethods(*this, + _tiedProperties.setRoot(props); + tie("id", SGRawValueMethods(*this, &FGAIBase::getID)); - props->tie("velocities/true-airspeed-kt", SGRawValuePointer(&speed)); - props->tie("velocities/vertical-speed-fps", + tie("velocities/true-airspeed-kt", SGRawValuePointer(&speed)); + tie("velocities/vertical-speed-fps", SGRawValueMethods(*this, &FGAIBase::_getVS_fps, &FGAIBase::_setVS_fps)); - props->tie("position/altitude-ft", + tie("position/altitude-ft", SGRawValueMethods(*this, &FGAIBase::_getAltitude, &FGAIBase::_setAltitude)); - props->tie("position/latitude-deg", + tie("position/latitude-deg", SGRawValueMethods(*this, &FGAIBase::_getLatitude, &FGAIBase::_setLatitude)); - props->tie("position/longitude-deg", + tie("position/longitude-deg", SGRawValueMethods(*this, &FGAIBase::_getLongitude, &FGAIBase::_setLongitude)); - props->tie("position/global-x", + tie("position/global-x", SGRawValueMethods(*this, &FGAIBase::_getCartPosX, 0)); - props->tie("position/global-y", + tie("position/global-y", SGRawValueMethods(*this, &FGAIBase::_getCartPosY, 0)); - props->tie("position/global-z", + tie("position/global-z", SGRawValueMethods(*this, &FGAIBase::_getCartPosZ, 0)); - props->tie("callsign", + tie("callsign", SGRawValueMethods(*this, &FGAIBase::_getCallsign, 0)); - props->tie("orientation/pitch-deg", SGRawValuePointer(&pitch)); - props->tie("orientation/roll-deg", SGRawValuePointer(&roll)); - props->tie("orientation/true-heading-deg", SGRawValuePointer(&hdg)); - - props->tie("radar/in-range", SGRawValuePointer(&in_range)); - props->tie("radar/bearing-deg", SGRawValuePointer(&bearing)); - props->tie("radar/elevation-deg", SGRawValuePointer(&elevation)); - props->tie("radar/range-nm", SGRawValuePointer(&range)); - props->tie("radar/h-offset", SGRawValuePointer(&horiz_offset)); - props->tie("radar/v-offset", SGRawValuePointer(&vert_offset)); - props->tie("radar/x-shift", SGRawValuePointer(&x_shift)); - props->tie("radar/y-shift", SGRawValuePointer(&y_shift)); - props->tie("radar/rotation", SGRawValuePointer(&rotation)); - props->tie("radar/ht-diff-ft", SGRawValuePointer(&ht_diff)); - props->tie("subID", SGRawValuePointer(&_subID)); - props->tie("controls/lighting/nav-lights", - SGRawValueFunctions(_isNight)); + tie("orientation/pitch-deg", SGRawValuePointer(&pitch)); + tie("orientation/roll-deg", SGRawValuePointer(&roll)); + tie("orientation/true-heading-deg", SGRawValuePointer(&hdg)); + + tie("radar/in-range", SGRawValuePointer(&in_range)); + tie("radar/bearing-deg", SGRawValuePointer(&bearing)); + tie("radar/elevation-deg", SGRawValuePointer(&elevation)); + tie("radar/range-nm", SGRawValuePointer(&range)); + tie("radar/h-offset", SGRawValuePointer(&horiz_offset)); + tie("radar/v-offset", SGRawValuePointer(&vert_offset)); + tie("radar/x-shift", SGRawValuePointer(&x_shift)); + tie("radar/y-shift", SGRawValuePointer(&y_shift)); + tie("radar/rotation", SGRawValuePointer(&rotation)); + tie("radar/ht-diff-ft", SGRawValuePointer(&ht_diff)); + tie("subID", SGRawValuePointer(&_subID)); + tie("controls/lighting/nav-lights", SGRawValueFunctions(_isNight)); + props->setBoolValue("controls/lighting/beacon", true); props->setBoolValue("controls/lighting/strobe", true); props->setBoolValue("controls/glide-path", true); @@ -467,43 +457,25 @@ void FGAIBase::bind() { props->setDoubleValue("sim/sound/avionics/volume", 0.0); props->setBoolValue("sim/sound/avionics/external-view", false); props->setBoolValue("sim/current-view/internal", false); - } void FGAIBase::unbind() { - props->untie("id"); - props->untie("velocities/true-airspeed-kt"); - props->untie("velocities/vertical-speed-fps"); - - props->untie("position/altitude-ft"); - props->untie("position/latitude-deg"); - props->untie("position/longitude-deg"); - props->untie("position/global-x"); - props->untie("position/global-y"); - props->untie("position/global-z"); - props->untie("callsign"); - - props->untie("orientation/pitch-deg"); - props->untie("orientation/roll-deg"); - props->untie("orientation/true-heading-deg"); - - props->untie("radar/in-range"); - props->untie("radar/bearing-deg"); - props->untie("radar/elevation-deg"); - props->untie("radar/range-nm"); - props->untie("radar/h-offset"); - props->untie("radar/v-offset"); - props->untie("radar/x-shift"); - props->untie("radar/y-shift"); - props->untie("radar/rotation"); - props->untie("radar/ht-diff-ft"); - - props->untie("controls/lighting/nav-lights"); - - props->setBoolValue("/sim/controls/radar/", true); + _tiedProperties.Untie(); + props->setBoolValue("/sim/controls/radar", true); + + removeSoundFx(); +} + +void FGAIBase::removeSoundFx() { // drop reference to sound effects now - _fx = 0; + if (_fx) + { + // must remove explicitly - since the sound manager also keeps a reference + _fx->unbind(); + // now drop last reference - kill the object + _fx = 0; + } } double FGAIBase::UpdateRadar(FGAIManager* manager) { @@ -636,7 +608,7 @@ SGVec3d FGAIBase::getCartPos() const { } bool FGAIBase::getGroundElevationM(const SGGeod& pos, double& elev, - const SGMaterial** material) const { + const simgear::BVHMaterial** material) const { return globals->get_scenery()->get_elevation_m(pos, elev, material, _model.get()); } @@ -691,7 +663,7 @@ bool FGAIBase::setParentNode() { model = _selected_ac; } else { model = ai->getChild(i); - string path = ai->getPath(); + //const string& path = ai->getPath(); const string name = model->getStringValue("name"); if (!model->nChildren()){ @@ -758,7 +730,7 @@ double FGAIBase::_getAltitude() const { double FGAIBase::_getAltitudeAGL(SGGeod inpos, double start){ getGroundElevationM(SGGeod::fromGeodM(inpos, start), - _elevation_m, &_material); + _elevation_m, NULL); return inpos.getElevationFt() - _elevation_m * SG_METER_TO_FEET; } diff --git a/src/AIModel/AIBase.hxx b/src/AIModel/AIBase.hxx index eaf2c3e..98f889a 100644 --- a/src/AIModel/AIBase.hxx +++ b/src/AIModel/AIBase.hxx @@ -23,12 +23,12 @@ #include #include -#include #include #include #include #include #include +#include #include #include @@ -38,12 +38,14 @@ using std::string; -class SGMaterial; +namespace simgear { +class BVHMaterial; +} class FGAIManager; class FGAIFlightPlan; class FGFX; class FGNasalModelDataProxy; -class FGAIModelData; // defined below +class FGAIModelData; // defined below class FGAIBase : public SGReferenced { @@ -52,7 +54,7 @@ public: enum object_type { otNull = 0, otAircraft, otShip, otCarrier, otBallistic, otRocket, otStorm, otThermal, otStatic, otWingman, otGroundVehicle, otEscort, otMultiplayer, - MAX_OBJECTS }; // Needs to be last!!! + MAX_OBJECTS }; // Needs to be last!!! FGAIBase(object_type ot, bool enableHot); virtual ~FGAIBase(); @@ -114,10 +116,9 @@ public: SGVec3d getCartPos() const; bool getGroundElevationM(const SGGeod& pos, double& elev, - const SGMaterial** material) const; + const simgear::BVHMaterial** material) const; double _elevation_m; - const SGMaterial* _material; double _getCartPosX() const; double _getCartPosY() const; @@ -143,7 +144,16 @@ public: protected: - + /** + * Tied-properties helper, record nodes which are tied for easy un-tie-ing + */ + template + void tie(const char* aRelPath, const SGRawValue& aRawValue) + { + _tiedProperties.Tie(props->getNode(aRelPath, true), aRawValue); + } + + simgear::TiedPropertyList _tiedProperties; SGPropertyNode_ptr _selected_ac; SGPropertyNode_ptr props; SGPropertyNode_ptr trigger_node; @@ -151,10 +161,10 @@ protected: FGAIManager* manager; // these describe the model's actual state - SGGeod pos; // WGS84 lat & lon in degrees, elev above sea-level in meters - double hdg; // True heading in degrees - double roll; // degrees, left is negative - double pitch; // degrees, nose-down is negative + SGGeod pos; // WGS84 lat & lon in degrees, elev above sea-level in meters + double hdg; // True heading in degrees + double roll; // degrees, left is negative + double pitch; // degrees, nose-down is negative double speed; // knots true airspeed double altitude_ft; // feet above sea level double vs; // vertical speed, feet per minute @@ -186,9 +196,9 @@ protected: double x_shift; // value used by radar display instrument double y_shift; // value used by radar display instrument double rotation; // value used by radar display instrument - double ht_diff; // value used by radar display instrument + double ht_diff; // value used by radar display instrument - string model_path; //Path to the 3D model + string model_path; //Path to the 3D model SGModelPlacement aip; bool delete_me; @@ -219,6 +229,7 @@ protected: double UpdateRadar(FGAIManager* manager); void removeModel(); + void removeSoundFx(); static int _newAIModelID(); @@ -469,4 +480,4 @@ private: bool _initialized; }; -#endif // _FG_AIBASE_HXX +#endif // _FG_AIBASE_HXX diff --git a/src/AIModel/AICarrier.cxx b/src/AIModel/AICarrier.cxx index 2f10969..8ebb53d 100644 --- a/src/AIModel/AICarrier.cxx +++ b/src/AIModel/AICarrier.cxx @@ -26,12 +26,10 @@ #include #include -#include #include #include #include
-#include
#include "AICarrier.hxx" @@ -75,7 +73,7 @@ void FGAICarrier::readFromScenario(SGPropertyNode* scFileNode) { std::vector props = scFileNode->getChildren("parking-pos"); std::vector::const_iterator it; for (it = props.begin(); it != props.end(); ++it) { - string name = (*it)->getStringValue("name", "unnamed"); + const string name = (*it)->getStringValue("name", "unnamed"); // Transform to the right coordinate frame, configuration is done in // the usual x-back, y-right, z-up coordinates, computations // in the simulation usual body x-forward, y-right, z-down coordinates @@ -167,7 +165,7 @@ void FGAICarrier::update(double dt) { SGVec3d cartPos = SGVec3d::fromGeod(pos); // The position of the eyepoint - at least near that ... - SGVec3d eyePos(globals->get_current_view()->get_view_pos()); + SGVec3d eyePos(globals->get_view_position_cart()); // Add the position offset of the AIModel to gain the earth // centered position SGVec3d eyeWrtCarrier = eyePos - cartPos; @@ -247,64 +245,63 @@ void FGAICarrier::bind() { props->untie("velocities/true-airspeed-kt"); - props->tie("controls/flols/source-lights", - SGRawValuePointer(&source)); - props->tie("controls/flols/distance-m", - SGRawValuePointer(&dist)); - props->tie("controls/flols/angle-degs", - SGRawValuePointer(&angle)); - props->tie("controls/turn-to-launch-hdg", - SGRawValuePointer(&turn_to_launch_hdg)); - props->tie("controls/in-to-wind", - SGRawValuePointer(&turn_to_launch_hdg)); - props->tie("controls/base-course-deg", - SGRawValuePointer(&base_course)); - props->tie("controls/base-speed-kts", - SGRawValuePointer(&base_speed)); - props->tie("controls/start-pos-lat-deg", - SGRawValueMethods(pos, &SGGeod::getLatitudeDeg)); - props->tie("controls/start-pos-long-deg", - SGRawValueMethods(pos, &SGGeod::getLongitudeDeg)); - props->tie("controls/mp-control", - SGRawValuePointer(&MPControl)); - props->tie("controls/ai-control", - SGRawValuePointer(&AIControl)); - props->tie("environment/surface-wind-speed-true-kts", - SGRawValuePointer(&wind_speed_kts)); - props->tie("environment/surface-wind-from-true-degs", - SGRawValuePointer(&wind_from_deg)); - props->tie("environment/rel-wind-from-degs", - SGRawValuePointer(&rel_wind_from_deg)); - props->tie("environment/rel-wind-from-carrier-hdg-degs", - SGRawValuePointer(&rel_wind)); - props->tie("environment/rel-wind-speed-kts", - SGRawValuePointer(&rel_wind_speed_kts)); - props->tie("environment/in-to-wind", + tie("controls/flols/source-lights", + SGRawValuePointer(&source)); + tie("controls/flols/distance-m", + SGRawValuePointer(&dist)); + tie("controls/flols/angle-degs", + SGRawValuePointer(&angle)); + tie("controls/turn-to-launch-hdg", + SGRawValuePointer(&turn_to_launch_hdg)); + tie("controls/in-to-wind", + SGRawValuePointer(&turn_to_launch_hdg)); + tie("controls/base-course-deg", + SGRawValuePointer(&base_course)); + tie("controls/base-speed-kts", + SGRawValuePointer(&base_speed)); + tie("controls/start-pos-lat-deg", + SGRawValueMethods(pos, &SGGeod::getLatitudeDeg)); + tie("controls/start-pos-long-deg", + SGRawValueMethods(pos, &SGGeod::getLongitudeDeg)); + tie("controls/mp-control", + SGRawValuePointer(&MPControl)); + tie("controls/ai-control", + SGRawValuePointer(&AIControl)); + tie("environment/surface-wind-speed-true-kts", + SGRawValuePointer(&wind_speed_kts)); + tie("environment/surface-wind-from-true-degs", + SGRawValuePointer(&wind_from_deg)); + tie("environment/rel-wind-from-degs", + SGRawValuePointer(&rel_wind_from_deg)); + tie("environment/rel-wind-from-carrier-hdg-degs", + SGRawValuePointer(&rel_wind)); + tie("environment/rel-wind-speed-kts", + SGRawValuePointer(&rel_wind_speed_kts)); + tie("environment/in-to-wind", SGRawValuePointer(&in_to_wind)); - //props->tie("controls/flols/wave-off-lights", - // SGRawValuePointer(&wave_off_lights)); - props->tie("controls/elevators", - SGRawValuePointer(&elevators)); - props->tie("surface-positions/elevators-pos-norm", - SGRawValuePointer(&pos_norm)); - props->tie("controls/constants/elevators/trans-time-s", - SGRawValuePointer(&transition_time)); - props->tie("controls/constants/elevators/time-constant", - SGRawValuePointer(&time_constant)); - props->tie("controls/jbd", + //tie("controls/flols/wave-off-lights", + // SGRawValuePointer(&wave_off_lights)); + tie("controls/elevators", + SGRawValuePointer(&elevators)); + tie("surface-positions/elevators-pos-norm", + SGRawValuePointer(&pos_norm)); + tie("controls/constants/elevators/trans-time-s", + SGRawValuePointer(&transition_time)); + tie("controls/constants/elevators/time-constant", + SGRawValuePointer(&time_constant)); + tie("controls/jbd", SGRawValuePointer(&jbd)); - props->tie("surface-positions/jbd-pos-norm", + tie("surface-positions/jbd-pos-norm", SGRawValuePointer(&jbd_pos_norm)); - props->tie("controls/constants/jbd/trans-time-s", + tie("controls/constants/jbd/trans-time-s", SGRawValuePointer(&jbd_transition_time)); - props->tie("controls/constants/jbd/time-constant", + tie("controls/constants/jbd/time-constant", SGRawValuePointer(&jbd_time_constant)); - props->tie("controls/turn-to-recovery-hdg", + tie("controls/turn-to-recovery-hdg", SGRawValuePointer(&turn_to_recovery_hdg)); - props->tie("controls/turn-to-base-course", + tie("controls/turn-to-base-course", SGRawValuePointer(&turn_to_base_course)); - props->setBoolValue("controls/flols/cut-lights", false); props->setBoolValue("controls/flols/wave-off-lights", false); props->setBoolValue("controls/flols/cond-datum-lights", true); @@ -315,36 +312,6 @@ void FGAICarrier::bind() { props->setDoubleValue("controls/lighting/flood-lights-red-norm", 0); } - -void FGAICarrier::unbind() { - FGAIShip::unbind(); - - props->untie("velocities/true-airspeed-kt"); - props->untie("controls/flols/source-lights"); - props->untie("controls/flols/distance-m"); - props->untie("controls/flols/angle-degs"); - props->untie("controls/turn-to-launch-hdg"); - props->untie("environment/wind-speed-true-kts"); - props->untie("environment/wind-from-true-degs"); - props->untie("environment/rel-wind-from-degs"); - props->untie("environment/rel-wind-speed-kts"); - props->untie("environment/in-to-wind"); - //props->untie("controls/flols/wave-off-lights"); - props->untie("controls/elevators"); - props->untie("surface-positions/elevators-pos-norm"); - props->untie("controls/constants/elevators/trans-time-secs"); - props->untie("controls/constants/elevators/time-constant"); - props->untie("controls/jbd"); - props->untie("surface-positions/jbd/pos-norm"); - props->untie("controls/constants/jbd/trans-time-s"); - props->untie("controls/jbd-time-constant"); - props->untie("controls/mp-control"); - props->untie("controls/ai-control"); - props->untie("controls/turn-to-recovery-hdg"); - props->untie("controls/turn-to-base-course"); -} - - bool FGAICarrier::getParkPosition(const string& id, SGGeod& geodPos, double& hdng, SGVec3d& uvw) { @@ -582,7 +549,7 @@ void FGAICarrier::UpdateElevator(double dt, double transition_time) { void FGAICarrier::UpdateJBD(double dt, double jbd_transition_time) { - string launchbar_state = _launchbar_state_node->getStringValue(); + const string launchbar_state = _launchbar_state_node->getStringValue(); double step = 0; if (launchbar_state == "Engaged"){ diff --git a/src/AIModel/AICarrier.hxx b/src/AIModel/AICarrier.hxx index da33805..a34279b 100644 --- a/src/AIModel/AICarrier.hxx +++ b/src/AIModel/AICarrier.hxx @@ -49,7 +49,6 @@ public: void setTACANChannelID(const string &); virtual void bind(); - virtual void unbind(); void UpdateWind ( double dt ); void setWind_from_east( double fps ); void setWind_from_north( double fps ); diff --git a/src/AIModel/AIEscort.cxx b/src/AIModel/AIEscort.cxx index c88e9af..8012ae9 100644 --- a/src/AIModel/AIEscort.cxx +++ b/src/AIModel/AIEscort.cxx @@ -27,12 +27,12 @@ #include #include -#include #include +#include #include #include
-#include
+#include #include @@ -91,46 +91,32 @@ void FGAIEscort::readFromScenario(SGPropertyNode* scFileNode) { void FGAIEscort::bind() { FGAIShip::bind(); - props->tie("station/rel-bearing-deg", + tie("station/rel-bearing-deg", SGRawValuePointer(&_stn_relbrg)); - props->tie("station/true-bearing-deg", + tie("station/true-bearing-deg", SGRawValuePointer(&_stn_truebrg)); - props->tie("station/range-nm", + tie("station/range-nm", SGRawValuePointer(&_stn_range)); - props->tie("station/range-limit-nm", + tie("station/range-limit-nm", SGRawValuePointer(&_stn_limit)); - props->tie("station/angle-limit-deg", + tie("station/angle-limit-deg", SGRawValuePointer(&_stn_angle_limit)); - props->tie("station/speed-kts", + tie("station/speed-kts", SGRawValuePointer(&_stn_speed)); - props->tie("station/height-ft", + tie("station/height-ft", SGRawValuePointer(&_stn_height)); - props->tie("controls/update-interval-sec", + tie("controls/update-interval-sec", SGRawValuePointer(&_interval)); - props->tie("controls/parent-mp-control", + tie("controls/parent-mp-control", SGRawValuePointer(&_MPControl)); - props->tie("station/target-range-nm", + tie("station/target-range-nm", SGRawValuePointer(&_tgtrange)); - props->tie("station/target-brg-deg-t", + tie("station/target-brg-deg-t", SGRawValuePointer(&_tgtbrg)); - props->tie("station/patrol", + tie("station/patrol", SGRawValuePointer(&_patrol)); } -void FGAIEscort::unbind() { - FGAIShip::unbind(); - - props->untie("station/rel-bearing-deg"); - props->untie("station/true-bearing-deg"); - props->untie("station/range-nm"); - props->untie("station/range-limit-nm"); - props->untie("station/angle-limit-deg"); - props->untie("station/speed-kts"); - props->untie("station/height-ft"); - props->untie("controls/update-interval-sec"); - -} - bool FGAIEscort::init(bool search_in_AI_path) { if (!FGAIShip::init(search_in_AI_path)) return false; @@ -204,13 +190,15 @@ bool FGAIEscort::getGroundElev(SGGeod inpos) { double height_m ; - if (globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(inpos, 3000), height_m, &_material,0)){ + const simgear::BVHMaterial* mat = 0; + if (globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(inpos, 3000), height_m, &mat, 0)){ + const SGMaterial* material = dynamic_cast(mat); _ht_agl_ft = inpos.getElevationFt() - height_m * SG_METER_TO_FEET; - if (_material) { - const vector& names = _material->get_names(); + if (material) { + const vector& names = material->get_names(); - _solid = _material->get_solid(); + _solid = material->get_solid(); if (!names.empty()) props->setStringValue("material/name", names[0].c_str()); diff --git a/src/AIModel/AIEscort.hxx b/src/AIModel/AIEscort.hxx index a300f64..38531eb 100644 --- a/src/AIModel/AIEscort.hxx +++ b/src/AIModel/AIEscort.hxx @@ -42,7 +42,6 @@ public: bool init(bool search_in_AI_path=false); virtual void bind(); - virtual void unbind(); virtual void reinit(); virtual void update (double dt); @@ -90,8 +89,6 @@ private: double _max_speed; - const SGMaterial* _material; - bool _MPControl, _patrol, _stn_deg_true; // std::string _parent; diff --git a/src/AIModel/AIFlightPlan.cxx b/src/AIModel/AIFlightPlan.cxx index 21ad6f9..5699a62 100644 --- a/src/AIModel/AIFlightPlan.cxx +++ b/src/AIModel/AIFlightPlan.cxx @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -35,6 +34,7 @@ #include
#include
#include +#include #include #include @@ -47,9 +47,6 @@ using std::cerr; FGAIWaypoint::FGAIWaypoint() { - latitude = 0; - longitude = 0; - altitude = 0; speed = 0; crossat = 0; finished = 0; @@ -61,7 +58,7 @@ FGAIWaypoint::FGAIWaypoint() { trackLength = 0; } -bool FGAIWaypoint::contains(string target) { +bool FGAIWaypoint::contains(const string& target) { size_t found = name.find(target); if (found == string::npos) return false; @@ -69,70 +66,63 @@ bool FGAIWaypoint::contains(string target) { return true; } -FGAIFlightPlan::FGAIFlightPlan() +double FGAIWaypoint::getLatitude() { - sid = 0; - repeat = false; - distance_to_go = 0; - lead_distance = 0; - start_time = 0; - arrivalTime = 0; - leg = 10; - gateId = 0; - lastNodeVisited = 0; - taxiRoute = 0; - wpt_iterator = waypoints.begin(); - isValid = true; + return pos.getLatitudeDeg(); } -FGAIFlightPlan::FGAIFlightPlan(const string& filename) +double FGAIWaypoint::getLongitude() { - int i; - sid = 0; - start_time = 0; - leg = 10; - gateId = 0; - taxiRoute = 0; - SGPath path( globals->get_fg_root() ); - path.append( ("/AI/FlightPlans/" + filename).c_str() ); - SGPropertyNode root; - repeat = false; + return pos.getLongitudeDeg(); +} - try { - readProperties(path.str(), &root); - } catch (const sg_exception &) { - SG_LOG(SG_AI, SG_ALERT, - "Error reading AI flight plan: " << path.str()); - // cout << path.str() << endl; - return; - } +double FGAIWaypoint::getAltitude() +{ + return pos.getElevationFt(); +} - SGPropertyNode * node = root.getNode("flightplan"); - for (i = 0; i < node->nChildren(); i++) { - //cout << "Reading waypoint " << i << endl; - FGAIWaypoint* wpt = new FGAIWaypoint; - SGPropertyNode * wpt_node = node->getChild(i); - wpt->setName (wpt_node->getStringValue("name", "END" )); - wpt->setLatitude (wpt_node->getDoubleValue("lat", 0 )); - wpt->setLongitude (wpt_node->getDoubleValue("lon", 0 )); - wpt->setAltitude (wpt_node->getDoubleValue("alt", 0 )); - wpt->setSpeed (wpt_node->getDoubleValue("ktas", 0 )); - wpt->setCrossat (wpt_node->getDoubleValue("crossat", -10000 )); - wpt->setGear_down (wpt_node->getBoolValue("gear-down", false )); - wpt->setFlaps_down (wpt_node->getBoolValue("flaps-down", false )); - wpt->setOn_ground (wpt_node->getBoolValue("on-ground", false )); - wpt->setTime_sec (wpt_node->getDoubleValue("time-sec", 0 )); - wpt->setTime (wpt_node->getStringValue("time", "" )); - - if (wpt->getName() == "END") wpt->setFinished(true); - else wpt->setFinished(false); - - pushBackWaypoint( wpt ); - } +void FGAIWaypoint::setLatitude(double lat) +{ + pos.setLatitudeDeg(lat); +} - wpt_iterator = waypoints.begin(); - isValid = true; - //cout << waypoints.size() << " waypoints read." << endl; +void FGAIWaypoint::setLongitude(double lon) +{ + pos.setLongitudeDeg(lon); +} + +void FGAIWaypoint::setAltitude(double alt) +{ + pos.setElevationFt(alt); +} + +FGAIFlightPlan::FGAIFlightPlan() : + sid(NULL), + repeat(false), + distance_to_go(0), + lead_distance(0), + leadInAngle(0), + start_time(0), + arrivalTime(0), + leg(0), + lastNodeVisited(0), + isValid(true) +{ + wpt_iterator = waypoints.begin(); +} + +FGAIFlightPlan::FGAIFlightPlan(const string& filename) : + sid(NULL), + repeat(false), + distance_to_go(0), + lead_distance(0), + leadInAngle(0), + start_time(0), + arrivalTime(0), + leg(10), + lastNodeVisited(0), + isValid(parseProperties(filename)) +{ } @@ -144,118 +134,126 @@ FGAIFlightPlan::FGAIFlightPlan(const string& filename) // traffic manager. FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac, const std::string& p, - double course, - time_t start, - FGAirport *dep, - FGAirport *arr, - bool firstLeg, - double radius, + double course, + time_t start, + FGAirport *dep, + FGAirport *arr, + bool firstLeg, + double radius, double alt, double lat, double lon, double speed, - const string& fltType, - const string& acType, - const string& airline) + const string& fltType, + const string& acType, + const string& airline) : + sid(NULL), + repeat(false), + distance_to_go(0), + lead_distance(0), + leadInAngle(0), + start_time(start), + arrivalTime(0), + leg(10), + lastNodeVisited(0), + isValid(false), + departure(dep), + arrival(arr) { - sid = 0; - repeat = false; - leg = 10; - gateId=0; - taxiRoute = 0; - start_time = start; - bool useInitialWayPoint = true; - bool useCurrentWayPoint = false; - SGPath path( globals->get_fg_root() ); - path.append( "/AI/FlightPlans" ); - path.append( p ); - - SGPropertyNode root; - isValid = true; - // This is a bit of a hack: - // Normally the value of course will be used to evaluate whether - // or not a waypoint will be used for midair initialization of - // an AI aircraft. However, if a course value of 999 will be passed - // when an update request is received, which will by definition always be - // on the ground and should include all waypoints. - if (course == 999) - { - useInitialWayPoint = false; - useCurrentWayPoint = true; - } - - if (path.exists()) - { - try - { - readProperties(path.str(), &root); - - SGPropertyNode * node = root.getNode("flightplan"); - - //pushBackWaypoint( init_waypoint ); - for (int i = 0; i < node->nChildren(); i++) { - //cout << "Reading waypoint " << i << endl; - FGAIWaypoint* wpt = new FGAIWaypoint; - SGPropertyNode * wpt_node = node->getChild(i); - wpt->setName (wpt_node->getStringValue("name", "END" )); - wpt->setLatitude (wpt_node->getDoubleValue("lat", 0 )); - wpt->setLongitude (wpt_node->getDoubleValue("lon", 0 )); - wpt->setAltitude (wpt_node->getDoubleValue("alt", 0 )); - wpt->setSpeed (wpt_node->getDoubleValue("ktas", 0 )); - wpt->setCrossat (wpt_node->getDoubleValue("crossat", -10000 )); - wpt->setGear_down (wpt_node->getBoolValue("gear-down", false )); - wpt->setFlaps_down (wpt_node->getBoolValue("flaps-down", false )); - - if (wpt->getName() == "END") wpt->setFinished(true); - else wpt->setFinished(false); - pushBackWaypoint(wpt); - } // of node loop - wpt_iterator = waypoints.begin(); - } catch (const sg_exception &e) { - SG_LOG(SG_AI, SG_WARN, "Error reading AI flight plan: " << - e.getMessage() << " from " << e.getOrigin()); - } + if (parseProperties(p)) { + isValid = true; } else { - // cout << path.str() << endl; - // cout << "Trying to create this plan dynamically" << endl; - // cout << "Route from " << dep->id << " to " << arr->id << endl; - time_t now = time(NULL) + fgGetLong("/sim/time/warp"); - time_t timeDiff = now-start; - leg = 1; - - if ((timeDiff > 60) && (timeDiff < 1500)) - leg = 2; - //else if ((timeDiff >= 1200) && (timeDiff < 1500)) { - //leg = 3; - //ac->setTakeOffStatus(2); - //} - else if ((timeDiff >= 1500) && (timeDiff < 2000)) - leg = 4; - else if (timeDiff >= 2000) - leg = 5; - /* - if (timeDiff >= 2000) - leg = 5; - */ - SG_LOG(SG_AI, SG_INFO, "Route from " << dep->getId() << " to " << arr->getId() << ". Set leg to : " << leg << " " << ac->getTrafficRef()->getCallSign()); - wpt_iterator = waypoints.begin(); - bool dist = 0; - isValid = create(ac, dep,arr, leg, alt, speed, lat, lon, - firstLeg, radius, fltType, acType, airline, dist); - wpt_iterator = waypoints.begin(); - } - + createWaypoints(ac, course, start, dep, arr, firstLeg, radius, + alt, lat, lon, speed, fltType, acType, airline); + } } - - - FGAIFlightPlan::~FGAIFlightPlan() { deleteWaypoints(); - delete taxiRoute; + //delete taxiRoute; +} + +void FGAIFlightPlan::createWaypoints(FGAIAircraft *ac, + double course, + time_t start, + FGAirport *dep, + FGAirport *arr, + bool firstLeg, + double radius, + double alt, + double lat, + double lon, + double speed, + const string& fltType, + const string& acType, + const string& airline) +{ + time_t now = time(NULL) + fgGetLong("/sim/time/warp"); + time_t timeDiff = now-start; + leg = 1; + + if ((timeDiff > 60) && (timeDiff < 1500)) + leg = 2; + //else if ((timeDiff >= 1200) && (timeDiff < 1500)) { + //leg = 3; + //ac->setTakeOffStatus(2); + //} + else if ((timeDiff >= 1500) && (timeDiff < 2000)) + leg = 4; + else if (timeDiff >= 2000) + leg = 5; + /* + if (timeDiff >= 2000) + leg = 5; + */ + SG_LOG(SG_AI, SG_INFO, "Route from " << dep->getId() << " to " << arr->getId() << ". Set leg to : " << leg << " " << ac->getTrafficRef()->getCallSign()); + wpt_iterator = waypoints.begin(); + bool dist = 0; + isValid = create(ac, dep, arr, leg, alt, speed, lat, lon, + firstLeg, radius, fltType, acType, airline, dist); + wpt_iterator = waypoints.begin(); } +bool FGAIFlightPlan::parseProperties(const std::string& filename) +{ + SGPath path( globals->get_fg_root() ); + path.append( "/AI/FlightPlans/" + filename ); + if (!path.exists()) { + return false; + } + + SGPropertyNode root; + try { + readProperties(path.str(), &root); + } catch (const sg_exception &e) { + SG_LOG(SG_AI, SG_ALERT, "Error reading AI flight plan: " << path.str() + << "message:" << e.getFormattedMessage()); + return false; + } + + SGPropertyNode * node = root.getNode("flightplan"); + for (int i = 0; i < node->nChildren(); i++) { + FGAIWaypoint* wpt = new FGAIWaypoint; + SGPropertyNode * wpt_node = node->getChild(i); + wpt->setName (wpt_node->getStringValue("name", "END" )); + wpt->setLatitude (wpt_node->getDoubleValue("lat", 0 )); + wpt->setLongitude (wpt_node->getDoubleValue("lon", 0 )); + wpt->setAltitude (wpt_node->getDoubleValue("alt", 0 )); + wpt->setSpeed (wpt_node->getDoubleValue("ktas", 0 )); + wpt->setCrossat (wpt_node->getDoubleValue("crossat", -10000 )); + wpt->setGear_down (wpt_node->getBoolValue("gear-down", false )); + wpt->setFlaps_down (wpt_node->getBoolValue("flaps-down", false )); + wpt->setOn_ground (wpt_node->getBoolValue("on-ground", false )); + wpt->setTime_sec (wpt_node->getDoubleValue("time-sec", 0 )); + wpt->setTime (wpt_node->getStringValue("time", "" )); + wpt->setFinished ((wpt->getName() == "END")); + pushBackWaypoint( wpt ); + } + + wpt_iterator = waypoints.begin(); + return true; +} FGAIWaypoint* const FGAIFlightPlan::getPreviousWaypoint( void ) const { @@ -269,6 +267,8 @@ FGAIWaypoint* const FGAIFlightPlan::getPreviousWaypoint( void ) const FGAIWaypoint* const FGAIFlightPlan::getCurrentWaypoint( void ) const { + if (wpt_iterator == waypoints.end()) + return 0; return *wpt_iterator; } @@ -286,21 +286,21 @@ FGAIWaypoint* const FGAIFlightPlan::getNextWaypoint( void ) const void FGAIFlightPlan::IncrementWaypoint(bool eraseWaypoints ) { - if (eraseWaypoints) + if (eraseWaypoints) { - if (wpt_iterator == waypoints.begin()) - wpt_iterator++; - else - { - delete *(waypoints.begin()); - waypoints.erase(waypoints.begin()); - wpt_iterator = waypoints.begin(); - wpt_iterator++; - } + if (wpt_iterator == waypoints.begin()) + wpt_iterator++; + else + if (!waypoints.empty()) + { + delete *(waypoints.begin()); + waypoints.erase(waypoints.begin()); + wpt_iterator = waypoints.begin(); + wpt_iterator++; + } } - else - wpt_iterator++; - + else + wpt_iterator++; } void FGAIFlightPlan::DecrementWaypoint(bool eraseWaypoints ) @@ -310,9 +310,10 @@ void FGAIFlightPlan::DecrementWaypoint(bool eraseWaypoints ) if (wpt_iterator == waypoints.end()) wpt_iterator--; else + if (!waypoints.empty()) { - delete *(waypoints.end()); - waypoints.erase(waypoints.end()); + delete *(waypoints.end()-1); + waypoints.erase(waypoints.end()-1); wpt_iterator = waypoints.end(); wpt_iterator--; } @@ -334,8 +335,7 @@ void FGAIFlightPlan::eraseLastWaypoint() // gives distance in feet from a position to a waypoint double FGAIFlightPlan::getDistanceToGo(double lat, double lon, FGAIWaypoint* wp) const{ - return SGGeodesy::distanceM(SGGeod::fromDeg(lon, lat), - SGGeod::fromDeg(wp->getLongitude(), wp->getLatitude())); + return SGGeodesy::distanceM(SGGeod::fromDeg(lon, lat), wp->getPos()); } // sets distance in feet from a lead point to the current waypoint @@ -383,14 +383,14 @@ void FGAIFlightPlan::setLeadDistance(double distance_ft){ } -double FGAIFlightPlan::getBearing(FGAIWaypoint* first, FGAIWaypoint* second) const{ - return getBearing(first->getLatitude(), first->getLongitude(), second); +double FGAIFlightPlan::getBearing(FGAIWaypoint* first, FGAIWaypoint* second) const +{ + return SGGeodesy::courseDeg(first->getPos(), second->getPos()); } - -double FGAIFlightPlan::getBearing(double lat, double lon, FGAIWaypoint* wp) const{ - return SGGeodesy::courseDeg(SGGeod::fromDeg(lon, lat), - SGGeod::fromDeg(wp->getLongitude(), wp->getLatitude())); +double FGAIFlightPlan::getBearing(const SGGeod& aPos, FGAIWaypoint* wp) const +{ + return SGGeodesy::courseDeg(aPos, wp->getPos()); } void FGAIFlightPlan::deleteWaypoints() @@ -398,6 +398,7 @@ void FGAIFlightPlan::deleteWaypoints() for (wpt_vector_iterator i = waypoints.begin(); i != waypoints.end();i++) delete (*i); waypoints.clear(); + wpt_iterator = waypoints.begin(); } // Delete all waypoints except the last, @@ -412,10 +413,7 @@ void FGAIFlightPlan::resetWaypoints() wpt_vector_iterator i = waypoints.end(); i--; wpt->setName ( (*i)->getName() ); - wpt->setLatitude ( (*i)->getLatitude() ); - wpt->setLongitude ( (*i)->getLongitude() ); - wpt->setAltitude ( (*i)->getAltitude() ); - wpt->setSpeed ( (*i)->getSpeed() ); + wpt->setPos ( (*i)->getPos() ); wpt->setCrossat ( (*i)->getCrossat() ); wpt->setGear_down ( (*i)->getGear_down() ); wpt->setFlaps_down ( (*i)->getFlaps_down() ); @@ -443,14 +441,6 @@ void FGAIFlightPlan::restart() wpt_iterator = waypoints.begin(); } - -void FGAIFlightPlan::deleteTaxiRoute() -{ - delete taxiRoute; - taxiRoute = 0; -} - - int FGAIFlightPlan::getRouteIndex(int i) { if ((i > 0) && (i < (int)waypoints.size())) { return waypoints[i]->getRouteIndex(); @@ -459,8 +449,7 @@ int FGAIFlightPlan::getRouteIndex(int i) { return 0; } - -double FGAIFlightPlan::checkTrackLength(string wptName) { +double FGAIFlightPlan::checkTrackLength(const string& wptName) const { // skip the first two waypoints: first one is behind, second one is partially done; double trackDistance = 0; wpt_vector_iterator wptvec = waypoints.begin(); @@ -483,3 +472,13 @@ void FGAIFlightPlan::shortenToFirst(unsigned int number, string name) } (waypoints.back())->setName((waypoints.back())->getName() + name); } + +void FGAIFlightPlan::setGate(const ParkingAssignment& pka) +{ + gate = pka; +} + +FGParking* FGAIFlightPlan::getParkingGate() +{ + return gate.parking(); +} diff --git a/src/AIModel/AIFlightPlan.hxx b/src/AIModel/AIFlightPlan.hxx index aef8648..7c9881a 100644 --- a/src/AIModel/AIFlightPlan.hxx +++ b/src/AIModel/AIFlightPlan.hxx @@ -14,28 +14,32 @@ // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. #ifndef _FG_AIFLIGHTPLAN_HXX #define _FG_AIFLIGHTPLAN_HXX -#include #include #include +#include +#include +#include +#include +#include +// forward decls class FGTaxiRoute; class FGRunway; class FGAIAircraft; class FGAirport; -class SGGeod; + +typedef SGSharedPtr FGAirportRef; class FGAIWaypoint { private: std::string name; - double latitude; - double longitude; - double altitude; + SGGeod pos; double speed; double crossat; bool finished; @@ -50,10 +54,11 @@ private: public: FGAIWaypoint(); ~FGAIWaypoint() {}; - void setName (std::string nam) { name = nam; }; - void setLatitude (double lat) { latitude = lat; }; - void setLongitude (double lon) { longitude = lon; }; - void setAltitude (double alt) { altitude = alt; }; + void setName (const std::string& nam) { name = nam; }; + void setLatitude (double lat); + void setLongitude (double lon); + void setAltitude (double alt); + void setPos (const SGGeod& aPos) { pos = aPos; } void setSpeed (double spd) { speed = spd; }; void setCrossat (double val) { crossat = val; }; void setFinished (bool fin) { finished = fin; }; @@ -63,14 +68,15 @@ public: void setRouteIndex (int rte) { routeIndex = rte; }; void setTime_sec (double ts ) { time_sec = ts; }; void setTrackLength (double tl ) { trackLength = tl; }; - void setTime (std::string tme) { time = tme; }; + void setTime (const std::string& tme) { time = tme; }; - bool contains(std::string name); + bool contains(const std::string& name); - std::string getName () { return name; }; - double getLatitude () { return latitude; }; - double getLongitude () { return longitude; }; - double getAltitude () { return altitude; }; + const std::string& getName() { return name; }; + const SGGeod& getPos () { return pos; }; + double getLatitude (); + double getLongitude (); + double getAltitude (); double getSpeed () { return speed; }; double getCrossat () { return crossat; }; @@ -81,7 +87,7 @@ public: bool isFinished () { return finished; }; double getTime_sec () { return time_sec; }; double getTrackLength() { return trackLength; }; - std::string getTime () { return time; }; + const std::string& getTime () { return time; }; }; @@ -122,20 +128,20 @@ public: void setLeadDistance(double distance_ft); double getLeadDistance( void ) const {return lead_distance;} double getBearing(FGAIWaypoint* previous, FGAIWaypoint* next) const; - double getBearing(double lat, double lon, FGAIWaypoint* next) const; - double checkTrackLength(std::string wptName); + double getBearing(const SGGeod& aPos, FGAIWaypoint* next) const; + + double checkTrackLength(const std::string& wptName) const; time_t getStartTime() const { return start_time; } time_t getArrivalTime() const { return arrivalTime; } bool create(FGAIAircraft *, FGAirport *dep, FGAirport *arr, int leg, double alt, double speed, double lat, double lon, bool firstLeg, double radius, const std::string& fltType, const std::string& aircraftType, const std::string& airline, double distance); - bool createPushBack(FGAIAircraft *, bool, FGAirport*, double, double, double, const std::string&, const std::string&, const std::string&); + bool createPushBack(FGAIAircraft *, bool, FGAirport*, double radius, const std::string&, const std::string&, const std::string&); bool createTakeOff(FGAIAircraft *, bool, FGAirport *, double, const std::string&); void setLeg(int val) { leg = val;} void setTime(time_t st) { start_time = st; } - int getGate() const { return gateId; } - void setGate(int id) { gateId = id; }; + double getLeadInAngle() const { return leadInAngle; } const std::string& getRunway() const; @@ -144,21 +150,21 @@ public: bool getRepeat(void) const { return repeat; } void restart(void); int getNrOfWayPoints() { return waypoints.size(); } - int getRouteIndex(int i); // returns the AI related index of this current routes. - FGTaxiRoute *getTaxiRoute() { return taxiRoute; } - void deleteTaxiRoute(); - std::string getRunway() { return activeRunway; } + + int getRouteIndex(int i); // returns the AI related index of this current routes. + + const std::string& getRunway() { return activeRunway; } bool isActive(time_t time) {return time >= this->getStartTime();} void incrementLeg() { leg++;}; - void setRunway(std::string rwy) { activeRunway = rwy; }; - std::string getRunwayClassFromTrafficType(std::string fltType); + void setRunway(const std::string& rwy) { activeRunway = rwy; }; + const char* getRunwayClassFromTrafficType(const std::string& fltType); void addWaypoint(FGAIWaypoint* wpt) { waypoints.push_back(wpt); }; - void setName(std::string n) { name = n; }; - std::string getName() { return name; }; + void setName(const std::string& n) { name = n; }; + const std::string& getName() { return name; }; void setSID(FGAIFlightPlan* fp) { sid = fp;}; FGAIFlightPlan* getSID() { return sid; }; @@ -167,6 +173,9 @@ public: void shortenToFirst(unsigned int number, std::string name); + void setGate(const ParkingAssignment& pka); + FGParking* getParkingGate(); + private: FGAIFlightPlan *sid; typedef std::vector wpt_vector_type; @@ -183,14 +192,15 @@ private: time_t start_time; time_t arrivalTime; // For AI/ATC purposes. int leg; - int gateId, lastNodeVisited; + ParkingAssignment gate; + PositionedID lastNodeVisited; std::string activeRunway; - FGTaxiRoute *taxiRoute; std::string name; bool isValid; - - void createPushBackFallBack(FGAIAircraft *, bool, FGAirport*, double, double, double, const std::string&, const std::string&, const std::string&); - bool createClimb(FGAIAircraft *, bool, FGAirport *, double, double, const std::string&); + FGAirportRef departure, arrival; + + void createPushBackFallBack(FGAIAircraft *, bool, FGAirport*, double radius, const std::string&, const std::string&, const std::string&); + bool createClimb(FGAIAircraft *, bool, FGAirport *, FGAirport* arrival, double, double, const std::string&); bool createCruise(FGAIAircraft *, bool, FGAirport*, FGAirport*, double, double, double, double, const std::string&); bool createDescent(FGAIAircraft *, FGAirport *, double latitude, double longitude, double speed, double alt,const std::string&, double distance); bool createLanding(FGAIAircraft *, FGAirport *, const std::string&); @@ -215,6 +225,28 @@ private: //void createCruiseFallback(bool, FGAirport*, FGAirport*, double, double, double, double); void evaluateRoutePart(double deplat, double deplon, double arrlat, double arrlon); + + /** + * look for and parse an PropertyList flight-plan file - essentially + * a flat list waypoint objects, encoded to properties + */ + bool parseProperties(const std::string& filename); + + void createWaypoints(FGAIAircraft *ac, + double course, + time_t start, + FGAirport *dep, + FGAirport *arr, + bool firstLeg, + double radius, + double alt, + double lat, + double lon, + double speed, + const std::string& fltType, + const std::string& acType, + const std::string& airline); + public: wpt_vector_iterator getFirstWayPoint() { return waypoints.begin(); }; wpt_vector_iterator getLastWayPoint() { return waypoints.end(); }; diff --git a/src/AIModel/AIFlightPlanCreate.cxx b/src/AIModel/AIFlightPlanCreate.cxx index 5eb04f2..b19099d 100644 --- a/src/AIModel/AIFlightPlanCreate.cxx +++ b/src/AIModel/AIFlightPlanCreate.cxx @@ -22,6 +22,7 @@ # include #endif +#include #include "AIFlightPlan.hxx" #include @@ -37,6 +38,7 @@ #include #include #include +#include /* FGAIFlightPlan::create() @@ -61,7 +63,7 @@ bool FGAIFlightPlan::create(FGAIAircraft * ac, FGAirport * dep, int currWpt = wpt_iterator - waypoints.begin(); switch (legNr) { case 1: - retVal = createPushBack(ac, firstFlight, dep, latitude, longitude, + retVal = createPushBack(ac, firstFlight, dep, radius, fltType, aircraftType, airline); // Pregenerate the taxi leg. //if (retVal) { @@ -77,7 +79,7 @@ bool FGAIFlightPlan::create(FGAIAircraft * ac, FGAirport * dep, retVal = createTakeOff(ac, firstFlight, dep, speed, fltType); break; case 4: - retVal = createClimb(ac, firstFlight, dep, speed, alt, fltType); + retVal = createClimb(ac, firstFlight, dep, arr, speed, alt, fltType); break; case 5: retVal = createCruise(ac, firstFlight, dep, arr, latitude, longitude, speed, @@ -101,6 +103,7 @@ bool FGAIFlightPlan::create(FGAIAircraft * ac, FGAirport * dep, SG_LOG(SG_AI, SG_ALERT, "AIFlightPlan::create() attempting to create unknown leg" " this is probably an internal program error"); + break; } wpt_iterator = waypoints.begin() + currWpt; //don't increment leg right away, but only once we pass the actual last waypoint that was created. @@ -144,6 +147,7 @@ FGAIWaypoint * FGAIFlightPlan::createInAir(FGAIAircraft * ac, wpt->setGear_down (false ); wpt->setFlaps_down (false ); wpt->setOn_ground (false ); + wpt->setCrossat (aElev ); return wpt; } @@ -205,25 +209,24 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight, const string & acType, const string & airline) { - double heading, lat, lon; // If this function is called during initialization, // make sure we obtain a valid gate ID first // and place the model at the location of the gate. - if (firstFlight) { - if (!(apt->getDynamics()->getAvailableParking(&lat, &lon, - &heading, &gateId, - radius, fltType, - acType, airline))) { - SG_LOG(SG_AI, SG_WARN, "Could not find parking for a " << - acType << - " of flight type " << fltType << - " of airline " << airline << - " at airport " << apt->getId()); - } + if (firstFlight) + { + gate = apt->getDynamics()->getAvailableParking(radius, fltType, + acType, airline); + if (!gate.isValid()) { + SG_LOG(SG_AI, SG_WARN, "Could not find parking for a " << + acType << + " of flight type " << fltType << + " of airline " << airline << + " at airport " << apt->getId()); + } } - string rwyClass = getRunwayClassFromTrafficType(fltType); + const string& rwyClass = getRunwayClassFromTrafficType(fltType); // Only set this if it hasn't been set by ATC already. if (activeRunway.empty()) { @@ -243,7 +246,7 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight, } intVec ids; - int runwayId = 0; + PositionedID runwayId = 0; if (gn->getVersion() > 0) { runwayId = gn->findNearestNodeOnRunway(runwayTakeoff); } else { @@ -254,75 +257,74 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight, // fallback mechanism for this. // Starting from gate 0 in this case is a bit of a hack // which requires a more proper solution later on. - delete taxiRoute; - taxiRoute = new FGTaxiRoute; + // delete taxiRoute; + // taxiRoute = new FGTaxiRoute; // Determine which node to start from. - int node = 0; + PositionedID node = 0; // Find out which node to start from - FGParking *park = apt->getDynamics()->getParking(gateId); + FGParking *park = gate.parking(); if (park) { node = park->getPushBackPoint(); - } - - if (node == -1) { - node = gateId; - } - // HAndle case where parking doens't have a node - if ((node == 0) && park) { - if (firstFlight) { - node = gateId; - } else { - node = lastNodeVisited; + if (node == -1) { + node = park->guid(); + } else if (node == 0) { + // HAndle case where parking doens't have a node + if (firstFlight) { + node = park->guid(); + } else { + node = lastNodeVisited; + } } } - - *taxiRoute = gn->findShortestRoute(node, runwayId); + + FGTaxiRoute taxiRoute = gn->findShortestRoute(node, runwayId); intVecIterator i; - if (taxiRoute->empty()) { + if (taxiRoute.empty()) { createDefaultTakeoffTaxi(ac, apt, rwy); return true; } - taxiRoute->first(); + taxiRoute.first(); //bool isPushBackPoint = false; if (firstFlight) { // If this is called during initialization, randomly // skip a number of waypoints to get a more realistic // taxi situation. - int nrWaypointsToSkip = rand() % taxiRoute->size(); + int nrWaypointsToSkip = rand() % taxiRoute.size(); // but make sure we always keep two active waypoints // to prevent a segmentation fault for (int i = 0; i < nrWaypointsToSkip - 3; i++) { - taxiRoute->next(&node); + taxiRoute.next(&node); } - apt->getDynamics()->releaseParking(gateId); + + gate.release(); // free up our gate as required } else { - if (taxiRoute->size() > 1) { - taxiRoute->next(&node); // chop off the first waypoint, because that is already the last of the pushback route + if (taxiRoute.size() > 1) { + taxiRoute.next(&node); // chop off the first waypoint, because that is already the last of the pushback route } } // push each node on the taxi route as a waypoint - int route; + // int route; //cerr << "Building taxi route" << endl; - while (taxiRoute->next(&node, &route)) { + while (taxiRoute.next(&node)) { char buffer[10]; - snprintf(buffer, 10, "%d", node); + snprintf(buffer, 10, "%lld", (long long int) node); FGTaxiNode *tn = apt->getDynamics()->getGroundNetwork()->findNode(node); FGAIWaypoint *wpt = - createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(), + createOnGround(ac, buffer, tn->geod(), apt->getElevation(), ac->getPerformance()->vTaxi()); - wpt->setRouteIndex(route); + // wpt->setRouteIndex(route); //cerr << "Nodes left " << taxiRoute->nodesLeft() << " "; - if (taxiRoute->nodesLeft() == 1) { + if (taxiRoute.nodesLeft() == 1) { // Note that we actually have hold points in the ground network, but this is just an initial test. //cerr << "Setting departurehold point: " << endl; wpt->setName( wpt->getName() + string("DepartureHold")); } - if (taxiRoute->nodesLeft() == 0) { + if (taxiRoute.nodesLeft() == 0) { wpt->setName(wpt->getName() + string("Accel")); } pushBackWaypoint(wpt); @@ -354,12 +356,11 @@ void FGAIFlightPlan::createDefaultLandingTaxi(FGAIAircraft * ac, ac->getPerformance()->vTaxi()); pushBackWaypoint(wpt); - double heading, lat, lon; - aAirport->getDynamics()->getParking(gateId, &lat, &lon, &heading); - wpt = - createOnGround(ac, "ENDtaxi", SGGeod::fromDeg(lon, lat), airportElev, - ac->getPerformance()->vTaxi()); - pushBackWaypoint(wpt); + if (gate.isValid()) { + wpt = createOnGround(ac, "ENDtaxi", gate.parking()->geod(), airportElev, + ac->getPerformance()->vTaxi()); + pushBackWaypoint(wpt); + } } bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt, @@ -368,14 +369,10 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt, const string & acType, const string & airline) { - double heading, lat, lon; - apt->getDynamics()->getAvailableParking(&lat, &lon, &heading, - &gateId, radius, fltType, + gate = apt->getDynamics()->getAvailableParking(radius, fltType, acType, airline); - SGGeod lastWptPos = - SGGeod::fromDeg(waypoints.back()->getLongitude(), - waypoints.back()->getLatitude()); + SGGeod lastWptPos = waypoints.back()->getPos(); FGGroundNetwork *gn = apt->getDynamics()->getGroundNetwork(); // Find a route from runway end to parking/gate. @@ -385,7 +382,7 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt, } intVec ids; - int runwayId = 0; + PositionedID runwayId = 0; if (gn->getVersion() == 1) { runwayId = gn->findNearestNodeOnRunway(lastWptPos); } else { @@ -396,39 +393,56 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt, // fallback mechanism for this. // Starting from gate 0 is a bit of a hack... //FGTaxiRoute route; - delete taxiRoute; - taxiRoute = new FGTaxiRoute; - if (gateId >= 0) - *taxiRoute = gn->findShortestRoute(runwayId, gateId); + // delete taxiRoute; + // taxiRoute = new FGTaxiRoute; + FGTaxiRoute taxiRoute; + if (gate.isValid()) + taxiRoute = gn->findShortestRoute(runwayId, gate.parking()->guid()); else - *taxiRoute = gn->findShortestRoute(runwayId, 0); + taxiRoute = gn->findShortestRoute(runwayId, 0); intVecIterator i; - if (taxiRoute->empty()) { + if (taxiRoute.empty()) { createDefaultLandingTaxi(ac, apt); return true; } - int node; - taxiRoute->first(); - int size = taxiRoute->size(); + PositionedID node; + taxiRoute.first(); + int size = taxiRoute.size(); // Omit the last two waypoints, as // those are created by createParking() - int route; + // int route; for (int i = 0; i < size - 2; i++) { - taxiRoute->next(&node, &route); + taxiRoute.next(&node); char buffer[10]; - snprintf(buffer, 10, "%d", node); + snprintf(buffer, 10, "%lld", (long long int) node); FGTaxiNode *tn = gn->findNode(node); FGAIWaypoint *wpt = - createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(), + createOnGround(ac, buffer, tn->geod(), apt->getElevation(), ac->getPerformance()->vTaxi()); - wpt->setRouteIndex(route); + // wpt->setRouteIndex(route); pushBackWaypoint(wpt); } return true; } +static double accelDistance(double v0, double v1, double accel) +{ + double t = fabs(v1 - v0) / accel; // time in seconds to change velocity + // area under the v/t graph: (t * v0) + (dV / 2t) where (dV = v1 - v0) + return t * 0.5 * (v1 + v0); +} + +// find the horizontal distance to gain the specific altiude, holding +// a constant pitch angle. Used to compute distance based on standard FD/AP +// PITCH mode prior to VS or CLIMB engaging. Visually, we want to avoid +// a dip in the nose angle after rotation, during initial climb-out. +static double pitchDistance(double pitchAngleDeg, double altGainM) +{ + return altGainM / tan(pitchAngleDeg * SG_DEGREES_TO_RADIANS); +} + /******************************************************************* * CreateTakeOff * A note on units: @@ -444,27 +458,20 @@ bool FGAIFlightPlan::createTakeOff(FGAIAircraft * ac, bool firstFlight, FGAirport * apt, double speed, const string & fltType) { + const double ACCEL_POINT = 105.0; + // climb-out angle in degrees. could move this to the perf-db but this + // value is pretty sane + const double INITIAL_PITCH_ANGLE = 10.0; + double accel = ac->getPerformance()->acceleration(); double vTaxi = ac->getPerformance()->vTaxi(); double vRotate = ac->getPerformance()->vRotate(); double vTakeoff = ac->getPerformance()->vTakeoff(); - //double vClimb = ac->getPerformance()->vClimb(); - - double accelMetric = (accel * SG_NM_TO_METER) / 3600; - double vTaxiMetric = (vTaxi * SG_NM_TO_METER) / 3600; - double vRotateMetric = (vRotate * SG_NM_TO_METER) / 3600; - double vTakeoffMetric = (vTakeoff * SG_NM_TO_METER) / 3600; - //double vClimbMetric = (vClimb * SG_NM_TO_METER) / 3600; - // Acceleration = dV / dT - // Acceleration X dT = dV - // dT = dT / Acceleration - //d = (Vf^2 - Vo^2) / (2*a) - //double accelTime = (vRotate - vTaxi) / accel; - //cerr << "Using " << accelTime << " as total acceleration time" << endl; - double accelDistance = - (vRotateMetric * vRotateMetric - - vTaxiMetric * vTaxiMetric) / (2 * accelMetric); - //cerr << "Using " << accelDistance << " " << accelMetric << " " << vRotateMetric << endl; + + double accelMetric = accel * SG_KT_TO_MPS; + double vTaxiMetric = vTaxi * SG_KT_TO_MPS; + double vRotateMetric = vRotate * SG_KT_TO_MPS; + FGAIWaypoint *wpt; // Get the current active runway, based on code from David Luff // This should actually be unified and extended to include @@ -472,46 +479,50 @@ bool FGAIFlightPlan::createTakeOff(FGAIAircraft * ac, bool firstFlight, // NOTE: DT (2009-01-18: IIRC, this is currently already the case, // because the getActive runway function takes care of that. if (firstFlight) { - string rwyClass = getRunwayClassFromTrafficType(fltType); + const string& rwyClass = getRunwayClassFromTrafficType(fltType); double heading = ac->getTrafficRef()->getCourse(); apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, heading); } + FGRunway * rwy = apt->getRunwayByIdent(activeRunway); assert( rwy != NULL ); - double airportElev = apt->getElevation(); - - accelDistance = - (vTakeoffMetric * vTakeoffMetric - - vTaxiMetric * vTaxiMetric) / (2 * accelMetric); - //cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl; - SGGeod accelPoint = rwy->pointOnCenterline(105.0 + accelDistance); + double d = accelDistance(vTaxiMetric, vRotateMetric, accelMetric) + ACCEL_POINT; + + SGGeod accelPoint = rwy->pointOnCenterline(d); wpt = createOnGround(ac, "rotate", accelPoint, airportElev, vTakeoff); pushBackWaypoint(wpt); - accelDistance = - ((vTakeoffMetric * 1.1) * (vTakeoffMetric * 1.1) - - vTaxiMetric * vTaxiMetric) / (2 * accelMetric); - //cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl; - accelPoint = rwy->pointOnCenterline(105.0 + accelDistance); - wpt = - createOnGround(ac, "rotate", accelPoint, airportElev + 1000, - vTakeoff * 1.1); + double vRef = vTakeoff + 20; // climb-out at v2 + 20kts + + double gearUpDist = d + pitchDistance(INITIAL_PITCH_ANGLE, 400 * SG_FEET_TO_METER); + accelPoint = rwy->pointOnCenterline(gearUpDist); + + wpt = cloneWithPos(ac, wpt, "gear-up", accelPoint); + wpt->setSpeed(vRef); + wpt->setCrossat(airportElev + 400); wpt->setOn_ground(false); + wpt->setGear_down(false); pushBackWaypoint(wpt); - - wpt = cloneWithPos(ac, wpt, "3000 ft", rwy->end()); - wpt->setAltitude(airportElev + 3000); + + // limit climbout speed to 240kts below 10000' + double vClimbBelow10000 = std::min(240.0, ac->getPerformance()->vClimb()); + + // create two climb-out points. This is important becuase the first climb point will + // be a (sometimes large) turn towards the destination, and we don't want to + // commence that turn below 2000' + double climbOut = d + pitchDistance(INITIAL_PITCH_ANGLE, 2000 * SG_FEET_TO_METER); + accelPoint = rwy->pointOnCenterline(climbOut); + wpt = createInAir(ac, "2000'", accelPoint, airportElev + 2000, vClimbBelow10000); pushBackWaypoint(wpt); - - // Finally, add two more waypoints, so that aircraft will remain under - // Tower control until they have reached the 3000 ft climb point - SGGeod pt = rwy->pointOnCenterline(5000 + rwy->lengthM() * 0.5); - wpt = cloneWithPos(ac, wpt, "5000 ft", pt); - wpt->setAltitude(airportElev + 5000); + + climbOut = d + pitchDistance(INITIAL_PITCH_ANGLE, 2500 * SG_FEET_TO_METER); + accelPoint = rwy->pointOnCenterline(climbOut); + wpt = createInAir(ac, "2500'", accelPoint, airportElev + 2500, vClimbBelow10000); pushBackWaypoint(wpt); + return true; } @@ -520,16 +531,16 @@ bool FGAIFlightPlan::createTakeOff(FGAIAircraft * ac, bool firstFlight, * initialize the Aircraft at the parking location ******************************************************************/ bool FGAIFlightPlan::createClimb(FGAIAircraft * ac, bool firstFlight, - FGAirport * apt, double speed, double alt, + FGAirport * apt, FGAirport* arrival, + double speed, double alt, const string & fltType) { FGAIWaypoint *wpt; -// bool planLoaded = false; - string fPLName; + // string fPLName; double vClimb = ac->getPerformance()->vClimb(); - + if (firstFlight) { - string rwyClass = getRunwayClassFromTrafficType(fltType); + const string& rwyClass = getRunwayClassFromTrafficType(fltType); double heading = ac->getTrafficRef()->getCourse(); apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, heading); @@ -541,18 +552,23 @@ bool FGAIFlightPlan::createClimb(FGAIAircraft * ac, bool firstFlight, //cerr << " Cloning waypoint " << endl; } } else { - FGRunway * rwy = apt->getRunwayByIdent(activeRunway); - assert( rwy != NULL ); - - SGGeod climb1 = rwy->pointOnCenterline(10 * SG_NM_TO_METER); + FGRunway* runway = apt->getRunwayByIdent(activeRunway); + SGGeod cur = runway->end(); + if (!waypoints.empty()) { + cur = waypoints.back()->getPos(); + } + + // compute course towards destination + double course = SGGeodesy::courseDeg(cur, arrival->geod()); + + SGGeod climb1 = SGGeodesy::direct(cur, course, 10 * SG_NM_TO_METER); wpt = createInAir(ac, "10000ft climb", climb1, 10000, vClimb); wpt->setGear_down(true); wpt->setFlaps_down(true); pushBackWaypoint(wpt); - SGGeod climb2 = rwy->pointOnCenterline(20 * SG_NM_TO_METER); - wpt = cloneWithPos(ac, wpt, "18000ft climb", climb2); - wpt->setAltitude(18000); + SGGeod climb2 = SGGeodesy::direct(cur, course, 20 * SG_NM_TO_METER); + wpt = createInAir(ac, "18000ft climb", climb2, 18000, vClimb); pushBackWaypoint(wpt); } return true; @@ -575,11 +591,9 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, FGAIWaypoint *wpt; double vDescent = ac->getPerformance()->vDescent(); double vApproach = ac->getPerformance()->vApproach(); - double vTouchdown = ac->getPerformance()->vTouchdown(); - //Beginning of Descent - string rwyClass = getRunwayClassFromTrafficType(fltType); + const string& rwyClass = getRunwayClassFromTrafficType(fltType); double heading = ac->getTrafficRef()->getCourse(); apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading); @@ -622,21 +636,20 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, origin = current; } - double dAlt = 0; // = alt - (apt->getElevation() + 2000); FGTaxiNode * tn = 0; if (apt->getDynamics()->getGroundNetwork()) { int node = apt->getDynamics()->getGroundNetwork()->findNearestNode(refPoint); tn = apt->getDynamics()->getGroundNetwork()->findNode(node); } + if (tn) { - dAlt = alt - ((tn->getElevationFt(apt->getElevation())) + 2000); + dAlt = alt - ((tn->getElevationFt()) + 2000); } else { dAlt = alt - (apt->getElevation() + 2000); } - + double nPoints = 100; - char buffer[16]; // The descent path contains the following phases: @@ -650,11 +663,11 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, //cerr << "Distance : " << distance << endl; //cerr << "Azimuth : " << azimuth << endl; //cerr << "Initial Lateral point: " << lateralOffset << endl; - double lat = refPoint.getLatitudeDeg(); - double lon = refPoint.getLongitudeDeg(); +// double lat = refPoint.getLatitudeDeg(); +// double lon = refPoint.getLongitudeDeg(); //cerr << "Reference point (" << lat << ", " << lon << ")." << endl; - lat = initialTarget.getLatitudeDeg(); - lon = initialTarget.getLongitudeDeg(); +// lat = initialTarget.getLatitudeDeg(); +// lon = initialTarget.getLongitudeDeg(); //cerr << "Initial Target point (" << lat << ", " << lon << ")." << endl; double ratio = initialTurnRadius / distance; @@ -723,8 +736,8 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, azimuth = SGGeodesy::courseDeg(origin, secondaryTarget); - lat = secondaryTarget.getLatitudeDeg(); - lon = secondaryTarget.getLongitudeDeg(); +// lat = secondaryTarget.getLatitudeDeg(); +// lon = secondaryTarget.getLongitudeDeg(); //cerr << "Secondary Target point (" << lat << ", " << lon << ")." << endl; //cerr << "Distance : " << distance << endl; //cerr << "Azimuth : " << azimuth << endl; @@ -748,8 +761,8 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, SGGeodesy::direct(origin, azimuth, newDistance, tertiaryTarget, dummyAz2); - lat = tertiaryTarget.getLatitudeDeg(); - lon = tertiaryTarget.getLongitudeDeg(); +// lat = tertiaryTarget.getLatitudeDeg(); +// lon = tertiaryTarget.getLongitudeDeg(); //cerr << "tertiary Target point (" << lat << ", " << lon << ")." << endl; @@ -798,7 +811,7 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, //FGTaxiNode * tn = apt->getDynamics()->getGroundNetwork()->findNearestNode(initialTarget); double currentAltitude = 0; if (tn) { - currentAltitude = (tn->getElevationFt(apt->getElevation())) + 2000; + currentAltitude = (tn->getElevationFt()) + 2000; } else { currentAltitude = apt->getElevation() + 2000; } @@ -822,34 +835,7 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, // The approach leg should bring the aircraft to approximately 4-6 nm out, after which the landing phase should take over. //cerr << "Phase 3: Approach" << endl; - double tgt_speed = vApproach; - distanceOut -= distanceCovered; - double touchDownPoint = 0; //(rwy->lengthM() * 0.1); - for (int i = 1; i < nPoints; i++) { - SGGeod result; - double currentDist = i * (distanceOut / nPoints); - //double currentAltitude = - // apt->getElevation() + 2000 - (i * 2000 / (nPoints-1)); - double alt = currentAltitude - (i * 2000 / (nPoints - 1)); - snprintf(buffer, 16, "final%03d", i); - result = rwy->pointOnCenterline((-distanceOut) + currentDist + touchDownPoint); - if (i == nPoints - 30) { - tgt_speed = vTouchdown; - } - wpt = createInAir(ac, buffer, result, alt, tgt_speed); - wpt->setCrossat(alt); - wpt->setTrackLength((distanceOut / nPoints)); - // account for the extra distance due to an extended downwind leg - if (i == 1) { - wpt->setTrackLength(wpt->getTrackLength() + distanceCovered); - } - //cerr << "Track Length : " << wpt->trackLength; - pushBackWaypoint(wpt); - //if (apt->ident() == fgGetString("/sim/presets/airport-id")) { - // cerr << " Position : " << result.getLatitudeDeg() << " " << result.getLongitudeDeg() << " " << currentAltitude << " " << apt->getElevation() << " " << distanceOut << endl; - //} - } - + //cerr << "Done" << endl; // Erase the two bogus BOD points: Note check for conflicts with scripted AI flightPlans @@ -859,7 +845,7 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, if (reposition) { double tempDistance; //double minDistance = HUGE_VAL; - string wptName; + //string wptName; tempDistance = SGGeodesy::distanceM(current, initialTarget); time_t eta = tempDistance / ((vDescent * SG_NM_TO_METER) / 3600.0) + now; @@ -880,10 +866,31 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, ac->resetPositionFromFlightPlan(); } waypoints[1]->setName( (waypoints[1]->getName() + string("legend"))); - waypoints.back()->setName(waypoints.back()->getName() + "LandingThreshold"); return true; } +/** + * compute the distance along the centerline, to the ILS glideslope + * transmitter. Return -1 if there's no GS for the runway + */ +static double runwayGlideslopeTouchdownDistance(FGRunway* rwy) +{ + FGNavRecord* gs = rwy->glideslope(); + if (!gs) { + return -1; + } + + SGVec3d runwayPosCart = SGVec3d::fromGeod(rwy->pointOnCenterline(0.0)); + // compute a unit vector in ECF cartesian space, from the runway beginning to the end + SGVec3d runwayDirectionVec = normalize(SGVec3d::fromGeod(rwy->end()) - runwayPosCart); + SGVec3d gsTransmitterVec = gs->cart() - runwayPosCart; + +// project the gsTransmitterVec along the runwayDirctionVec to get out +// final value (in metres) + double dist = dot(runwayDirectionVec, gsTransmitterVec); + return dist; +} + /******************************************************************* * CreateLanding * Create a flight path from the "permision to land" point (currently @@ -896,110 +903,82 @@ bool FGAIFlightPlan::createLanding(FGAIAircraft * ac, FGAirport * apt, { double vTouchdown = ac->getPerformance()->vTouchdown(); double vTaxi = ac->getPerformance()->vTaxi(); - double decel = ac->getPerformance()->deceleration() * 1.4; - - double vTouchdownMetric = (vTouchdown * SG_NM_TO_METER) / 3600; - double vTaxiMetric = (vTaxi * SG_NM_TO_METER) / 3600; - double decelMetric = (decel * SG_NM_TO_METER) / 3600; - - //string rwyClass = getRunwayClassFromTrafficType(fltType); - //double heading = ac->getTrafficRef()->getCourse(); - //apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading); - //rwy = apt->getRunwayByIdent(activeRunway); - + double decel = ac->getPerformance()->decelerationOnGround(); + double vApproach = ac->getPerformance()->vApproach(); + + double vTouchdownMetric = vTouchdown * SG_KT_TO_MPS; + double vTaxiMetric = vTaxi * SG_KT_TO_MPS; + double decelMetric = decel * SG_KT_TO_MPS; - FGAIWaypoint *wpt; - //double aptElev = apt->getElevation(); - double currElev = 0; char buffer[12]; FGRunway * rwy = apt->getRunwayByIdent(activeRunway); assert( rwy != NULL ); - SGGeod refPoint = rwy->pointOnCenterline(0); - FGTaxiNode *tn = 0; - if (apt->getDynamics()->getGroundNetwork()) { - int node = apt->getDynamics()->getGroundNetwork()->findNearestNode(refPoint); - tn = apt->getDynamics()->getGroundNetwork()->findNode(node); - } - if (tn) { - currElev = tn->getElevationFt(apt->getElevation()); - } else { - currElev = apt->getElevation(); + SGGeod threshold = rwy->threshold(); + double currElev = threshold.getElevationFt(); + + double touchdownDistance = runwayGlideslopeTouchdownDistance(rwy); + if (touchdownDistance < 0.0) { + double landingLength = rwy->lengthM() - (rwy->displacedThresholdM()); + // touchdown 25% of the way along the landing area + touchdownDistance = rwy->displacedThresholdM() + (landingLength * 0.25); } - - + SGGeod coord; - - - /*double distanceOut = rwy->lengthM() * .1; - double nPoints = 20; - for (int i = 1; i < nPoints; i++) { - snprintf(buffer, 12, "flare%d", i); - double currentDist = i * (distanceOut / nPoints); - double currentAltitude = apt->getElevation() + 20 - (i * 20 / nPoints); - coord = rwy->pointOnCenterline((currentDist * (i / nPoints))); - wpt = createInAir(ac, buffer, coord, currentAltitude, (vTouchdown)); - }*/ - double rolloutDistance = - (vTouchdownMetric * vTouchdownMetric - vTaxiMetric * vTaxiMetric) / (2 * decelMetric); - //cerr << " touchdown speed = " << vTouchdown << ". Rollout distance " << rolloutDistance << endl; + // find glideslope entry point, 2000' above touchdown elevation + double glideslopeEntry = -((2000 * SG_FEET_TO_METER) / tan(3.0)) + touchdownDistance; + FGAIWaypoint *wpt = createInAir(ac, "Glideslope begin", rwy->pointOnCenterline(glideslopeEntry), + currElev + 2000, vApproach); + pushBackWaypoint(wpt); + + // deceleration point, 500' above touchdown elevation - slow from approach speed + // to touchdown speed + double decelPoint = -((500 * SG_FEET_TO_METER) / tan(3.0)) + touchdownDistance; + wpt = createInAir(ac, "500' decel", rwy->pointOnCenterline(decelPoint), + currElev + 2000, vTouchdown); + pushBackWaypoint(wpt); + + // compute elevation above the runway start, based on a 3-degree glideslope + double heightAboveRunwayStart = touchdownDistance * + tan(3.0 * SG_DEGREES_TO_RADIANS) * SG_METER_TO_FEET; + wpt = createInAir(ac, "CrossThreshold", rwy->begin(), + heightAboveRunwayStart + currElev, vTouchdown); + pushBackWaypoint(wpt); + + double rolloutDistance = accelDistance(vTouchdownMetric, vTaxiMetric, decelMetric); + int nPoints = 50; for (int i = 1; i < nPoints; i++) { snprintf(buffer, 12, "landing03%d", i); - - coord = rwy->pointOnCenterline((rolloutDistance * ((double) i / (double) nPoints))); - wpt = createOnGround(ac, buffer, coord, currElev, 2*vTaxi); + double t = ((double) i) / nPoints; + coord = rwy->pointOnCenterline(touchdownDistance + (rolloutDistance * t)); + double vel = (vTouchdownMetric * (1.0 - t)) + (vTaxiMetric * t); + wpt = createOnGround(ac, buffer, coord, currElev, vel); wpt->setCrossat(currElev); pushBackWaypoint(wpt); } + wpt->setSpeed(vTaxi); - double mindist = 1.1 * rolloutDistance; - double maxdist = rwy->lengthM(); - //cerr << "Finding nearest exit" << endl; + double mindist = (1.1 * rolloutDistance) + touchdownDistance; + FGGroundNetwork *gn = apt->getDynamics()->getGroundNetwork(); - if (gn) { - double min = 0; - for (int i = ceil(mindist); i < floor(maxdist); i++) { - coord = rwy->pointOnCenterline(mindist); - int nodeId = 0; - if (gn->getVersion() > 0) { - nodeId = gn->findNearestNodeOnRunway(coord); - } else { - nodeId = gn->findNearestNode(coord); - } - if (tn) - tn = gn->findNode(nodeId); - else { - break; - } - - double dist = SGGeodesy::distanceM(coord, tn->getGeod()); - if (dist < (min + 0.75)) { - break; - } - min = dist; - } - if (tn) { - wpt = createOnGround(ac, buffer, tn->getGeod(), currElev, vTaxi); - pushBackWaypoint(wpt); - } + if (!gn) { + return true; + } + + coord = rwy->pointOnCenterline(mindist); + int nodeId = 0; + if (gn->getVersion() > 0) { + nodeId = gn->findNearestNodeOnRunway(coord, rwy); + } else { + nodeId = gn->findNearestNode(coord); + } + + FGTaxiNode* tn = gn->findNode(nodeId); + if (tn) { + wpt = createOnGround(ac, buffer, tn->geod(), currElev, vTaxi); + pushBackWaypoint(wpt); } - //cerr << "Done. " << endl; - - /* - //Runway Threshold - wpt = createOnGround(ac, "Threshold", rwy->threshold(), aptElev, vTouchdown); - wpt->crossat = apt->getElevation(); - pushBackWaypoint(wpt); - - // Roll-out - wpt = createOnGround(ac, "Center", rwy->geod(), aptElev, vTaxi*2); - pushBackWaypoint(wpt); - - SGGeod rollOut = rwy->pointOnCenterline(rwy->lengthM() * 0.9); - wpt = createOnGround(ac, "Roll Out", rollOut, aptElev, vTaxi); - wpt->crossat = apt->getElevation(); - pushBackWaypoint(wpt); - */ + return true; } @@ -1012,34 +991,32 @@ bool FGAIFlightPlan::createParking(FGAIAircraft * ac, FGAirport * apt, { FGAIWaypoint *wpt; double aptElev = apt->getElevation(); - double lat = 0.0, lat2 = 0.0; - double lon = 0.0, lon2 = 0.0; - double az2 = 0.0; - double heading = 0.0; - double vTaxi = ac->getPerformance()->vTaxi(); double vTaxiReduced = vTaxi * (2.0 / 3.0); - apt->getDynamics()->getParking(gateId, &lat, &lon, &heading); - heading += 180.0; - if (heading > 360) - heading -= 360; - geo_direct_wgs_84(0, lat, lon, heading, - 2.2 * radius, &lat2, &lon2, &az2); - wpt = - createOnGround(ac, "taxiStart", SGGeod::fromDeg(lon2, lat2), - aptElev, vTaxiReduced); + if (!gate.isValid()) { + wpt = createOnGround(ac, "END-Parking", apt->geod(), aptElev, + vTaxiReduced); + pushBackWaypoint(wpt); + return true; + } + + FGParking* parking = gate.parking(); + double heading = SGMiscd::normalizePeriodic(0, 360, parking->getHeading() + 180.0); + double az; // unused + SGGeod pos; + + SGGeodesy::direct(parking->geod(), heading, 2.2 * parking->getRadius(), + pos, az); + + wpt = createOnGround(ac, "taxiStart", pos, aptElev, vTaxiReduced); pushBackWaypoint(wpt); - geo_direct_wgs_84(0, lat, lon, heading, - 0.1 * radius, &lat2, &lon2, &az2); - - wpt = - createOnGround(ac, "taxiStart2", SGGeod::fromDeg(lon2, lat2), - aptElev, vTaxiReduced); + SGGeodesy::direct(parking->geod(), heading, 0.1 * parking->getRadius(), + pos, az); + wpt = createOnGround(ac, "taxiStart2", pos, aptElev, vTaxiReduced); pushBackWaypoint(wpt); - wpt = - createOnGround(ac, "END-Parking", SGGeod::fromDeg(lon, lat), aptElev, + wpt = createOnGround(ac, "END-Parking", parking->geod(), aptElev, vTaxiReduced); pushBackWaypoint(wpt); return true; @@ -1068,21 +1045,21 @@ bool FGAIFlightPlan::createParking(FGAIAircraft * ac, FGAirport * apt, * - ul (ultralight: I can imagine that these may share a runway with ga on some airports) * - mil (all military traffic) */ -string FGAIFlightPlan::getRunwayClassFromTrafficType(string fltType) +const char* FGAIFlightPlan::getRunwayClassFromTrafficType(const string& fltType) { if ((fltType == "gate") || (fltType == "cargo")) { - return string("com"); + return "com"; } if (fltType == "ga") { - return string("gen"); + return "gen"; } if (fltType == "ul") { - return string("ul"); + return "ul"; } if ((fltType == "mil-fighter") || (fltType == "mil-transport")) { - return string("mil"); + return "mil"; } - return string("com"); + return "com"; } diff --git a/src/AIModel/AIFlightPlanCreateCruise.cxx b/src/AIModel/AIFlightPlanCreateCruise.cxx index 8759219..15c3d64 100644 --- a/src/AIModel/AIFlightPlanCreateCruise.cxx +++ b/src/AIModel/AIFlightPlanCreateCruise.cxx @@ -14,7 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * **************************************************************************/ @@ -25,7 +25,6 @@ #include #include -#include #include #include @@ -296,7 +295,7 @@ bool FGAIFlightPlan::createCruise(FGAIAircraft *ac, bool firstFlight, FGAirport wpt = createInAir(ac, "Cruise", SGGeod::fromDeg(longitude, latitude), alt, vCruise); pushBackWaypoint(wpt); - string rwyClass = getRunwayClassFromTrafficType(fltType); + const string& rwyClass = getRunwayClassFromTrafficType(fltType); double heading = ac->getTrafficRef()->getCourse(); arr->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading); FGRunway* rwy = arr->getRunwayByIdent(activeRunway); diff --git a/src/AIModel/AIFlightPlanCreatePushBack.cxx b/src/AIModel/AIFlightPlanCreatePushBack.cxx index 708d71e..eeecfe7 100644 --- a/src/AIModel/AIFlightPlanCreatePushBack.cxx +++ b/src/AIModel/AIFlightPlanCreatePushBack.cxx @@ -22,6 +22,8 @@ # include #endif +#include + #include #include @@ -39,150 +41,115 @@ // TODO: Use James Turner's createOnGround functions. bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac, bool firstFlight, FGAirport *dep, - double latitude, - double longitude, double radius, const string& fltType, const string& aircraftType, const string& airline) { - double lat, lon, heading; double vTaxi = ac->getPerformance()->vTaxi(); double vTaxiBackward = vTaxi * (-2.0/3.0); double vTaxiReduced = vTaxi * (2.0/3.0); - FGTaxiRoute *pushBackRoute; // Active runway can be conditionally set by ATC, so at the start of a new flight, this // must be reset. activeRunway.clear(); if (!(dep->getDynamics()->getGroundNetwork()->exists())) { //cerr << "Push Back fallback" << endl; - createPushBackFallBack(ac, firstFlight, dep, latitude, longitude, + createPushBackFallBack(ac, firstFlight, dep, radius, fltType, aircraftType, airline); - } else { - if (firstFlight) { - - if (!(dep->getDynamics()->getAvailableParking(&lat, &lon, - &heading, &gateId, - radius, fltType, - aircraftType, airline))) { - SG_LOG(SG_AI, SG_WARN, "Warning: Could not find parking for a " << - aircraftType << - " of flight type " << fltType << - " of airline " << airline << - " at airport " << dep->getId()); - return false; - char buffer[10]; - snprintf (buffer, 10, "%d", gateId); - SGGeod coord = coord.fromDeg(lon, lat); - //FGTaxiNode *tn = dep->getDynamics()->getGroundNetwork()->findNode(node); - FGAIWaypoint *wpt = createOnGround(ac, string(buffer), coord, dep->getElevation(), vTaxiBackward); - wpt->setRouteIndex(-1); - pushBackWaypoint(wpt); - } - //cerr << "Success : GateId = " << gateId << endl; - SG_LOG(SG_AI, SG_WARN, "Warning: Succesfully found a parking for a " << - aircraftType << - " of flight type " << fltType << - " of airline " << airline << - " at airport " << dep->getId()); - } else { - //cerr << "Push Back follow-up Flight" << endl; - dep->getDynamics()->getParking(gateId, &lat, &lon, &heading); - } - if (gateId < 0) { - createPushBackFallBack(ac, firstFlight, dep, latitude, longitude, - radius, fltType, aircraftType, airline); - return true; + return true; + } + + // establish the parking position / gate if required + if (firstFlight) { + gate = dep->getDynamics()->getAvailableParking(radius, fltType, + aircraftType, airline); + if (!gate.isValid()) { + SG_LOG(SG_AI, SG_WARN, "Warning: Could not find parking for a " << + aircraftType << + " of flight type " << fltType << + " of airline " << airline << + " at airport " << dep->getId()); + return false; + } + } + + if (!gate.isValid()) { + createPushBackFallBack(ac, firstFlight, dep, + radius, fltType, aircraftType, airline); + return true; + } + + FGGroundNetwork* groundNet = dep->getDynamics()->getGroundNetwork(); + FGParking *parking = gate.parking(); + if (parking && parking->getPushBackPoint() > 0) { + FGTaxiRoute route = groundNet->findShortestRoute(parking->guid(), parking->getPushBackPoint(), false); + + int size = route.size(); + if (size < 2) { + SG_LOG(SG_AI, SG_ALERT, "Push back route from gate " << parking->ident() << " has only " << size << " nodes."); + SG_LOG(SG_AI, SG_ALERT, "Using " << parking->getPushBackPoint()); } - //cerr << "getting parking " << gateId; - //cerr << " for a " << - // aircraftType << - // " of flight type " << fltType << - // " of airline " << airline << - // " at airport " << dep->getId() << endl; - FGParking *parking = dep->getDynamics()->getParking(gateId); - int pushBackNode = parking->getPushBackPoint(); - - - pushBackRoute = parking->getPushBackRoute(); - if ((pushBackNode > 0) && (pushBackRoute == 0)) { // Load the already established route for this gate - int node, rte; - FGTaxiRoute route; - //cerr << "Creating push-back for " << gateId << " (" << parking->getName() << ") using push-back point " << pushBackNode << endl; - route = dep->getDynamics()->getGroundNetwork()->findShortestRoute(gateId, pushBackNode, false); - parking->setPushBackRoute(new FGTaxiRoute(route)); - - - pushBackRoute = parking->getPushBackRoute(); - int size = pushBackRoute->size(); - if (size < 2) { - SG_LOG(SG_AI, SG_ALERT, "Push back route from gate " << gateId << " has only " << size << " nodes."); - SG_LOG(SG_AI, SG_ALERT, "Using " << pushBackNode); - } - pushBackRoute->first(); - while (pushBackRoute->next(&node, &rte)) - { - //FGTaxiNode *tn = apt->getDynamics()->getGroundNetwork()->findSegment(node)->getEnd(); - char buffer[10]; - snprintf (buffer, 10, "%d", node); - FGTaxiNode *tn = dep->getDynamics()->getGroundNetwork()->findNode(node); - //ids.pop_back(); - //wpt = new waypoint; - SGGeod coord = coord.fromDeg(tn->getLongitude(), tn->getLatitude()); - FGAIWaypoint *wpt = createOnGround(ac, string(buffer), coord, dep->getElevation(), vTaxiBackward); - - wpt->setRouteIndex(rte); - pushBackWaypoint(wpt); - } - // some special considerations for the last point: - waypoints.back()->setName(string("PushBackPoint")); - waypoints.back()->setSpeed(vTaxi); - ac->setTaxiClearanceRequest(true); - } else { // In case of a push forward departure... - ac->setTaxiClearanceRequest(false); - double lat2 = 0.0, lon2 = 0.0, az2 = 0.0; - - //cerr << "Creating final push forward point for gate " << gateId << endl; - FGTaxiNode *tn = dep->getDynamics()->getGroundNetwork()->findNode(gateId); - FGTaxiSegmentVectorIterator ts = tn->getBeginRoute(); - FGTaxiSegmentVectorIterator te = tn->getEndRoute(); - // if the starting node equals the ending node, then there aren't any routes for this parking. - // in cases like these we should flag the gate as being inoperative and return false - if (ts == te) { - SG_LOG(SG_AI, SG_ALERT, "Gate " << gateId << "doesn't seem to have routes associated with it."); - parking->setAvailable(false); - return false; + + route.first(); + PositionedID node, previous= 0; + + while (route.next(&node)) + { + char buffer[10]; + snprintf (buffer, 10, "%lld", (long long int) node); + FGTaxiNode *tn = groundNet->findNode(node); + FGAIWaypoint *wpt = createOnGround(ac, string(buffer), tn->geod(), dep->getElevation(), vTaxiBackward); + + if (previous) { + FGTaxiSegment* segment = groundNet->findSegment(previous, node); + wpt->setRouteIndex(segment->getIndex()); + } else { + // not on the route yet, make up a unique segment ID + int x = (int) tn->guid(); + wpt->setRouteIndex(x); } - tn = (*ts)->getEnd(); - lastNodeVisited = tn->getIndex(); - if (tn == NULL) { - SG_LOG(SG_AI, SG_ALERT, "No valid taxinode found"); - exit(1); - } - double distance = (*ts)->getLength(); - //cerr << "Length of push forward route = " << distance << " and heading is " << heading << endl; - lat2 = tn->getLatitude(); - lon2 = tn->getLongitude(); - - for (int i = 1; i < 10; i++) { - geo_direct_wgs_84 ( 0, lat, lon, heading, - ((i / 10.0) * distance), &lat2, &lon2, &az2 ); - char buffer[16]; - snprintf(buffer, 16, "pushback-%02d", i); - SGGeod coord = coord.fromDeg(lon2, lat2); - //cerr << i << endl; - FGAIWaypoint *wpt = createOnGround(ac, string(buffer), coord, dep->getElevation(), vTaxiReduced); - - wpt->setRouteIndex((*ts)->getIndex()); - pushBackWaypoint(wpt); - } - // cerr << "Done " << endl; - waypoints.back()->setName(string("PushBackPoint")); - // cerr << "Done assinging new name" << endl; + + pushBackWaypoint(wpt); + previous = node; } + // some special considerations for the last point: + waypoints.back()->setName(string("PushBackPoint")); + waypoints.back()->setSpeed(vTaxi); + ac->setTaxiClearanceRequest(true); + } else { // In case of a push forward departure... + ac->setTaxiClearanceRequest(false); + double az2 = 0.0; + + FGTaxiSegment* pushForwardSegment = dep->getDynamics()->getGroundNetwork()->findSegment(parking->guid(), 0); + // there aren't any routes for this parking. + if (!pushForwardSegment) { + SG_LOG(SG_AI, SG_ALERT, "Gate " << parking->ident() << "doesn't seem to have routes associated with it."); + return false; + } + + lastNodeVisited = pushForwardSegment->getEnd()->getIndex(); + double distance = pushForwardSegment->getLength(); + + double parkingHeading = parking->getHeading(); + + for (int i = 1; i < 10; i++) { + SGGeod pushForwardPt; + SGGeodesy::direct(parking->geod(), parkingHeading, + ((i / 10.0) * distance), pushForwardPt, az2); + char buffer[16]; + snprintf(buffer, 16, "pushback-%02d", i); + FGAIWaypoint *wpt = createOnGround(ac, string(buffer), pushForwardPt, dep->getElevation(), vTaxiReduced); + + wpt->setRouteIndex(pushForwardSegment->getIndex()); + pushBackWaypoint(wpt); + } + + waypoints.back()->setName(string("PushBackPoint")); + // cerr << "Done assinging new name" << endl; } + return true; } /******************************************************************* @@ -191,49 +158,29 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac, * network yet. ******************************************************************/ void FGAIFlightPlan::createPushBackFallBack(FGAIAircraft *ac, bool firstFlight, FGAirport *dep, - double latitude, - double longitude, double radius, const string& fltType, const string& aircraftType, const string& airline) { - double heading; - double lat; - double lon; - double lat2 = 0.0; - double lon2 = 0.0; double az2 = 0.0; double vTaxi = ac->getPerformance()->vTaxi(); double vTaxiBackward = vTaxi * (-2.0/3.0); double vTaxiReduced = vTaxi * (2.0/3.0); - - - dep->getDynamics()->getParking(-1, &lat, &lon, &heading); - - heading += 180.0; - if (heading > 360) - heading -= 360; - - SGGeod coord = coord.fromDeg(lon, lat); - FGAIWaypoint *wpt = createOnGround(ac, string("park"), coord, dep->getElevation(), vTaxiBackward); + double heading = 180.0; // this is a completely arbitrary heading! + FGAIWaypoint *wpt = createOnGround(ac, string("park"), dep->geod(), dep->getElevation(), vTaxiBackward); pushBackWaypoint(wpt); - geo_direct_wgs_84 ( 0, lat, lon, heading, - 10, - &lat2, &lon2, &az2 ); - coord = coord.fromDeg(lon2, lat2); + SGGeod coord; + SGGeodesy::direct(dep->geod(), heading, 10, coord, az2); wpt = createOnGround(ac, string("park2"), coord, dep->getElevation(), vTaxiBackward); pushBackWaypoint(wpt); - - geo_direct_wgs_84 ( 0, lat, lon, heading, - 2.2*radius, - &lat2, &lon2, &az2 ); - coord = coord.fromDeg(lon2, lat2); + + SGGeodesy::direct(dep->geod(), heading, 2.2 * radius, coord, az2); wpt = createOnGround(ac, string("taxiStart"), coord, dep->getElevation(), vTaxiReduced); pushBackWaypoint(wpt); diff --git a/src/AIModel/AIGroundVehicle.cxx b/src/AIModel/AIGroundVehicle.cxx index b39a1e1..b813b3c 100644 --- a/src/AIModel/AIGroundVehicle.cxx +++ b/src/AIModel/AIGroundVehicle.cxx @@ -24,7 +24,7 @@ #include -#include
+#include #include #include @@ -84,61 +84,40 @@ void FGAIGroundVehicle::readFromScenario(SGPropertyNode* scFileNode) { void FGAIGroundVehicle::bind() { FGAIShip::bind(); - props->tie("controls/constants/elevation-coeff", + tie("controls/constants/elevation-coeff", SGRawValuePointer(&_elevation_coeff)); - props->tie("controls/constants/pitch-coeff", + tie("controls/constants/pitch-coeff", SGRawValuePointer(&_pitch_coeff)); - props->tie("position/ht-AGL-ft", + tie("position/ht-AGL-ft", SGRawValuePointer(&_ht_agl_ft)); - props->tie("hitch/rel-bearing-deg", + tie("hitch/rel-bearing-deg", SGRawValuePointer(&_relbrg)); - props->tie("hitch/tow-angle-deg", + tie("hitch/tow-angle-deg", SGRawValuePointer(&_tow_angle)); - props->tie("hitch/range-ft", + tie("hitch/range-ft", SGRawValuePointer(&_range_ft)); - props->tie("hitch/x-offset-ft", + tie("hitch/x-offset-ft", SGRawValuePointer(&_x_offset)); - props->tie("hitch/y-offset-ft", + tie("hitch/y-offset-ft", SGRawValuePointer(&_y_offset)); - props->tie("hitch/z-offset-ft", + tie("hitch/z-offset-ft", SGRawValuePointer(&_z_offset)); - props->tie("hitch/parent-x-offset-ft", + tie("hitch/parent-x-offset-ft", SGRawValuePointer(&_parent_x_offset)); - props->tie("hitch/parent-y-offset-ft", + tie("hitch/parent-y-offset-ft", SGRawValuePointer(&_parent_y_offset)); - props->tie("hitch/parent-z-offset-ft", + tie("hitch/parent-z-offset-ft", SGRawValuePointer(&_parent_z_offset)); - props->tie("controls/constants/tow-angle/gain", + tie("controls/constants/tow-angle/gain", SGRawValuePointer(&_tow_angle_gain)); - props->tie("controls/constants/tow-angle/limit-deg", + tie("controls/constants/tow-angle/limit-deg", SGRawValuePointer(&_tow_angle_limit)); - props->tie("controls/contact-x1-offset-ft", + tie("controls/contact-x1-offset-ft", SGRawValuePointer(&_contact_x1_offset)); - props->tie("controls/contact-x2-offset-ft", + tie("controls/contact-x2-offset-ft", SGRawValuePointer(&_contact_x2_offset)); } -void FGAIGroundVehicle::unbind() { - FGAIShip::unbind(); - - props->untie("controls/constants/elevation-coeff"); - props->untie("controls/constants/pitch-coeff"); - props->untie("position/ht-AGL-ft"); - props->untie("hitch/rel-bearing-deg"); - props->untie("hitch/tow-angle-deg"); - props->untie("hitch/range-ft"); - props->untie("hitch/x-offset-ft"); - props->untie("hitch/y-offset-ft"); - props->untie("hitch/z-offset-ft"); - props->untie("hitch/parent-x-offset-ft"); - props->untie("hitch/parent-y-offset-ft"); - props->untie("hitch/parent-y-offset-ft"); - props->untie("controls/constants/tow-angle/gain"); - props->untie("controls/constants/tow-angle/limit-deg"); - props->untie("controls/contact-x1-offset-ft"); - props->untie("controls/contact-x2-offset-ft"); -} - bool FGAIGroundVehicle::init(bool search_in_AI_path) { if (!FGAIShip::init(search_in_AI_path)) return false; @@ -258,13 +237,13 @@ bool FGAIGroundVehicle::getPitch() { //double max_alt = 10000; if (globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(geodFront, 3000), - elev_front, &_material, 0)){ + elev_front, NULL, 0)){ front_elev_m = elev_front + _z_offset_m; } else return false; if (globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(geodRear, 3000), - elev_rear, &_material, 0)){ + elev_rear, NULL, 0)){ rear_elev_m = elev_rear; } else return false; diff --git a/src/AIModel/AIGroundVehicle.hxx b/src/AIModel/AIGroundVehicle.hxx index 08491ba..fcb822a 100644 --- a/src/AIModel/AIGroundVehicle.hxx +++ b/src/AIModel/AIGroundVehicle.hxx @@ -40,7 +40,6 @@ public: bool init(bool search_in_AI_path=false); virtual void bind(); - virtual void unbind(); virtual void reinit(); virtual void update (double dt); @@ -94,8 +93,6 @@ private: double _hitch_x_offset_m, _hitch_y_offset_m, _hitch_z_offset_m; double _dt_count, _next_run, _break_count; - const SGMaterial* _material; - }; #endif // FG_AIGROUNDVEHICLE_HXX diff --git a/src/AIModel/AIManager.cxx b/src/AIModel/AIManager.cxx index d2232fc..3c56172 100644 --- a/src/AIModel/AIManager.cxx +++ b/src/AIModel/AIManager.cxx @@ -19,15 +19,17 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include +#include +#include #include #include #include +#include +#include #include
- #include -#include #include "AIManager.hxx" #include "AIAircraft.hxx" @@ -49,30 +51,19 @@ FGAIManager::FGAIManager() : cb_ai_detailed(SGPropertyChangeCallback(this,&FGAIManager::updateLOD, fgGetNode("/sim/rendering/static-lod/ai-detailed", true))) { - _dt = 0.0; - mNumAiModels = 0; - for (unsigned i = 0; i < FGAIBase::MAX_OBJECTS; ++i) - mNumAiTypeModels[i] = 0; } -FGAIManager::~FGAIManager() { - ai_list_iterator ai_list_itr = ai_list.begin(); - - while(ai_list_itr != ai_list.end()) { - (*ai_list_itr)->unbind(); - ++ai_list_itr; - } +FGAIManager::~FGAIManager() +{ + std::for_each(ai_list.begin(), ai_list.end(), boost::mem_fn(&FGAIBase::unbind)); } void FGAIManager::init() { root = fgGetNode("sim/ai", true); - enabled = root->getNode("enabled", true)->getBoolValue(); - - if (!enabled) - return; + enabled = root->getNode("enabled", true); thermal_lift_node = fgGetNode("/environment/thermal-lift-fps", true); wind_from_east_node = fgGetNode("/environment/wind-from-east-fps",true); @@ -92,13 +83,22 @@ FGAIManager::init() { void FGAIManager::postinit() { // postinit, so that it can access the Nasal subsystem - map scenarios; + + if (!root->getBoolValue("scenarios-enabled", true)) + return; + + // scenarios enabled, AI subsystem required + if (!enabled->getBoolValue()) + enabled->setBoolValue(true); + + // process all scenarios + std::map scenarios; for (int i = 0 ; i < root->nChildren() ; i++) { SGPropertyNode *n = root->getChild(i); if (strcmp(n->getName(), "scenario")) continue; - string name = n->getStringValue(); + const string& name = n->getStringValue(); if (name.empty()) continue; @@ -114,15 +114,10 @@ FGAIManager::postinit() { } void -FGAIManager::reinit() { +FGAIManager::reinit() +{ update(0.0); - - ai_list_iterator ai_list_itr = ai_list.begin(); - - while(ai_list_itr != ai_list.end()) { - (*ai_list_itr)->reinit(); - ++ai_list_itr; - } + std::for_each(ai_list.begin(), ai_list.end(), boost::mem_fn(&FGAIBase::reinit)); } void @@ -137,51 +132,51 @@ FGAIManager::unbind() { root->untie("count"); } +void FGAIManager::removeDeadItem(FGAIBase* base) +{ + SGPropertyNode *props = base->_getProps(); + + props->setBoolValue("valid", false); + base->unbind(); + + // for backward compatibility reset properties, so that aircraft, + // which don't know the property, keep working + // TODO: remove after a while + props->setIntValue("id", -1); + props->setBoolValue("radar/in-range", false); + props->setIntValue("refuel/tanker", false); +} + void FGAIManager::update(double dt) { // initialize these for finding nearest thermals range_nearest = 10000.0; strength = 0.0; - if (!enabled) + if (!enabled->getBoolValue()) return; - FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("traffic-manager"); - _dt = dt; - - ai_list_iterator ai_list_itr = ai_list.begin(); - - while(ai_list_itr != ai_list.end()) { - - if ((*ai_list_itr)->getDie()) { - tmgr->release((*ai_list_itr)->getID()); - --mNumAiModels; - --(mNumAiTypeModels[(*ai_list_itr)->getType()]); - FGAIBase *base = (*ai_list_itr).get(); - SGPropertyNode *props = base->_getProps(); + fetchUserState(); - props->setBoolValue("valid", false); - base->unbind(); - - // for backward compatibility reset properties, so that aircraft, - // which don't know the property, keep working - // TODO: remove after a while - props->setIntValue("id", -1); - props->setBoolValue("radar/in-range", false); - props->setIntValue("refuel/tanker", false); - - ai_list_itr = ai_list.erase(ai_list_itr); + // partition the list into dead followed by alive + ai_list_iterator firstAlive = + std::stable_partition(ai_list.begin(), ai_list.end(), boost::mem_fn(&FGAIBase::getDie)); + + // clean up each item and finally remove from the container + for (ai_list_iterator it=ai_list.begin(); it != firstAlive; ++it) { + removeDeadItem(*it); + } + + ai_list.erase(ai_list.begin(), firstAlive); + + // every remaining item is alive + BOOST_FOREACH(FGAIBase* base, ai_list) { + if (base->isa(FGAIBase::otThermal)) { + processThermal(dt, (FGAIThermal*)base); } else { - fetchUserState(); - if ((*ai_list_itr)->isa(FGAIBase::otThermal)) { - FGAIBase *base = (*ai_list_itr).get(); - processThermal((FGAIThermal*)base); - } else { - (*ai_list_itr)->update(_dt); - } - ++ai_list_itr; + base->update(dt); } - } + } // of live AI objects iteration thermal_lift_node->setDoubleValue( strength ); // for thermals } @@ -190,18 +185,13 @@ FGAIManager::update(double dt) { void FGAIManager::updateLOD(SGPropertyNode* node) { - ai_list_iterator ai_list_itr = ai_list.begin(); - while(ai_list_itr != ai_list.end()) - { - (*ai_list_itr)->updateLOD(); - ++ai_list_itr; - } + SG_UNUSED(node); + std::for_each(ai_list.begin(), ai_list.end(), boost::mem_fn(&FGAIBase::updateLOD)); } void FGAIManager::attach(FGAIBase *model) { - //unsigned idx = mNumAiTypeModels[model->getType()]; const char* typeString = model->getTypeString(); SGPropertyNode* root = globals->get_props()->getNode("ai/models", true); SGPropertyNode* p; @@ -223,8 +213,7 @@ FGAIManager::attach(FGAIBase *model) p = root->getNode(typeString, i, true); model->setManager(this, p); ai_list.push_back(model); - ++mNumAiModels; - ++(mNumAiTypeModels[model->getType()]); + model->init(model->getType()==FGAIBase::otAircraft || model->getType()==FGAIBase::otMultiplayer || model->getType()==FGAIBase::otStatic); @@ -232,27 +221,10 @@ FGAIManager::attach(FGAIBase *model) p->setBoolValue("valid", true); } -void -FGAIManager::destroyObject( int ID ) { - ai_list_iterator ai_list_itr = ai_list.begin(); - - while(ai_list_itr != ai_list.end()) { - - if ((*ai_list_itr)->getID() == ID) { - --mNumAiModels; - --(mNumAiTypeModels[(*ai_list_itr)->getType()]); - (*ai_list_itr)->unbind(); - ai_list_itr = ai_list.erase(ai_list_itr); - } else - ++ai_list_itr; - } - -} - int FGAIManager::getNumAiObjects(void) const { - return mNumAiModels; + return ai_list.size(); } void @@ -274,8 +246,8 @@ FGAIManager::fetchUserState( void ) { // only keep the results from the nearest thermal void -FGAIManager::processThermal( FGAIThermal* thermal ) { - thermal->update(_dt); +FGAIManager::processThermal( double dt, FGAIThermal* thermal ) { + thermal->update(dt); if ( thermal->_getRange() < range_nearest ) { range_nearest = thermal->_getRange(); @@ -304,7 +276,7 @@ FGAIManager::processScenario( const string &filename ) { if (strcmp(scEntry->getName(), "entry")) continue; - std::string type = scEntry->getStringValue("type", "aircraft"); + const std::string& type = scEntry->getStringValue("type", "aircraft"); if (type == "tanker") { // refueling scenarios FGAITanker* tanker = new FGAITanker; @@ -378,8 +350,8 @@ FGAIManager::loadScenarioFile(const std::string& filename) } catch (const sg_exception &t) { SG_LOG(SG_AI, SG_ALERT, "Failed to load scenario '" << path.str() << "': " << t.getFormattedMessage()); - return 0; } + return 0; } bool @@ -394,16 +366,16 @@ FGAIManager::getStartPosition(const string& id, const string& pid, for (int i = 0 ; (!found) && i < root->nChildren() ; i++) { SGPropertyNode *aiEntry = root->getChild( i ); if ( !strcmp( aiEntry->getName(), "scenario" ) ) { - string filename = aiEntry->getStringValue(); + const string& filename = aiEntry->getStringValue(); SGPropertyNode_ptr scenarioTop = loadScenarioFile(filename); if (scenarioTop) { SGPropertyNode* scenarios = scenarioTop->getChild("scenario"); if (scenarios) { for (int i = 0; i < scenarios->nChildren(); i++) { SGPropertyNode* scEntry = scenarios->getChild(i); - std::string type = scEntry->getStringValue("type"); - std::string pnumber = scEntry->getStringValue("pennant-number"); - std::string name = scEntry->getStringValue("name"); + const std::string& type = scEntry->getStringValue("type"); + const std::string& pnumber = scEntry->getStringValue("pennant-number"); + const std::string& name = scEntry->getStringValue("name"); if (type == "carrier" && (pnumber == id || name == id)) { SGSharedPtr carrier = new FGAICarrier; carrier->readFromScenario(scEntry); @@ -430,6 +402,9 @@ FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range ai_list_iterator ai_list_itr = ai_list.begin(); ai_list_iterator end = ai_list.end(); + SGGeod pos(SGGeod::fromDegFt(lon, lat, alt)); + SGVec3d cartPos(SGVec3d::fromGeod(pos)); + while (ai_list_itr != end) { double tgt_alt = (*ai_list_itr)->_getAltitude(); int type = (*ai_list_itr)->getType(); @@ -446,11 +421,9 @@ FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range continue; } - double tgt_lat = (*ai_list_itr)->_getLatitude(); - double tgt_lon = (*ai_list_itr)->_getLongitude(); int id = (*ai_list_itr)->getID(); - double range = calcRange(lat, lon, tgt_lat, tgt_lon); + double range = calcRange(cartPos, (*ai_list_itr)); //SG_LOG(SG_AI, SG_DEBUG, "AIManager: AI list size " // << ai_list.size() @@ -478,14 +451,10 @@ FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range } double -FGAIManager::calcRange(double lat, double lon, double lat2, double lon2) const +FGAIManager::calcRange(const SGVec3d& aCartPos, FGAIBase* aObject) const { - double course, az2, distance; - - //calculate the bearing and range of the second pos from the first - geo_inverse_wgs_84(lat, lon, lat2, lon2, &course, &az2, &distance); - distance *= SG_METER_TO_FEET; - return distance; + double distM = dist(aCartPos, aObject->getCartPos()); + return distM * SG_METER_TO_FEET; } //end AIManager.cxx diff --git a/src/AIModel/AIManager.hxx b/src/AIModel/AIManager.hxx index fad76a6..d9ce287 100644 --- a/src/AIModel/AIManager.hxx +++ b/src/AIModel/AIManager.hxx @@ -59,7 +59,7 @@ public: } FGAIManager(); - ~FGAIManager(); + virtual ~FGAIManager(); void init(); void postinit(); @@ -70,7 +70,6 @@ public: void updateLOD(SGPropertyNode* node); void attach(FGAIBase *model); - void destroyObject( int ID ); const FGAIBase *calcCollision(double alt, double lat, double lon, double fuse_range); inline double get_user_latitude() const { return user_latitude; } @@ -95,14 +94,12 @@ public: SGGeod& geodPos, double& hdng, SGVec3d& uvw); private: - - bool enabled; - int mNumAiTypeModels[FGAIBase::MAX_OBJECTS]; - int mNumAiModels; - - double calcRange(double lat, double lon, double lat2, double lon2)const; + void removeDeadItem(FGAIBase* base); + + double calcRange(const SGVec3d& aCartPos, FGAIBase* aObject) const; SGPropertyNode_ptr root; + SGPropertyNode_ptr enabled; SGPropertyNode_ptr thermal_lift_node; SGPropertyNode_ptr user_latitude_node; SGPropertyNode_ptr user_longitude_node; @@ -128,14 +125,13 @@ private: double user_agl; double wind_from_east; double wind_from_north; - double _dt; void fetchUserState( void ); // used by thermals double range_nearest; double strength; - void processThermal( FGAIThermal* thermal ); + void processThermal( double dt, FGAIThermal* thermal ); SGPropertyChangeCallback cb_ai_bare; SGPropertyChangeCallback cb_ai_detailed; diff --git a/src/AIModel/AIMultiplayer.cxx b/src/AIModel/AIMultiplayer.cxx index caa45ce..0c9bd97 100644 --- a/src/AIModel/AIMultiplayer.cxx +++ b/src/AIModel/AIMultiplayer.cxx @@ -54,8 +54,8 @@ bool FGAIMultiplayer::init(bool search_in_AI_path) { isTanker = false; // do this until this property is // passed over the net - string str1 = _getCallsign(); - string str2 = "MOBIL"; + const string& str1 = _getCallsign(); + const string str2 = "MOBIL"; string::size_type loc1= str1.find( str2, 0 ); if ( (loc1 != string::npos && str2 != "") ){ @@ -74,10 +74,10 @@ bool FGAIMultiplayer::init(bool search_in_AI_path) { void FGAIMultiplayer::bind() { FGAIBase::bind(); - props->tie("refuel/contact", SGRawValuePointer(&contact)); - props->tie("tanker", SGRawValuePointer(&isTanker)); + tie("refuel/contact", SGRawValuePointer(&contact)); + tie("tanker", SGRawValuePointer(&isTanker)); - props->tie("controls/invisible", + tie("controls/invisible", SGRawValuePointer(&invisible)); #define AIMPROProp(type, name) \ @@ -87,30 +87,18 @@ SGRawValueMethods(*this, &FGAIMultiplayer::get##name) SGRawValueMethods(*this, \ &FGAIMultiplayer::get##name, &FGAIMultiplayer::set##name) - //props->tie("callsign", AIMPROProp(const char *, CallSign)); + //tie("callsign", AIMPROProp(const char *, CallSign)); - props->tie("controls/allow-extrapolation", - AIMPRWProp(bool, AllowExtrapolation)); - props->tie("controls/lag-adjust-system-speed", - AIMPRWProp(double, LagAdjustSystemSpeed)); + tie("controls/allow-extrapolation", + AIMPRWProp(bool, AllowExtrapolation)); + tie("controls/lag-adjust-system-speed", + AIMPRWProp(double, LagAdjustSystemSpeed)); #undef AIMPROProp #undef AIMPRWProp } -void FGAIMultiplayer::unbind() { - FGAIBase::unbind(); - - //props->untie("callsign"); - props->untie("controls/allow-extrapolation"); - props->untie("controls/lag-adjust-system-speed"); - props->untie("controls/invisible"); - props->untie("refuel/contact"); - props->untie("tanker"); - -} - void FGAIMultiplayer::update(double dt) { using namespace simgear; diff --git a/src/AIModel/AIMultiplayer.hxx b/src/AIModel/AIMultiplayer.hxx index 4c8d50a..7487849 100644 --- a/src/AIModel/AIMultiplayer.hxx +++ b/src/AIModel/AIMultiplayer.hxx @@ -34,7 +34,6 @@ public: virtual bool init(bool search_in_AI_path=false); virtual void bind(); - virtual void unbind(); virtual void update(double dt); void addMotionInfo(FGExternalMotionData& motionInfo, long stamp); @@ -86,7 +85,7 @@ private: long mLastTimestamp; - // Propertiies for tankers + // Properties for tankers SGPropertyNode_ptr refuel_node; bool isTanker; bool contact; // set if this tanker is within fuelling range diff --git a/src/AIModel/AIShip.cxx b/src/AIModel/AIShip.cxx index 5cf94f1..0ec8f65 100644 --- a/src/AIModel/AIShip.cxx +++ b/src/AIModel/AIShip.cxx @@ -90,7 +90,7 @@ void FGAIShip::readFromScenario(SGPropertyNode* scFileNode) { setRudder(scFileNode->getFloatValue("rudder", 0.0)); setName(scFileNode->getStringValue("name", "Titanic")); setRadius(scFileNode->getDoubleValue("turn-radius-ft", 2000)); - std::string flightplan = scFileNode->getStringValue("flightplan"); + const std::string& flightplan = scFileNode->getStringValue("flightplan"); setRepeat(scFileNode->getBoolValue("repeat", false)); setRestart(scFileNode->getBoolValue("restart", false)); setStartTime(scFileNode->getStringValue("time", "")); @@ -146,110 +146,72 @@ void FGAIShip::reinit() void FGAIShip::bind() { FGAIBase::bind(); - props->tie("surface-positions/rudder-pos-deg", + tie("surface-positions/rudder-pos-deg", SGRawValuePointer(&_rudder)); - props->tie("controls/heading-lock", + tie("controls/heading-lock", SGRawValuePointer(&_hdg_lock)); - props->tie("controls/tgt-speed-kts", + tie("controls/tgt-speed-kts", SGRawValuePointer(&tgt_speed)); - props->tie("controls/tgt-heading-degs", + tie("controls/tgt-heading-degs", SGRawValuePointer(&tgt_heading)); - props->tie("controls/constants/rudder", + tie("controls/constants/rudder", SGRawValuePointer(&_rudder_constant)); - props->tie("controls/constants/roll-factor", + tie("controls/constants/roll-factor", SGRawValuePointer(&_roll_factor)); - props->tie("controls/constants/roll", + tie("controls/constants/roll", SGRawValuePointer(&_roll_constant)); - props->tie("controls/constants/rudder", + tie("controls/constants/rudder", SGRawValuePointer(&_rudder_constant)); - props->tie("controls/constants/speed", + tie("controls/constants/speed", SGRawValuePointer(&_speed_constant)); - props->tie("waypoint/range-nm", + tie("waypoint/range-nm", SGRawValuePointer(&_wp_range)); - props->tie("waypoint/brg-deg", + tie("waypoint/brg-deg", SGRawValuePointer(&_course)); - props->tie("waypoint/rangerate-nm-sec", + tie("waypoint/rangerate-nm-sec", SGRawValuePointer(&_range_rate)); - props->tie("waypoint/new", + tie("waypoint/new", SGRawValuePointer(&_new_waypoint)); - props->tie("waypoint/missed", + tie("waypoint/missed", SGRawValuePointer(&_missed)); - props->tie("waypoint/missed-count-sec", + tie("waypoint/missed-count-sec", SGRawValuePointer(&_missed_count)); - props->tie("waypoint/missed-range-nm", + tie("waypoint/missed-range-nm", SGRawValuePointer(&_missed_range)); - props->tie("waypoint/missed-time-sec", + tie("waypoint/missed-time-sec", SGRawValuePointer(&_missed_time_sec)); - props->tie("waypoint/wait-count-sec", + tie("waypoint/wait-count-sec", SGRawValuePointer(&_wait_count)); - props->tie("waypoint/xtrack-error-ft", + tie("waypoint/xtrack-error-ft", SGRawValuePointer(&_xtrack_error)); - props->tie("waypoint/waiting", + tie("waypoint/waiting", SGRawValuePointer(&_waiting)); - props->tie("waypoint/lead-angle-deg", + tie("waypoint/lead-angle-deg", SGRawValuePointer(&_lead_angle)); - props->tie("waypoint/tunnel", + tie("waypoint/tunnel", SGRawValuePointer(&_tunnel)); - props->tie("waypoint/alt-curr-m", + tie("waypoint/alt-curr-m", SGRawValuePointer(&_curr_alt)); - props->tie("waypoint/alt-prev-m", + tie("waypoint/alt-prev-m", SGRawValuePointer(&_prev_alt)); - props->tie("submodels/serviceable", + tie("submodels/serviceable", SGRawValuePointer(&_serviceable)); - props->tie("controls/turn-radius-ft", + tie("controls/turn-radius-ft", SGRawValuePointer(&turn_radius_ft)); - props->tie("controls/turn-radius-corrected-ft", + tie("controls/turn-radius-corrected-ft", SGRawValuePointer(&_rd_turn_radius_ft)); - props->tie("controls/constants/lead-angle/gain", + tie("controls/constants/lead-angle/gain", SGRawValuePointer(&_lead_angle_gain)); - props->tie("controls/constants/lead-angle/limit-deg", + tie("controls/constants/lead-angle/limit-deg", SGRawValuePointer(&_lead_angle_limit)); - props->tie("controls/constants/lead-angle/proportion", + tie("controls/constants/lead-angle/proportion", SGRawValuePointer(&_proportion)); - props->tie("controls/fixed-turn-radius-ft", + tie("controls/fixed-turn-radius-ft", SGRawValuePointer(&_fixed_turn_radius)); - props->tie("controls/restart", + tie("controls/restart", SGRawValuePointer(&_restart)); - props->tie("velocities/speed-kts", - SGRawValuePointer(&speed)); -} - -void FGAIShip::unbind() { - FGAIBase::unbind(); - props->untie("surface-positions/rudder-pos-deg"); - props->untie("controls/heading-lock"); - props->untie("controls/tgt-speed-kts"); - props->untie("controls/tgt-heading-degs"); - props->untie("controls/constants/roll"); - props->untie("controls/constants/rudder"); - props->untie("controls/constants/roll-factor"); - props->untie("controls/constants/speed"); - props->untie("waypoint/range-nm"); - props->untie("waypoint/range-brg-deg"); - props->untie("waypoint/rangerate-nm-sec"); - props->untie("waypoint/new"); - props->untie("waypoint/missed"); - props->untie("waypoint/missed-count-sec"); - props->untie("waypoint/missed-time-sec"); - props->untie("waypoint/missed-range"); - props->untie("waypoint/wait-count-sec"); - props->untie("waypoint/lead-angle-deg"); - props->untie("waypoint/xtrack-error-ft"); - props->untie("waypoint/waiting"); - props->untie("waypoint/tunnel"); - props->untie("waypoint/alt-curr-m"); - props->untie("waypoint/alt-prev-m"); - props->untie("submodels/serviceable"); - props->untie("controls/turn-radius-ft"); - props->untie("controls/turn-radius-corrected-ft"); - props->untie("controls/constants/lead-angle/gain"); - props->untie("controls/constants/lead-angle/limit-deg"); - props->untie("controls/constants/lead-angle/proportion"); - props->untie("controls/fixed-turn-radius-ft"); - props->untie("controls/constants/speed"); - props->untie("controls/restart"); - props->untie("velocities/speed-kts"); - + tie("velocities/speed-kts", + SGRawValuePointer(&speed)); } void FGAIShip::update(double dt) { @@ -303,7 +265,7 @@ void FGAIShip::Run(double dt) { if (_fp_init) ProcessFlightPlan(dt); - string type = getTypeString(); + const string& type = getTypeString(); double alpha; double rudder_limit; @@ -1088,7 +1050,7 @@ void FGAIShip::setWPPos() { if (curr->getOn_ground()){ if (globals->get_scenery()->get_elevation_m(SGGeod::fromGeodM(wppos, 3000), - elevation_m, &_material, 0)){ + elevation_m, NULL, 0)){ wppos.setElevationM(elevation_m); } diff --git a/src/AIModel/AIShip.hxx b/src/AIModel/AIShip.hxx index 5c3c3b7..34038db 100644 --- a/src/AIModel/AIShip.hxx +++ b/src/AIModel/AIShip.hxx @@ -39,7 +39,6 @@ public: virtual bool init(bool search_in_AI_path=false); virtual void bind(); - virtual void unbind(); virtual void update(double dt); virtual void reinit(); @@ -108,8 +107,6 @@ private: SGGeod wppos; - const SGMaterial* _material; - double getRange(double lat, double lon, double lat2, double lon2) const; double getCourse(double lat, double lon, double lat2, double lon2) const; double getDaySeconds(); diff --git a/src/AIModel/AITanker.cxx b/src/AIModel/AITanker.cxx index ab7c8ed..6ab43e0 100644 --- a/src/AIModel/AITanker.cxx +++ b/src/AIModel/AITanker.cxx @@ -43,20 +43,14 @@ void FGAITanker::readFromScenario(SGPropertyNode* scFileNode) { void FGAITanker::bind() { FGAIAircraft::bind(); - props->tie("refuel/contact", SGRawValuePointer(&contact)); - props->tie("position/altitude-agl-ft",SGRawValuePointer(&altitude_agl_ft)); + tie("refuel/contact", SGRawValuePointer(&contact)); + tie("position/altitude-agl-ft",SGRawValuePointer(&altitude_agl_ft)); + props->setStringValue("navaids/tacan/channel-ID", TACAN_channel_id.c_str()); props->setStringValue("name", _name.c_str()); props->setBoolValue("tanker", true); } -void FGAITanker::unbind() { - FGAIAircraft::unbind(); - props->untie("refuel/contact"); - props->untie("position/altitude-agl-ft"); - -} - void FGAITanker::setTACANChannelID(const string& id) { TACAN_channel_id = id; } diff --git a/src/AIModel/AITanker.hxx b/src/AIModel/AITanker.hxx index ce4329a..8dcfe11 100644 --- a/src/AIModel/AITanker.hxx +++ b/src/AIModel/AITanker.hxx @@ -41,7 +41,6 @@ public: virtual void readFromScenario(SGPropertyNode* scFileNode); virtual void bind(); - virtual void unbind(); virtual const char* getTypeString(void) const { return "tanker"; } diff --git a/src/AIModel/AIThermal.cxx b/src/AIModel/AIThermal.cxx index f47ba23..4b1d653 100644 --- a/src/AIModel/AIThermal.cxx +++ b/src/AIModel/AIThermal.cxx @@ -77,38 +77,25 @@ bool FGAIThermal::init(bool search_in_AI_path) { } void FGAIThermal::bind() { - props->tie("position/altitude-agl-ft", // for debug and tweak + FGAIBase::bind(); + tie("position/altitude-agl-ft", // for debug and tweak SGRawValuePointer(&altitude_agl_ft)); - props->tie("alt-rel", // for debug and tweak + tie("alt-rel", // for debug and tweak SGRawValuePointer(&alt_rel)); - props->tie("time", // for debug and tweak + tie("time", // for debug and tweak SGRawValuePointer(&time)); - props->tie("xx", // for debug and tweak + tie("xx", // for debug and tweak SGRawValuePointer(&xx)); - props->tie("is-forming", // for debug abd tweak + tie("is-forming", // for debug abd tweak SGRawValuePointer(&is_forming)); - props->tie("is-formed", // for debug abd tweak + tie("is-formed", // for debug abd tweak SGRawValuePointer(&is_formed)); - props->tie("is-dying", // for debug abd tweak + tie("is-dying", // for debug abd tweak SGRawValuePointer(&is_dying)); - props->tie("is-dead", // for debug abd tweak + tie("is-dead", // for debug abd tweak SGRawValuePointer(&is_dead)); - FGAIBase::bind(); } -void FGAIThermal::unbind() { - props->untie("position/altitude-agl-ft"); - props->untie("alt-rel"); - props->untie("time"); - props->untie("is-forming"); - props->untie("is-formed"); - props->untie("is-dying"); - props->untie("is-dead"); - props->untie("xx"); - FGAIBase::unbind(); -} - - void FGAIThermal::update(double dt) { FGAIBase::update(dt); Run(dt); diff --git a/src/AIModel/AIThermal.hxx b/src/AIModel/AIThermal.hxx index 675a8ae..1f37acd 100644 --- a/src/AIModel/AIThermal.hxx +++ b/src/AIModel/AIThermal.hxx @@ -33,49 +33,45 @@ using std::string; class FGAIThermal : public FGAIBase { public: - FGAIThermal(); ~FGAIThermal(); - - void readFromScenario(SGPropertyNode* scFileNode); + + void readFromScenario(SGPropertyNode* scFileNode); virtual bool init(bool search_in_AI_path=false); - virtual void bind(); - virtual void unbind(); + virtual void bind(); virtual void update(double dt); inline void setMaxStrength( double s ) { max_strength = s; }; - inline void setDiameter( double d ) { diameter = d; }; - inline void setHeight( double h ) { height = h; }; - inline void setMaxUpdraft( double lift ) { v_up_max = lift; }; + inline void setDiameter( double d ) { diameter = d; }; + inline void setHeight( double h ) { height = h; }; + inline void setMaxUpdraft( double lift ) { v_up_max = lift; }; inline void setMinUpdraft( double sink ) { v_up_min = sink; }; inline void setR_up_frac( double r ) { r_up_frac = r; }; - + inline double getStrength() const { return strength; }; - inline double getDiameter() const { return diameter; }; - inline double getHeight() const { return height; }; - inline double getV_up_max() const { return v_up_max; }; + inline double getDiameter() const { return diameter; }; + inline double getHeight() const { return height; }; + inline double getV_up_max() const { return v_up_max; }; inline double getV_up_min() const { return v_up_min; }; inline double getR_up_frac() const { return r_up_frac; }; - virtual const char* getTypeString(void) const { return "thermal"; } + virtual const char* getTypeString(void) const { return "thermal"; } void getGroundElev(double dt); - private: - void Run(double dt); double get_strength_fac(double alt_frac); double max_strength; - double strength; - double diameter; - double height; - double factor; + double strength; + double diameter; + double height; + double factor; double alt_rel; double alt; double v_up_max; double v_up_min; - double r_up_frac; + double r_up_frac; double cycle_timer; double dt_count; double time; @@ -94,6 +90,4 @@ private: }; - - #endif // _FG_AIThermal_HXX diff --git a/src/AIModel/AIWingman.cxx b/src/AIModel/AIWingman.cxx index 9ba0104..10ffd89 100644 --- a/src/AIModel/AIWingman.cxx +++ b/src/AIModel/AIWingman.cxx @@ -22,7 +22,6 @@ #endif #include -#include #include "AIWingman.hxx" @@ -81,125 +80,83 @@ void FGAIWingman::bind() { props->untie("controls/slave-to-ac"); - props->tie("id", SGRawValueMethods(*this, + tie("id", SGRawValueMethods(*this, &FGAIBase::getID)); - props->tie("subID", SGRawValueMethods(*this, + tie("subID", SGRawValueMethods(*this, &FGAIBase::_getSubID)); - props->tie("position/altitude-ft", + tie("position/altitude-ft", SGRawValueMethods(*this, &FGAIBase::_getElevationFt, &FGAIBase::_setAltitude)); - props->tie("position/latitude-deg", + tie("position/latitude-deg", SGRawValueMethods(*this, &FGAIBase::_getLatitude, &FGAIBase::_setLatitude)); - props->tie("position/longitude-deg", + tie("position/longitude-deg", SGRawValueMethods(*this, &FGAIBase::_getLongitude, &FGAIBase::_setLongitude)); - props->tie("controls/break", SGRawValuePointer(&_break)); - props->tie("controls/join", SGRawValuePointer(&_join)); + tie("controls/break", SGRawValuePointer(&_break)); + tie("controls/join", SGRawValuePointer(&_join)); - props->tie("controls/formate-to-ac", + tie("controls/formate-to-ac", SGRawValueMethods (*this, &FGAIWingman::getFormate, &FGAIWingman::setFormate)); - props->tie("controls/tgt-heading-deg", + tie("controls/tgt-heading-deg", SGRawValueMethods (*this, &FGAIWingman::getTgtHdg, &FGAIWingman::setTgtHdg)); - props->tie("controls/tgt-speed-kt", + tie("controls/tgt-speed-kt", SGRawValueMethods (*this, &FGAIWingman::getTgtSpd, &FGAIWingman::setTgtSpd)); - props->tie("controls/break-deg-rel", + tie("controls/break-deg-rel", SGRawValueMethods (*this, &FGAIWingman::getBrkAng, &FGAIWingman::setBrkAng)); - props->tie("controls/coefficients/heading", + tie("controls/coefficients/heading", SGRawValuePointer(&_coeff_hdg)); - props->tie("controls/coefficients/pitch", + tie("controls/coefficients/pitch", SGRawValuePointer(&_coeff_pch)); - props->tie("controls/coefficients/bank", + tie("controls/coefficients/bank", SGRawValuePointer(&_coeff_bnk)); - props->tie("controls/coefficients/speed", + tie("controls/coefficients/speed", SGRawValuePointer(&_coeff_spd)); - props->tie("orientation/pitch-deg", SGRawValuePointer(&pitch)); - props->tie("orientation/roll-deg", SGRawValuePointer(&roll)); - props->tie("orientation/true-heading-deg", SGRawValuePointer(&hdg)); + tie("orientation/pitch-deg", SGRawValuePointer(&pitch)); + tie("orientation/roll-deg", SGRawValuePointer(&roll)); + tie("orientation/true-heading-deg", SGRawValuePointer(&hdg)); - props->tie("submodels/serviceable", SGRawValuePointer(&serviceable)); + tie("submodels/serviceable", SGRawValuePointer(&serviceable)); - props->tie("load/rel-brg-to-user-deg", + tie("load/rel-brg-to-user-deg", SGRawValueMethods (*this, &FGAIBallistic::getRelBrgHitchToUser)); - props->tie("load/elev-to-user-deg", + tie("load/elev-to-user-deg", SGRawValueMethods (*this, &FGAIBallistic::getElevHitchToUser)); - props->tie("velocities/vertical-speed-fps", + tie("velocities/vertical-speed-fps", SGRawValuePointer(&vs)); - props->tie("velocities/true-airspeed-kt", + tie("velocities/true-airspeed-kt", SGRawValuePointer(&speed)); - props->tie("velocities/speed-east-fps", + tie("velocities/speed-east-fps", SGRawValuePointer(&_speed_east_fps)); - props->tie("velocities/speed-north-fps", + tie("velocities/speed-north-fps", SGRawValuePointer(&_speed_north_fps)); - props->tie("position/x-offset", + tie("position/x-offset", SGRawValueMethods(*this, &FGAIBase::_getXOffset, &FGAIBase::setXoffset)); - props->tie("position/y-offset", + tie("position/y-offset", SGRawValueMethods(*this, &FGAIBase::_getYOffset, &FGAIBase::setYoffset)); - props->tie("position/z-offset", + tie("position/z-offset", SGRawValueMethods(*this, &FGAIBase::_getZOffset, &FGAIBase::setZoffset)); - props->tie("position/tgt-x-offset", + tie("position/tgt-x-offset", SGRawValueMethods(*this, &FGAIBallistic::getTgtXOffset, &FGAIBallistic::setTgtXOffset)); - props->tie("position/tgt-y-offset", + tie("position/tgt-y-offset", SGRawValueMethods(*this, &FGAIBallistic::getTgtYOffset, &FGAIBallistic::setTgtYOffset)); - props->tie("position/tgt-z-offset", + tie("position/tgt-z-offset", SGRawValueMethods(*this, &FGAIBallistic::getTgtZOffset, &FGAIBallistic::setTgtZOffset)); } -void FGAIWingman::unbind() { - FGAIBallistic::unbind(); - - props->untie("id"); - props->untie("SubID"); - - props->untie("orientation/pitch-deg"); - props->untie("orientation/roll-deg"); - props->untie("orientation/true-heading-deg"); - - props->untie("controls/formate-to-ac"); - props->untie("controls/break"); - props->untie("controls/join"); - props->untie("controls/tgt-heading-deg"); - props->untie("controls/tgt-speed-kt"); - props->untie("controls/break-deg-rel"); - props->untie("controls/coefficients/heading"); - props->untie("controls/coefficients/pitch"); - props->untie("controls/coefficients/bank"); - props->untie("controls/coefficients/speed"); - - props->untie("submodels/serviceable"); - - props->untie("velocities/true-airspeed-kt"); - props->untie("velocities/vertical-speed-fps"); - props->untie("velocities/speed_east_fps"); - props->untie("velocities/speed_north_fps"); - - props->untie("load/rel-brg-to-user-deg"); - props->untie("load/elev-to-user-deg"); - - props->untie("position/altitude-ft"); - props->untie("position/latitude-deg"); - props->untie("position/longitude-deg"); - props->untie("position/x-offset"); - props->untie("position/y-offset"); - props->untie("position/z-offset"); - props->untie("position/tgt-x-offset"); - props->untie("position/tgt-y-offset"); - props->untie("position/tgt-z-offset"); -} - bool FGAIWingman::init(bool search_in_AI_path) { if (!FGAIBallistic::init(search_in_AI_path)) return false; @@ -286,6 +243,7 @@ void FGAIWingman::formateToAC(double dt){ double p_hdg, p_pch, p_rll, p_agl, p_ht, p_wow = 0; setTgtOffsets(dt, 25); + _setUserPos(); if (_pnode != 0) { setParentPos(); @@ -296,7 +254,6 @@ void FGAIWingman::formateToAC(double dt){ setOffsetPos(_parentpos, p_hdg, p_pch, p_rll); setSpeed(_p_spd_node->getDoubleValue()); }else { - _setUserPos(); p_hdg = manager->get_user_heading(); p_pch = manager->get_user_pitch(); p_rll = manager->get_user_roll(); @@ -384,6 +341,7 @@ void FGAIWingman::Join(double dt) { double p_hdg, p_pch, p_rll = 0; setTgtOffsets(dt, 25); + _setUserPos(); if (_pnode != 0) { setParentPos(); @@ -394,7 +352,6 @@ void FGAIWingman::Join(double dt) { parent_hdg = _p_hdg_node->getDoubleValue(); parent_spd = _p_spd_node->getDoubleValue(); }else { - _setUserPos(); p_hdg = manager->get_user_heading(); p_pch = manager->get_user_pitch(); p_rll = manager->get_user_roll(); diff --git a/src/AIModel/AIWingman.hxx b/src/AIModel/AIWingman.hxx index c291069..0b02b74 100644 --- a/src/AIModel/AIWingman.hxx +++ b/src/AIModel/AIWingman.hxx @@ -26,7 +26,6 @@ #include "AIBase.hxx" #include -#include class FGAIWingman : public FGAIBallistic { @@ -38,7 +37,6 @@ public: bool init(bool search_in_AI_path=false); virtual void bind(); - virtual void unbind(); virtual void reinit(); virtual void update (double dt); diff --git a/src/AIModel/performancedata.cxx b/src/AIModel/performancedata.cxx index f8da70d..80d62db 100644 --- a/src/AIModel/performancedata.cxx +++ b/src/AIModel/performancedata.cxx @@ -4,8 +4,9 @@ #endif #include "performancedata.hxx" -#include "AIAircraft.hxx" +#include +#include "AIAircraft.hxx" // For now, make this a define // Later on, additional class variables can simulate settings such as braking power @@ -13,42 +14,63 @@ // to the AIAircraft. #define BRAKE_SETTING 1.6 -PerformanceData::PerformanceData(double acceleration, - double deceleration, - double climbRate, - double descentRate, - double vRotate, - double vTakeOff, - double vClimb, - double vCruise, - double vDescent, - double vApproach, - double vTouchdown, - double vTaxi) : - _acceleration(acceleration), - _deceleration(deceleration), - _climbRate(climbRate), - _descentRate(descentRate), - _vRotate(vRotate), - _vTakeOff(vTakeOff), - _vClimb(vClimb), - _vCruise(vCruise), - _vDescent(vDescent), - _vApproach(vApproach), - _vTouchdown(vTouchdown), - _vTaxi(vTaxi) +PerformanceData::PerformanceData() : + _acceleration(4.0), + _deceleration(2.0), + _climbRate(3000.0), + _descentRate(1500.0), + _vRotate(150.0), + _vTakeOff(160.0), + _vClimb(300.0), + _vCruise(430.0), + _vDescent(300.0), + _vApproach(170.0), + _vTouchdown(150.0), + _vTaxi(15.0) { - _rollrate = 9.0; // degrees per second - _maxbank = 30.0; // passenger friendly bank angle + _rollrate = 9.0; // degrees per second + _maxbank = 30.0; // passenger friendly bank angle + } -// read perf data from file -PerformanceData::PerformanceData( const std::string& filename) -{} +PerformanceData::PerformanceData(PerformanceData* clone) : + _acceleration(clone->_acceleration), + _deceleration(clone->_deceleration), + _climbRate(clone->_climbRate), + _descentRate(clone->_descentRate), + _vRotate(clone->_vRotate), + _vTakeOff(clone->_vTakeOff), + _vClimb(clone->_vClimb), + _vCruise(clone->_vCruise), + _vDescent(clone->_vDescent), + _vApproach(clone->_vApproach), + _vTouchdown(clone->_vTouchdown), + _vTaxi(clone->_vTaxi) +{ + _rollrate = clone->_rollrate; + _maxbank = clone->_maxbank; +} PerformanceData::~PerformanceData() {} +void PerformanceData::initFromProps(SGPropertyNode *db_node) +{ +// read the values, using the existing values as defaults + _acceleration = db_node->getDoubleValue("acceleration-kts-hour", _acceleration); + _deceleration = db_node->getDoubleValue("deceleration-kts-hour", _deceleration); + _climbRate = db_node->getDoubleValue("climbrate-fpm", _climbRate); + _descentRate = db_node->getDoubleValue("decentrate-fpm", _descentRate); + _vRotate = db_node->getDoubleValue("rotate-speed-kts", _vRotate); + _vTakeOff = db_node->getDoubleValue("takeoff-speed-kts", _vTakeOff); + _vClimb = db_node->getDoubleValue("climb-speed-kts", _vClimb); + _vCruise = db_node->getDoubleValue("cruise-speed-kts", _vCruise); + _vDescent = db_node->getDoubleValue("decent-speed-kts", _vDescent); + _vApproach = db_node->getDoubleValue("approach-speed-kts", _vApproach); + _vTouchdown = db_node->getDoubleValue("touchdown-speed-kts", _vTouchdown); + _vTaxi = db_node->getDoubleValue("taxi-speed-kts", _vTaxi); +} + double PerformanceData::actualSpeed(FGAIAircraft* ac, double tgt_speed, double dt, bool maxBrakes) { // if (tgt_speed > _vTaxi & ac->onGround()) // maximum taxi speed on ground // tgt_speed = _vTaxi; @@ -85,6 +107,11 @@ double PerformanceData::actualSpeed(FGAIAircraft* ac, double tgt_speed, double d return speed; } +double PerformanceData::decelerationOnGround() const +{ + return _deceleration * BRAKE_SETTING; +} + double PerformanceData::actualBankAngle(FGAIAircraft* ac, double tgt_roll, double dt) { // check maximum bank angle if (fabs(tgt_roll) > _maxbank) diff --git a/src/AIModel/performancedata.hxx b/src/AIModel/performancedata.hxx index 41b89ca..a8aef64 100644 --- a/src/AIModel/performancedata.hxx +++ b/src/AIModel/performancedata.hxx @@ -5,6 +5,7 @@ #include class FGAIAircraft; +class SGPropertyNode; /** Data storage for aircraft performance data. This is used to properly simulate the flight of AIAircrafts. @@ -14,19 +15,12 @@ Data storage for aircraft performance data. This is used to properly simulate th class PerformanceData { public: - PerformanceData(double acceleration, - double deceleration, - double climbRate, - double descentRate, - double vRotate, - double vTakeOff, - double vClimb, - double vCruise, - double vDescent, - double vApproach, - double vTouchdown, - double vTaxi); - PerformanceData(const std::string& filename); + PerformanceData(); + + PerformanceData(PerformanceData* clone); + + void initFromProps(SGPropertyNode* props); + ~PerformanceData(); double actualSpeed(FGAIAircraft* ac, double tgt_speed, double dt, bool needMaxBrake); @@ -52,6 +46,7 @@ public: inline double vTouchdown () { return _vTouchdown; }; inline double vCruise () { return _vCruise; }; + double decelerationOnGround() const; private: double _acceleration; double _deceleration; diff --git a/src/AIModel/performancedb.cxx b/src/AIModel/performancedb.cxx index 842cb9b..274e8a1 100644 --- a/src/AIModel/performancedb.cxx +++ b/src/AIModel/performancedb.cxx @@ -1,4 +1,11 @@ -#include +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "performancedb.hxx" + +#include + #include #include #include @@ -8,7 +15,7 @@ #include #include -#include "performancedb.hxx" +#include "performancedata.hxx" using std::string; using std::cerr; @@ -33,31 +40,29 @@ void PerformanceDB::registerPerformanceData(const std::string& id, PerformanceDa _db[id] = data; } -void PerformanceDB::registerPerformanceData(const std::string& id, const std::string& filename) { - registerPerformanceData(id, new PerformanceData(filename)); -} - -PerformanceData* PerformanceDB::getDataFor(const std::string& id) { - if (_db.find(id) == _db.end()) // id not found -> return jet_transport data +PerformanceData* PerformanceDB::getDataFor(const string& acType, const string& acClass) +{ + // first, try with the specific aircraft type, such as 738 or A322 + if (_db.find(acType) != _db.end()) { + return _db[acType]; + } + + const string& alias = findAlias(acType); + if (_db.find(alias) != _db.end()) { + return _db[alias]; + } + + SG_LOG(SG_AI, SG_INFO, "no performance data for " << acType); + + if (_db.find(acClass) == _db.end()) { return _db["jet_transport"]; - - return _db[id]; + } + + return _db[acClass]; } -void PerformanceDB::load(SGPath filename) { - string name; - double acceleration; - double deceleration; - double climbRate; - double descentRate; - double vRotate; - double vTakeOff; - double vClimb; - double vCruise; - double vDescent; - double vApproach; - double vTouchdown; - double vTaxi; +void PerformanceDB::load(const SGPath& filename) +{ SGPropertyNode root; try { readProperties(filename.str(), &root); @@ -68,24 +73,55 @@ void PerformanceDB::load(SGPath filename) { } SGPropertyNode * node = root.getNode("performancedb"); - for (int i = 0; i < node->nChildren(); i++) { + for (int i = 0; i < node->nChildren(); i++) { SGPropertyNode * db_node = node->getChild(i); - name = db_node->getStringValue("type", "heavy_jet"); - acceleration = db_node->getDoubleValue("acceleration-kts-hour", 4.0); - deceleration = db_node->getDoubleValue("deceleration-kts-hour", 2.0); - climbRate = db_node->getDoubleValue("climbrate-fpm", 3000.0); - descentRate = db_node->getDoubleValue("decentrate-fpm", 1500.0); - vRotate = db_node->getDoubleValue("rotate-speed-kts", 150.0); - vTakeOff = db_node->getDoubleValue("takeoff-speed-kts", 160.0); - vClimb = db_node->getDoubleValue("climb-speed-kts", 300.0); - vCruise = db_node->getDoubleValue("cruise-speed-kts", 430.0); - vDescent = db_node->getDoubleValue("decent-speed-kts", 300.0); - vApproach = db_node->getDoubleValue("approach-speed-kts", 170.0); - vTouchdown = db_node->getDoubleValue("touchdown-speed-kts", 150.0); - vTaxi = db_node->getDoubleValue("taxi-speed-kts", 15.0); - - registerPerformanceData(name, new PerformanceData( - acceleration, deceleration, climbRate, descentRate, vRotate, vTakeOff, vClimb, vCruise, vDescent, vApproach, vTouchdown, vTaxi)); - } + if (!strcmp(db_node->getName(), "aircraft")) { + PerformanceData* data = NULL; + if (db_node->hasChild("base")) { + const string& baseName = db_node->getStringValue("base"); + PerformanceData* baseData = _db[baseName]; + if (!baseData) { + SG_LOG(SG_AI, SG_ALERT, + "Error reading AI aircraft performance database: unknown base type " << baseName); + return; + } + + // clone base data to 'inherit' from it + data = new PerformanceData(baseData); + } else { + data = new PerformanceData; + } + + data->initFromProps(db_node); + const string& name = db_node->getStringValue("type", "heavy_jet"); + registerPerformanceData(name, data); + } else if (!strcmp(db_node->getName(), "alias")) { + const string& alias(db_node->getStringValue("alias")); + if (alias.empty()) { + SG_LOG(SG_AI, SG_ALERT, "performance DB alias entry with no definition"); + continue; + } + + BOOST_FOREACH(SGPropertyNode* matchNode, db_node->getChildren("match")) { + const string& match(matchNode->getStringValue()); + _aliases.push_back(StringPair(match, alias)); + } + } else { + SG_LOG(SG_AI, SG_ALERT, "unrecognized performance DB entry:" << db_node->getName()); + } + } // of nodes iteration } +const string& PerformanceDB::findAlias(const string& acType) const +{ + BOOST_FOREACH(const StringPair& alias, _aliases) { + if (acType.find(alias.first) == 0) { // matched! + return alias.second; + } + } // of alias iteration + + static const string empty; + return empty; +} + + diff --git a/src/AIModel/performancedb.hxx b/src/AIModel/performancedb.hxx index 61d040d..43defb4 100644 --- a/src/AIModel/performancedb.hxx +++ b/src/AIModel/performancedb.hxx @@ -2,10 +2,11 @@ #define PERFORMANCEDB_HXX #include -#include #include +#include -#include "performancedata.hxx" +class PerformanceData; +class SGPath; /** * Registry for performance data. @@ -25,11 +26,23 @@ public: void registerPerformanceData(const std::string& id, PerformanceData* data); void registerPerformanceData(const std::string& id, const std::string& filename); - PerformanceData* getDataFor(const std::string& id); - void load(SGPath path); + /** + * get performance data for an aircraft type / class. Type is specific, eg + * '738' or 'A319'. Class is more generic, such as 'jet_transport'. + */ + PerformanceData* getDataFor(const std::string& acType, const std::string& acClass); + void load(const SGPath& path); private: std::map _db; + + const std::string& findAlias(const std::string& acType) const; + + typedef std::pair StringPair; + /// alias list, to allow type/class names to share data. This is used to merge + /// related types together. Note it's ordered, and not a map since we permit + /// partial matches when merging - the first matching alias is used. + std::vector _aliases; }; #endif diff --git a/src/AIModel/submodel.cxx b/src/AIModel/submodel.cxx index e4746bf..dac04da 100644 --- a/src/AIModel/submodel.cxx +++ b/src/AIModel/submodel.cxx @@ -23,6 +23,8 @@ #include "AIManager.hxx" #include "AIBallistic.hxx" +using std::cout; +using std::endl; const double FGSubmodelMgr::lbs_to_slugs = 0.031080950172; @@ -33,7 +35,7 @@ FGSubmodelMgr::FGSubmodelMgr() yaw_offset = 0.0; //out[0] = out[1] = out[2] = 0; - string contents_node; + //string contents_node; contrail_altitude = 30000; _count = 0; _found_sub = true; @@ -208,7 +210,7 @@ void FGSubmodelMgr::update(double dt) if (trigger && (*submodel_iterator)->count != 0) { //int id = (*submodel_iterator)->id; - string name = (*submodel_iterator)->name; + //const string& name = (*submodel_iterator)->name; SG_LOG(SG_AI, SG_DEBUG, "Submodels release: " << (*submodel_iterator)->id @@ -317,7 +319,7 @@ void FGSubmodelMgr::load() if (path) { const int id = 0; - string Path = path->getStringValue(); + const string& Path = path->getStringValue(); bool Seviceable =_serviceable_node->getBoolValue(); setData(id, Path, Seviceable); } @@ -343,7 +345,7 @@ void FGSubmodelMgr::transform(submodel *sm) int id = sm->id; //int sub_id = sm->sub_id; - string name = sm->name; + //const string& name = sm->name; if (sm->speed_node != 0) @@ -498,9 +500,9 @@ void FGSubmodelMgr::loadAI() } int id = (*sm_list_itr)->getID(); - string type = (*sm_list_itr)->getTypeString(); bool serviceable = (*sm_list_itr)->_getServiceable(); + //string type = (*sm_list_itr)->getTypeString(); //cout << "loadAI: type " << type << " path "<< path << " serviceable " << serviceable << endl; setData(id, path, serviceable); @@ -510,7 +512,7 @@ void FGSubmodelMgr::loadAI() -void FGSubmodelMgr::setData(int id, string& path, bool serviceable) +void FGSubmodelMgr::setData(int id, const string& path, bool serviceable) { SGPropertyNode root; @@ -596,13 +598,13 @@ void FGSubmodelMgr::setData(int id, string& path, bool serviceable) sm->prop->tie("serviceable", SGRawValuePointer(&(sm->serviceable))); sm->prop->tie("random", SGRawValuePointer(&(sm->random))); sm->prop->tie("slaved", SGRawValuePointer(&(sm->slaved))); - string name = sm->name; + const string& name = sm->name; sm->prop->setStringValue("name", name.c_str()); - string submodel = sm->submodel; + const string& submodel = sm->submodel; sm->prop->setStringValue("submodel", submodel.c_str()); - string force_path = sm->force_path; + const string& force_path = sm->force_path; sm->prop->setStringValue("force_path", force_path.c_str()); //cout << "set force_path Sub " << force_path << endl; @@ -614,7 +616,7 @@ void FGSubmodelMgr::setData(int id, string& path, bool serviceable) } } -void FGSubmodelMgr::setSubData(int id, string& path, bool serviceable) +void FGSubmodelMgr::setSubData(int id, const string& path, bool serviceable) { SGPropertyNode root; SGPath config = globals->resolve_aircraft_path(path); @@ -701,14 +703,14 @@ void FGSubmodelMgr::setSubData(int id, string& path, bool serviceable) sm->prop->tie("random", SGRawValuePointer(&(sm->random))); sm->prop->tie("slaved", SGRawValuePointer(&(sm->slaved))); - string name = sm->name; + const string& name = sm->name; sm->prop->setStringValue("name", name.c_str()); - string submodel = sm->submodel; + const string& submodel = sm->submodel; sm->prop->setStringValue("submodel-path", submodel.c_str()); // cout << " set submodel path AI" << submodel<< endl; - string force_path = sm->force_path; + const string& force_path = sm->force_path; sm->prop->setStringValue("force_path", force_path.c_str()); //cout << "set force_path AI" << force_path << endl; @@ -729,7 +731,7 @@ void FGSubmodelMgr::loadSubmodels() submodel_iterator = submodels.begin(); while (submodel_iterator != submodels.end()) { - string submodel = (*submodel_iterator)->submodel; + const string& submodel = (*submodel_iterator)->submodel; if (!submodel.empty()) { //int id = (*submodel_iterator)->id; bool serviceable = true; @@ -824,7 +826,7 @@ void FGSubmodelMgr::valueChanged(SGPropertyNode *prop) const char* _model_added = _model_added_node->getStringValue(); - basic_string ::size_type indexCh2b; + std::basic_string ::size_type indexCh2b; string str2 = _model_added; const char *cstr2b = "multiplayer"; @@ -839,8 +841,7 @@ void FGSubmodelMgr::valueChanged(SGPropertyNode *prop) SGPropertyNode_ptr path_node = sub_node->getChild("path", 0, true); SGPropertyNode_ptr callsign_node = a_node->getChild("callsign", 0, true); - string callsign = callsign_node->getStringValue(); - + //const string& callsign = callsign_node->getStringValue(); //cout << "Submodels: model added - " << callsign <<" read callsign "<< endl; return; @@ -862,8 +863,8 @@ void FGSubmodelMgr::setParentNode(int id) { model = _selected_ac; } else { model = ai->getChild(i); - string path = ai->getPath(); - const string name = model->getStringValue("name"); + //const string& path = ai->getPath(); + //const string& name = model->getStringValue("name"); int parent_id = model->getIntValue("id"); if (!model->nChildren()){ continue; diff --git a/src/AIModel/submodel.hxx b/src/AIModel/submodel.hxx index 71ba869..d4a0d8d 100644 --- a/src/AIModel/submodel.hxx +++ b/src/AIModel/submodel.hxx @@ -191,8 +191,8 @@ private: void loadAI(); void loadSubmodels(); - void setData(int id, std::string& path, bool serviceable); - void setSubData(int id, std::string& path, bool serviceable); + void setData(int id, const std::string& path, bool serviceable); + void setSubData(int id, const std::string& path, bool serviceable); void valueChanged (SGPropertyNode *); void transform(submodel *); void setParentNode(int parent_id); diff --git a/src/ATC/CommStation.cxx b/src/ATC/CommStation.cxx index 45bc2cc..b5280f3 100644 --- a/src/ATC/CommStation.cxx +++ b/src/ATC/CommStation.cxx @@ -1,67 +1,36 @@ #include "CommStation.hxx" -#include - -#include - -namespace { - -typedef std::multimap FrequencyMap; -static FrequencyMap static_frequencies; - -typedef std::pair FrequencyMapRange; - -} // of anonymous namespace +#include namespace flightgear { -CommStation::CommStation(const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq) : - FGPositioned(t, name, pos), +CommStation::CommStation(PositionedID aGuid, const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq) : + FGPositioned(aGuid, t, name, pos), mRangeNM(range), mFreqKhz(freq), - mAirport(NULL) -{ - static_frequencies.insert(std::make_pair(freq, this)); - - init(true); + mAirport(0) +{ } -void CommStation::setAirport(FGAirport* apt) +void CommStation::setAirport(PositionedID apt) { mAirport = apt; } - -double CommStation::freqMHz() const + +FGAirport* CommStation::airport() const { - return mFreqKhz / 100.0; + return (FGAirport*) NavDataCache::instance()->loadById(mAirport); } -PositionedBinding* -CommStation::createBinding(SGPropertyNode* nd) const +double CommStation::freqMHz() const { - return new CommStationBinding(this, nd); + return mFreqKhz / 1000.0; } CommStation* CommStation::findByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt) { - FrequencyMapRange range = static_frequencies.equal_range(freqKhz); - FGPositioned::List results; - for (; range.first != range.second; ++range.first) { - CommStation* sta = range.first->second; - if (filt && !filt->pass(sta)) { - continue; // filtered out - } - - results.push_back(sta); - } - - if (results.empty()) { - return NULL; - } - - FGPositioned::sortByRange(results, pos); - return (CommStation*) results.front().ptr(); + return (CommStation*) NavDataCache::instance()->findCommByFreq(freqKhz, pos, filt).ptr(); } } // of namespace flightgear diff --git a/src/ATC/CommStation.hxx b/src/ATC/CommStation.hxx index 89c0d1f..04a7fe3 100644 --- a/src/ATC/CommStation.hxx +++ b/src/ATC/CommStation.hxx @@ -11,12 +11,10 @@ namespace flightgear class CommStation : public FGPositioned { public: - CommStation(const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq); + CommStation(PositionedID aGuid, const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq); - void setAirport(FGAirport* apt); - FGAirport* airport() const { return mAirport; } - - virtual flightgear::PositionedBinding* createBinding(SGPropertyNode* nd) const; + void setAirport(PositionedID apt); + FGAirport* airport() const; int rangeNm() const { return mRangeNM; } @@ -30,7 +28,7 @@ public: private: int mRangeNM; int mFreqKhz; - FGAirport* mAirport; + PositionedID mAirport; }; } // of namespace flightgear diff --git a/src/ATC/atc_mgr.cxx b/src/ATC/atc_mgr.cxx index d6cb17f..67d53c9 100644 --- a/src/ATC/atc_mgr.cxx +++ b/src/ATC/atc_mgr.cxx @@ -26,7 +26,6 @@ #include -#include #include #include #include @@ -49,6 +48,8 @@ void FGATCManager::init() { int leg = 0; + trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); + // find a reasonable controller for our user's aircraft.. // Let's start by working out the following three scenarios: // Starting on ground at a parking position @@ -75,7 +76,7 @@ void FGATCManager::init() { ai_ac.setLongitude( longitude ); ai_ac.setLatitude ( latitude ); ai_ac.setAltitude ( altitude ); - ai_ac.setPerformance("jet_transport"); + ai_ac.setPerformance("", "jet_transport"); // NEXT UP: Create a traffic Schedule and fill that with appropriate information. This we can use to flight planning. // Note that these are currently only defaults. @@ -102,12 +103,11 @@ void FGATCManager::init() { FGAirport *apt = FGAirport::findByIdent(airport); if (apt && onGround) {// && !runway.empty()) { FGAirportDynamics* dcs = apt->getDynamics(); - int park_index = dcs->getNrOfParkings() - 1; - //cerr << "found information: " << runway << " " << airport << ": parking = " << parking << endl; fp = new FGAIFlightPlan; - while (park_index >= 0 && dcs->getParkingName(park_index) != parking) park_index--; + ParkingAssignment pk(dcs->getParkingByName(parking)); + // No valid parking location, so either at the runway or at a random location. - if (parking.empty() || (park_index < 0)) { + if (!pk.isValid()) { if (!runway.empty()) { controller = apt->getDynamics()->getTowerController(); int stationFreq = apt->getDynamics()->getTowerFrequency(2); @@ -135,17 +135,14 @@ void FGATCManager::init() { leg = 1; //double, lat, lon, head; // Unused variables; //int getId = apt->getDynamics()->getParking(gateId, &lat, &lon, &head); - FGParking* parking = dcs->getParking(park_index); - aircraftRadius = parking->getRadius(); - string fltType = parking->getType(); // gate / ramp, ga, etc etc. + aircraftRadius = pk.parking()->getRadius(); + string fltType = pk.parking()->getType(); // gate / ramp, ga, etc etc. string aircraftType; // Unused. string airline; // Currently used for gate selection, but a fallback mechanism will apply when not specified. - fp->setGate(park_index); + fp->setGate(pk); if (!(fp->createPushBack(&ai_ac, false, apt, - latitude, - longitude, aircraftRadius, fltType, aircraftType, @@ -264,8 +261,7 @@ void FGATCManager::update ( double time ) { //string airport = fgGetString("/sim/presets/airport-id"); //FGAirport *apt = FGAirport::findByIdent(airport); // AT this stage we should update the flightplan, so that waypoint incrementing is conducted as well as leg loading. - static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); - int n = trans_num->getIntValue(); + int n = trans_num->getIntValue(); if (n == 1) { //cerr << "Toggling ground network visibility " << networkVisible << endl; networkVisible = !networkVisible; diff --git a/src/ATC/atc_mgr.hxx b/src/ATC/atc_mgr.hxx index 0d9efa7..f169e34 100644 --- a/src/ATC/atc_mgr.hxx +++ b/src/ATC/atc_mgr.hxx @@ -52,6 +52,7 @@ private: FGATCController *controller, *prevController; // The ATC controller that is responsible for the user's aircraft. bool networkVisible; bool initSucceeded; + SGPropertyNode_ptr trans_num; public: FGATCManager(); @@ -61,4 +62,4 @@ public: void update(double time); }; -#endif // _ATC_MRG_HXX_ \ No newline at end of file +#endif // _ATC_MRG_HXX_ diff --git a/src/ATC/atcdialog.cxx b/src/ATC/atcdialog.cxx index bb5e31e..1bf0c9c 100644 --- a/src/ATC/atcdialog.cxx +++ b/src/ATC/atcdialog.cxx @@ -44,8 +44,7 @@ #include #include - -FGATCDialogNew *currentATCDialog; +using std::string; static SGPropertyNode *getNamedNode(SGPropertyNode *prop, const char *name) { @@ -146,17 +145,16 @@ void FGATCDialogNew::frequencyDisplay(const std::string& ident) return; } - int n = 0; for (unsigned int c=0; c < comms.size(); ++c) { flightgear::CommStation* comm = comms[c]; // add frequency line (modified copy of ) - SGPropertyNode *entry = freq_group->getNode("group", n, true); + SGPropertyNode *entry = freq_group->getNode("group", c, true); copyProperties(freq_group->getNode("group-template", true), entry); entry->removeChildren("enabled", true); entry->setStringValue("text[0]/label", comm->ident()); - + char buf[8]; snprintf(buf, 8, "%.2f", comm->freqMHz()); if(buf[5] == '3') buf[5] = '2'; @@ -164,7 +162,6 @@ void FGATCDialogNew::frequencyDisplay(const std::string& ident) buf[7] = '\0'; entry->setStringValue("text[1]/label", buf); - ++n; } _gui->showDialog(dialog_name); @@ -190,8 +187,9 @@ static bool doFrequencyDisplay( const SGPropertyNode* args ) FGATCDialogNew * FGATCDialogNew::_instance = NULL; FGATCDialogNew::FGATCDialogNew() +: _gui(NULL), + dialogVisible(true) { - dialogVisible = true; } FGATCDialogNew::~FGATCDialogNew() @@ -201,15 +199,15 @@ FGATCDialogNew::~FGATCDialogNew() void FGATCDialogNew::init() { - // Add ATC-dialog to the command list + // Add ATC-dialog to the command list globals->get_commands()->addCommand("ATC-dialog", FGATCDialogNew::popup ); - // Add ATC-freq-search to the command list - globals->get_commands()->addCommand("ATC-freq-search", doFrequencySearch); - globals->get_commands()->addCommand("ATC-freq-display", doFrequencyDisplay); + // Add ATC-freq-search to the command list + globals->get_commands()->addCommand("ATC-freq-search", doFrequencySearch); + globals->get_commands()->addCommand("ATC-freq-display", doFrequencyDisplay); - // initialize properties polled in Update() - //globals->get_props()->setStringValue("/sim/atc/freq-airport", ""); - globals->get_props()->setIntValue("/sim/atc/transmission-num", -1); + // initialize properties polled in Update() + //globals->get_props()->setStringValue("/sim/atc/freq-airport", ""); + globals->get_props()->setIntValue("/sim/atc/transmission-num", -1); } @@ -249,6 +247,10 @@ void FGATCDialogNew::update(double dt) { const char *dialog_name = "atc-dialog"; _gui = (NewGUI *)globals->get_subsystem("gui"); + if (!_gui) { + return; + } + SGPropertyNode_ptr dlg = _gui->getDialogProperties(dialog_name); if (!dlg) return; @@ -285,14 +287,4 @@ void FGATCDialogNew::update(double dt) { } else { _gui->showDialog(dialog_name); } - //dialogVisible = !dialogVisible; - return; - /* - static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); - int n = trans_num->getIntValue(); - if (n >= 0) { - trans_num->setIntValue(-1); - // PopupCallback(n); - cerr << "Selected transmission message" << n << endl; - } */ } diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 8e29f04..8d5c801 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "trafficcontrol.hxx" @@ -51,6 +52,9 @@ #include using std::sort; +using std::string; +using std::cout; +using std::endl; /*************************************************************************** * ActiveRunway @@ -175,7 +179,9 @@ FGTrafficRecord::FGTrafficRecord(): allowTransmission(true), allowPushback(true), priority(0), - latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0) + timer(0), + latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0), + aircraft(NULL) { } @@ -273,8 +279,8 @@ int FGTrafficRecord::crosses(FGGroundNetwork * net, if (intentions.size()) { for (i = intentions.begin(); i != intentions.end(); i++) { if ((*i) > 0) { - if ((currentTargetNode == - net->findSegment(*i)->getEnd()->getIndex())) { + if (currentTargetNode == + net->findSegment(*i)->getEnd()->getIndex()) { //cerr << "Current crosses at " << currentTargetNode <getTrafficRef()->getDepartureTime(); @@ -429,7 +435,7 @@ void FGTrafficRecord::setHeadingAdjustment(double heading) instruction.setHeading(heading); } -bool FGTrafficRecord::pushBackAllowed() +bool FGTrafficRecord::pushBackAllowed() const { return allowPushback; } @@ -456,7 +462,7 @@ FGATCInstruction::FGATCInstruction() } -bool FGATCInstruction::hasInstruction() +bool FGATCInstruction::hasInstruction() const { return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude || resolveCircularWait); @@ -477,6 +483,8 @@ FGATCController::FGATCController() available = true; lastTransmission = 0; initialized = false; + lastTransmissionDirection = ATC_AIR_TO_GROUND; + group = NULL; } FGATCController::~FGATCController() @@ -588,8 +596,7 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, heading); rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway); - fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> - getDynamics()->getSID(activeRunway, heading); + fp = NULL; rec->getAircraft()->GetFlightPlan()->setSID(fp); if (fp) { SID = fp->getName() + " departure"; @@ -736,11 +743,8 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, SGGeod sender_pos; double sender_alt_ft, sender_alt; if(ground_to_air) { - sender_alt_ft = parent->getElevation(); - sender_alt = sender_alt_ft * SG_FEET_TO_METER; - sender_pos= SGGeod::fromDegM( parent->getLongitude(), - parent->getLatitude(), sender_alt ); - } + sender_pos = parent->parent()->geod(); + } else { sender_alt_ft = rec->getAltitude(); sender_alt = sender_alt_ft * SG_FEET_TO_METER; @@ -771,7 +775,7 @@ string FGATCController::formatATCFrequency3_2(int freq) // TODO: Set transponder codes according to real-world routes. // The current version just returns a random string of four octal numbers. -string FGATCController::genTransponderCode(string fltRules) +string FGATCController::genTransponderCode(const string& fltRules) { if (fltRules == "VFR") { return string("1200"); @@ -1192,7 +1196,7 @@ bool FGStartupController::checkTransmissionState(int st, time_t now, time_t star if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) { //cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl; - static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); + SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); int n = trans_num->getIntValue(); if (n == 0) { trans_num->setIntValue(-1); @@ -1298,7 +1302,7 @@ static void WorldCoordinate(osg::Matrix& obj_pos, double lat, double lon, double elev, double hdg, double slope) { SGGeod geod = SGGeod::fromDegM(lon, lat, elev); - obj_pos = geod.makeZUpFrame(); + obj_pos = makeZUpFrame(geod); // hdg is not a compass heading, but a counter-clockwise rotation // around the Z axis obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS, @@ -1328,7 +1332,7 @@ void FGStartupController::render(bool visible) group = new osg::Group; FGScenery * local_scenery = globals->get_scenery(); //double elevation_meters = 0.0; - double elevation_feet = 0.0; + //double elevation_feet = 0.0; //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) { @@ -1342,10 +1346,10 @@ void FGStartupController::render(bool visible) if (pos > 0) { FGTaxiSegment *segment = parent->getGroundNetwork()->findSegment(pos); SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude()))); - SGGeod end (SGGeod::fromDeg(segment->getEnd()->getLongitude(), segment->getEnd()->getLatitude())); + SGGeod end (segment->getEnd()->geod()); double length = SGGeodesy::distanceM(start, end); - //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod()); + //heading = SGGeodesy::headingDeg(start->geod(), end->geod()); double az2, heading; //, distanceM; SGGeodesy::inverse(start, end, heading, az2, length); @@ -1365,12 +1369,12 @@ void FGStartupController::render(bool visible) } else { elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER); } - double elevationEnd = segment->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationEnd = segment->getEnd()->getElevationM(); if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) { SGGeod center2 = end; center2.setElevationM(SG_MAX_ELEVATION_M); if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { - elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; + //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; //elevation_meters += 0.5; } else { @@ -1427,13 +1431,13 @@ void FGStartupController::render(bool visible) obj_trans->setDataVariance(osg::Object::STATIC); FGTaxiSegment *segment = parent->getGroundNetwork()->findSegment(k); - double elevationStart = segment->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); - double elevationEnd = segment->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationStart = segment->getStart()->getElevationM(); + double elevationEnd = segment->getEnd ()->getElevationM(); if ((elevationStart == 0) || (elevationStart == parent->getElevation())) { - SGGeod center2 = segment->getStart()->getGeod(); + SGGeod center2 = segment->getStart()->geod(); center2.setElevationM(SG_MAX_ELEVATION_M); if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) { - elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5; + //elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5; //elevation_meters += 0.5; } else { @@ -1442,10 +1446,10 @@ void FGStartupController::render(bool visible) segment->getStart()->setElevation(elevationStart); } if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) { - SGGeod center2 = segment->getEnd()->getGeod(); + SGGeod center2 = segment->getEnd()->geod(); center2.setElevationM(SG_MAX_ELEVATION_M); if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { - elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; + //elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; //elevation_meters += 0.5; } else { @@ -1461,8 +1465,9 @@ void FGStartupController::render(bool visible) //cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl; - - WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope ); + SGGeod segCenter(segment->getCenter()); + WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(), + segCenter.getLongitudeDeg(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope ); //WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) ); @@ -1690,7 +1695,7 @@ FGATCInstruction FGApproachController::getInstruction(int id) } -ActiveRunway *FGApproachController::getRunway(string name) +ActiveRunway *FGApproachController::getRunway(const string& name) { ActiveRunwayVecIterator rwy = activeRunways.begin(); if (activeRunways.size()) { diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx index 7c17665..3d58d58 100644 --- a/src/ATC/trafficcontrol.hxx +++ b/src/ATC/trafficcontrol.hxx @@ -21,10 +21,9 @@ #ifndef _TRAFFIC_CONTROL_HXX_ #define _TRAFFIC_CONTROL_HXX_ - -#ifndef __cplusplus -# error This library requires C++ -#endif +#include +#include +#include #include #include @@ -38,18 +37,8 @@ #include #include - -#include -#include -#include - -using std::string; -using std::vector; -using std::list; - - -typedef vector intVec; -typedef vector::iterator intVecIterator; +typedef std::vector intVec; +typedef std::vector::iterator intVecIterator; class FGAIFlightPlan; // forward reference @@ -78,34 +67,34 @@ private: public: FGATCInstruction(); - bool hasInstruction (); - bool getHoldPattern () { + bool hasInstruction () const; + bool getHoldPattern () const { return holdPattern; }; - bool getHoldPosition () { + bool getHoldPosition () const { return holdPosition; }; - bool getChangeSpeed () { + bool getChangeSpeed () const { return changeSpeed; }; - bool getChangeHeading () { + bool getChangeHeading () const { return changeHeading; }; - bool getChangeAltitude() { + bool getChangeAltitude() const { return changeAltitude; }; - double getSpeed () { + double getSpeed () const { return speed; }; - double getHeading () { + double getHeading () const { return heading; }; - double getAlt () { + double getAlt () const { return alt; }; - bool getCheckForCircularWait() { + bool getCheckForCircularWait() const { return resolveCircularWait; }; @@ -162,7 +151,7 @@ private: intVec intentions; FGATCInstruction instruction; double latitude, longitude, heading, speed, altitude, radius; - string runway; + std::string runway; //FGAISchedule *trafficRef; FGAIAircraft *aircraft; @@ -177,7 +166,7 @@ public: radius = rad; }; void setPositionAndIntentions(int pos, FGAIFlightPlan *route); - void setRunway(string rwy) { + void setRunway(const std::string& rwy) { runway = rwy; }; void setLeg(int lg) { @@ -203,34 +192,34 @@ public: int crosses (FGGroundNetwork *, FGTrafficRecord &other); bool isOpposing (FGGroundNetwork *, FGTrafficRecord &other, int node); - bool isActive(int margin); + bool isActive(int margin) const; bool onRoute(FGGroundNetwork *, FGTrafficRecord &other); - bool getSpeedAdjustment() { + bool getSpeedAdjustment() const { return instruction.getChangeSpeed(); }; - double getLatitude () { + double getLatitude () const { return latitude ; }; - double getLongitude() { + double getLongitude() const { return longitude; }; - double getHeading () { + double getHeading () const { return heading ; }; - double getSpeed () { + double getSpeed () const { return speed ; }; - double getAltitude () { + double getAltitude () const { return altitude ; }; - double getRadius () { + double getRadius () const { return radius ; }; - int getWaitsForId () { + int getWaitsForId () const { return waitsForId; }; @@ -243,10 +232,10 @@ public: instruction.setChangeHeading(false); }; - bool hasHeadingAdjustment() { + bool hasHeadingAdjustment() const { return instruction.getChangeHeading(); }; - bool hasHoldPosition() { + bool hasHoldPosition() const { return instruction.getHoldPosition(); }; void setHoldPosition (bool inst) { @@ -264,7 +253,7 @@ public: instruction.setResolveCircularWait(false); }; - string getRunway() { + const std::string& getRunway() const { return runway; }; //void setCallSign(string clsgn) { callsign = clsgn; }; @@ -276,21 +265,21 @@ public: allowTransmission=true; }; //string getCallSign() { return callsign; }; - FGAIAircraft *getAircraft() { + FGAIAircraft *getAircraft() const { return aircraft; }; - int getTime() { + int getTime() const { return timer; }; - int getLeg() { + int getLeg() const { return leg; }; void setTime(time_t time) { timer = time; }; - bool pushBackAllowed(); - bool allowTransmissions() { + bool pushBackAllowed() const; + bool allowTransmissions() const { return allowTransmission; }; void allowPushBack() { allowPushback =true;}; @@ -304,27 +293,27 @@ public: void nextFrequency() { frequencyId++; }; - int getNextFrequency() { + int getNextFrequency() const { return frequencyId; }; intVec& getIntentions() { return intentions; }; - int getCurrentPosition() { + int getCurrentPosition() const { return currentPos; }; void setPriority(int p) { priority = p; }; - int getPriority() { return priority; }; + int getPriority() const { return priority; }; }; -typedef list TrafficVector; -typedef list::iterator TrafficVectorIterator; +typedef std::list TrafficVector; +typedef std::list::iterator TrafficVectorIterator; -typedef vector TimeVector; -typedef vector::iterator TimeVectorIterator; +typedef std::vector TimeVector; +typedef std::vector::iterator TimeVectorIterator; -typedef vector AircraftVec; -typedef vector::iterator AircraftVecIterator; +typedef std::vector AircraftVec; +typedef std::vector::iterator AircraftVecIterator; /*********************************************************************** * Active runway, a utility class to keep track of which aircraft has @@ -333,20 +322,20 @@ typedef vector::iterator AircraftVecIterator; class ActiveRunway { private: - string rwy; + std::string rwy; int currentlyCleared; double distanceToFinal; TimeVector estimatedArrivalTimes; AircraftVec departureCue; public: - ActiveRunway(string r, int cc) { + ActiveRunway(const std::string& r, int cc) { rwy = r; currentlyCleared = cc; distanceToFinal = 6.0 * SG_NM_TO_METER; }; - string getRunwayName() { + std::string getRunwayName() { return rwy; }; int getCleared () { @@ -379,8 +368,8 @@ public: void printDepartureCue(); }; -typedef vector ActiveRunwayVec; -typedef vector::iterator ActiveRunwayVecIterator; +typedef std::vector ActiveRunwayVec; +typedef std::vector::iterator ActiveRunwayVecIterator; /** * class FGATCController @@ -399,8 +388,8 @@ protected: double dt_count; osg::Group* group; - string formatATCFrequency3_2(int ); - string genTransponderCode(string fltRules); + std::string formatATCFrequency3_2(int ); + std::string genTransponderCode(const std::string& fltRules); bool isUserAircraft(FGAIAircraft*); public: @@ -454,9 +443,9 @@ public: dt_count = dt; }; void transmit(FGTrafficRecord *rec, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible); - string getGateName(FGAIAircraft *aircraft); + std::string getGateName(FGAIAircraft *aircraft); virtual void render(bool) = 0; - virtual string getName() = 0; + virtual std::string getName() = 0; virtual void update(double) = 0; @@ -490,7 +479,7 @@ public: virtual FGATCInstruction getInstruction(int id); virtual void render(bool); - virtual string getName(); + virtual std::string getName(); virtual void update(double dt); bool hasActiveTraffic() { return activeTraffic.size() != 0; @@ -526,7 +515,7 @@ public: virtual FGATCInstruction getInstruction(int id); virtual void render(bool); - virtual string getName(); + virtual std::string getName(); virtual void update(double dt); bool hasActiveTraffic() { @@ -566,10 +555,10 @@ public: virtual FGATCInstruction getInstruction(int id); virtual void render(bool); - virtual string getName(); + virtual std::string getName(); virtual void update(double dt); - ActiveRunway* getRunway(string name); + ActiveRunway* getRunway(const std::string& name); bool hasActiveTraffic() { return activeTraffic.size() != 0; diff --git a/src/ATCDCL/ATC.cxx b/src/ATCDCL/ATC.cxx index 776ec34..9e86801 100644 --- a/src/ATCDCL/ATC.cxx +++ b/src/ATCDCL/ATC.cxx @@ -27,6 +27,7 @@ #include #include +#include #include #include
@@ -35,301 +36,378 @@ #include FGATC::FGATC() : - _playing(false), - _voiceOK(false), - _sgr(NULL), - freqClear(true), - receiving(false), - respond(false), - responseID(""), - runResponseCounter(false), - _runReleaseCounter(false), - responseReqd(false), - _type(INVALID), - _display(false), - // Transmission timing stuff - pending_transmission(""), - _timeout(0), - _pending(false), - _transmit(false), - _transmitting(false), - _counter(0.0), - _max_count(5.0) + freq(0), + _currentStation(NULL), + range(0), + _voice(true), + _playing(false), + _sgr(NULL), + _type(INVALID), + _display(false) +#ifdef OLD_ATC_MGR + ,freqClear(true), + receiving(false), + respond(false), + responseID(""), + runResponseCounter(false), + _runReleaseCounter(false), + responseReqd(false), + // Transmission timing stuff + pending_transmission(""), + _timeout(0), + _pending(false), + _transmit(false), + _transmitting(false), + _counter(0.0), + _max_count(5.0) +#endif { - SGSoundMgr *smgr = globals->get_soundmgr(); - _sgr = smgr->find("atc", true); - _sgr->tie_to_listener(); - - _volume = fgGetNode("/sim/sound/atc/volume", true); - _enabled = fgGetNode("/sim/sound/atc/enabled", true); - _atc_external = fgGetNode("/sim/sound/atc/external-view", true); - _internal = fgGetNode("/sim/current-view/internal", true); + SGSoundMgr *smgr = globals->get_soundmgr(); + _sgr = smgr->find("atc", true); + _sgr->tie_to_listener(); + + _masterVolume = fgGetNode("/sim/sound/atc/volume", true); + _enabled = fgGetNode("/sim/sound/atc/enabled", true); + _atc_external = fgGetNode("/sim/sound/atc/external-view", true); + _internal = fgGetNode("/sim/current-view/internal", true); } FGATC::~FGATC() { } +#ifndef OLD_ATC_MGR // Derived classes wishing to use the response counter should // call this from their own Update(...). -void FGATC::Update(double dt) { - if(runResponseCounter) { - //cout << responseCounter << '\t' << responseTime << '\n'; - if(responseCounter >= responseTime) { - runResponseCounter = false; - respond = true; - //cout << "RESPOND\n"; - } else { - responseCounter += dt; - } - } - - if(_runReleaseCounter) { - if(_releaseCounter >= _releaseTime) { - freqClear = true; - _runReleaseCounter = false; - } else { - _releaseCounter += dt; - } - } - - // Transmission stuff cribbed from AIPlane.cxx - if(_pending) { - if(GetFreqClear()) { - //cout << "TUNED STATION FREQ CLEAR\n"; - SetFreqInUse(); - _pending = false; - _transmit = true; - _transmitting = false; - } else { - if(_timeout > 0.0) { // allows count down to be avoided by initially setting it to zero - _timeout -= dt; - if(_timeout <= 0.0) { - _timeout = 0.0; - _pending = false; - // timed out - don't render. - } - } - } - } +void FGATC::update(double dt) { + // TODO This doesn't really do anything specific to this instance. + // All FGATCs share the same "_sgr" sound group. So this really should + // only be done once for all FGATCs. #ifdef ENABLE_AUDIO_SUPPORT - bool active = _atc_external->getBoolValue() || - _internal->getBoolValue(); - - if ( active && _enabled->getBoolValue() ) { - _sgr->set_volume( _volume->getFloatValue() ); - _sgr->resume(); // no-op if already in resumed state - } else { - _sgr->suspend(); - } -#endif + bool active = _atc_external->getBoolValue() || + _internal->getBoolValue(); - if(_transmit) { - _counter = 0.0; - _max_count = 5.0; // FIXME - hardwired length of message - need to calculate it! - - //cout << "Transmission = " << pending_transmission << '\n'; - if(_display) { - //Render(pending_transmission, ident, false); - Render(pending_transmission); - } - _transmit = false; - _transmitting = true; - } else if(_transmitting) { - if(_counter >= _max_count) { - //NoRender(plane.callsign); commented out since at the moment NoRender is designed just to stop repeating messages, - // and this will be primarily used on single messages. - _transmitting = false; - //if(tuned_station) tuned_station->NotifyTransmissionFinished(plane.callsign); - // TODO - need to let the plane the transmission is aimed at that it's finished. - // However, for now we'll just release the frequency since if we don't it all goes pear-shaped - _releaseCounter = 0.0; - _releaseTime = 0.9; - _runReleaseCounter = true; - } - _counter += dt; - } + if ( active && _enabled->getBoolValue() ) { + _sgr->set_volume( _masterVolume->getFloatValue() ); + _sgr->resume(); // no-op if already in resumed state + } else { + _sgr->suspend(); + } +#endif } +#endif -void FGATC::ReceiveUserCallback(int code) { - SG_LOG(SG_ATC, SG_WARN, "WARNING - whichever ATC class was intended to receive callback code " << code << " didn't get it!!!"); -} +void FGATC::SetStation(flightgear::CommStation* sta) { + if (_currentStation == sta) + return; + _currentStation = sta; -void FGATC::SetResponseReqd(const string& rid) { - receiving = false; - responseReqd = true; - respond = false; // TODO - this ignores the fact that more than one plane could call this before response - // Shouldn't happen with AI only, but user could confuse things?? - responseID = rid; - runResponseCounter = true; - responseCounter = 0.0; - responseTime = 1.8; // TODO - randomize this slightly. -} + if (sta) + { + switch (sta->type()) { + case FGPositioned::FREQ_ATIS: _type = ATIS; break; + case FGPositioned::FREQ_AWOS: _type = AWOS; break; + default: + sta = NULL; + break; + } + } -void FGATC::NotifyTransmissionFinished(const string& rid) { - //cout << "Transmission finished, callsign = " << rid << '\n'; - receiving = false; - responseID = rid; - if(responseReqd) { - runResponseCounter = true; - responseCounter = 0.0; - responseTime = 1.2; // TODO - randomize this slightly, and allow it to be dependent on the transmission and how busy the ATC is. - respond = false; // TODO - this ignores the fact that more than one plane could call this before response - // Shouldn't happen with AI only, but user could confuse things?? - } else { - freqClear = true; - } -} + if (sta == NULL) + { + range = 0; + ident = ""; + name = ""; + freq = 0; -void FGATC::SetStation(flightgear::CommStation* sta) { - switch (sta->type()) { - case FGPositioned::FREQ_ATIS: _type = ATIS; break; - case FGPositioned::FREQ_AWOS: _type = AWOS; break; - default: - throw sg_exception("unsupported comm station type"); + SetNoDisplay(); + update(0); // one last update + } + else + { + _geod = sta->geod(); + _cart = sta->cart(); + + range = sta->rangeNm(); + ident = sta->airport()->ident(); + name = sta->airport()->name(); + freq = sta->freqKHz(); + SetDisplay(); } - - _geod = sta->geod(); - _cart = sta->cart(); - range = sta->rangeNm(); - ident = sta->airport()->ident(); - name = sta->airport()->name(); - freq = sta->freqKHz(); } // Render a transmission // Outputs the transmission either on screen or as audio depending on user preference // The refname is a string to identify this sample to the sound manager // The repeating flag indicates whether the message should be repeated continuously or played once. -void FGATC::Render(string& msg, const float volume, - const string& refname, const bool repeating) { - if (volume < 0.05) return; +void FGATC::Render(std::string& msg, const float volume, + const std::string& refname, const bool repeating) { + if ((!_display) ||(volume < 0.05)) + { + NoRender(refname); + return; + } - if (repeating) - fgSetString("/sim/messages/atis", msg.c_str()); - else - fgSetString("/sim/messages/atc", msg.c_str()); + if (repeating) + fgSetString("/sim/messages/atis", msg.c_str()); + else + fgSetString("/sim/messages/atc", msg.c_str()); #ifdef ENABLE_AUDIO_SUPPORT - _voice = (_voiceOK && fgGetBool("/sim/sound/voice")); - if(_voice) { - size_t len; - void* buf = _vPtr->WriteMessage((char*)msg.c_str(), &len); - if(buf) { - NoRender(refname); - try { + bool useVoice = _voice && fgGetBool("/sim/sound/voice") && fgGetBool("/sim/sound/atc/enabled"); + SGSoundSample *simple = _sgr->find(refname); + if(useVoice) { + if (simple && (_currentMsg == msg)) + { + simple->set_volume(volume); + } + else + { + _currentMsg = msg; + size_t len; + void* buf = NULL; + FGATCVoice* vPtr = GetVoicePointer(); + if (vPtr) + buf = vPtr->WriteMessage((char*)msg.c_str(), &len); + NoRender(refname); + if(buf) { + try { // >>> Beware: must pass a (new) object to the (add) method, // >>> because the (remove) method is going to do a (delete) // >>> whether that's what you want or not. - SGSoundSample *simple = new SGSoundSample(&buf, len, 8000); - simple->set_volume(volume); - _sgr->add(simple, refname); - _sgr->play(refname, repeating); - } catch ( sg_io_exception &e ) { - SG_LOG(SG_ATC, SG_ALERT, e.getFormattedMessage()); - } - } - } -#endif // ENABLE_AUDIO_SUPPORT - if(!_voice) { - // first rip the underscores and the pause hints out of the string - these are for the convienience of the voice parser - for(unsigned int i = 0; i < msg.length(); ++i) { - if((msg.substr(i,1) == "_") || (msg.substr(i,1) == "/")) { - msg[i] = ' '; - } - } - } - _playing = true; + simple = new SGSoundSample(&buf, len, 8000); + simple->set_volume(volume); + _sgr->add(simple, refname); + _sgr->play(refname, repeating); + } catch ( sg_io_exception &e ) { + SG_LOG(SG_ATC, SG_ALERT, e.getFormattedMessage()); + } + } + } + } + else + if (simple) + { + NoRender(refname); + } +#else + bool useVoice = false; +#endif // ENABLE_AUDIO_SUPPORT + + if (!useVoice) + { + // first rip the underscores and the pause hints out of the string - these are for the convenience of the voice parser + for(unsigned int i = 0; i < msg.length(); ++i) { + if((msg.substr(i,1) == "_") || (msg.substr(i,1) == "/")) { + msg[i] = ' '; + } + } + } + _playing = true; } // Cease rendering a transmission. -void FGATC::NoRender(const string& refname) { - if(_playing) { - if(_voice) { -#ifdef ENABLE_AUDIO_SUPPORT - _sgr->stop(refname); - _sgr->remove(refname); +void FGATC::NoRender(const std::string& refname) { + if(_playing) { + if(_voice) { +#ifdef ENABLE_AUDIO_SUPPORT + _sgr->stop(refname); + _sgr->remove(refname); #endif - } - _playing = false; - } + } + _playing = false; + } +} + +#ifdef OLD_ATC_MGR +// Derived classes wishing to use the response counter should +// call this from their own Update(...). +void FGATC::Update(double dt) { + +#ifdef ENABLE_AUDIO_SUPPORT + bool active = _atc_external->getBoolValue() || + _internal->getBoolValue(); + + if ( active && _enabled->getBoolValue() ) { + _sgr->set_volume( _masterVolume->getFloatValue() ); + _sgr->resume(); // no-op if already in resumed state + } else { + _sgr->suspend(); + } +#endif + + if(runResponseCounter) { + //cout << responseCounter << '\t' << responseTime << '\n'; + if(responseCounter >= responseTime) { + runResponseCounter = false; + respond = true; + //cout << "RESPOND\n"; + } else { + responseCounter += dt; + } + } + + if(_runReleaseCounter) { + if(_releaseCounter >= _releaseTime) { + freqClear = true; + _runReleaseCounter = false; + } else { + _releaseCounter += dt; + } + } + + // Transmission stuff cribbed from AIPlane.cxx + if(_pending) { + if(GetFreqClear()) { + //cout << "TUNED STATION FREQ CLEAR\n"; + SetFreqInUse(); + _pending = false; + _transmit = true; + _transmitting = false; + } else { + if(_timeout > 0.0) { // allows count down to be avoided by initially setting it to zero + _timeout -= dt; + if(_timeout <= 0.0) { + _timeout = 0.0; + _pending = false; + // timed out - don't render. + } + } + } + } + + if(_transmit) { + _counter = 0.0; + _max_count = 5.0; // FIXME - hardwired length of message - need to calculate it! + + //cout << "Transmission = " << pending_transmission << '\n'; + if(_display) { + //Render(pending_transmission, ident, false); + Render(pending_transmission); + } + _transmit = false; + _transmitting = true; + } else if(_transmitting) { + if(_counter >= _max_count) { + //NoRender(plane.callsign); commented out since at the moment NoRender is designed just to stop repeating messages, + // and this will be primarily used on single messages. + _transmitting = false; + //if(tuned_station) tuned_station->NotifyTransmissionFinished(plane.callsign); + // TODO - need to let the plane the transmission is aimed at that it's finished. + // However, for now we'll just release the frequency since if we don't it all goes pear-shaped + _releaseCounter = 0.0; + _releaseTime = 0.9; + _runReleaseCounter = true; + } + _counter += dt; + } +} + +void FGATC::ReceiveUserCallback(int code) { + SG_LOG(SG_ATC, SG_WARN, "WARNING - whichever ATC class was intended to receive callback code " << code << " didn't get it!!!"); +} + +void FGATC::SetResponseReqd(const string& rid) { + receiving = false; + responseReqd = true; + respond = false; // TODO - this ignores the fact that more than one plane could call this before response + // Shouldn't happen with AI only, but user could confuse things?? + responseID = rid; + runResponseCounter = true; + responseCounter = 0.0; + responseTime = 1.8; // TODO - randomize this slightly. +} + +void FGATC::NotifyTransmissionFinished(const string& rid) { + //cout << "Transmission finished, callsign = " << rid << '\n'; + receiving = false; + responseID = rid; + if(responseReqd) { + runResponseCounter = true; + responseCounter = 0.0; + responseTime = 1.2; // TODO - randomize this slightly, and allow it to be dependent on the transmission and how busy the ATC is. + respond = false; // TODO - this ignores the fact that more than one plane could call this before response + // Shouldn't happen with AI only, but user could confuse things?? + } else { + freqClear = true; + } } // Generate the text of a message from its parameters and the current context. string FGATC::GenText(const string& m, int c) { - return(""); + return(""); } ostream& operator << (ostream& os, atc_type atc) { - switch(atc) { - case(AWOS): return(os << "AWOS"); - case(ATIS): return(os << "ATIS"); - case(GROUND): return(os << "GROUND"); - case(TOWER): return(os << "TOWER"); - case(APPROACH): return(os << "APPROACH"); - case(DEPARTURE): return(os << "DEPARTURE"); - case(ENROUTE): return(os << "ENROUTE"); - case(INVALID): return(os << "INVALID"); - } - return(os << "ERROR - Unknown switch in atc_type operator << "); + switch(atc) { + case(AWOS): return(os << "AWOS"); + case(ATIS): return(os << "ATIS"); + case(GROUND): return(os << "GROUND"); + case(TOWER): return(os << "TOWER"); + case(APPROACH): return(os << "APPROACH"); + case(DEPARTURE): return(os << "DEPARTURE"); + case(ENROUTE): return(os << "ENROUTE"); + case(INVALID): return(os << "INVALID"); + } + return(os << "ERROR - Unknown switch in atc_type operator << "); } std::istream& operator >> ( std::istream& fin, ATCData& a ) { - double f; - char ch; - char tp; - - fin >> tp; - - switch(tp) { - case 'I': - a.type = ATIS; - break; - case 'T': - a.type = TOWER; - break; - case 'G': - a.type = GROUND; - break; - case 'A': - a.type = APPROACH; - break; - case '[': - a.type = INVALID; - return fin >> skipeol; - default: - SG_LOG(SG_ATC, SG_ALERT, "Warning - unknown type \'" << tp << "\' found whilst reading ATC frequency data!\n"); - a.type = INVALID; - return fin >> skipeol; - } - - double lat, lon, elev; - - fin >> lat >> lon >> elev >> f >> a.range >> a.ident; - a.geod = SGGeod::fromDegM(lon, lat, elev); - a.name = ""; - fin >> ch; - if(ch != '"') a.name += ch; - while(1) { - //in >> noskipws - fin.unsetf(std::ios::skipws); - fin >> ch; - if((ch == '"') || (ch == 0x0A)) { - break; - } // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the " - a.name += ch; - } - fin.setf(std::ios::skipws); - //cout << "Comm name = " << a.name << '\n'; - - a.freq = (int)(f*100.0 + 0.5); - - // cout << a.ident << endl; - - // generate cartesian coordinates - a.cart = SGVec3d::fromGeod(a.geod); - return fin >> skipeol; -} + double f; + char ch; + char tp; + + fin >> tp; + + switch(tp) { + case 'I': + a.type = ATIS; + break; + case 'T': + a.type = TOWER; + break; + case 'G': + a.type = GROUND; + break; + case 'A': + a.type = APPROACH; + break; + case '[': + a.type = INVALID; + return fin >> skipeol; + default: + SG_LOG(SG_ATC, SG_ALERT, "Warning - unknown type \'" << tp << "\' found whilst reading ATC frequency data!\n"); + a.type = INVALID; + return fin >> skipeol; + } + + double lat, lon, elev; + + fin >> lat >> lon >> elev >> f >> a.range >> a.ident; + a.geod = SGGeod::fromDegM(lon, lat, elev); + a.name = ""; + fin >> ch; + if(ch != '"') a.name += ch; + while(1) { + //in >> noskipws + fin.unsetf(std::ios::skipws); + fin >> ch; + if((ch == '"') || (ch == 0x0A)) { + break; + } // we shouldn't need the 0x0A but it makes a nice safely in case someone leaves off the " + a.name += ch; + } + fin.setf(std::ios::skipws); + //cout << "Comm name = " << a.name << '\n'; + a.freq = (int)(f*100.0 + 0.5); + + // cout << a.ident << endl; + + // generate cartesian coordinates + a.cart = SGVec3d::fromGeod(a.geod); + return fin >> skipeol; +} +#endif diff --git a/src/ATCDCL/ATC.hxx b/src/ATCDCL/ATC.hxx index 17f743c..6a0918a 100644 --- a/src/ATCDCL/ATC.hxx +++ b/src/ATCDCL/ATC.hxx @@ -42,20 +42,6 @@ namespace flightgear class CommStation; } -// Convert a frequency in MHz to tens of kHz -// so we can use it e.g. as an index into commlist_freq -// -// If freq > 1000 assume it's already in tens of KHz; -// otherwise assume MHz. -// -// Note: 122.375 must be rounded DOWN to 12237 -// in order to be consistent with apt.dat et cetera. -inline int kHz10(double freq) -{ - if (freq > 1000.) return int(freq); - return int(freq*100.0 + 0.25); -} - // Possible types of ATC type that the radios may be tuned to. // INVALID implies not tuned in to anything. enum atc_type { @@ -69,6 +55,7 @@ enum atc_type { INVALID /* must be last element; see ATC_NUM_TYPES */ }; +#ifdef OLD_ATC_MGR const int ATC_NUM_TYPES = 1 + INVALID; // DCL - new experimental ATC data store @@ -98,30 +85,32 @@ struct RunwayDetails { }; std::ostream& operator << (std::ostream& os, atc_type atc); +#endif class FGATC { - friend class FGATCMgr; + friend class FGATISMgr; public: - + FGATC(); virtual ~FGATC(); - - virtual void Init()=0; - + + virtual void init()=0; + // Run the internal calculations // Derived classes should call this method from their own Update methods if they // wish to use the response timer functionality. - virtual void Update(double dt); - - // Recieve a coded callback from the ATC menu system based on the user's selection - virtual void ReceiveUserCallback(int code); - + virtual void update(double dt); + // Indicate that this instance should output to the display if appropriate inline void SetDisplay() { _display = true; } // Indicate that this instance should not output to the display inline void SetNoDisplay() { _display = false; } +#ifdef OLD_ATC_MGR + // Receive a coded callback from the ATC menu system based on the user's selection + virtual void ReceiveUserCallback(int code); + // Generate the text of a message from its parameters and the current context. virtual std::string GenText(const std::string& m, int c); @@ -141,21 +130,24 @@ public: // AI traffic should check FreqClear() is true prior to transmitting. // The user will just have to wait for a gap in dialog as in real life. - // Return the type of ATC station that the class represents - inline atc_type GetType() { return _type; } - // Set the core ATC data - void SetStation(flightgear::CommStation* sta); inline int get_freq() const { return freq; } inline void set_freq(const int fq) {freq = fq;} inline int get_range() const { return range; } inline void set_range(const int rg) {range = rg;} +#endif + // Return the type of ATC station that the class represents + inline atc_type GetType() { return _type; } + + // Set the core ATC data + void SetStation(flightgear::CommStation* sta); + inline const std::string& get_ident() { return ident; } inline void set_ident(const std::string& id) { ident = id; } inline const std::string& get_name() { return name; } inline void set_name(const std::string& nm) { name = nm; } - + protected: // Render a transmission @@ -163,31 +155,31 @@ protected: // The refname is a string to identify this sample to the sound manager // The repeating flag indicates whether the message should be repeated continuously or played once. void Render(std::string& msg, const float volume = 1.0, - const std::string& refname = "", bool repeating = false); + const std::string& refname = "", bool repeating = false); // Cease rendering all transmission from this station. // Requires the sound manager refname if audio, else "". void NoRender(const std::string& refname); + virtual FGATCVoice* GetVoicePointer() = 0; + SGGeod _geod; SGVec3d _cart; int freq; - std::map active_on; - + flightgear::CommStation* _currentStation; + int range; std::string ident; // Code of the airport its at. std::string name; // Name transmitted in the broadcast. + std::string _currentMsg; // Current message being transmitted - // Rendering related stuff bool _voice; // Flag - true if we are using voice - bool _playing; // Indicates a message in progress - bool _voiceOK; // Flag - true if at least one voice has loaded OK - FGATCVoice* _vPtr; + bool _playing; // Indicates a message in progress SGSharedPtr _sgr; // default sample group; - +#ifdef OLD_ATC_MGR bool freqClear; // Flag to indicate if the frequency is clear of ongoing dialog bool receiving; // Flag to indicate we are receiving a transmission @@ -204,11 +196,14 @@ protected: bool responseReqd; // Flag to indicate we should be responding to a request/report double _releaseTime; double _releaseCounter; + std::string pending_transmission; // derived classes set this string before calling Transmit(...) +#endif atc_type _type; bool _display; // Flag to indicate whether we should be outputting to the ATC display. - std::string pending_transmission; // derived classes set this string before calling Transmit(...) - + private: + +#ifdef OLD_ATC_MGR // Transmission timing stuff. double _timeout; bool _pending; @@ -216,13 +211,16 @@ private: bool _transmitting; // we are transmitting double _counter; double _max_count; +#endif - SGPropertyNode_ptr _volume; + SGPropertyNode_ptr _masterVolume; SGPropertyNode_ptr _enabled; SGPropertyNode_ptr _atc_external; SGPropertyNode_ptr _internal; }; +#ifdef OLD_ATC_MGR std::istream& operator>> ( std::istream& fin, ATCData& a ); +#endif #endif // _FG_ATC_HXX diff --git a/src/ATCDCL/ATCDialogOld.cxx b/src/ATCDCL/ATCDialogOld.cxx deleted file mode 100644 index 8a6ced1..0000000 --- a/src/ATCDCL/ATCDialogOld.cxx +++ /dev/null @@ -1,308 +0,0 @@ -// ATCDialog.cxx - Functions and classes to handle the pop-up ATC dialog -// -// Written by Alexander Kappes and David Luff, started February 2003. -// -// Copyright (C) 2003 Alexander Kappes and David Luff -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include -#include - -#include
-#include // mkDialog -#include -#include
- -#include "ATCDialogOld.hxx" -#include "ATC.hxx" -#include "ATCmgr.hxx" -#include "ATCutils.hxx" -#include -#include - -#include - -using std::ostringstream; -using std::cerr; -using std::endl; - -FGATCDialog *current_atcdialog; - -// For the command manager - maybe eventually this should go in the built in command list -#if 0 -static bool do_ATC_dialog(const SGPropertyNode* arg) { - cerr << "Running ATCDCL do_ATC_dialog" << endl; - current_atcdialog->PopupDialog(); - return(true); -} - -static bool do_ATC_freq_search(const SGPropertyNode* arg) { - current_atcdialog->FreqDialog(); - return(true); -} -#endif - -ATCMenuEntry::ATCMenuEntry() { - stationid = ""; - //stationfr = 0; - transmission = ""; - menuentry = ""; - callback_code = 0; -} - -ATCMenuEntry::~ATCMenuEntry() { -} - -void atcUppercase(string &s) { - for(unsigned int i=0; i... entry matches 'name' -static SGPropertyNode *getNamedNode(SGPropertyNode *prop, const char *name) { - SGPropertyNode* p; - - for (int i = 0; i < prop->nChildren(); i++) - if ((p = getNamedNode(prop->getChild(i), name))) - return p; - - if (!strcmp(prop->getStringValue("name"), name)) - return prop; - - return 0; -} - - -FGATCDialog::FGATCDialog() { - _callbackPending = false; - _callbackTimer = 0.0; - _callbackWait = 0.0; - _callbackPtr = NULL; - _callbackCode = 0; - _gui = (NewGUI *)globals->get_subsystem("gui"); -} - -FGATCDialog::~FGATCDialog() { -} - -void FGATCDialog::Init() { - // Add ATC-dialog to the command list - //globals->get_commands()->addCommand("ATC-dialog", do_ATC_dialog); - // Add ATC-freq-search to the command list - //globals->get_commands()->addCommand("ATC-freq-search", do_ATC_freq_search); - - // initialize properties polled in Update() - //globals->get_props()->setStringValue("/sim/atc/freq-airport", ""); - //globals->get_props()->setIntValue("/sim/atc/transmission-num", -1); -} - -void FGATCDialog::Update(double dt) { - //static SGPropertyNode_ptr airport = globals->get_props()->getNode("/sim/atc/freq-airport", true); - //string s = airport->getStringValue(); - //if (!s.empty()) { - // airport->setStringValue(""); - // FreqDisplay(s); - //} - - //static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); - //int n = trans_num->getIntValue(); - //if (n >= 0) { - // trans_num->setIntValue(-1); - // PopupCallback(n); - //} - - //if(_callbackPending) { - // if(_callbackTimer > _callbackWait) { - // _callbackPtr->ReceiveUserCallback(_callbackCode); - // _callbackPtr->NotifyTransmissionFinished(fgGetString("/sim/user/callsign")); - // _callbackPending = false; - // } else { - // _callbackTimer += dt; - // } - //} -} - -// Add an entry -void FGATCDialog::add_entry(const string& station, const string& transmission, const string& menutext, atc_type type, int code) { - - ATCMenuEntry a; - - a.stationid = station; - a.transmission = transmission; - a.menuentry = menutext; - a.callback_code = code; - - (available_dialog[type])[station.c_str()].push_back(a); - -} - -void FGATCDialog::remove_entry( const string &station, const string &trans, atc_type type ) { - atcmentry_vec_type* p = &((available_dialog[type])[station]); - atcmentry_vec_iterator current = p->begin(); - while(current != p->end()) { - if(current->transmission == trans) current = p->erase(current); - else ++current; - } -} - -void FGATCDialog::remove_entry( const string &station, int code, atc_type type ) { - atcmentry_vec_type* p = &((available_dialog[type])[station]); - atcmentry_vec_iterator current = p->begin(); - while(current != p->end()) { - if(current->callback_code == code) current = p->erase(current); - else ++current; - } -} - -// query the database whether the transmission is already registered; -bool FGATCDialog::trans_reg( const string &station, const string &trans, atc_type type ) { - atcmentry_vec_type* p = &((available_dialog[type])[station]); - atcmentry_vec_iterator current = p->begin(); - for ( ; current != p->end() ; ++current ) { - if ( current->transmission == trans ) return true; - } - return false; -} - -// query the database whether the transmission is already registered; -bool FGATCDialog::trans_reg( const string &station, int code, atc_type type ) { - atcmentry_vec_type* p = &((available_dialog[type])[station]); - atcmentry_vec_iterator current = p->begin(); - for ( ; current != p->end() ; ++current ) { - if ( current->callback_code == code ) return true; - } - return false; -} - -// Display the ATC popup dialog box with options relevant to the users current situation. -void FGATCDialog::PopupDialog() { - const char *dialog_name = "atc-dialog"; - SGPropertyNode_ptr dlg = _gui->getDialogProperties(dialog_name); - if (!dlg) - return; - - _gui->closeDialog(dialog_name); - - SGPropertyNode_ptr button_group = getNamedNode(dlg, "transmission-choice"); - // remove all transmission buttons - button_group->removeChildren("button", false); - - string label; - FGATCMgr* pAtcMgr = globals->get_ATC_mgr(); - if (!pAtcMgr) - { - SG_LOG(SG_ATC, SG_ALERT, "ERROR! No ATC manager! Oops..."); - return; - } - FGATC* atcptr = pAtcMgr->GetComm1ATCPointer(); // Hardwired to comm1 at the moment - - if (!atcptr) { - label = "Not currently tuned to any ATC service"; - mkDialog(label.c_str()); - return; - } - - if(atcptr->GetType() == ATIS) { - label = "Tuned to ATIS - no communication possible"; - mkDialog(label.c_str()); - return; - } - - atcmentry_vec_type atcmlist = (available_dialog[atcptr->GetType()])[atcptr->get_ident()]; - atcmentry_vec_iterator current = atcmlist.begin(); - atcmentry_vec_iterator last = atcmlist.end(); - - if(!atcmlist.size()) { - label = "No transmission available"; - mkDialog(label.c_str()); - return; - } - - const int bufsize = 32; - char buf[bufsize]; - // loop over all entries in atcmentrylist - for (int n = 0; n < 10; ++n) { - snprintf(buf, bufsize, "/sim/atc/opt[%d]", n); - fgSetBool(buf, false); - - if (current == last) - continue; - - // add transmission button (modified copy of ) - SGPropertyNode *entry = button_group->getNode("button", n, true); - copyProperties(button_group->getNode("button-template", true), entry); - entry->removeChildren("enabled", true); - entry->setStringValue("property", buf); - entry->setIntValue("keynum", '1' + n); - if (n == 0) - entry->setBoolValue("default", true); - - snprintf(buf, bufsize, "%d", n + 1); - string legend = string(buf) + ". " + current->menuentry; - entry->setStringValue("legend", legend.c_str()); - entry->setIntValue("binding/value", n); - current++; - } - - _gui->showDialog(dialog_name); - return; -} - -void FGATCDialog::PopupCallback(int num) { - FGATCMgr* pAtcMgr = globals->get_ATC_mgr(); - if (!pAtcMgr) - { - SG_LOG(SG_ATC, SG_ALERT, "ERROR! No ATC manager! Oops..."); - return; - } - FGATC* atcptr = pAtcMgr->GetComm1ATCPointer(); // FIXME - Hardwired to comm1 at the moment - - if (!atcptr) - return; - - if (atcptr->GetType() == TOWER) { - //cout << "TOWER " << endl; - //cout << "ident is " << atcptr->get_ident() << endl; - atcmentry_vec_type atcmlist = (available_dialog[TOWER])[atcptr->get_ident()]; - int size = atcmlist.size(); - if(size && num < size) { - //cout << "Doing callback...\n"; - ATCMenuEntry a = atcmlist[num]; - atcptr->SetFreqInUse(); - string pilot = atcptr->GenText(a.transmission, a.callback_code); - fgSetString("/sim/messages/pilot", pilot.c_str()); - // This is the user's speech getting displayed. - _callbackPending = true; - _callbackTimer = 0.0; - _callbackWait = 5.0; - _callbackPtr = atcptr; - _callbackCode = a.callback_code; - } else { - //cout << "No options available...\n"; - } - //cout << "Donded" << endl; - } -} - - - diff --git a/src/ATCDCL/ATCDialogOld.hxx b/src/ATCDCL/ATCDialogOld.hxx deleted file mode 100644 index b60fa59..0000000 --- a/src/ATCDCL/ATCDialogOld.hxx +++ /dev/null @@ -1,116 +0,0 @@ -// ATCDialog.hxx - Functions and classes to handle the pop-up ATC dialog -// -// Written by Alexander Kappes and David Luff, started February 2003. -// -// Copyright (C) 2003 Alexander Kappes and David Luff -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -#ifndef ATC_DIALOG_HXX -#define ATC_DIALOG_HXX - -#include - -#include -#include - -#include "ATC.hxx" - -using std::vector; -using std::map; - -class NewGUI; - -// ATCMenuEntry - an encapsulation of an entry in the ATC dialog -struct ATCMenuEntry { - - string stationid; // ID of transmitting station - //int stationfr; // ? - string transmission; // Actual speech of transmission - string menuentry; // Shortened version for display in the dialog - int callback_code; // This code is supplied by the registering station, and then - // returned to the registering station if that option is chosen. - // The actual value is only understood by the registering station - - // FGATCDialog only stores it and returns it if appropriate. - - ATCMenuEntry(); - ~ATCMenuEntry(); -}; - -typedef vector < ATCMenuEntry > atcmentry_vec_type; -typedef atcmentry_vec_type::iterator atcmentry_vec_iterator; - -typedef map < string, atcmentry_vec_type > atcmentry_map_type; -typedef atcmentry_map_type::iterator atcmentry_map_iterator; - -void atcUppercase(string &s); - -//void ATCDialogInit(); -//void ATCDoDialog(atc_type type); - -class FGATCDialog { - -public: - - FGATCDialog(); - ~FGATCDialog(); - - void Init(); - - void Update(double dt); - - void PopupDialog(); - - void PopupCallback(int); - - void add_entry( const string& station, const string& transmission, const string& menutext, atc_type type, int code); - - void remove_entry( const string &station, const string &trans, atc_type type ); - - void remove_entry( const string &station, int code, atc_type type ); - - // query the database whether the transmission is already registered; - bool trans_reg( const string &station, const string &trans, atc_type type ); - - // query the database whether the transmission is already registered; - bool trans_reg( const string &station, int code, atc_type type ); - - // Display a frequency search dialog for nearby stations - void FreqDialog(); - - // Display the comm ATC frequencies for airport ident - // where ident is a valid ICAO code. - void FreqDisplay(string& ident); - -private: - - atcmentry_map_type available_dialog[ATC_NUM_TYPES]; - - int freq; - bool reset; - - bool _callbackPending; - double _callbackTimer; - double _callbackWait; - FGATC* _callbackPtr; - int _callbackCode; - - NewGUI *_gui; -}; - -extern FGATCDialog *current_atcdialog; - -#endif // ATC_DIALOG_HXX - diff --git a/src/ATCDCL/ATCProjection.cxx b/src/ATCDCL/ATCProjection.cxx index fb6b1be..ecc08cc 100644 --- a/src/ATCDCL/ATCProjection.cxx +++ b/src/ATCDCL/ATCProjection.cxx @@ -1,4 +1,4 @@ -// ATCProjection.cxx - A convienience projection class for the ATC/AI system. +// ATCProjection.cxx - A convenience projection class for the ATC/AI system. // // Written by David Luff, started 2002. // diff --git a/src/ATCDCL/ATCProjection.hxx b/src/ATCDCL/ATCProjection.hxx index fbc93a0..94dfb92 100644 --- a/src/ATCDCL/ATCProjection.hxx +++ b/src/ATCDCL/ATCProjection.hxx @@ -1,4 +1,4 @@ -// ATCProjection.hxx - A convienience projection class for the ATC/AI system. +// ATCProjection.hxx - A convenience projection class for the ATC/AI system. // // Written by David Luff, started 2002. // diff --git a/src/ATCDCL/ATCVoice.cxx b/src/ATCDCL/ATCVoice.cxx index 07825f7..71f15e5 100644 --- a/src/ATCDCL/ATCVoice.cxx +++ b/src/ATCDCL/ATCVoice.cxx @@ -26,12 +26,16 @@ #include "ATCVoice.hxx" #include +#include #include #include #include #include #include +#include +#include + #include #include #include @@ -41,49 +45,124 @@ using namespace std; -FGATCVoice::FGATCVoice() { - SoundData = 0; - rawSoundData = 0; +FGATCVoice::FGATCVoice() : + rawSoundData(0), + rawDataSize(0), + SoundData(0) +{ } FGATCVoice::~FGATCVoice() { if (rawSoundData) - free( rawSoundData ); + free( rawSoundData ); delete SoundData; } -// Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce). +// Load all data for the requested voice. // Return true if successful. -bool FGATCVoice::LoadVoice(const string& voice) { - std::ifstream fin; +bool FGATCVoice::LoadVoice(const string& voicename) +{ + rawDataSize = 0; + if (rawSoundData) + free(rawSoundData); + rawSoundData = NULL; + + // determine voice directory + SGPath voicepath = globals->get_fg_root(); + voicepath.append( "ATC" ); + voicepath.append( "voices" ); + voicepath.append( voicename ); + + simgear::Dir d(voicepath); + if (!d.exists()) + { + SG_LOG(SG_ATC, SG_ALERT, "Unable to load ATIS voice. No such directory: " << voicepath.str()); + return false; + } + + // load all files from the voice's directory + simgear::PathList paths = d.children(simgear::Dir::TYPE_FILE); + bool Ok = false; + for (unsigned int i=0; iget_soundmgr(); + int format, freq; + void *data; + size_t size; + if (!smgr->load(path.str(), &data, &format, &size, &freq)) + return false; + + // append to existing data + if (!rawSoundData) + rawSoundData = (char*)data; + else + { + rawSoundData = (char*) realloc(rawSoundData, rawDataSize + size); + // new data starts behind existing sound data + offset = rawDataSize; + if (!rawSoundData) + { + SG_LOG(SG_ATC, SG_ALERT, "Out of memory. Cannot load file " << path.str()); + rawDataSize = 0; + return false; + } + // append to existing sound data + memcpy(rawSoundData+offset, data, size); + free(data); + data = NULL; + } + rawDataSize += size; - SGPath path = globals->get_fg_root(); - string file = voice + ".wav"; - path.append( "ATC" ); - path.append( file ); - - string full_path = path.str(); - int format, freq; - SGSoundMgr *smgr = globals->get_soundmgr(); - void *data; - if (!smgr->load(full_path, &data, &format, &rawDataSize, &freq)) - return false; - rawSoundData = (char*)data; #ifdef VOICE_TEST cout << "ATCVoice: format: " << format << " size: " << rawDataSize << endl; -#endif - path = globals->get_fg_root(); - string wordPath = "ATC/" + voice + ".vce"; - path.append(wordPath); +#endif + + // load and parse index file (.vce) + return ParseVoiceIndex(basepath, file, offset); +} + +// Load and parse a voice index file (.vce) +bool FGATCVoice::ParseVoiceIndex(const SGPath& basepath, const string& file, size_t globaloffset) +{ + // path to voice index file + SGPath path(basepath); + path.append(file + ".vce"); // Now load the word data + std::ifstream fin; fin.open(path.c_str(), ios::in); if(!fin) { SG_LOG(SG_ATC, SG_ALERT, "Unable to open input file " << path.c_str()); return(false); } - SG_LOG(SG_ATC, SG_INFO, "Opened word data file " << wordPath << " OK..."); + SG_LOG(SG_ATC, SG_INFO, "Opened word data file " << path.c_str() << " OK..."); + char numwds[10]; char wrd[100]; string wrdstr; @@ -92,30 +171,44 @@ bool FGATCVoice::LoadVoice(const string& voice) { unsigned int wrdOffset; // Offset into the raw sound data that the word sample begins unsigned int wrdLength; // Length of the word sample in bytes WordData wd; + + // first entry: number of words in the index fin >> numwds; unsigned int numwords = atoi(numwds); //cout << numwords << '\n'; + + // now load each word, its file offset and length for(unsigned int i=0; i < numwords; ++i) { + // read data fin >> wrd; - wrdstr = wrd; fin >> wrdOffsetStr; fin >> wrdLengthStr; + + wrdstr = wrd; wrdOffset = atoi(wrdOffsetStr); wrdLength = atoi(wrdLengthStr); - wd.offset = wrdOffset; + + // store word in map + wd.offset = wrdOffset + globaloffset; wd.length = wrdLength; wordMap[wrdstr] = wd; - string ws2 = wrdstr; - for(string::iterator p = ws2.begin(); p != ws2.end(); p++){ - *p = tolower(*p); - if (*p == '-') *p = '_'; - } - if (wrdstr != ws2) wordMap[ws2] = wd; + + // post-process words + string ws2 = wrdstr; + for(string::iterator p = ws2.begin(); p != ws2.end(); p++){ + *p = tolower(*p); + if (*p == '-') + *p = '_'; + } + + // store alternative version of word (lowercase/no hyphen) + if (wrdstr != ws2) + wordMap[ws2] = wd; //cout << wrd << "\t\t" << wrdOffset << "\t\t" << wrdLength << '\n'; //cout << i << '\n'; } - + fin.close(); return(true); } diff --git a/src/ATCDCL/ATCVoice.hxx b/src/ATCDCL/ATCVoice.hxx index 5128cc5..55fe68a 100644 --- a/src/ATCDCL/ATCVoice.hxx +++ b/src/ATCDCL/ATCVoice.hxx @@ -28,6 +28,7 @@ #include class SGSoundSample; +class SGPath; struct WordData { unsigned int offset; // Offset of beginning of word sample into raw sound sample @@ -47,13 +48,15 @@ public: // Load the two voice files - one containing the raw sound data (.wav) and one containing the word positions (.vce). // Return true if successful. - bool LoadVoice(const std::string& voice); + bool LoadVoice(const std::string& voicename); // Given a desired message, return a pointer to the data buffer and write the buffer length into len. // Sets len to something other than 0 if the returned buffer is valid. void* WriteMessage(const std::string& message, size_t *len); private: + bool AppendVoiceFile(const SGPath& basepath, const std::string& file); + bool ParseVoiceIndex(const SGPath& basepath, const std::string& file, size_t globaloffset); // the sound and word position data char* rawSoundData; diff --git a/src/ATCDCL/ATCmgr.cxx b/src/ATCDCL/ATCmgr.cxx deleted file mode 100644 index 334523f..0000000 --- a/src/ATCDCL/ATCmgr.cxx +++ /dev/null @@ -1,316 +0,0 @@ -// ATCmgr.cxx - Implementation of FGATCMgr - a global Flightgear ATC manager. -// -// Written by David Luff, started February 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include - -#include -#include -#include
- -#include "ATCmgr.hxx" -#include "ATCDialogOld.hxx" -#include "ATCutils.hxx" -#include "atis.hxx" - -using flightgear::CommStation; - -FGATCMgr::FGATCMgr() : - initDone(false), - atc_list(new atc_list_type), -#ifdef ENABLE_AUDIO_SUPPORT - voice(true), - voiceOK(false), - v1(0) -#else - voice(false), -#endif -{ - globals->set_ATC_mgr(this); -} - -FGATCMgr::~FGATCMgr() { - globals->set_ATC_mgr(NULL); - delete v1; -} - -void FGATCMgr::bind() { -} - -void FGATCMgr::unbind() { -} - -void FGATCMgr::init() { - //cout << "ATCMgr::init called..." << endl; - - lon_node = fgGetNode("/position/longitude-deg", true); - lat_node = fgGetNode("/position/latitude-deg", true); - elev_node = fgGetNode("/position/altitude-ft", true); - atc_list_itr = atc_list->begin(); - - // Search for connected ATC stations once per 0.8 seconds or so - // globals->get_event_mgr()->add( "fgATCSearch()", fgATCSearch, - // FGEvent::FG_EVENT_READY, 800); - // - // For some reason the above doesn't compile - including Time/event.hxx stops compilation. - // Is this still true after the reorganization of the event managar?? - // -EMH- - - // Initialise the ATC Dialogs - SG_LOG(SG_ATC, SG_INFO, " ATC Dialog System"); - current_atcdialog = new FGATCDialog; - current_atcdialog->Init(); - - initDone = true; - //cout << "ATCmgr::init done!" << endl; -} - -void FGATCMgr::update(double dt) { - if(!initDone) { - init(); - SG_LOG(SG_ATC, SG_WARN, "Warning - ATCMgr::update(...) called before ATCMgr::init()"); - } - - current_atcdialog->Update(dt); - - //cout << "Entering update..." << endl; - //Traverse the list of active stations. - //Only update one class per update step to avoid the whole ATC system having to calculate between frames. - //Eventually we should only update every so many steps. - //cout << "In FGATCMgr::update - atc_list.size = " << atc_list->size() << endl; - if(atc_list->size()) { - if(atc_list_itr == atc_list->end()) { - atc_list_itr = atc_list->begin(); - } - //cout << "Updating " << (*atc_list_itr)->get_ident() << ' ' << (*atc_list_itr)->GetType() << '\n'; - //cout << "Freq = " << (*atc_list_itr)->get_freq() << '\n'; - //cout << "Updating...\n"; - (*atc_list_itr).second->Update(dt * atc_list->size()); - //cout << "Done ATC update..." << endl; - ++atc_list_itr; - } - -#ifdef ATC_TEST - //cout << "ATC_LIST: " << atc_list->size() << ' '; - for(atc_list_iterator it = atc_list->begin(); it != atc_list->end(); it++) { - cout << (*it)->get_ident() << ' '; - } - //cout << '\n'; -#endif - - // Search the tuned frequencies every now and then - this should be done with the event scheduler - static int i = 0; // Very ugly - but there should only ever be one instance of FGATCMgr. - if(i == 15) { - //cout << "About to search navcomm1" << endl; - FreqSearch("comm", 0); - FreqSearch("nav", 0); - } - if(i == 30) { - //cout << "About to search navcomm2" << endl; - FreqSearch("comm", 1); - FreqSearch("nav", 1); - i = 0; - } - ++i; - - //cout << "comm1 type = " << comm_type[0] << '\n'; - //cout << "Leaving update..." << endl; -} - -typedef map MSI; - -void FGATCMgr::ZapOtherService(const string ncunit, const string svc_name){ - for (atc_list_iterator svc = atc_list->begin(); svc != atc_list->end(); svc++) { - - if (svc->first != svc_name) { - MSI &actv = svc->second->active_on; - // OK, we have found some OTHER service; - // see if it is (was) active on our unit: - if (!actv.count(ncunit)) continue; - //cout << "Eradicating '" << svc->first << "' from: " << ncunit << endl; - actv.erase(ncunit); - if (!actv.size()) { - //cout << "Eradicating service: '" << svc->first << "'" << endl; - svc->second->SetNoDisplay(); - svc->second->Update(0); // one last update - SG_LOG(SG_ATC, SG_INFO, "would have erased ATC service:" << svc->second->get_name()<< "/" - << svc->second->get_ident()); - // delete svc->second; - atc_list->erase(svc); - // ALL pointers into the ATC list are now invalid, - // so let's reset them: - atc_list_itr = atc_list->begin(); - } - break; // cannot be duplicates in the active list - } - } -} - -// Find in list - return a currently active ATC pointer given ICAO code and type -// Return NULL if the given service is not in the list -// - *** THE CALLING FUNCTION MUST CHECK FOR THIS *** -FGATC* FGATCMgr::FindInList(const string& id, const atc_type& tp) { - string ndx = id + decimalNumeral(tp); - if (!atc_list->count(ndx)) return 0; - return (*atc_list)[ndx]; -} - -// Return a pointer to an appropriate voice for a given type of ATC -// creating the voice if necessary - ie. make sure exactly one copy -// of every voice in use exists in memory. -// -// TODO - in the future this will get more complex and dole out country/airport -// specific voices, and possible make sure that the same voice doesn't get used -// at different airports in quick succession if a large enough selection are available. -FGATCVoice* FGATCMgr::GetVoicePointer(const atc_type& type) { - // TODO - implement me better - maintain a list of loaded voices and other voices!! - if(voice) { - switch(type) { - case ATIS: case AWOS: -#ifdef ENABLE_AUDIO_SUPPORT - // Delayed loading fo all available voices, needed because the - // soundmanager might not be initialized (at all) at this point. - // For now we'll do one hardwired one - - /* I've loaded the voice even if /sim/sound/pause is true - * since I know no way of forcing load of the voice if the user - * subsequently switches /sim/sound/audible to true. - * (which is the right thing to do -- CLO) :-) - */ - if (!voiceOK && fgGetBool("/sim/sound/working")) { - v1 = new FGATCVoice; - try { - voiceOK = v1->LoadVoice("default"); - voice = voiceOK; - } catch ( sg_io_exception & e) { - voiceOK = false; - SG_LOG(SG_ATC, SG_ALERT, "Unable to load default voice : " - << e.getFormattedMessage().c_str()); - voice = false; - delete v1; - v1 = 0; - } - } -#endif - if(voiceOK) { - return(v1); - } - case TOWER: - return(NULL); - case APPROACH: - return(NULL); - case GROUND: - return(NULL); - default: - return(NULL); - } - return(NULL); - } else { - return(NULL); - } -} - -// Search for ATC stations by frequency -void FGATCMgr::FreqSearch(const string navcomm, const int unit) { - - - string ncunit = navcomm + "[" + decimalNumeral(unit) + "]"; - string commbase = "/instrumentation/" + ncunit; - string commfreq = commbase + "/frequencies/selected-mhz"; - SGPropertyNode_ptr comm_node = fgGetNode(commfreq.c_str(), false); - - //cout << "FreqSearch: " << ncunit - // << " node: " << comm_node << endl; - if (!comm_node) return; // no such radio unit - - ATCData data; - // Note: 122.375 must be rounded DOWN to 12237 - // in order to be consistent with apt.dat et cetera. - int freqKhz = static_cast(comm_node->getDoubleValue() * 100.0 + 0.25); - - _aircraftPos = SGGeod::fromDegFt(lon_node->getDoubleValue(), - lat_node->getDoubleValue(), elev_node->getDoubleValue()); - - class RangeFilter : public CommStation::Filter { - public: - RangeFilter( const SGGeod & pos ) : - CommStation::Filter(), - _cart(SGVec3d::fromGeod(pos)), - _pos(pos) - {} - - virtual bool pass(FGPositioned* aPos) const - { - flightgear::CommStation * stn = dynamic_cast(aPos); - if( NULL == stn ) return false; - // do the range check in cartesian space, since the distances are potentially - // large enough that the geodetic functions become unstable - // (eg, station on opposite side of the planet) - double rangeM = SGMiscd::max( stn->rangeNm(), 10.0 ) * SG_NM_TO_METER; - double d2 = distSqr( aPos->cart(), _cart); - - return d2 <= (rangeM * rangeM); - } - private: - SGVec3d _cart; - SGGeod _pos; - }; - - RangeFilter rangeFilter(_aircraftPos ); - - CommStation* sta = CommStation::findByFreq(freqKhz, _aircraftPos, &rangeFilter ); - if (!sta) { - ZapOtherService(ncunit, "x x x"); - return; - } - - // Get rid of any *other* service that was on this radio unit: - FGPositioned::Type ty = sta->type(); - string svc_name = sta->ident() + FGPositioned::nameForType(ty); - ZapOtherService(ncunit, svc_name); - // See if the service already exists, possibly connected to - // some other radio unit: - if (atc_list->count(svc_name)) { - // make sure the service knows it's tuned on this radio: - FGATC* svc = (*atc_list)[svc_name]; - svc->active_on[ncunit] = 1; - svc->SetDisplay(); - return; - } - - // This was a switch-case statement but the compiler didn't like - // the new variable creation with it. - if(ty == FGPositioned::FREQ_ATIS || ty == FGPositioned::FREQ_AWOS) { - (*atc_list)[svc_name] = new FGATIS; - FGATC* svc = (*atc_list)[svc_name]; - if(svc != NULL) { - svc->SetStation(sta); - svc->active_on[ncunit] = 1; - svc->SetDisplay(); - svc->Init(); - } - } - -} diff --git a/src/ATCDCL/ATCmgr.hxx b/src/ATCDCL/ATCmgr.hxx deleted file mode 100644 index 179f65f..0000000 --- a/src/ATCDCL/ATCmgr.hxx +++ /dev/null @@ -1,114 +0,0 @@ -// ATCMgr.hxx - definition of FGATCMgr -// - a global management class for FlightGear generated ATC -// -// Written by David Luff, started February 2002. -// -// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License as -// published by the Free Software Foundation; either version 2 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -#ifndef _FG_ATCMGR_HXX -#define _FG_ATCMGR_HXX - -#include - -#include -#include -#include - -#include "ATC.hxx" - -class FGATCMgr : public SGSubsystem -{ - -private: - - bool initDone; // Hack - guard against update getting called before init - - // A list of pointers to all currently active ATC classes - typedef std::map atc_list_type; - typedef atc_list_type::iterator atc_list_iterator; - typedef atc_list_type::const_iterator atc_list_const_iterator; - - // Everything put in this list should be created dynamically - // on the heap and ***DELETED WHEN REMOVED!!!!!*** - atc_list_type* atc_list; - atc_list_iterator atc_list_itr; - // Any member function of FGATCMgr is permitted to leave this iterator pointing - // at any point in or at the end of the list. - // Hence any new access must explicitly first check for atc_list.end() before dereferencing. - - // Position of the Users Aircraft - SGGeod _aircraftPos; - - // Pointers to current users position - SGPropertyNode_ptr lon_node; - SGPropertyNode_ptr lat_node; - SGPropertyNode_ptr elev_node; - - //FGATIS atis; - - // Voice related stuff - bool voice; // Flag - true if we are using voice -#ifdef ENABLE_AUDIO_SUPPORT - bool voiceOK; // Flag - true if at least one voice has loaded OK - FGATCVoice* v1; -#endif - -public: - - FGATCMgr(); - ~FGATCMgr(); - - void init(); - - void bind(); - - void unbind(); - - void update(double dt); - - - // Return a pointer to an appropriate voice for a given type of ATC - // creating the voice if necessary - ie. make sure exactly one copy - // of every voice in use exists in memory. - // - // TODO - in the future this will get more complex and dole out country/airport - // specific voices, and possible make sure that the same voice doesn't get used - // at different airports in quick succession if a large enough selection are available. - FGATCVoice* GetVoicePointer(const atc_type& type); - - atc_type GetComm1ATCType() { return(INVALID/* kludge */); } - FGATC* GetComm1ATCPointer() { return(0/* kludge */); } - atc_type GetComm2ATCType() { return(INVALID); } - FGATC* GetComm2ATCPointer() { return(0/* kludge */); } - - -private: - - // Remove a class from the atc_list and delete it from memory - // *if* no other comm channel or AI plane is using it. - void ZapOtherService(const std::string ncunit, const std::string svc_name); - - // Return a pointer to a class in the list given ICAO code and type - // Return NULL if the given service is not in the list - // - *** THE CALLING FUNCTION MUST CHECK FOR THIS *** - FGATC* FindInList(const std::string& id, const atc_type& tp); - - // Search the specified radio for stations on the same frequency and in range. - void FreqSearch(const std::string navcomm, const int unit); -}; - -#endif // _FG_ATCMGR_HXX diff --git a/src/ATCDCL/ATCutils.cxx b/src/ATCDCL/ATCutils.cxx index 313405e..2960401 100644 --- a/src/ATCDCL/ATCutils.cxx +++ b/src/ATCDCL/ATCutils.cxx @@ -23,8 +23,8 @@ #endif #include +#include -#include #include #include #include diff --git a/src/ATCDCL/ATCutils.hxx b/src/ATCDCL/ATCutils.hxx index 01f2e9e..38f0bda 100644 --- a/src/ATCDCL/ATCutils.hxx +++ b/src/ATCDCL/ATCutils.hxx @@ -21,7 +21,6 @@ #include #include -#include #include #include using std::string; diff --git a/src/ATCDCL/ATISmgr.cxx b/src/ATCDCL/ATISmgr.cxx new file mode 100644 index 0000000..b52d956 --- /dev/null +++ b/src/ATCDCL/ATISmgr.cxx @@ -0,0 +1,149 @@ +// ATISmgr.cxx - Implementation of FGATISMgr - a global Flightgear ATIS manager. +// +// Written by David Luff, started February 2002. +// +// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk +// Copyright (C) 2012 Thorsten Brehm +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include
+ +#include "ATISmgr.hxx" +#include "atis.hxx" + +FGATISMgr::FGATISMgr() : + _currentUnit(0), + _maxCommRadios(4) +#ifdef ENABLE_AUDIO_SUPPORT + ,useVoice(true), + voice(0) +#endif +{ + globals->set_ATIS_mgr(this); +} + +FGATISMgr::~FGATISMgr() +{ + globals->set_ATIS_mgr(NULL); + + for (unsigned int unit = 0;unit < _maxCommRadios; ++unit) + { + delete radios[unit]; + radios[unit] = NULL; + } + +#ifdef ENABLE_AUDIO_SUPPORT + delete voice; +#endif +} + +void FGATISMgr::init() +{ + for (unsigned int unit = 0;unit < _maxCommRadios; ++unit) + { + if (unit < _maxCommRadios/2) + radios.push_back(new FGATIS("comm", unit)); + else + radios.push_back(new FGATIS("nav", unit - _maxCommRadios/2)); + } +} + +void FGATISMgr::reinit() +{ +#ifdef ENABLE_AUDIO_SUPPORT + if ((voiceName != "")&& + (voiceName != fgGetString("/sim/atis/voice", "default"))) + { + voiceName = fgGetString("/sim/atis/voice", "default"); + delete voice; + voice = NULL; + useVoice = true; + } +#endif +} + +void FGATISMgr::update(double dt) +{ + // update only runs every now and then (1-2 per second) + if (++_currentUnit >= _maxCommRadios) + _currentUnit = 0; + + FGATC* commRadio = radios[_currentUnit]; + if (commRadio) + commRadio->update(dt * _maxCommRadios); +} + +// Return a pointer to an appropriate voice for a given type of ATC +// creating the voice if necessary - i.e. make sure exactly one copy +// of every voice in use exists in memory. +// +// TODO - in the future this will get more complex and dole out country/airport +// specific voices, and possible make sure that the same voice doesn't get used +// at different airports in quick succession if a large enough selection are available. +FGATCVoice* FGATISMgr::GetVoicePointer(const atc_type& type) +{ +#ifdef ENABLE_AUDIO_SUPPORT + // TODO - implement me better - maintain a list of loaded voices and other voices!! + if(useVoice) + { + switch(type) + { + case ATIS: case AWOS: + // Delayed loading for all available voices, needed because the + // sound manager might not be initialized (at all) at this point. + // For now we'll do one hard-wired one + + /* I've loaded the voice even if /sim/sound/pause is true + * since I know no way of forcing load of the voice if the user + * subsequently switches /sim/sound/audible to true. + * (which is the right thing to do -- CLO) :-) + */ + if (!voice && fgGetBool("/sim/sound/working")) { + voice = new FGATCVoice; + voiceName = fgGetString("/sim/atis/voice", "default"); + try { + useVoice = voice->LoadVoice(voiceName); + } catch ( sg_io_exception & e) { + SG_LOG(SG_ATC, SG_ALERT, "Unable to load voice '" << voiceName << "': " + << e.getFormattedMessage().c_str()); + useVoice = false; + delete voice; + voice = 0; + } + } + return voice; + case TOWER: + return NULL; + case APPROACH: + return NULL; + case GROUND: + return NULL; + default: + return NULL; + } + } +#endif + + return NULL; +} diff --git a/src/ATCDCL/ATISmgr.hxx b/src/ATCDCL/ATISmgr.hxx new file mode 100644 index 0000000..29aaef4 --- /dev/null +++ b/src/ATCDCL/ATISmgr.hxx @@ -0,0 +1,66 @@ +// ATISmgr.hxx - definition of FGATISMgr +// - a global management class for FlightGear generated ATIS +// +// Written by David Luff, started February 2002. +// +// Copyright (C) 2002 David C Luff - david.luff@nottingham.ac.uk +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef _FG_ATISMGR_HXX +#define _FG_ATISMGR_HXX + +#include + +#include + +#include "ATC.hxx" + +class FGATISMgr : public SGSubsystem +{ +private: + // A vector containing all comm radios + std::vector radios; + + unsigned int _currentUnit; + unsigned int _maxCommRadios; + +#ifdef ENABLE_AUDIO_SUPPORT + bool useVoice; // Flag - true if we are using voice + FGATCVoice* voice; + std::string voiceName; // currently loaded voice name +#endif + +public: + FGATISMgr(); + ~FGATISMgr(); + + void init(); + void reinit(); + void update(double dt); + + // Return a pointer to an appropriate voice for a given type of ATC + // creating the voice if necessary - i.e. make sure exactly one copy + // of every voice in use exists in memory. + // + // TODO - in the future this will get more complex and dole out country/airport + // specific voices, and possible make sure that the same voice doesn't get used + // at different airports in quick succession if a large enough selection are available. + FGATCVoice* GetVoicePointer(const atc_type& type); + +private: +}; + +#endif // _FG_ATISMGR_HXX diff --git a/src/ATCDCL/CMakeLists.txt b/src/ATCDCL/CMakeLists.txt index 8f03631..fd1a373 100644 --- a/src/ATCDCL/CMakeLists.txt +++ b/src/ATCDCL/CMakeLists.txt @@ -3,9 +3,8 @@ include(FlightGearComponent) set(SOURCES ATC.cxx atis.cxx - ATCDialogOld.cxx ATCVoice.cxx - ATCmgr.cxx + ATISmgr.cxx ATCutils.cxx ATCProjection.cxx ) @@ -13,9 +12,8 @@ set(SOURCES set(HEADERS ATC.hxx atis.hxx - ATCDialogOld.hxx ATCVoice.hxx - ATCmgr.hxx + ATISmgr.hxx ATCutils.hxx ATCProjection.hxx atis_lexicon.hxx diff --git a/src/ATCDCL/atis.cxx b/src/ATCDCL/atis.cxx index b87369e..3bc9e2f 100644 --- a/src/ATCDCL/atis.cxx +++ b/src/ATCDCL/atis.cxx @@ -2,6 +2,7 @@ // This is the implementation of the FGATIS class // // Written by David Luff, started October 2001. +// Extended by Thorsten Brehm, October 2012. // // Copyright (C) 2001 David C Luff - david.luff@nottingham.ac.uk // @@ -36,6 +37,7 @@ #include #include #include +#include #include // atoi() #include // sprintf @@ -55,9 +57,11 @@ #include #include +#include +#include #include "ATCutils.hxx" -#include "ATCmgr.hxx" +#include "ATISmgr.hxx" using std::string; using std::map; @@ -65,30 +69,50 @@ using std::cout; using std::cout; using boost::ref; using boost::tie; +using flightgear::CommStation; -FGATIS::FGATIS() : +FGATIS::FGATIS(const std::string& name, int num) : + _name(name), + _num(num), + _cb_attention(this, &FGATIS::attend, fgGetNode("/environment/attention", true)), transmission(""), trans_ident(""), old_volume(0), atis_failed(false), + msg_time(0), + cur_time(0), msg_OK(0), - attention(0), + _attention(false), + _check_transmission(true), _prev_display(0), - refname("atis") + _time_before_search_sec(0), + _last_frequency(0) { - FGATCMgr* pAtcMgr = globals->get_ATC_mgr(); - if (!pAtcMgr) + _root = fgGetNode("/instrumentation", true)->getNode(_name, num, true); + _volume = _root->getNode("volume",true); + _serviceable = _root->getNode("serviceable",true); + + if (name != "nav") { - SG_LOG(SG_ATC, SG_ALERT, "ERROR! No ATC manager! Oops..."); - _vPtr = NULL; - } - else - _vPtr = pAtcMgr->GetVoicePointer(ATIS); - _voiceOK = (_vPtr == NULL ? false : true); - if (!(_type != ATIS || _type == AWOS)) { - SG_LOG(SG_ATC, SG_ALERT, "ERROR - _type not ATIS or AWOS in atis.cxx"); + // only drive "operable" for non-nav instruments (nav radio drives this separately) + _operable = _root->getNode("operable",true); + _operable->setBoolValue(false); } - fgTie("/environment/attention", this, (int_getter)0, &FGATIS::attend); + + _electrical = fgGetNode("/systems/electrical/outputs",true)->getNode(_name,num, true); + _atis = _root->getNode("atis",true); + _freq = _root->getNode("frequencies/selected-mhz",true); + + // current position + _lon_node = fgGetNode("/position/longitude-deg", true); + _lat_node = fgGetNode("/position/latitude-deg", true); + _elev_node = fgGetNode("/position/altitude-ft", true); + + // backward compatibility: some properties may not exist (but default to "ON") + if (!_serviceable->hasValue()) + _serviceable->setBoolValue(true); + if (!_electrical->hasValue()) + _electrical->setDoubleValue(24.0); /////////////// // FIXME: This would be more flexible and more extensible @@ -97,46 +121,66 @@ FGATIS::FGATIS() : // // Load the remap list from the .hxx file: using namespace lex; -# define NIL "" -# define REMAP(from,to) _remap[#from] = to; -# include "atis_remap.hxx" -# undef REMAP -# undef NIL -#ifdef ATIS_TEST - SG_LOG(SG_ATC, SG_ALERT, "ATIS initialized"); -#endif + # define NIL "" + # define REMAP(from,to) _remap[#from] = to; + # include "atis_remap.hxx" + # undef REMAP + # undef NIL + + #ifdef ATIS_TEST + SG_LOG(SG_ATC, SG_ALERT, "ATIS initialized"); + #endif + + _report.psl = 0; } // Hint: // http://localhost:5400/environment/attention?value=1&submit=update -FGATIS::~FGATIS() { - fgUntie("/environment/attention"); +FGATCVoice* FGATIS::GetVoicePointer() +{ + FGATISMgr* pAtisMgr = globals->get_ATIS_mgr(); + if (!pAtisMgr) + { + SG_LOG(SG_ATC, SG_ALERT, "ERROR! No ATIS manager! Oops..."); + return NULL; + } + + return pAtisMgr->GetVoicePointer(ATIS); } -void FGATIS::Init() { +void FGATIS::init() +{ // Nothing to see here. Move along. } +void FGATIS::reinit() +{ + _time_before_search_sec = 0; + _check_transmission = true; +} + void -FGATIS::attend (int attn) +FGATIS::attend(SGPropertyNode* node) { - attention = attn; + if (node->getBoolValue()) + _attention = true; #ifdef ATMO_TEST int flag = fgGetInt("/sim/logging/atmo"); if (flag) { FGAltimeter().check_model(); - FGAltimeter().dump_stack(); + FGAltimeter().dump_stack(); } #endif } // Main update function - checks whether we are displaying or not the correct message. -void FGATIS::Update(double dt) { +void FGATIS::update(double dt) { cur_time = globals->get_time_params()->get_cur_time(); msg_OK = (msg_time < cur_time); + #ifdef ATIS_TEST if (msg_OK || _display != _prev_display) { cout << "ATIS Update: " << _display << " " << _prev_display @@ -146,40 +190,61 @@ void FGATIS::Update(double dt) { msg_time = cur_time; } #endif - if(_display) { - double volume(0); - for (map::iterator act = active_on.begin(); - act != active_on.end(); act++) { - string prop = "/instrumentation/" + act->first + "/volume"; - volume += globals->get_props()->getDoubleValue(prop.c_str()); - } - -// Check if we need to update the message -// - basically every hour and if the weather changes significantly at the station -// If !_prev_display, the radio had been detuned for a while and our -// "transmission" variable was lost when we were de-instantiated. - int rslt = GenTransmission(!_prev_display, attention); - TreeOut(msg_OK); - if (rslt || volume != old_volume) { - //cout << "ATIS calling ATC::render volume: " << volume << endl; - Render(transmission, volume, refname, true); + + double volume = 0; + if ((_electrical->getDoubleValue() > 8) && _serviceable->getBoolValue()) + { + // radio is switched on and OK + if (_operable.valid()) + _operable->setBoolValue(true); + + _check_transmission |= search(dt); + + if (_display) + { + volume = _volume->getDoubleValue(); + } + } + else + { + // radio is OFF + if (_operable.valid()) + _operable->setBoolValue(false); + _time_before_search_sec = 0; + } + + if (volume > 0.05) + { + bool changed = false; + if (_check_transmission) + { + _check_transmission = false; + // Check if we need to update the message + // - basically every hour and if the weather changes significantly at the station + // If !_prev_display, the radio had been detuned for a while and our + // "transmission" variable was lost when we were de-instantiated. + if (genTransmission(!_prev_display, _attention)) + { + // update output property + treeOut(msg_OK); + changed = true; + } + } + + if (changed || volume != old_volume) { + // audio output enabled + Render(transmission, volume, _name, true); old_volume = volume; } + _prev_display = _display; } else { -// We shouldn't be displaying - //cout << "ATIS.CXX - calling NoRender()..." << endl; - NoRender(refname); + // silence + NoRender(_name); + _prev_display = false; } - _prev_display = _display; - attention = 0; -} + _attention = false; -string uppercase(const string &s) { - string rslt(s); - for(string::iterator p = rslt.begin(); p != rslt.end(); p++){ - *p = toupper(*p); - } - return rslt; + FGATC::update(dt); } // Replace all occurrences of a given word. @@ -198,7 +263,7 @@ string replace_word(const string _orig, const string _www, const string _nnn){ where += nnn.length(); } - www = uppercase(www); + www = simgear::strutils::uppercase(www); for ( ; (where = orig.find(www, where)) != string::npos ; ) { orig.replace(where, www.length(), nnn); where += nnn.length(); @@ -225,318 +290,749 @@ const int minute(60); // measured in seconds // ascertaining which airports are in the US, let alone // (b) ascertaining which other places use inches. // -int Apt_US_CA(const string id) { -// Assume all IDs have length 3 or 4. -// No counterexamples have been seen. - if (id.length() == 4) { - if (id.substr(0,1) == "K") return 1; - if (id.substr(0,2) == "CY") return 1; - } - for (string::const_iterator ptr = id.begin(); ptr != id.end(); ptr++) { - if (isdigit(*ptr)) return 1; - } - return 0; +bool Apt_US_CA(const string id) +{ + // Assume all IDs have length 3 or 4. + // No counterexamples have been seen. + if (id.length() == 4) { + if (id.substr(0,1) == "K") return true; + if (id.substr(0,2) == "CY") return true; + } + for (string::const_iterator ptr = id.begin(); ptr != id.end(); ptr++) { + if (isdigit(*ptr)) return true; + } + return false; } -// Generate the actual broadcast ATIS transmission. -// Regen means regenerate the /current/ transmission. -// Special means generate a new transmission, with a new sequence. -// Returns 1 if we actually generated something. -int FGATIS::GenTransmission(const int regen, const int special) { - using namespace atmodel; - using namespace lex; +// voice spacers +static const string BRK = ".\n"; +static const string PAUSE = " / "; - string BRK = ".\n"; - string PAUSE = " / "; +/** Generate the actual broadcast ATIS transmission. +* 'regen' triggers a regeneration of the /current/ transmission. +* 'forceUpdate' generates a new transmission, with a new sequence. +* Returns 1 if we actually generated something. +*/ +bool FGATIS::genTransmission(const int regen, bool forceUpdate) +{ + using namespace lex; - int interval = _type == ATIS ? - ATIS_interval // ATIS updated hourly - : 2*minute; // AWOS updated more frequently + // ATIS updated hourly, AWOS updated more frequently + int interval = _type == ATIS ? ATIS_interval : 2*minute; - FGAirport* apt = FGAirport::findByIdent(ident); - int sequence = apt->getDynamics()->updateAtisSequence(interval, special); - if (!regen && sequence > LTRS) { -//xx if (msg_OK) cout << "ATIS: no change: " << sequence << endl; -//xx msg_time = cur_time; - return 0; // no change since last time - } + // check if pressure has changed significantly and we need to update ATIS + double Psl = fgGetDouble("/environment/pressure-sea-level-inhg"); + if (fabs(Psl-_report.psl) >= 0.15) + forceUpdate = true; + + FGAirport* apt = FGAirport::findByIdent(ident); + int sequence = apt->getDynamics()->updateAtisSequence(interval, forceUpdate); + if (!regen && sequence > LTRS) { + //xx if (msg_OK) cout << "ATIS: no change: " << sequence << endl; + //xx msg_time = cur_time; + return false; // no change since last time + } - const int bs(100); - char buf[bs]; - string time_str = fgGetString("sim/time/gmt-string"); - string hours, mins; - string phonetic_seq_string; + _report.psl = Psl; + transmission = ""; - transmission = ""; + // collect data and create report + createReport(apt); - int US_CA = Apt_US_CA(ident); + // add facility name + genFacilityInfo(); - if (!US_CA) { -// UK CAA radiotelephony manual indicates ATIS transmissions start -// with "This is ..." - transmission += This_is + " "; - } else { - // In the US they just start with the airport name. - } + if (_type == ATIS) { + // ATIS phraseology starts with "... airport information" + transmission += airport_information + " "; + } else { + // AWOS + transmission += Automated_weather_observation + " "; + } - // SG_LOG(SG_ATC, SG_ALERT, "ATIS: facility name: " << name); + string phonetic_seq_string = GetPhoneticLetter(sequence); // Add the sequence letter + transmission += phonetic_seq_string + BRK; -// Note that at this point, multi-word facility names -// will sometimes contain hyphens, not spaces. - - vector name_words; - boost::split(name_words, name, boost::is_any_of(" -")); - - for (vector::const_iterator wordp = name_words.begin(); - wordp != name_words.end(); wordp++) { - string word(*wordp); -// Remap some abbreviations that occur in apt.dat, to -// make things nicer for the text-to-speech system: - for (MSS::const_iterator replace = _remap.begin(); - replace != _remap.end(); replace++) { - // Due to inconsistent capitalisation in the apt.dat file, we need - // to do a case-insensitive comparison here. - string tmp1 = word, tmp2 = replace->first; - boost::algorithm::to_lower(tmp1); - boost::algorithm::to_lower(tmp2); - if (tmp1 == tmp2) { - word = replace->second; - break; + genTimeInfo(); + + // some warnings may appear at the beginning + genWarnings(-1); + + if (_type == ATIS) // as opposed to AWOS + genRunwayInfo(apt); + + // some warnings may appear after runway info + genWarnings(0); + + // transition level + genTransitionLevel(apt); + + // weather + if (!_report.concise) + transmission += Weather + BRK; + + genWindInfo(); + + // clouds and visibility + { + string vis_info, cloud_info; + bool v = genVisibilityInfo(vis_info); + bool c = genCloudInfo(cloud_info); + _report.cavok = !(v || c); + if (!_report.cavok) + { + // there is some visibility or cloud restriction + transmission += vis_info + cloud_info; + } + else + { + // Abbreviation CAVOK vs full "clouds and visibility..." does not really depend on + // US vs rest of the world, it really seems to depend on the airport. Just use + // it as a heuristic. + if ((_report.US_CA)||(_report.concise)) + transmission += cav_ok + BRK; + else + transmission += clouds_and_visibility_OK + BRK; + } + } + + // precipitation + genPrecipitationInfo(); + + // temperature + genTemperatureInfo(); + + // pressure + genPressureInfo(); + + // TODO check whether "no significant change" applies - somehow... + transmission += No_sig + BRK; // sounds better with festival than "nosig" + + // some warnings may appear at the very end + genWarnings(1); + + if ((!_report.concise)|| _report.US_CA) + transmission += Advise_on_initial_contact_you_have_information; + else + transmission += information; + transmission += " " + phonetic_seq_string + "."; + + if (!_report.US_CA) + { + // non-US ATIS ends with "out!" + transmission += " " + out; + } + + // Pause in between two messages must be 3-5 seconds + transmission += " / / / / / / / / "; + + ///////////////////////////////////////////////////////// + // post-processing + ///////////////////////////////////////////////////////// + transmission_readable = transmission; + + // Take the previous readable string and munge it to + // be relatively-more acceptable to the primitive tts system. + // Note that : ; and . are among the token-delimiters recognized + // by the tts system. + for (size_t where;;) { + where = transmission.find_first_of(":."); + if (where == string::npos) break; + transmission.replace(where, 1, PAUSE); + } + + return true; +} + +/** Collect (most of) the data and create report. + */ +void FGATIS::createReport(const FGAirport* apt) +{ + // check country + _report.US_CA = Apt_US_CA(ident); + + // switch to enable brief ATIS message (really depends on the airport) + _report.concise = fgGetBool("/sim/atis/concise-reports", false); + + _report.ils = false; + + // time information + string time_str = fgGetString("sim/time/gmt-string"); + // Warning - this is fragile if the time string format changes + _report.hours = time_str.substr(0,2).c_str(); + _report.mins = time_str.substr(3,2).c_str(); + + // pressure/temperature + { + double press, temp; + double Tsl = fgGetDouble("/environment/temperature-sea-level-degc"); + tie(press, temp) = PT_vs_hpt(_geod.getElevationM(), _report.psl*atmodel::inHg, Tsl + atmodel::freezing); + #if 0 + SG_LOG(SG_ATC, SG_ALERT, "Field P: " << press << " T: " << temp); + SG_LOG(SG_ATC, SG_ALERT, "based on elev " << elev + << " Psl: " << Psl + << " Tsl: " << Tsl); + #endif + _report.qnh = FGAtmo().QNH(_geod.getElevationM(), press); + _report.temp = int(SGMiscd::round(FGAtmo().fake_T_vs_a_us(_geod.getElevationFt(), Tsl))); + } + + // dew point + double dpsl = fgGetDouble("/environment/dewpoint-sea-level-degc"); + _report.dewpoint = int(SGMiscd::round(FGAtmo().fake_dp_vs_a_us(dpsl, _geod.getElevationFt()))); + + // precipitation + _report.rain_norm = fgGetDouble("environment/rain-norm"); + _report.snow_norm = fgGetDouble("environment/snow-norm"); + + // NOTAMs + _report.notam = 0; + if (fgGetBool("/sim/atis/random-notams", true)) + { + _report.notam = fgGetInt("/sim/atis/notam-id", 0); // fixed NOTAM for testing/debugging only + if (!_report.notam) + { + // select pseudo-random NOTAM (changes every hour, differs for each airport) + char cksum = 0; + string name = apt->getName(); + for(string::iterator p = name.begin(); p != name.end(); p++) + { + cksum += *p; + } + cksum ^= atoi(_report.hours.c_str()); + _report.notam = cksum % 12; // 12 intentionally higher than number of available NOTAMs, so they don't appear too often + // debugging + //fgSetInt("/sim/atis/selected-notam", _report.notam); + } + } +} + +void FGATIS::genPrecipitationInfo(void) +{ + using namespace lex; + + double rain_norm = _report.rain_norm; + double snow_norm = _report.snow_norm; + + // report rain or snow - which ever is worse + if (rain_norm > 0.7) + transmission += heavy + " " + rain + BRK; + else + if (snow_norm > 0.7) + transmission += heavy + " " + snow + BRK; + else + if (rain_norm > 0.4) + transmission += moderate + " " + rain + BRK; + else + if (snow_norm > 0.4) + transmission += moderate + " " + snow + BRK; + else + if (rain_norm > 0.2) + transmission += light + " " + rain + BRK; + else + if (snow_norm > 0.05) + transmission += light + " " + snow + BRK; + else + if (rain_norm > 0.05) + transmission += light + " " + drizzle + BRK; +} + +void FGATIS::genTimeInfo(void) +{ + using namespace lex; + + if (!_report.concise) + transmission += Time + " "; + + // speak each digit separately: + transmission += ConvertNumToSpokenDigits(_report.hours + _report.mins); + transmission += " " + zulu + BRK; +} + +bool FGATIS::genVisibilityInfo(string& vis_info) +{ + using namespace lex; + + double visibility = fgGetDouble("/environment/config/boundary/entry[0]/visibility-m"); + bool IsMax = false; + bool USE_KM = !_report.US_CA; + + vis_info += Visibility + ": "; + if (USE_KM) + { + visibility /= 1000.0; // convert to statute miles + // integer kilometers + if (visibility >= 9.5) + { + visibility = 10; + IsMax = true; + } + snprintf(buf, sizeof(buf), "%i", int(.5 + visibility)); + // "kelometers" instead of "kilometers" since the festival language generator doesn't get it right otherwise + vis_info += ConvertNumToSpokenDigits(buf) + " " + kelometers; + } + else + { + visibility /= atmodel::sm; // convert to statute miles + if (visibility < 0.25) { + vis_info += less_than_one_quarter; + } else if (visibility < 0.5) { + vis_info += one_quarter; + } else if (visibility < 0.75) { + vis_info += one_half; + } else if (visibility < 1.0) { + vis_info += three_quarters; + } else if (visibility >= 1.5 && visibility < 2.0) { + vis_info += one_and_one_half; + } else { + // integer miles + if (visibility > 9.5) + { + visibility = 10; + IsMax = true; + } + snprintf(buf, sizeof(buf), "%i", int(.5 + visibility)); + vis_info += ConvertNumToSpokenDigits(buf); + } + } + if (IsMax) + { + vis_info += " " + or_more; + } + vis_info += BRK; + return !IsMax; +} + +void FGATIS::addTemperature(int Temp) +{ + if (Temp < 0) + transmission += lex::minus + " "; + else + if (Temp > 0) + { + transmission += lex::plus + " "; + } + snprintf(buf, sizeof(buf), "%i", abs(Temp)); + transmission += ConvertNumToSpokenDigits(buf); + if (_report.US_CA) + transmission += " " + lex::Celsius; +} + +void FGATIS::genTemperatureInfo() +{ + // temperature + transmission += lex::Temperature + ": "; + addTemperature(_report.temp); + + // dewpoint + transmission += BRK + lex::Dewpoint + ": "; + addTemperature(_report.dewpoint); + + transmission += BRK; +} + +bool FGATIS::genCloudInfo(string& cloud_info) +{ + using namespace lex; + + bool did_some = false; + bool did_ceiling = false; + + for (int layer = 0; layer <= 4; layer++) { + snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/coverage", layer); + string coverage = fgGetString(buf); + if (coverage == clear) + continue; + snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/thickness-ft", layer); + if (fgGetDouble(buf) == 0) + continue; + snprintf(buf, sizeof(buf), "/environment/clouds/layer[%i]/elevation-ft", layer); + double ceiling = int(fgGetDouble(buf) - _geod.getElevationFt()); + if (ceiling > 12000) + continue; + + // BEWARE: At the present time, the environment system has no + // way (so far as I know) to represent a "thin broken" or + // "thin overcast" layer. If/when such things are implemented + // in the environment system, code will have to be written here + // to handle them. + + // First, do the prefix if any: + if (coverage == scattered || coverage == few) { + if (!did_some) { + if (_report.concise) + cloud_info += Clouds + ": "; + else + cloud_info += Sky_condition + ": "; + did_some = true; + } + } else /* must be a ceiling */ if (!did_ceiling) { + cloud_info += " " + Ceiling + ": "; + did_ceiling = true; + did_some = true; + } else { + cloud_info += " "; // no prefix required } + int cig00 = int(SGMiscd::round(ceiling/100)); // hundreds of feet + if (cig00) { + int cig000 = cig00/10; + cig00 -= cig000*10; // just the hundreds digit + if (cig000) { + snprintf(buf, sizeof(buf), "%i", cig000); + cloud_info += ConvertNumToSpokenDigits(buf); + cloud_info += " " + thousand + " "; + } + if (cig00) { + snprintf(buf, sizeof(buf), "%i", cig00); + cloud_info += ConvertNumToSpokenDigits(buf); + cloud_info += " " + hundred + " "; + } + } else { + // Should this be "sky obscured?" + cloud_info += " " + zero + " "; // not "zero hundred" + } + cloud_info += coverage + BRK; } - transmission += word + " "; - } + if (!did_some) + cloud_info += " " + Sky + " " + clear + BRK; + return did_some; +} - if (_type == ATIS /* as opposed to AWOS */) { - transmission += airport_information + " "; - } else { - transmission += Automated_weather_observation + " "; - } +void FGATIS::genFacilityInfo(void) +{ + if ((!_report.US_CA)&&(!_report.concise)) + { + // UK CAA radiotelephony manual indicates ATIS transmissions start + // with "This is ...", while US just starts with airport name. + transmission += lex::This_is + " "; + } - phonetic_seq_string = GetPhoneticLetter(sequence); // Add the sequence letter - transmission += phonetic_seq_string + BRK; - -// Warning - this is fragile if the time string format changes - hours = time_str.substr(0,2).c_str(); - mins = time_str.substr(3,2).c_str(); -// speak each digit separately: - transmission += ConvertNumToSpokenDigits(hours + mins); - transmission += " " + zulu + " " + weather + BRK; - - transmission += wind + ": "; - - double wind_speed = fgGetDouble("/environment/config/boundary/entry[0]/wind-speed-kt"); - double wind_dir = fgGetDouble("/environment/config/boundary/entry[0]/wind-from-heading-deg"); - while (wind_dir <= 0) wind_dir += 360; -// The following isn't as bad a kludge as it might seem. -// It combines the magvar at the /aircraft/ location with -// the wind direction in the environment/config array. -// But if the aircraft is close enough to the station to -// be receiving the ATIS signal, this should be a good-enough -// approximation. For more-distant aircraft, the wind_dir -// shouldn't be corrected anyway. -// The less-kludgy approach would be to use the magvar associated -// with the station, but that is not tabulated in the stationweather -// structure as it stands, and computing it would be expensive. -// Also note that as it stands, there is only one environment in -// the entire FG universe, so the aircraft environment is the same -// as the station environment anyway. - wind_dir -= fgGetDouble("/environment/magnetic-variation-deg"); // wind_dir now magnetic - if (wind_speed == 0) { -// Force west-facing rwys to be used in no-wind situations -// which is consistent with Flightgear's initial setup: - wind_dir = 270; - transmission += " " + light_and_variable; - } else { - // FIXME: get gust factor in somehow - snprintf(buf, bs, "%03.0f", 5*SGMiscd::round(wind_dir/5)); - transmission += ConvertNumToSpokenDigits(buf); + // SG_LOG(SG_ATC, SG_ALERT, "ATIS: facility name: " << name); - snprintf(buf, bs, "%1.0f", wind_speed); - transmission += " " + at + " " + ConvertNumToSpokenDigits(buf) + BRK; - } + // Note that at this point, multi-word facility names + // will sometimes contain hyphens, not spaces. + vector name_words; + boost::split(name_words, name, boost::is_any_of(" -")); -// Sounds better with a pause in there: - transmission += PAUSE; - - int did_some(0); - int did_ceiling(0); - - for (int layer = 0; layer <= 4; layer++) { - snprintf(buf, bs, "/environment/clouds/layer[%i]/coverage", layer); - string coverage = fgGetString(buf); - if (coverage == clear) continue; - snprintf(buf, bs, "/environment/clouds/layer[%i]/thickness-ft", layer); - if (fgGetDouble(buf) == 0) continue; - snprintf(buf, bs, "/environment/clouds/layer[%i]/elevation-ft", layer); - double ceiling = int(fgGetDouble(buf) - _geod.getElevationFt()); - if (ceiling > 12000) continue; - -// BEWARE: At the present time, the environment system has no -// way (so far as I know) to represent a "thin broken" or -// "thin overcast" layer. If/when such things are implemented -// in the environment system, code will have to be written here -// to handle them. - -// First, do the prefix if any: - if (coverage == scattered || coverage == few) { - if (!did_some) { - transmission += " " + Sky_condition + ": "; - did_some++; + for (vector::const_iterator wordp = name_words.begin(); + wordp != name_words.end(); wordp++) { + string word(*wordp); + // Remap some abbreviations that occur in apt.dat, to + // make things nicer for the text-to-speech system: + for (MSS::const_iterator replace = _remap.begin(); + replace != _remap.end(); replace++) { + // Due to inconsistent capitalisation in the apt.dat file, we need + // to do a case-insensitive comparison here. + string tmp1 = word, tmp2 = replace->first; + boost::algorithm::to_lower(tmp1); + boost::algorithm::to_lower(tmp2); + if (tmp1 == tmp2) { + word = replace->second; + break; + } } - } else /* must be a ceiling */ if (!did_ceiling) { - transmission += " " + Ceiling + ": "; - did_ceiling++; - did_some++; + transmission += word + " "; + } +} + +void FGATIS::genWindInfo(void) +{ + using namespace lex; + + transmission += Wind + ": "; + + double wind_speed = fgGetDouble("/environment/config/boundary/entry[0]/wind-speed-kt"); + double wind_dir = fgGetDouble("/environment/config/boundary/entry[0]/wind-from-heading-deg"); + while (wind_dir <= 0) wind_dir += 360; + // The following isn't as bad a kludge as it might seem. + // It combines the magvar at the /aircraft/ location with + // the wind direction in the environment/config array. + // But if the aircraft is close enough to the station to + // be receiving the ATIS signal, this should be a good-enough + // approximation. For more-distant aircraft, the wind_dir + // shouldn't be corrected anyway. + // The less-kludgy approach would be to use the magvar associated + // with the station, but that is not tabulated in the stationweather + // structure as it stands, and computing it would be expensive. + // Also note that as it stands, there is only one environment in + // the entire FG universe, so the aircraft environment is the same + // as the station environment anyway. + wind_dir -= fgGetDouble("/environment/magnetic-variation-deg"); // wind_dir now magnetic + if (wind_speed == 0) { + // Force west-facing rwys to be used in no-wind situations + // which is consistent with Flightgear's initial setup: + wind_dir = 270; + transmission += " " + light_and_variable; } else { - transmission += " "; // no prefix required - } - int cig00 = int(SGMiscd::round(ceiling/100)); // hundreds of feet - if (cig00) { - int cig000 = cig00/10; - cig00 -= cig000*10; // just the hundreds digit - if (cig000) { - snprintf(buf, bs, "%i", cig000); + // FIXME: get gust factor in somehow + snprintf(buf, sizeof(buf), "%03.0f", 5*SGMiscd::round(wind_dir/5)); transmission += ConvertNumToSpokenDigits(buf); - transmission += " " + thousand + " "; - } - if (cig00) { - snprintf(buf, bs, "%i", cig00); + if (!_report.concise) + transmission += " " + degrees; + transmission += " "; + snprintf(buf, sizeof(buf), "%1.0f", wind_speed); + transmission += at + " " + ConvertNumToSpokenDigits(buf); + if (!_report.concise) + transmission += " " + knots; + } + transmission += BRK; +} + +void FGATIS::genTransitionLevel(const FGAirport* apt) +{ + double hPa = _report.qnh/atmodel::mbar; + + /* Transition level is the flight level above which aircraft must use standard pressure and below + * which airport pressure settings must be used. + * Following definitions are taken from German ATIS: + * QNH <= 977 hPa: TRL 80 + * QNH <= 1013 hPa: TRL 70 + * QNH > 1013 hPa: TRL 60 + * (maybe differs slightly for other countries...) + */ + int tl = 60; + if (hPa <= 977) + tl = 80; + else + if (hPa <= 1013) + tl = 70; + + // add an offset to the transition level for high altitude airports (just guessing here, + // seems reasonable) + double elevationFt = apt->getElevation(); + int e = int(elevationFt / 1000.0); + if (e >= 3) + { + // TL steps in 10(00)ft + tl += (e-2)*10; + } + + snprintf(buf, sizeof(buf), "%02i", tl); + transmission += lex::Transition_level + ": " + ConvertNumToSpokenDigits(buf) + BRK; +} + +void FGATIS::genPressureInfo(void) +{ + using namespace lex; + + // hectopascal for most of the world (not US, not CA) + if(!_report.US_CA) { + double hPa = _report.qnh/atmodel::mbar; + transmission += QNH + ": "; + snprintf(buf, sizeof(buf), "%03.0f", _report.qnh / atmodel::mbar); transmission += ConvertNumToSpokenDigits(buf); - transmission += " " + hundred + " "; - } + // "hectopascal" replaced "millibars" in new ATIS standard since 2011 + if ((!_report.concise)||(hPa < 1000)) + transmission += " " + hectopascal; // "hectopascal" must be provided for values below 1000 (to avoid confusion with inHg) + + // Many (European) airports (with lots of US traffic?) provide both, hPa and inHg announcements. + // Europeans keep the "decimal" in inHg readings to make the distinction to hPa even more apparent. + // hPa/inHg separated by "equals" or "or" with some airports + if (_report.concise) + transmission += " " + equals + " "; + else + transmission += " " + Or + " "; + snprintf(buf, sizeof(buf), "%04.2f", _report.qnh / atmodel::inHg); + transmission += ConvertNumToSpokenDigits(buf); + if (!_report.concise) + transmission += " " + inches; + transmission += BRK; } else { - // Should this be "sky obscured?" - transmission += " " + zero + " "; // not "zero hundred" + // use inches of mercury for US/CA + transmission += Altimeter + ": "; + double asetting = _report.qnh / atmodel::inHg; + // shift two decimal places, US/CA airports omit the "decimal" in inHg settings + asetting *= 100.; + snprintf(buf, sizeof(buf), "%04.0f", asetting); + transmission += ConvertNumToSpokenDigits(buf); } - transmission += coverage + BRK; - } - if (!did_some) transmission += " " + Sky + " " + clear + BRK; - transmission += Temperature + ": "; - double Tsl = fgGetDouble("/environment/temperature-sea-level-degc"); - int temp = int(SGMiscd::round(FGAtmo().fake_T_vs_a_us(_geod.getElevationFt(), Tsl))); - if(temp < 0) { - transmission += lex::minus + " "; - } - snprintf(buf, bs, "%i", abs(temp)); - transmission += ConvertNumToSpokenDigits(buf); - if (US_CA) transmission += " " + Celsius; - transmission += " " + dewpoint + " "; - double dpsl = fgGetDouble("/environment/dewpoint-sea-level-degc"); - temp = int(SGMiscd::round(FGAtmo().fake_dp_vs_a_us(dpsl, _geod.getElevationFt()))); - if(temp < 0) { - transmission += lex::minus + " "; - } - snprintf(buf, bs, "%i", abs(temp)); - transmission += ConvertNumToSpokenDigits(buf); - if (US_CA) transmission += " " + Celsius; - transmission += BRK; - - transmission += Visibility + ": "; - double visibility = fgGetDouble("/environment/config/boundary/entry[0]/visibility-m"); - visibility /= atmodel::sm; // convert to statute miles - if (visibility < 0.25) { - transmission += less_than_one_quarter; - } else if (visibility < 0.5) { - transmission += one_quarter; - } else if (visibility < 0.75) { - transmission += one_half; - } else if (visibility < 1.0) { - transmission += three_quarters; - } else if (visibility >= 1.5 && visibility < 2.0) { - transmission += one_and_one_half; - } else { - // integer miles - if (visibility > 10) visibility = 10; - sprintf(buf, "%i", int(.5 + visibility)); - transmission += ConvertNumToSpokenDigits(buf); - } - transmission += BRK; + transmission += BRK; +} - double myQNH; - double Psl = fgGetDouble("/environment/pressure-sea-level-inhg"); - { - double press, temp; - - tie(press, temp) = PT_vs_hpt(_geod.getElevationM(), Psl*inHg, Tsl + freezing); -#if 0 - SG_LOG(SG_ATC, SG_ALERT, "Field P: " << press << " T: " << temp); - SG_LOG(SG_ATC, SG_ALERT, "based on elev " << elev - << " Psl: " << Psl - << " Tsl: " << Tsl); -#endif - myQNH = FGAtmo().QNH(_geod.getElevationM(), press); - } +void FGATIS::genRunwayInfo(const FGAirport* apt) +{ + using namespace lex; -// Convert to millibars for most of the world (not US, not CA) - if((!US_CA) && fgGetBool("/sim/atc/use-millibars")) { - transmission += QNH + ": "; - myQNH /= mbar; - if (myQNH > 1000) myQNH -= 1000; // drop high digit - snprintf(buf, bs, "%03.0f", myQNH); - transmission += ConvertNumToSpokenDigits(buf) + " " + millibars + BRK; - } else { - transmission += Altimeter + ": "; - double asetting = myQNH / inHg; // use inches of mercury - asetting *= 100.; // shift two decimal places - snprintf(buf, bs, "%04.0f", asetting); - transmission += ConvertNumToSpokenDigits(buf) + BRK; - } + if (!apt) + return; + + FGRunway* rwy = apt->getActiveRunwayForUsage(); + if (!rwy) + return; + + string rwy_no = rwy->ident(); + if(rwy_no != "NN") + { + FGNavRecord* ils = rwy->ILS(); + if (ils) + { + _report.ils = true; + transmission += Expect_I_L_S_approach + " "+ runway + " "+ConvertRwyNumToSpokenString(rwy_no) + BRK; + if (fgGetBool("/sim/atis/announce-ils-frequency", false)) + { + // this is handy - but really non-standard (so disabled by default) + snprintf(buf, sizeof(buf), "%5.2f", ils->get_freq()/100.0); + transmission += I_L_S + " " + ConvertNumToSpokenDigits(buf) + BRK; + } + } + else + { + transmission += Expect_visual_approach + " "+ runway + " "+ConvertRwyNumToSpokenString(rwy_no) + BRK; + } - if (_type == ATIS /* as opposed to AWOS */) { - const FGAirport* apt = fgFindAirportID(ident); - if (apt) { - string rwy_no = apt->getActiveRunwayForUsage()->ident(); - if(rwy_no != "NN") { transmission += Landing_and_departing_runway + " "; transmission += ConvertRwyNumToSpokenString(rwy_no) + BRK; -#ifdef ATIS_TEST + #ifdef ATIS_TEST if (msg_OK) { msg_time = cur_time; cout << "In atis.cxx, r.rwy_no: " << rwy_no << " wind_dir: " << wind_dir << endl; } -#endif - } + #endif + } +} + +void FGATIS::genWarnings(int position) +{ + using namespace lex; + bool dayTime = (fgGetDouble("/sim/time/sun-angle-rad") < 1.57); + + if (position == -1) // warnings at beginning of ATIS + { + // bird related warnings at day-time only (birds are VFR-only! ;-) ) + if (dayTime) + { + if (_report.notam == 1) + transmission += Attention + ": " + flock_of_birds + " " + in_the_vicinity_of_the_airport + BRK; + else + if (_report.notam == 2) + transmission += Attention + ": " + bird_activity + " " + in_the_vicinity_of_the_airport + BRK; + } + } + else + if (position == 0) // warnings after runway messages + { + if ((_report.notam == 3)&&(_report.ils)) + { + // "__I_LS_" necessary to trick the language generator into pronouncing it properly + transmission += Attention + ": " + short_time__I_LS_interference_possible_by_taxiing_aircraft + BRK; + } + } + else + if (position == 1) // warnings at the end of the report + { + // "runway wet-wet-wet" warning in heavy rain + if (_report.rain_norm > 0.6) + { + // "wet" is repeated 3 times in ATIS warnings, since the word is difficult + // to understand over radio - but the message is important. + transmission += runway_wet + " " + wet + " " + wet + BRK; + } + + if (_report.notam == 4) + { + // intentional: "reed" instead of "read" since festival gets it wrong otherwise + transmission += reed_back_all_runway_hold_instructions + BRK; + } + else + if ((_report.notam == 5)&& _report.cavok && dayTime && + (_report.rain_norm == 0) && (_report.snow_norm == 0)) // ;-) + { + transmission += Attention + ": " + glider_operation_in_sector + BRK; + } } - transmission += On_initial_contact_advise_you_have_information + " "; - transmission += phonetic_seq_string; - transmission += "... " + BRK + PAUSE + PAUSE; - } - transmission_readable = transmission; -// Take the previous readable string and munge it to -// be relatively-more acceptable to the primitive tts system. -// Note that : ; and . are among the token-delimeters recognized -// by the tts system. - for (size_t where;;) { - where = transmission.find_first_of(":."); - if (where == string::npos) break; - transmission.replace(where, 1, PAUSE); - } - return 1; } -// Put the transmission into the property tree, -// possibly in multiple places if multiple radios -// are tuned to the same ATIS. +// Put the transmission into the property tree. // You can see it by pointing a web browser // at the property tree. The second comm radio is: // http://localhost:5400/instrumentation/comm[1] // // (Also, if in debug mode, dump it to the console.) -void FGATIS::TreeOut(int msg_OK){ - for (map::iterator act = active_on.begin(); - act != active_on.end(); - act++){ - string prop = "/instrumentation/" + act->first + "/atis"; - globals->get_props()->setStringValue(prop.c_str(), - ("
\n" + transmission_readable + "
\n").c_str()); -#ifdef ATIS_TEST - if (msg_OK) cout << "**** ATIS active on: " << prop << endl; -#endif - } -#ifdef ATIS_TEST - if (msg_OK) cout << transmission_readable << endl; -#endif +void FGATIS::treeOut(int msg_OK) +{ + _atis->setStringValue("
\n" + transmission_readable + "
\n"); + SG_LOG(SG_ATC, SG_DEBUG, "**** ATIS active on: " << _name << + "transmission: " << transmission_readable); +} + + +class RangeFilter : public CommStation::Filter +{ +public: + RangeFilter( const SGGeod & pos ) : + CommStation::Filter(), + _cart(SGVec3d::fromGeod(pos)), + _pos(pos) + { + } + + virtual bool pass(FGPositioned* aPos) const + { + flightgear::CommStation * stn = dynamic_cast(aPos); + if( NULL == stn ) + return false; + + // do the range check in cartesian space, since the distances are potentially + // large enough that the geodetic functions become unstable + // (eg, station on opposite side of the planet) + double rangeM = SGMiscd::max( stn->rangeNm(), 10.0 ) * SG_NM_TO_METER; + double d2 = distSqr( aPos->cart(), _cart); + + return d2 <= (rangeM * rangeM); + } + + virtual CommStation::Type minType() const + { + return CommStation::FREQ_ATIS; + } + + virtual CommStation::Type maxType() const + { + return CommStation::FREQ_AWOS; + } + +private: + SGVec3d _cart; + SGGeod _pos; +}; + +// Search for ATC stations by frequency +bool FGATIS::search(double dt) +{ + double frequency = _freq->getDoubleValue(); + + // Note: 122.375 must be rounded DOWN to 122370 + // in order to be consistent with apt.dat et cetera. + int freqKhz = 10 * static_cast(frequency * 100 + 0.25); + + // only search tuned frequencies when necessary + _time_before_search_sec -= dt; + + // throttle frequency searches + if ((freqKhz == _last_frequency)&&(_time_before_search_sec > 0)) + return false; + + _last_frequency = freqKhz; + _time_before_search_sec = 4.0; + + // Position of the Users Aircraft + SGGeod aircraftPos = SGGeod::fromDegFt(_lon_node->getDoubleValue(), + _lat_node->getDoubleValue(), + _elev_node->getDoubleValue()); + + RangeFilter rangeFilter(aircraftPos ); + CommStation* sta = CommStation::findByFreq(freqKhz, aircraftPos, &rangeFilter ); + SetStation(sta); + if (sta && sta->airport()) + { + SG_LOG(SG_ATC, SG_DEBUG, "FGATIS " << _name << ": " << sta->airport()->name()); + } + else + { + SG_LOG(SG_ATC, SG_DEBUG, "FGATIS " << _name << ": no station."); + } + + return true; } diff --git a/src/ATCDCL/atis.hxx b/src/ATCDCL/atis.hxx index 0c6124b..525001f 100644 --- a/src/ATCDCL/atis.hxx +++ b/src/ATCDCL/atis.hxx @@ -19,7 +19,6 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - #ifndef _FG_ATIS_HXX #define _FG_ATIS_HXX @@ -28,74 +27,125 @@ #include #include +#include #include "ATC.hxx" -//DCL - a complete guess for now. -#define FG_ATIS_DEFAULT_RANGE 30 - +class FGAirport; + typedef std::map MSS; class FGATIS : public FGATC { - - //atc_type type; - - // The actual ATIS transmission - // This is generated from the prevailing conditions when required. - // This is the version with markup, suitable for voice synthesis: - std::string transmission; - - // Same as above, but in a form more readable as text. - std::string transmission_readable; - - // for failure modeling - std::string trans_ident; // transmitted ident - double old_volume; - bool atis_failed; // atis failed? - time_t msg_time; // for moderating error messages - time_t cur_time; - int msg_OK; - int attention; - - bool _prev_display; // Previous value of _display flag - MSS _remap; // abbreviations to be expanded - - // Aircraft position - // ATIS is actually a special case in that unlike other ATC eg.tower it doesn't actually know about - // or the whereabouts of the aircraft it is transmitting to. However, to ensure consistancy of - // operation with the other ATC classes the ATIS class must calculate range to the aircraft in order - // to decide whether to render the transmission - hence the users plane details must be stored. - //SGPropertyNode_ptr airplane_lon_node; - //SGPropertyNode_ptr airplane_lat_node; - //SGPropertyNode_ptr airplane_elev_node; - - public: - - FGATIS(void); - ~FGATIS(void); - virtual void Init(); - void attend (int); - - //run the ATIS instance - void Update(double dt); - - //inline void set_type(const atc_type tp) {type = tp;} - inline const std::string& get_trans_ident() { return trans_ident; } - inline void set_refname(const std::string& r) { refname = r; } - - private: - - std::string refname; // Holds the refname of a transmission in progress - - // Generate the ATIS transmission text: - int GenTransmission(const int regen, const int special); - - // Put the text into the property tree - // (and in debug mode, print it on the console): - void TreeOut(int msgOK); - - friend std::istream& operator>> ( std::istream&, FGATIS& ); + std::string _name; + int _num; + + SGPropertyNode_ptr _root; + SGPropertyNode_ptr _volume; + SGPropertyNode_ptr _serviceable; + SGPropertyNode_ptr _operable; + SGPropertyNode_ptr _electrical; + SGPropertyNode_ptr _freq; + SGPropertyNode_ptr _atis; + + // Pointers to current users position + SGPropertyNode_ptr _lon_node; + SGPropertyNode_ptr _lat_node; + SGPropertyNode_ptr _elev_node; + + SGPropertyChangeCallback _cb_attention; + + // The actual ATIS transmission + // This is generated from the prevailing conditions when required. + // This is the version with markup, suitable for voice synthesis: + std::string transmission; + + // Same as above, but in a form more readable as text. + std::string transmission_readable; + + // for failure modeling + std::string trans_ident; // transmitted ident + double old_volume; + bool atis_failed; // atis failed? + time_t msg_time; // for moderating error messages + time_t cur_time; + int msg_OK; + bool _attention; + bool _check_transmission; + + bool _prev_display; // Previous value of _display flag + MSS _remap; // abbreviations to be expanded + + // internal periodic station search timer + double _time_before_search_sec; + int _last_frequency; + + // temporary buffer for string conversions + char buf[100]; + + // data for the current ATIS report + struct + { + std::string phonetic_seq_string; + bool US_CA; + bool cavok; + bool concise; + bool ils; + int temp; + int dewpoint; + double psl; + double qnh; + double rain_norm, snow_norm; + int notam; + std::string hours,mins; + } _report; + +public: + + FGATIS(const std::string& name, int num); + + void init(); + void reinit(); + + void attend(SGPropertyNode* node); + + //run the ATIS instance + void update(double dt); + + //inline void set_type(const atc_type tp) {type = tp;} + inline const std::string& get_trans_ident() { return trans_ident; } + +protected: + virtual FGATCVoice* GetVoicePointer(); + +private: + + void createReport (const FGAirport* apt); + + /** generate the ATIS transmission text */ + bool genTransmission (const int regen, bool forceUpdate); + void genTimeInfo (void); + void genFacilityInfo (void); + void genPrecipitationInfo(void); + bool genVisibilityInfo (std::string& vis_info); + bool genCloudInfo (std::string& cloud_info); + void genWindInfo (void); + void genTemperatureInfo (void); + void genTransitionLevel (const FGAirport* apt); + void genPressureInfo (void); + void genRunwayInfo (const FGAirport* apt); + void genWarnings (int position); + + void addTemperature (int Temp); + + // Put the text into the property tree + // (and in debug mode, print it on the console): + void treeOut(int msgOK); + + // Search the specified radio for stations on the same frequency and in range. + bool search(double dt); + + friend std::istream& operator>> ( std::istream&, FGATIS& ); }; typedef int (FGATIS::*int_getter)() const; diff --git a/src/ATCDCL/atis_lexicon.hxx b/src/ATCDCL/atis_lexicon.hxx index d201674..ef6f027 100644 --- a/src/ATCDCL/atis_lexicon.hxx +++ b/src/ATCDCL/atis_lexicon.hxx @@ -15,11 +15,11 @@ Q(Airfield) Q(Airbase) Q(Junior) Q(Celsius) -Q(wind) +Q(Wind) Q(zulu) Q(zulu_weather) Q(Automated_weather_observation) -Q(weather) +Q(Weather) Q(airport_information) Q(International) Q(Regional) @@ -45,9 +45,10 @@ Q(overcast) Q(thin) Q(Sky_condition) Q(Sky) +Q(Clouds) Q(Ceiling) Q(minus) -Q(dewpoint) +Q(Dewpoint) Q(Visibility) Q(less_than_one_quarter) Q(one_quarter) @@ -56,13 +57,66 @@ Q(three_quarters) Q(one_and_one_half) Q(Altimeter) Q(QNH) -Q(millibars) Q(Landing_and_departing_runway) -Q(On_initial_contact_advise_you_have_information) +Q(Advise_on_initial_contact_you_have_information) Q(This_is) +Q(information) +Q(millibars) +Q(hectopascal) +Q(inches) +Q(I_L_S) +Q(visual) +Q(cav_ok) +Q(clouds_and_visibility_OK) +Q(out) +Q(equals) +Q(Expect_I_L_S_approach) +Q(Expect_visual_approach) +Q(Transition_level) +Q(No_sig) +Q(Time) +Q(kelometers) +Q(Attention) +Q(flock_of_birds) +Q(bird_activity) +Q(in_the_vicinity_of_the_airport) +Q(short_time__I_LS_interference_possible_by_taxiing_aircraft) +Q(reed_back_all_runway_hold_instructions) +Q(glider_operation_in_sector) +Q(airport) +Q(runway_wet) +Q(runway_in_use) +Q(arrivals) +Q(runway) +Q(runways) +Q(expect) +Q(approach) +Q(departures) +Q(wet) +Q(ice) +Q(closed) +Q(light) +Q(moderate) +Q(heavy) +Q(rain) +Q(drizzle) +Q(snow) +Q(fog) +Q(plus) +Q(hours) +Q(variable) +Q(from) +Q(Or) +Q(And) +Q(to) +Q(maximum) +Q(between) +Q(degrees) +Q(or_more) Q(left) Q(right) Q(center) +Q(knots) } #undef Q diff --git a/src/Aircraft/CMakeLists.txt b/src/Aircraft/CMakeLists.txt index 3dcd385..5c379f0 100644 --- a/src/Aircraft/CMakeLists.txt +++ b/src/Aircraft/CMakeLists.txt @@ -4,12 +4,14 @@ set(SOURCES controls.cxx replay.cxx flightrecorder.cxx + FlightHistory.cxx ) set(HEADERS controls.hxx replay.hxx flightrecorder.hxx + FlightHistory.hxx ) diff --git a/src/Aircraft/FlightHistory.cxx b/src/Aircraft/FlightHistory.cxx new file mode 100644 index 0000000..0a30261 --- /dev/null +++ b/src/Aircraft/FlightHistory.cxx @@ -0,0 +1,192 @@ +// FlightHistory +// +// Written by James Turner, started December 2012. +// +// Copyright (C) 2012 James Turner - zakalawe (at) mac com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "FlightHistory.hxx" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include
+#include
+ +FGFlightHistory::FGFlightHistory() : + m_sampleInterval(5.0), + m_validSampleCount(SAMPLE_BUCKET_WIDTH) +{ +} + +FGFlightHistory::~FGFlightHistory() +{ +} + +void FGFlightHistory::init() +{ + m_enabled = fgGetNode("/sim/history/enabled", true); + m_sampleInterval = fgGetDouble("/sim/history/sample-interval-sec", 1.0); + if (m_sampleInterval <= 0.0) { // would be bad + SG_LOG(SG_FLIGHT, SG_INFO, "invalid flight-history sample interval:" << m_sampleInterval + << ", defaulting to " << m_sampleInterval); + m_sampleInterval = 1.0; + } + + // cap memory use at 4MB + m_maxMemoryUseBytes = fgGetInt("/sim/history/max-memory-use-bytes", 1024 * 1024 * 4); + m_weightOnWheels = NULL; +// reset the history when we detect a take-off + if (fgGetBool("/sim/history/clear-on-takeoff", true)) { + m_weightOnWheels = fgGetNode("/gear/gear[1]/wow", 0, true); + m_lastWoW = m_weightOnWheels->getBoolValue(); + } + + // force bucket re-allocation + m_validSampleCount = SAMPLE_BUCKET_WIDTH; +} + +void FGFlightHistory::shutdown() +{ + clear(); +} + +void FGFlightHistory::reinit() +{ + shutdown(); + init(); +} + +void FGFlightHistory::update(double dt) +{ + if ((dt == 0.0) || !m_enabled->getBoolValue()) { + return; // paused or disabled + } + + if (m_weightOnWheels) { + + if (m_lastWoW && !m_weightOnWheels->getBoolValue()) { + SG_LOG(SG_FLIGHT, SG_INFO, "history: detected main-gear takeoff, clearing history"); + clear(); + } + } // of rest-on-takeoff enabled + +// spatial check - moved at least 1m since last capture + if (!m_buckets.empty()) { + SGVec3d lastCaptureCart(SGVec3d::fromGeod(m_buckets.back()->samples[m_validSampleCount - 1].position)); + double d2 = distSqr(lastCaptureCart, globals->get_aircraft_position_cart()); + if (d2 <= 1.0) { + return; + } + } + + double elapsed = globals->get_sim_time_sec() - m_lastCaptureTime; + if (elapsed > m_sampleInterval) { + capture(); + } +} + +void FGFlightHistory::allocateNewBucket() +{ + SampleBucket* bucket = NULL; + if (!m_buckets.empty() && (currentMemoryUseBytes() > m_maxMemoryUseBytes)) { + bucket = m_buckets.front(); + m_buckets.erase(m_buckets.begin()); + } else { + bucket = new SampleBucket; + } + + m_buckets.push_back(bucket); + m_validSampleCount = 0; +} + +void FGFlightHistory::capture() +{ + if (m_validSampleCount == SAMPLE_BUCKET_WIDTH) { + // bucket is full, allocate a new one + allocateNewBucket(); + } + + m_lastCaptureTime = globals->get_sim_time_sec(); + Sample* sample = m_buckets.back()->samples + m_validSampleCount; + + sample->simTimeMSec = static_cast(m_lastCaptureTime * 1000.0); + sample->position = globals->get_aircraft_position(); + + double heading, pitch, roll; + globals->get_aircraft_orientation(heading, pitch, roll); + sample->heading = static_cast(heading); + sample->pitch = static_cast(pitch); + sample->roll = static_cast(roll); + + ++m_validSampleCount; +} + +SGGeodVec FGFlightHistory::pathForHistory(double minEdgeLengthM) const +{ + SGGeodVec result; + if (m_buckets.empty()) { + return result; + } + + result.push_back(m_buckets.front()->samples[0].position); + SGVec3d lastOutputCart = SGVec3d::fromGeod(result.back()); + double minLengthSqr = minEdgeLengthM * minEdgeLengthM; + + BOOST_FOREACH(SampleBucket* bucket, m_buckets) { + unsigned int count = (bucket == m_buckets.back() ? m_validSampleCount : SAMPLE_BUCKET_WIDTH); + + // iterate over all the valid samples in the bucket + for (unsigned int index = 0; index < count; ++index) { + SGGeod g = bucket->samples[index].position; + SGVec3d cart(SGVec3d::fromGeod(g)); + if (distSqr(cart, lastOutputCart) > minLengthSqr) { + lastOutputCart = cart; + result.push_back(g); + } + } // of samples iteration + } // of buckets iteration + + return result; +} + +void FGFlightHistory::clear() +{ + BOOST_FOREACH(SampleBucket* ptr, m_buckets) { + delete ptr; + } + m_buckets.clear(); + m_validSampleCount = SAMPLE_BUCKET_WIDTH; +} + +size_t FGFlightHistory::currentMemoryUseBytes() const +{ + return sizeof(SampleBucket) * m_buckets.size(); +} + diff --git a/src/Aircraft/FlightHistory.hxx b/src/Aircraft/FlightHistory.hxx new file mode 100644 index 0000000..38e3d44 --- /dev/null +++ b/src/Aircraft/FlightHistory.hxx @@ -0,0 +1,110 @@ +// FlightHistory +// +// Written by James Turner, started December 2012. +// +// Copyright (C) 2012 James Turner - zakalawe (at) mac com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef FG_AIRCRAFT_FLIGHT_HISTORY_HXX +#define FG_AIRCRAFT_FLIGHT_HISTORY_HXX + +#include +#include +#include + +#include +#include // for std::auto_ptr + +typedef std::vector SGGeodVec; + +/** + * record the history of the aircraft's movements, making it available + * as a contiguous block. This can be used to show the historical flight-path + * over a long period of time (unlike the replay system), but only a small, + * fixed set of properties are recorded. (Positioned and orientation, but + * not velocity, acceleration, control inputs, or so on) + */ +class FGFlightHistory : public SGSubsystem +{ +public: + FGFlightHistory(); + virtual ~FGFlightHistory(); + + virtual void init(); + virtual void shutdown(); + virtual void reinit(); + virtual void update(double dt); + + /** + * retrieve the path, collapsing segments shorter than + * the specified minimum length + */ + SGGeodVec pathForHistory(double minEdgeLengthM = 50.0) const; +private: + /** + * @class A single data sample in the history system. + */ + class Sample + { + public: + SGGeod position; + /// heading, pitch and roll can be recorded at lower precision + /// than a double - actually 16 bits might be sufficient + float heading, pitch, roll; + int simTimeMSec; + }; + + static const int SAMPLE_BUCKET_WIDTH = 1024; + + /** + * Bucket is a fixed-size container of samples. This is a crude slab + * allocation of samples, in chunks defined by the width constant above. + * Keep in mind that even with a 1Hz sample frequency, we use less than + * 200kbytes per hour - avoiding continous malloc traffic, or expensive + * std::vector reallocations, is the key factor here. + */ + class SampleBucket + { + public: + Sample samples[SAMPLE_BUCKET_WIDTH]; + }; + + double m_lastCaptureTime; + double m_sampleInterval; ///< sample interval in seconds +/// our store of samples (in buckets). The last bucket is partially full, +/// with the number of valid samples indicated by m_validSampleCount + std::vector m_buckets; + +/// number of valid samples in the final bucket + unsigned int m_validSampleCount; + + SGPropertyNode_ptr m_weightOnWheels; + SGPropertyNode_ptr m_enabled; + + bool m_lastWoW; + size_t m_maxMemoryUseBytes; + + void allocateNewBucket(); + + void clear(); + void capture(); + + size_t currentMemoryUseBytes() const; +}; + +#endif \ No newline at end of file diff --git a/src/Aircraft/controls.cxx b/src/Aircraft/controls.cxx index dcd4a55..4fc9246 100644 --- a/src/Aircraft/controls.cxx +++ b/src/Aircraft/controls.cxx @@ -34,39 +34,12 @@ // Constructor FGControls::FGControls() : - aileron( 0.0 ), - aileron_trim( 0.0 ), - elevator( 0.0 ), - elevator_trim( 0.0 ), - rudder( 0.0 ), - rudder_trim( 0.0 ), flaps( 0.0 ), slats( 0.0 ), - BLC( false ), - spoilers( 0.0 ), - speedbrake( 0.0 ), - wing_sweep( 0.0 ), - wing_fold( false ), - drag_chute( false ), - throttle_idle( true ), - dump_valve( false ), - brake_left( 0.0 ), - brake_right( 0.0 ), - copilot_brake_left( 0.0 ), - copilot_brake_right( 0.0 ), - brake_parking( 0.0 ), - steering( 0.0 ), - nose_wheel_steering( true ), - gear_down( true ), antiskid( true ), tailhook( false ), launchbar( false ), - catapult_launch_cmd( false ), tailwheel_lock( true ), - wing_heat( false ), - pitot_heat( true ), - wiper( 0 ), - window_heat( false ), battery_switch( true ), external_power( false ), APU_generator( false ), @@ -82,15 +55,12 @@ FGControls::FGControls() : panel_norm( 0.0 ), instruments_norm( 0.0 ), dome_norm( 0.0 ), - master_arm( false ), station_select( 1 ), release_ALL( false ), vertical_adjust( 0.0 ), fore_aft_adjust( 0.0 ), + cmd_selector_valve( 0 ), off_start_run( 0 ), - APU_fire_switch( false ), - autothrottle_arm( false ), - autothrottle_engage( false ), heading_select( 0.0 ), altitude_select( 50000.0 ), bank_angle_select( 30.0 ), @@ -100,6 +70,15 @@ FGControls::FGControls() : vertical_mode( 0 ), lateral_mode( 0 ) { + auto_coordination = fgGetNode("/controls/flight/auto-coordination", true); + auto_coordination_factor = fgGetNode("/controls/flight/auto-coordination-factor", false ); + if( NULL == auto_coordination_factor ) { + auto_coordination_factor = fgGetNode("/controls/flight/auto-coordination-factor", true ); + auto_coordination_factor->setDoubleValue( 0.5 ); + } + + reset_all(); + globals->set_controls( this ); } @@ -160,62 +139,44 @@ void FGControls::reset_all() autothrottle_arm = false; autothrottle_engage = false; set_autopilot_engage( ALL_AUTOPILOTS, false ); + + brake_left = brake_right + = copilot_brake_left = copilot_brake_right + = brake_parking = 0.0; + + set_fuel_selector(ALL_TANKS, false); + set_to_engine(ALL_TANKS, 0); + set_to_tank(ALL_TANKS, 0); + set_boost_pump(ALL_TANKS, false); + + set_alternate_extension(ALL_WHEELS, false); + + set_mixture(ALL_ENGINES, 1.0); + set_prop_advance(ALL_ENGINES, 1.0); + set_generator_breaker(ALL_ENGINES, false); + set_bus_tie(ALL_ENGINES, false); + set_engine_bleed(ALL_ENGINES, false); + set_feed_tank(ALL_ENGINES, -1); // feed off + set_cowl_flaps_norm(ALL_ENGINES, 0.0); } // Destructor FGControls::~FGControls() { + if (globals) + globals->set_controls( NULL ); } void FGControls::init () { - throttle_idle = true; - for ( int engine = 0; engine < MAX_ENGINES; engine++ ) { - throttle[engine] = 0.0; - mixture[engine] = 1.0; - fuel_pump[engine] = false; - prop_advance[engine] = 1.0; - magnetos[engine] = 0; - feed_tank[engine] = -1; // set to -1 to turn off all tanks 0 feeds all engines from center body tank - starter[engine] = false; - feather[engine] = false; - ignition[engine] = false; - fire_switch[engine] = false; - fire_bottle_discharge[engine] = false; - cutoff[engine] = true; - augmentation[engine] = false; - reverser[engine] = false; - water_injection[engine] = false; - nitrous_injection[engine] = false; - cowl_flaps_norm[engine] = 0.0; - condition[engine] = 1.0; - carb_heat[engine] = false; - inlet_heat[engine] = false; - generator_breaker[engine] = false; - bus_tie[engine] = false; - engine_bleed[engine] = false; - } - - for ( int tank = 0; tank < MAX_TANKS; tank++ ) { - fuel_selector[tank] = false; - to_engine[tank] = 0; - to_tank[tank] = 0; - } - - for( int pump = 0; pump < MAX_TANKS * MAX_BOOSTPUMPS; pump++ ) { - boost_pump[pump] = false; - } - - brake_left = brake_right - = copilot_brake_left = copilot_brake_right - = brake_parking = 0.0; - for ( int wheel = 0; wheel < MAX_WHEELS; wheel++ ) { - alternate_extension[wheel] = false; - } +} - auto_coordination = fgGetNode("/sim/auto-coordination", true); +void +FGControls::reinit() +{ + reset_all(); } static inline void _SetRoot( simgear::TiedPropertyList & tiedProperties, const char * root, int index = 0 ) @@ -226,6 +187,7 @@ static inline void _SetRoot( simgear::TiedPropertyList & tiedProperties, const c void FGControls::bind () { + reset_all(); int index, i; // flight controls @@ -643,9 +605,11 @@ void FGControls::unbind () void FGControls::update (double dt) { -} - + SG_UNUSED(dt); + // nothing here, don't call again + suspend(); +} //////////////////////////////////////////////////////////////////////// // Setters and adjusters. @@ -656,11 +620,7 @@ FGControls::set_aileron (double pos) { aileron = pos; SG_CLAMP_RANGE( aileron, -1.0, 1.0 ); - - // check for autocoordination - if ( auto_coordination->getBoolValue() ) { - set_rudder( aileron / 2.0 ); - } + do_autocoordination(); } void @@ -668,11 +628,7 @@ FGControls::move_aileron (double amt) { aileron += amt; SG_CLAMP_RANGE( aileron, -1.0, 1.0 ); - - // check for autocoordination - if ( auto_coordination->getBoolValue() ) { - set_rudder( aileron / 2.0 ); - } + do_autocoordination(); } void diff --git a/src/Aircraft/controls.hxx b/src/Aircraft/controls.hxx index a738bc2..980233e 100644 --- a/src/Aircraft/controls.hxx +++ b/src/Aircraft/controls.hxx @@ -253,6 +253,7 @@ private: SGPropertyNode_ptr auto_coordination; + SGPropertyNode_ptr auto_coordination_factor; simgear::TiedPropertyList _tiedProperties; public: @@ -264,7 +265,8 @@ public: void bind (); void unbind (); void update (double dt); - + virtual void reinit(); + // Reset function void reset_all(void); @@ -637,6 +639,14 @@ public: // controls/autoflight/autopilot[n]/ void set_autopilot_engage( int ap, bool val ); +private: + inline void do_autocoordination() { + // check for autocoordination + if ( auto_coordination->getBoolValue() ) { + double factor = auto_coordination_factor->getDoubleValue(); + if( factor > 0.0 ) set_rudder( aileron * factor ); + } + } }; diff --git a/src/Aircraft/flightrecorder.cxx b/src/Aircraft/flightrecorder.cxx index dd4ccc1..5e37b2c 100644 --- a/src/Aircraft/flightrecorder.cxx +++ b/src/Aircraft/flightrecorder.cxx @@ -16,7 +16,7 @@ // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. // /////////////////////////////////////////////////////////////////////////////// @@ -33,10 +33,12 @@ #include #include #include +#include #include
#include "flightrecorder.hxx" using namespace FlightRecorder; +using std::string; FGFlightRecorder::FGFlightRecorder(const char* pConfigName) : m_RecorderNode(fgGetNode("/sim/flight-recorder", true)), @@ -54,6 +56,21 @@ FGFlightRecorder::reinit(void) { m_ConfigNode = 0; + SGPropertyNode_ptr ConfigNode; + int Selected = m_RecorderNode->getIntValue(m_ConfigName, 0); + SG_LOG(SG_SYSTEMS, SG_INFO, "FlightRecorder: Recorder configuration #" << Selected); + if (Selected >= 0) + ConfigNode = m_RecorderNode->getChild("config", Selected); + + if (!ConfigNode.valid()) + ConfigNode = getDefault(); + + reinit(ConfigNode); +} + +void +FGFlightRecorder::reinit(SGPropertyNode_ptr ConfigNode) +{ m_TotalRecordSize = 0; m_CaptureDouble.clear(); @@ -63,13 +80,7 @@ FGFlightRecorder::reinit(void) m_CaptureInt8.clear(); m_CaptureBool.clear(); - int Selected = m_RecorderNode->getIntValue(m_ConfigName, 0); - SG_LOG(SG_SYSTEMS, SG_INFO, "FlightRecorder: Recorder configuration #" << Selected); - if (Selected >= 0) - m_ConfigNode = m_RecorderNode->getChild("config", Selected); - - if (!m_ConfigNode.valid()) - initDefault(); + m_ConfigNode = ConfigNode; if (!m_ConfigNode.valid()) { @@ -143,9 +154,11 @@ FGFlightRecorder::haveProperty(SGPropertyNode* pProperty) /** Read default flight-recorder configuration. * Default should match properties as hard coded for versions up to FG2.4.0. */ -void -FGFlightRecorder::initDefault(void) +SGPropertyNode_ptr +FGFlightRecorder::getDefault(void) { + SGPropertyNode_ptr ConfigNode; + // set name of active flight recorder type SG_LOG(SG_SYSTEMS, SG_INFO, "FlightRecorder: No custom configuration. Loading generic default recorder."); @@ -166,7 +179,7 @@ FGFlightRecorder::initDefault(void) try { readProperties(path.str(), m_RecorderNode->getChild("config", 0 ,true), 0); - m_ConfigNode = m_RecorderNode->getChild("config", 0 ,false); + ConfigNode = m_RecorderNode->getChild("config", 0 ,false); } catch (sg_io_exception &e) { SG_LOG(SG_SYSTEMS, SG_ALERT, "FlightRecorder: Error reading file '" << @@ -174,6 +187,8 @@ FGFlightRecorder::initDefault(void) } } } + + return ConfigNode; } /** Read signal list below given base node. @@ -447,16 +462,20 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer, const return; int Offset = 0; - double ratio; + double ratio = 1.0; if (pLastBuffer) { double NextSimTime = _pNextBuffer->sim_time; double LastSimTime = _pLastBuffer->sim_time; - ratio = (SimTime - LastSimTime) / (NextSimTime - LastSimTime); - } - else - { - ratio = 1.0; + double Numerator = SimTime - LastSimTime; + double dt = NextSimTime - LastSimTime; + // avoid divide by zero and other quirks + if ((Numerator > 0.0)&&(dt != 0.0)) + { + ratio = Numerator / dt; + if (ratio > 1.0) + ratio = 1.0; + } } Offset += sizeof(double); @@ -546,3 +565,37 @@ FGFlightRecorder::replay(double SimTime, const FGReplayData* _pNextBuffer, const } } } + +int +FGFlightRecorder::getConfig(SGPropertyNode* root, const char* typeStr, const FlightRecorder::TSignalList& SignalList) +{ + static const char* InterpolationTypes[] = {"discrete", "linear", "angular-rad", "angular-deg"}; + size_t SignalCount = SignalList.size(); + SGPropertyNode* Signals = root->getNode("signals", true); + for (size_t i=0; iaddChild("signal"); + SignalProp->setStringValue("type", typeStr); + SignalProp->setStringValue("interpolation", InterpolationTypes[SignalList[i].Interpolation]); + SignalProp->setStringValue("property", SignalList[i].Signal->getPath()); + } + SG_LOG(SG_SYSTEMS, SG_DEBUG, "FlightRecorder: Have " << SignalCount << " signals of type " << typeStr); + root->setIntValue(typeStr, SignalCount); + return SignalCount; +} + +void +FGFlightRecorder::getConfig(SGPropertyNode* root) +{ + root->setStringValue("name", m_RecorderNode->getStringValue("active-config-name", "")); + int SignalCount = 0; + SignalCount += getConfig(root, "double", m_CaptureDouble); + SignalCount += getConfig(root, "float", m_CaptureFloat); + SignalCount += getConfig(root, "int", m_CaptureInteger); + SignalCount += getConfig(root, "int16", m_CaptureInt16); + SignalCount += getConfig(root, "int8", m_CaptureInt8); + SignalCount += getConfig(root, "bool", m_CaptureBool); + + root->setIntValue("recorder/record-size", getRecordSize()); + root->setIntValue("recorder/signal-count", SignalCount); +} diff --git a/src/Aircraft/flightrecorder.hxx b/src/Aircraft/flightrecorder.hxx index a2045f6..3fddc11 100644 --- a/src/Aircraft/flightrecorder.hxx +++ b/src/Aircraft/flightrecorder.hxx @@ -16,7 +16,7 @@ // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. // /////////////////////////////////////////////////////////////////////////////// @@ -54,6 +54,7 @@ public: virtual ~FGFlightRecorder(); void reinit (void); + void reinit (SGPropertyNode_ptr ConfigNode); FGReplayData* createEmptyRecord (void); FGReplayData* capture (double SimTime, FGReplayData* pRecycledBuffer); void replay (double SimTime, const FGReplayData* pNextBuffer, @@ -61,17 +62,20 @@ public: void deleteRecord (FGReplayData* pRecord); int getRecordSize (void) { return m_TotalRecordSize;} + void getConfig (SGPropertyNode* root); private: - void initDefault(void); + SGPropertyNode_ptr getDefault(void); void initSignalList(const char* pSignalType, FlightRecorder::TSignalList& SignalList, SGPropertyNode_ptr BaseNode); void processSignalList(const char* pSignalType, FlightRecorder::TSignalList& SignalList, SGPropertyNode_ptr SignalListNode, - string PropPrefix="", int Count = 1); + std::string PropPrefix="", int Count = 1); bool haveProperty(FlightRecorder::TSignalList& Capture,SGPropertyNode* pProperty); bool haveProperty(SGPropertyNode* pProperty); + int getConfig(SGPropertyNode* root, const char* typeStr, const FlightRecorder::TSignalList& SignalList); + SGPropertyNode_ptr m_RecorderNode; SGPropertyNode_ptr m_ConfigNode; @@ -83,7 +87,7 @@ private: FlightRecorder::TSignalList m_CaptureBool; int m_TotalRecordSize; - string m_ConfigName; + std::string m_ConfigName; }; #endif /* FLIGHTRECORDER_HXX_ */ diff --git a/src/Aircraft/replay.cxx b/src/Aircraft/replay.cxx index 702a8f9..cea3bf2 100644 --- a/src/Aircraft/replay.cxx +++ b/src/Aircraft/replay.cxx @@ -1,7 +1,7 @@ // replay.cxx - a system to record and replay FlightGear flights // // Written by Curtis Olson, started July 2003. -// Updated by Thorsten Brehm, September 2011. +// Updated by Thorsten Brehm, September 2011 and November 2012. // // Copyright (C) 2003 Curtis L. Olson - http://www.flightgear.org/~curt // @@ -29,17 +29,55 @@ #include #include +#include +#include +#include +#include +#include #include
#include "replay.hxx" #include "flightrecorder.hxx" +using std::deque; +using std::vector; +using simgear::gzContainerReader; +using simgear::gzContainerWriter; + +#if 1 + #define MY_SG_DEBUG SG_DEBUG +#else + #define MY_SG_DEBUG SG_ALERT +#endif + +/** Magic string to verify valid FG flight recorder tapes. */ +static const char* const FlightRecorderFileMagic = "FlightGear Flight Recorder Tape"; + +namespace ReplayContainer +{ + enum Type + { + Invalid = -1, + Header = 0, /**< Used for initial file header (fixed identification string). */ + MetaData = 1, /**< XML data / properties with arbitrary data, such as description, aircraft type, ... */ + Properties = 2, /**< XML data describing the recorded flight recorder properties. + Format is identical to flight recorder XML configuration. Also contains some + extra data to verify flight recorder consistency. */ + RawData = 3 /**< Actual binary data blobs (the recorder's tape). + One "RawData" blob is used for each resolution. */ + }; +} + /** * Constructor */ FGReplay::FGReplay() : + sim_time(0), + last_mt_time(0.0), + last_lt_time(0.0), + last_msg_time(0), last_replay_state(0), m_high_res_time(60.0), m_medium_res_time(600.0), @@ -88,6 +126,9 @@ FGReplay::clear() m_pRecorder->deleteRecord(recycler.front()); recycler.pop_front(); } + + // clear messages belonging to old replay session + fgGetNode("/sim/replay/messages", 0, true)->removeChildren("msg", false); } /** @@ -98,11 +139,15 @@ void FGReplay::init() { disable_replay = fgGetNode("/sim/replay/disable", true); - replay_master = fgGetNode("/sim/freeze/replay-state", true); + replay_master = fgGetNode("/sim/replay/replay-state", true); replay_time = fgGetNode("/sim/replay/time", true); replay_time_str = fgGetNode("/sim/replay/time-str", true); replay_looped = fgGetNode("/sim/replay/looped", true); speed_up = fgGetNode("/sim/speed-up", true); + + // alias to keep backward compatibility + fgGetNode("/sim/freeze/replay-state", true)->alias(replay_master); + reinit(); } @@ -116,6 +161,7 @@ FGReplay::reinit() sim_time = 0.0; last_mt_time = 0.0; last_lt_time = 0.0; + last_msg_time = 0.0; // Flush queues clear(); @@ -128,20 +174,9 @@ FGReplay::reinit() m_medium_sample_rate = fgGetDouble("/sim/replay/buffer/medium-res-sample-dt", 0.5); // medium term sample rate (sec) m_long_sample_rate = fgGetDouble("/sim/replay/buffer/low-res-sample-dt", 5.0); // long term sample rate (sec) - // Create an estimated nr of required ReplayData objects - // 120 is an estimated maximum frame rate. - int estNrObjects = (int) ((m_high_res_time*120) + (m_medium_res_time*m_medium_sample_rate) + - (m_low_res_time*m_long_sample_rate)); - for (int i = 0; i < estNrObjects; i++) - { - FGReplayData* r = m_pRecorder->createEmptyRecord(); - if (r) - recycler.push_back(r); - else - { - SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Out of memory!"); - } - } + fillRecycler(); + loadMessages(); + replay_master->setIntValue(0); disable_replay->setBoolValue(0); replay_time->setDoubleValue(0); @@ -157,7 +192,6 @@ FGReplay::bind() { } - /** * Unbind from the property tree */ @@ -168,6 +202,25 @@ FGReplay::unbind() // nothing to unbind } +void +FGReplay::fillRecycler() +{ + // Create an estimated nr of required ReplayData objects + // 120 is an estimated maximum frame rate. + int estNrObjects = (int) ((m_high_res_time*120) + (m_medium_res_time*m_medium_sample_rate) + + (m_low_res_time*m_long_sample_rate)); + for (int i = 0; i < estNrObjects; i++) + { + FGReplayData* r = m_pRecorder->createEmptyRecord(); + if (r) + recycler.push_back(r); + else + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Out of memory!"); + } + } +} + static void printTimeStr(char* pStrBuffer,double _Time, bool ShowDecimal=true) { @@ -195,14 +248,67 @@ printTimeStr(char* pStrBuffer,double _Time, bool ShowDecimal=true) sprintf(&pStrBuffer[len],".%u",d); } +void +FGReplay::guiMessage(const char* message) +{ + fgSetString("/sim/messages/copilot", message); +} + +void +FGReplay::loadMessages() +{ + // load messages + replay_messages.clear(); + simgear::PropertyList msgs = fgGetNode("/sim/replay/messages", true)->getChildren("msg"); + + for (simgear::PropertyList::iterator it = msgs.begin();it != msgs.end();++it) + { + const char* msgText = (*it)->getStringValue("text", ""); + const double msgTime = (*it)->getDoubleValue("time", -1.0); + const char* msgSpeaker = (*it)->getStringValue("speaker", "pilot"); + if ((msgText[0] != 0)&&(msgTime >= 0)) + { + FGReplayMessages data; + data.sim_time = msgTime; + data.message = msgText; + data.speaker = msgSpeaker; + replay_messages.push_back(data); + } + } + current_msg = replay_messages.begin(); +} + +void +FGReplay::replayMessage(double time) +{ + if (time < last_msg_time) + { + current_msg = replay_messages.begin(); + } + + // check if messages have to be triggered + while ((current_msg != replay_messages.end())&& + (time >= current_msg->sim_time)) + { + // don't trigger messages when too long ago (fast-forward/skipped replay) + if (time - current_msg->sim_time < 3.0) + { + fgGetNode("/sim/messages", true)->getNode(current_msg->speaker, true)->setStringValue(current_msg->message); + } + ++current_msg; + } + last_msg_time = time; +} + /** Start replay session */ bool -FGReplay::start() +FGReplay::start(bool NewTape) { // freeze the fdm, resume from sim pause double StartTime = get_start_time(); double EndTime = get_end_time(); + was_finished_already = false; fgSetDouble("/sim/replay/start-time", StartTime); fgSetDouble("/sim/replay/end-time", EndTime); char StrBuffer[30]; @@ -216,15 +322,26 @@ FGReplay::start() buffer_elements*m_pRecorder->getRecordSize() / (1024*1024.0)); if ((fgGetBool("/sim/freeze/master"))|| (0 == replay_master->getIntValue())) - fgSetString("/sim/messages/copilot", "Replay active. 'Esc' to stop."); + guiMessage("Replay active. 'Esc' to stop."); fgSetBool ("/sim/freeze/master", 0); fgSetBool ("/sim/freeze/clock", 0); if (0 == replay_master->getIntValue()) { replay_master->setIntValue(1); - replay_time->setDoubleValue(-1); + if (NewTape) + { + // start replay at initial time, when loading a new tape + replay_time->setDoubleValue(StartTime); + } + else + { + // start replay at "loop interval" when starting instant replay + replay_time->setDoubleValue(-1); + } replay_time_str->setStringValue(""); } + loadMessages(); + return true; } @@ -244,6 +361,7 @@ FGReplay::update( double dt ) if (fgGetBool("/sim/freeze/master",false)|| fgGetBool("/sim/freeze/clock",false)) { + // unpause - disable the replay system in next loop fgSetBool("/sim/freeze/master",false); fgSetBool("/sim/freeze/clock",false); last_replay_state = 1; @@ -252,6 +370,7 @@ FGReplay::update( double dt ) if ((replay_master->getIntValue() != 3)|| (last_replay_state == 3)) { + // disable the replay system current_replay_state = replay_master->getIntValue(); replay_master->setIntValue(0); replay_time->setDoubleValue(0); @@ -264,7 +383,7 @@ FGReplay::update( double dt ) fgSetBool("/sim/sound/enabled",true); fgSetBool("/sim/replay/mute",false); } - fgSetString("/sim/messages/copilot", "Replay stopped. Your controls!"); + guiMessage("Replay stopped. Your controls!"); } } @@ -302,14 +421,17 @@ FGReplay::update( double dt ) { // replay active double current_time = replay_time->getDoubleValue(); - if (current_time<=0.0) + bool ResetTime = (current_time<=0.0); + if (ResetTime) { // initialize start time double startTime = get_start_time(); double endTime = get_end_time(); fgSetDouble( "/sim/replay/start-time", startTime ); fgSetDouble( "/sim/replay/end-time", endTime ); - double duration = fgGetDouble( "/sim/replay/duration" ); + double duration = 0; + if (replay_looped->getBoolValue()) + fgGetDouble("/sim/replay/duration"); if( duration && (duration < (endTime - startTime)) ) { current_time = endTime - duration; } else { @@ -318,13 +440,28 @@ FGReplay::update( double dt ) } bool IsFinished = replay( replay_time->getDoubleValue() ); if (IsFinished) + { + if (!was_finished_already) + { + guiMessage("End of tape. 'Esc' to return."); + was_finished_already = true; + } current_time = (replay_looped->getBoolValue()) ? -1 : get_end_time()+0.01; + } else + { current_time += dt * speed_up->getDoubleValue(); + was_finished_already = false; + } replay_time->setDoubleValue(current_time); char StrBuffer[30]; printTimeStr(StrBuffer,current_time); replay_time_str->setStringValue((const char*)StrBuffer); + + // when time skipped (looped replay), trigger listeners to reset views etc + if (ResetTime) + replay_master->setIntValue(replay_state); + return; // don't record the replay session } case 2: // normal replay operation @@ -335,12 +472,19 @@ FGReplay::update( double dt ) // flight recording - //cerr << "Recording replay" << endl; - sim_time += dt * speed_up->getDoubleValue(); - // sanity check, don't collect data if FDM data isn't good - if (!fgGetBool("/sim/fdm-initialized", false)) { + if ((!fgGetBool("/sim/fdm-initialized", false))||(dt==0.0)) return; + + { + double new_sim_time = sim_time + dt * speed_up->getDoubleValue(); + // don't record multiple records with the same timestamp (or go backwards in time) + if (new_sim_time <= sim_time) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Time warp detected!"); + return; + } + sim_time = new_sim_time; } FGReplayData* r = record(sim_time); @@ -351,9 +495,7 @@ FGReplay::update( double dt ) } // update the short term list - //stamp("point_06"); short_term.push_back( r ); - //stamp("point_07"); FGReplayData *st_front = short_term.front(); if (!st_front) @@ -361,39 +503,45 @@ FGReplay::update( double dt ) SG_LOG(SG_SYSTEMS, SG_ALERT, "ReplaySystem: Inconsistent data!"); } - if ( sim_time - st_front->sim_time > m_high_res_time ) { - while ( sim_time - st_front->sim_time > m_high_res_time ) { + if ( sim_time - st_front->sim_time > m_high_res_time ) + { + while ( sim_time - st_front->sim_time > m_high_res_time ) + { st_front = short_term.front(); recycler.push_back(st_front); short_term.pop_front(); } - //stamp("point_08"); + // update the medium term list - if ( sim_time - last_mt_time > m_medium_sample_rate ) { + if ( sim_time - last_mt_time > m_medium_sample_rate ) + { last_mt_time = sim_time; st_front = short_term.front(); medium_term.push_back( st_front ); short_term.pop_front(); FGReplayData *mt_front = medium_term.front(); - if ( sim_time - mt_front->sim_time > m_medium_res_time ) { - //stamp("point_09"); - while ( sim_time - mt_front->sim_time > m_medium_res_time ) { + if ( sim_time - mt_front->sim_time > m_medium_res_time ) + { + while ( sim_time - mt_front->sim_time > m_medium_res_time ) + { mt_front = medium_term.front(); recycler.push_back(mt_front); medium_term.pop_front(); } // update the long term list - if ( sim_time - last_lt_time > m_long_sample_rate ) { + if ( sim_time - last_lt_time > m_long_sample_rate ) + { last_lt_time = sim_time; mt_front = medium_term.front(); long_term.push_back( mt_front ); medium_term.pop_front(); FGReplayData *lt_front = long_term.front(); - if ( sim_time - lt_front->sim_time > m_low_res_time ) { - //stamp("point_10"); - while ( sim_time - lt_front->sim_time > m_low_res_time ) { + if ( sim_time - lt_front->sim_time > m_low_res_time ) + { + while ( sim_time - lt_front->sim_time > m_low_res_time ) + { lt_front = long_term.front(); recycler.push_back(lt_front); long_term.pop_front(); @@ -429,9 +577,7 @@ FGReplay::record(double time) recycler.pop_front(); } - r = m_pRecorder->capture(time, r); - - return r; + return m_pRecorder->capture(time, r); } /** @@ -490,6 +636,8 @@ FGReplay::replay( double time ) { // find the two frames to interpolate between double t1, t2; + replayMessage(time); + if ( short_term.size() > 0 ) { t1 = short_term.back()->sim_time; t2 = short_term.front()->sim_time; @@ -498,52 +646,43 @@ FGReplay::replay( double time ) { replay( time, short_term.back() ); // replay is finished now return true; - // cout << "first frame" << endl; } else if ( time <= t1 && time >= t2 ) { interpolate( time, short_term ); - // cout << "from short term" << endl; } else if ( medium_term.size() > 0 ) { t1 = short_term.front()->sim_time; t2 = medium_term.back()->sim_time; if ( time <= t1 && time >= t2 ) { replay(time, medium_term.back(), short_term.front()); - // cout << "from short/medium term" << endl; } else { t1 = medium_term.back()->sim_time; t2 = medium_term.front()->sim_time; if ( time <= t1 && time >= t2 ) { interpolate( time, medium_term ); - // cout << "from medium term" << endl; } else if ( long_term.size() > 0 ) { t1 = medium_term.front()->sim_time; t2 = long_term.back()->sim_time; if ( time <= t1 && time >= t2 ) { replay(time, long_term.back(), medium_term.front()); - // cout << "from medium/long term" << endl; } else { t1 = long_term.back()->sim_time; t2 = long_term.front()->sim_time; if ( time <= t1 && time >= t2 ) { interpolate( time, long_term ); - // cout << "from long term" << endl; } else { // replay the oldest long term frame replay(time, long_term.front()); - // cout << "oldest long term frame" << endl; } } } else { // replay the oldest medium term frame replay(time, medium_term.front()); - // cout << "oldest medium term frame" << endl; } } } else { // replay the oldest short term frame replay(time, short_term.front()); - // cout << "oldest short term frame" << endl; } } else { // nothing to replay @@ -590,3 +729,418 @@ FGReplay::get_end_time() return 0.0; } } + +/** Save raw replay data in a separate container */ +static bool +saveRawReplayData(gzContainerWriter& output, const replay_list_type& ReplayData, size_t RecordSize) +{ + // get number of records in this stream + size_t Count = ReplayData.size(); + + // write container header for raw data + if (!output.writeContainerHeader(ReplayContainer::RawData, Count * RecordSize)) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Failed to save replay data. Cannot write data container. Disk full?"); + return false; + } + + // write the raw data (all records in the given list) + replay_list_type::const_iterator it = ReplayData.begin(); + size_t CheckCount = 0; + while ((it != ReplayData.end())&& + !output.fail()) + { + const FGReplayData* pRecord = *it++; + output.write((char*)pRecord, RecordSize); + CheckCount++; + } + + // Did we really write as much as we intended? + if (CheckCount != Count) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Failed to save replay data. Expected to write " << Count << " records, but wrote " << CheckCount); + return false; + } + + SG_LOG(SG_SYSTEMS, MY_SG_DEBUG, "Saved " << CheckCount << " records of size " << RecordSize); + return !output.fail(); +} + +/** Load raw replay data from a separate container */ +static bool +loadRawReplayData(gzContainerReader& input, FGFlightRecorder* pRecorder, replay_list_type& ReplayData, size_t RecordSize) +{ + size_t Size = 0; + simgear::ContainerType Type = ReplayContainer::Invalid; + + // write container header for raw data + if (!input.readContainerHeader(&Type, &Size)) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Failed to load replay data. Missing data container."); + return false; + } + else + if (Type != ReplayContainer::RawData) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Failed to load replay data. Expected data container, got " << Type); + return false; + } + + // read the raw data + size_t Count = Size / RecordSize; + SG_LOG(SG_SYSTEMS, MY_SG_DEBUG, "Loading replay data. Container size is " << Size << ", record size " << RecordSize << + ", expected record count " << Count << "."); + + size_t CheckCount = 0; + for (CheckCount=0; (CheckCountcreateEmptyRecord(); + input.read((char*) pBuffer, RecordSize); + ReplayData.push_back(pBuffer); + } + + // did we get all we have hoped for? + if (CheckCount != Count) + { + if (input.eof()) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Unexpected end of file."); + } + SG_LOG(SG_SYSTEMS, SG_ALERT, "Failed to load replay data. Expected " << Count << " records, but got " << CheckCount); + return false; + } + + SG_LOG(SG_SYSTEMS, MY_SG_DEBUG, "Loaded " << CheckCount << " records of size " << RecordSize); + return true; +} + +/** Write flight recorder tape with given filename and meta properties to disk */ +bool +FGReplay::saveTape(const char* Filename, SGPropertyNode* MetaDataProps) +{ + bool ok = true; + + /* open output stream *******************************************/ + gzContainerWriter output(Filename, FlightRecorderFileMagic); + if (!output.good()) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Cannot open file" << Filename); + return false; + } + + /* write meta data **********************************************/ + ok &= output.writeContainer(ReplayContainer::MetaData, MetaDataProps); + + /* write flight recorder configuration **************************/ + SGPropertyNode_ptr Config; + if (ok) + { + Config = new SGPropertyNode(); + m_pRecorder->getConfig(Config.get()); + ok &= output.writeContainer(ReplayContainer::Properties, Config.get()); + } + + /* write raw data ***********************************************/ + if (Config) + { + size_t RecordSize = Config->getIntValue("recorder/record-size", 0); + SG_LOG(SG_SYSTEMS, MY_SG_DEBUG, "Total signal count: " << Config->getIntValue("recorder/signal-count", 0) + << ", record size: " << RecordSize); + if (ok) + ok &= saveRawReplayData(output, short_term, RecordSize); + if (ok) + ok &= saveRawReplayData(output, medium_term, RecordSize); + if (ok) + ok &= saveRawReplayData(output, long_term, RecordSize); + Config = 0; + } + + /* done *********************************************************/ + output.close(); + + return ok; +} + +/** Write flight recorder tape to disk. User/script command. */ +bool +FGReplay::saveTape(const SGPropertyNode* ConfigData) +{ + const char* tapeDirectory = fgGetString("/sim/replay/tape-directory", ""); + const char* aircraftType = fgGetString("/sim/aircraft", "unknown"); + + SGPropertyNode_ptr myMetaData = new SGPropertyNode(); + SGPropertyNode* meta = myMetaData->getNode("meta", 0, true); + + // add some data to the file - so we know for which aircraft/version it was recorded + meta->setStringValue("aircraft-type", aircraftType); + meta->setStringValue("aircraft-description", fgGetString("/sim/description", "")); + meta->setStringValue("aircraft-fdm", fgGetString("/sim/flight-model", "")); + meta->setStringValue("closest-airport-id", fgGetString("/sim/airport/closest-airport-id", "")); + const char* aircraft_version = fgGetString("/sim/aircraft-version", ""); + if (aircraft_version[0]==0) + aircraft_version = "(undefined)"; + meta->setStringValue("aircraft-version", aircraft_version); + + // add information on the tape's recording duration + double Duration = get_end_time()-get_start_time(); + meta->setDoubleValue("tape-duration", Duration); + char StrBuffer[30]; + printTimeStr(StrBuffer, Duration, false); + meta->setStringValue("tape-duration-str", StrBuffer); + + // add simulator version + copyProperties(fgGetNode("/sim/version", 0, true), meta->getNode("version", 0, true)); + if (ConfigData->getNode("user-data")) + { + copyProperties(ConfigData->getNode("user-data"), meta->getNode("user-data", 0, true)); + } + + // store replay messages + copyProperties(fgGetNode("/sim/replay/messages", 0, true), myMetaData->getNode("messages", 0, true)); + + // generate file name (directory + aircraft type + date + time + suffix) + SGPath p(tapeDirectory); + p.append(aircraftType); + p.concat("-"); + time_t calendar_time = time(NULL); + struct tm *local_tm; + local_tm = localtime( &calendar_time ); + char time_str[256]; + strftime( time_str, 256, "%Y%02m%02d-%02H%02M%02S", local_tm); + p.concat(time_str); + p.concat(".fgtape"); + + bool ok = true; + // make sure we're not overwriting something + if (p.exists()) + { + // same timestamp!? + SG_LOG(SG_SYSTEMS, SG_ALERT, "Error, flight recorder tape file with same name already exists."); + ok = false; + } + + if (ok) + ok &= saveTape(p.c_str(), myMetaData.get()); + + if (ok) + guiMessage("Flight recorder tape saved successfully!"); + else + guiMessage("Failed to save tape! See log output."); + + return ok; +} + +/** Read a flight recorder tape with given filename from disk and return meta properties. + * Actual data and signal configuration is not read when in "Preview" mode. + */ +bool +FGReplay::loadTape(const char* Filename, bool Preview, SGPropertyNode* UserData) +{ + bool ok = true; + + /* open input stream ********************************************/ + gzContainerReader input(Filename, FlightRecorderFileMagic); + if (input.eof() || !input.good()) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Cannot open file " << Filename); + ok = false; + } + + SGPropertyNode_ptr MetaDataProps = new SGPropertyNode(); + + /* read meta data ***********************************************/ + if (ok) + { + char* MetaData = NULL; + size_t Size = 0; + simgear::ContainerType Type = ReplayContainer::Invalid; + if (!input.readContainer(&Type, &MetaData, &Size) || Size<1) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "File not recognized. This is not a valid FlightGear flight recorder tape: " << Filename + << ". Invalid meta data."); + ok = false; + } + else + if (Type != ReplayContainer::MetaData) + { + SG_LOG(SG_SYSTEMS, MY_SG_DEBUG, "Invalid header. Container type " << Type); + SG_LOG(SG_SYSTEMS, SG_ALERT, "File not recognized. This is not a valid FlightGear flight recorder tape: " << Filename); + ok = false; + } + else + { + try + { + readProperties(MetaData, Size-1, MetaDataProps); + copyProperties(MetaDataProps->getNode("meta", 0, true), UserData); + } catch (const sg_exception &e) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Error reading flight recorder tape: " << Filename + << ", XML parser message:" << e.getFormattedMessage()); + ok = false; + } + } + + if (MetaData) + { + //printf("%s\n", MetaData); + free(MetaData); + MetaData = NULL; + } + } + + /* read flight recorder configuration **************************/ + if ((ok)&&(!Preview)) + { + SG_LOG(SG_SYSTEMS, MY_SG_DEBUG, "Loading flight recorder data..."); + char* ConfigXML = NULL; + size_t Size = 0; + simgear::ContainerType Type = ReplayContainer::Invalid; + if (!input.readContainer(&Type, &ConfigXML, &Size) || Size<1) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "File not recognized. This is not a valid FlightGear flight recorder tape: " << Filename + << ". Invalid configuration container."); + ok = false; + } + else + if ((!ConfigXML)||(Type != ReplayContainer::Properties)) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "File not recognized. This is not a valid FlightGear flight recorder tape: " << Filename + << ". Unexpected container type, expected \"properties\"."); + ok = false; + } + + SGPropertyNode_ptr Config = new SGPropertyNode(); + if (ok) + { + try + { + readProperties(ConfigXML, Size-1, Config); + } catch (const sg_exception &e) + { + SG_LOG(SG_SYSTEMS, SG_ALERT, "Error reading flight recorder tape: " << Filename + << ", XML parser message:" << e.getFormattedMessage()); + ok = false; + } + if (ok) + { + // reconfigure the recorder - and wipe old data (no longer matches the current recorder) + m_pRecorder->reinit(Config); + clear(); + fillRecycler(); + } + } + + if (ConfigXML) + { + free(ConfigXML); + ConfigXML = NULL; + } + + /* read raw data ***********************************************/ + if (ok) + { + size_t RecordSize = m_pRecorder->getRecordSize(); + size_t OriginalSize = Config->getIntValue("recorder/record-size", 0); + // check consistency - ugly things happen when data vs signals mismatch + if ((OriginalSize != RecordSize)&& + (OriginalSize != 0)) + { + ok = false; + SG_LOG(SG_SYSTEMS, SG_ALERT, "Error: Data inconsistency. Flight recorder tape has record size " << RecordSize + << ", expected size was " << OriginalSize << "."); + } + + if (ok) + ok &= loadRawReplayData(input, m_pRecorder, short_term, RecordSize); + if (ok) + ok &= loadRawReplayData(input, m_pRecorder, medium_term, RecordSize); + if (ok) + ok &= loadRawReplayData(input, m_pRecorder, long_term, RecordSize); + + // restore replay messages + if (ok) + { + copyProperties(MetaDataProps->getNode("messages", 0, true), + fgGetNode("/sim/replay/messages", 0, true)); + } + sim_time = get_end_time(); + // TODO we could (re)store these too + last_mt_time = last_lt_time = sim_time; + } + /* done *********************************************************/ + } + + input.close(); + + if (!Preview) + { + if (ok) + { + guiMessage("Flight recorder tape loaded successfully!"); + start(true); + } + else + guiMessage("Failed to load tape. See log output."); + } + + return ok; +} + +/** List available tapes in current directory. + * Limits to tapes matching current aircraft when SameAircraftFilter is enabled. + */ +bool +FGReplay::listTapes(bool SameAircraftFilter, const SGPath& tapeDirectory) +{ + const std::string& aircraftType = simgear::strutils::uppercase(fgGetString("/sim/aircraft", "unknown")); + + // process directory listing of ".fgtape" files + simgear::Dir dir(tapeDirectory); + simgear::PathList list = dir.children(simgear::Dir::TYPE_FILE, ".fgtape"); + + SGPropertyNode* TapeList = fgGetNode("/sim/replay/tape-list", true); + TapeList->removeChildren("tape", false); + int Index = 0; + size_t l = aircraftType.size(); + for (simgear::PathList::iterator it = list.begin(); it!=list.end(); ++it) + { + SGPath file(it->file()); + std::string name(file.base()); + if ((!SameAircraftFilter)|| + (0==simgear::strutils::uppercase(name).compare(0,l, aircraftType))) + { + TapeList->getNode("tape", Index++, true)->setStringValue(name); + } + } + + return true; +} + +/** Load a flight recorder tape from disk. User/script command. */ +bool +FGReplay::loadTape(const SGPropertyNode* ConfigData) +{ + SGPath tapeDirectory(fgGetString("/sim/replay/tape-directory", "")); + + // see if shall really load the file - or just obtain the meta data for preview + bool Preview = ConfigData->getBoolValue("preview", 0); + + // file/tape to be loaded + std::string tape = ConfigData->getStringValue("tape", ""); + + if (tape.empty()) + { + if (!Preview) + return listTapes(ConfigData->getBoolValue("same-aircraft", 0), tapeDirectory); + return true; + } + else + { + SGPropertyNode* UserData = fgGetNode("/sim/gui/dialogs/flightrecorder/preview", true); + tapeDirectory.append(tape); + tapeDirectory.concat(".fgtape"); + SG_LOG(SG_SYSTEMS, MY_SG_DEBUG, "Checking flight recorder file " << tapeDirectory << ", preview: " << Preview); + return loadTape(tapeDirectory.c_str(), Preview, UserData); + } +} diff --git a/src/Aircraft/replay.hxx b/src/Aircraft/replay.hxx index 7eaa591..3cf3b5a 100644 --- a/src/Aircraft/replay.hxx +++ b/src/Aircraft/replay.hxx @@ -30,13 +30,12 @@ #include -#include - #include #include #include -using std::deque; +#include +#include class FGFlightRecorder; @@ -46,9 +45,14 @@ typedef struct { /* more data here, hidden to the outside world */ } FGReplayData; -typedef deque < FGReplayData *> replay_list_type; - +typedef struct { + double sim_time; + std::string message; + std::string speaker; +} FGReplayMessages; +typedef std::deque < FGReplayData *> replay_list_type; +typedef std::vector < FGReplayMessages > replay_messages_type; /** * A recording/replay module for FlightGear flights @@ -57,9 +61,7 @@ typedef deque < FGReplayData *> replay_list_type; class FGReplay : public SGSubsystem { - public: - FGReplay (); virtual ~FGReplay(); @@ -68,27 +70,44 @@ public: virtual void bind(); virtual void unbind(); virtual void update( double dt ); - bool start(); + bool start(bool NewTape=false); + + bool saveTape(const SGPropertyNode* ConfigData); + bool loadTape(const SGPropertyNode* ConfigData); private: void clear(); FGReplayData* record(double time); void interpolate(double time, const replay_list_type &list); void replay(double time, FGReplayData* pCurrentFrame, FGReplayData* pOldFrame=NULL); + void guiMessage(const char* message); + void loadMessages(); + void fillRecycler(); bool replay( double time ); + void replayMessage( double time ); + double get_start_time(); double get_end_time(); + bool listTapes(bool SameAircraftFilter, const SGPath& tapeDirectory); + bool saveTape(const char* Filename, SGPropertyNode* MetaData); + bool loadTape(const char* Filename, bool Preview, SGPropertyNode* UserData); + double sim_time; double last_mt_time; double last_lt_time; + double last_msg_time; + replay_messages_type::iterator current_msg; int last_replay_state; + bool was_finished_already; replay_list_type short_term; replay_list_type medium_term; replay_list_type long_term; replay_list_type recycler; + replay_messages_type replay_messages; + SGPropertyNode_ptr disable_replay; SGPropertyNode_ptr replay_master; SGPropertyNode_ptr replay_time; @@ -106,5 +125,4 @@ private: FGFlightRecorder* m_pRecorder; }; - #endif // _FG_REPLAY_HXX diff --git a/src/Airports/apt_loader.cxx b/src/Airports/apt_loader.cxx index eca0afb..a3aa490 100644 --- a/src/Airports/apt_loader.cxx +++ b/src/Airports/apt_loader.cxx @@ -39,20 +39,22 @@ #include #include #include -#include +#include #include #include "simple.hxx" #include "runways.hxx" #include "pavement.hxx" - +#include #include #include using namespace std; +typedef SGSharedPtr FGPavementPtr; + static FGPositioned::Type fptypeFromRobinType(int aType) { switch (aType) { @@ -65,23 +67,26 @@ static FGPositioned::Type fptypeFromRobinType(int aType) } } + +namespace flightgear +{ + class APTLoader { public: APTLoader() : last_apt_id(""), - last_apt_name(""), last_apt_elev(0.0), - last_apt_info(""), - last_apt_type("") - {} - - + last_apt_info("") + { + currentAirportID = 0; + cache = NavDataCache::instance(); + } - void parseAPT(const string &aptdb_file) + void parseAPT(const SGPath &aptdb_file) { - sg_gzifstream in( aptdb_file ); + sg_gzifstream in( aptdb_file.str() ); if ( !in.is_open() ) { SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << aptdb_file ); @@ -148,6 +153,8 @@ public: double elev = atof( token[3].c_str() ); tower = SGGeod::fromDegFt(lon, lat, elev + last_apt_elev); got_tower = true; + + cache->insertTower(currentAirportID, tower); } else if ( line_id == 19 ) { // windsock entry (ignore) } else if ( line_id == 20 ) { @@ -181,7 +188,7 @@ public: } } - addAirport(); + finishAirport(); } private: @@ -192,48 +199,50 @@ private: int rwy_count; bool got_tower; string last_apt_id; - string last_apt_name; double last_apt_elev; SGGeod tower; string last_apt_info; - string last_apt_type; string pavement_ident; bool pavement; - vector runways; - vector taxiways; + //vector runways; + //vector taxiways; vector pavements; - vector commStations; - void addAirport() - { - if (last_apt_id.empty()) { + NavDataCache* cache; + PositionedID currentAirportID; + + void finishAirport() + { + if (currentAirportID == 0) { return; } - + if (!rwy_count) { - SG_LOG(SG_GENERAL, SG_ALERT, "ERROR: No runways for " << last_apt_id - << ", skipping." ); - return; + currentAirportID = 0; + SG_LOG(SG_GENERAL, SG_ALERT, "ERROR: No runways for " << last_apt_id + << ", skipping." ); + return; } double lat = rwy_lat_accum / (double)rwy_count; double lon = rwy_lon_accum / (double)rwy_count; if (!got_tower) { - // tower height hard coded for now... - const float tower_height = 50.0f; - // make a little off the heading for 1 runway airports... - float fudge_lon = fabs(sin(last_rwy_heading * SGD_DEGREES_TO_RADIANS)) * .003f; - float fudge_lat = .003f - fudge_lon; - tower = SGGeod::fromDegFt(lon + fudge_lon, lat + fudge_lat, last_apt_elev + tower_height); + // tower height hard coded for now... + const float tower_height = 50.0f; + // make a little off the heading for 1 runway airports... + float fudge_lon = fabs(sin(last_rwy_heading * SGD_DEGREES_TO_RADIANS)) * .003f; + float fudge_lat = .003f - fudge_lon; + tower = SGGeod::fromDegFt(lon + fudge_lon, lat + fudge_lat, last_apt_elev + tower_height); + + cache->insertTower(currentAirportID, tower); } SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev)); - FGAirport* apt = new FGAirport(last_apt_id, pos, tower, last_apt_name, false, - fptypeFromRobinType(atoi(last_apt_type.c_str()))); - apt->setRunwaysAndTaxiways(runways, taxiways, pavements); - apt->setCommStations(commStations); + cache->updatePosition(currentAirportID, pos); + + currentAirportID = 0; } void parseAirportLine(const vector& token) @@ -241,25 +250,26 @@ private: const string& id(token[4]); double elev = atof( token[1].c_str() ); - addAirport(); + // finish the previous airport + finishAirport(); - last_apt_id = id; last_apt_elev = elev; - last_apt_name = ""; got_tower = false; + string name; // build the name for ( unsigned int i = 5; i < token.size() - 1; ++i ) { - last_apt_name += token[i]; - last_apt_name += " "; + name += token[i] + " "; } - last_apt_name += token[token.size() - 1]; - last_apt_type = token[0]; + name += token[token.size() - 1]; // clear runway list for start of next airport rwy_lon_accum = 0.0; rwy_lat_accum = 0.0; rwy_count = 0; + + int robinType = atoi(token[0].c_str()); + currentAirportID = cache->insertAirport(fptypeFromRobinType(robinType), id, name); } void parseRunwayLine810(const vector& token) @@ -282,9 +292,8 @@ private: SGGeod pos(SGGeod::fromDegFt(lon, lat, last_apt_elev)); if (rwy_no[0] == 'x') { - // taxiway - FGTaxiway* t = new FGTaxiway(rwy_no, pos, heading, length, width, surface_code); - taxiways.push_back(t); + cache->insertRunway(FGPositioned::TAXIWAY,rwy_no, pos, currentAirportID, + heading, length, width, 0, 0, surface_code); } else { // (pair of) runways string rwy_displ_threshold = token[6]; @@ -299,18 +308,18 @@ private: double stopway1 = atof( stop[0].c_str() ); double stopway2 = atof( stop[1].c_str() ); - FGRunway* rwy = new FGRunway(NULL, rwy_no, pos, heading, length, - width, displ_thresh1, stopway1, surface_code, false); - runways.push_back(rwy); - - FGRunway* reciprocal = new FGRunway(NULL, FGRunway::reverseIdent(rwy_no), - pos, heading + 180.0, length, width, - displ_thresh2, stopway2, surface_code, true); - - runways.push_back(reciprocal); + PositionedID rwy = cache->insertRunway(FGPositioned::RUNWAY, rwy_no, pos, + currentAirportID, heading, length, + width, displ_thresh1, stopway1, + surface_code); - rwy->setReciprocalRunway(reciprocal); - reciprocal->setReciprocalRunway(rwy); + PositionedID reciprocal = cache->insertRunway(FGPositioned::RUNWAY, + FGRunway::reverseIdent(rwy_no), pos, + currentAirportID, heading + 180.0, length, + width, displ_thresh2, stopway2, + surface_code); + + cache->setRunwayReciprocal(rwy, reciprocal); } } @@ -352,17 +361,18 @@ private: double stopway1 = atof( token[12].c_str() ); double stopway2 = atof( token[21].c_str() ); - FGRunway* rwy = new FGRunway(NULL, rwy_no_1, pos, heading_1, length, - width, displ_thresh1, stopway1, surface_code, false); - runways.push_back(rwy); - - FGRunway* reciprocal = new FGRunway(NULL, rwy_no_2, - pos, heading_2, length, width, - displ_thresh2, stopway2, surface_code, true); - runways.push_back(reciprocal); + PositionedID rwy = cache->insertRunway(FGPositioned::RUNWAY, rwy_no_1, pos, + currentAirportID, heading_1, length, + width, displ_thresh1, stopway1, + surface_code); - rwy->setReciprocalRunway(reciprocal); - reciprocal->setReciprocalRunway(rwy); + PositionedID reciprocal = cache->insertRunway(FGPositioned::RUNWAY, + rwy_no_2, pos, + currentAirportID, heading_2, length, + width, displ_thresh2, stopway2, + surface_code); + + cache->setRunwayReciprocal(rwy, reciprocal); } void parseWaterRunwayLine850(const vector& token) @@ -393,17 +403,16 @@ private: const string& rwy_no_1(token[3]); const string& rwy_no_2(token[6]); - FGRunway* rwy = new FGRunway(NULL, rwy_no_1, pos, heading_1, length, - width, 0.0, 0.0, 13, false); - runways.push_back(rwy); - - FGRunway* reciprocal = new FGRunway(NULL, rwy_no_2, - pos, heading_2, length, width, - 0.0, 0.0, 13, true); - runways.push_back(reciprocal); + PositionedID rwy = cache->insertRunway(FGPositioned::RUNWAY, rwy_no_1, pos, + currentAirportID, heading_1, length, + width, 0.0, 0.0, 13); + + PositionedID reciprocal = cache->insertRunway(FGPositioned::RUNWAY, + rwy_no_2, pos, + currentAirportID, heading_2, length, + width, 0.0, 0.0, 13); - rwy->setReciprocalRunway(reciprocal); - reciprocal->setReciprocalRunway(rwy); + cache->setRunwayReciprocal(rwy, reciprocal); } void parseHelipadLine850(const vector& token) @@ -425,9 +434,9 @@ private: const string& rwy_no(token[1]); int surface_code = atoi( token[7].c_str() ); - FGRunway* rwy = new FGRunway(NULL, rwy_no, pos, heading, length, - width, 0.0, 0.0, surface_code, false); - runways.push_back(rwy); + cache->insertRunway(FGPositioned::RUNWAY, rwy_no, pos, + currentAirportID, heading, length, + width, 0.0, 0.0, surface_code); } void parsePavementLine850(const vector& token) @@ -449,7 +458,7 @@ private: FGPavement* pvt = 0; if ( !pavement_ident.empty() ) { - pvt = new FGPavement( pavement_ident, pos ); + pvt = new FGPavement( 0, pavement_ident, pos ); pavements.push_back( pvt ); pavement_ident = ""; } else { @@ -475,7 +484,7 @@ private: rwy_lat_accum / (double)rwy_count, last_apt_elev); // short int representing tens of kHz: - int freqKhz = atoi(token[1].c_str()); + int freqKhz = atoi(token[1].c_str()) * 10; int rangeNm = 50; FGPositioned::Type ty; // Make sure we only pass on stations with at least a name @@ -484,8 +493,13 @@ private: switch (lineId) { case 50: ty = FGPositioned::FREQ_AWOS; - if (token[2] == "ATIS") { + for( size_t i = 2; i < token.size(); ++i ) + { + if( token[i] == "ATIS" ) + { ty = FGPositioned::FREQ_ATIS; + break; + } } break; @@ -496,50 +510,52 @@ private: case 55: case 56: ty = FGPositioned::FREQ_APP_DEP; break; default: - throw sg_range_exception("unupported apt.dat comm station type"); + throw sg_range_exception("unsupported apt.dat comm station type"); } - commStations.push_back(new flightgear::CommStation(token[2], ty, pos, rangeNm, freqKhz)); + // Name can contain white spaces. All tokens after the second token are + // part of the name. + std::string name = token[2]; + for( size_t i = 3; i < token.size(); ++i ) + name += ' ' + token[i]; + + cache->insertCommStation(ty, name, pos, freqKhz, rangeNm, currentAirportID); } else SG_LOG( SG_GENERAL, SG_DEBUG, "Found unnamed comm. Skipping: " << lineId); } }; - - + // Load the airport data base from the specified aptdb file. The // metar file is used to mark the airports as having metar available // or not. -bool fgAirportDBLoad( const string &aptdb_file, const std::string &metar_file ) +bool airportDBLoad( const SGPath &aptdb_file ) { - - APTLoader ld; - ld.parseAPT(aptdb_file); - // - // Load the metar.dat file and update apt db with stations that - // have metar data. - // - - sg_gzifstream metar_in( metar_file ); - if ( !metar_in.is_open() ) { - SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << metar_file ); - } - - string ident; - while ( metar_in ) { - metar_in >> ident; - if ( ident == "#" || ident == "//" ) { - metar_in >> skipeol; - } else { - FGAirport* apt = FGAirport::findByIdent(ident); - if (apt) { - apt->setMetar(true); - } - } + APTLoader ld; + ld.parseAPT(aptdb_file); + return true; +} + +bool metarDataLoad(const SGPath& metar_file) +{ + sg_gzifstream metar_in( metar_file.str() ); + if ( !metar_in.is_open() ) { + SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << metar_file ); + return false; + } + + NavDataCache* cache = NavDataCache::instance(); + string ident; + while ( metar_in ) { + metar_in >> ident; + if ( ident == "#" || ident == "//" ) { + metar_in >> skipeol; + } else { + cache->setAirportMetar(ident, true); } - - SG_LOG(SG_GENERAL, SG_INFO, "[FINISHED LOADING]"); - - return true; + } + + return true; } +} // of namespace flightgear diff --git a/src/Airports/apt_loader.hxx b/src/Airports/apt_loader.hxx index 106268f..636c1c8 100644 --- a/src/Airports/apt_loader.hxx +++ b/src/Airports/apt_loader.hxx @@ -27,13 +27,19 @@ #include -#include +class SGPath; +namespace flightgear +{ + // Load the airport data base from the specified aptdb file. The // metar file is used to mark the airports as having metar available // or not. -bool fgAirportDBLoad( const std::string &aptdb_file, - const std::string &metar_file ); +bool airportDBLoad(const SGPath& path); + +bool metarDataLoad(const SGPath& path); + +} // of namespace flighgear #endif // _FG_APT_LOADER_HXX diff --git a/src/Airports/dynamicloader.cxx b/src/Airports/dynamicloader.cxx index 26af2b7..bf9335a 100644 --- a/src/Airports/dynamicloader.cxx +++ b/src/Airports/dynamicloader.cxx @@ -18,164 +18,228 @@ #endif #include +#include // for strcmp +#include #include "dynamicloader.hxx" +#include +#include +#include + +/***************************************************************************** + * Helper function for parsing position string + ****************************************************************************/ +static double processPosition(const string &pos) +{ + string prefix; + string subs; + string degree; + string decimal; + int sign = 1; + double value; + subs = pos; + prefix= subs.substr(0,1); + if (prefix == string("S") || (prefix == string("W"))) + sign = -1; + subs = subs.substr(1, subs.length()); + degree = subs.substr(0, subs.find(" ",0)); + decimal = subs.substr(subs.find(" ",0), subs.length()); + + value = sign * (atof(degree.c_str()) + atof(decimal.c_str())/60.0); + return value; +} + FGAirportDynamicsXMLLoader::FGAirportDynamicsXMLLoader(FGAirportDynamics* dyn): - XMLVisitor(), _dynamics(dyn) {} + XMLVisitor(), _dynamics(dyn) +{} void FGAirportDynamicsXMLLoader::startXML () { //cout << "FGAirportDynamicsLoader::Start XML" << endl; } -void FGAirportDynamicsXMLLoader::endXML () { - //cout << "End XML" << endl; +void FGAirportDynamicsXMLLoader::endXML () +{ + std::map::iterator it; + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + + for (it = _parkingPushbacks.begin(); it != _parkingPushbacks.end(); ++it) { + std::map::iterator j = _idMap.find(it->second); + if (j == _idMap.end()) { + SG_LOG(SG_NAVAID, SG_WARN, "bad groundnet, no node for index:" << it->first); + continue; + } + + cache->setParkingPushBackRoute(it->first, j->second); + } + + BOOST_FOREACH(PositionedID id, _unreferencedNodes) { + SG_LOG(SG_NAVAID, SG_WARN, "unreferenced groundnet node:" << id); + } + } -void FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttributes &atts) { - // const char *attval; - FGParking park; - FGTaxiNode taxiNode; - FGTaxiSegment taxiSegment; +void FGAirportDynamicsXMLLoader::startParking(const XMLAttributes &atts) +{ + string type; int index = 0; - string idxStr; - taxiSegment.setIndex(index); - //cout << "Start element " << name << endl; - string attname; - string value; - string gateName; - string gateNumber; - string attval; - string lat; - string lon; - int holdPointType; - int pushBackPoint; + string gateName, gateNumber; + string lat, lon; + double heading = 0.0; + double radius = 1.0; + string airlineCodes; + int pushBackRoute = 0; - if (name == string("Parking")) - { - pushBackPoint = 0; - for (int i = 0; i < atts.size(); i++) + for (int i = 0; i < atts.size(); i++) { - //cout << " " << atts.getName(i) << '=' << atts.getValue(i) << endl; - attname = atts.getName(i); - if (attname == string("index")) { - park.setIndex(std::atoi(atts.getValue(i))); - idxStr = atts.getValue(i); - } - else if (attname == string("type")) - park.setType(atts.getValue(i)); - else if (attname == string("name")) - gateName = atts.getValue(i); - else if (attname == string("number")) + string attname(atts.getName(i)); + if (attname == "index") { + index = std::atoi(atts.getValue(i)); + } else if (attname == "type") + type = atts.getValue(i); + else if (attname == "name") + gateName = atts.getValue(i); + else if (attname == "number") gateNumber = atts.getValue(i); - else if (attname == string("lat")) - park.setLatitude(atts.getValue(i)); - else if (attname == string("lon")) - park.setLongitude(atts.getValue(i)); - else if (attname == string("heading")) - park.setHeading(std::atof(atts.getValue(i))); - else if (attname == string("radius")) { - string radius = atts.getValue(i); - if (radius.find("M") != string::npos) - radius = radius.substr(0, radius.find("M",0)); - //cerr << "Radius " << radius <addParking(park); + else if (attname == "airlineCodes") + airlineCodes = atts.getValue(i); + else if (attname == "pushBackRoute") { + pushBackRoute = std::atoi(atts.getValue(i)); } - if (name == string("node")) - { - for (int i = 0; i < atts.size() ; i++) - { - attname = atts.getName(i); - if (attname == string("index")) - taxiNode.setIndex(std::atoi(atts.getValue(i))); - if (attname == string("lat")) - taxiNode.setLatitude(atts.getValue(i)); - if (attname == string("lon")) - taxiNode.setLongitude(atts.getValue(i)); - if (attname == string("isOnRunway")) - taxiNode.setOnRunway((bool) std::atoi(atts.getValue(i))); - if (attname == string("holdPointType")) { - attval = atts.getValue(i); - if (attval==string("none")) { - holdPointType=0; - } else if (attval==string("normal")) { - holdPointType=1; - } else if (attval==string("CAT II/III")) { - holdPointType=3; - } else if (attval==string("PushBack")) { - holdPointType=3; - } else { - holdPointType=0; - } - //cerr << "Setting Holding point to " << holdPointType << endl; - taxiNode.setHoldPointType(holdPointType); - } } - _dynamics->getGroundNetwork()->addNode(taxiNode); - } - if (name == string("arc")) - { - taxiSegment.setIndex(++index); - for (int i = 0; i < atts.size() ; i++) + + SGGeod pos(SGGeod::fromDeg(processPosition(lon), processPosition(lat))); + + PositionedID guid = flightgear::NavDataCache::instance()->insertParking(gateName + gateNumber, pos, + _dynamics->parent()->guid(), + heading, radius, type, airlineCodes); + if (pushBackRoute > 0) { + _parkingPushbacks[guid] = pushBackRoute; + } + + _idMap[index] = guid; +} + +void FGAirportDynamicsXMLLoader::startNode(const XMLAttributes &atts) +{ + int index = 0; + string lat, lon; + bool onRunway = false; + int holdPointType = 0; + + for (int i = 0; i < atts.size() ; i++) { - attname = atts.getName(i); - if (attname == string("begin")) - taxiSegment.setStartNodeRef(std::atoi(atts.getValue(i))); - if (attname == string("end")) - taxiSegment.setEndNodeRef(std::atoi(atts.getValue(i))); - if (attname == string("isPushBackRoute")) - taxiSegment.setPushBackType((bool) std::atoi(atts.getValue(i))); - } - _dynamics->getGroundNetwork()->addSegment(taxiSegment); + string attname(atts.getName(i)); + if (attname == "index") + index = std::atoi(atts.getValue(i)); + else if (attname == "lat") + lat = atts.getValue(i); + else if (attname == "lon") + lon = atts.getValue(i); + else if (attname == "isOnRunway") + onRunway = std::atoi(atts.getValue(i)) != 0; + else if (attname == "holdPointType") { + string attval = atts.getValue(i); + if (attval=="none") { + holdPointType=0; + } else if (attval=="normal") { + holdPointType=1; + } else if (attval=="CAT II/III") { + holdPointType=3; + } else if (attval=="PushBack") { + holdPointType=3; + } else { + holdPointType=0; + } } - // sort by radius, in asending order, so that smaller gates are first in the list + } + + if (_idMap.find(index) != _idMap.end()) { + SG_LOG(SG_NAVAID, SG_WARN, "duplicate ground-net index:" << index); + } + + SGGeod pos(SGGeod::fromDeg(processPosition(lon), processPosition(lat))); + PositionedID guid = flightgear::NavDataCache::instance()->insertTaxiNode(pos, + _dynamics->parent()->guid(), holdPointType, onRunway); + _idMap[index] = guid; + _unreferencedNodes.insert(guid); } -void FGAirportDynamicsXMLLoader::endElement (const char * name) { - //cout << "End element " << name << endl; - if (name == string("version")) { - _dynamics->getGroundNetwork()->addVersion(atoi(value.c_str())); - //std::cerr << "version" << value<< std::endl; - } - if (name == string("AWOS")) { - _dynamics->addAwosFreq(atoi(value.c_str())); - //cerr << "Adding AWOS" << value<< endl; - } - if (name == string("UNICOM")) { - _dynamics->addUnicomFreq(atoi(value.c_str())); - //cerr << "UNICOM" << value<< endl; - } -if (name == string("CLEARANCE")) { - _dynamics->addClearanceFreq(atoi(value.c_str())); - //cerr << "Adding CLEARANCE" << value<< endl; +void FGAirportDynamicsXMLLoader::startArc(const XMLAttributes &atts) +{ + int begin = 0, end = 0; + bool isPushBackRoute = false; + + for (int i = 0; i < atts.size() ; i++) + { + string attname = atts.getName(i); + if (attname == "begin") + begin = std::atoi(atts.getValue(i)); + else if (attname == "end") + end = std::atoi(atts.getValue(i)); + else if (attname == "isPushBackRoute") + isPushBackRoute = std::atoi(atts.getValue(i)) != 0; + } + + IntPair e(begin, end); + if (_arcSet.find(e) != _arcSet.end()) { + SG_LOG(SG_NAVAID, SG_WARN, _dynamics->parent()->ident() << " ground-net: skipping duplicate edge:" << begin << "->" << end); + return; } -if (name == string("GROUND")) { - _dynamics->addGroundFreq(atoi(value.c_str())); - //cerr << "Adding GROUND" << value<< endl; + + _arcSet.insert(e); + flightgear::NavDataCache::instance()->insertGroundnetEdge(_dynamics->parent()->guid(), + _idMap[begin], _idMap[end]); + + _unreferencedNodes.erase(_idMap[begin]); + _unreferencedNodes.erase(_idMap[end]); + + if (isPushBackRoute) { + flightgear::NavDataCache::instance()->markGroundnetAsPushback(_idMap[end]); } +} -if (name == string("TOWER")) { - _dynamics->addTowerFreq(atoi(value.c_str())); - //cerr << "Adding TOWER" << value<< endl; - } -if (name == string("APPROACH")) { - _dynamics->addApproachFreq(atoi(value.c_str())); - //cerr << "Adding approach" << value<< endl; +void FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttributes &atts) +{ + if (!strcmp("Parking", name)) { + startParking(atts); + } else if (!strcmp("node", name)) { + startNode(atts); + } else if (!strcmp("arc", name)) { + startArc(atts); } +} +void FGAirportDynamicsXMLLoader::endElement (const char * name) +{ + int valueAsInt = atoi(value.c_str()); + if (!strcmp("version", name)) { + _dynamics->getGroundNetwork()->addVersion(valueAsInt); + } else if (!strcmp("AWOS", name)) { + _dynamics->addAwosFreq(valueAsInt); + } else if (!strcmp("UNICOM", name)) { + _dynamics->addUnicomFreq(valueAsInt); + } else if (!strcmp("CLEARANCE", name)) { + _dynamics->addClearanceFreq(valueAsInt); + } else if (!strcmp("GROUND", name)) { + _dynamics->addGroundFreq(valueAsInt); + } else if (!strcmp("TOWER", name)) { + _dynamics->addTowerFreq(valueAsInt); + } else if (!strcmp("APPROACH", name)) { + _dynamics->addApproachFreq(valueAsInt); + } } void FGAirportDynamicsXMLLoader::data (const char * s, int len) { diff --git a/src/Airports/dynamicloader.hxx b/src/Airports/dynamicloader.hxx index 672cda5..b204e9b 100644 --- a/src/Airports/dynamicloader.hxx +++ b/src/Airports/dynamicloader.hxx @@ -19,6 +19,7 @@ #include #include "dynamics.hxx" +#include class FGAirportDynamicsXMLLoader : public XMLVisitor { public: @@ -35,8 +36,26 @@ protected: virtual void error (const char * message, int line, int column); private: + void startParking(const XMLAttributes &atts); + void startNode(const XMLAttributes &atts); + void startArc(const XMLAttributes &atts); + FGAirportDynamics* _dynamics; string value; + + // map from local (groundnet.xml) to global (nav-cache) IDs for nodes + std::map _idMap; + + // data integrity - watch for unreferenced nodes and duplicated edges + typedef std::pair IntPair; + std::set _arcSet; + + std::set _unreferencedNodes; + + // map from allocated parking position to its local push-back node + // used to defer binding the push-back node until we've processed + // all nodes + std::map _parkingPushbacks; }; #endif diff --git a/src/Airports/dynamics.cxx b/src/Airports/dynamics.cxx index 5a07796..d505d58 100644 --- a/src/Airports/dynamics.cxx +++ b/src/Airports/dynamics.cxx @@ -23,6 +23,10 @@ #endif #include +#include +#include + +#include #include @@ -32,26 +36,122 @@ #include #include #include -#include #include
#include
#include #include +#include -#include -#include +#include "simple.hxx" +#include "dynamics.hxx" using std::string; using std::vector; using std::sort; using std::random_shuffle; -#include "simple.hxx" -#include "dynamics.hxx" +class ParkingAssignment::ParkingAssignmentPrivate +{ +public: + ParkingAssignmentPrivate(FGParking* pk, FGAirport* apt) : + refCount(0), + parking(pk), + airport(apt) + { + assert(pk); + assert(apt); + retain(); // initial count of 1 + } + + ~ParkingAssignmentPrivate() + { + airport->getDynamics()->releaseParking(parking->guid()); + } + + void release() + { + if ((--refCount) == 0) { + delete this; + } + } + + void retain() + { + ++refCount; + } + + unsigned int refCount; + SGSharedPtr parking; + SGSharedPtr airport; +}; + +ParkingAssignment::ParkingAssignment() : + _sharedData(NULL) +{ +} + +ParkingAssignment::~ParkingAssignment() +{ + if (_sharedData) { + _sharedData->release(); + } +} + +ParkingAssignment::ParkingAssignment(FGParking* pk, FGAirport* apt) : + _sharedData(NULL) +{ + if (pk) { + _sharedData = new ParkingAssignmentPrivate(pk, apt); + } +} + +ParkingAssignment::ParkingAssignment(const ParkingAssignment& aOther) : + _sharedData(aOther._sharedData) +{ + if (_sharedData) { + _sharedData->retain(); + } +} + +void ParkingAssignment::operator=(const ParkingAssignment& aOther) +{ + if (_sharedData == aOther._sharedData) { + return; // self-assignment, special case + } + + if (_sharedData) { + _sharedData->release(); + } + + _sharedData = aOther._sharedData; + if (_sharedData) { + _sharedData->retain(); + } +} + +void ParkingAssignment::release() +{ + if (_sharedData) { + _sharedData->release(); + _sharedData = NULL; + } +} + +bool ParkingAssignment::isValid() const +{ + return (_sharedData != NULL); +} + +FGParking* ParkingAssignment::parking() const +{ + return _sharedData ? _sharedData->parking.ptr() : NULL; +} + +//////////////////////////////////////////////////////////////////////////////// FGAirportDynamics::FGAirportDynamics(FGAirport * ap): - _ap(ap), rwyPrefs(ap), SIDs(ap), - startupController (this), + _ap(ap), rwyPrefs(ap), + startupController (this), towerController (this), approachController (this), atisSequenceIndex(-1), @@ -70,218 +170,114 @@ FGAirportDynamics::~FGAirportDynamics() // Initialization required after XMLRead void FGAirportDynamics::init() { - // This may seem a bit weird to first randomly shuffle the parkings - // and then sort them again. However, parkings are sorted here by ascending - // radius. Since many parkings have similar radii, with each radius class they will - // still be allocated relatively systematically. Randomizing prior to sorting will - // prevent any initial orderings to be destroyed, leading (hopefully) to a more - // naturalistic gate assignment. - random_shuffle(parkings.begin(), parkings.end()); - sort(parkings.begin(), parkings.end()); - // add the gate positions to the ground network. - groundNetwork.setParent(_ap); - groundNetwork.addNodes(&parkings); - groundNetwork.init(); + groundNetwork.init(_ap); groundNetwork.setTowerController(&towerController); } -bool FGAirportDynamics::getAvailableParking(double *lat, double *lon, - double *heading, int *gateId, - double rad, - const string & flType, - const string & acType, - const string & airline) +FGParking* FGAirportDynamics::innerGetAvailableParking(double radius, const string & flType, + const string & airline, + bool skipEmptyAirlineCode) { - bool found = false; - bool available = false; - - - FGParkingVecIterator i; - if (parkings.begin() == parkings.end()) { - //cerr << "Could not find parking spot at " << _ap->getId() << endl; - *lat = _ap->getLatitude(); - *lon = _ap->getLongitude(); - * gateId = -1; - *heading = 0; - found = true; - } else { - // First try finding a parking with a designated airline code - for (i = parkings.begin(); !(i == parkings.end() || found); i++) { - available = true; - // Taken by another aircraft - if (!(i->isAvailable())) { - available = false; - continue; - } - // No airline codes, so skip - if (i->getCodes().empty()) { - available = false; - continue; - } else { // Airline code doesn't match - //cerr << "Code = " << airline << ": Codes " << i->getCodes(); - if (i->getCodes().find(airline, 0) == string::npos) { - available = false; - //cerr << "Unavailable" << endl; - continue; - } else { - //cerr << "Available" << endl; - } - } - // Type doesn't match - if (i->getType() != flType) { - available = false; - continue; - } - // too small - if (i->getRadius() < rad) { - available = false; - continue; - } - - if (available) { - *lat = i->getLatitude(); - *lon = i->getLongitude(); - *heading = i->getHeading(); - *gateId = i->getIndex(); - i->setAvailable(false); - found = true; - } - } - // then try again for those without codes. - for (i = parkings.begin(); !(i == parkings.end() || found); i++) { - available = true; - if (!(i->isAvailable())) { - available = false; - continue; - } - if (!(i->getCodes().empty())) { - if ((i->getCodes().find(airline, 0) == string::npos)) { - available = false; - continue; - } - } - if (i->getType() != flType) { - available = false; - continue; - } - - if (i->getRadius() < rad) { - available = false; - continue; - } - - if (available) { - *lat = i->getLatitude(); - *lon = i->getLongitude(); - *heading = i->getHeading(); - *gateId = i->getIndex(); - i->setAvailable(false); - found = true; - } - } - // And finally once more if that didn't work. Now ignore the airline codes, as a last resort - for (i = parkings.begin(); !(i == parkings.end() || found); i++) { - available = true; - if (!(i->isAvailable())) { - available = false; - continue; - } - if (i->getType() != flType) { - available = false; - continue; - } - - if (i->getRadius() < rad) { - available = false; - continue; - } - - if (available) { - *lat = i->getLatitude(); - *lon = i->getLongitude(); - *heading = i->getHeading(); - *gateId = i->getIndex(); - i->setAvailable(false); - found = true; - } - } + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + BOOST_FOREACH(PositionedID pk, cache->findAirportParking(_ap->guid(), flType, radius)) { + if (!isParkingAvailable(pk)) { + continue; } - if (!found) { - //cerr << "Traffic overflow at" << _ap->getId() - // << ". flType = " << flType - // << ". airline = " << airline - // << " Radius = " <getLatitude(); - *lon = _ap->getLongitude(); - *heading = 0; - *gateId = -1; - //exit(1); + + FGParking* parking = getParking(pk); + if (skipEmptyAirlineCode && parking->getCodes().empty()) { + continue; } - return found; + + if (!airline.empty() && !parking->getCodes().empty()) { + if (parking->getCodes().find(airline, 0) == string::npos) { + continue; + } + } + + setParkingAvailable(pk, false); + return parking; + } + + return NULL; } -void FGAirportDynamics::getParking(int id, double *lat, double *lon, - double *heading) +ParkingAssignment FGAirportDynamics::getAvailableParking(double radius, const string & flType, + const string & acType, + const string & airline) { - if (id < 0) { - *lat = _ap->getLatitude(); - *lon = _ap->getLongitude(); - *heading = 0; - } else { - FGParkingVecIterator i = parkings.begin(); - for (i = parkings.begin(); i != parkings.end(); i++) { - if (id == i->getIndex()) { - *lat = i->getLatitude(); - *lon = i->getLongitude(); - *heading = i->getHeading(); - } - } - } + SG_UNUSED(acType); // sadly not used at the moment + + // most exact seach - airline codes must be present and match + FGParking* result = innerGetAvailableParking(radius, flType, airline, true); + if (result) { + return ParkingAssignment(result, _ap); + } + + // more tolerant - gates with empty airline codes are permitted + result = innerGetAvailableParking(radius, flType, airline, false); + if (result) { + return ParkingAssignment(result, _ap); + } + + // fallback - ignore the airline code entirely + result = innerGetAvailableParking(radius, flType, string(), false); + return result ? ParkingAssignment(result, _ap) : ParkingAssignment(); } -FGParking *FGAirportDynamics::getParking(int id) +FGParking *FGAirportDynamics::getParking(PositionedID id) const { - FGParkingVecIterator i = parkings.begin(); - for (i = parkings.begin(); i != parkings.end(); i++) { - if (id == i->getIndex()) { - return &(*i); - } - } - return 0; + return static_cast(flightgear::NavDataCache::instance()->loadById(id)); } -string FGAirportDynamics::getParkingName(int id) +string FGAirportDynamics::getParkingName(PositionedID id) const { - FGParkingVecIterator i = parkings.begin(); - for (i = parkings.begin(); i != parkings.end(); i++) { - if (id == i->getIndex()) { - return i->getName(); - } - } + FGParking* p = getParking(id); + if (p) { + return p->getName(); + } + + return string(); +} - return string("overflow"); +ParkingAssignment FGAirportDynamics::getParkingByName(const std::string& name) const +{ + PositionedID guid = flightgear::NavDataCache::instance()->airportItemWithIdent(parent()->guid(), FGPositioned::PARKING, name); + if (guid == 0) { + return ParkingAssignment(); + } + + return ParkingAssignment(getParking(guid), _ap); } -void FGAirportDynamics::releaseParking(int id) +void FGAirportDynamics::setParkingAvailable(PositionedID guid, bool available) { - if (id >= 0) { + if (available) { + releaseParking(guid); + } else { + occupiedParkings.insert(guid); + } +} - FGParkingVecIterator i = parkings.begin(); - for (i = parkings.begin(); i != parkings.end(); i++) { - if (id == i->getIndex()) { - i->setAvailable(true); - } - } - } +bool FGAirportDynamics::isParkingAvailable(PositionedID parking) const +{ + return (occupiedParkings.find(parking) == occupiedParkings.end()); +} + +void FGAirportDynamics::releaseParking(PositionedID id) +{ + ParkingSet::iterator it = occupiedParkings.find(id); + if (it == occupiedParkings.end()) { + return; + } + + occupiedParkings.erase(it); } void FGAirportDynamics::setRwyUse(const FGRunwayPreference & ref) { rwyPrefs = ref; - //cerr << "Exiting due to not implemented yet" << endl; - //exit(1); } bool FGAirportDynamics::innerGetActiveRunway(const string & trafficType, @@ -462,27 +458,12 @@ string FGAirportDynamics::chooseRunwayFallback() return rwy->ident(); } -void FGAirportDynamics::addParking(FGParking & park) -{ - parkings.push_back(park); -} - -double FGAirportDynamics::getLatitude() const -{ - return _ap->getLatitude(); -} - -double FGAirportDynamics::getLongitude() const -{ - return _ap->getLongitude(); -} - double FGAirportDynamics::getElevation() const { return _ap->getElevation(); } -const string & FGAirportDynamics::getId() const +const string FGAirportDynamics::getId() const { return _ap->getId(); } @@ -544,13 +525,6 @@ int FGAirportDynamics::getTowerFrequency(unsigned nr) return towerFreq; } - -FGAIFlightPlan *FGAirportDynamics::getSID(string activeRunway, - double heading) -{ - return SIDs.getBest(activeRunway, heading); -} - const std::string FGAirportDynamics::getAtisSequence() { if (atisSequenceIndex == -1) { diff --git a/src/Airports/dynamics.hxx b/src/Airports/dynamics.hxx index 7e828e4..007c8a7 100644 --- a/src/Airports/dynamics.hxx +++ b/src/Airports/dynamics.hxx @@ -22,24 +22,50 @@ #ifndef _AIRPORT_DYNAMICS_HXX_ #define _AIRPORT_DYNAMICS_HXX_ +#include + #include #include "parking.hxx" #include "groundnetwork.hxx" #include "runwayprefs.hxx" -#include "sidstar.hxx" // forward decls class FGAirport; class FGEnvironment; +class ParkingAssignment +{ +public: + ParkingAssignment(); + ~ParkingAssignment(); + +// create a parking assignment (and mark it as unavailable) + ParkingAssignment(FGParking* pk, FGAirport* apt); + + ParkingAssignment(const ParkingAssignment& aOther); + void operator=(const ParkingAssignment& aOther); + + bool isValid() const; + FGParking* parking() const; + + void release(); +private: + void clear(); + + class ParkingAssignmentPrivate; + ParkingAssignmentPrivate* _sharedData; +}; + class FGAirportDynamics { private: FGAirport* _ap; - FGParkingVec parkings; + typedef std::set ParkingSet; + // if a parking item is in this set, it is occupied + ParkingSet occupiedParkings; + FGRunwayPreference rwyPrefs; - FGSidStar SIDs; FGStartupController startupController; FGGroundNetwork groundNetwork; FGTowerController towerController; @@ -65,8 +91,9 @@ private: bool innerGetActiveRunway(const std::string &trafficType, int action, std::string &runway, double heading); std::string chooseRwyByHeading(stringVec rwys, double heading); - double elevation; - + FGParking* innerGetAvailableParking(double radius, const std::string & flType, + const std::string & airline, + bool skipEmptyAirlineCode); public: FGAirportDynamics(FGAirport* ap); ~FGAirportDynamics(); @@ -91,36 +118,36 @@ public: }; void init(); - double getLongitude() const; - // Returns degrees - double getLatitude() const; - // Returns ft + double getElevation() const; - const string& getId() const; - + const std::string getId() const; + + FGAirport* parent() const + { return _ap; } + void getActiveRunway(const string& trafficType, int action, string& runway, double heading); - - void addParking(FGParking& park); - bool getAvailableParking(double *lat, double *lon, - double *heading, int *gate, double rad, const string& fltype, - const string& acType, const string& airline); - void getParking (int id, double *lat, double* lon, double *heading); - FGParking *getParking(int i); - void releaseParking(int id); - string getParkingName(int i); - int getNrOfParkings() { - return parkings.size(); - }; - //FGAirport *getAddress() { return this; }; - //const string &getName() const { return _name;}; - // Returns degrees - - // Departure / Arrival procedures - FGSidStar * getSIDs() { - return &SIDs; - }; - FGAIFlightPlan * getSID(string activeRunway, double heading); - + + /** + * retrieve an available parking by GateID, or -1 if no suitable + * parking location could be found. + */ + ParkingAssignment getAvailableParking(double radius, const std::string& fltype, + const std::string& acType, const std::string& airline); + + void setParkingAvailable(PositionedID guid, bool available); + + bool isParkingAvailable(PositionedID parking) const; + + FGParking *getParking(PositionedID i) const; + void releaseParking(PositionedID id); + std::string getParkingName(PositionedID i) const; + + /** + * Find a parking gate index by name. Note names are often not unique + * in our data, so will return the first match. If the parking is found, + * it will be marked as in-use (unavailable) + */ + ParkingAssignment getParkingByName(const std::string& name) const; // ATC related functions. FGStartupController *getStartupController() { diff --git a/src/Airports/gnnode.cxx b/src/Airports/gnnode.cxx index 0dc0e8f..5c61f89 100644 --- a/src/Airports/gnnode.cxx +++ b/src/Airports/gnnode.cxx @@ -1,98 +1,59 @@ #include "gnnode.hxx" -#include "groundnetwork.hxx" -#include -#include +#include + +#include "groundnetwork.hxx" -#include +#include #include
#include -using std::sort; - -/***************************************************************************** - * Helper function for parsing position string - ****************************************************************************/ -double processPosition(const string &pos) -{ - string prefix; - string subs; - string degree; - string decimal; - int sign = 1; - double value; - subs = pos; - prefix= subs.substr(0,1); - if (prefix == string("S") || (prefix == string("W"))) - sign = -1; - subs = subs.substr(1, subs.length()); - degree = subs.substr(0, subs.find(" ",0)); - decimal = subs.substr(subs.find(" ",0), subs.length()); - - - //cerr << sign << " "<< degree << " " << decimal << endl; - value = sign * (atof(degree.c_str()) + atof(decimal.c_str())/60.0); - //cerr << value <hasSmallerHeadingDiff(*b); -//} - -bool sortByLength(FGTaxiSegment *a, FGTaxiSegment *b) { - return a->getLength() > b->getLength(); -} +using namespace flightgear; /************************************************************************** * FGTaxiNode *************************************************************************/ -void FGTaxiNode::setElevation(double val) -{ - geod.setElevationM(val); -} - -void FGTaxiNode::setLatitude (double val) -{ - geod.setLatitudeDeg(val); -} -void FGTaxiNode::setLongitude(double val) +FGTaxiNode::FGTaxiNode(PositionedID aGuid, const SGGeod& pos, bool aOnRunway, int aHoldType) : + FGPositioned(aGuid, FGPositioned::PARKING, "", pos), + isOnRunway(aOnRunway), + holdType(aHoldType) { - geod.setLongitudeDeg(val); + } -void FGTaxiNode::setLatitude (const string& val) +FGTaxiNode::~FGTaxiNode() { - geod.setLatitudeDeg(processPosition(val)); } -void FGTaxiNode::setLongitude(const string& val) -{ - geod.setLongitudeDeg(processPosition(val)); -} - -double FGTaxiNode::getElevationFt(double refelev) +void FGTaxiNode::setElevation(double val) { - double elevF = geod.getElevationFt(); - double elevationEnd = 0; - if ((elevF == 0) || (elevF == refelev)) { - SGGeod center2 = geod; - FGScenery * local_scenery = globals->get_scenery(); - center2.setElevationM(SG_MAX_ELEVATION_M); - if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { - geod.setElevationM(elevationEnd); - } + // ignored for the moment +} + +double FGTaxiNode::getElevationFt() +{ + if (mPosition.getElevationFt() == 0.0) { + SGGeod center2 = mPosition; + FGScenery* local_scenery = globals->get_scenery(); + center2.setElevationM(SG_MAX_ELEVATION_M); + double elevationEnd = -100; + if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { + + SGGeod newPos = mPosition; + newPos.setElevationM(elevationEnd); + // this will call modifyPosition to update mPosition + NavDataCache* cache = NavDataCache::instance(); + NavDataCache::Transaction txn(cache); + cache->updatePosition(guid(), newPos); + txn.commit(); } - //cerr << "Returning elevation : " << geod.getElevationM() << ". Ref elev (feet) = " << refelev << endl; - return geod.getElevationFt(); + } + + return mPosition.getElevationFt(); } -double FGTaxiNode::getElevationM(double refelev) +double FGTaxiNode::getElevationM() { - //double refelevFt = refelev * SG_METER_TO_FEET; - //double retval = getElevationFt(refelevFt); - //cerr << "Returning elevation : " << geod.getElevationM() << ". Ref elev (meters) = " << refelev << endl; - return geod.getElevationM(); + return getElevationFt() * SG_FEET_TO_METER; } diff --git a/src/Airports/gnnode.hxx b/src/Airports/gnnode.hxx index aa4e7ed..2ade667 100644 --- a/src/Airports/gnnode.hxx +++ b/src/Airports/gnnode.hxx @@ -16,109 +16,33 @@ #ifndef _GN_NODE_HXX_ #define _GN_NODE_HXX_ -#include -#include - #include -#include - -class FGTaxiSegment; -typedef std::vector FGTaxiSegmentVector; -typedef FGTaxiSegmentVector::iterator FGTaxiSegmentVectorIterator; +#include -bool sortByHeadingDiff(FGTaxiSegment *a, FGTaxiSegment *b); -bool sortByLength (FGTaxiSegment *a, FGTaxiSegment *b); +#include -class FGTaxiNode +class FGTaxiNode : public FGPositioned { -private: - SGGeod geod; - int index; - +protected: bool isOnRunway; int holdType; - FGTaxiSegmentVector next; // a vector of pointers to all the segments leaving from this node - - // used in way finding - double pathScore; - FGTaxiNode* previousNode; - FGTaxiSegment* previousSeg; - - -public: - FGTaxiNode() : - index(0), - isOnRunway(false), - holdType(0), - pathScore(0), - previousNode(0), - previousSeg(0) -{ -}; - - FGTaxiNode(const FGTaxiNode &other) : - geod(other.geod), - index(other.index), - isOnRunway(other.isOnRunway), - holdType(other.holdType), - next(other.next), - pathScore(other.pathScore), - previousNode(other.previousNode), - previousSeg(other.previousSeg) -{ -}; -FGTaxiNode &operator =(const FGTaxiNode &other) -{ - geod = other.geod; - index = other.index; - isOnRunway = other.isOnRunway; - holdType = other.holdType; - next = other.next; - pathScore = other.pathScore; - previousNode = other.previousNode; - previousSeg = other.previousSeg; - return *this; -}; - - void setIndex(int idx) { index = idx; }; - void setLatitude (double val); - void setLongitude(double val); +public: + FGTaxiNode(PositionedID aGuid, const SGGeod& pos, bool aOnRunway, int aHoldType); + virtual ~FGTaxiNode(); + void setElevation(double val); - void setLatitude (const std::string& val); - void setLongitude(const std::string& val); - void addSegment(FGTaxiSegment *segment) { next.push_back(segment); }; - void setHoldPointType(int val) { holdType = val; }; - void setOnRunway(bool val) { isOnRunway = val; }; - - void setPathScore (double val) { pathScore = val; }; - void setPreviousNode(FGTaxiNode *val) { previousNode = val; }; - void setPreviousSeg (FGTaxiSegment *val) { previousSeg = val; }; - - FGTaxiNode *getPreviousNode() { return previousNode; }; - FGTaxiSegment *getPreviousSegment() { return previousSeg; }; - - double getPathScore() { return pathScore; }; - double getLatitude() { return geod.getLatitudeDeg();}; - double getLongitude(){ return geod.getLongitudeDeg();}; - double getElevationM (double refelev=0); - double getElevationFt(double refelev=0); - - const SGGeod& getGeod() const { return geod; } - - int getIndex() { return index; }; - int getHoldPointType() { return holdType; }; - bool getIsOnRunway() { return isOnRunway; }; - - FGTaxiNode *getAddress() { return this;}; - FGTaxiSegmentVectorIterator getBeginRoute() { return next.begin(); }; - FGTaxiSegmentVectorIterator getEndRoute() { return next.end(); }; - bool operator<(const FGTaxiNode &other) const { return index < other.index; }; - + double getElevationM (); + double getElevationFt(); + + PositionedID getIndex() const { return guid(); }; + int getHoldPointType() const { return holdType; }; + bool getIsOnRunway() const { return isOnRunway; }; }; -typedef std::vector FGTaxiNodeVector; +typedef SGSharedPtr FGTaxiNode_ptr; +typedef std::vector FGTaxiNodeVector; typedef FGTaxiNodeVector::iterator FGTaxiNodeVectorIterator; #endif diff --git a/src/Airports/groundnetwork.cxx b/src/Airports/groundnetwork.cxx index 8e9b16f..62d229f 100644 --- a/src/Airports/groundnetwork.cxx +++ b/src/Airports/groundnetwork.cxx @@ -27,7 +27,8 @@ #include #include #include - +#include +#include #include #include @@ -35,17 +36,21 @@ #include #include -#include #include #include #include +#include +#include +#include #include #include +#include #include #include #include +#include #include @@ -53,68 +58,50 @@ #include "groundnetwork.hxx" +using std::string; +using flightgear::NavDataCache; /*************************************************************************** * FGTaxiSegment **************************************************************************/ -void FGTaxiSegment::setStart(FGTaxiNodeVector * nodes) +FGTaxiSegment::FGTaxiSegment(PositionedID aStart, PositionedID aEnd) : + startNode(aStart), + endNode(aEnd), + isActive(0), + index(0), + oppositeDirection(0) { - FGTaxiNodeVectorIterator i = nodes->begin(); - while (i != nodes->end()) { - //cerr << "Scanning start node index" << (*i)->getIndex() << endl; - if ((*i)->getIndex() == startNode) { - start = (*i)->getAddress(); - (*i)->addSegment(this); - return; - } - i++; - } - SG_LOG(SG_GENERAL, SG_ALERT, - "Could not find start node " << startNode << endl); -} +}; -void FGTaxiSegment::setEnd(FGTaxiNodeVector * nodes) +SGGeod FGTaxiSegment::getCenter() const { - FGTaxiNodeVectorIterator i = nodes->begin(); - while (i != nodes->end()) { - //cerr << "Scanning end node index" << (*i)->getIndex() << endl; - if ((*i)->getIndex() == endNode) { - end = (*i)->getAddress(); - return; - } - i++; - } - SG_LOG(SG_GENERAL, SG_ALERT, - "Could not find end node " << endNode << endl); + FGTaxiNode* start(getStart()), *end(getEnd()); + double heading, length, az2; + SGGeodesy::inverse(start->geod(), end->geod(), heading, az2, length); + return SGGeodesy::direct(start->geod(), heading, length * 0.5); } +FGTaxiNode* FGTaxiSegment::getEnd() const +{ + return static_cast(NavDataCache::instance()->loadById(endNode)); +} - -// There is probably a computationally cheaper way of -// doing this. -void FGTaxiSegment::setDimensions(double elevation) +FGTaxiNode* FGTaxiSegment::getStart() const { - length = SGGeodesy::distanceM(start->getGeod(), end->getGeod()); - //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod()); - - double az2; //, distanceM; - SGGeodesy::inverse(start->getGeod(), end->getGeod(), heading, az2, length); - double coveredDistance = length * 0.5; - SGGeodesy::direct(start->getGeod(), heading, coveredDistance, center, az2); - //start->setElevation(elevation); - //end->setElevation(elevation); - //cerr << "Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl; + return static_cast(NavDataCache::instance()->loadById(startNode)); } +double FGTaxiSegment::getLength() const +{ + return dist(getStart()->cart(), getEnd()->cart()); +} -//void FGTaxiSegment::setCourseDiff(double crse) -//{ -// headingDiff = fabs(course - crse); +double FGTaxiSegment::getHeading() const +{ + return SGGeodesy::courseDeg(getStart()->geod(), getEnd()->geod()); +} -// if (headingDiff > 180) -// headingDiff = fabs(headingDiff - 360); -//} void FGTaxiSegment::block(int id, time_t blockTime, time_t now) { @@ -158,103 +145,30 @@ void FGTaxiSegment::unblock(time_t now) /*************************************************************************** * FGTaxiRoute **************************************************************************/ -bool FGTaxiRoute::next(int *nde) +bool FGTaxiRoute::next(PositionedID *nde) { - //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++) - // cerr << "FGTaxiRoute contains : " << *(i) << endl; - //cerr << "Offset from end: " << nodes.end() - currNode << endl; - //if (currNode != nodes.end()) - // cerr << "true" << endl; - //else - // cerr << "false" << endl; - //if (nodes.size() != (routes.size()) +1) - // cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl; - if (currNode == nodes.end()) return false; + *nde = *(currNode); - if (currNode != nodes.begin()) // make sure route corresponds to the end node - currRoute++; - currNode++; - return true; -}; -bool FGTaxiRoute::next(int *nde, int *rte) -{ - //for (intVecIterator i = nodes.begin(); i != nodes.end(); i++) - // cerr << "FGTaxiRoute contains : " << *(i) << endl; - //cerr << "Offset from end: " << nodes.end() - currNode << endl; - //if (currNode != nodes.end()) - // cerr << "true" << endl; - //else - // cerr << "false" << endl; - if (nodes.size() != (routes.size()) + 1) { - SG_LOG(SG_GENERAL, SG_ALERT, - "ALERT: Misconfigured TaxiRoute : " << nodes. - size() << " " << routes.size()); - exit(1); - } - if (currNode == nodes.end()) - return false; - *nde = *(currNode); - //*rte = *(currRoute); - if (currNode != nodes.begin()) // Make sure route corresponds to the end node - { - *rte = *(currRoute); - currRoute++; - } else { - // If currNode points to the first node, this means the aircraft is not on the taxi node - // yet. Make sure to return a unique identifyer in this situation though, because otherwise - // the speed adjust AI code may be unable to resolve whether two aircraft are on the same - // taxi route or not. the negative of the preceding route seems a logical choice, as it is - // unique for any starting location. - // Note that this is probably just a temporary fix until I get Parking / tower control working. - *rte = -1 * *(currRoute); - } currNode++; return true; }; - -void FGTaxiRoute::rewind(int route) -{ - int currPoint; - int currRoute; - first(); - do { - if (!(next(&currPoint, &currRoute))) { - SG_LOG(SG_GENERAL, SG_ALERT, - "Error in rewinding TaxiRoute: current" << currRoute << - " goal " << route); - } - } while (currRoute != route); -} - - - - /*************************************************************************** * FGGroundNetwork() **************************************************************************/ -bool compare_nodes(FGTaxiNode * a, FGTaxiNode * b) -{ - return (*a) < (*b); -} - -bool compare_segments(FGTaxiSegment * a, FGTaxiSegment * b) -{ - return (*a) < (*b); -} bool compare_trafficrecords(FGTrafficRecord a, FGTrafficRecord b) { return (a.getIntentions().size() < b.getIntentions().size()); } -FGGroundNetwork::FGGroundNetwork() +FGGroundNetwork::FGGroundNetwork() : + parent(NULL) { hasNetwork = false; - foundRoute = false; totalDistance = 0; maxDistance = 0; //maxDepth = 1000; @@ -268,55 +182,27 @@ FGGroundNetwork::FGGroundNetwork() FGGroundNetwork::~FGGroundNetwork() { - //cerr << "Running Groundnetwork Destructor " << endl; - bool saveData = false; - ofstream cachefile; - if (fgGetBool("/sim/ai/groundnet-cache")) { - SGPath cacheData(fgGetString("/sim/fg-home")); - cacheData.append("ai"); - string airport = parent->getId(); - - if ((airport) != "") { - char buffer[128]; - ::snprintf(buffer, 128, "%c/%c/%c/", - airport[0], airport[1], airport[2]); - cacheData.append(buffer); - if (!cacheData.exists()) { - cacheData.create_dir(0777); - } - cacheData.append(airport + "-groundnet-cache.txt"); - cachefile.open(cacheData.str().c_str()); - saveData = true; - } - } - cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl; - for (FGTaxiNodeVectorIterator node = nodes.begin(); - node != nodes.end(); node++) { - if (saveData) { - cachefile << (*node)->getIndex () << " " - << (*node)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " " - << endl; - } - delete(*node); - } - nodes.clear(); - pushBackNodes.clear(); - for (FGTaxiSegmentVectorIterator seg = segments.begin(); - seg != segments.end(); seg++) { - delete(*seg); - } - segments.clear(); - if (saveData) { - cachefile.close(); - } +// JMT 2012-09-8 - disabling the groundnet-caching as part of enabling the +// navcache. The problem isn't the NavCache - it's that for the past few years, +// we have not being running destructors on FGPositioned classes, and hence, +// not running this code. +// When I fix FGPositioned lifetimes (unloading-at-runtime support), this +// will need to be re-visited so it can run safely during shutdown. +#if 0 + saveElevationCache(); +#endif + BOOST_FOREACH(FGTaxiSegment* seg, segments) { + delete seg; + } } -void FGGroundNetwork::saveElevationCache() { - //cerr << "Running Groundnetwork Destructor " << endl; +void FGGroundNetwork::saveElevationCache() +{ +#if 0 bool saveData = false; ofstream cachefile; if (fgGetBool("/sim/ai/groundnet-cache")) { - SGPath cacheData(fgGetString("/sim/fg-home")); + SGPath cacheData(globals->get_fg_home()); cacheData.append("ai"); string airport = parent->getId(); @@ -334,251 +220,196 @@ void FGGroundNetwork::saveElevationCache() { } } cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl; - for (FGTaxiNodeVectorIterator node = nodes.begin(); + for (IndexTaxiNodeMap::iterator node = nodes.begin(); node != nodes.end(); node++) { if (saveData) { - cachefile << (*node)->getIndex () << " " - << (*node)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " " + cachefile << node->second->getIndex () << " " + << node->second->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " " << endl; } } if (saveData) { cachefile.close(); } +#endif } -void FGGroundNetwork::addSegment(const FGTaxiSegment & seg) -{ - segments.push_back(new FGTaxiSegment(seg)); -} - -void FGGroundNetwork::addNode(const FGTaxiNode & node) -{ - nodes.push_back(new FGTaxiNode(node)); -} - -void FGGroundNetwork::addNodes(FGParkingVec * parkings) -{ - FGTaxiNode n; - FGParkingVecIterator i = parkings->begin(); - while (i != parkings->end()) { - n.setIndex(i->getIndex()); - n.setLatitude(i->getLatitude()); - n.setLongitude(i->getLongitude()); - n.setElevation(parent->getElevation()*SG_FEET_TO_METER); - nodes.push_back(new FGTaxiNode(n)); - - i++; - } -} - - - -void FGGroundNetwork::init() +void FGGroundNetwork::init(FGAirport* pr) { if (networkInitialized) { FGATCController::init(); //cerr << "FGground network already initialized" << endl; return; } + + parent = pr; + assert(parent); hasNetwork = true; nextSave = 0; int index = 1; - sort(nodes.begin(), nodes.end(), compare_nodes); - //sort(segments.begin(), segments.end(), compare_segments()); - FGTaxiSegmentVectorIterator i = segments.begin(); - while (i != segments.end()) { - (*i)->setStart(&nodes); - (*i)->setEnd(&nodes); - (*i)->setDimensions(parent->getElevation() * SG_FEET_TO_METER); - (*i)->setIndex(index); - if ((*i)->isPushBack()) { - pushBackNodes.push_back((*i)->getEnd()); - } - //SG_LOG(SG_GENERAL, SG_BULK, "initializing segment " << (*i)->getIndex() << endl); - //SG_LOG(SG_GENERAL, SG_BULK, "Track distance = " << (*i)->getLength() << endl); - //SG_LOG(SG_GENERAL, SG_BULK, "Track runs from " << (*i)->getStart()->getIndex() << " to " - // << (*i)->getEnd()->getIndex() << endl); - i++; - index++; + + loadSegments(); + + // establish pairing of segments + BOOST_FOREACH(FGTaxiSegment* segment, segments) { + segment->setIndex(index++); + + if (segment->oppositeDirection) { + continue; // already establish + } + + FGTaxiSegment* opp = findSegment(segment->endNode, segment->startNode); + if (opp) { + assert(opp->oppositeDirection == NULL); + segment->oppositeDirection = opp; + opp->oppositeDirection = segment; + } } - i = segments.begin(); - while (i != segments.end()) { - FGTaxiSegmentVectorIterator j = (*i)->getEnd()->getBeginRoute(); - while (j != (*i)->getEnd()->getEndRoute()) { - if ((*j)->getEnd()->getIndex() == (*i)->getStart()->getIndex()) { -// int start1 = (*i)->getStart()->getIndex(); -// int end1 = (*i)->getEnd() ->getIndex(); -// int start2 = (*j)->getStart()->getIndex(); -// int end2 = (*j)->getEnd()->getIndex(); -// int oppIndex = (*j)->getIndex(); - //cerr << "Opposite of " << (*i)->getIndex() << " (" << start1 << "," << end1 << ") " - // << "happens to be " << oppIndex << " (" << start2 << "," << end2 << ") " << endl; - (*i)->setOpposite(*j); - break; - } - j++; - } - i++; - } - //FGTaxiNodeVectorIterator j = nodes.begin(); - //while (j != nodes.end()) { - // if ((*j)->getHoldPointType() == 3) { - // pushBackNodes.push_back((*j)); - // } - // j++; - //} - //cerr << "Done initializing ground network" << endl; - //exit(1); if (fgGetBool("/sim/ai/groundnet-cache")) { - SGPath cacheData(fgGetString("/sim/fg-home")); - cacheData.append("ai"); - string airport = parent->getId(); - - if ((airport) != "") { - char buffer[128]; - ::snprintf(buffer, 128, "%c/%c/%c/", - airport[0], airport[1], airport[2]); - cacheData.append(buffer); - if (!cacheData.exists()) { - cacheData.create_dir(0777); - } - int index; - double elev; - cacheData.append(airport + "-groundnet-cache.txt"); - if (cacheData.exists()) { - ifstream data(cacheData.c_str()); - string revisionStr; - data >> revisionStr; - if (revisionStr != "[GroundNetcachedata:ref:2011:09:04]") { - SG_LOG(SG_GENERAL, SG_ALERT,"GroundNetwork Warning: discarding outdated cachefile " << - cacheData.c_str() << " for Airport " << airport); - } else { - for (FGTaxiNodeVectorIterator i = nodes.begin(); - i != nodes.end(); - i++) { - (*i)->setElevation(parent->getElevation() * SG_FEET_TO_METER); - data >> index >> elev; - if (data.eof()) - break; - if (index != (*i)->getIndex()) { - SG_LOG(SG_GENERAL, SG_ALERT, "Index read from ground network cache at airport " << airport << " does not match index in the network itself"); - } else { - (*i)->setElevation(elev); - } - } - } - } - } + parseCache(); } - //cerr << "Finished initializing " << parent->getId() << " groundnetwork " << endl; + networkInitialized = true; } -int FGGroundNetwork::findNearestNode(const SGGeod & aGeod) +void FGGroundNetwork::loadSegments() { - double minDist = HUGE_VAL; - int index = -1; - - for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end(); - itr++) { - double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod()); - if (d < minDist) { - minDist = d; - index = (*itr)->getIndex(); - //cerr << "Minimum distance of " << minDist << " for index " << index << endl; - } - } - - return index; + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); +// iterate over all ground-net nodes in this airport + BOOST_FOREACH(PositionedID node, cache->groundNetNodes(parent->guid(), false)) { + // find all segments leaving the node + BOOST_FOREACH(PositionedID end, cache->groundNetEdgesFrom(node, false)) { + segments.push_back(new FGTaxiSegment(node, end)); + } + } } -int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod) +void FGGroundNetwork::parseCache() { - double minDist = HUGE_VAL; - int index = -1; - - for (FGTaxiNodeVectorIterator itr = nodes.begin(); itr != nodes.end(); - itr++) { - if (!((*itr)->getIsOnRunway())) { - continue; - } - double d = SGGeodesy::distanceM(aGeod, (*itr)->getGeod()); - if (d < minDist) { - minDist = d; - index = (*itr)->getIndex(); - //cerr << "Minimum distance of " << minDist << " for index " << index << endl; + SGPath cacheData(globals->get_fg_home()); + cacheData.append("ai"); + string airport = parent->getId(); + + if (airport.empty()) { + return; + } +#if 0 + char buffer[128]; + ::snprintf(buffer, 128, "%c/%c/%c/", + airport[0], airport[1], airport[2]); + cacheData.append(buffer); + if (!cacheData.exists()) { + cacheData.create_dir(0777); + } + int index; + double elev; + cacheData.append(airport + "-groundnet-cache.txt"); + if (cacheData.exists()) { + ifstream data(cacheData.c_str()); + string revisionStr; + data >> revisionStr; + if (revisionStr != "[GroundNetcachedata:ref:2011:09:04]") { + SG_LOG(SG_GENERAL, SG_ALERT,"GroundNetwork Warning: discarding outdated cachefile " << + cacheData.c_str() << " for Airport " << airport); + } else { + for (IndexTaxiNodeMap::iterator i = nodes.begin(); + i != nodes.end(); + i++) { + i->second->setElevation(parent->elevation() * SG_FEET_TO_METER); + data >> index >> elev; + if (data.eof()) + break; + if (index != i->second->getIndex()) { + SG_LOG(SG_GENERAL, SG_ALERT, "Index read from ground network cache at airport " << airport << " does not match index in the network itself"); + } else { + i->second->setElevation(elev); } + } } - - return index; + } +#endif } +int FGGroundNetwork::findNearestNode(const SGGeod & aGeod) const +{ + const bool onRunway = false; + return NavDataCache::instance()->findGroundNetNode(parent->guid(), aGeod, onRunway); +} -int FGGroundNetwork::findNearestNode(double lat, double lon) +int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod, FGRunway* aRunway) const { - return findNearestNode(SGGeod::fromDeg(lon, lat)); + const bool onRunway = true; + return NavDataCache::instance()->findGroundNetNode(parent->guid(), aGeod, onRunway, aRunway); } -FGTaxiNode *FGGroundNetwork::findNode(unsigned idx) -{ /* - for (FGTaxiNodeVectorIterator - itr = nodes.begin(); - itr != nodes.end(); itr++) - { - if (itr->getIndex() == idx) - return itr->getAddress(); - } */ - - if ((idx >= 0) && (idx < nodes.size())) - return nodes[idx]->getAddress(); - else - return 0; +FGTaxiNode* FGGroundNetwork::findNode(PositionedID idx) const +{ + + return static_cast(NavDataCache::instance()->loadById(idx)); } -FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) -{ /* - for (FGTaxiSegmentVectorIterator - itr = segments.begin(); - itr != segments.end(); itr++) - { - if (itr->getIndex() == idx) - return itr->getAddress(); - } - */ +FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) const +{ if ((idx > 0) && (idx <= segments.size())) - return segments[idx - 1]->getAddress(); + return segments[idx - 1]; else { //cerr << "Alert: trying to find invalid segment " << idx << endl; return 0; } } +FGTaxiSegment* FGGroundNetwork::findSegment(PositionedID from, PositionedID to) const +{ + if (from == 0) { + return NULL; + } + + // completely boring linear search of segments. Can be improved if/when + // this ever becomes a hot-spot + BOOST_FOREACH(FGTaxiSegment* seg, segments) { + if (seg->startNode != from) { + continue; + } + + if ((to == 0) || (seg->endNode == to)) { + return seg; + } + } + + return NULL; // not found +} + +static int edgePenalty(FGTaxiNode* tn) +{ + return (tn->type() == FGPositioned::PARKING ? 10000 : 0) + + (tn->getIsOnRunway() ? 1000 : 0); +} -FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, +class ShortestPathData +{ +public: + ShortestPathData() : + score(HUGE_VAL) + {} + + double score; + FGTaxiNode_ptr previousNode; +}; + +FGTaxiRoute FGGroundNetwork::findShortestRoute(PositionedID start, PositionedID end, bool fullSearch) { //implements Dijkstra's algorithm to find shortest distance route from start to end //taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm - - //double INFINITE = 100000000000.0; - // initialize scoring values - int nParkings = parent->getDynamics()->getNrOfParkings(); - FGTaxiNodeVector *currNodesSet; - if (fullSearch) { - currNodesSet = &nodes; - } else { - currNodesSet = &pushBackNodes; - } - - for (FGTaxiNodeVectorIterator - itr = currNodesSet->begin(); itr != currNodesSet->end(); itr++) { - (*itr)->setPathScore(HUGE_VAL); //infinity by all practical means - (*itr)->setPreviousNode(0); // - (*itr)->setPreviousSeg(0); // - } - + FGTaxiNodeVector unvisited; + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + std::map searchData; + + BOOST_FOREACH(PositionedID n, cache->groundNetNodes(parent->guid(), !fullSearch)) { + unvisited.push_back(findNode(n)); + } + FGTaxiNode *firstNode = findNode(start); if (!firstNode) { @@ -587,7 +418,7 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, << " at " << ((parent) ? parent->getId() : "")); return FGTaxiRoute(); } - firstNode->setPathScore(0); + searchData[firstNode].score = 0.0; FGTaxiNode *lastNode = findNode(end); if (!lastNode) @@ -598,87 +429,55 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end, return FGTaxiRoute(); } - FGTaxiNodeVector unvisited(*currNodesSet); // working copy - while (!unvisited.empty()) { - FGTaxiNode *best = *(unvisited.begin()); - for (FGTaxiNodeVectorIterator - itr = unvisited.begin(); itr != unvisited.end(); itr++) { - if ((*itr)->getPathScore() < best->getPathScore()) - best = (*itr); + FGTaxiNode *best = unvisited.front(); + BOOST_FOREACH(FGTaxiNode* i, unvisited) { + if (searchData[i].score < searchData[best].score) { + best = i; + } } - + + // remove 'best' from the unvisited set FGTaxiNodeVectorIterator newend = remove(unvisited.begin(), unvisited.end(), best); unvisited.erase(newend, unvisited.end()); if (best == lastNode) { // found route or best not connected break; - } else { - for (FGTaxiSegmentVectorIterator - seg = best->getBeginRoute(); - seg != best->getEndRoute(); seg++) { - if (fullSearch || (*seg)->isPushBack()) { - FGTaxiNode *tgt = (*seg)->getEnd(); - if (!tgt) - { - SG_LOG(SG_GENERAL, SG_ALERT, - "Error in ground network. Found empty segment " - << " at " << ((parent) ? parent->getId() : "")); - return FGTaxiRoute(); - } - double alt = - best->getPathScore() + (*seg)->getLength() + - (*seg)->getPenalty(nParkings); - if (alt < tgt->getPathScore()) { // Relax (u,v) - tgt->setPathScore(alt); - tgt->setPreviousNode(best); - tgt->setPreviousSeg(*seg); // - } - } else { - // // cerr << "Skipping TaxiSegment " << (*seg)->getIndex() << endl; - } - } } - } + + BOOST_FOREACH(PositionedID targetId, cache->groundNetEdgesFrom(best->guid(), !fullSearch)) { + FGTaxiNode* tgt = (FGTaxiNode*) cache->loadById(targetId); + double edgeLength = dist(best->cart(), tgt->cart()); + double alt = searchData[best].score + edgeLength + edgePenalty(tgt); + if (alt < searchData[tgt].score) { // Relax (u,v) + searchData[tgt].score = alt; + searchData[tgt].previousNode = best; + } + } // of outgoing arcs/segments from current best node iteration + } // of unvisited nodes remaining - if (lastNode->getPathScore() == HUGE_VAL) { + if (searchData[lastNode].score == HUGE_VAL) { // no valid route found if (fullSearch) { SG_LOG(SG_GENERAL, SG_ALERT, "Failed to find route from waypoint " << start << " to " << end << " at " << parent->getId()); } - FGTaxiRoute empty; - return empty; - //exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's - } else { - // assemble route from backtrace information - intVec nodes, routes; - FGTaxiNode *bt = lastNode; - while (bt->getPreviousNode() != 0) { - nodes.push_back(bt->getIndex()); - routes.push_back(bt->getPreviousSegment()->getIndex()); - bt = bt->getPreviousNode(); - } - nodes.push_back(start); - reverse(nodes.begin(), nodes.end()); - reverse(routes.begin(), routes.end()); - - return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0); - } -} - -int FGTaxiSegment::getPenalty(int nGates) -{ - int penalty = 0; - if (end->getIndex() < nGates) { - penalty += 10000; - } - if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active. - penalty += 1000; + + return FGTaxiRoute(); } - return penalty; + + // assemble route from backtrace information + PositionedIDVec nodes; + FGTaxiNode *bt = lastNode; + while (searchData[bt].previousNode != 0) { + nodes.push_back(bt->guid()); + bt = searchData[bt].previousNode; + } + nodes.push_back(start); + reverse(nodes.begin(), nodes.end()); + return FGTaxiRoute(nodes, searchData[lastNode].score, 0); } /* ATC Related Functions */ @@ -691,7 +490,8 @@ void FGGroundNetwork::announcePosition(int id, double radius, int leg, FGAIAircraft * aircraft) { - init(); + assert(parent); + TrafficVectorIterator i = activeTraffic.begin(); // Search search if the current id alread has an entry // This might be faster using a map instead of a vector, but let's start by taking a safe route @@ -767,7 +567,7 @@ bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, Traffic if ((state >= minState) && (state <= maxState) && available) { if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) { //cerr << "Checking state " << state << " for " << i->getAircraft()->getCallSign() << endl; - static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); + SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); int n = trans_num->getIntValue(); if (n == 0) { trans_num->setIntValue(-1); @@ -893,7 +693,7 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat, TrafficVectorIterator current, closest, closestOnNetwork; TrafficVectorIterator i = activeTraffic.begin(); bool otherReasonToSlowDown = false; - bool previousInstruction; +// bool previousInstruction; if (activeTraffic.size()) { //while ((i->getId() != id) && (i != activeTraffic.end())) while (i != activeTraffic.end()) { @@ -912,10 +712,10 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat, current = i; //closest = current; - previousInstruction = current->getSpeedAdjustment(); +// previousInstruction = current->getSpeedAdjustment(); double mindist = HUGE_VAL; if (activeTraffic.size()) { - double course, dist, bearing, minbearing, az2; + double course, dist, bearing, az2; // minbearing, SGGeod curr(SGGeod::fromDegM(lon, lat, alt)); //TrafficVector iterator closest; closest = current; @@ -937,7 +737,7 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat, mindist = dist; closest = i; closestOnNetwork = i; - minbearing = bearing; +// minbearing = bearing; } } @@ -961,7 +761,7 @@ void FGGroundNetwork::checkSpeedAdjustment(int id, double lat, // << endl; mindist = dist; closest = i; - minbearing = bearing; +// minbearing = bearing; otherReasonToSlowDown = true; } } @@ -1070,7 +870,7 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat, } bool origStatus = current->hasHoldPosition(); current->setHoldPosition(false); - SGGeod curr(SGGeod::fromDegM(lon, lat, alt)); + //SGGeod curr(SGGeod::fromDegM(lon, lat, alt)); int currentRoute = i->getCurrentPosition(); int nextRoute; if (i->getIntentions().size()) { @@ -1090,7 +890,7 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat, // current->setHoldPosition(true); //} SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude()))); - SGGeod end (SGGeod::fromDeg(nx->getStart()->getLongitude(), nx->getStart()->getLatitude())); + SGGeod end (nx->getStart()->geod()); double distance = SGGeodesy::distanceM(start, end); if (nx->hasBlock(now) && (distance < i->getRadius() * 4)) { @@ -1327,7 +1127,7 @@ static void WorldCoordinate(osg::Matrix& obj_pos, double lat, double lon, double elev, double hdg, double slope) { SGGeod geod = SGGeod::fromDegM(lon, lat, elev); - obj_pos = geod.makeZUpFrame(); + obj_pos = makeZUpFrame(geod); // hdg is not a compass heading, but a counter-clockwise rotation // around the Z axis obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS, @@ -1359,7 +1159,7 @@ void FGGroundNetwork::render(bool visible) group = new osg::Group; FGScenery * local_scenery = globals->get_scenery(); // double elevation_meters = 0.0; - double elevation_feet = 0.0; +// double elevation_feet = 0.0; time_t now = time(NULL) + fgGetLong("/sim/time/warp"); //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) { //double dx = 0; @@ -1369,10 +1169,10 @@ void FGGroundNetwork::render(bool visible) if (pos >= 0) { SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude()))); - SGGeod end (SGGeod::fromDeg(segments[pos]->getEnd()->getLongitude(), segments[pos]->getEnd()->getLatitude())); + SGGeod end (segments[pos]->getEnd()->geod()); double length = SGGeodesy::distanceM(start, end); - //heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod()); + //heading = SGGeodesy::headingDeg(start->geod(), end->geod()); double az2, heading; //, distanceM; SGGeodesy::inverse(start, end, heading, az2, length); @@ -1392,14 +1192,14 @@ void FGGroundNetwork::render(bool visible) } else { elevationStart = ((i)->getAircraft()->_getAltitude()); } - double elevationEnd = segments[pos]->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationEnd = segments[pos]->getEnd()->getElevationM(); //cerr << "Using elevation " << elevationEnd << endl; if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) { SGGeod center2 = end; center2.setElevationM(SG_MAX_ELEVATION_M); if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { - elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; +// elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; //elevation_meters += 0.5; } else { @@ -1453,13 +1253,13 @@ void FGGroundNetwork::render(bool visible) obj_trans->setDataVariance(osg::Object::STATIC); // Experimental: Calculate slope here, based on length, and the individual elevations - double elevationStart = segments[k]->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); - double elevationEnd = segments[k]->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER); + double elevationStart = segments[k]->getStart()->getElevationM(); + double elevationEnd = segments[k]->getEnd ()->getElevationM(); if ((elevationStart == 0) || (elevationStart == parent->getElevation())) { - SGGeod center2 = segments[k]->getStart()->getGeod(); + SGGeod center2 = segments[k]->getStart()->geod(); center2.setElevationM(SG_MAX_ELEVATION_M); if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) { - elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5; +// elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5; //elevation_meters += 0.5; } else { @@ -1468,10 +1268,10 @@ void FGGroundNetwork::render(bool visible) segments[k]->getStart()->setElevation(elevationStart); } if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) { - SGGeod center2 = segments[k]->getEnd()->getGeod(); + SGGeod center2 = segments[k]->getEnd()->geod(); center2.setElevationM(SG_MAX_ELEVATION_M); if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) { - elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; +// elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5; //elevation_meters += 0.5; } else { @@ -1487,8 +1287,8 @@ void FGGroundNetwork::render(bool visible) // cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl; - - WorldCoordinate( obj_pos, segments[k]->getLatitude(), segments[k]->getLongitude(), elevationMean+ 0.5, -(segments[k]->getHeading()), slope ); + SGGeod segCenter = segments[k]->getCenter(); + WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(), segCenter.getLongitudeDeg(), elevationMean+ 0.5, -(segments[k]->getHeading()), slope ); obj_trans->setMatrix( obj_pos ); //osg::Vec3 center(0, 0, 0) diff --git a/src/Airports/groundnetwork.hxx b/src/Airports/groundnetwork.hxx index 78f1282..de8d94e 100644 --- a/src/Airports/groundnetwork.hxx +++ b/src/Airports/groundnetwork.hxx @@ -24,38 +24,28 @@ #ifndef _GROUNDNETWORK_HXX_ #define _GROUNDNETWORK_HXX_ -#include -#include -#include -#include - - #include -#include #include #include #include - -class Block; -using std::string; -using std::vector; -using std::list; +#include #include "gnnode.hxx" #include "parking.hxx" #include +class Block; +class FGRunway; class FGTaxiSegment; // forward reference class FGAIFlightPlan; // forward reference class FGAirport; // forward reference -typedef vector FGTaxiSegmentVector; -typedef vector::iterator FGTaxiSegmentVectorIterator; +typedef std::vector FGTaxiSegmentVector; +typedef FGTaxiSegmentVector::iterator FGTaxiSegmentVectorIterator; -//typedef vector FGTaxiSegmentPointerVector; -//typedef vector::iterator FGTaxiSegmentPointerVectorIterator; +typedef std::map IndexTaxiNodeMap; class Block { @@ -73,7 +63,7 @@ public: bool operator< (const Block &other) const { return blocktime < other.blocktime; }; }; -typedef vector BlockList; +typedef std::vector BlockList; typedef BlockList::iterator BlockListIterator; /*************************************************************************************** @@ -82,143 +72,58 @@ typedef BlockList::iterator BlockListIterator; class FGTaxiSegment { private: - int startNode; - int endNode; - double length; - double heading; - SGGeod center; + const PositionedID startNode; + const PositionedID endNode; + bool isActive; - bool isPushBackRoute; BlockList blockTimes; - FGTaxiNode *start; - FGTaxiNode *end; + int index; FGTaxiSegment *oppositeDirection; - - + friend class FGGroundNetwork; public: - FGTaxiSegment() : - startNode(0), - endNode(0), - length(0), - heading(0), - isActive(0), - isPushBackRoute(0), - start(0), - end(0), - index(0), - oppositeDirection(0) - { - }; - - FGTaxiSegment (const FGTaxiSegment &other) : - startNode (other.startNode), - endNode (other.endNode), - length (other.length), - heading (other.heading), - center (other.center), - isActive (other.isActive), - isPushBackRoute (other.isPushBackRoute), - blockTimes (other.blockTimes), - start (other.start), - end (other.end), - index (other.index), - oppositeDirection (other.oppositeDirection) - { - }; - - FGTaxiSegment& operator=(const FGTaxiSegment &other) - { - startNode = other.startNode; - endNode = other.endNode; - length = other.length; - heading = other.heading; - center = other.center; - isActive = other.isActive; - isPushBackRoute = other.isPushBackRoute; - blockTimes = other.blockTimes; - start = other.start; - end = other.end; - index = other.index; - oppositeDirection = other.oppositeDirection; - return *this; - }; - + FGTaxiSegment(PositionedID start, PositionedID end); + void setIndex (int val) { index = val; }; - void setStartNodeRef (int val) { - startNode = val; - }; - void setEndNodeRef (int val) { - endNode = val; - }; - - void setOpposite(FGTaxiSegment *opp) { - oppositeDirection = opp; - }; - - void setStart(FGTaxiNodeVector *nodes); - void setEnd (FGTaxiNodeVector *nodes); - void setPushBackType(bool val) { - isPushBackRoute = val; - }; + void setDimensions(double elevation); void block(int id, time_t blockTime, time_t now); void unblock(time_t now); bool hasBlock(time_t now); - FGTaxiNode * getEnd() { - return end; - }; - FGTaxiNode * getStart() { - return start; - }; - double getLength() { - return length; - }; + FGTaxiNode * getEnd() const; + FGTaxiNode * getStart() const; + + double getLength() const; + + // compute the center of the arc + SGGeod getCenter() const; + + double getHeading() const; + int getIndex() { - return index; - }; - double getLatitude() { - return center.getLatitudeDeg(); - }; - double getLongitude() { - return center.getLongitudeDeg(); - }; - double getHeading() { - return heading; - }; - bool isPushBack() { - return isPushBackRoute; + return index; }; int getPenalty(int nGates); - FGTaxiSegment *getAddress() { - return this; - }; - bool operator<(const FGTaxiSegment &other) const { return index < other.index; }; - //bool hasSmallerHeadingDiff (const FGTaxiSegment &other) const { return headingDiff < other.headingDiff; }; + FGTaxiSegment *opposite() { return oppositeDirection; }; - void setCourseDiff(double crse); - - - - }; -typedef vector intVec; -typedef vector::iterator intVecIterator; +typedef std::vector intVec; +typedef std::vector::iterator intVecIterator; @@ -228,60 +133,45 @@ typedef vector::iterator intVecIterator; class FGTaxiRoute { private: - intVec nodes; - intVec routes; + PositionedIDVec nodes; double distance; -// int depth; - intVecIterator currNode; - intVecIterator currRoute; + PositionedIDVec::iterator currNode; public: FGTaxiRoute() { distance = 0; currNode = nodes.begin(); - currRoute = routes.begin(); }; - FGTaxiRoute(intVec nds, intVec rts, double dist, int dpth) { + + FGTaxiRoute(const PositionedIDVec& nds, double dist, int dpth) { nodes = nds; - routes = rts; distance = dist; currNode = nodes.begin(); - currRoute = routes.begin(); -// depth = dpth; }; FGTaxiRoute& operator= (const FGTaxiRoute &other) { nodes = other.nodes; - routes = other.routes; distance = other.distance; -// depth = other.depth; currNode = nodes.begin(); - currRoute = routes.begin(); return *this; }; FGTaxiRoute(const FGTaxiRoute& copy) : nodes(copy.nodes), - routes(copy.routes), distance(copy.distance), -// depth(copy.depth), - currNode(nodes.begin()), - currRoute(routes.begin()) + currNode(nodes.begin()) {}; bool operator< (const FGTaxiRoute &other) const { return distance < other.distance; }; bool empty () { - return nodes.begin() == nodes.end(); + return nodes.empty(); }; - bool next(int *nde); - bool next(int *nde, int *rte); - void rewind(int legNr); - + bool next(PositionedID *nde); + void first() { currNode = nodes.begin(); - currRoute = routes.begin(); }; int size() { return nodes.size(); @@ -289,12 +179,10 @@ public: int nodesLeft() { return nodes.end() - currNode; }; - -// int getDepth() { return depth; }; }; -typedef vector TaxiRouteVector; -typedef vector::iterator TaxiRouteVectorIterator; +typedef std::vector TaxiRouteVector; +typedef std::vector::iterator TaxiRouteVectorIterator; /************************************************************************************** * class FGGroundNetWork @@ -308,13 +196,9 @@ private: //int maxDepth; int count; int version; - FGTaxiNodeVector nodes; - FGTaxiNodeVector pushBackNodes; + FGTaxiSegmentVector segments; - //intVec route; - //intVec nodesStack; - //intVec routesStack; - TaxiRouteVector routes; + TrafficVector activeTraffic; TrafficVectorIterator currTraffic; @@ -332,19 +216,17 @@ private: double heading, double speed, double alt); - + void parseCache(); + + void loadSegments(); public: FGGroundNetwork(); ~FGGroundNetwork(); - - void addNode (const FGTaxiNode& node); - void addNodes (FGParkingVec *parkings); - void addSegment(const FGTaxiSegment& seg); - void setVersion (int v) { version = v;}; + void setVersion (int v) { version = v;}; int getVersion() { return version; }; - void init(); + void init(FGAirport* pr); bool exists() { return hasNetwork; }; @@ -352,22 +234,21 @@ public: towerController = twrCtrlr; }; - int findNearestNode(double lat, double lon); - int findNearestNode(const SGGeod& aGeod); - int findNearestNodeOnRunway(const SGGeod& aGeod); - - FGTaxiNode *findNode(unsigned idx); - FGTaxiSegment *findSegment(unsigned idx); - FGTaxiRoute findShortestRoute(int start, int end, bool fullSearch=true); - //void trace(FGTaxiNode *, int, int, double dist); - - int getNrOfNodes() { - return nodes.size(); - }; - - void setParent(FGAirport *par) { - parent = par; - }; + int findNearestNode(const SGGeod& aGeod) const; + int findNearestNodeOnRunway(const SGGeod& aGeod, FGRunway* aRunway = NULL) const; + + FGTaxiNode *findNode(PositionedID idx) const; + FGTaxiSegment *findSegment(unsigned idx) const; + + /** + * Find the taxiway segment joining two (ground-net) nodes. Returns + * NULL if no such segment exists. + * It is permitted to pass 0 for the 'to' ID, indicating that any + * segment originating at 'from' is acceptable. + */ + FGTaxiSegment* findSegment(PositionedID from, PositionedID to) const; + + FGTaxiRoute findShortestRoute(PositionedID start, PositionedID end, bool fullSearch=true); virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, double lat, double lon, double hdg, double spd, double alt, @@ -381,7 +262,7 @@ public: AtcMsgDir msgDir); bool checkForCircularWaits(int id); virtual void render(bool); - virtual string getName(); + virtual std::string getName(); virtual void update(double dt); void saveElevationCache(); diff --git a/src/Airports/parking.cxx b/src/Airports/parking.cxx index d1fb651..032a125 100644 --- a/src/Airports/parking.cxx +++ b/src/Airports/parking.cxx @@ -31,26 +31,26 @@ #include #include "parking.hxx" -#include "groundnetwork.hxx" /********************************************************************************* * FGParking ********************************************************************************/ -// FGParking::FGParking(double lat, -// double lon, -// double hdg, -// double rad, -// int idx, -// const string &name, -// const string &tpe, -// const string &codes) -// : FGTaxiNode(lat,lon,idx) -// { -// heading = hdg; -// parkingName = name; -// type = tpe; -// airlineCodes = codes; -// } -FGParking::~FGParking() { - delete pushBackRoute; + +FGParking::FGParking(PositionedID aGuid, const SGGeod& pos, + double aHeading, double aRadius, + const std::string& name, const std::string& aType, + const std::string& codes, + PositionedID pushBackNode) : + FGTaxiNode(aGuid, pos, false, 0), + heading(aHeading), + radius(aRadius), + parkingName(name), + type(aType), + airlineCodes(codes), + pushBackPoint(pushBackNode) +{ +} + +FGParking::~FGParking() +{ } diff --git a/src/Airports/parking.hxx b/src/Airports/parking.hxx index 0cac08b..ff466d2 100644 --- a/src/Airports/parking.hxx +++ b/src/Airports/parking.hxx @@ -30,98 +30,48 @@ #endif #include +#include #include #include +#include // for std::auto_ptr #include "gnnode.hxx" -using std::string; -using std::vector; -class FGTaxiRoute; - - -class FGParking : public FGTaxiNode { +class FGParking : public FGTaxiNode +{ private: - double heading; - double radius; - string parkingName; - string type; - string airlineCodes; - - bool available; - int pushBackPoint; - FGTaxiRoute *pushBackRoute; - + const double heading; + const double radius; + const std::string parkingName; + const std::string type; + const std::string airlineCodes; + const PositionedID pushBackPoint; + + SG_DISABLE_COPY(FGParking); public: - FGParking() : - heading(0), - radius(0), - available(true), - pushBackPoint(0), - pushBackRoute(0) - { - }; - - FGParking(const FGParking &other) : - FGTaxiNode (other), - heading (other.heading), - radius (other.radius), - parkingName (other.parkingName), - type (other.type), - airlineCodes (other.airlineCodes), - available (other.available), - pushBackPoint(other.pushBackPoint), - pushBackRoute(other.pushBackRoute) - { - }; - - - FGParking& operator =(const FGParking &other) - { - FGTaxiNode::operator=(other); - heading = other.heading; - radius = other.radius; - parkingName = other.parkingName; - type = other.type; - airlineCodes = other.airlineCodes; - available = other.available; - pushBackPoint= other.pushBackPoint; - pushBackRoute= other.pushBackRoute; - return *this; - }; - ~FGParking(); -// FGParking(double lat, -// double lon, -// double hdg, -// double rad, -// int idx, -// const string& name, -// const string& tpe, -// const string& codes); - + FGParking(PositionedID aGuid, const SGGeod& pos, + double heading, double radius, + const std::string& name, const std::string& type, + const std::string& codes, + PositionedID pushBackNode); + virtual ~FGParking(); +#if 0 void setHeading (double hdg) { heading = hdg; }; void setRadius (double rad) { radius = rad; }; - void setName (const string& name) { parkingName = name; }; - void setType (const string& tpe) { type = tpe; }; - void setCodes (const string& codes){ airlineCodes= codes;}; - - void setPushBackRoute(FGTaxiRoute *val) { pushBackRoute = val; }; - void setPushBackPoint(int val) { pushBackPoint = val; }; - - bool isAvailable () { return available;}; - void setAvailable(bool val) { available = val; }; + void setName (const std::string& name) { parkingName = name; }; + void setType (const std::string& tpe) { type = tpe; }; + void setCodes (const std::string& codes){ airlineCodes= codes;}; +#endif - double getHeading () { return heading; }; - double getRadius () { return radius; }; - - string getType () { return type; }; - string getCodes () { return airlineCodes;}; - string getName () { return parkingName; }; + double getHeading () const { return heading; }; + double getRadius () const { return radius; }; - FGTaxiRoute * getPushBackRoute () { return pushBackRoute; }; + std::string getType () const { return type; }; + std::string getCodes () const { return airlineCodes;}; + std::string getName () const { return parkingName; }; int getPushBackPoint () { return pushBackPoint; }; @@ -129,8 +79,8 @@ public: return radius < other.radius; }; }; -typedef vector FGParkingVec; -typedef vector::iterator FGParkingVecIterator; -typedef vector::const_iterator FGParkingVecConstIterator; +typedef std::vector FGParkingVec; +typedef FGParkingVec::iterator FGParkingVecIterator; +typedef FGParkingVec::const_iterator FGParkingVecConstIterator; #endif diff --git a/src/Airports/pavement.cxx b/src/Airports/pavement.cxx index 4f06a9a..dbb57d1 100644 --- a/src/Airports/pavement.cxx +++ b/src/Airports/pavement.cxx @@ -24,10 +24,9 @@ #include "pavement.hxx" -FGPavement::FGPavement(const std::string& aIdent, const SGGeod& aPos) : - FGPositioned(PAVEMENT, aIdent, aPos) +FGPavement::FGPavement(PositionedID aGuid, const std::string& aIdent, const SGGeod& aPos) : + FGPositioned(aGuid, PAVEMENT, aIdent, aPos) { - init(false); // FGPositioned::init } void FGPavement::addNode(const SGGeod &aPos, bool aClose) diff --git a/src/Airports/pavement.hxx b/src/Airports/pavement.hxx index 9137cfc..c9a4580 100644 --- a/src/Airports/pavement.hxx +++ b/src/Airports/pavement.hxx @@ -59,7 +59,7 @@ public: typedef std::vector > NodeList; - FGPavement(const std::string& aIdent, const SGGeod& aPos); + FGPavement(PositionedID aGuid, const std::string& aIdent, const SGGeod& aPos); void addNode(const SGGeod &aPos, bool aClose = false); void addBezierNode(const SGGeod &aPos, const SGGeod &aCtrlPt, bool aClose = false); diff --git a/src/Airports/runwaybase.cxx b/src/Airports/runwaybase.cxx index 951f860..9bcee4e 100644 --- a/src/Airports/runwaybase.cxx +++ b/src/Airports/runwaybase.cxx @@ -46,36 +46,30 @@ using std::string; * 12 - lakebed */ -FGRunwayBase::FGRunwayBase(Type aTy, const string& aIdent, +FGRunwayBase::FGRunwayBase(PositionedID aGuid, Type aTy, const string& aIdent, const SGGeod& aGeod, const double heading, const double length, const double width, - const int surface_code, - bool index) : - FGPositioned(aTy, aIdent, aGeod) + const int surface_code) : + FGPositioned(aGuid, aTy, aIdent, aGeod) { _heading = heading; _length = length; _width = width; _surface_code = surface_code; - - init(index); } SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const { - SGGeod result; - double dummyAz2; double halfLengthMetres = lengthM() * 0.5; - SGGeodesy::direct(mPosition, _heading, - aOffset - halfLengthMetres, - result, dummyAz2); + SGGeod result = SGGeodesy::direct(mPosition, _heading, + aOffset - halfLengthMetres); + result.setElevationM(mPosition.getElevationM()); + return result; } - - SGGeod FGRunwayBase::pointOffCenterline(double aOffset, double lateralOffset) const { SGGeod result; @@ -100,11 +94,12 @@ bool FGRunwayBase::isHardSurface() const return ((_surface_code == 1) || (_surface_code == 2)); } -FGTaxiway::FGTaxiway(const string& aIdent, +FGTaxiway::FGTaxiway(PositionedID aGuid, + const string& aIdent, const SGGeod& aGeod, const double heading, const double length, const double width, const int surface_code) : - FGRunwayBase(TAXIWAY, aIdent, aGeod, heading, length, width, surface_code, false) + FGRunwayBase(aGuid, TAXIWAY, aIdent, aGeod, heading, length, width, surface_code) { } diff --git a/src/Airports/runwaybase.hxx b/src/Airports/runwaybase.hxx index ca0a9fb..25d2340 100644 --- a/src/Airports/runwaybase.hxx +++ b/src/Airports/runwaybase.hxx @@ -39,12 +39,11 @@ class FGRunwayBase : public FGPositioned { public: - FGRunwayBase(Type aTy, const std::string& aIdent, + FGRunwayBase(PositionedID aGuid, Type aTy, const std::string& aIdent, const SGGeod& aGeod, const double heading, const double length, const double width, - const int surface_code, - bool index); + const int surface_code); /** * Retrieve a position on the extended centerline. Positive values @@ -100,7 +99,8 @@ protected: class FGTaxiway : public FGRunwayBase { public: - FGTaxiway(const std::string& aIdent, + FGTaxiway(PositionedID aGuid, + const std::string& aIdent, const SGGeod& aGeod, const double heading, const double length, const double width, diff --git a/src/Airports/runwayprefs.cxx b/src/Airports/runwayprefs.cxx index f3110a1..fac1807 100644 --- a/src/Airports/runwayprefs.cxx +++ b/src/Airports/runwayprefs.cxx @@ -26,6 +26,7 @@ #endif #include +#include #include #include diff --git a/src/Airports/runways.cxx b/src/Airports/runways.cxx index bd2df30..70a2ee1 100644 --- a/src/Airports/runways.cxx +++ b/src/Airports/runways.cxx @@ -40,34 +40,12 @@ #include #include #include -#include +#include using std::string; -static std::string cleanRunwayNo(const std::string& aRwyNo) -{ - if (aRwyNo[0] == 'x') { - return std::string(); // no ident for taxiways - } - - string result(aRwyNo); - // canonicalise runway ident - if ((aRwyNo.size() == 1) || !isdigit(aRwyNo[1])) { - result = "0" + aRwyNo; - } - - // trim off trailing garbage - if (result.size() > 2) { - char suffix = toupper(result[2]); - if (suffix == 'X') { - result = result.substr(0, 2); - } - } - - return result; -} - -FGRunway::FGRunway(FGAirport* aAirport, const string& aIdent, +FGRunway::FGRunway(PositionedID aGuid, + PositionedID aAirport, const string& aIdent, const SGGeod& aGeod, const double heading, const double length, const double width, @@ -75,13 +53,14 @@ FGRunway::FGRunway(FGAirport* aAirport, const string& aIdent, const double stopway, const int surface_code, bool reciprocal) : - FGRunwayBase(RUNWAY, cleanRunwayNo(aIdent), aGeod, heading, length, width, surface_code, true), + FGRunwayBase(aGuid, RUNWAY, aIdent, aGeod, + heading, length, width, surface_code), _airport(aAirport), _isReciprocal(reciprocal), - _reciprocal(NULL), + _reciprocal(0), _displ_thresh(displ_thresh), _stopway(stopway), - _ils(NULL) + _ils(0) { } @@ -145,39 +124,48 @@ SGGeod FGRunway::threshold() const return pointOnCenterline(_displ_thresh * SG_FEET_TO_METER); } -void FGRunway::processThreshold(SGPropertyNode* aThreshold) +void FGRunway::setReciprocalRunway(PositionedID other) { - assert(ident() == aThreshold->getStringValue("rwy")); - - double lon = aThreshold->getDoubleValue("lon"), - lat = aThreshold->getDoubleValue("lat"); - SGGeod newThreshold(SGGeod::fromDegM(lon, lat, mPosition.getElevationM())); - - _heading = aThreshold->getDoubleValue("hdg-deg"); - _displ_thresh = aThreshold->getDoubleValue("displ-m") * SG_METER_TO_FEET; - _stopway = aThreshold->getDoubleValue("stopw-m") * SG_METER_TO_FEET; + assert(_reciprocal==0); + _reciprocal = other; +} + +FGAirport* FGRunway::airport() const +{ + return (FGAirport*) flightgear::NavDataCache::instance()->loadById(_airport); +} + +FGRunway* FGRunway::reciprocalRunway() const +{ + return (FGRunway*) flightgear::NavDataCache::instance()->loadById(_reciprocal); +} + +FGNavRecord* FGRunway::ILS() const +{ + if (_ils == 0) { + return NULL; + } - // compute the new runway center, based on the threshold lat/lon and length, - double offsetFt = (0.5 * _length); - SGGeod newCenter; - double dummy; - SGGeodesy::direct(newThreshold, _heading, offsetFt * SG_FEET_TO_METER, newCenter, dummy); - mPosition = newCenter; -} - -void FGRunway::setReciprocalRunway(FGRunway* other) + return (FGNavRecord*) flightgear::NavDataCache::instance()->loadById(_ils); +} + +FGNavRecord* FGRunway::glideslope() const { - assert(_reciprocal==NULL); - assert((other->_reciprocal == NULL) || (other->_reciprocal == this)); + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + PositionedID gsId = cache->findNavaidForRunway(guid(), FGPositioned::GS); + if (gsId == 0) { + return NULL; + } - _reciprocal = other; + return (FGNavRecord*) cache->loadById(gsId); } -std::vector FGRunway::getSIDs() +std::vector FGRunway::getSIDs() const { + FGAirport* apt = airport(); std::vector result; - for (unsigned int i=0; i<_airport->numSIDs(); ++i) { - flightgear::SID* s = _airport->getSIDByIndex(i); + for (unsigned int i=0; inumSIDs(); ++i) { + flightgear::SID* s = apt->getSIDByIndex(i); if (s->isForRunway(this)) { result.push_back(s); } @@ -186,11 +174,12 @@ std::vector FGRunway::getSIDs() return result; } -std::vector FGRunway::getSTARs() +std::vector FGRunway::getSTARs() const { + FGAirport* apt = airport(); std::vector result; - for (unsigned int i=0; i<_airport->numSTARs(); ++i) { - flightgear::STAR* s = _airport->getSTARByIndex(i); + for (unsigned int i=0; inumSTARs(); ++i) { + flightgear::STAR* s = apt->getSTARByIndex(i); if (s->isForRunway(this)) { result.push_back(s); } @@ -199,9 +188,17 @@ std::vector FGRunway::getSTARs() return result; } -flightgear::PositionedBinding* -FGRunway::createBinding(SGPropertyNode* nd) const +std::vector FGRunway::getApproaches() const { - return new flightgear::RunwayBinding(this, nd); + FGAirport* apt = airport(); + std::vector result; + for (unsigned int i=0; inumApproaches(); ++i) { + flightgear::Approach* s = apt->getApproachByIndex(i); + if (s->runway() == this) { + result.push_back(s); + } + } // of approaches at the airport iteration + + return result; } diff --git a/src/Airports/runways.hxx b/src/Airports/runways.hxx index 2cdc2e4..36f342c 100644 --- a/src/Airports/runways.hxx +++ b/src/Airports/runways.hxx @@ -36,19 +36,21 @@ class SGPropertyNode; namespace flightgear { class SID; class STAR; + class Approach; } class FGRunway : public FGRunwayBase { - FGAirport* _airport; + PositionedID _airport; bool _isReciprocal; - FGRunway* _reciprocal; + PositionedID _reciprocal; double _displ_thresh; double _stopway; - FGNavRecord* _ils; + PositionedID _ils; public: - FGRunway(FGAirport* aAirport, const std::string& rwy_no, + FGRunway(PositionedID aGuid, + PositionedID aAirport, const std::string& rwy_no, const SGGeod& aGeod, const double heading, const double length, const double width, @@ -77,7 +79,7 @@ public: { return _isReciprocal; } /** - * Get the runway begining point - this is syntatic sugar, equivalent to + * Get the runway beginning point - this is syntatic sugar, equivalent to * calling pointOnCenterline(0.0); */ SGGeod begin() const; @@ -102,36 +104,33 @@ public: /** * Airport this runway is located at */ - FGAirport* airport() const - { return _airport; } + FGAirport* airport() const; - // FIXME - should die once airport / runway creation is cleaned up - void setAirport(FGAirport* aAirport) - { _airport = aAirport; } + FGNavRecord* ILS() const; - FGNavRecord* ILS() const { return _ils; } - void setILS(FGNavRecord* nav) { _ils = nav; } + /** + * retrieve the associated glideslope transmitter, if one is defined. + */ + FGNavRecord* glideslope() const; - FGRunway* reciprocalRunway() const - { return _reciprocal; } - void setReciprocalRunway(FGRunway* other); + void setILS(PositionedID nav) { _ils = nav; } - virtual flightgear::PositionedBinding* createBinding(SGPropertyNode* nd) const; + FGRunway* reciprocalRunway() const; - /** - * Helper to process property data loaded from an ICAO.threshold.xml file - */ - void processThreshold(SGPropertyNode* aThreshold); + void setReciprocalRunway(PositionedID other); /** * Get SIDs (DPs) associated with this runway */ - std::vector getSIDs(); + std::vector getSIDs() const; /** * Get STARs associared with this runway */ - std::vector getSTARs(); + std::vector getSTARs() const; + + + std::vector getApproaches() const; }; diff --git a/src/Airports/simple.cxx b/src/Airports/simple.cxx index d9ac0e6..769e4ab 100644 --- a/src/Airports/simple.cxx +++ b/src/Airports/simple.cxx @@ -31,12 +31,14 @@ #include "simple.hxx" #include +#include #include #include #include #include #include +#include #include #include @@ -47,32 +49,33 @@ #include #include #include -#include #include +#include using std::vector; using std::pair; using namespace flightgear; -// magic import of a helper which uses FGPositioned internals -extern char** searchAirportNamesAndIdents(const std::string& aFilter); /*************************************************************************** * FGAirport ***************************************************************************/ -FGAirport::FGAirport(const string &id, const SGGeod& location, const SGGeod& tower_location, +AirportCache FGAirport::airportCache; + +FGAirport::FGAirport(PositionedID aGuid, const string &id, const SGGeod& location, const string &name, bool has_metar, Type aType) : - FGPositioned(aType, id, location), - _tower_location(tower_location), + FGPositioned(aGuid, aType, id, location), _name(name), _has_metar(has_metar), _dynamics(0), + mTowerDataLoaded(false), mRunwaysLoaded(false), - mTaxiwaysLoaded(true) + mTaxiwaysLoaded(false), + mProceduresLoaded(false), + mILSDataLoaded(false) { - init(true); // init FGPositioned } @@ -96,6 +99,15 @@ bool FGAirport::isHeliport() const return type() == HELIPORT; } +bool FGAirport::isAirportType(FGPositioned* pos) +{ + if (!pos) { + return false; + } + + return (pos->type() >= AIRPORT) && (pos->type() <= SEAPORT); +} + FGAirportDynamics * FGAirport::getDynamics() { if (_dynamics) { @@ -104,11 +116,11 @@ FGAirportDynamics * FGAirport::getDynamics() _dynamics = new FGAirportDynamics(this); XMLLoader::load(_dynamics); - + _dynamics->init(); + FGRunwayPreference rwyPrefs(this); XMLLoader::load(&rwyPrefs); _dynamics->setRwyUse(rwyPrefs); - XMLLoader::load(_dynamics->getSIDs()); return _dynamics; } @@ -124,53 +136,30 @@ FGRunway* FGAirport::getRunwayByIndex(unsigned int aIndex) const loadRunways(); assert(aIndex >= 0 && aIndex < mRunways.size()); - return mRunways[aIndex]; + return (FGRunway*) flightgear::NavDataCache::instance()->loadById(mRunways[aIndex]); } bool FGAirport::hasRunwayWithIdent(const string& aIdent) const { - return (getIteratorForRunwayIdent(aIdent) != mRunways.end()); + return flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent) != 0; } FGRunway* FGAirport::getRunwayByIdent(const string& aIdent) const { - Runway_iterator it = getIteratorForRunwayIdent(aIdent); - if (it == mRunways.end()) { + PositionedID id = flightgear::NavDataCache::instance()->airportItemWithIdent(guid(), FGPositioned::RUNWAY, aIdent); + if (id == 0) { SG_LOG(SG_GENERAL, SG_ALERT, "no such runway '" << aIdent << "' at airport " << ident()); throw sg_range_exception("unknown runway " + aIdent + " at airport:" + ident(), "FGAirport::getRunwayByIdent"); } - return *it; + return (FGRunway*) flightgear::NavDataCache::instance()->loadById(id); } -FGAirport::Runway_iterator -FGAirport::getIteratorForRunwayIdent(const string& aIdent) const -{ - if (aIdent.empty()) - return mRunways.end(); - - loadRunways(); - - string ident(aIdent); - if ((aIdent.size() == 1) || !isdigit(aIdent[1])) { - ident = "0" + aIdent; - } - - Runway_iterator it = mRunways.begin(); - for (; it != mRunways.end(); ++it) { - if ((*it)->ident() == ident) { - return it; - } - } - - return it; // end() -} FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const { loadRunways(); - Runway_iterator it = mRunways.begin(); FGRunway* result = NULL; double currentBestQuality = 0.0; @@ -180,17 +169,17 @@ FGRunway* FGAirport::findBestRunwayForHeading(double aHeading) const double surfaceWeight = param->getDoubleValue("surface-weight", 10); double deviationWeight = param->getDoubleValue("deviation-weight", 1); - for (; it != mRunways.end(); ++it) { - double good = (*it)->score(lengthWeight, widthWeight, surfaceWeight); - - double dev = aHeading - (*it)->headingDeg(); + BOOST_FOREACH(PositionedID id, mRunways) { + FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id); + double good = rwy->score(lengthWeight, widthWeight, surfaceWeight); + double dev = aHeading - rwy->headingDeg(); SG_NORMALIZE_RANGE(dev, -180.0, 180.0); double bad = fabs(deviationWeight * dev) + 1e-20; double quality = good / bad; if (quality > currentBestQuality) { currentBestQuality = quality; - result = *it; + result = rwy; } } @@ -201,19 +190,20 @@ FGRunway* FGAirport::findBestRunwayForPos(const SGGeod& aPos) const { loadRunways(); - Runway_iterator it = mRunways.begin(); FGRunway* result = NULL; double currentLowestDev = 180.0; - for (; it != mRunways.end(); ++it) { - double inboundCourse = SGGeodesy::courseDeg(aPos, (*it)->end()); - double dev = inboundCourse - (*it)->headingDeg(); + BOOST_FOREACH(PositionedID id, mRunways) { + FGRunway* rwy = (FGRunway*) flightgear::NavDataCache::instance()->loadById(id); + + double inboundCourse = SGGeodesy::courseDeg(aPos, rwy->end()); + double dev = inboundCourse - rwy->headingDeg(); SG_NORMALIZE_RANGE(dev, -180.0, 180.0); dev = fabs(dev); if (dev < currentLowestDev) { // new best match currentLowestDev = dev; - result = *it; + result = rwy; } } // of runway iteration @@ -225,9 +215,9 @@ bool FGAirport::hasHardRunwayOfLengthFt(double aLengthFt) const { loadRunways(); - unsigned int numRunways(mRunways.size()); - for (unsigned int r=0; rloadById(id); + if (rwy->isReciprocal()) { continue; // we only care about lengths, so don't do work twice } @@ -249,8 +239,9 @@ unsigned int FGAirport::numTaxiways() const FGTaxiway* FGAirport::getTaxiwayByIndex(unsigned int aIndex) const { loadTaxiways(); + assert(aIndex >= 0 && aIndex < mTaxiways.size()); - return mTaxiways[aIndex]; + return (FGTaxiway*) flightgear::NavDataCache::instance()->loadById(mTaxiways[aIndex]); } unsigned int FGAirport::numPavements() const @@ -263,29 +254,12 @@ FGPavement* FGAirport::getPavementByIndex(unsigned int aIndex) const { loadTaxiways(); assert(aIndex >= 0 && aIndex < mPavements.size()); - return mPavements[aIndex]; -} - -void FGAirport::setRunwaysAndTaxiways(vector& rwys, - vector& txwys, - vector& pvts) -{ - mRunways.swap(rwys); - Runway_iterator it = mRunways.begin(); - for (; it != mRunways.end(); ++it) { - (*it)->setAirport(this); - } - - mTaxiways.swap(txwys); - mPavements.swap(pvts); + return (FGPavement*) flightgear::NavDataCache::instance()->loadById(mPavements[aIndex]); } FGRunway* FGAirport::getActiveRunwayForUsage() const { - static FGEnvironmentMgr* envMgr = NULL; - if (!envMgr) { - envMgr = (FGEnvironmentMgr *) globals->get_subsystem("environment"); - } + FGEnvironmentMgr* envMgr = (FGEnvironmentMgr *) globals->get_subsystem("environment"); // This forces West-facing rwys to be used in no-wind situations // which is consistent with Flightgear's initial setup. @@ -321,6 +295,9 @@ FGAirport* FGAirport::findClosest(const SGGeod& aPos, double aCuttofNm, Filter* FGAirport::HardSurfaceFilter::HardSurfaceFilter(double minLengthFt) : mMinLengthFt(minLengthFt) { + if (minLengthFt < 0.0) { + mMinLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 0.0); + } } bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const @@ -330,31 +307,31 @@ bool FGAirport::HardSurfaceFilter::passAirport(FGAirport* aApt) const FGAirport* FGAirport::findByIdent(const std::string& aIdent) { - FGPositionedRef r; + AirportCache::iterator it = airportCache.find(aIdent); + if (it != airportCache.end()) + return it->second; + PortsFilter filter; - r = FGPositioned::findNextWithPartialId(r, aIdent, &filter); - if (!r) { - return NULL; // we don't warn here, let the caller do that - } - return static_cast(r.ptr()); + FGAirport* r = static_cast (FGPositioned::findFirstWithIdent(aIdent, &filter).get()); + + // add airport to the cache (even when it's NULL, so we don't need to search in vain again) + airportCache[aIdent] = r; + + // we don't warn here when r==NULL, let the caller do that + return r; } FGAirport* FGAirport::getByIdent(const std::string& aIdent) { - FGPositionedRef r; - PortsFilter filter; - r = FGPositioned::findNextWithPartialId(r, aIdent, &filter); - if (!r) { + FGAirport* r = findByIdent(aIdent); + if (!r) throw sg_range_exception("No such airport with ident: " + aIdent); - } - return static_cast(r.ptr()); + return r; } char** FGAirport::searchNamesAndIdents(const std::string& aFilter) { - // we delegate all the work to a horrible helper in FGPositioned, which can - // access the (private) index data. - return searchAirportNamesAndIdents(aFilter); + return NavDataCache::instance()->searchAirportNamesAndIdents(aFilter); } // find basic airport location info from airport database @@ -373,8 +350,10 @@ void FGAirport::loadRunways() const return; // already loaded, great } - mRunwaysLoaded = true; loadSceneryDefinitions(); + + mRunwaysLoaded = true; + mRunways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::RUNWAY); } void FGAirport::loadTaxiways() const @@ -382,6 +361,9 @@ void FGAirport::loadTaxiways() const if (mTaxiwaysLoaded) { return; // already loaded, great } + + mTaxiwaysLoaded = true; + mTaxiways = flightgear::NavDataCache::instance()->airportItemsOfType(guid(), FGPositioned::TAXIWAY); } void FGAirport::loadProcedures() const @@ -398,30 +380,28 @@ void FGAirport::loadProcedures() const } SG_LOG(SG_GENERAL, SG_INFO, ident() << ": loading procedures from " << path.str()); - Route::loadAirportProcedures(path, const_cast(this)); + RouteBase::loadAirportProcedures(path, const_cast(this)); } void FGAirport::loadSceneryDefinitions() const -{ - // allow users to disable the scenery data in the short-term - // longer term, this option can probably disappear - if (!fgGetBool("/sim/paths/use-custom-scenery-data")) { - return; +{ + NavDataCache* cache = NavDataCache::instance(); + SGPath path; + if (!XMLLoader::findAirportData(ident(), "threshold", path)) { + return; // no XML threshold data } - SGPath path; - SGPropertyNode_ptr rootNode = new SGPropertyNode; - if (XMLLoader::findAirportData(ident(), "threshold", path)) { - readProperties(path.str(), rootNode); - const_cast(this)->readThresholdData(rootNode); + if (!cache->isCachedFileModified(path)) { + // cached values are correct, we're all done + return; } - // repeat for the tower data - rootNode = new SGPropertyNode; - if (XMLLoader::findAirportData(ident(), "twr", path)) { + flightgear::NavDataCache::Transaction txn(cache); + SGPropertyNode_ptr rootNode = new SGPropertyNode; readProperties(path.str(), rootNode); - const_cast(this)->readTowerData(rootNode); - } + const_cast(this)->readThresholdData(rootNode); + cache->stampCacheFile(path); + txn.commit(); } void FGAirport::readThresholdData(SGPropertyNode* aRoot) @@ -432,7 +412,7 @@ void FGAirport::readThresholdData(SGPropertyNode* aRoot) SGPropertyNode* t0 = runway->getChild("threshold", 0), *t1 = runway->getChild("threshold", 1); assert(t0); - assert(t1); // too strict? mayeb we should finally allow single-ended runways + assert(t1); // too strict? maybe we should finally allow single-ended runways processThreshold(t0); processThreshold(t1); @@ -442,15 +422,66 @@ void FGAirport::readThresholdData(SGPropertyNode* aRoot) void FGAirport::processThreshold(SGPropertyNode* aThreshold) { // first, let's identify the current runway - string id(aThreshold->getStringValue("rwy")); - if (!hasRunwayWithIdent(id)) { + string rwyIdent(aThreshold->getStringValue("rwy")); + NavDataCache* cache = NavDataCache::instance(); + PositionedID id = cache->airportItemWithIdent(guid(), FGPositioned::RUNWAY, rwyIdent); + if (id == 0) { SG_LOG(SG_GENERAL, SG_DEBUG, "FGAirport::processThreshold: " - "found runway not defined in the global data:" << ident() << "/" << id); + "found runway not defined in the global data:" << ident() << "/" << rwyIdent); return; } - FGRunway* rwy = getRunwayByIdent(id); - rwy->processThreshold(aThreshold); + double lon = aThreshold->getDoubleValue("lon"), + lat = aThreshold->getDoubleValue("lat"); + SGGeod newThreshold(SGGeod::fromDegM(lon, lat, mPosition.getElevationM())); + + double newHeading = aThreshold->getDoubleValue("hdg-deg"); + double newDisplacedThreshold = aThreshold->getDoubleValue("displ-m") * SG_METER_TO_FEET; + double newStopway = aThreshold->getDoubleValue("stopw-m") * SG_METER_TO_FEET; + + cache->updateRunwayThreshold(id, newThreshold, + newHeading, newDisplacedThreshold, newStopway); +} + +SGGeod FGAirport::getTowerLocation() const +{ + validateTowerData(); + + NavDataCache* cache = NavDataCache::instance(); + PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER); + if (towers.empty()) { + SG_LOG(SG_GENERAL, SG_ALERT, "No towers defined for:" <loadById(towers.front()); + return tower->geod(); +} + +void FGAirport::validateTowerData() const +{ + if (mTowerDataLoaded) { + return; + } + + mTowerDataLoaded = true; + NavDataCache* cache = NavDataCache::instance(); + SGPath path; + if (!XMLLoader::findAirportData(ident(), "twr", path)) { + return; // no XML tower data + } + + if (!cache->isCachedFileModified(path)) { + // cached values are correct, we're all done + return; + } + + flightgear::NavDataCache::Transaction txn(cache); + SGPropertyNode_ptr rootNode = new SGPropertyNode; + readProperties(path.str(), rootNode); + const_cast(this)->readTowerData(rootNode); + cache->stampCacheFile(path); + txn.commit(); } void FGAirport::readTowerData(SGPropertyNode* aRoot) @@ -463,132 +494,79 @@ void FGAirport::readTowerData(SGPropertyNode* aRoot) // scenery for a precise terrain elevation, we use the field elevation // (this is also what the apt.dat code does) double fieldElevationM = geod().getElevationM(); - - _tower_location = SGGeod::fromDegM(lon, lat, fieldElevationM + elevM); + SGGeod towerLocation(SGGeod::fromDegM(lon, lat, fieldElevationM + elevM)); + + NavDataCache* cache = NavDataCache::instance(); + PositionedIDVec towers = cache->airportItemsOfType(guid(), FGPositioned::TOWER); + if (towers.empty()) { + cache->insertTower(guid(), towerLocation); + } else { + // update the position + cache->updatePosition(towers.front(), towerLocation); + } } -bool FGAirport::buildApproach(Waypt* aEnroute, STAR* aSTAR, FGRunway* aRwy, WayptVec& aRoute) +bool FGAirport::validateILSData() { - loadProcedures(); - - if ((aRwy && (aRwy->airport() != this))) { - throw sg_exception("invalid parameters", "FGAirport::buildApproach"); - } - - if (aSTAR) { - bool ok = aSTAR->route(aRwy, aEnroute, aRoute); - if (!ok) { - SG_LOG(SG_GENERAL, SG_WARN, ident() << ": build approach, STAR " << aSTAR->ident() - << " failed to route from transition " << aEnroute->ident()); - return false; - } - } else if (aEnroute) { - // no a STAR specified, just use enroute point directly - aRoute.push_back(aEnroute); + if (mILSDataLoaded) { + return false; } - if (!aRwy) { - // no runway selected yet, but we loaded the STAR, so that's fine, we're done - return true; + mILSDataLoaded = true; + NavDataCache* cache = NavDataCache::instance(); + SGPath path; + if (!XMLLoader::findAirportData(ident(), "ils", path)) { + return false; // no XML tower data } -// build the approach (possibly including transition), and including the missed segment - vector aps; - for (unsigned int j=0; jrunway() == aRwy) { - aps.push_back(mApproaches[j]); - } - } // of approach filter by runway - - if (aps.empty()) { - SG_LOG(SG_GENERAL, SG_INFO, ident() << "; no approaches defined for runway " << aRwy->ident()); - // could build a fallback approach here + if (!cache->isCachedFileModified(path)) { + // cached values are correct, we're all done return false; } - for (unsigned int k=0; kroute(aRoute.back(), aRoute)) { - return true; - } - } // of initial approach iteration - - SG_LOG(SG_GENERAL, SG_INFO, ident() << ": unable to find transition to runway " - << aRwy->ident() << ", assume vectors"); - - WayptRef v(new ATCVectors(NULL, this)); - aRoute.push_back(v); - return aps.front()->routeFromVectors(aRoute); -} + SGPropertyNode_ptr rootNode = new SGPropertyNode; + readProperties(path.str(), rootNode); -pair -FGAirport::selectSID(const SGGeod& aDest, FGRunway* aRwy) -{ - loadProcedures(); - - WayptRef enroute; - flightgear::SID* sid = NULL; - double d = 1e9; - - for (unsigned int i=0; iisForRunway(aRwy)) { - continue; - } - - WayptRef e = mSIDs[i]->findBestTransition(aDest); - if (!e) { - continue; // strange, but let's not worry about it - } + flightgear::NavDataCache::Transaction txn(cache); + readILSData(rootNode); + cache->stampCacheFile(path); + txn.commit(); - // assert(e->isFixedPosition()); - double ed = SGGeodesy::distanceM(aDest, e->position()); - if (ed < d) { // new best match - enroute = e; - d = ed; - sid = mSIDs[i]; - } - } // of SID iteration - - if (!mSIDs.empty() && !sid) { - SG_LOG(SG_GENERAL, SG_INFO, ident() << "selectSID, no SID found (runway=" - << (aRwy ? aRwy->ident() : "no runway preference")); - } - - return std::make_pair(sid, enroute); -} - -pair -FGAirport::selectSTAR(const SGGeod& aOrigin, FGRunway* aRwy) -{ - loadProcedures(); - - WayptRef enroute; - STAR* star = NULL; - double d = 1e9; - - for (unsigned int i=0; iisForRunway(aRwy)) { - continue; - } - - SG_LOG(SG_GENERAL, SG_INFO, "STAR " << mSTARs[i]->ident() << " is valid for runway"); - WayptRef e = mSTARs[i]->findBestTransition(aOrigin); - if (!e) { - continue; // strange, but let's not worry about it - } - - // assert(e->isFixedPosition()); - double ed = SGGeodesy::distanceM(aOrigin, e->position()); - if (ed < d) { // new best match - enroute = e; - d = ed; - star = mSTARs[i]; - } - } // of STAR iteration - - return std::make_pair(star, enroute); +// we loaded data, tell the caller it might need to reload things + return true; +} + +void FGAirport::readILSData(SGPropertyNode* aRoot) +{ + NavDataCache* cache = NavDataCache::instance(); + + // find the entry matching the runway + SGPropertyNode* runwayNode, *ilsNode; + for (int i=0; (runwayNode = aRoot->getChild("runway", i)) != NULL; ++i) { + for (int j=0; (ilsNode = runwayNode->getChild("ils", j)) != NULL; ++j) { + // must match on both nav-ident and runway ident, to support the following: + // - runways with multiple distinct ILS installations (KEWD, for example) + // - runways where both ends share the same nav ident (LFAT, for example) + PositionedID ils = cache->findILS(guid(), ilsNode->getStringValue("rwy"), + ilsNode->getStringValue("nav-id")); + if (ils == 0) { + SG_LOG(SG_GENERAL, SG_INFO, "reading ILS data for " << ident() << + ", couldn;t find runway/navaid for:" << + ilsNode->getStringValue("rwy") << "/" << + ilsNode->getStringValue("nav-id")); + continue; + } + + double hdgDeg = ilsNode->getDoubleValue("hdg-deg"), + lon = ilsNode->getDoubleValue("lon"), + lat = ilsNode->getDoubleValue("lat"), + elevM = ilsNode->getDoubleValue("elev-m"); + + cache->updateILS(ils, SGGeod::fromDegM(lon, lat, elevM), hdgDeg); + } // of ILS iteration + } // of runway iteration } - void FGAirport::addSID(flightgear::SID* aSid) { mSIDs.push_back(aSid); @@ -664,59 +642,43 @@ Approach* FGAirport::getApproachByIndex(unsigned int aIndex) const return mApproaches[aIndex]; } -class AirportNodeListener : public SGPropertyChangeListener +Approach* FGAirport::findApproachWithIdent(const std::string& aIdent) const { -public: - AirportNodeListener() - { - SGPropertyNode* airports = fgGetNode("/sim/airport"); - airports->addChangeListener(this, false); - } - - virtual void valueChanged(SGPropertyNode*) - { - } - - virtual void childAdded(SGPropertyNode* pr, SGPropertyNode* child) - { - FGAirport* apt = FGAirport::findByIdent(child->getName()); - if (!apt) { - return; - } - - flightgear::PositionedBinding::bind(apt, child); + loadProcedures(); + for (unsigned int i=0; iident() == aIdent) { + return mApproaches[i]; } -}; - -void FGAirport::installPropertyListener() -{ - new AirportNodeListener; -} - -flightgear::PositionedBinding* -FGAirport::createBinding(SGPropertyNode* nd) const -{ - return new flightgear::AirportBinding(this, nd); + } + + return NULL; } -void FGAirport::setCommStations(CommStationList& comms) -{ - mCommStations.swap(comms); - for (unsigned int c=0; csetAirport(this); - } +CommStationList +FGAirport::commStations() const +{ + NavDataCache* cache = NavDataCache::instance(); + CommStationList result; + BOOST_FOREACH(PositionedID pos, cache->airportItemsOfType(guid(), + FGPositioned::FREQ_GROUND, + FGPositioned::FREQ_UNICOM)) + { + result.push_back((CommStation*) cache->loadById(pos)); + } + + return result; } CommStationList FGAirport::commStationsOfType(FGPositioned::Type aTy) const { - CommStationList result; - for (unsigned int c=0; ctype() == aTy) { - result.push_back(mCommStations[c]); - } - } - return result; + NavDataCache* cache = NavDataCache::instance(); + CommStationList result; + BOOST_FOREACH(PositionedID pos, cache->airportItemsOfType(guid(), aTy)) { + result.push_back((CommStation*) cache->loadById(pos)); + } + + return result; } // get airport elevation diff --git a/src/Airports/simple.hxx b/src/Airports/simple.hxx index 03d66a1..8604527 100644 --- a/src/Airports/simple.hxx +++ b/src/Airports/simple.hxx @@ -31,6 +31,7 @@ #include #include +#include #include @@ -40,10 +41,7 @@ class FGRunway; class FGTaxiway; class FGPavement; class SGPropertyNode; - -typedef SGSharedPtr FGRunwayPtr; -typedef SGSharedPtr FGTaxiwayPtr; -typedef SGSharedPtr FGPavementPtr; +class FGAirport; namespace flightgear { class SID; @@ -56,6 +54,7 @@ namespace flightgear { typedef std::vector WayptVec; typedef std::vector CommStationList; + typedef std::map AirportCache; } @@ -66,7 +65,7 @@ namespace flightgear { class FGAirport : public FGPositioned { public: - FGAirport(const std::string& id, const SGGeod& location, const SGGeod& tower, + FGAirport(PositionedID aGuid, const std::string& id, const SGGeod& location, const std::string& name, bool has_metar, Type aType); ~FGAirport(); @@ -82,10 +81,19 @@ public: bool isSeaport() const; bool isHeliport() const; + static bool isAirportType(FGPositioned* pos); + virtual const std::string& name() const { return _name; } - const SGGeod& getTowerLocation() const { return _tower_location; } + /** + * reload the ILS data from XML if required. + * @result true if the data was refreshed, false if no data was loaded + * or previously cached data is still correct. + */ + bool validateILSData(); + + SGGeod getTowerLocation() const; void setMetar(bool value) { _has_metar = value; } @@ -120,10 +128,6 @@ public: unsigned int numPavements() const; FGPavement* getPavementByIndex(unsigned int aIndex) const; - - void setRunwaysAndTaxiways(std::vector& rwys, - std::vector& txwys, - std::vector& pvts); class AirportFilter : public Filter { @@ -159,7 +163,7 @@ public: class HardSurfaceFilter : public AirportFilter { public: - HardSurfaceFilter(double minLengthFt); + HardSurfaceFilter(double minLengthFt = -1); virtual bool passAirport(FGAirport* aApt) const; @@ -186,9 +190,8 @@ public: unsigned int numApproaches() const; flightgear::Approach* getApproachByIndex(unsigned int aIndex) const; - - static void installPropertyListener(); - + flightgear::Approach* findApproachWithIdent(const std::string& aIdent) const; + /** * Syntactic wrapper around FGPositioned::findClosest - find the closest * match for filter, and return it cast to FGAirport. The default filter @@ -215,37 +218,12 @@ public: * matches in a format suitable for use by a puaList. */ static char** searchNamesAndIdents(const std::string& aFilter); - - bool buildApproach(flightgear::Waypt* aEnroute, flightgear::STAR* aSTAR, - FGRunway* aRwy, flightgear::WayptVec& aRoute); - - /** - * Given a destiation point, select the best SID and transition waypt from - * this airport. Returns (NULL,NULL) is no SIDs are defined, otherwise the - * best SID/transition is that which is closest to the destination point. - */ - std::pair selectSID(const SGGeod& aDest, FGRunway* aRwy); - - /** - * Select a STAR and enroute transition waypt, given an origin (departure) position. - * returns (NULL, NULL) is no suitable STAR is exists - */ - std::pair selectSTAR(const SGGeod& aOrigin, FGRunway* aRwy); - - virtual flightgear::PositionedBinding* createBinding(SGPropertyNode* nd) const; - - void setCommStations(flightgear::CommStationList& comms); - + flightgear::CommStationList commStationsOfType(FGPositioned::Type aTy) const; - const flightgear::CommStationList& commStations() const - { return mCommStations; } + flightgear::CommStationList commStations() const; private: - typedef std::vector::const_iterator Runway_iterator; - /** - * Helper to locate a runway by ident - */ - Runway_iterator getIteratorForRunwayIdent(const std::string& aIdent) const; + static flightgear::AirportCache airportCache; // disable these FGAirport operator=(FGAirport &other); @@ -261,13 +239,16 @@ private: */ void readThresholdData(SGPropertyNode* aRoot); void processThreshold(SGPropertyNode* aThreshold); + + void readILSData(SGPropertyNode* aRoot); + + void validateTowerData() const; /** * Helper to parse property data loaded from an ICAO.twr.xml filke */ void readTowerData(SGPropertyNode* aRoot); - SGGeod _tower_location; std::string _name; bool _has_metar; FGAirportDynamics *_dynamics; @@ -276,20 +257,24 @@ private: void loadTaxiways() const; void loadProcedures() const; + mutable bool mTowerDataLoaded; mutable bool mRunwaysLoaded; mutable bool mTaxiwaysLoaded; mutable bool mProceduresLoaded; + bool mILSDataLoaded; + + mutable PositionedIDVec mRunways; + mutable PositionedIDVec mTaxiways; + PositionedIDVec mPavements; - std::vector mRunways; - std::vector mTaxiways; - std::vector mPavements; - - std::vector mSIDs; - std::vector mSTARs; - std::vector mApproaches; + typedef SGSharedPtr SIDRef; + typedef SGSharedPtr STARRef; + typedef SGSharedPtr ApproachRef; - flightgear::CommStationList mCommStations; -}; + std::vector mSIDs; + std::vector mSTARs; + std::vector mApproaches; + }; // find basic airport location info from airport database const FGAirport *fgFindAirportID( const std::string& id); diff --git a/src/Airports/xmlloader.cxx b/src/Airports/xmlloader.cxx index b2ce07e..f30e169 100644 --- a/src/Airports/xmlloader.cxx +++ b/src/Airports/xmlloader.cxx @@ -20,6 +20,7 @@ #include #include #include +#include #include
#include
@@ -29,60 +30,52 @@ #include "runwayprefloader.hxx" #include "dynamics.hxx" +#include "simple.hxx" #include "runwayprefs.hxx" +#include + using std::string; XMLLoader::XMLLoader() {} XMLLoader::~XMLLoader() {} -void XMLLoader::load(FGAirportDynamics* d) { - FGAirportDynamicsXMLLoader visitor(d); - if (fgGetBool("/sim/paths/use-custom-scenery-data") == false) { - SGPath parkpath( globals->get_fg_root() ); - parkpath.append( "/AI/Airports/" ); - parkpath.append( d->getId() ); - parkpath.append( "parking.xml" ); - SG_LOG(SG_GENERAL, SG_DEBUG, "running old loader:" << parkpath.c_str()); - if (parkpath.exists()) { - try { - readXML(parkpath.str(), visitor); - d->init(); - } - catch (const sg_exception &) { - } - } - } else { - if(loadAirportXMLDataIntoVisitor(d->getId(), "groundnet", visitor)) { - d->init(); - } +void XMLLoader::load(FGAirportDynamics* d) +{ + SGPath path; + if (!findAirportData(d->parent()->ident(), "groundnet", path)) { + return; } -} -void XMLLoader::load(FGRunwayPreference* p) { - FGRunwayPreferenceXMLLoader visitor(p); - if (fgGetBool("/sim/paths/use-custom-scenery-data") == false) { - SGPath rwyPrefPath( globals->get_fg_root() ); - rwyPrefPath.append( "AI/Airports/" ); - rwyPrefPath.append( p->getId() ); - rwyPrefPath.append( "rwyuse.xml" ); - if (rwyPrefPath.exists()) { - try { - readXML(rwyPrefPath.str(), visitor); - } - catch (const sg_exception &) { - } - } - } else { - loadAirportXMLDataIntoVisitor(p->getId(), "rwyuse", visitor); + flightgear::NavDataCache* cache = flightgear::NavDataCache::instance(); + if (!cache->isCachedFileModified(path)) { + return; } + + SG_LOG(SG_NAVAID, SG_INFO, "reading groundnet data from " << path); + SGTimeStamp t; + try { + flightgear::NavDataCache::Transaction txn(cache); + t.stamp(); + { + // drop all current data + cache->dropGroundnetFor(d->parent()->guid()); + + FGAirportDynamicsXMLLoader visitor(d); + readXML(path.str(), visitor); + } // ensure visitor is destroyed so its destructor runs + cache->stampCacheFile(path); + txn.commit(); + } catch (sg_exception& e) { + SG_LOG(SG_NAVAID, SG_INFO, "parsing groundnet XML failed:" << e.getFormattedMessage()); + } + + SG_LOG(SG_NAVAID, SG_INFO, "parsing groundnet XML took " << t.elapsedMSec()); } -void XMLLoader::load(FGSidStar* p) { - SGPath path; - if (findAirportData(p->getId(), "SID", path)) { - p->load(path); - } +void XMLLoader::load(FGRunwayPreference* p) { + FGRunwayPreferenceXMLLoader visitor(p); + loadAirportXMLDataIntoVisitor(p->getId(), "rwyuse", visitor); } bool XMLLoader::findAirportData(const std::string& aICAO, @@ -119,7 +112,7 @@ bool XMLLoader::loadAirportXMLDataIntoVisitor(const string& aICAO, { SGPath path; if (!findAirportData(aICAO, aFileName, path)) { - SG_LOG(SG_GENERAL, SG_DEBUG, "loadAirportXMLDataIntoVisitor: failed to find data for " << aICAO << "/" << aFileName); + SG_LOG(SG_NAVAID, SG_DEBUG, "loadAirportXMLDataIntoVisitor: failed to find data for " << aICAO << "/" << aFileName); return false; } diff --git a/src/Autopilot/analogcomponent.hxx b/src/Autopilot/analogcomponent.hxx index ccbd099..fc658be 100644 --- a/src/Autopilot/analogcomponent.hxx +++ b/src/Autopilot/analogcomponent.hxx @@ -133,6 +133,9 @@ protected: it != _output_list.end(); ++it) (*it)->setDoubleValue( value ); } + +public: + const PeriodicalValue * getPeriodicalValue() const { return _periodical; } }; inline void AnalogComponent::disabled( double dt ) diff --git a/src/Autopilot/autopilot.cxx b/src/Autopilot/autopilot.cxx index be3a4f0..d6e87eb 100644 --- a/src/Autopilot/autopilot.cxx +++ b/src/Autopilot/autopilot.cxx @@ -59,13 +59,15 @@ Autopilot::Autopilot( SGPropertyNode_ptr rootNode, SGPropertyNode_ptr configNode _serviceable(true), _rootNode(rootNode) { - - componentForge["pid-controller"] = new CreateAndConfigureFunctor(); - componentForge["pi-simple-controller"] = new CreateAndConfigureFunctor(); - componentForge["predict-simple"] = new CreateAndConfigureFunctor(); - componentForge["filter"] = new CreateAndConfigureFunctor(); - componentForge["logic"] = new CreateAndConfigureFunctor(); - componentForge["flipflop"] = new CreateAndConfigureFunctor(); + if (componentForge.empty()) + { + componentForge["pid-controller"] = new CreateAndConfigureFunctor(); + componentForge["pi-simple-controller"] = new CreateAndConfigureFunctor(); + componentForge["predict-simple"] = new CreateAndConfigureFunctor(); + componentForge["filter"] = new CreateAndConfigureFunctor(); + componentForge["logic"] = new CreateAndConfigureFunctor(); + componentForge["flipflop"] = new CreateAndConfigureFunctor(); + } if( configNode == NULL ) configNode = rootNode; @@ -85,8 +87,10 @@ Autopilot::Autopilot( SGPropertyNode_ptr rootNode, SGPropertyNode_ptr configNode component->set_name( buf.str() ); } - SG_LOG( SG_AUTOPILOT, SG_INFO, "adding autopilot component \"" << childName << "\" as \"" << component->get_name() << "\"" ); - add_component(component); + double updateInterval = node->getDoubleValue( "update-interval-secs", 0.0 ); + + SG_LOG( SG_AUTOPILOT, SG_DEBUG, "adding autopilot component \"" << childName << "\" as \"" << component->get_name() << "\" with interval=" << updateInterval ); + add_component(component,updateInterval); } } @@ -105,7 +109,7 @@ void Autopilot::unbind() _rootNode->untie( "serviceable" ); } -void Autopilot::add_component( Component * component ) +void Autopilot::add_component( Component * component, double updateInterval ) { if( component == NULL ) return; @@ -119,7 +123,7 @@ void Autopilot::add_component( Component * component ) if( name != component->get_name() ) SG_LOG( SG_ALL, SG_WARN, "Duplicate autopilot component " << component->get_name() << ", renamed to " << name ); - set_subsystem( name.c_str(), component ); + set_subsystem( name.c_str(), component, updateInterval ); } void Autopilot::update( double dt ) diff --git a/src/Autopilot/autopilot.hxx b/src/Autopilot/autopilot.hxx index ccda272..b24977e 100644 --- a/src/Autopilot/autopilot.hxx +++ b/src/Autopilot/autopilot.hxx @@ -54,7 +54,7 @@ public: std::string get_name() const { return _name; } void set_name( const std::string & name ) { _name = name; } - void add_component( Component * component ); + void add_component( Component * component, double updateInterval ); protected: diff --git a/src/Autopilot/autopilotgroup.cxx b/src/Autopilot/autopilotgroup.cxx index a856c34..c60ea30 100644 --- a/src/Autopilot/autopilotgroup.cxx +++ b/src/Autopilot/autopilotgroup.cxx @@ -44,14 +44,18 @@ using simgear::PropertyList; class FGXMLAutopilotGroupImplementation : public FGXMLAutopilotGroup { public: + FGXMLAutopilotGroupImplementation(const std::string& nodeName) : + FGXMLAutopilotGroup(), _nodeName(nodeName) {} virtual void addAutopilot( const std::string & name, SGPropertyNode_ptr apNode, SGPropertyNode_ptr config ); virtual void removeAutopilot( const std::string & name ); void init(); + InitStatus incrementalInit(); void reinit(); void update( double dt ); private: void initFrom( SGPropertyNode_ptr rootNode, const char * childName ); vector _autopilotNames; + std::string _nodeName; }; @@ -65,7 +69,9 @@ void FGXMLAutopilotGroupImplementation::addAutopilot( const std::string & name, } FGXMLAutopilot::Autopilot * ap = new FGXMLAutopilot::Autopilot( apNode, config ); ap->set_name( name ); - set_subsystem( name, ap ); + + double updateInterval = config->getDoubleValue( "update-interval-secs", 0.0 ); + set_subsystem( name, ap, updateInterval ); _autopilotNames.push_back( name ); } @@ -96,14 +102,15 @@ void FGXMLAutopilotGroupImplementation::reinit() init(); } +SGSubsystem::InitStatus FGXMLAutopilotGroupImplementation::incrementalInit() +{ + init(); + return INIT_DONE; +} + void FGXMLAutopilotGroupImplementation::init() { - static const char * nodeNames[] = { - "autopilot", - "property-rule" - }; - for( unsigned i = 0; i < sizeof(nodeNames)/sizeof(nodeNames[0]); i++ ) - initFrom( fgGetNode( "/sim/systems" ), nodeNames[i] ); + initFrom( fgGetNode( "/sim/systems" ), _nodeName.c_str() ); SGSubsystemGroup::bind(); SGSubsystemGroup::init(); @@ -171,7 +178,7 @@ void FGXMLAutopilotGroup::addAutopilotFromFile( const std::string & name, SGProp } } -FGXMLAutopilotGroup * FGXMLAutopilotGroup::createInstance() +FGXMLAutopilotGroup * FGXMLAutopilotGroup::createInstance(const std::string& nodeName) { - return new FGXMLAutopilotGroupImplementation(); + return new FGXMLAutopilotGroupImplementation(nodeName); } diff --git a/src/Autopilot/autopilotgroup.hxx b/src/Autopilot/autopilotgroup.hxx index 6e6c1f1..5390f8c 100644 --- a/src/Autopilot/autopilotgroup.hxx +++ b/src/Autopilot/autopilotgroup.hxx @@ -31,7 +31,7 @@ class FGXMLAutopilotGroup : public SGSubsystemGroup { public: - static FGXMLAutopilotGroup * createInstance(); + static FGXMLAutopilotGroup * createInstance(const std::string& nodeName); void addAutopilotFromFile( const std::string & name, SGPropertyNode_ptr apNode, const char * path ); virtual void addAutopilot( const std::string & name, SGPropertyNode_ptr apNode, SGPropertyNode_ptr config ) = 0; virtual void removeAutopilot( const std::string & name ) = 0; diff --git a/src/Autopilot/component.cxx b/src/Autopilot/component.cxx index 1da236c..f1c2894 100644 --- a/src/Autopilot/component.cxx +++ b/src/Autopilot/component.cxx @@ -46,7 +46,7 @@ bool Component::configure( SGPropertyNode_ptr configNode ) SGPropertyNode_ptr prop; SGPropertyNode_ptr child = configNode->getChild(i); - string cname(child->getName()); + std::string cname(child->getName()); if( configure( cname, child ) ) continue; @@ -87,7 +87,7 @@ bool Component::configure( const std::string & nodeName, SGPropertyNode_ptr conf if ( (prop = configNode->getChild( "value" )) != NULL ) { delete _enable_value; - _enable_value = new string(prop->getStringValue()); + _enable_value = new std::string(prop->getStringValue()); } if ( (prop = configNode->getChild( "honor-passive" )) != NULL ) { diff --git a/src/Autopilot/digitalfilter.cxx b/src/Autopilot/digitalfilter.cxx index 6f83b01..d0e4b94 100644 --- a/src/Autopilot/digitalfilter.cxx +++ b/src/Autopilot/digitalfilter.cxx @@ -32,6 +32,26 @@ using std::cout; namespace FGXMLAutopilot { +/** + * + * + */ +class DigitalFilterImplementation : public SGReferenced { +protected: + virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode) = 0; +public: + virtual ~DigitalFilterImplementation() {} + DigitalFilterImplementation(); + virtual void initialize( double initvalue ) {} + virtual double compute( double dt, double input ) = 0; + bool configure( SGPropertyNode_ptr configNode ); + + void setDigitalFilter( DigitalFilter * digitalFilter ) { _digitalFilter = digitalFilter; } + +protected: + DigitalFilter * _digitalFilter; +}; + /* --------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------- */ class GainFilterImplementation : public DigitalFilterImplementation { @@ -55,6 +75,7 @@ class DerivativeFilterImplementation : public GainFilterImplementation { public: DerivativeFilterImplementation(); double compute( double dt, double input ); + virtual void initialize( double initvalue ); }; class ExponentialFilterImplementation : public GainFilterImplementation { @@ -66,7 +87,7 @@ protected: public: ExponentialFilterImplementation(); double compute( double dt, double input ); - virtual void initialize( double output ); + virtual void initialize( double initvalue ); }; class MovingAverageFilterImplementation : public DigitalFilterImplementation { @@ -78,7 +99,7 @@ protected: public: MovingAverageFilterImplementation(); double compute( double dt, double input ); - virtual void initialize( double output ); + virtual void initialize( double initvalue ); }; class NoiseSpikeFilterImplementation : public DigitalFilterImplementation { @@ -89,7 +110,7 @@ protected: public: NoiseSpikeFilterImplementation(); double compute( double dt, double input ); - virtual void initialize( double output ); + virtual void initialize( double initvalue ); }; /* --------------------------------------------------------------------------------- */ @@ -101,6 +122,10 @@ using namespace FGXMLAutopilot; /* --------------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------------- */ +DigitalFilterImplementation::DigitalFilterImplementation() : + _digitalFilter(NULL) +{ +} bool DigitalFilterImplementation::configure( SGPropertyNode_ptr configNode ) { @@ -156,6 +181,12 @@ DerivativeFilterImplementation::DerivativeFilterImplementation() : { } +void DerivativeFilterImplementation::initialize( double initvalue ) +{ + _input_1 = initvalue; +} + + bool DerivativeFilterImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode ) { if( GainFilterImplementation::configure( nodeName, configNode ) ) @@ -185,9 +216,9 @@ MovingAverageFilterImplementation::MovingAverageFilterImplementation() : { } -void MovingAverageFilterImplementation::initialize( double output ) +void MovingAverageFilterImplementation::initialize( double initvalue ) { - _output_1 = output; + _output_1 = initvalue; } double MovingAverageFilterImplementation::compute( double dt, double input ) @@ -220,26 +251,24 @@ NoiseSpikeFilterImplementation::NoiseSpikeFilterImplementation() : { } -void NoiseSpikeFilterImplementation::initialize( double output ) +void NoiseSpikeFilterImplementation::initialize( double initvalue ) { - _output_1 = output; + _output_1 = initvalue; } double NoiseSpikeFilterImplementation::compute( double dt, double input ) { - double maxChange = _rateOfChangeInput.get_value() * dt; + double delta = input - _output_1; + if( fabs(delta) <= SGLimitsd::min() ) return input; // trivial - double output_0 = _output_1; + double maxChange = _rateOfChangeInput.get_value() * dt; + const PeriodicalValue * periodical = _digitalFilter->getPeriodicalValue(); + if( periodical ) delta = periodical->normalizeSymmetric( delta ); - if (_output_1 - input > maxChange) { - output_0 = _output_1 - maxChange; - } else if( _output_1 - input < -maxChange ) { - output_0 = _output_1 + maxChange; - } else if (fabs(input - _output_1) <= maxChange) { - output_0 = input; - } - _output_1 = output_0; - return output_0; + if( fabs(delta) <= maxChange ) + return (_output_1 = input); + else + return (_output_1 = _output_1 + copysign( maxChange, delta )); } bool NoiseSpikeFilterImplementation::configure( const std::string & nodeName, SGPropertyNode_ptr configNode ) @@ -262,9 +291,9 @@ ExponentialFilterImplementation::ExponentialFilterImplementation() { } -void ExponentialFilterImplementation::initialize( double output ) +void ExponentialFilterImplementation::initialize( double initvalue ) { - output_1 = output_2 = output; + output_1 = output_2 = initvalue; } double ExponentialFilterImplementation::compute( double dt, double input ) @@ -318,6 +347,11 @@ DigitalFilter::DigitalFilter() : { } +DigitalFilter::~DigitalFilter() +{ +} + + static map *> componentForge; bool DigitalFilter::configure(const string& nodeName, SGPropertyNode_ptr configNode) @@ -343,6 +377,7 @@ bool DigitalFilter::configure(const string& nodeName, SGPropertyNode_ptr configN return true; } _implementation = (*componentForge[type])( configNode->getParent() ); + _implementation->setDigitalFilter( this ); return true; } diff --git a/src/Autopilot/digitalfilter.hxx b/src/Autopilot/digitalfilter.hxx index d9b0ac4..3b7afab 100644 --- a/src/Autopilot/digitalfilter.hxx +++ b/src/Autopilot/digitalfilter.hxx @@ -32,26 +32,13 @@ namespace FGXMLAutopilot { /** - * - * - */ -class DigitalFilterImplementation : public SGReferenced { -protected: - virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode) = 0; -public: - virtual void initialize( double output ) {} - virtual double compute( double dt, double input ) = 0; - bool configure( SGPropertyNode_ptr configNode ); -}; - -/** * brief@ DigitalFilter - a selection of digital filters * */ class DigitalFilter : public AnalogComponent { private: - SGSharedPtr _implementation; + SGSharedPtr _implementation; enum InitializeTo { INITIALIZE_OUTPUT, @@ -71,7 +58,7 @@ protected: public: DigitalFilter(); - ~DigitalFilter() {} + ~DigitalFilter(); }; diff --git a/src/Autopilot/flipflop.cxx b/src/Autopilot/flipflop.cxx index 8046ce5..b22adfa 100644 --- a/src/Autopilot/flipflop.cxx +++ b/src/Autopilot/flipflop.cxx @@ -272,7 +272,7 @@ public: * @brief constructor for a MonoFlopImplementation * @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false) */ - MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ) {} + MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ), _t(0.0) {} /** * @brief evaluates the output state from the input lines and returns to the stable state * after expiry of the internal timer @@ -464,7 +464,7 @@ void FlipFlop::update( bool firstTime, double dt ) if(_debug) { cout << "updating flip-flop \"" << get_name() << "\"" << endl; cout << "prev. Output:" << q0 << endl; - for( InputMap::const_iterator it = _input.begin(); it != _input.end(); it++ ) + for( InputMap::const_iterator it = _input.begin(); it != _input.end(); ++it ) cout << "Input \"" << (*it).first << "\":" << (*it).second->test() << endl; cout << "new Output:" << q << endl; } diff --git a/src/Autopilot/flipflop.hxx b/src/Autopilot/flipflop.hxx index dfdb382..e81c551 100644 --- a/src/Autopilot/flipflop.hxx +++ b/src/Autopilot/flipflop.hxx @@ -38,6 +38,7 @@ protected: */ virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr configNode ) { return false; } public: + virtual ~FlipFlopImplementation() {} /** * @brief evaluates the output state from the input lines * @param dt the elapsed time in seconds from since the last call diff --git a/src/Autopilot/functor.hxx b/src/Autopilot/functor.hxx index 2f1ac36..4b1fda6 100644 --- a/src/Autopilot/functor.hxx +++ b/src/Autopilot/functor.hxx @@ -31,6 +31,7 @@ namespace FGXMLAutopilot { template class FunctorBase { public: + virtual ~FunctorBase() {} virtual TBase * operator()( SGPropertyNode_ptr configNode ) = 0; }; diff --git a/src/Autopilot/inputvalue.cxx b/src/Autopilot/inputvalue.cxx index cbda665..331aede 100644 --- a/src/Autopilot/inputvalue.cxx +++ b/src/Autopilot/inputvalue.cxx @@ -18,8 +18,11 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // +#include + #include "inputvalue.hxx" #include
+ using namespace FGXMLAutopilot; PeriodicalValue::PeriodicalValue( SGPropertyNode_ptr root ) @@ -34,11 +37,21 @@ PeriodicalValue::PeriodicalValue( SGPropertyNode_ptr root ) } } -double PeriodicalValue::normalize( double value ) +double PeriodicalValue::normalize( double value ) const { return SGMiscd::normalizePeriodic( minPeriod->get_value(), maxPeriod->get_value(), value ); } +double PeriodicalValue::normalizeSymmetric( double value ) const +{ + double minValue = minPeriod->get_value(); + double maxValue = maxPeriod->get_value(); + + value = SGMiscd::normalizePeriodic( minValue, maxValue, value ); + double width_2 = (maxValue - minValue)/2; + return value > width_2 ? width_2 - value : value; +} + InputValue::InputValue( SGPropertyNode_ptr node, double value, double offset, double scale) : _value(0.0), _abs(false) diff --git a/src/Autopilot/inputvalue.hxx b/src/Autopilot/inputvalue.hxx index bdf0b63..c85bfd7 100644 --- a/src/Autopilot/inputvalue.hxx +++ b/src/Autopilot/inputvalue.hxx @@ -45,7 +45,8 @@ private: InputValue_ptr maxPeriod; // The maximum value of the period public: PeriodicalValue( SGPropertyNode_ptr node ); - double normalize( double value ); + double normalize( double value ) const; + double normalizeSymmetric( double value ) const; }; /** diff --git a/src/Autopilot/logic.cxx b/src/Autopilot/logic.cxx index 8327042..6f8a530 100644 --- a/src/Autopilot/logic.cxx +++ b/src/Autopilot/logic.cxx @@ -24,7 +24,6 @@ #ifndef HAVE_CONFIG_H # include #endif -#include #endif #include "logic.hxx" @@ -45,7 +44,7 @@ void Logic::set_output( bool value ) if( _inverted ) value = !value; // set all outputs to the given value - for( OutputMap::iterator it = _output.begin(); it != _output.end(); it++ ) + for( OutputMap::iterator it = _output.begin(); it != _output.end(); ++it ) (*it).second->setValue( value ); } diff --git a/src/Autopilot/pidcontroller.cxx b/src/Autopilot/pidcontroller.cxx index c3631a2..d41608c 100644 --- a/src/Autopilot/pidcontroller.cxx +++ b/src/Autopilot/pidcontroller.cxx @@ -96,7 +96,6 @@ PIDController::PIDController(): void PIDController::update( bool firstTime, double dt ) { double edf_n = 0.0; - double delta_u_n = 0.0; // incremental output double u_n = 0.0; // absolute output double u_min = _minInput.get_value(); @@ -159,6 +158,7 @@ void PIDController::update( bool firstTime, double dt ) // Calculates the incremental output: double ti = Ti.get_value(); + double delta_u_n = 0.0; // incremental output if ( ti > 0.0 ) { delta_u_n = Kp.get_value() * ( (ep_n - ep_n_1) + ((Ts/ti) * e_n) diff --git a/src/Autopilot/route_mgr.cxx b/src/Autopilot/route_mgr.cxx index 1c4fa52..337f2e9 100644 --- a/src/Autopilot/route_mgr.cxx +++ b/src/Autopilot/route_mgr.cxx @@ -37,78 +37,27 @@ #include #include +#include #include #include #include -#include - -#include #include -#include #include #include "Main/fg_props.hxx" #include "Navaids/positioned.hxx" #include -#include #include #include "Airports/simple.hxx" #include "Airports/runways.hxx" - -#define RM "/autopilot/route-manager/" - #include #include -using namespace flightgear; - -class PropertyWatcher : public SGPropertyChangeListener -{ -public: - void watch(SGPropertyNode* p) - { - p->addChangeListener(this, false); - } - - virtual void valueChanged(SGPropertyNode*) - { - fire(); - } -protected: - virtual void fire() = 0; -}; - -/** - * Template adapter, created by convenience helper below - */ -template -class MethodPropertyWatcher : public PropertyWatcher -{ -public: - typedef void (T::*fire_method)(); - - MethodPropertyWatcher(T* obj, fire_method m) : - _object(obj), - _method(m) - { ; } - -protected: - virtual void fire() - { // dispatch to the object method we're helping - (_object->*_method)(); - } - -private: - T* _object; - fire_method _method; -}; +#define RM "/autopilot/route-manager/" -template -PropertyWatcher* createWatcher(T* obj, void (T::*m)()) -{ - return new MethodPropertyWatcher(obj, m); -} +using namespace flightgear; +using std::string; static bool commandLoadFlightPlan(const SGPropertyNode* arg) { @@ -131,7 +80,7 @@ static bool commandActivateFlightPlan(const SGPropertyNode* arg) if (activate) { self->activate(); } else { - + self->deactivate(); } return true; @@ -148,7 +97,7 @@ static bool commandSetActiveWaypt(const SGPropertyNode* arg) { FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); int index = arg->getIntValue("index"); - if ((index < 0) || (index >= self->numWaypts())) { + if ((index < 0) || (index >= self->numLegs())) { return false; } @@ -232,16 +181,16 @@ static bool commandInsertWaypt(const SGPropertyNode* arg) } else { return false; // failed to build waypoint } - + + FlightPlan::Leg* leg = self->flightPlan()->insertWayptAtIndex(wp, index); if (alt >= 0) { - wp->setAltitude(alt, flightgear::RESTRICT_AT); + leg->setAltitude(RESTRICT_AT, alt); } if (ias > 0) { - wp->setSpeed(ias, flightgear::RESTRICT_AT); + leg->setSpeed(RESTRICT_AT, ias); } - - self->insertWayptAtIndex(wp, index); + return true; } @@ -249,14 +198,14 @@ static bool commandDeleteWaypt(const SGPropertyNode* arg) { FGRouteMgr* self = (FGRouteMgr*) globals->get_subsystem("route-manager"); int index = arg->getIntValue("index"); - self->removeWayptAtIndex(index); + self->removeLegAtIndex(index); return true; } ///////////////////////////////////////////////////////////////////////////// FGRouteMgr::FGRouteMgr() : - _currentIndex(0), + _plan(NULL), input(fgGetNode( RM "input", true )), mirror(fgGetNode( RM "route", true )) { @@ -284,23 +233,23 @@ FGRouteMgr::~FGRouteMgr() void FGRouteMgr::init() { SGPropertyNode_ptr rm(fgGetNode(RM)); - lon = fgGetNode( "/position/longitude-deg", true ); - lat = fgGetNode( "/position/latitude-deg", true ); - alt = fgGetNode( "/position/altitude-ft", true ); magvar = fgGetNode("/environment/magnetic-variation-deg", true); departure = fgGetNode(RM "departure", true); departure->tie("airport", SGRawValueMethods(*this, &FGRouteMgr::getDepartureICAO, &FGRouteMgr::setDepartureICAO)); + departure->tie("runway", SGRawValueMethods(*this, + &FGRouteMgr::getDepartureRunway, + &FGRouteMgr::setDepartureRunway)); + departure->tie("sid", SGRawValueMethods(*this, + &FGRouteMgr::getSID, + &FGRouteMgr::setSID)); + departure->tie("name", SGRawValueMethods(*this, &FGRouteMgr::getDepartureName, NULL)); - departure->setStringValue("runway", ""); - - _departureWatcher = createWatcher(this, &FGRouteMgr::departureChanged); - _departureWatcher->watch(departure->getChild("runway")); - + departure->tie("field-elevation-ft", SGRawValueMethods(*this, + &FGRouteMgr::getDestinationFieldElevation, NULL)); departure->getChild("etd", 0, true); - _departureWatcher->watch(departure->getChild("sid", 0, true)); departure->getChild("takeoff-time", 0, true); destination = fgGetNode(RM "destination", true); @@ -308,15 +257,22 @@ void FGRouteMgr::init() { destination->tie("airport", SGRawValueMethods(*this, &FGRouteMgr::getDestinationICAO, &FGRouteMgr::setDestinationICAO)); + destination->tie("runway", SGRawValueMethods(*this, + &FGRouteMgr::getDestinationRunway, + &FGRouteMgr::setDestinationRunway)); + destination->tie("star", SGRawValueMethods(*this, + &FGRouteMgr::getSTAR, + &FGRouteMgr::setSTAR)); + destination->tie("approach", SGRawValueMethods(*this, + &FGRouteMgr::getApproach, + &FGRouteMgr::setApproach)); + destination->tie("name", SGRawValueMethods(*this, &FGRouteMgr::getDestinationName, NULL)); - - _arrivalWatcher = createWatcher(this, &FGRouteMgr::arrivalChanged); - _arrivalWatcher->watch(destination->getChild("runway", 0, true)); + destination->tie("field-elevation-ft", SGRawValueMethods(*this, + &FGRouteMgr::getDestinationFieldElevation, NULL)); destination->getChild("eta", 0, true); - _arrivalWatcher->watch(destination->getChild("star", 0, true)); - _arrivalWatcher->watch(destination->getChild("transition", 0, true)); destination->getChild("touchdown-time", 0, true); alternate = fgGetNode(RM "alternate", true); @@ -330,11 +286,10 @@ void FGRouteMgr::init() { cruise->getChild("speed-kts", 0, true); cruise->setDoubleValue("speed-kts", 160.0); - _routingType = cruise->getChild("routing", 0, true); - _routingType->setIntValue(ROUTE_HIGH_AIRWAYS); - totalDistance = fgGetNode(RM "total-distance", true); totalDistance->setDoubleValue(0.0); + distanceToGo = fgGetNode(RM "distance-remaining-nm", true); + distanceToGo->setDoubleValue(0.0); ete = fgGetNode(RM "ete", true); ete->setDoubleValue(0.0); @@ -350,6 +305,7 @@ void FGRouteMgr::init() { _edited = fgGetNode(RM "signals/edited", true); _finished = fgGetNode(RM "signals/finished", true); + _flightplanChanged = fgGetNode(RM "signals/flightplan-changed", true); _currentWpt = fgGetNode(RM "current-wp", true); _currentWpt->tie(SGRawValueMethods @@ -371,13 +327,14 @@ void FGRouteMgr::init() { wpn->getChild("dist", 0, true); wpn->getChild("eta", 0, true); - update_mirror(); _pathNode = fgGetNode(RM "file-path", 0, true); } void FGRouteMgr::postinit() { + setFlightPlan(new FlightPlan()); + SGPath path(_pathNode->getStringValue()); if (!path.isNull()) { SG_LOG(SG_AUTOPILOT, SG_INFO, "loading flight-plan from: " << path.str()); @@ -393,15 +350,17 @@ void FGRouteMgr::postinit() for (it = waypoints->begin(); it != waypoints->end(); ++it) { WayptRef w = waypointFromString(*it); if (w) { - _route.push_back(w); + _plan->insertWayptAtIndex(w, -1); } } - SG_LOG(SG_AUTOPILOT, SG_INFO, "loaded initial waypoints:" << _route.size()); + SG_LOG(SG_AUTOPILOT, SG_INFO, "loaded initial waypoints:" << numLegs()); + update_mirror(); } - update_mirror(); weightOnWheels = fgGetNode("/gear/gear[0]/wow", true); + groundSpeed = fgGetNode("/velocities/groundspeed-kt", true); + // check airbone flag agrees with presets } @@ -413,581 +372,249 @@ bool FGRouteMgr::isRouteActive() const return active->getBoolValue(); } +bool FGRouteMgr::saveRoute(const SGPath& p) +{ + if (!_plan) { + return false; + } + + return _plan->save(p); +} + +bool FGRouteMgr::loadRoute(const SGPath& p) +{ + FlightPlan* fp = new FlightPlan; + if (!fp->load(p)) { + delete fp; + return false; + } + + setFlightPlan(fp); + return true; +} + +FlightPlan* FGRouteMgr::flightPlan() const +{ + return _plan; +} + +void FGRouteMgr::setFlightPlan(FlightPlan* plan) +{ + if (plan == _plan) { + return; + } + + if (_plan) { + _plan->removeDelegate(this); + delete _plan; + active->setBoolValue(false); + } + + _plan = plan; + _plan->addDelegate(this); + + _flightplanChanged->fireValueChanged(); + +// fire all the callbacks! + departureChanged(); + arrivalChanged(); + waypointsChanged(); + currentWaypointChanged(); +} + void FGRouteMgr::update( double dt ) { if (dt <= 0.0) { return; // paused, nothing to do here } - double groundSpeed = fgGetDouble("/velocities/groundspeed-kt", 0.0); + double gs = groundSpeed->getDoubleValue(); if (airborne->getBoolValue()) { time_t now = time(NULL); elapsedFlightTime->setDoubleValue(difftime(now, _takeoffTime)); + + if (weightOnWheels->getBoolValue()) { + // touch down + destination->setIntValue("touchdown-time", time(NULL)); + airborne->setBoolValue(false); + } } else { // not airborne - if (weightOnWheels->getBoolValue() || (groundSpeed < 40)) { - return; + if (weightOnWheels->getBoolValue() || (gs < 40)) { + // either taking-off or rolling-out after touchdown + } else { + airborne->setBoolValue(true); + _takeoffTime = time(NULL); // start the clock + departure->setIntValue("takeoff-time", _takeoffTime); } - - airborne->setBoolValue(true); - _takeoffTime = time(NULL); // start the clock - departure->setIntValue("takeoff-time", _takeoffTime); } if (!active->getBoolValue()) { return; } + if (checkFinished()) { + endOfRoute(); + } + // basic course/distance information - SGGeod currentPos = SGGeod::fromDegFt(lon->getDoubleValue(), - lat->getDoubleValue(),alt->getDoubleValue()); + SGGeod currentPos = globals->get_aircraft_position(); - Waypt* curWpt = currentWaypt(); - if (!curWpt) { + FlightPlan::Leg* leg = _plan ? _plan->currentLeg() : NULL; + if (!leg) { return; } double courseDeg; double distanceM; - boost::tie(courseDeg, distanceM) = curWpt->courseAndDistanceFrom(currentPos); + boost::tie(courseDeg, distanceM) = leg->waypoint()->courseAndDistanceFrom(currentPos); -// update wp0 / wp1 / wp-last for legacy users +// update wp0 / wp1 / wp-last wp0->setDoubleValue("dist", distanceM * SG_METER_TO_NM); + wp0->setDoubleValue("true-bearing-deg", courseDeg); courseDeg -= magvar->getDoubleValue(); // expose magnetic bearing wp0->setDoubleValue("bearing-deg", courseDeg); setETAPropertyFromDistance(wp0->getChild("eta"), distanceM); - double totalDistanceRemaining = distanceM; // distance to current waypoint + double totalPathDistanceNm = _plan->totalDistanceNm(); + double totalDistanceRemaining = distanceM * SG_METER_TO_NM; // distance to current waypoint + +// total distance to go, is direct distance to wp0, plus the remaining +// path distance from wp0 + totalDistanceRemaining += (totalPathDistanceNm - leg->distanceAlongRoute()); + + wp0->setDoubleValue("distance-along-route-nm", + leg->distanceAlongRoute()); + wp0->setDoubleValue("remaining-distance-nm", + totalPathDistanceNm - leg->distanceAlongRoute()); - Waypt* nextWpt = nextWaypt(); - if (nextWpt) { - boost::tie(courseDeg, distanceM) = nextWpt->courseAndDistanceFrom(currentPos); + FlightPlan::Leg* nextLeg = _plan->nextLeg(); + if (nextLeg) { + boost::tie(courseDeg, distanceM) = nextLeg->waypoint()->courseAndDistanceFrom(currentPos); wp1->setDoubleValue("dist", distanceM * SG_METER_TO_NM); + wp1->setDoubleValue("true-bearing-deg", courseDeg); courseDeg -= magvar->getDoubleValue(); // expose magnetic bearing wp1->setDoubleValue("bearing-deg", courseDeg); - setETAPropertyFromDistance(wp1->getChild("eta"), distanceM); - } - - Waypt* prev = curWpt; - for (unsigned int i=_currentIndex + 1; i<_route.size(); ++i) { - Waypt* w = _route[i]; - if (w->flag(WPT_DYNAMIC)) continue; - totalDistanceRemaining += SGGeodesy::distanceM(prev->position(), w->position()); - prev = w; - - + setETAPropertyFromDistance(wp1->getChild("eta"), distanceM); + wp1->setDoubleValue("distance-along-route-nm", + nextLeg->distanceAlongRoute()); + wp1->setDoubleValue("remaining-distance-nm", + totalPathDistanceNm - nextLeg->distanceAlongRoute()); } - wpn->setDoubleValue("dist", totalDistanceRemaining * SG_METER_TO_NM); - ete->setDoubleValue(totalDistanceRemaining * SG_METER_TO_NM / groundSpeed * 3600.0); + distanceToGo->setDoubleValue(totalDistanceRemaining); + wpn->setDoubleValue("dist", totalDistanceRemaining); + ete->setDoubleValue(totalDistanceRemaining / gs * 3600.0); setETAPropertyFromDistance(wpn->getChild("eta"), totalDistanceRemaining); } -void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDistance) +void FGRouteMgr::clearRoute() { - double speed = fgGetDouble("/velocities/groundspeed-kt", 0.0); - if (speed < 1.0) { - aProp->setStringValue("--:--"); - return; + if (_plan) { + _plan->clear(); } - - char eta_str[64]; - double eta = aDistance * SG_METER_TO_NM / speed; - if ( eta >= 100.0 ) { - eta = 99.999; // clamp - } - - if ( eta < (1.0/6.0) ) { - eta *= 60.0; // within 10 minutes, bump up to min/secs - } - - int major = (int)eta, - minor = (int)((eta - (int)eta) * 60.0); - snprintf( eta_str, 64, "%d:%02d", major, minor ); - aProp->setStringValue( eta_str ); } -flightgear::WayptRef FGRouteMgr::removeWayptAtIndex(int aIndex) -{ - int index = aIndex; - if (aIndex < 0) { // negative indices count the the end - index = _route.size() + index; - } - - if ((index < 0) || (index >= numWaypts())) { - SG_LOG(SG_AUTOPILOT, SG_WARN, "removeWayptAtIndex with invalid index:" << aIndex); - return NULL; - } - WayptVec::iterator it = _route.begin(); - it += index; - - WayptRef w = *it; // hold a ref now, in case _route is the only other owner - _route.erase(it); - - update_mirror(); - - if (_currentIndex == index) { - currentWaypointChanged(); // current waypoint was removed - } - else - if (_currentIndex > index) { - --_currentIndex; // shift current index down if necessary - } - - _edited->fireValueChanged(); - checkFinished(); - - return w; -} - -struct NotGeneratedWayptPredicate : public std::unary_function +Waypt* FGRouteMgr::currentWaypt() const { - bool operator() (const Waypt* w) const - { - return (w->flag(WPT_GENERATED) == false); + if (_plan && _plan->currentLeg()) { + return _plan->currentLeg()->waypoint(); } -}; - - -void FGRouteMgr::clearRoute() -{ -// erase all non-generated waypoints - WayptVec::iterator r = - std::remove_if(_route.begin(), _route.end(), NotGeneratedWayptPredicate()); - _route.erase(r, _route.end()); - - _currentIndex = -1; - update_mirror(); - active->setBoolValue(false); - _edited->fireValueChanged(); + return NULL; } -/** - * route between index-1 and index, using airways. - */ -bool FGRouteMgr::routeToIndex(int index, RouteType aRouteType) +int FGRouteMgr::currentIndex() const { - WayptRef wp1; - WayptRef wp2; - - if (index == -1) { - index = _route.size(); // can still be zero, of course - } - - if (index == 0) { - if (!_departure) { - SG_LOG(SG_AUTOPILOT, SG_WARN, "routeToIndex: no departure set"); - return false; - } - - wp1 = new NavaidWaypoint(_departure.get(), NULL); - } else { - wp1 = wayptAtIndex(index - 1); - } - - if (index >= numWaypts()) { - if (!_destination) { - SG_LOG(SG_AUTOPILOT, SG_WARN, "routeToIndex: no destination set"); - return false; - } - - wp2 = new NavaidWaypoint(_destination.get(), NULL); - } else { - wp2 = wayptAtIndex(index); - } - - double distNm = SGGeodesy::distanceNm(wp1->position(), wp2->position()); - if (distNm < 100.0) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "routeToIndex: existing waypoints are nearby, direct route"); - return true; + if (!_plan) { + return 0; } - WayptVec r; - switch (aRouteType) { - case ROUTE_HIGH_AIRWAYS: - Airway::highLevel()->route(wp1, wp2, r); - break; - - case ROUTE_LOW_AIRWAYS: - Airway::lowLevel()->route(wp1, wp2, r); - break; - - case ROUTE_VOR: - throw sg_exception("VOR routing not supported yet"); - } - - if (r.empty()) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "routeToIndex: no route found"); - return false; - } - - WayptVec::iterator it = _route.begin(); - it += index; - _route.insert(it, r.begin(), r.end()); - - update_mirror(); - _edited->fireValueChanged(); - return true; + return _plan->currentIndex(); } -void FGRouteMgr::autoRoute() +Waypt* FGRouteMgr::wayptAtIndex(int index) const { - if (!_departure || !_destination) { - return; - } - - string runwayId(departure->getStringValue("runway")); - FGRunway* runway = NULL; - if (_departure->hasRunwayWithIdent(runwayId)) { - runway = _departure->getRunwayByIdent(runwayId); + if (!_plan) { + throw sg_range_exception("wayptAtindex: no flightplan"); } - FGRunway* dstRunway = NULL; - runwayId = destination->getStringValue("runway"); - if (_destination->hasRunwayWithIdent(runwayId)) { - dstRunway = _destination->getRunwayByIdent(runwayId); - } - - _route.clear(); // clear out the existing, first -// SID - flightgear::SID* sid; - WayptRef sidTrans; - - boost::tie(sid, sidTrans) = _departure->selectSID(_destination->geod(), runway); - if (sid) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "selected SID " << sid->ident()); - if (sidTrans) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "\tvia " << sidTrans->ident() << " transition"); - } - - sid->route(runway, sidTrans, _route); - departure->setStringValue("sid", sid->ident()); - } else { - // use airport location for airway search - sidTrans = new NavaidWaypoint(_departure.get(), NULL); - departure->setStringValue("sid", ""); - } - -// STAR - destination->setStringValue("transition", ""); - destination->setStringValue("star", ""); - - STAR* star; - WayptRef starTrans; - boost::tie(star, starTrans) = _destination->selectSTAR(_departure->geod(), dstRunway); - if (star) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "selected STAR " << star->ident()); - if (starTrans) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "\tvia " << starTrans->ident() << " transition"); - destination->setStringValue("transition", starTrans->ident()); - } - destination->setStringValue("star", star->ident()); - } else { - // use airport location for search - starTrans = new NavaidWaypoint(_destination.get(), NULL); - } - -// route between them - WayptVec airwayRoute; - if (Airway::highLevel()->route(sidTrans, starTrans, airwayRoute)) { - _route.insert(_route.end(), airwayRoute.begin(), airwayRoute.end()); - } - -// add the STAR if we have one - if (star) { - _destination->buildApproach(starTrans, star, dstRunway, _route); - } - - update_mirror(); - _edited->fireValueChanged(); + return _plan->legAtIndex(index)->waypoint(); } -void FGRouteMgr::departureChanged() +int FGRouteMgr::numLegs() const { -// remove existing departure waypoints - WayptVec::iterator it = _route.begin(); - for (; it != _route.end(); ++it) { - if (!(*it)->flag(WPT_DEPARTURE)) { - break; - } - } - - // erase() invalidates iterators, so grab now - WayptRef enroute; - if (it == _route.end()) { - if (_destination) { - enroute = new NavaidWaypoint(_destination.get(), NULL); - } - } else { - enroute = *it; - } - - _route.erase(_route.begin(), it); - if (!_departure) { - waypointsChanged(); - return; + if (_plan) { + return _plan->numLegs(); } - WayptVec wps; - buildDeparture(enroute, wps); - for (it = wps.begin(); it != wps.end(); ++it) { - (*it)->setFlag(WPT_DEPARTURE); - (*it)->setFlag(WPT_GENERATED); - } - _route.insert(_route.begin(), wps.begin(), wps.end()); - - update_mirror(); - waypointsChanged(); + return 0; } -void FGRouteMgr::buildDeparture(WayptRef enroute, WayptVec& wps) +void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDistance) { - string runwayId(departure->getStringValue("runway")); - if (!_departure->hasRunwayWithIdent(runwayId)) { -// valid airport, but no runway selected, so just the airport noide itself - wps.push_back(new NavaidWaypoint(_departure.get(), NULL)); - return; - } - - FGRunway* r = _departure->getRunwayByIdent(runwayId); - string sidId = departure->getStringValue("sid"); - flightgear::SID* sid = _departure->findSIDWithIdent(sidId); - if (!sid) { -// valid runway, but no SID selected/found, so just the runway node for now - if (!sidId.empty() && (sidId != "(none)")) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "SID not found:" << sidId); - } - - wps.push_back(new RunwayWaypt(r, NULL)); + double speed = groundSpeed->getDoubleValue(); + if (speed < 1.0) { + aProp->setStringValue("--:--"); return; } - -// we have a valid SID, awesome - string trans(departure->getStringValue("transition")); - WayptRef t = sid->findTransitionByName(trans); - if (!t && enroute) { - t = sid->findBestTransition(enroute->position()); - } - sid->route(r, t, wps); - if (!wps.empty() && wps.front()->flag(WPT_DYNAMIC)) { - // ensure first waypoint is static, to simplify other computations - wps.insert(wps.begin(), new RunwayWaypt(r, NULL)); - } -} - -void FGRouteMgr::arrivalChanged() -{ - // remove existing arrival waypoints - WayptVec::reverse_iterator rit = _route.rbegin(); - for (; rit != _route.rend(); ++rit) { - if (!(*rit)->flag(WPT_ARRIVAL)) { - break; - } - } - - // erase() invalidates iterators, so grab now - WayptRef enroute; - WayptVec::iterator it; - - if (rit != _route.rend()) { - enroute = *rit; - it = rit.base(); // convert to fwd iterator - } else { - it = _route.begin(); + char eta_str[64]; + double eta = aDistance * SG_METER_TO_NM / speed; + if ( eta >= 100.0 ) { + eta = 99.999; // clamp } - - _route.erase(it, _route.end()); - WayptVec wps; - buildArrival(enroute, wps); - for (it = wps.begin(); it != wps.end(); ++it) { - (*it)->setFlag(WPT_ARRIVAL); - (*it)->setFlag(WPT_GENERATED); + if ( eta < (1.0/6.0) ) { + eta *= 60.0; // within 10 minutes, bump up to min/secs } - _route.insert(_route.end(), wps.begin(), wps.end()); - update_mirror(); - waypointsChanged(); + int major = (int)eta, + minor = (int)((eta - (int)eta) * 60.0); + snprintf( eta_str, 64, "%d:%02d", major, minor ); + aProp->setStringValue( eta_str ); } -void FGRouteMgr::buildArrival(WayptRef enroute, WayptVec& wps) +void FGRouteMgr::removeLegAtIndex(int aIndex) { - if (!_destination) { + if (!_plan) { return; } - string runwayId(destination->getStringValue("runway")); - if (!_destination->hasRunwayWithIdent(runwayId)) { -// valid airport, but no runway selected, so just the airport node itself - wps.push_back(new NavaidWaypoint(_destination.get(), NULL)); - return; - } - - FGRunway* r = _destination->getRunwayByIdent(runwayId); - string starId = destination->getStringValue("star"); - STAR* star = _destination->findSTARWithIdent(starId); - if (!star) { -// valid runway, but no STAR selected/found, so just the runway node for now - wps.push_back(new RunwayWaypt(r, NULL)); - return; - } - -// we have a valid STAR - string trans(destination->getStringValue("transition")); - WayptRef t = star->findTransitionByName(trans); - if (!t && enroute) { - t = star->findBestTransition(enroute->position()); - } - - _destination->buildApproach(t, star, r, wps); + _plan->deleteIndex(aIndex); } - + void FGRouteMgr::waypointsChanged() { - -} - -void FGRouteMgr::insertWayptAtIndex(Waypt* aWpt, int aIndex) -{ - if (!aWpt) { - return; - } - - int index = aIndex; - if ((aIndex == -1) || (aIndex > (int) _route.size())) { - index = _route.size(); - } - - WayptVec::iterator it = _route.begin(); - it += index; - - if (_currentIndex >= index) { - ++_currentIndex; - } - - _route.insert(it, aWpt); - update_mirror(); _edited->fireValueChanged(); -} - -WayptRef FGRouteMgr::waypointFromString(const string& tgt ) -{ - string target(boost::to_upper_copy(tgt)); - WayptRef wpt; - -// extract altitude - double altFt = cruise->getDoubleValue("altitude-ft"); - RouteRestriction altSetting = RESTRICT_NONE; - - size_t pos = target.find( '@' ); - if ( pos != string::npos ) { - altFt = atof( target.c_str() + pos + 1 ); - target = target.substr( 0, pos ); - if ( !strcmp(fgGetString("/sim/startup/units"), "meter") ) - altFt *= SG_METER_TO_FEET; - altSetting = RESTRICT_AT; - } - -// check for lon,lat - pos = target.find( ',' ); - if ( pos != string::npos ) { - double lon = atof( target.substr(0, pos).c_str()); - double lat = atof( target.c_str() + pos + 1); - char buf[32]; - char ew = (lon < 0.0) ? 'W' : 'E'; - char ns = (lat < 0.0) ? 'S' : 'N'; - snprintf(buf, 32, "%c%03d%c%03d", ew, (int) fabs(lon), ns, (int)fabs(lat)); - - wpt = new BasicWaypt(SGGeod::fromDeg(lon, lat), buf, NULL); - if (altSetting != RESTRICT_NONE) { - wpt->setAltitude(altFt, altSetting); - } - return wpt; - } - - SGGeod basePosition; - if (_route.empty()) { - // route is empty, use current position - basePosition = SGGeod::fromDeg(lon->getDoubleValue(), lat->getDoubleValue()); - } else { - basePosition = _route.back()->position(); - } - - string_list pieces(simgear::strutils::split(target, "/")); - FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition); - if (!p) { - SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front()); - return NULL; - } - - if (pieces.size() == 1) { - wpt = new NavaidWaypoint(p, NULL); - } else if (pieces.size() == 3) { - // navaid/radial/distance-nm notation - double radial = atof(pieces[1].c_str()), - distanceNm = atof(pieces[2].c_str()); - radial += magvar->getDoubleValue(); // convert to true bearing - wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm); - } else if (pieces.size() == 2) { - FGAirport* apt = dynamic_cast(p.ptr()); - if (!apt) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "Waypoint is not an airport:" << pieces.front()); - return NULL; - } - - if (!apt->hasRunwayWithIdent(pieces[1])) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]); - return NULL; - } - - FGRunway* runway = apt->getRunwayByIdent(pieces[1]); - wpt = new NavaidWaypoint(runway, NULL); - } else if (pieces.size() == 4) { - // navid/radial/navid/radial notation - FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition); - if (!p2) { - SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]); - return NULL; - } - - double r1 = atof(pieces[1].c_str()), - r2 = atof(pieces[3].c_str()); - r1 += magvar->getDoubleValue(); - r2 += magvar->getDoubleValue(); - - SGGeod intersection; - bool ok = SGGeodesy::radialIntersection(p->geod(), r1, p2->geod(), r2, intersection); - if (!ok) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "no valid intersection for:" << target); - return NULL; - } - - std::string name = p->ident() + "-" + p2->ident(); - wpt = new BasicWaypt(intersection, name, NULL); - } - if (!wpt) { - SG_LOG(SG_AUTOPILOT, SG_INFO, "Unable to parse waypoint:" << target); - return NULL; +// removing waypoints, deactivate if we hit the end. + if (currentIndex() >= numLegs()) { + endOfRoute(); } - - if (altSetting != RESTRICT_NONE) { - wpt->setAltitude(altFt, altSetting); - } - return wpt; } // mirror internal route to the property system for inspection by other subsystems void FGRouteMgr::update_mirror() { mirror->removeChildren("wp"); + NewGUI * gui = (NewGUI *)globals->get_subsystem("gui"); + FGDialog* rmDlg = gui ? gui->getDialog("route-manager") : NULL; + + if (!_plan) { + mirror->setIntValue("num", 0); + if (rmDlg) { + rmDlg->updateValues(); + } + return; + } - int num = numWaypts(); + int num = _plan->numLegs(); + for (int i = 0; i < num; i++) { - Waypt* wp = _route[i]; + FlightPlan::Leg* leg = _plan->legAtIndex(i); + WayptRef wp = leg->waypoint(); SGPropertyNode *prop = mirror->getChild("wp", i, 1); const SGGeod& pos(wp->position()); @@ -996,16 +623,13 @@ void FGRouteMgr::update_mirror() prop->setDoubleValue("latitude-deg",pos.getLatitudeDeg()); // leg course+distance - if (i < (num - 1)) { - Waypt* next = _route[i+1]; - std::pair crsDist = - next->courseAndDistanceFrom(pos); - prop->setDoubleValue("leg-bearing-true-deg", crsDist.first); - prop->setDoubleValue("leg-distance-nm", crsDist.second * SG_METER_TO_NM); - } + + prop->setDoubleValue("leg-bearing-true-deg", leg->courseDeg()); + prop->setDoubleValue("leg-distance-nm", leg->distanceNm()); + prop->setDoubleValue("distance-along-route-nm", leg->distanceAlongRoute()); - if (wp->altitudeRestriction() != RESTRICT_NONE) { - double ft = wp->altitudeFt(); + if (leg->altitudeRestriction() != RESTRICT_NONE) { + double ft = leg->altitudeFt(); prop->setDoubleValue("altitude-m", ft * SG_FEET_TO_METER); prop->setDoubleValue("altitude-ft", ft); prop->setIntValue("flight-level", static_cast(ft / 1000) * 10); @@ -1014,10 +638,10 @@ void FGRouteMgr::update_mirror() prop->setDoubleValue("altitude-ft", -9999.9); } - if (wp->speedRestriction() == SPEED_RESTRICT_MACH) { - prop->setDoubleValue("speed-mach", wp->speedMach()); - } else if (wp->speedRestriction() != RESTRICT_NONE) { - prop->setDoubleValue("speed-kts", wp->speedKts()); + if (leg->speedRestriction() == SPEED_RESTRICT_MACH) { + prop->setDoubleValue("speed-mach", leg->speedMach()); + } else if (leg->speedRestriction() != RESTRICT_NONE) { + prop->setDoubleValue("speed-kts", leg->speedKts()); } if (wp->flag(WPT_ARRIVAL)) { @@ -1036,13 +660,13 @@ void FGRouteMgr::update_mirror() } // of waypoint iteration // set number as listener attachment point - mirror->setIntValue("num", _route.size()); + mirror->setIntValue("num", _plan->numLegs()); - NewGUI * gui = (NewGUI *)globals->get_subsystem("gui"); - FGDialog* rmDlg = gui->getDialog("route-manager"); if (rmDlg) { rmDlg->updateValues(); } + + totalDistance->setDoubleValue(_plan->totalDistanceNm()); } // command interface /autopilot/route-manager/input: @@ -1071,13 +695,13 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop) SGPath path(mgr->_pathNode->getStringValue()); mgr->saveRoute(path); } else if (!strcmp(s, "@NEXT")) { - mgr->jumpToIndex(mgr->_currentIndex + 1); + mgr->jumpToIndex(mgr->currentIndex() + 1); } else if (!strcmp(s, "@PREVIOUS")) { - mgr->jumpToIndex(mgr->_currentIndex - 1); + mgr->jumpToIndex(mgr->currentIndex() - 1); } else if (!strncmp(s, "@JUMP", 5)) { mgr->jumpToIndex(atoi(s + 5)); } else if (!strncmp(s, "@DELETE", 7)) - mgr->removeWayptAtIndex(atoi(s + 7)); + mgr->removeLegAtIndex(atoi(s + 7)); else if (!strncmp(s, "@INSERT", 7)) { char *r; int pos = strtol(s + 7, &r, 10); @@ -1086,18 +710,11 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop) while (isspace(*r)) r++; if (*r) - mgr->insertWayptAtIndex(mgr->waypointFromString(r), pos); - } else if (!strncmp(s, "@ROUTE", 6)) { - char* r; - int endIndex = strtol(s + 6, &r, 10); - RouteType rt = (RouteType) mgr->_routingType->getIntValue(); - mgr->routeToIndex(endIndex, rt); - } else if (!strcmp(s, "@AUTOROUTE")) { - mgr->autoRoute(); + mgr->flightPlan()->insertWayptAtIndex(mgr->waypointFromString(r), pos); } else if (!strcmp(s, "@POSINIT")) { mgr->initAtPosition(); } else - mgr->insertWayptAtIndex(mgr->waypointFromString(s), -1); + mgr->flightPlan()->insertWayptAtIndex(mgr->waypointFromString(s), -1); } void FGRouteMgr::initAtPosition() @@ -1114,499 +731,482 @@ void FGRouteMgr::initAtPosition() if (airborne->getBoolValue()) { SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: airborne, clearing departure info"); - _departure = NULL; - departure->setStringValue("runway", ""); + _plan->setDeparture((FGAirport*) NULL); return; } // on the ground - SGGeod pos = SGGeod::fromDegFt(lon->getDoubleValue(), - lat->getDoubleValue(), alt->getDoubleValue()); - if (!_departure) { - _departure = FGAirport::findClosest(pos, 20.0); - if (!_departure) { + SGGeod pos = globals->get_aircraft_position(); + if (!_plan->departureAirport()) { + _plan->setDeparture(FGAirport::findClosest(pos, 20.0)); + if (!_plan->departureAirport()) { SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: couldn't find an airport within 20nm"); - departure->setStringValue("runway", ""); return; } } std::string rwy = departure->getStringValue("runway"); + FGRunway* r = NULL; if (!rwy.empty()) { - // runway already set, fine - return; + r = _plan->departureAirport()->getRunwayByIdent(rwy); + } else { + r = _plan->departureAirport()->findBestRunwayForPos(pos); } - FGRunway* r = _departure->findBestRunwayForPos(pos); if (!r) { return; } - departure->setStringValue("runway", r->ident().c_str()); + _plan->setDeparture(r); SG_LOG(SG_AUTOPILOT, SG_INFO, "initAtPosition: starting at " - << _departure->ident() << " on runway " << r->ident()); + << _plan->departureAirport()->ident() << " on runway " << r->ident()); } bool FGRouteMgr::haveUserWaypoints() const { - return std::find_if(_route.begin(), _route.end(), NotGeneratedWayptPredicate()) != _route.end(); + // FIXME + return false; } bool FGRouteMgr::activate() { + if (!_plan) { + SG_LOG(SG_AUTOPILOT, SG_WARN, "::activate, no flight plan defined"); + return false; + } + if (isRouteActive()) { SG_LOG(SG_AUTOPILOT, SG_WARN, "duplicate route-activation, no-op"); return false; } - _currentIndex = 0; - currentWaypointChanged(); - - /* double routeDistanceNm = _route->total_distance() * SG_METER_TO_NM; - totalDistance->setDoubleValue(routeDistanceNm); - double cruiseSpeedKts = cruise->getDoubleValue("speed", 0.0); - if (cruiseSpeedKts > 1.0) { - // very very crude approximation, doesn't allow for climb / descent - // performance or anything else at all - ete->setDoubleValue(routeDistanceNm / cruiseSpeedKts * (60.0 * 60.0)); - } - */ + _plan->setCurrentIndex(0); active->setBoolValue(true); SG_LOG(SG_AUTOPILOT, SG_INFO, "route-manager, activate route ok"); return true; } +void FGRouteMgr::deactivate() +{ + if (!isRouteActive()) { + return; + } + + SG_LOG(SG_AUTOPILOT, SG_INFO, "deactivating flight plan"); + active->setBoolValue(false); +} void FGRouteMgr::sequence() { - if (!active->getBoolValue()) { + if (!_plan || !active->getBoolValue()) { SG_LOG(SG_AUTOPILOT, SG_ALERT, "trying to sequence waypoints with no active route"); return; } - if (checkFinished()) { + int nextIndex = _plan->currentIndex() + 1; + if (nextIndex >= _plan->numLegs()) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "sequenced on final leg, deactivating route"); + endOfRoute(); return; } - - _currentIndex++; - currentWaypointChanged(); + + _plan->setCurrentIndex(nextIndex); } -bool FGRouteMgr::checkFinished() +void FGRouteMgr::endOfRoute() { - if (_currentIndex < (int) _route.size()) { - return false; - } - SG_LOG(SG_AUTOPILOT, SG_INFO, "reached end of active route"); _finished->fireValueChanged(); active->setBoolValue(false); - return true; } -void FGRouteMgr::jumpToIndex(int index) +bool FGRouteMgr::checkFinished() { - if ((index < 0) || (index >= (int) _route.size())) { - SG_LOG(SG_AUTOPILOT, SG_ALERT, "passed invalid index (" << - index << ") to FGRouteMgr::jumpToIndex"); - return; + if (!_plan) { + return true; + } + + bool done = false; +// done if we're stopped on the destination runway + if (_plan->currentLeg() && + (_plan->currentLeg()->waypoint()->source() == _plan->destinationRunway())) + { + double gs = groundSpeed->getDoubleValue(); + done = weightOnWheels->getBoolValue() && (gs < 25); + } + + if (done) { + SG_LOG(SG_AUTOPILOT, SG_INFO, "checkFinished: on the ground on destination runway, we're done"); } + + return done; +} - if (_currentIndex == index) { - return; // no-op +void FGRouteMgr::jumpToIndex(int index) +{ + if (!_plan) { + return; } -// all the checks out the way, go ahead and update state - _currentIndex = index; - currentWaypointChanged(); - _currentWpt->fireValueChanged(); + _plan->setCurrentIndex(index); } void FGRouteMgr::currentWaypointChanged() { Waypt* cur = currentWaypt(); - Waypt* next = nextWaypt(); + FlightPlan::Leg* next = _plan ? _plan->nextLeg() : NULL; wp0->getChild("id")->setStringValue(cur ? cur->ident() : ""); - wp1->getChild("id")->setStringValue(next ? next->ident() : ""); + wp1->getChild("id")->setStringValue(next ? next->waypoint()->ident() : ""); _currentWpt->fireValueChanged(); - SG_LOG(SG_AUTOPILOT, SG_INFO, "route manager, current-wp is now " << _currentIndex); + SG_LOG(SG_AUTOPILOT, SG_INFO, "route manager, current-wp is now " << currentIndex()); } -int FGRouteMgr::findWayptIndex(const SGGeod& aPos) const -{ - for (int i=0; imatches(aPos)) { - return i; - } +const char* FGRouteMgr::getDepartureICAO() const +{ + if (!_plan || !_plan->departureAirport()) { + return ""; } - return -1; + return _plan->departureAirport()->ident().c_str(); } -Waypt* FGRouteMgr::currentWaypt() const +const char* FGRouteMgr::getDepartureName() const { - if ((_currentIndex < 0) || (_currentIndex >= numWaypts())) - return NULL; - return wayptAtIndex(_currentIndex); + if (!_plan || !_plan->departureAirport()) { + return ""; + } + + return _plan->departureAirport()->name().c_str(); } -Waypt* FGRouteMgr::previousWaypt() const +const char* FGRouteMgr::getDepartureRunway() const { - if (_currentIndex == 0) { - return NULL; + if (_plan && _plan->departureRunway()) { + return _plan->departureRunway()->ident().c_str(); } - return wayptAtIndex(_currentIndex - 1); + return ""; } -Waypt* FGRouteMgr::nextWaypt() const +void FGRouteMgr::setDepartureRunway(const char* aIdent) { - if ((_currentIndex < 0) || ((_currentIndex + 1) >= numWaypts())) { - return NULL; + FGAirport* apt = _plan->departureAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setDeparture(apt); + } else if (apt->hasRunwayWithIdent(aIdent)) { + _plan->setDeparture(apt->getRunwayByIdent(aIdent)); } - - return wayptAtIndex(_currentIndex + 1); } -Waypt* FGRouteMgr::wayptAtIndex(int index) const +void FGRouteMgr::setDepartureICAO(const char* aIdent) { - if ((index < 0) || (index >= numWaypts())) { - throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex"); + if ((aIdent == NULL) || (strlen(aIdent) < 4)) { + _plan->setDeparture((FGAirport*) NULL); + } else { + _plan->setDeparture(FGAirport::findByIdent(aIdent)); + } +} + +const char* FGRouteMgr::getSID() const +{ + if (_plan && _plan->sid()) { + return _plan->sid()->ident().c_str(); } - return _route[index]; + return ""; } -SGPropertyNode_ptr FGRouteMgr::wayptNodeAtIndex(int index) const +static double headingDiffDeg(double a, double b) { - if ((index < 0) || (index >= numWaypts())) { - throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex"); - } - - return mirror->getChild("wp", index); + double rawDiff = b - a; + SG_NORMALIZE_RANGE(rawDiff, -180.0, 180.0); + return rawDiff; } -bool FGRouteMgr::saveRoute(const SGPath& path) +flightgear::SID* createDefaultSID(FGRunway* aRunway, double enrouteCourse) { - SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str()); - try { - SGPropertyNode_ptr d(new SGPropertyNode); - SGPath path(_pathNode->getStringValue()); - d->setIntValue("version", 2); - - if (_departure) { - d->setStringValue("departure/airport", _departure->ident()); - d->setStringValue("departure/sid", departure->getStringValue("sid")); - d->setStringValue("departure/runway", departure->getStringValue("runway")); - } - - if (_destination) { - d->setStringValue("destination/airport", _destination->ident()); - d->setStringValue("destination/star", destination->getStringValue("star")); - d->setStringValue("destination/transition", destination->getStringValue("transition")); - d->setStringValue("destination/runway", destination->getStringValue("runway")); + if (!aRunway) { + return NULL; + } + + double runwayElevFt = aRunway->end().getElevationFt(); + WayptVec wpts; + std::ostringstream ss; + ss << aRunway->ident() << "-3"; + + SGGeod p = aRunway->pointOnCenterline(aRunway->lengthM() + (3.0 * SG_NM_TO_METER)); + WayptRef w = new BasicWaypt(p, ss.str(), NULL); + w->setAltitude(runwayElevFt + 3000.0, RESTRICT_AT); + wpts.push_back(w); + + ss.str(""); + ss << aRunway->ident() << "-6"; + p = aRunway->pointOnCenterline(aRunway->lengthM() + (6.0 * SG_NM_TO_METER)); + w = new BasicWaypt(p, ss.str(), NULL); + w->setAltitude(runwayElevFt + 6000.0, RESTRICT_AT); + wpts.push_back(w); + + if (enrouteCourse >= 0.0) { + // valid enroute course + int index = 3; + double course = aRunway->headingDeg(); + double diff; + while (fabs(diff = headingDiffDeg(course, enrouteCourse)) > 45.0) { + // turn in the sign of the heading change 45 degrees + course += copysign(45.0, diff); + ss.str(""); + ss << "DEP-" << index++; + SGGeod pos = wpts.back()->position(); + pos = SGGeodesy::direct(pos, course, 3.0 * SG_NM_TO_METER); + w = new BasicWaypt(pos, ss.str(), NULL); + wpts.push_back(w); } - - // route nodes - SGPropertyNode* routeNode = d->getChild("route", 0, true); - for (unsigned int i=0; i<_route.size(); ++i) { - Waypt* wpt = _route[i]; - wpt->saveAsNode(routeNode->getChild("wp", i, true)); - } // of waypoint iteration - writeProperties(path.str(), d, true /* write-all */); - return true; - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_ALERT, "Failed to save flight-plan '" << path.str() << "'. " << e.getMessage()); - return false; + } else { + // no enroute course, just keep runway heading + ss.str(""); + ss << aRunway->ident() << "-9"; + p = aRunway->pointOnCenterline(aRunway->lengthM() + (9.0 * SG_NM_TO_METER)); + w = new BasicWaypt(p, ss.str(), NULL); + w->setAltitude(runwayElevFt + 9000.0, RESTRICT_AT); + wpts.push_back(w); + } + + BOOST_FOREACH(Waypt* w, wpts) { + w->setFlag(WPT_DEPARTURE); + w->setFlag(WPT_GENERATED); } + + return flightgear::SID::createTempSID("DEFAULT", aRunway, wpts); } -bool FGRouteMgr::loadRoute(const SGPath& path) +void FGRouteMgr::setSID(const char* aIdent) { - if (!path.exists()) - { - SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << path.str() - << "'. The file does not exist."); - return false; - } - - // deactivate route first - active->setBoolValue(false); + FGAirport* apt = _plan->departureAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setSID((flightgear::SID*) NULL); + return; + } - SGPropertyNode_ptr routeData(new SGPropertyNode); + if (!strcmp(aIdent, "DEFAULT")) { + double enrouteCourse = -1.0; + if (_plan->destinationAirport()) { + enrouteCourse = SGGeodesy::courseDeg(apt->geod(), _plan->destinationAirport()->geod()); + } + + _plan->setSID(createDefaultSID(_plan->departureRunway(), enrouteCourse)); + return; + } - SG_LOG(SG_IO, SG_INFO, "going to read flight-plan from:" << path.str()); + string ident(aIdent); + size_t hyphenPos = ident.find('-'); + if (hyphenPos != string::npos) { + string sidIdent = ident.substr(0, hyphenPos); + string transIdent = ident.substr(hyphenPos + 1); - try { - readProperties(path.str(), routeData); - } catch (sg_exception& ) { - // if XML parsing fails, the file might be simple textual list of waypoints - return loadPlainTextRoute(path); - } - - try { - int version = routeData->getIntValue("version", 1); - if (version == 1) { - loadVersion1XMLRoute(routeData); - } else if (version == 2) { - loadVersion2XMLRoute(routeData); - } else { - throw sg_io_exception("unsupported XML route version"); - } - return true; - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin() - << "'. " << e.getMessage()); - return false; + flightgear::SID* sid = apt->findSIDWithIdent(sidIdent); + Transition* trans = sid ? sid->findTransitionByName(transIdent) : NULL; + _plan->setSID(trans); + } else { + _plan->setSID(apt->findSIDWithIdent(aIdent)); } } -void FGRouteMgr::loadXMLRouteHeader(SGPropertyNode_ptr routeData) +const char* FGRouteMgr::getDestinationICAO() const { - // departure nodes - SGPropertyNode* dep = routeData->getChild("departure"); - if (dep) { - string depIdent = dep->getStringValue("airport"); - _departure = (FGAirport*) fgFindAirportID(depIdent); - departure->setStringValue("runway", dep->getStringValue("runway")); - departure->setStringValue("sid", dep->getStringValue("sid")); - departure->setStringValue("transition", dep->getStringValue("transition")); - } - -// destination - SGPropertyNode* dst = routeData->getChild("destination"); - if (dst) { - _destination = (FGAirport*) fgFindAirportID(dst->getStringValue("airport")); - destination->setStringValue("runway", dst->getStringValue("runway")); - destination->setStringValue("star", dst->getStringValue("star")); - destination->setStringValue("transition", dst->getStringValue("transition")); - } - -// alternate - SGPropertyNode* alt = routeData->getChild("alternate"); - if (alt) { - alternate->setStringValue(alt->getStringValue("airport")); - } // of cruise data loading - -// cruise - SGPropertyNode* crs = routeData->getChild("cruise"); - if (crs) { - cruise->setDoubleValue("speed-kts", crs->getDoubleValue("speed-kts")); - cruise->setDoubleValue("mach", crs->getDoubleValue("mach")); - cruise->setDoubleValue("altitude-ft", crs->getDoubleValue("altitude-ft")); - } // of cruise data loading - + if (!_plan || !_plan->destinationAirport()) { + return ""; + } + + return _plan->destinationAirport()->ident().c_str(); } -void FGRouteMgr::loadVersion2XMLRoute(SGPropertyNode_ptr routeData) +const char* FGRouteMgr::getDestinationName() const { - loadXMLRouteHeader(routeData); + if (!_plan || !_plan->destinationAirport()) { + return ""; + } -// route nodes - WayptVec wpts; - SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); - for (int i=0; inChildren(); ++i) { - SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); - WayptRef wpt = Waypt::createFromProperties(NULL, wpNode); - wpts.push_back(wpt); - } // of route iteration - - _route = wpts; + return _plan->destinationAirport()->name().c_str(); } -void FGRouteMgr::loadVersion1XMLRoute(SGPropertyNode_ptr routeData) +void FGRouteMgr::setDestinationICAO(const char* aIdent) { - loadXMLRouteHeader(routeData); - -// route nodes - WayptVec wpts; - SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); - for (int i=0; inChildren(); ++i) { - SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); - WayptRef wpt = parseVersion1XMLWaypt(wpNode); - wpts.push_back(wpt); - } // of route iteration - - _route = wpts; + if ((aIdent == NULL) || (strlen(aIdent) < 4)) { + _plan->setDestination((FGAirport*) NULL); + } else { + _plan->setDestination(FGAirport::findByIdent(aIdent)); + } } -WayptRef FGRouteMgr::parseVersion1XMLWaypt(SGPropertyNode* aWP) +const char* FGRouteMgr::getDestinationRunway() const { - SGGeod lastPos; - if (!_route.empty()) { - lastPos = _route.back()->position(); - } else if (_departure) { - lastPos = _departure->geod(); - } - - WayptRef w; - string ident(aWP->getStringValue("ident")); - if (aWP->hasChild("longitude-deg")) { - // explicit longitude/latitude - w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"), - aWP->getDoubleValue("latitude-deg")), ident, NULL); - - } else { - string nid = aWP->getStringValue("navid", ident.c_str()); - FGPositionedRef p = FGPositioned::findClosestWithIdent(nid, lastPos); - if (!p) { - throw sg_io_exception("bad route file, unknown navid:" + nid); - } - - SGGeod pos(p->geod()); - if (aWP->hasChild("offset-nm") && aWP->hasChild("offset-radial")) { - double radialDeg = aWP->getDoubleValue("offset-radial"); - // convert magnetic radial to a true radial! - radialDeg += magvar->getDoubleValue(); - double offsetNm = aWP->getDoubleValue("offset-nm"); - double az2; - SGGeodesy::direct(p->geod(), radialDeg, offsetNm * SG_NM_TO_METER, pos, az2); - } - - w = new BasicWaypt(pos, ident, NULL); + if (_plan && _plan->destinationRunway()) { + return _plan->destinationRunway()->ident().c_str(); } - double altFt = aWP->getDoubleValue("altitude-ft", -9999.9); - if (altFt > -9990.0) { - w->setAltitude(altFt, RESTRICT_AT); - } - - return w; + return ""; } -bool FGRouteMgr::loadPlainTextRoute(const SGPath& path) +void FGRouteMgr::setDestinationRunway(const char* aIdent) { - try { - sg_gzifstream in(path.str().c_str()); - if (!in.is_open()) { - throw sg_io_exception("Cannot open file for reading."); - } - - WayptVec wpts; - while (!in.eof()) { - string line; - getline(in, line, '\n'); - // trim CR from end of line, if found - if (line[line.size() - 1] == '\r') { - line.erase(line.size() - 1, 1); - } - - line = simgear::strutils::strip(line); - if (line.empty() || (line[0] == '#')) { - continue; // ignore empty/comment lines - } - - WayptRef w = waypointFromString(line); - if (!w) { - throw sg_io_exception("Failed to create waypoint from line '" + line + "'."); - } - - wpts.push_back(w); - } // of line iteration - - _route = wpts; - return true; - } catch (sg_exception& e) { - SG_LOG(SG_IO, SG_ALERT, "Failed to load route from: '" << path.str() << "'. " << e.getMessage()); - return false; + FGAirport* apt = _plan->destinationAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setDestination(apt); + } else if (apt->hasRunwayWithIdent(aIdent)) { + _plan->setDestination(apt->getRunwayByIdent(aIdent)); } } -const char* FGRouteMgr::getDepartureICAO() const +const char* FGRouteMgr::getApproach() const { - if (!_departure) { - return ""; + if (_plan && _plan->approach()) { + return _plan->approach()->ident().c_str(); } - return _departure->ident().c_str(); + return ""; } -const char* FGRouteMgr::getDepartureName() const +flightgear::Approach* createDefaultApproach(FGRunway* aRunway, double aEnrouteCourse) { - if (!_departure) { - return ""; + if (!aRunway) { + return NULL; } - - return _departure->name().c_str(); -} -void FGRouteMgr::setDepartureICAO(const char* aIdent) -{ - if ((aIdent == NULL) || (strlen(aIdent) < 4)) { - _departure = NULL; - } else { - _departure = FGAirport::findByIdent(aIdent); + double thresholdElevFt = aRunway->threshold().getElevationFt(); + const double approachHeightFt = 2000.0; + double glideslopeDistanceM = (approachHeightFt * SG_FEET_TO_METER) / + tan(3.0 * SG_DEGREES_TO_RADIANS); + + std::ostringstream ss; + ss << aRunway->ident() << "-12"; + WayptVec wpts; + SGGeod p = aRunway->pointOnCenterline(-12.0 * SG_NM_TO_METER); + WayptRef w = new BasicWaypt(p, ss.str(), NULL); + w->setAltitude(thresholdElevFt + 4000, RESTRICT_AT); + wpts.push_back(w); + +// work back form the first point on the centerline + + if (aEnrouteCourse >= 0.0) { + // valid enroute course + int index = 4; + double course = aRunway->headingDeg(); + double diff; + while (fabs(diff = headingDiffDeg(aEnrouteCourse, course)) > 45.0) { + // turn in the sign of the heading change 45 degrees + course -= copysign(45.0, diff); + ss.str(""); + ss << "APP-" << index++; + SGGeod pos = wpts.front()->position(); + pos = SGGeodesy::direct(pos, course + 180.0, 3.0 * SG_NM_TO_METER); + w = new BasicWaypt(pos, ss.str(), NULL); + wpts.insert(wpts.begin(), w); + } + } + + p = aRunway->pointOnCenterline(-8.0 * SG_NM_TO_METER); + ss.str(""); + ss << aRunway->ident() << "-8"; + w = new BasicWaypt(p, ss.str(), NULL); + w->setAltitude(thresholdElevFt + approachHeightFt, RESTRICT_AT); + wpts.push_back(w); + + p = aRunway->pointOnCenterline(-glideslopeDistanceM); + ss.str(""); + ss << aRunway->ident() << "-GS"; + w = new BasicWaypt(p, ss.str(), NULL); + w->setAltitude(thresholdElevFt + approachHeightFt, RESTRICT_AT); + wpts.push_back(w); + + BOOST_FOREACH(Waypt* w, wpts) { + w->setFlag(WPT_APPROACH); + w->setFlag(WPT_GENERATED); } - departureChanged(); + return Approach::createTempApproach("DEFAULT", aRunway, wpts); } -const char* FGRouteMgr::getDestinationICAO() const +void FGRouteMgr::setApproach(const char* aIdent) { - if (!_destination) { - return ""; + FGAirport* apt = _plan->destinationAirport(); + if (!strcmp(aIdent, "DEFAULT")) { + double enrouteCourse = -1.0; + if (_plan->departureAirport()) { + enrouteCourse = SGGeodesy::courseDeg(_plan->departureAirport()->geod(), apt->geod()); + } + + _plan->setApproach(createDefaultApproach(_plan->destinationRunway(), enrouteCourse)); + return; } - return _destination->ident().c_str(); + if (!apt || (aIdent == NULL)) { + _plan->setApproach(NULL); + } else { + _plan->setApproach(apt->findApproachWithIdent(aIdent)); + } } -const char* FGRouteMgr::getDestinationName() const +const char* FGRouteMgr::getSTAR() const { - if (!_destination) { - return ""; + if (_plan && _plan->star()) { + return _plan->star()->ident().c_str(); } - return _destination->name().c_str(); + return ""; } -void FGRouteMgr::setDestinationICAO(const char* aIdent) +void FGRouteMgr::setSTAR(const char* aIdent) { - if ((aIdent == NULL) || (strlen(aIdent) < 4)) { - _destination = NULL; + FGAirport* apt = _plan->destinationAirport(); + if (!apt || (aIdent == NULL)) { + _plan->setSTAR((STAR*) NULL); + return; + } + + string ident(aIdent); + size_t hyphenPos = ident.find('-'); + if (hyphenPos != string::npos) { + string starIdent = ident.substr(0, hyphenPos); + string transIdent = ident.substr(hyphenPos + 1); + + STAR* star = apt->findSTARWithIdent(starIdent); + Transition* trans = star ? star->findTransitionByName(transIdent) : NULL; + _plan->setSTAR(trans); } else { - _destination = FGAirport::findByIdent(aIdent); + _plan->setSTAR(apt->findSTARWithIdent(aIdent)); } - - arrivalChanged(); } -FGAirportRef FGRouteMgr::departureAirport() const +WayptRef FGRouteMgr::waypointFromString(const std::string& target) { - return _departure; + return _plan->waypointFromString(target); } -FGAirportRef FGRouteMgr::destinationAirport() const +double FGRouteMgr::getDepartureFieldElevation() const { - return _destination; + if (!_plan || !_plan->departureAirport()) { + return 0.0; + } + + return _plan->departureAirport()->elevation(); } -FGRunway* FGRouteMgr::departureRunway() const +double FGRouteMgr::getDestinationFieldElevation() const { - if (!_departure) { - return NULL; - } - - string runwayId(departure->getStringValue("runway")); - if (!_departure->hasRunwayWithIdent(runwayId)) { - return NULL; - } - - return _departure->getRunwayByIdent(runwayId); + if (!_plan || !_plan->destinationAirport()) { + return 0.0; + } + + return _plan->destinationAirport()->elevation(); } -FGRunway* FGRouteMgr::destinationRunway() const +SGPropertyNode_ptr FGRouteMgr::wayptNodeAtIndex(int index) const { - if (!_destination) { - return NULL; - } - - string runwayId(destination->getStringValue("runway")); - if (!_destination->hasRunwayWithIdent(runwayId)) { - return NULL; - } - - return _destination->getRunwayByIdent(runwayId); + if ((index < 0) || (index >= numWaypts())) { + throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex"); + } + + return mirror->getChild("wp", index); } - diff --git a/src/Autopilot/route_mgr.hxx b/src/Autopilot/route_mgr.hxx index 2b3409b..b66a06b 100644 --- a/src/Autopilot/route_mgr.hxx +++ b/src/Autopilot/route_mgr.hxx @@ -25,26 +25,21 @@ #define _ROUTE_MGR_HXX 1 #include -#include #include -#include +#include // forward decls class SGPath; class PropertyWatcher; -class FGAirport; -class FGRunway; - -typedef SGSharedPtr FGAirportRef; - /** * Top level route manager class * */ -class FGRouteMgr : public SGSubsystem +class FGRouteMgr : public SGSubsystem, + public flightgear::FlightPlan::Delegate { public: FGRouteMgr(); @@ -55,53 +50,31 @@ public: void bind (); void unbind (); void update (double dt); - - void insertWayptAtIndex(flightgear::Waypt* aWpt, int aIndex); - flightgear::WayptRef removeWayptAtIndex(int index); - void clearRoute(); - - typedef enum { - ROUTE_HIGH_AIRWAYS, ///< high-level airways routing - ROUTE_LOW_AIRWAYS, ///< low-level airways routing - ROUTE_VOR ///< VOR-VOR routing - } RouteType; - - /** - * Insert waypoints from index-1 to index. In practice this means you can - * 'fill in the gaps' between defined waypoints. If index=0, the departure - * airport is used as index-1; if index is -1, the destination airport is - * used as the final waypoint. - */ - bool routeToIndex(int index, RouteType aRouteType); - - void autoRoute(); - bool isRouteActive() const; - int currentIndex() const - { return _currentIndex; } - + int currentIndex() const; + + void setFlightPlan(flightgear::FlightPlan* plan); + flightgear::FlightPlan* flightPlan() const; + + void clearRoute(); + flightgear::Waypt* currentWaypt() const; - flightgear::Waypt* nextWaypt() const; - flightgear::Waypt* previousWaypt() const; - const flightgear::WayptVec& waypts() const - { return _route; } + int numLegs() const; +// deprecated int numWaypts() const - { return _route.size(); } - + { return numLegs(); } + +// deprecated flightgear::Waypt* wayptAtIndex(int index) const; - + SGPropertyNode_ptr wayptNodeAtIndex(int index) const; - - /** - * Find a waypoint in the route, by position, and return its index, or - * -1 if no matching waypoint was found in the route. - */ - int findWayptIndex(const SGGeod& aPos) const; - + + void removeLegAtIndex(int aIndex); + /** * Activate a built route. This checks for various mandatory pieces of * data, such as departure and destination airports, and creates waypoints @@ -111,6 +84,11 @@ public: * route could not be activated for some reason */ bool activate(); + + /** + * deactivate the route if active + */ + void deactivate(); /** * Step to the next waypoint on the active route @@ -125,38 +103,20 @@ public: bool saveRoute(const SGPath& p); bool loadRoute(const SGPath& p); + flightgear::WayptRef waypointFromString(const std::string& target); + /** * Helper command to setup current airport/runway if necessary */ void initAtPosition(); - - /** - * Create a WayPoint from a string in the following format: - * - simple identifier - * - decimal-lon,decimal-lat - * - airport-id/runway-id - * - navaid/radial-deg/offset-nm - */ - flightgear::WayptRef waypointFromString(const std::string& target); - - FGAirportRef departureAirport() const; - FGAirportRef destinationAirport() const; - - FGRunway* departureRunway() const; - FGRunway* destinationRunway() const; + private: - flightgear::WayptVec _route; - int _currentIndex; + flightgear::FlightPlan* _plan; time_t _takeoffTime; time_t _touchdownTime; - FGAirportRef _departure; - FGAirportRef _destination; - + // automatic inputs - SGPropertyNode_ptr lon; - SGPropertyNode_ptr lat; - SGPropertyNode_ptr alt; SGPropertyNode_ptr magvar; // automatic outputs @@ -166,6 +126,7 @@ private: SGPropertyNode_ptr cruise; ///< cruise information SGPropertyNode_ptr totalDistance; + SGPropertyNode_ptr distanceToGo; SGPropertyNode_ptr ete; SGPropertyNode_ptr elapsedFlightTime; @@ -180,8 +141,6 @@ private: SGPropertyNode_ptr _pathNode; SGPropertyNode_ptr _currentWpt; - /// integer property corresponding to the RouteType enum - SGPropertyNode_ptr _routingType; /** * Signal property to notify people that the route was edited @@ -193,8 +152,16 @@ private: */ SGPropertyNode_ptr _finished; + SGPropertyNode_ptr _flightplanChanged; + void setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDistance); + /** + * retrieve the cached path distance along a leg + */ + double cachedLegPathDistanceM(int index) const; + double cachedWaypointPathTotalDistance(int index) const; + class InputListener : public SGPropertyChangeListener { public: InputListener(FGRouteMgr *m) : mgr(m) {} @@ -205,31 +172,21 @@ private: SGPropertyNode_ptr input; SGPropertyNode_ptr weightOnWheels; - + SGPropertyNode_ptr groundSpeed; + InputListener *listener; SGPropertyNode_ptr mirror; - - void departureChanged(); - void buildDeparture(flightgear::WayptRef enroute, flightgear::WayptVec& wps); - - void arrivalChanged(); - void buildArrival(flightgear::WayptRef enroute, flightgear::WayptVec& wps); - + /** * Helper to keep various pieces of state in sync when the route is * modified (waypoints added, inserted, removed). Notably, this fires the * 'edited' signal. */ - void waypointsChanged(); + virtual void waypointsChanged(); void update_mirror(); - void currentWaypointChanged(); - - /** - * Parse a route/wp node (from a saved, property-lsit formatted route) - */ - void parseRouteWaypoint(SGPropertyNode* aWP); + virtual void currentWaypointChanged(); /** * Check if we've reached the final waypoint. @@ -237,13 +194,10 @@ private: */ bool checkFinished(); - - bool loadPlainTextRoute(const SGPath& path); - - void loadVersion1XMLRoute(SGPropertyNode_ptr routeData); - void loadVersion2XMLRoute(SGPropertyNode_ptr routeData); - void loadXMLRouteHeader(SGPropertyNode_ptr routeData); - flightgear::WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP); + /* + * update state when we pass the final waypoint + */ + void endOfRoute(); /** * Predicate for helping the UI - test if at least one waypoint was @@ -256,12 +210,27 @@ private: const char* getDepartureName() const; void setDepartureICAO(const char* aIdent); + const char* getDepartureRunway() const; + void setDepartureRunway(const char* aIdent); + + const char* getSID() const; + void setSID(const char* aIdent); + const char* getDestinationICAO() const; const char* getDestinationName() const; void setDestinationICAO(const char* aIdent); - PropertyWatcher* _departureWatcher; - PropertyWatcher* _arrivalWatcher; + const char* getDestinationRunway() const; + void setDestinationRunway(const char* aIdent); + + const char* getApproach() const; + void setApproach(const char* aIdent); + + const char* getSTAR() const; + void setSTAR(const char* aIdent); + + double getDepartureFieldElevation() const; + double getDestinationFieldElevation() const; }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e558433..b09b6a3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,6 +8,7 @@ foreach( mylibfolder Aircraft ATC ATCDCL + Canvas Radio Autopilot Cockpit @@ -27,9 +28,11 @@ foreach( mylibfolder Time Traffic FDM + Viewer Main - ) + ) + + add_subdirectory(${mylibfolder}) - add_subdirectory(${mylibfolder}) endforeach( mylibfolder ) diff --git a/src/Canvas/CMakeLists.txt b/src/Canvas/CMakeLists.txt new file mode 100644 index 0000000..d3dae9d --- /dev/null +++ b/src/Canvas/CMakeLists.txt @@ -0,0 +1,17 @@ +include(FlightGearComponent) + +set(SOURCES + canvas_mgr.cxx + FGCanvasSystemAdapter.cxx + gui_mgr.cxx + window.cxx +) + +set(HEADERS + canvas_mgr.hxx + FGCanvasSystemAdapter.hxx + gui_mgr.hxx + window.hxx +) + +flightgear_component(Canvas "${SOURCES}" "${HEADERS}") \ No newline at end of file diff --git a/src/Canvas/FGCanvasSystemAdapter.cxx b/src/Canvas/FGCanvasSystemAdapter.cxx new file mode 100644 index 0000000..f6c9452 --- /dev/null +++ b/src/Canvas/FGCanvasSystemAdapter.cxx @@ -0,0 +1,118 @@ +#include "FGCanvasSystemAdapter.hxx" + +#include
+#include +#include + +#include +#include + +namespace canvas +{ + //---------------------------------------------------------------------------- + simgear::canvas::FontPtr + FGCanvasSystemAdapter::getFont(const std::string& name) const + { + SGPath path = globals->resolve_resource_path("Fonts/" + name); + if( path.isNull() ) + { + SG_LOG + ( + SG_GL, + SG_ALERT, + "canvas::Text: No such font: " << name + ); + return simgear::canvas::FontPtr(); + } + + SG_LOG + ( + SG_GL, + SG_INFO, + "canvas::Text: using font file " << path.str() + ); + + simgear::canvas::FontPtr font = osgText::readFontFile(path.c_str()); + if( !font ) + SG_LOG + ( + SG_GL, + SG_ALERT, + "canvas::Text: Failed to open font file " << path.c_str() + ); + + return font; + } + + //---------------------------------------------------------------------------- + void FGCanvasSystemAdapter::addCamera(osg::Camera* camera) const + { + globals->get_renderer()->addCamera(camera, false); + } + + //---------------------------------------------------------------------------- + void FGCanvasSystemAdapter::removeCamera(osg::Camera* camera) const + { + globals->get_renderer()->removeCamera(camera); + } + + //---------------------------------------------------------------------------- + osg::Image* FGCanvasSystemAdapter::getImage(const std::string& path) const + { + SGPath tpath = globals->resolve_resource_path(path); + if( tpath.isNull() || !tpath.exists() ) + { + SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such image: " << path); + return 0; + } + + return osgDB::readImageFile(tpath.c_str()); + } + + /** + * Get current FGNasalSys instance. + */ + static FGNasalSys* getNasalSys() + { + static FGNasalSys* nasal_sys = 0; + // TODO if Nasal is able to be removed and/or recreated at runtime we need + // to ensure that always the current instance is used + if( !nasal_sys ) + { + nasal_sys = dynamic_cast(globals->get_subsystem("nasal")); + if( !nasal_sys ) + throw std::runtime_error("FGCanvasSystemAdapter: no NasalSys"); + } + + return nasal_sys; + } + + //---------------------------------------------------------------------------- + naContext FGCanvasSystemAdapter::getNasalContext() const + { + return getNasalSys()->context(); + } + + //---------------------------------------------------------------------------- + int FGCanvasSystemAdapter::gcSave(naRef r) + { + return getNasalSys()->gcSave(r); + } + + //---------------------------------------------------------------------------- + void FGCanvasSystemAdapter::gcRelease(int key) + { + getNasalSys()->gcRelease(key); + } + + //------------------------------------------------------------------------------ + naRef FGCanvasSystemAdapter::callMethod( naRef code, + naRef self, + int argc, + naRef* args, + naRef locals ) + { + return getNasalSys()->callMethod(code, self, argc, args, locals); + } + +} diff --git a/src/Canvas/FGCanvasSystemAdapter.hxx b/src/Canvas/FGCanvasSystemAdapter.hxx new file mode 100644 index 0000000..d56397a --- /dev/null +++ b/src/Canvas/FGCanvasSystemAdapter.hxx @@ -0,0 +1,35 @@ +/* + * FGCanvasSystemAdapter.hxx + * + * Created on: 02.11.2012 + * Author: tom + */ + +#ifndef FG_CANVASSYSTEMADAPTER_HXX_ +#define FG_CANVASSYSTEMADAPTER_HXX_ + +#include + +namespace canvas +{ + class FGCanvasSystemAdapter: + public simgear::canvas::SystemAdapter + { + public: + virtual simgear::canvas::FontPtr getFont(const std::string& name) const; + virtual void addCamera(osg::Camera* camera) const; + virtual void removeCamera(osg::Camera* camera) const; + virtual osg::Image* getImage(const std::string& path) const; + + virtual naContext getNasalContext() const; + virtual int gcSave(naRef r); + virtual void gcRelease(int key); + virtual naRef callMethod( naRef code, + naRef self, + int argc, + naRef* args, + naRef locals ); + }; +} + +#endif /* FG_CANVASSYSTEMADAPTER_HXX_ */ diff --git a/src/Canvas/canvas_fwd.hpp b/src/Canvas/canvas_fwd.hpp new file mode 100644 index 0000000..1512f00 --- /dev/null +++ b/src/Canvas/canvas_fwd.hpp @@ -0,0 +1,32 @@ +// Canvas forward declarations +// +// Copyright (C) 2012 Thomas Geymayer +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_FWD_HPP_ +#define CANVAS_FWD_HPP_ + +#include +#include + +namespace canvas +{ + class Window; + typedef boost::shared_ptr WindowPtr; + typedef boost::weak_ptr WindowWeakPtr; +} + +#endif /* CANVAS_FWD_HPP_ */ diff --git a/src/Canvas/canvas_mgr.cxx b/src/Canvas/canvas_mgr.cxx new file mode 100644 index 0000000..ca4515e --- /dev/null +++ b/src/Canvas/canvas_mgr.cxx @@ -0,0 +1,77 @@ +// Canvas with 2D rendering api +// +// Copyright (C) 2012 Thomas Geymayer +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "canvas_mgr.hxx" + +#include +#include +#include
+#include + +#include + +//------------------------------------------------------------------------------ +CanvasMgr::CanvasMgr(): + simgear::canvas::CanvasMgr + ( + fgGetNode("/canvas/by-index", true), + simgear::canvas::SystemAdapterPtr( new canvas::FGCanvasSystemAdapter ) + ) +{ + using simgear::canvas::Canvas; + Canvas::addPlacementFactory + ( + "object", + boost::bind + ( + &FGODGauge::set_texture, + _1, + boost::bind(&Canvas::getTexture, _2), + boost::bind(&Canvas::getCullCallback, _2) + ) + ); +} + +//------------------------------------------------------------------------------ +unsigned int +CanvasMgr::getCanvasTexId(const simgear::canvas::CanvasPtr& canvas) const +{ + osg::Texture2D* tex = canvas->getTexture(); + if( !tex ) + return 0; + +// osgViewer::Viewer::Contexts contexts; +// globals->get_renderer()->getViewer()->getContexts(contexts); +// +// if( contexts.empty() ) +// return 0; + + static osg::Camera* guiCamera = + flightgear::getGUICamera(flightgear::CameraGroup::getDefault()); + + osg::State* state = guiCamera->getGraphicsContext()->getState(); //contexts[0]->getState(); + if( !state ) + return 0; + + osg::Texture::TextureObject* tobj = + tex->getTextureObject( state->getContextID() ); + if( !tobj ) + return 0; + + return tobj->_id; +} diff --git a/src/Canvas/canvas_mgr.hxx b/src/Canvas/canvas_mgr.hxx new file mode 100644 index 0000000..fd4768c --- /dev/null +++ b/src/Canvas/canvas_mgr.hxx @@ -0,0 +1,43 @@ +// Canvas with 2D rendering api +// +// Copyright (C) 2012 Thomas Geymayer +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_MGR_H_ +#define CANVAS_MGR_H_ + +#include +#include + +class CanvasMgr: + public simgear::canvas::CanvasMgr +{ + public: + CanvasMgr(); + + /** + * Get OpenGL texture name for given canvas + * + * @deprecated This was only meant to be used by the PUI CanvasWidget + * implementation as PUI can't handle osg::Texture objects. + * Use getCanvas(index)->getTexture() instead. + * + * @return OpenGL texture name + */ + unsigned int getCanvasTexId(const simgear::canvas::CanvasPtr& canvas) const; +}; + +#endif /* CANVAS_MGR_H_ */ diff --git a/src/Canvas/gui_mgr.cxx b/src/Canvas/gui_mgr.cxx new file mode 100644 index 0000000..0c563ce --- /dev/null +++ b/src/Canvas/gui_mgr.cxx @@ -0,0 +1,507 @@ +// Canvas gui/dialog manager +// +// Copyright (C) 2012 Thomas Geymayer +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "gui_mgr.hxx" +#include + +#include
+#include
+#include
+#include +#include + +#include +#include + +#include +#include +#include + +#include + +/** + * Event handler + */ +class GUIEventHandler: + public osgGA::GUIEventHandler +{ + public: + GUIEventHandler(GUIMgr* gui_mgr): + _gui_mgr( gui_mgr ) + {} + + bool handle( const osgGA::GUIEventAdapter& ea, + osgGA::GUIActionAdapter& aa, + osg::Object*, + osg::NodeVisitor* ) + { + if( ea.getHandled() ) + return false; + return _gui_mgr->handleEvent(ea); + } + + protected: + GUIMgr *_gui_mgr; +}; + +/** + * Track a canvas placement on a window + */ +class WindowPlacement: + public simgear::canvas::Placement +{ + public: + WindowPlacement( SGPropertyNode* node, + canvas::WindowPtr window, + simgear::canvas::CanvasPtr canvas ): + Placement(node), + _window(window), + _canvas(canvas) + {} + + /** + * Remove placement from window + */ + virtual ~WindowPlacement() + { + canvas::WindowPtr window = _window.lock(); + simgear::canvas::CanvasPtr canvas = _canvas.lock(); + + if( window && canvas && canvas == window->getCanvas().lock() ) + window->setCanvas( simgear::canvas::CanvasPtr() ); + } + + private: + canvas::WindowWeakPtr _window; + simgear::canvas::CanvasWeakPtr _canvas; +}; + +/** + * Store pointer to window as user data + */ +class WindowUserData: + public osg::Referenced +{ + public: + canvas::WindowWeakPtr window; + WindowUserData(canvas::WindowPtr window): + window(window) + {} +}; + +//------------------------------------------------------------------------------ +typedef boost::shared_ptr WindowPtr; +WindowPtr windowFactory(SGPropertyNode* node) +{ + return WindowPtr(new canvas::Window(node)); +} + +//------------------------------------------------------------------------------ +GUIMgr::GUIMgr(): + PropertyBasedMgr( fgGetNode("/sim/gui/canvas", true), + "window", + &windowFactory ), + _event_handler( new GUIEventHandler(this) ), + _transform( new osg::MatrixTransform ), + _width(_props, "size[0]"), + _height(_props, "size[1]"), + _resize(canvas::Window::NONE), + _last_cursor(MOUSE_CURSOR_NONE), + _last_scroll_time(0) +{ + _width = _height = -1; + + osg::Camera* camera = + flightgear::getGUICamera( flightgear::CameraGroup::getDefault() ); + assert(camera); + camera->addChild(_transform); + + simgear::canvas::Canvas::addPlacementFactory + ( + "window", + boost::bind(&GUIMgr::addPlacement, this, _1, _2) + ); + + osg::StateSet* stateSet = _transform->getOrCreateStateSet(); + stateSet->setDataVariance(osg::Object::STATIC); + stateSet->setRenderBinDetails(1000, "RenderBin"); + + // speed optimization? + stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + stateSet->setAttribute(new osg::BlendFunc( + osg::BlendFunc::SRC_ALPHA, + osg::BlendFunc::ONE_MINUS_SRC_ALPHA) + ); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + stateSet->setMode(GL_FOG, osg::StateAttribute::OFF); + stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); +} + +//------------------------------------------------------------------------------ +void GUIMgr::init() +{ + handleResize + ( + 0, + 0, + fgGetInt("/sim/startup/xsize"), + fgGetInt("/sim/startup/ysize") + ); + + PropertyBasedMgr::init(); + + globals->get_renderer() + ->getViewer() + ->getEventHandlers() + // GUI is on top of everything so lets install as first event handler + .push_front( _event_handler ); +} + +//------------------------------------------------------------------------------ +void GUIMgr::shutdown() +{ + PropertyBasedMgr::shutdown(); + + globals->get_renderer() + ->getViewer() + ->removeEventHandler( _event_handler ); +} + +//------------------------------------------------------------------------------ +void GUIMgr::elementCreated(simgear::PropertyBasedElementPtr element) +{ + canvas::WindowPtr window = + boost::static_pointer_cast(element); + + size_t layer_index = std::max(0, window->getProps()->getIntValue("layer", 1)); + osg::Group *layer = 0; + + if( layer_index < _transform->getNumChildren() ) + { + layer = _transform->getChild(layer_index)->asGroup(); + assert(layer); + } + else + { + while( _transform->getNumChildren() <= layer_index ) + { + layer = new osg::Group; + _transform->addChild(layer); + } + } + window->getGroup()->setUserData(new WindowUserData(window)); + layer->addChild(window->getGroup()); +} + +//------------------------------------------------------------------------------ +bool GUIMgr::handleEvent(const osgGA::GUIEventAdapter& ea) +{ + switch( ea.getEventType() ) + { + case osgGA::GUIEventAdapter::PUSH: + case osgGA::GUIEventAdapter::RELEASE: +// case osgGA::GUIEventAdapter::DOUBLECLICK: +// // DOUBLECLICK doesn't seem to be triggered... + case osgGA::GUIEventAdapter::DRAG: + case osgGA::GUIEventAdapter::MOVE: + case osgGA::GUIEventAdapter::SCROLL: + return handleMouse(ea); + case osgGA::GUIEventAdapter::RESIZE: + handleResize( ea.getWindowX(), + ea.getWindowY(), + ea.getWindowWidth(), + ea.getWindowHeight() ); + return false; // Let other event handlers also consume resize events + default: + return false; + } +} + +//------------------------------------------------------------------------------ +canvas::WindowPtr GUIMgr::getWindow(size_t i) +{ + return boost::static_pointer_cast(_elements[i]); +} + +//------------------------------------------------------------------------------ +simgear::canvas::Placements +GUIMgr::addPlacement( SGPropertyNode* node, + simgear::canvas::CanvasPtr canvas ) +{ + int placement_index = node->getIntValue("index", -1); + + simgear::canvas::Placements placements; + for( size_t i = 0; i < _elements.size(); ++i ) + { + if( placement_index >= 0 && static_cast(i) != placement_index ) + continue; + + canvas::WindowPtr window = getWindow(i); + if( !window ) + continue; + + window->setCanvas(canvas); + placements.push_back( + simgear::canvas::PlacementPtr(new WindowPlacement(node, window, canvas)) + ); + } + return placements; +} + +/* +RESIZE AREAS +============ + +| || | _ inside corner region (L-shaped part inside margin) both +|___||_|_ _ _/ directions can be resized (outside only one axis) +| || | | +| || | +| || |_____|__ _ +| || | } margin_neg \ +| ========|== <-- window border |_ area where resize +| | } margin_pos | can be initiated +|____________|__/ _/ +|<- corner ->| +*/ +const float resize_margin_pos = 12; +const float resize_margin_neg = 2; +const float resize_corner = 20; + +//------------------------------------------------------------------------------ +bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea) +{ + if( !_transform->getNumChildren() ) + return false; + + namespace sc = simgear::canvas; + sc::MouseEventPtr event(new sc::MouseEvent); + event->time = ea.getTime(); + + event->screen_pos.x() = 0.5 * (ea.getXnormalized() + 1) * _width + 0.5; + event->screen_pos.y() = 0.5 * (ea.getYnormalized() + 1) * _height + 0.5; + if( ea.getMouseYOrientation() + != osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS ) + event->screen_pos.y() = _height - event->screen_pos.y(); + + event->delta.x() = event->getScreenX() - _last_x; + event->delta.y() = event->getScreenY() - _last_y; + + _last_x = event->getScreenX(); + _last_y = event->getScreenY(); + + event->client_pos = event->screen_pos; + event->button = ea.getButton(); + event->state = ea.getButtonMask(); + event->mod = ea.getModKeyMask(); + + if( !_resize_window.expired() ) + { + switch( ea.getEventType() ) + { + case osgGA::GUIEventAdapter::RELEASE: + _resize_window.lock()->handleResize(canvas::Window::NONE); + _resize_window.reset(); + break; + case osgGA::GUIEventAdapter::DRAG: + _resize_window.lock()->handleResize(_resize, event->delta); + return true; + default: + return false; + } + } + + canvas::WindowPtr window_at_cursor; + for( int i = _transform->getNumChildren() - 1; i >= 0; --i ) + { + osg::Group *layer = _transform->getChild(i)->asGroup(); + assert(layer); + if( !layer->getNumChildren() ) + continue; + + for( int j = layer->getNumChildren() - 1; j >= 0; --j ) + { + assert(layer->getChild(j)->getUserData()); + canvas::WindowPtr window = + static_cast(layer->getChild(j)->getUserData()) + ->window.lock(); + float margin = window->isResizable() ? resize_margin_pos : 0; + if( window->getRegion().contains( event->getScreenX(), + event->getScreenY(), + margin ) ) + { + window_at_cursor = window; + break; + } + } + + if( window_at_cursor ) + break; + } + + if( window_at_cursor ) + { + const SGRect& reg = window_at_cursor->getRegion(); + + if( window_at_cursor->isResizable() + && ( ea.getEventType() == osgGA::GUIEventAdapter::MOVE + || ea.getEventType() == osgGA::GUIEventAdapter::PUSH + || ea.getEventType() == osgGA::GUIEventAdapter::RELEASE + ) + && !reg.contains( event->getScreenX(), + event->getScreenY(), + -resize_margin_neg ) ) + { + if( !_last_cursor ) + _last_cursor = fgGetMouseCursor(); + + _resize = 0; + + if( event->getScreenX() <= reg.l() + resize_corner ) + _resize |= canvas::Window::LEFT; + else if( event->getScreenX() >= reg.r() - resize_corner ) + _resize |= canvas::Window::RIGHT; + + if( event->getScreenY() <= reg.t() + resize_corner ) + _resize |= canvas::Window::TOP; + else if( event->getScreenY() >= reg.b() - resize_corner ) + _resize |= canvas::Window::BOTTOM; + + static const int cursor_mapping[] = + { + 0, MOUSE_CURSOR_LEFTSIDE, MOUSE_CURSOR_RIGHTSIDE, 0, + MOUSE_CURSOR_TOPSIDE, MOUSE_CURSOR_TOPLEFT, MOUSE_CURSOR_TOPRIGHT, 0, + MOUSE_CURSOR_BOTTOMSIDE, MOUSE_CURSOR_BOTTOMLEFT, MOUSE_CURSOR_BOTTOMRIGHT, + }; + + if( !cursor_mapping[_resize] ) + return false; + + fgSetMouseCursor(cursor_mapping[_resize]); + + if( ea.getEventType() == osgGA::GUIEventAdapter::PUSH ) + { + _resize_window = window_at_cursor; + window_at_cursor->doRaise(); + window_at_cursor->handleResize( _resize | canvas::Window::INIT, + event->delta ); + } + + return true; + } + } + + if( _last_cursor ) + { + fgSetMouseCursor(_last_cursor); + _last_cursor = 0; + return true; + } + + canvas::WindowPtr target_window = window_at_cursor; + switch( ea.getEventType() ) + { + case osgGA::GUIEventAdapter::PUSH: + _last_push = window_at_cursor; + event->type = sc::Event::MOUSE_DOWN; + break; + case osgGA::GUIEventAdapter::SCROLL: + switch( ea.getScrollingMotion() ) + { + case osgGA::GUIEventAdapter::SCROLL_UP: + event->delta.y() = 1; + break; + case osgGA::GUIEventAdapter::SCROLL_DOWN: + event->delta.y() = -1; + break; + default: + return false; + } + + // osg sends two events for every scrolling motion. We don't need + // duplicate events, so lets ignore the second event with the same + // timestamp. + if( _last_scroll_time == ea.getTime() ) + return window_at_cursor ? true : false; + _last_scroll_time = ea.getTime(); + + event->type = sc::Event::WHEEL; + break; + case osgGA::GUIEventAdapter::MOVE: + { + canvas::WindowPtr last_mouse_over = _last_mouse_over.lock(); + if( last_mouse_over != window_at_cursor && last_mouse_over ) + { + sc::MouseEventPtr move_event( new sc::MouseEvent(*event) ); + move_event->type = sc::Event::MOUSE_LEAVE; + + // Let the event position be always relative to the top left window + // corner + move_event->client_pos.x() -= last_mouse_over->getRegion().x(); + move_event->client_pos.y() -= last_mouse_over->getRegion().y(); + + last_mouse_over->handleMouseEvent(move_event); + } + _last_mouse_over = window_at_cursor; + event->type = sc::Event::MOUSE_MOVE; + break; + } + case osgGA::GUIEventAdapter::RELEASE: + target_window = _last_push.lock(); + _last_push.reset(); + event->type = sc::Event::MOUSE_UP; + break; + + case osgGA::GUIEventAdapter::DRAG: + target_window = _last_push.lock(); + event->type = sc::Event::DRAG; + break; + + default: + return false; + } + + if( target_window ) + { + // Let the event position be always relative to the top left window corner + event->client_pos.x() -= target_window->getRegion().x(); + event->client_pos.y() -= target_window->getRegion().y(); + + return target_window->handleMouseEvent(event); + } + else + return false; +} + +//------------------------------------------------------------------------------ +void GUIMgr::handleResize(int x, int y, int width, int height) +{ + if( _width == width && _height == height ) + return; + + _width = width; + _height = height; + + // Origin should be at top left corner, therefore we need to mirror the y-axis + _transform->setMatrix(osg::Matrix( + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, _height, 0, 1 + )); +} diff --git a/src/Canvas/gui_mgr.hxx b/src/Canvas/gui_mgr.hxx new file mode 100644 index 0000000..23888a5 --- /dev/null +++ b/src/Canvas/gui_mgr.hxx @@ -0,0 +1,77 @@ +// Canvas gui/dialog manager +// +// Copyright (C) 2012 Thomas Geymayer +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_GUI_MGR_HXX_ +#define CANVAS_GUI_MGR_HXX_ + +#include "canvas_fwd.hpp" + +#include +#include +#include + +#include +#include +#include + +namespace osgGA +{ + class GUIEventAdapter; +} + +class GUIEventHandler; +class GUIMgr: + public simgear::PropertyBasedMgr +{ + public: + GUIMgr(); + + virtual void init(); + virtual void shutdown(); + + virtual void elementCreated(simgear::PropertyBasedElementPtr element); + + bool handleEvent(const osgGA::GUIEventAdapter& ea); + + protected: + + osg::ref_ptr _event_handler; + osg::ref_ptr _transform; + + simgear::PropertyObject _width, + _height; + + canvas::WindowWeakPtr _last_push, + _last_mouse_over, + _resize_window; + uint8_t _resize; + int _last_cursor; + + float _last_x, + _last_y; + double _last_scroll_time; + + canvas::WindowPtr getWindow(size_t i); + simgear::canvas::Placements + addPlacement(SGPropertyNode*, simgear::canvas::CanvasPtr canvas ); + + bool handleMouse(const osgGA::GUIEventAdapter& ea); + void handleResize(int x, int y, int width, int height); +}; + +#endif /* CANVAS_GUI_MGR_HXX_ */ diff --git a/src/Canvas/window.cxx b/src/Canvas/window.cxx new file mode 100644 index 0000000..8c0494b --- /dev/null +++ b/src/Canvas/window.cxx @@ -0,0 +1,171 @@ +// Window for placing a Canvas onto it (for dialogs, menus, etc.) +// +// Copyright (C) 2012 Thomas Geymayer +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "window.hxx" +#include + +#include + +#include + +namespace canvas +{ + //---------------------------------------------------------------------------- + Window::Window(SGPropertyNode* node): + PropertyBasedElement(node), + _image( simgear::canvas::CanvasPtr(), + node, + simgear::canvas::Style() ), + _resizable(false), + _resize_top(node, "resize-top"), + _resize_right(node, "resize-right"), + _resize_bottom(node, "resize-bottom"), + _resize_left(node, "resize-left"), + _resize_status(node, "resize-status") + { + _image.removeListener(); + + // TODO probably better remove default position and size + node->setFloatValue("x", 50); + node->setFloatValue("y", 100); + node->setFloatValue("size[0]", 400); + node->setFloatValue("size[1]", 300); + + node->setFloatValue("source/right", 1); + node->setFloatValue("source/bottom", 1); + node->setBoolValue("source/normalized", true); + } + + //---------------------------------------------------------------------------- + Window::~Window() + { + + } + + //---------------------------------------------------------------------------- + void Window::update(double delta_time_sec) + { + _image.update(delta_time_sec); + } + + //---------------------------------------------------------------------------- + void Window::valueChanged(SGPropertyNode * node) + { + bool handled = false; + if( node->getParent() == _node ) + { + handled = true; + if( node->getNameString() == "raise-top" ) + doRaise(node); + else if( node->getNameString() == "resize" ) + _resizable = node->getBoolValue(); + else + handled = false; + } + + if( !handled ) + _image.valueChanged(node); + } + + //---------------------------------------------------------------------------- + osg::Group* Window::getGroup() + { + return _image.getMatrixTransform(); + } + + //---------------------------------------------------------------------------- + const SGRect& Window::getRegion() const + { + return _image.getRegion(); + } + + //---------------------------------------------------------------------------- + void Window::setCanvas(simgear::canvas::CanvasPtr canvas) + { + _image.setSrcCanvas(canvas); + } + + //---------------------------------------------------------------------------- + simgear::canvas::CanvasWeakPtr Window::getCanvas() const + { + return _image.getSrcCanvas(); + } + + //---------------------------------------------------------------------------- + bool Window::isResizable() const + { + return _resizable; + } + + //---------------------------------------------------------------------------- + bool Window::handleMouseEvent(const simgear::canvas::MouseEventPtr& event) + { + if( !getCanvas().expired() ) + return getCanvas().lock()->handleMouseEvent(event); + else + return false; + } + + //---------------------------------------------------------------------------- + void Window::handleResize(uint8_t mode, const osg::Vec2f& delta) + { + if( mode == NONE ) + { + _resize_status = 0; + return; + } + else if( mode & INIT ) + { + _resize_top = getRegion().t(); + _resize_right = getRegion().r(); + _resize_bottom = getRegion().b(); + _resize_left = getRegion().l(); + _resize_status = 1; + } + + if( mode & BOTTOM ) + _resize_bottom += delta.y(); + else if( mode & TOP ) + _resize_top += delta.y(); + + if( mode & canvas::Window::RIGHT ) + _resize_right += delta.x(); + else if( mode & canvas::Window::LEFT ) + _resize_left += delta.x(); + } + + //---------------------------------------------------------------------------- + void Window::doRaise(SGPropertyNode* node_raise) + { + if( node_raise && !node_raise->getBoolValue() ) + return; + + BOOST_FOREACH(osg::Group* parent, getGroup()->getParents()) + { + // Remove window... + parent->removeChild(getGroup()); + + // ...and add again as topmost window + parent->addChild(getGroup()); + } + + if( node_raise ) + node_raise->setBoolValue(false); + } + +} // namespace canvas diff --git a/src/Canvas/window.hxx b/src/Canvas/window.hxx new file mode 100644 index 0000000..46fb48e --- /dev/null +++ b/src/Canvas/window.hxx @@ -0,0 +1,81 @@ +// Window for placing a Canvas onto it (for dialogs, menus, etc.) +// +// Copyright (C) 2012 Thomas Geymayer +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_WINDOW_HXX_ +#define CANVAS_WINDOW_HXX_ + +#include +#include +#include +#include + +#include +#include + +namespace canvas +{ + class Window: + public simgear::PropertyBasedElement + { + public: + + enum Resize + { + NONE = 0, + LEFT = 1, + RIGHT = LEFT << 1, + TOP = RIGHT << 1, + BOTTOM = TOP << 1, + INIT = BOTTOM << 1 + }; + + Window(SGPropertyNode* node); + virtual ~Window(); + + virtual void update(double delta_time_sec); + virtual void valueChanged(SGPropertyNode* node); + + osg::Group* getGroup(); + const SGRect& getRegion() const; + + void setCanvas(simgear::canvas::CanvasPtr canvas); + simgear::canvas::CanvasWeakPtr getCanvas() const; + + bool isResizable() const; + + bool handleMouseEvent(const simgear::canvas::MouseEventPtr& event); + + void handleResize(uint8_t mode, const osg::Vec2f& delta = osg::Vec2f()); + + void doRaise(SGPropertyNode* node_raise = 0); + + protected: + + simgear::canvas::Image _image; + bool _resizable; + + simgear::PropertyObject _resize_top, + _resize_right, + _resize_bottom, + _resize_left, + _resize_status; + + }; +} // namespace canvas + +#endif /* CANVAS_WINDOW_HXX_ */ diff --git a/src/Cockpit/CMakeLists.txt b/src/Cockpit/CMakeLists.txt index d68022e..42b03ba 100644 --- a/src/Cockpit/CMakeLists.txt +++ b/src/Cockpit/CMakeLists.txt @@ -1,15 +1,29 @@ include(FlightGearComponent) set(SOURCES + cockpitDisplayManager.cxx panel.cxx panel_io.cxx built_in/FGMagRibbon.cxx + agradar.cxx + groundradar.cxx + od_gauge.cxx + render_area_2d.cxx + wxradar.cxx + NavDisplay.cxx ) set(HEADERS + cockpitDisplayManager.hxx panel.hxx panel_io.hxx built_in/FGMagRibbon.hxx + agradar.hxx + groundradar.hxx + od_gauge.hxx + render_area_2d.hxx + wxradar.hxx + NavDisplay.hxx ) diff --git a/src/Cockpit/NavDisplay.cxx b/src/Cockpit/NavDisplay.cxx new file mode 100644 index 0000000..9e9b052 --- /dev/null +++ b/src/Cockpit/NavDisplay.cxx @@ -0,0 +1,1482 @@ +// navigation display texture +// +// Written by James Turner, forked from wxradar code +// +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "NavDisplay.hxx" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include // for cout, endl + +using std::stringstream; +using std::endl; +using std::setprecision; +using std::fixed; +using std::setw; +using std::setfill; +using std::cout; +using std::endl; +using std::map; +using std::string; + +#include
+#include
+#include "panel.hxx" +#include +#include +#include +#include +#include +#include +#include +#include "od_gauge.hxx" + +static const char *DEFAULT_FONT = "typewriter.txf"; + +static +osg::Matrixf degRotation(float angle) +{ + return osg::Matrixf::rotate(angle * SG_DEGREES_TO_RADIANS, 0.0f, 0.0f, -1.0f); +} + +static osg::Vec4 readColor(SGPropertyNode* colorNode, const osg::Vec4& c) +{ + osg::Vec4 result; + result.r() = colorNode->getDoubleValue("red", c.r()); + result.g() = colorNode->getDoubleValue("green", c.g()); + result.b() = colorNode->getDoubleValue("blue", c.b()); + result.a() = colorNode->getDoubleValue("alpha", c.a()); + return result; +} + +static osgText::Text::AlignmentType readAlignment(const std::string& t) +{ + if (t == "left-top") { + return osgText::Text::LEFT_TOP; + } else if (t == "left-center") { + return osgText::Text::LEFT_CENTER; + } else if (t == "left-bottom") { + return osgText::Text::LEFT_BOTTOM; + } else if (t == "center-top") { + return osgText::Text::CENTER_TOP; + } else if (t == "center-center") { + return osgText::Text::CENTER_CENTER; + } else if (t == "center-bottom") { + return osgText::Text::CENTER_BOTTOM; + } else if (t == "right-top") { + return osgText::Text::RIGHT_TOP; + } else if (t == "right-center") { + return osgText::Text::RIGHT_CENTER; + } else if (t == "right-bottom") { + return osgText::Text::RIGHT_BOTTOM; + } else if (t == "left-baseline") { + return osgText::Text::LEFT_BASE_LINE; + } else if (t == "center-baseline") { + return osgText::Text::CENTER_BASE_LINE; + } else if (t == "right-baseline") { + return osgText::Text::RIGHT_BASE_LINE; + } + + return osgText::Text::BASE_LINE; +} + +static string formatPropertyValue(SGPropertyNode* nd, const string& format) +{ + assert(nd); + static char buf[512]; + if (format.find('d') != string::npos) { + ::snprintf(buf, 512, format.c_str(), nd->getIntValue()); + return buf; + } + + if (format.find('s') != string::npos) { + ::snprintf(buf, 512, format.c_str(), nd->getStringValue()); + return buf; + } + +// assume it's a double/float + ::snprintf(buf, 512, format.c_str(), nd->getDoubleValue()); + return buf; +} + +static osg::Vec2 mult(const osg::Vec2& v, const osg::Matrixf& m) +{ + osg::Vec3 r = m.preMult(osg::Vec3(v.x(), v.y(), 0.0)); + return osg::Vec2(r.x(), r.y()); +} + +class NavDisplay::CacheListener : public SGPropertyChangeListener +{ +public: + CacheListener(NavDisplay *nd) : + _nd(nd) + {} + + virtual void valueChanged (SGPropertyNode * prop) + { + _nd->invalidatePositionedCache(); + } +private: + NavDisplay* _nd; +}; + +class NavDisplay::ForceUpdateListener : public SGPropertyChangeListener +{ +public: + ForceUpdateListener(NavDisplay *nd) : + _nd(nd) + {} + + virtual void valueChanged (SGPropertyNode * prop) + { + _nd->forceUpdate(); + } +private: + NavDisplay* _nd; +}; + +/////////////////////////////////////////////////////////////////// + +class SymbolRule +{ +public: + SymbolRule() + { + + } + + bool initFromNode(SGPropertyNode* node, NavDisplay* owner) + { + if (!node->getChild("type")) { + return false; + } + + type = node->getStringValue("type"); + boost::to_lower(type); + SGPropertyNode* enableNode = node->getChild("enable"); + if (enableNode) { + enable.reset(sgReadCondition(fgGetNode("/"), enableNode)); + } + + int n=0; + while (node->hasChild("state", n)) { + string m = node->getChild("state", n++)->getStringValue(); + if (m[0] == '!') { + excluded_states.insert(m.substr(1)); + } else { + required_states.insert(m); + } + } // of matches parsing + + + return true; + } + + void setDefinition(SymbolDef* d) + { + definition = d; + } + + SymbolDef* getDefinition() const + { return definition; } + + bool matches(const string_set& states) const + { + BOOST_FOREACH(const string& s, required_states) { + if (states.count(s) == 0) { + return false; + } + } + + BOOST_FOREACH(const string& s, excluded_states) { + if (states.count(s) != 0) { + return false; + } + } + + return true; + } + + // return if the enabled state changed (needs a cache update) + bool checkEnabled() + { + if (enable.get()) { + bool wasEnabled = enabled; + enabled = enable->test(); + return (enabled != wasEnabled); + } else { + enabled = true; + return false; + } + } + + bool enabled; // cached enabled state + std::string type; + + // record instances for limiting by count + int instanceCount; +private: + SymbolDef* definition; + + std::auto_ptr enable; + string_set required_states; + string_set excluded_states; +}; + +class SymbolDef +{ +public: + SymbolDef() : limitCount(0) { } + + bool initFromNode(SGPropertyNode* node, NavDisplay* owner) + { + if (node->getChild("type")) { + SymbolRule* builtinRule = new SymbolRule; + builtinRule->initFromNode(node, owner); + builtinRule->setDefinition(this); + owner->addRule(builtinRule); + } + + if (node->hasChild("width")) { + float w = node->getFloatValue("width"); + float h = node->getFloatValue("height", w); + xy0.x() = -w * 0.5; + xy0.y() = -h * 0.5; + xy1.x() = w * 0.5; + xy1.y() = h * 0.5; + } else { + xy0.x() = node->getFloatValue("x0", 0.0); + xy0.y() = node->getFloatValue("y0", 0.0); + xy1.x() = node->getFloatValue("x1", 5); + xy1.y() = node->getFloatValue("y1", 5); + } + + double texSize = node->getFloatValue("texture-size", owner->textureSize()); + + uv0.x() = node->getFloatValue("u0", 0) / texSize; + uv0.y() = node->getFloatValue("v0", 0) / texSize; + uv1.x() = node->getFloatValue("u1", 1) / texSize; + uv1.y() = node->getFloatValue("v1", 1) / texSize; + + color = readColor(node->getChild("color"), osg::Vec4(1, 1, 1, 1)); + priority = node->getIntValue("priority", 0); + zOrder = node->getIntValue("zOrder", 0); + rotateToHeading = node->getBoolValue("rotate-to-heading", false); + roundPos = node->getBoolValue("round-position", true); + hasText = false; + if (node->hasChild("text")) { + hasText = true; + alignment = readAlignment(node->getStringValue("text-align")); + textTemplate = node->getStringValue("text"); + textOffset.x() = node->getFloatValue("text-offset-x", 0); + textOffset.y() = node->getFloatValue("text-offset-y", 0); + textColor = readColor(node->getChild("text-color"), color); + + SGPropertyNode* enableNode = node->getChild("text-enable"); + if (enableNode) { + textEnable.reset(sgReadCondition(fgGetNode("/"), enableNode)); + } + } + + drawLine = node->getBoolValue("draw-line", false); + lineColor = readColor(node->getChild("line-color"), color); + drawRouteLeg = node->getBoolValue("draw-leg", false); + + stretchSymbol = node->getBoolValue("stretch-symbol", false); + if (stretchSymbol) { + stretchY2 = node->getFloatValue("y2"); + stretchY3 = node->getFloatValue("y3"); + stretchV2 = node->getFloatValue("v2") / texSize; + stretchV3 = node->getFloatValue("v3") / texSize; + } + + SGPropertyNode* limitNode = node->getChild("limit"); + if (limitNode) { + limitCount = limitNode->getIntValue(); + } + + return true; + } + + osg::Vec2 xy0, xy1; + osg::Vec2 uv0, uv1; + osg::Vec4 color; + + int priority; + int zOrder; + bool rotateToHeading; + bool roundPos; ///< should position be rounded to integer values + bool hasText; + std::auto_ptr textEnable; + bool textEnabled; ///< cache condition result + osg::Vec4 textColor; + osg::Vec2 textOffset; + osgText::Text::AlignmentType alignment; + string textTemplate; + + bool drawLine; + osg::Vec4 lineColor; + +// symbol stretching creates three quads (instead of one) - a start, +// middle and end quad, positioned along the line of the symbol. +// X (and U) axis values determined by the values above, so we only need +// to define the Y (and V) values to build the other quads. + bool stretchSymbol; + double stretchY2, stretchY3; + double stretchV2, stretchV3; + + bool drawRouteLeg; + + int limitCount, instanceCount; +}; + +class SymbolInstance +{ +public: + SymbolInstance(const osg::Vec2& p, double h, SymbolDef* def, SGPropertyNode* vars) : + pos(p), + headingDeg(h), + definition(def), + props(vars) + { } + + osg::Vec2 pos; // projected position + osg::Vec2 endPos; + double headingDeg; + SymbolDef* definition; + SGPropertyNode_ptr props; + + string text() const + { + assert(definition->hasText); + string r; + size_t lastPos = 0; + + while (true) { + size_t pos = definition->textTemplate.find('{', lastPos); + if (pos == string::npos) { // no more replacements + r.append(definition->textTemplate.substr(lastPos)); + break; + } + + r.append(definition->textTemplate.substr(lastPos, pos - lastPos)); + + size_t endReplacement = definition->textTemplate.find('}', pos+1); + if (endReplacement <= pos) { + return "bad replacement"; + } + + string spec = definition->textTemplate.substr(pos + 1, endReplacement - (pos + 1)); + // look for formatter in spec + size_t colonPos = spec.find(':'); + if (colonPos == string::npos) { + // simple replacement + r.append(props->getStringValue(spec)); + } else { + string format = spec.substr(colonPos + 1); + string prop = spec.substr(0, colonPos); + r.append(formatPropertyValue(props->getNode(prop), format)); + } + + lastPos = endReplacement + 1; + } + + return r; + } +}; + +////////////////////////////////////////////////////////////////// + +NavDisplay::NavDisplay(SGPropertyNode *node) : + _name(node->getStringValue("name", "nd")), + _num(node->getIntValue("number", 0)), + _time(0.0), + _updateInterval(node->getDoubleValue("update-interval-sec", 0.1)), + _forceUpdate(true), + _odg(0), + _scale(0), + _view_heading(0), + _font_size(0), + _font_spacing(0), + _rangeNm(0), + _maxSymbols(100) +{ + _Instrument = fgGetNode(string("/instrumentation/" + _name).c_str(), _num, true); + _font_node = _Instrument->getNode("font", true); + +#define INITFONT(p, val, type) if (!_font_node->hasValue(p)) _font_node->set##type##Value(p, val) + INITFONT("name", DEFAULT_FONT, String); + INITFONT("size", 8, Float); + INITFONT("line-spacing", 0.25, Float); + INITFONT("color/red", 0, Float); + INITFONT("color/green", 0.8, Float); + INITFONT("color/blue", 0, Float); + INITFONT("color/alpha", 1, Float); +#undef INITFONT + + _textureSize = _Instrument->getNode("symbol-texture-size", true)->getIntValue(); + SGPropertyNode* symbolsNode = node->getNode("symbols"); + SGPropertyNode* symbol; + + map definitionDict; + for (int i = 0; (symbol = symbolsNode->getChild("symbol", i)) != NULL; ++i) { + SymbolDef* def = new SymbolDef; + if (!def->initFromNode(symbol, this)) { + delete def; + continue; + } + + const char* id = symbol->getStringValue("id"); + if (id && strlen(id)) { + definitionDict[id] = def; + } + + _definitions.push_back(def); + } // of symbol definition parsing + + BOOST_FOREACH(SGPropertyNode* rule, symbolsNode->getChildren("rule")) { + SymbolRule* r = new SymbolRule; + if (!r->initFromNode(rule, this)) { + delete r; + continue; + } + + const char* id = rule->getStringValue("symbol"); + if (id && strlen(id) && (definitionDict.find(id) != definitionDict.end())) { + r->setDefinition(definitionDict[id]); + } else { + SG_LOG(SG_INSTR, SG_WARN, "symbol rule has missing/unknown definition id:" << id); + delete r; + continue; + } + + addRule(r); + } + +} + + +NavDisplay::~NavDisplay() +{ + delete _odg; +} + +void +NavDisplay::init () +{ + _cachedItemsValid = false; + _cacheListener.reset(new CacheListener(this)); + _forceUpdateListener.reset(new ForceUpdateListener(this)); + + _serviceable_node = _Instrument->getNode("serviceable", true); + _rangeNode = _Instrument->getNode("range", true); + if (!_rangeNode->hasValue()) { + _rangeNode->setDoubleValue(40.0); + } + _rangeNode->addChangeListener(_cacheListener.get()); + _rangeNode->addChangeListener(_forceUpdateListener.get()); + + _xCenterNode = _Instrument->getNode("x-center"); + if (!_xCenterNode->hasValue()) { + _xCenterNode->setDoubleValue(0.5); + } + _xCenterNode->addChangeListener(_forceUpdateListener.get()); + _yCenterNode = _Instrument->getNode("y-center"); + if (!_yCenterNode->hasValue()) { + _yCenterNode->setDoubleValue(0.5); + } + _yCenterNode->addChangeListener(_forceUpdateListener.get()); + + // texture name to use in 2D and 3D instruments + _texture_path = _Instrument->getStringValue("radar-texture-path", + "Aircraft/Instruments/Textures/od_wxradar.rgb"); + + string path = _Instrument->getStringValue("symbol-texture-path", + "Aircraft/Instruments/Textures/nd-symbols.png"); + SGPath tpath = globals->resolve_aircraft_path(path); + if (!tpath.exists()) { + SG_LOG(SG_INSTR, SG_WARN, "ND symbol texture not found:" << path); + } + + // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect + _symbolTexture = SGLoadTexture2D(tpath, NULL, false, false); + + _odg = new FGODGauge; + _odg->setSize(_Instrument->getIntValue("texture-size", 512)); + + _route = static_cast(globals->get_subsystem("route-manager")); + + _navRadio1Node = fgGetNode("/instrumentation/nav[0]", true); + _navRadio2Node = fgGetNode("/instrumentation/nav[1]", true); + + _excessDataNode = _Instrument->getChild("excess-data", 0, true); + _excessDataNode->setBoolValue(false); + _testModeNode = _Instrument->getChild("test-mode", 0, true); + _testModeNode->setBoolValue(false); + + _viewHeadingNode = _Instrument->getChild("view-heading-deg", 0, true); + _userLatNode = _Instrument->getChild("user-latitude-deg", 0, true); + _userLonNode = _Instrument->getChild("user-longitude-deg", 0, true); + _userPositionEnable = _Instrument->getChild("user-position", 0, true); + + _customSymbols = _Instrument->getChild("symbols", 0, true); + +// OSG geometry setup + _radarGeode = new osg::Geode; + + _geom = new osg::Geometry; + _geom->setUseDisplayList(false); + + osg::StateSet *stateSet = _geom->getOrCreateStateSet(); + stateSet->setTextureAttributeAndModes(0, _symbolTexture.get()); + stateSet->setDataVariance(osg::Object::STATIC); + + // Initially allocate space for 128 quads + _vertices = new osg::Vec2Array; + _vertices->setDataVariance(osg::Object::DYNAMIC); + _vertices->reserve(128 * 4); + _geom->setVertexArray(_vertices); + _texCoords = new osg::Vec2Array; + _texCoords->setDataVariance(osg::Object::DYNAMIC); + _texCoords->reserve(128 * 4); + _geom->setTexCoordArray(0, _texCoords); + + _quadColors = new osg::Vec4Array; + _quadColors->setDataVariance(osg::Object::DYNAMIC); + _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + _geom->setColorArray(_quadColors); + + _symbolPrimSet = new osg::DrawArrays(osg::PrimitiveSet::QUADS); + _symbolPrimSet->setDataVariance(osg::Object::DYNAMIC); + _geom->addPrimitiveSet(_symbolPrimSet); + + _geom->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f), + osg::Vec3f(256.0f, 256.0f, 0.0f))); + + _radarGeode->addDrawable(_geom); + _odg->allocRT(); + // Texture in the 2D panel system + FGTextureManager::addTexture(_texture_path.c_str(), _odg->getTexture()); + + _lineGeometry = new osg::Geometry; + _lineGeometry->setUseDisplayList(false); + stateSet = _lineGeometry->getOrCreateStateSet(); + osg::LineWidth *lw = new osg::LineWidth(); + lw->setWidth(2.0); + stateSet->setAttribute(lw); + + _lineVertices = new osg::Vec2Array; + _lineVertices->setDataVariance(osg::Object::DYNAMIC); + _lineVertices->reserve(128 * 4); + _lineGeometry->setVertexArray(_lineVertices); + + + _lineColors = new osg::Vec4Array; + _lineColors->setDataVariance(osg::Object::DYNAMIC); + _lineGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + _lineGeometry->setColorArray(_lineColors); + + _linePrimSet = new osg::DrawArrays(osg::PrimitiveSet::LINES); + _linePrimSet->setDataVariance(osg::Object::DYNAMIC); + _lineGeometry->addPrimitiveSet(_linePrimSet); + + _lineGeometry->setInitialBound(osg::BoundingBox(osg::Vec3f(-256.0f, -256.0f, 0.0f), + osg::Vec3f(256.0f, 256.0f, 0.0f))); + + _radarGeode->addDrawable(_lineGeometry); + + _textGeode = new osg::Geode; + + osg::Camera *camera = _odg->getCamera(); + camera->addChild(_radarGeode.get()); + camera->addChild(_textGeode.get()); + osg::Texture2D* tex = _odg->getTexture(); + camera->setProjectionMatrixAsOrtho2D(0, tex->getTextureWidth(), + 0, tex->getTextureHeight()); + + updateFont(); +} + +void +NavDisplay::update (double delta_time_sec) +{ + if (!fgGetBool("sim/sceneryloaded", false)) { + return; + } + + if (!_odg || !_serviceable_node->getBoolValue()) { + _Instrument->setStringValue("status", ""); + return; + } + + if (_forceUpdate) { + _forceUpdate = false; + _time = 0.0; + } else { + _time += delta_time_sec; + if (_time < _updateInterval){ + return; + } + _time -= _updateInterval; + } + + _rangeNm = _rangeNode->getFloatValue(); + if (_testModeNode->getBoolValue()) { + _view_heading = 90; + } else if (_Instrument->getBoolValue("aircraft-heading-up", true)) { + _view_heading = fgGetDouble("/orientation/heading-deg"); + } else { + _view_heading = _Instrument->getFloatValue("heading-up-deg", 0.0); + } + _viewHeadingNode->setDoubleValue(_view_heading); + + double xCenterFrac = _xCenterNode->getDoubleValue(); + double yCenterFrac = _yCenterNode->getDoubleValue(); + int pixelSize = _odg->size(); + + int rangePixels = _Instrument->getIntValue("range-pixels", -1); + if (rangePixels < 0) { + // hacky - assume (as is very common) that x-frac doesn't vary, and + // y-frac is used to position the center at either the top or bottom of + // the pixel area. Measure from the center to the furthest edge (top or bottom) + rangePixels = pixelSize * std::max(fabs(1.0 - yCenterFrac), fabs(yCenterFrac)); + } + + _scale = rangePixels / _rangeNm; + _Instrument->setDoubleValue("scale", _scale); + + + _centerTrans = osg::Matrixf::translate(xCenterFrac * pixelSize, + yCenterFrac * pixelSize, 0.0); + +// scale from nm to display units, rotate so aircraft heading is up +// (as opposed to north), and compensate for centering + _projectMat = osg::Matrixf::scale(_scale, _scale, 1.0) * + degRotation(-_view_heading) * _centerTrans; + + if (_userPositionEnable->getBoolValue()) { + _pos = SGGeod::fromDeg(_userLonNode->getDoubleValue(), _userLatNode->getDoubleValue()); + } else { + _pos = globals->get_aircraft_position(); + } + + // invalidate the cache of positioned items, if we travelled more than 1nm + if (_cachedItemsValid) { + SGVec3d cartNow(SGVec3d::fromGeod(_pos)); + double movedNm = dist(_cachedPos, cartNow) * SG_METER_TO_NM; + _cachedItemsValid = (movedNm < 1.0); + } + + _vertices->clear(); + _lineVertices->clear(); + _lineColors->clear(); + _quadColors->clear(); + _texCoords->clear(); + _textGeode->removeDrawables(0, _textGeode->getNumDrawables()); + + BOOST_FOREACH(SymbolInstance* si, _symbols) { + delete si; + } + _symbols.clear(); + + BOOST_FOREACH(SymbolDef* d, _definitions) { + d->instanceCount = 0; + d->textEnabled = d->textEnable.get() ? d->textEnable->test() : true; + } + + bool enableChanged = false; + BOOST_FOREACH(SymbolRule* r, _rules) { + enableChanged |= r->checkEnabled(); + } + + if (enableChanged) { + SG_LOG(SG_INSTR, SG_INFO, "NS rule enables changed, rebuilding cache"); + _cachedItemsValid = false; + } + + if (_testModeNode->getBoolValue()) { + addTestSymbols(); + } else { + processRoute(); + processNavRadios(); + processAI(); + processCustomSymbols(); + findItems(); + limitDisplayedSymbols(); + } + + addSymbolsToScene(); + + _symbolPrimSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size()); + _symbolPrimSet->dirty(); + _linePrimSet->set(osg::PrimitiveSet::LINES, 0, _lineVertices->size()); + _linePrimSet->dirty(); +} + + +void +NavDisplay::updateFont() +{ + float red = _font_node->getFloatValue("color/red"); + float green = _font_node->getFloatValue("color/green"); + float blue = _font_node->getFloatValue("color/blue"); + float alpha = _font_node->getFloatValue("color/alpha"); + _font_color.set(red, green, blue, alpha); + + _font_size = _font_node->getFloatValue("size"); + _font_spacing = _font_size * _font_node->getFloatValue("line-spacing"); + string path = _font_node->getStringValue("name", DEFAULT_FONT); + + SGPath tpath; + if (path[0] != '/') { + tpath = globals->get_fg_root(); + tpath.append("Fonts"); + tpath.append(path); + } else { + tpath = path; + } + + osg::ref_ptr fontOptions = new osgDB::ReaderWriter::Options("monochrome"); + osg::ref_ptr font = osgText::readFontFile(tpath.c_str(), fontOptions.get()); + + if (font != 0) { + _font = font; + _font->setMinFilterHint(osg::Texture::NEAREST); + _font->setMagFilterHint(osg::Texture::NEAREST); + _font->setGlyphImageMargin(0); + _font->setGlyphImageMarginRatio(0); + } +} + +void NavDisplay::addSymbolToScene(SymbolInstance* sym) +{ + SymbolDef* def = sym->definition; + + osg::Vec2 verts[4]; + verts[0] = def->xy0; + verts[1] = osg::Vec2(def->xy1.x(), def->xy0.y()); + verts[2] = def->xy1; + verts[3] = osg::Vec2(def->xy0.x(), def->xy1.y()); + + if (def->rotateToHeading) { + osg::Matrixf m(degRotation(sym->headingDeg - _view_heading)); + for (int i=0; i<4; ++i) { + verts[i] = mult(verts[i], m); + } + } + + osg::Vec2 pos = sym->pos; + if (def->roundPos) { + pos = osg::Vec2((int) pos.x(), (int) pos.y()); + } + + _texCoords->push_back(def->uv0); + _texCoords->push_back(osg::Vec2(def->uv1.x(), def->uv0.y())); + _texCoords->push_back(def->uv1); + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->uv1.y())); + + for (int i=0; i<4; ++i) { + _vertices->push_back(verts[i] + pos); + _quadColors->push_back(def->color); + } + + if (def->stretchSymbol) { + osg::Vec2 stretchVerts[4]; + stretchVerts[0] = osg::Vec2(def->xy0.x(), def->stretchY2); + stretchVerts[1] = osg::Vec2(def->xy1.x(), def->stretchY2); + stretchVerts[2] = osg::Vec2(def->xy1.x(), def->stretchY3); + stretchVerts[3] = osg::Vec2(def->xy0.x(), def->stretchY3); + + osg::Matrixf m(degRotation(sym->headingDeg - _view_heading)); + for (int i=0; i<4; ++i) { + stretchVerts[i] = mult(stretchVerts[i], m); + } + + // stretched quad + _vertices->push_back(verts[2] + pos); + _vertices->push_back(stretchVerts[1] + sym->endPos); + _vertices->push_back(stretchVerts[0] + sym->endPos); + _vertices->push_back(verts[3] + pos); + + _texCoords->push_back(def->uv1); + _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV2)); + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV2)); + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->uv1.y())); + + for (int i=0; i<4; ++i) { + _quadColors->push_back(def->color); + } + + // quad three, for the end portion + for (int i=0; i<4; ++i) { + _vertices->push_back(stretchVerts[i] + sym->endPos); + _quadColors->push_back(def->color); + } + + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV2)); + _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV2)); + _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV3)); + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV3)); + } + + if (def->drawLine) { + addLine(sym->pos, sym->endPos, def->lineColor); + } + + if (!def->hasText || !def->textEnabled) { + return; + } + + osgText::Text* t = new osgText::Text; + t->setFont(_font.get()); + t->setFontResolution(12, 12); + t->setCharacterSize(_font_size); + t->setLineSpacing(_font_spacing); + t->setColor(def->textColor); + t->setAlignment(def->alignment); + t->setText(sym->text()); + + + osg::Vec2 textPos = def->textOffset + pos; +// ensure we use ints here, or text visual quality goes bad + t->setPosition(osg::Vec3((int)textPos.x(), (int)textPos.y(), 0)); + _textGeode->addDrawable(t); +} + +class OrderByPriority +{ +public: + bool operator()(SymbolInstance* a, SymbolInstance* b) + { + return a->definition->priority > b->definition->priority; + } +}; + +void NavDisplay::limitDisplayedSymbols() +{ +// gloabl symbol limit + _maxSymbols= _Instrument->getIntValue("max-symbols", _maxSymbols); + if ((int) _symbols.size() <= _maxSymbols) { + _excessDataNode->setBoolValue(false); + return; + } + + std::sort(_symbols.begin(), _symbols.end(), OrderByPriority()); + _symbols.resize(_maxSymbols); + _excessDataNode->setBoolValue(true); +} + +class OrderByZ +{ +public: + bool operator()(SymbolInstance* a, SymbolInstance* b) + { + return a->definition->zOrder > b->definition->zOrder; + } +}; + +void NavDisplay::addSymbolsToScene() +{ + std::sort(_symbols.begin(), _symbols.end(), OrderByZ()); + BOOST_FOREACH(SymbolInstance* sym, _symbols) { + addSymbolToScene(sym); + } +} + +void NavDisplay::addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec4& color) +{ + _lineVertices->push_back(a); + _lineVertices->push_back(b); + _lineColors->push_back(color); + _lineColors->push_back(color); +} + +osg::Vec2 NavDisplay::projectBearingRange(double bearingDeg, double rangeNm) const +{ + osg::Vec3 p(0, rangeNm, 0.0); + p = degRotation(bearingDeg).preMult(p); + p = _projectMat.preMult(p); + return osg::Vec2(p.x(), p.y()); +} + +osg::Vec2 NavDisplay::projectGeod(const SGGeod& geod) const +{ + double rangeM, bearing, az2; + SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM); + return projectBearingRange(bearing, rangeM * SG_METER_TO_NM); +} + +class Filter : public FGPositioned::Filter +{ +public: + Filter(NavDisplay* nd) : _owner(nd) { } + + double minRunwayLengthFt; + + virtual bool pass(FGPositioned* aPos) const + { + if (aPos->type() == FGPositioned::FIX) { + string ident(aPos->ident()); + // ignore fixes which end in digits + if ((ident.size() > 4) && isdigit(ident[3]) && isdigit(ident[4])) { + return false; + } + } + + if (aPos->type() == FGPositioned::AIRPORT) { + FGAirport* apt = (FGAirport*) aPos; + if (!apt->hasHardRunwayOfLengthFt(minRunwayLengthFt)) { + return false; + } + } + + // check against current rule states + return _owner->isPositionedShown(aPos); + } + + virtual FGPositioned::Type minType() const { + return FGPositioned::AIRPORT; + } + + virtual FGPositioned::Type maxType() const { + return FGPositioned::OBSTACLE; + } + +private: + NavDisplay* _owner; +}; + +void NavDisplay::findItems() +{ + if (!_cachedItemsValid) { + Filter filt(this); + filt.minRunwayLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 2000); + bool wasTimeLimited; + _itemsInRange = FGPositioned::findClosestNPartial(_pos, _maxSymbols, _rangeNm, + &filt, wasTimeLimited); + _cachedItemsValid = true; + _cachedPos = SGVec3d::fromGeod(_pos); + + if (wasTimeLimited) { + // re-query next frame, to load incrementally + _cachedItemsValid = false; + } + } + + // sort by distance from pos, so symbol limits are accurate + FGPositioned::sortByRange(_itemsInRange, _pos); + + BOOST_FOREACH(FGPositioned* pos, _itemsInRange) { + foundPositionedItem(pos); + } +} + +void NavDisplay::processRoute() +{ + _routeSources.clear(); + flightgear::FlightPlan* fp = _route->flightPlan(); + RoutePath path(fp); + int current = _route->currentIndex(); + + for (int l=0; lnumLegs(); ++l) { + flightgear::FlightPlan::Leg* leg = fp->legAtIndex(l); + flightgear::WayptRef wpt(leg->waypoint()); + _routeSources.insert(wpt->source()); + + string_set state; + state.insert("on-active-route"); + + if (l < current) { + state.insert("passed"); + } + + if (l == current) { + state.insert("current-wp"); + } + + if (l > current) { + state.insert("future"); + } + + if (l == (current + 1)) { + state.insert("next-wp"); + } + + SymbolRuleVector rules; + findRules("waypoint" , state, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + SGGeod g = path.positionForIndex(l); + SGPropertyNode* vars = _route->wayptNodeAtIndex(l); + if (!vars) { + continue; // shouldn't happen, but let's guard against it + } + + double heading; + computeWayptPropsAndHeading(wpt, g, vars, heading); + + osg::Vec2 projected = projectGeod(g); + BOOST_FOREACH(SymbolRule* r, rules) { + addSymbolInstance(projected, heading, r->getDefinition(), vars); + + if (r->getDefinition()->drawRouteLeg) { + SGGeodVec gv(path.pathForIndex(l)); + if (!gv.empty()) { + osg::Vec2 pr = projectGeod(gv[0]); + for (unsigned int i=1; igetDefinition()->lineColor); + pr = p; + } + } + } // of leg drawing enabled + } // of matching rules iteration + } // of waypoints iteration +} + +void NavDisplay::computeWayptPropsAndHeading(flightgear::Waypt* wpt, const SGGeod& pos, SGPropertyNode* nd, double& heading) +{ + double rangeM, az2; + SGGeodesy::inverse(_pos, pos, heading, az2, rangeM); + nd->setIntValue("radial", heading); + nd->setDoubleValue("distance-nm", rangeM * SG_METER_TO_NM); + + heading = nd->getDoubleValue("leg-bearing-true-deg"); +} + +void NavDisplay::processNavRadios() +{ + _nav1Station = processNavRadio(_navRadio1Node); + _nav2Station = processNavRadio(_navRadio2Node); + + foundPositionedItem(_nav1Station); + foundPositionedItem(_nav2Station); +} + +FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio) +{ + double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0); + FGNavRecord* nav = FGNavList::findByFreq(mhz, _pos, FGNavList::navFilter()); + if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) { + // station was not found + return NULL; + } + + + return nav; +} + +bool NavDisplay::anyRuleForType(const string& type) const +{ + BOOST_FOREACH(SymbolRule* r, _rules) { + if (!r->enabled) { + continue; + } + + if (r->type == type) { + return true; + } + } + + return false; +} + +void NavDisplay::findRules(const string& type, const string_set& states, SymbolRuleVector& rules) +{ + BOOST_FOREACH(SymbolRule* candidate, _rules) { + if (!candidate->enabled || (candidate->type != type)) { + continue; + } + + if (candidate->matches(states)) { + rules.push_back(candidate); + } + } +} + +bool NavDisplay::isPositionedShown(FGPositioned* pos) +{ + SymbolRuleVector rules; + isPositionedShownInner(pos, rules); + return !rules.empty(); +} + +void NavDisplay::isPositionedShownInner(FGPositioned* pos, SymbolRuleVector& rules) +{ + string type = FGPositioned::nameForType(pos->type()); + boost::to_lower(type); + if (!anyRuleForType(type)) { + return; // not diplayed at all, we're done + } + + string_set states; + computePositionedState(pos, states); + + findRules(type, states, rules); +} + +void NavDisplay::foundPositionedItem(FGPositioned* pos) +{ + if (!pos) { + return; + } + + SymbolRuleVector rules; + isPositionedShownInner(pos, rules); + if (rules.empty()) { + return; + } + + SGPropertyNode_ptr vars(new SGPropertyNode); + double heading; + computePositionedPropsAndHeading(pos, vars, heading); + + osg::Vec2 projected = projectGeod(pos->geod()); + if (pos->type() == FGPositioned::RUNWAY) { + FGRunway* rwy = (FGRunway*) pos; + projected = projectGeod(rwy->threshold()); + } + + BOOST_FOREACH(SymbolRule* r, rules) { + SymbolInstance* ins = addSymbolInstance(projected, heading, r->getDefinition(), vars); + if ((ins)&&(pos->type() == FGPositioned::RUNWAY)) { + FGRunway* rwy = (FGRunway*) pos; + ins->endPos = projectGeod(rwy->end()); + } + } +} + +void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyNode* nd, double& heading) +{ + nd->setStringValue("id", pos->ident()); + nd->setStringValue("name", pos->name()); + nd->setDoubleValue("elevation-ft", pos->elevation()); + nd->setIntValue("heading-deg", 0); + heading = 0.0; + + switch (pos->type()) { + case FGPositioned::VOR: + case FGPositioned::LOC: + case FGPositioned::TACAN: { + FGNavRecord* nav = static_cast(pos); + nd->setDoubleValue("frequency-mhz", nav->get_freq()); + + if (pos == _nav1Station) { + heading = _navRadio1Node->getDoubleValue("radials/target-radial-deg"); + } else if (pos == _nav2Station) { + heading = _navRadio2Node->getDoubleValue("radials/target-radial-deg"); + } + + nd->setIntValue("heading-deg", heading); + break; + } + + case FGPositioned::AIRPORT: + case FGPositioned::SEAPORT: + case FGPositioned::HELIPORT: + + break; + + case FGPositioned::RUNWAY: { + FGRunway* rwy = static_cast(pos); + heading = rwy->headingDeg(); + nd->setDoubleValue("heading-deg", heading); + nd->setIntValue("length-ft", rwy->lengthFt()); + nd->setStringValue("airport", rwy->airport()->ident()); + break; + } + + default: + break; + } +} + +void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states) +{ + if (_routeSources.count(pos) != 0) { + states.insert("on-active-route"); + } + + flightgear::FlightPlan* fp = _route->flightPlan(); + switch (pos->type()) { + case FGPositioned::VOR: + case FGPositioned::LOC: + if (pos == _nav1Station) { + states.insert("tuned"); + states.insert("nav1"); + } + + if (pos == _nav2Station) { + states.insert("tuned"); + states.insert("nav2"); + } + break; + + case FGPositioned::AIRPORT: + case FGPositioned::SEAPORT: + case FGPositioned::HELIPORT: + // mark alternates! + // once the FMS system has some way to tell us about them, of course + + if (pos == fp->departureAirport()) { + states.insert("departure"); + } + + if (pos == fp->destinationAirport()) { + states.insert("destination"); + } + break; + + case FGPositioned::RUNWAY: + if (pos == fp->departureRunway()) { + states.insert("departure"); + } + + if (pos == fp->destinationRunway()) { + states.insert("destination"); + } + break; + + case FGPositioned::OBSTACLE: + #if 0 + FGObstacle* obs = (FGObstacle*) pos; + if (obj->isLit()) { + states.insert("lit"); + } + + if (obj->getHeightAGLFt() >= 1000) { + states.insert("greater-1000-ft"); + } + #endif + break; + + default: + break; + } // FGPositioned::Type switch +} + +static string mapAINodeToType(SGPropertyNode* model) +{ + // assume all multiplayer items are aircraft for the moment. Not ideal. + if (!strcmp(model->getName(), "multiplayer")) { + return "ai-aircraft"; + } + + return string("ai-") + model->getName(); +} + +void NavDisplay::processAI() +{ + SGPropertyNode *ai = fgGetNode("/ai/models", true); + for (int i = ai->nChildren() - 1; i >= 0; i--) { + SGPropertyNode *model = ai->getChild(i); + if (!model->nChildren()) { + continue; + } + + // prefix types with 'ai-', to avoid any chance of namespace collisions + // with fg-positioned. + string_set ss; + computeAIStates(model, ss); + SymbolRuleVector rules; + findRules(mapAINodeToType(model), ss, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + double heading = model->getDoubleValue("orientation/true-heading-deg"); + SGGeod aiModelPos = SGGeod::fromDegFt(model->getDoubleValue("position/longitude-deg"), + model->getDoubleValue("position/latitude-deg"), + model->getDoubleValue("position/altitude-ft")); + // compute some additional props + int fl = (aiModelPos.getElevationFt() / 1000); + model->setIntValue("flight-level", fl * 10); + + osg::Vec2 projected = projectGeod(aiModelPos); + BOOST_FOREACH(SymbolRule* r, rules) { + addSymbolInstance(projected, heading, r->getDefinition(), (SGPropertyNode*) model); + } + } // of ai models iteration +} + +void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states) +{ + int threatLevel = ai->getIntValue("tcas/threat-level",-1); + if (threatLevel < 1) + threatLevel = 0; + + states.insert("tcas"); + + std::ostringstream os; + os << "tcas-threat-level-" << threatLevel; + states.insert(os.str()); + + double vspeed = ai->getDoubleValue("velocities/vertical-speed-fps"); + if (vspeed < -3.0) { + states.insert("descending"); + } else if (vspeed > 3.0) { + states.insert("climbing"); + } +} + +SymbolInstance* NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars) +{ + if (isProjectedClipped(proj)) { + return NULL; + } + + if ((def->limitCount > 0) && (def->instanceCount >= def->limitCount)) { + return NULL; + } + + ++def->instanceCount; + SymbolInstance* sym = new SymbolInstance(proj, heading, def, vars); + _symbols.push_back(sym); + return sym; +} + +bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const +{ + double size = _odg->size(); + return (projected.x() < 0.0) || + (projected.y() < 0.0) || + (projected.x() >= size) || + (projected.y() >= size); +} + +void NavDisplay::addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars) +{ + string_set stateSet; + BOOST_FOREACH(std::string s, simgear::strutils::split(states, ",")) { + stateSet.insert(s); + } + + SymbolRuleVector rules; + findRules(type, stateSet, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + osg::Vec2 projected = projectGeod(pos); + BOOST_FOREACH(SymbolRule* r, rules) { + addSymbolInstance(projected, heading, r->getDefinition(), vars); + } +} + +void NavDisplay::addTestSymbols() +{ + _pos = SGGeod::fromDeg(-122.3748889, 37.6189722); // KSFO + + SGGeod a1; + double dummy; + SGGeodesy::direct(_pos, 45.0, 20.0 * SG_NM_TO_METER, a1, dummy); + + addTestSymbol("airport", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 95.0, 40.0 * SG_NM_TO_METER, a1, dummy); + + addTestSymbol("vor", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 120, 80.0 * SG_NM_TO_METER, a1, dummy); + + addTestSymbol("airport", "destination", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 80.0, 20.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); + + + SGGeodesy::direct(_pos, 140.0, 20.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 110.0, 10.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 110.0, 5.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); +} + +void NavDisplay::addRule(SymbolRule* r) +{ + _rules.push_back(r); +} + +void NavDisplay::computeCustomSymbolStates(const SGPropertyNode* sym, string_set& states) +{ + BOOST_FOREACH(SGPropertyNode* st, sym->getChildren("state")) { + states.insert(st->getStringValue()); + } +} + +void NavDisplay::processCustomSymbols() +{ + for (int i = _customSymbols->nChildren() - 1; i >= 0; i--) { + SGPropertyNode *symNode = _customSymbols->getChild(i); + if (!symNode->nChildren()) { + continue; + } + string_set ss; + computeCustomSymbolStates(symNode, ss); + SymbolRuleVector rules; + findRules(symNode->getName(), ss, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + double heading = symNode->getDoubleValue("true-heading-deg", 0.0); + SGGeod pos = SGGeod::fromDegFt(symNode->getDoubleValue("longitude-deg"), + symNode->getDoubleValue("latitude-deg"), + symNode->getDoubleValue("altitude-ft")); + + + osg::Vec2 projected = projectGeod(pos); + BOOST_FOREACH(SymbolRule* r, rules) { + addSymbolInstance(projected, heading, r->getDefinition(), symNode); + } + } // of custom symbols iteration +} + + diff --git a/src/Cockpit/NavDisplay.hxx b/src/Cockpit/NavDisplay.hxx new file mode 100644 index 0000000..62e9d45 --- /dev/null +++ b/src/Cockpit/NavDisplay.hxx @@ -0,0 +1,207 @@ +// Wx Radar background texture +// +// Written by Harald JOHNSEN, started May 2005. +// With major amendments by Vivian MEAZZA May 2007 +// Ported to OSG by Tim MOORE Jun 2007 +// +// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef _INST_ND_HXX +#define _INST_ND_HXX + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +class FGODGauge; +class FGRouteMgr; +class FGNavRecord; + +class SymbolInstance; +class SymbolDef; +class SymbolRule; + +namespace flightgear +{ + class Waypt; +} + +typedef std::set string_set; +typedef std::vector SymbolRuleVector; +typedef std::vector SymbolDefVector; + +class NavDisplay : public SGSubsystem +{ +public: + + NavDisplay(SGPropertyNode *node); + virtual ~NavDisplay(); + + virtual void init(); + virtual void update(double dt); + + void invalidatePositionedCache() + { + _cachedItemsValid = false; + } + + double textureSize() const + { return _textureSize; } + + void forceUpdate() + { _forceUpdate = true; } + + bool anyRuleForType(const std::string& type) const; + bool isPositionedShown(FGPositioned* pos); +protected: + std::string _name; + int _num; + double _time; + double _updateInterval; + bool _forceUpdate; + + SGPropertyNode_ptr _serviceable_node; + SGPropertyNode_ptr _Instrument; + SGPropertyNode_ptr _radar_mode_control_node; + SGPropertyNode_ptr _user_heading_node; + SGPropertyNode_ptr _testModeNode; + SGPropertyNode_ptr _userLatNode, _userLonNode, _userPositionEnable; + + FGODGauge *_odg; + + // Convenience function for creating a property node with a + // default value + template + SGPropertyNode *getInstrumentNode(const char *name, DefaultType value); + +private: + friend class SymbolRule; + friend class SymbolDef; + + void addRule(SymbolRule*); + + void addSymbolsToScene(); + void addSymbolToScene(SymbolInstance* sym); + void limitDisplayedSymbols(); + + void findItems(); + void isPositionedShownInner(FGPositioned* pos, SymbolRuleVector& rules); + void foundPositionedItem(FGPositioned* pos); + void computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyNode* nd, double& heading); + void computePositionedState(FGPositioned* pos, string_set& states); + void processRoute(); + void computeWayptPropsAndHeading(flightgear::Waypt* wpt, const SGGeod& pos, SGPropertyNode* nd, double& heading); + void processNavRadios(); + FGNavRecord* processNavRadio(const SGPropertyNode_ptr& radio); + void processAI(); + void computeAIStates(const SGPropertyNode* ai, string_set& states); + + void computeCustomSymbolStates(const SGPropertyNode* sym, string_set& states); + void processCustomSymbols(); + + void findRules(const std::string& type, const string_set& states, SymbolRuleVector& rules); + + SymbolInstance* addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars); + void addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec4& color); + osg::Vec2 projectBearingRange(double bearingDeg, double rangeNm) const; + osg::Vec2 projectGeod(const SGGeod& geod) const; + bool isProjectedClipped(const osg::Vec2& projected) const; + void updateFont(); + + void addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars); + void addTestSymbols(); + + std::string _texture_path; + unsigned int _textureSize; + + float _scale; // factor to convert nm to display units + float _view_heading; + + SGPropertyNode_ptr _Radar_controls; + + + + SGPropertyNode_ptr _font_node; + SGPropertyNode_ptr _ai_enabled_node; + SGPropertyNode_ptr _navRadio1Node; + SGPropertyNode_ptr _navRadio2Node; + SGPropertyNode_ptr _xCenterNode, _yCenterNode; + SGPropertyNode_ptr _viewHeadingNode; + + osg::ref_ptr _symbolTexture; + osg::ref_ptr _radarGeode; + osg::ref_ptr _textGeode; + + osg::Geometry *_geom; + + osg::DrawArrays* _symbolPrimSet; + osg::Vec2Array *_vertices; + osg::Vec2Array *_texCoords; + osg::Vec4Array* _quadColors; + + osg::Geometry* _lineGeometry; + osg::DrawArrays* _linePrimSet; + osg::Vec2Array* _lineVertices; + osg::Vec4Array* _lineColors; + + + osg::Matrixf _centerTrans; + osg::Matrixf _projectMat; + + osg::ref_ptr _font; + osg::Vec4 _font_color; + float _font_size; + float _font_spacing; + + FGRouteMgr* _route; + SGGeod _pos; + double _rangeNm; + SGPropertyNode_ptr _rangeNode; + + SymbolDefVector _definitions; + SymbolRuleVector _rules; + FGNavRecord* _nav1Station; + FGNavRecord* _nav2Station; + std::vector _symbols; + std::set _routeSources; + + bool _cachedItemsValid; + SGVec3d _cachedPos; + FGPositioned::List _itemsInRange; + SGPropertyNode_ptr _excessDataNode; + int _maxSymbols; + SGPropertyNode_ptr _customSymbols; + + class CacheListener; + std::auto_ptr _cacheListener; + + class ForceUpdateListener; + std::auto_ptr _forceUpdateListener; +}; + +#endif // _INST_ND_HXX diff --git a/src/Cockpit/agradar.cxx b/src/Cockpit/agradar.cxx new file mode 100644 index 0000000..768fdea --- /dev/null +++ b/src/Cockpit/agradar.cxx @@ -0,0 +1,325 @@ +// Air Ground Radar +// +// Written by Vivian MEAZZA, started Feb 2008. +// +// +// Copyright (C) 2008 Vivian Meazza +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include
+#include
+#include "agradar.hxx" + + +agRadar::agRadar(SGPropertyNode *node) : wxRadarBg(node) +{ + + _name = node->getStringValue("name", "air-ground-radar"); + _num = node->getIntValue("number", 0); + +} + +agRadar::~agRadar () +{ +} + +void +agRadar::init () +{ + _user_hdg_deg_node = fgGetNode("/orientation/heading-deg", true); + _user_pitch_deg_node = fgGetNode("/orientation/pitch-deg", true); + _user_roll_deg_node = fgGetNode("/orientation/roll-deg", true); + + _terrain_warning_node = fgGetNode("/sim/alarms/terrain-warning", true); + _terrain_warning_node->setBoolValue(false); + + wxRadarBg::init(); + + // those properties are used by a radar instrument of a MFD + // input switch = OFF | TST | STBY | ON + // input mode = WX | WXA | MAP | TW + // output status = STBY | TEST | WX | WXA | MAP | blank + // input lightning = true | false + // input TRK = +/- n degrees + // input TILT = +/- n degree + // input autotilt = true | false + // input range = n nm (20/40/80) + // input display-mode = arc | rose | map | plan + + _Instrument->setFloatValue("trk", 0.0); + _Instrument->setFloatValue("tilt",-2.5); + _Instrument->setStringValue("status",""); + _Instrument->setIntValue("mode-control", 5); + + _Instrument->setBoolValue("stabilisation/roll", false); + _Instrument->setBoolValue("stabilisation/pitch", false); + + _xOffsetMNode = getInstrumentNode("antenna/x-offset-m", 0.0); + _yOffsetMNode = getInstrumentNode("antenna/y-offset-m", 0.0); + _zOffsetMNode = getInstrumentNode("antenna/z-offset-m", 0.0); + + _elevLimitDegNode = getInstrumentNode("terrain-warning/elev-limit-deg", 2.0); + _elevStepDegNode = getInstrumentNode("terrain-warning/elev-step-deg", 1.0); + _azLimitDegNode = getInstrumentNode("terrain-warning/az-limit-deg", 1.0); + _azStepDegNode = getInstrumentNode("terrain-warning/az-step-deg", 1.5); + _maxRangeMNode = getInstrumentNode("terrain-warning/max-range-m", 4000.0); + _minRangeMNode = getInstrumentNode("terrain-warning/min-range-m", 250.0); + _tiltNode = getInstrumentNode("terrain-warning/tilt", -2.0); + + _brgDegNode = getInstrumentNode("terrain-warning/hit/brg-deg", 0.0); + _rangeMNode = getInstrumentNode("terrain-warning/hit/range-m", 0.0); + _elevationMNode = getInstrumentNode("terrain-warning/hit/elevation-m", 0.0); + _materialNode = getInstrumentNode("terrain-warning/hit/material", ""); + _bumpinessNode = getInstrumentNode("terrain-warning/hit/bumpiness", 0.0); + + _rollStabNode = getInstrumentNode("terrain-warning/stabilisation/roll", + true); + _pitchStabNode = getInstrumentNode("terrain-warning/stabilisation/pitch", + false); +// cout << "init done" << endl; + +} + +void +agRadar::update (double delta_time_sec) +{ + if (!_sceneryLoaded->getBoolValue()) + return; + + if ( !_odg || ! _serviceable_node->getBoolValue() ) { + _Instrument->setStringValue("status",""); + return; + } + + _time += delta_time_sec; + + if (_time < _interval) + return; + + _time = 0.0; + + update_terrain(); +// wxRadarBg::update(delta_time_sec); +} + +void +agRadar::setUserPos() +{ + userpos.setLatitudeDeg(_user_lat_node->getDoubleValue()); + userpos.setLongitudeDeg(_user_lon_node->getDoubleValue()); + userpos.setElevationM(_user_alt_node->getDoubleValue() * SG_FEET_TO_METER); +} + +SGVec3d +agRadar::getCartUserPos() const { + SGVec3d cartUserPos = SGVec3d::fromGeod(userpos); + return cartUserPos; +} + +SGVec3d +agRadar::getCartAntennaPos() const { + + float yaw = _user_hdg_deg_node->getDoubleValue(); + float pitch = _user_pitch_deg_node->getDoubleValue(); + float roll = _user_roll_deg_node->getDoubleValue(); + + double x_offset_m =_xOffsetMNode->getDoubleValue(); + double y_offset_m =_yOffsetMNode->getDoubleValue(); + double z_offset_m =_zOffsetMNode->getDoubleValue(); + + // convert geodetic positions to geocentered + SGVec3d cartuserPos = getCartUserPos(); + + // Transform to the right coordinate frame, configuration is done in + // the x-forward, y-right, z-up coordinates (feet), computation + // in the simulation usual body x-forward, y-right, z-down coordinates + // (meters) ) + SGVec3d _off(x_offset_m, y_offset_m, -z_offset_m); + + // Transform the user position to the horizontal local coordinate system. + SGQuatd hlTrans = SGQuatd::fromLonLat(userpos); + + // and postrotate the orientation of the user model wrt the horizontal + // local frame + hlTrans *= SGQuatd::fromYawPitchRollDeg(yaw,pitch,roll); + + // The offset converted to the usual body fixed coordinate system + // rotated to the earth-fixed coordinates axis + SGVec3d off = hlTrans.backTransform(_off); + + // Add the position offset of the user model to get the geocentered position + SGVec3d offsetPos = cartuserPos + off; + + return offsetPos; +} + +void +agRadar::setAntennaPos() { + SGGeodesy::SGCartToGeod(getCartAntennaPos(), antennapos); +} + +void +agRadar::setUserVec(double az, double el) +{ + float yaw = _user_hdg_deg_node->getDoubleValue(); + float pitch = _user_pitch_deg_node->getDoubleValue(); + float roll = _user_roll_deg_node->getDoubleValue(); + double tilt = _Instrument->getDoubleValue("tilt"); + double trk = _Instrument->getDoubleValue("trk"); + bool roll_stab = _Instrument->getBoolValue("stabilisation/roll"); + bool pitch_stab = _Instrument->getBoolValue("stabilisation/pitch"); + + SGQuatd offset = SGQuatd::fromYawPitchRollDeg(az + trk, el + tilt, 0); + + // Transform the antenna position to the horizontal local coordinate system. + SGQuatd hlTrans = SGQuatd::fromLonLat(antennapos); + + // and postrotate the orientation of the radar wrt the horizontal + // local frame + hlTrans *= SGQuatd::fromYawPitchRollDeg(yaw, + pitch_stab ? 0 :pitch, + roll_stab ? 0 : roll); + hlTrans *= offset; + + // now rotate the rotation vector back into the + // earth centered frames coordinates + SGVec3d angleaxis(1,0,0); + uservec = hlTrans.backTransform(angleaxis); + +} + +bool +agRadar::getMaterial(){ + + const simgear::BVHMaterial* mat = 0; + if (globals->get_scenery()->get_elevation_m(hitpos, _elevation_m, &mat)){ + //_ht_agl_ft = pos.getElevationFt() - _elevation_m * SG_METER_TO_FEET; + const SGMaterial* material = dynamic_cast(mat); + if (material) { + const std::vector& names = material->get_names(); + + _solid = material->get_solid(); + _load_resistance = material->get_load_resistance(); + _frictionFactor = material->get_friction_factor(); + _bumpinessFactor = material->get_bumpiness(); + + if (!names.empty()) + _mat_name = names[0]; + else + _mat_name = ""; + + } + /*cout << "material " << mat_name + << " solid " << _solid + << " load " << _load_resistance + << " frictionFactor " << frictionFactor + << " _bumpinessFactor " << _bumpinessFactor + << endl;*/ + return true; + } else { + return false; + } + +} + +void +agRadar::update_terrain() +{ + int mode = _radar_mode_control_node->getIntValue(); + + double el_limit = 1; + double el_step = 1; + double az_limit = 50; + double az_step = 10; + double max_range = 40000; + double min_range = 250; + double tilt = -2.5; + bool roll_stab = _rollStabNode->getBoolValue(); + bool pitch_stab = _pitchStabNode->getBoolValue(); + const char* status = ""; + bool hdg_mkr = true; + + if (mode == 5){ + status = "TW"; + hdg_mkr = false; + tilt = _tiltNode->getDoubleValue(); + el_limit = _elevLimitDegNode->getDoubleValue(); + el_step = _elevStepDegNode->getDoubleValue(); + az_limit = _azLimitDegNode->getDoubleValue(); + az_step = _azStepDegNode->getDoubleValue(); + max_range = _maxRangeMNode->getDoubleValue(); + min_range = _minRangeMNode->getDoubleValue(); + } + + _Instrument->setDoubleValue("tilt", tilt); + _Instrument->setBoolValue("stabilisation/roll", roll_stab); + _Instrument->setBoolValue("stabilisation/pitch", pitch_stab); + _Instrument->setStringValue("status", status); + _Instrument->setDoubleValue("limit-deg", az_limit); + _Instrument->setBoolValue("heading-marker", hdg_mkr); + setUserPos(); + setAntennaPos(); + SGVec3d cartantennapos = getCartAntennaPos(); + + for(double brg = -az_limit; brg <= az_limit; brg += az_step){ + for(double elev = el_limit; elev >= - el_limit; elev -= el_step){ + setUserVec(brg, elev); + SGVec3d nearestHit; + globals->get_scenery()->get_cart_ground_intersection(cartantennapos, uservec, nearestHit); + SGGeodesy::SGCartToGeod(nearestHit, hitpos); + + double course1, course2, distance; + + SGGeodesy::inverse(hitpos, antennapos, course1, course2, distance); + + if (distance >= min_range && distance <= max_range) { + _terrain_warning_node->setBoolValue(true); + getMaterial(); + _brgDegNode->setDoubleValue(course2); + _rangeMNode->setDoubleValue(distance); + _materialNode->setStringValue(_mat_name.c_str()); + _bumpinessNode->setDoubleValue(_bumpinessFactor); + _elevationMNode->setDoubleValue(_elevation_m); + } else { + _terrain_warning_node->setBoolValue(false); + _brgDegNode->setDoubleValue(0); + _rangeMNode->setDoubleValue(0); + _materialNode->setStringValue(""); + _bumpinessNode->setDoubleValue(0); + _elevationMNode->setDoubleValue(0); + } + + //cout << "usr hdg " << _user_hdg_deg_node->getDoubleValue() + // << " ant brg " << course2 + // << " elev " << _Instrument->getDoubleValue("tilt") + // << " gnd rng nm " << distance * SG_METER_TO_NM + // << " ht " << hitpos.getElevationFt() + // << " mat " << _mat_name + // << " solid " << _solid + // << " bumpiness " << _bumpinessFactor + // << endl; + + } + } +} + + diff --git a/src/Cockpit/agradar.hxx b/src/Cockpit/agradar.hxx new file mode 100644 index 0000000..dda152e0b --- /dev/null +++ b/src/Cockpit/agradar.hxx @@ -0,0 +1,94 @@ +// Air Ground Radar +// +// Written by Vivian MEAZZA, started Feb 2008. +// +// +// Copyright (C) 2008 Vivain MEAZZA - vivian.meazza@lineone.net +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifndef _INST_AGRADAR_HXX +#define _INST_AGRADAR_HXX + +#include +#include +#include + +#include "wxradar.hxx" + +class agRadar : public wxRadarBg{ +public: + + agRadar ( SGPropertyNode *node ); + agRadar (); + virtual ~agRadar (); + + virtual void init (); + virtual void update (double dt); + + void setUserPos(); + void setUserVec(double az, double el); + void update_terrain(); + void setAntennaPos(); + + bool getMaterial(); + + double _load_resistance; // ground load resistanc N/m^2 + double _frictionFactor; // dimensionless modifier for Coefficient of Friction + double _bumpinessFactor; // dimensionless modifier for Bumpiness + double _elevation_m; // ground elevation in meters + bool _solid; // if true ground is solid for FDMs + + std::string _mat_name; // ground material + + SGVec3d getCartUserPos() const; + SGVec3d getCartAntennaPos()const; + + SGVec3d uservec; + + SGPropertyNode_ptr _user_hdg_deg_node; + SGPropertyNode_ptr _user_roll_deg_node; + SGPropertyNode_ptr _user_pitch_deg_node; + SGPropertyNode_ptr _terrain_warning_node; + + SGPropertyNode_ptr _xOffsetMNode; + SGPropertyNode_ptr _yOffsetMNode; + SGPropertyNode_ptr _zOffsetMNode; + + SGPropertyNode_ptr _elevLimitDegNode; + SGPropertyNode_ptr _elevStepDegNode; + SGPropertyNode_ptr _azLimitDegNode; + SGPropertyNode_ptr _azStepDegNode; + SGPropertyNode_ptr _maxRangeMNode; + SGPropertyNode_ptr _minRangeMNode; + SGPropertyNode_ptr _tiltNode; + + SGPropertyNode_ptr _brgDegNode; + SGPropertyNode_ptr _rangeMNode; + SGPropertyNode_ptr _elevationMNode; + SGPropertyNode_ptr _materialNode; + SGPropertyNode_ptr _bumpinessNode; + + SGPropertyNode_ptr _rollStabNode; + SGPropertyNode_ptr _pitchStabNode; + + SGGeod userpos; + SGGeod hitpos; + SGGeod antennapos; +}; + +#endif // _INST_AGRADAR_HXX diff --git a/src/Cockpit/cockpitDisplayManager.cxx b/src/Cockpit/cockpitDisplayManager.cxx new file mode 100644 index 0000000..18e6e7d --- /dev/null +++ b/src/Cockpit/cockpitDisplayManager.cxx @@ -0,0 +1,126 @@ +// cockpitDisplayManager.cxx -- manage cockpit displays, typically +// rendered using a sub-camera or render-texture +// +// Copyright (C) 2012 James Turner zakalawe@mac.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "cockpitDisplayManager.hxx" + +#include +#include +#include +#include + +#include + +#include
+#include
+ +#include "agradar.hxx" +#include "NavDisplay.hxx" +#include "groundradar.hxx" +#include "wxradar.hxx" + +namespace flightgear +{ + +CockpitDisplayManager::CockpitDisplayManager () +{ +} + +CockpitDisplayManager::~CockpitDisplayManager () +{ +} + +SGSubsystem::InitStatus CockpitDisplayManager::incrementalInit() +{ + init(); + return INIT_DONE; +} + +void CockpitDisplayManager::init() +{ + SGPropertyNode_ptr config_props = new SGPropertyNode; + SGPropertyNode* path_n = fgGetNode("/sim/instrumentation/path"); + if (!path_n) { + SG_LOG(SG_COCKPIT, SG_WARN, "No instrumentation model specified for this model!"); + return; + } + + SGPath config = globals->resolve_aircraft_path(path_n->getStringValue()); + SG_LOG( SG_COCKPIT, SG_INFO, "Reading cockpit displays from " << config.str() ); + + try { + readProperties( config.str(), config_props ); + if (!build(config_props)) { + throw sg_exception( + "Detected an internal inconsistency in the instrumentation\n" + "system specification file. See earlier errors for details."); + } + } catch (const sg_exception& e) { + SG_LOG(SG_COCKPIT, SG_ALERT, "Failed to load instrumentation system model: " + << config.str() << ":" << e.getFormattedMessage() ); + } + + // bind() created instruments before init. + BOOST_FOREACH(std::string s, _displays) { + get_subsystem(s)->bind(); + } + + SGSubsystemGroup::init(); +} + +bool CockpitDisplayManager::build (SGPropertyNode* config_props) +{ + for ( int i = 0; i < config_props->nChildren(); ++i ) { + SGPropertyNode *node = config_props->getChild(i); + std::string name = node->getName(); + + std::ostringstream subsystemname; + subsystemname << "instrument-" << i << '-' + << node->getStringValue("name", name.c_str()); + int index = node->getIntValue("number", 0); + if (index > 0) + subsystemname << '['<< index << ']'; + std::string id = subsystemname.str(); + + if ( name == "radar" ) { + set_subsystem( id, new wxRadarBg ( node ) ); + + } else if ( name == "groundradar" ) { + set_subsystem( id, new GroundRadar( node ) ); + + } else if ( name == "air-ground-radar" ) { + set_subsystem( id, new agRadar( node ) ); + + } else if ( name == "navigation-display" ) { + set_subsystem( id, new NavDisplay( node ) ); + + } else { + // probably a regular instrument + continue; + } + // only add to our list if we build a display + _displays.push_back(id); + } + return true; +} + +} // of namespace flightgear diff --git a/src/Cockpit/cockpitDisplayManager.hxx b/src/Cockpit/cockpitDisplayManager.hxx new file mode 100644 index 0000000..53febaf --- /dev/null +++ b/src/Cockpit/cockpitDisplayManager.hxx @@ -0,0 +1,54 @@ +// cockpitDisplayManager.hxx -- manage cockpit displays, typically +// rendered using a sub-camera or render-texture +// +// Copyright (C) 2012 James Turner zakalawe@mac.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef COCKPIT_DISPLAY_MGR_HXX +#define COCKPIT_DISPLAY_MGR_HXX 1 + +#include + +#include +#include + +class SGPropertyNode; + +namespace flightgear +{ + +/** + * Manage aircraft displays. + */ +class CockpitDisplayManager : public SGSubsystemGroup +{ +public: + + CockpitDisplayManager (); + virtual ~CockpitDisplayManager (); + + virtual void init(); + virtual InitStatus incrementalInit(); + +private: + bool build (SGPropertyNode* config_props); + + std::vector _displays; +}; + +} // of namespace lfightgear + +#endif // COCKPIT_DISPLAY_MGR_HXX diff --git a/src/Cockpit/groundradar.cxx b/src/Cockpit/groundradar.cxx new file mode 100644 index 0000000..b7d2834 --- /dev/null +++ b/src/Cockpit/groundradar.cxx @@ -0,0 +1,354 @@ +// groundradar.cxx - Background layer for the ATC radar. +// +// Copyright (C) 2007 Csaba Halasz. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include
+#include
+#include +#include +#include +#include +#include +#include +#include + +#include "groundradar.hxx" + +static const char* airport_source_node_name = "airport-id-source"; +static const char* default_airport_node_name = "/sim/airport/closest-airport-id"; +static const char* texture_node_name = "texture-name"; +static const char* default_texture_name = "Aircraft/Instruments/Textures/od_groundradar.rgb"; +static const char* range_source_node_name = "range-source"; +static const char* default_range_node_name = "/instrumentation/radar/range"; + +struct SingleFrameCallback : public osg::Camera::DrawCallback +{ + virtual void operator () (const osg::Camera& camera) const + { + const_cast(camera).setNodeMask(0); + } +}; + +GroundRadar::GroundRadar(SGPropertyNode *node) +{ + _airport_node = fgGetNode(node->getStringValue(airport_source_node_name, default_airport_node_name), true); + _range_node = fgGetNode(node->getStringValue(range_source_node_name, default_range_node_name), true); + createTexture(node->getStringValue(texture_node_name, default_texture_name)); + updateTexture(); + _airport_node->addChangeListener(this); + _range_node->addChangeListener(this); +} + +GroundRadar::~GroundRadar() +{ + _airport_node->removeChangeListener(this); + _range_node->removeChangeListener(this); +} + +void GroundRadar::update (double /* dt */) +{ + +} + +void GroundRadar::valueChanged(SGPropertyNode*) +{ + updateTexture(); +} + +inline static osg::Vec3 fromPolar(double fi, double r) +{ + return osg::Vec3(sin(fi * SGD_DEGREES_TO_RADIANS) * r, cos(fi * SGD_DEGREES_TO_RADIANS) * r, 0); +} + +void GroundRadar::createTexture(const char* texture_name) +{ + setSize(TextureHalfSize + TextureHalfSize); + allocRT(); + + _geode = new osg::Geode(); + osg::StateSet* stateset = _geode->getOrCreateStateSet(); + stateset->setMode(GL_BLEND, osg::StateAttribute::OFF); + + osg::Vec4Array* taxi_color = new osg::Vec4Array; + taxi_color->push_back(osg::Vec4(0.0f, 0.5f, 0.0f, 1.0f)); + osg::Vec4Array* rwy_color = new osg::Vec4Array; + rwy_color->push_back(osg::Vec4(0.0f, 0.5f, 0.5f, 1.0f)); + + osg::Geometry *taxi_geom = new osg::Geometry(); + taxi_geom->setColorArray(taxi_color); + taxi_geom->setColorBinding(osg::Geometry::BIND_OVERALL); + taxi_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); // Taxiways + _geode->addDrawable(taxi_geom); + + osg::Geometry *pvt_geom = new osg::Geometry(); + pvt_geom->setColorArray(taxi_color); + pvt_geom->setColorBinding(osg::Geometry::BIND_OVERALL); + // no primitive set for the moment. It needs tessellation + _geode->addDrawable(pvt_geom); + + osg::Geometry *rwy_geom = new osg::Geometry(); + rwy_geom->setColorArray(rwy_color); + rwy_geom->setColorBinding(osg::Geometry::BIND_OVERALL); + rwy_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); // Runways + _geode->addDrawable(rwy_geom); + + osg::Camera* camera = getCamera(); + camera->setPostDrawCallback(new SingleFrameCallback()); + camera->addChild(_geode.get()); + camera->setNodeMask(0); + camera->setProjectionMatrixAsOrtho2D(0, getTexture()->getTextureWidth(), 0, getTexture()->getTextureHeight()); + + // Texture in the 2D panel system + FGTextureManager::addTexture(texture_name, getTexture()); +} + +void GroundRadar::addRunwayVertices(const FGRunwayBase* aRunway, double aTowerLat, double aTowerLon, double aScale, osg::Vec3Array* aVertices) +{ + double az1, az2, dist_m; + geo_inverse_wgs_84(aTowerLat, aTowerLon, aRunway->latitude(), aRunway->longitude(), &az1, &az2, &dist_m); + + osg::Vec3 center = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0); + osg::Vec3 leftcenter = fromPolar(aRunway->headingDeg(), aRunway->lengthM() * aScale / 2) + center; + osg::Vec3 lefttop = fromPolar(aRunway->headingDeg() - 90, aRunway->widthM() * aScale / 2) + leftcenter; + osg::Vec3 leftbottom = leftcenter * 2 - lefttop; + osg::Vec3 rightbottom = center * 2 - lefttop; + osg::Vec3 righttop = center * 2 - leftbottom; + + aVertices->push_back(lefttop); + aVertices->push_back(leftbottom); + aVertices->push_back(rightbottom); + aVertices->push_back(righttop); +} + +osg::Geometry *GroundRadar::addPavementGeometry(const FGPavement* aPavement, double aTowerLat, double aTowerLon, double aScale) +{ + + osg::ref_ptr tess = new osgUtil::Tessellator; + osg::ref_ptr polygon = new osg::Geometry; + osg::ref_ptr pts = new osg::Vec3Array; + + double az1, az2, dist_m; + const FGPavement::NodeList &nodeLst = aPavement->getNodeList(); + FGPavement::NodeList::const_iterator it = nodeLst.begin(), + loopBegin = it; + while ( it != nodeLst.end() ) + { + bool close = (*it)->mClose; + geo_inverse_wgs_84(aTowerLat, aTowerLon, (*it)->mPos.getLatitudeDeg(), (*it)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m); + osg::Vec3 p1 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0); + const FGPavement::BezierNode *bn = dynamic_cast( it->ptr() ); + if ( bn != 0 ) + { + geo_inverse_wgs_84(aTowerLat, aTowerLon, bn->mControl.getLatitudeDeg(), bn->mControl.getLongitudeDeg(), &az1, &az2, &dist_m); + osg::Vec3 p2 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0), + p3; + ++it; + if ( it == nodeLst.end() || close ) + { + geo_inverse_wgs_84(aTowerLat, aTowerLon, (*loopBegin)->mPos.getLatitudeDeg(), (*loopBegin)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m); + } + else + { + geo_inverse_wgs_84(aTowerLat, aTowerLon, (*it)->mPos.getLatitudeDeg(), (*it)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m); + } + p3 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0); + simgear::BezierCurve bCurv( p1, p2, p3 ); + simgear::BezierCurve::PointList &ptList = bCurv.pointList(); + for ( simgear::BezierCurve::PointList::iterator ii = ptList.begin(); ii != ptList.end(); ++ii ) + { + pts->push_back( *ii ); + } + pts->pop_back(); // Last point belongs to next segment + } + else + { + pts->push_back( p1 ); + ++it; + } + + if ( close ) // One loop for the moment + break; + } + geo_inverse_wgs_84(aTowerLat, aTowerLon, (*loopBegin)->mPos.getLatitudeDeg(), (*loopBegin)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m); + osg::Vec3 p1 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0); + pts->push_back( p1 ); + polygon->setVertexArray( pts.get() ); + + polygon->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, 0, pts->size() ) ); + + tess->setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); + tess->setBoundaryOnly( false ); + tess->setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); + tess->retessellatePolygons( *polygon ); + return polygon.release(); +} + +void GroundRadar::updateTexture() +{ + osg::ref_ptr rwy_vertices = new osg::Vec3Array; + osg::ref_ptr taxi_vertices = new osg::Vec3Array; + osg::ref_ptr pvt_vertices = new osg::Vec3Array; + + const string airport_name = _airport_node->getStringValue(); + + const FGAirport* airport = fgFindAirportID(airport_name); + if (airport == 0) + return; + + const SGGeod& tower_location = airport->getTowerLocation(); + const double tower_lat = tower_location.getLatitudeDeg(); + const double tower_lon = tower_location.getLongitudeDeg(); + double scale = SG_METER_TO_NM * 200 / _range_node->getDoubleValue(); + + const FGAirport* apt = fgFindAirportID(airport_name); + assert(apt); + + for (unsigned int i=0; inumTaxiways(); ++i) + { + FGTaxiway* txwy(apt->getTaxiwayByIndex(i)); + addRunwayVertices(txwy, tower_lat, tower_lon, scale, taxi_vertices.get()); + } + osg::Geometry *taxi_geom = dynamic_cast(_geode->getDrawable(0)); + taxi_geom->setVertexArray(taxi_vertices.get()); + osg::DrawArrays* taxi = dynamic_cast(taxi_geom->getPrimitiveSet(0)); + taxi->setCount(taxi_vertices->size()); + + osg::Geometry *pvt_geom = dynamic_cast(_geode->getDrawable(1)); + osg::Geometry::PrimitiveSetList &pvt_prim_list = pvt_geom->getPrimitiveSetList(); + pvt_prim_list.clear(); + for (unsigned int i=0; inumPavements(); ++i) + { + FGPavement* pvt(apt->getPavementByIndex(i)); + osg::ref_ptr geom = addPavementGeometry(pvt, tower_lat, tower_lon, scale); + osg::Geometry::PrimitiveSetList &prim_list = geom->getPrimitiveSetList(); + osg::Vec3Array *vertices = dynamic_cast(geom->getVertexArray()); + size_t before = pvt_vertices->size(), + count = vertices->size(); + for (size_t i = 0; i < count; ++i ) + { + pvt_vertices->push_back( (*vertices)[i] ); + } + for (osg::Geometry::PrimitiveSetList::iterator ii = prim_list.begin(); ii != prim_list.end(); ++ii ) + { + osg::DrawArrays *da; + osg::DrawElementsUByte *de1; + osg::DrawElementsUShort *de2; + osg::DrawElementsUInt *de3; + if ((da = dynamic_cast(ii->get())) != 0) + { + osg::DrawArrays *ps = new osg::DrawArrays(*da); + ps->setFirst(da->getFirst() + before); + pvt_prim_list.push_back(ps); + } + else if ((de1 = dynamic_cast(ii->get())) != 0) + { + if (before + count <= 255) + { + osg::DrawElementsUByte *ps = new osg::DrawElementsUByte(*de1); + for (size_t j = 0; j < ps->size(); ++j) + { + (*ps)[j] += before; + } + pvt_prim_list.push_back(ps); + } + else if (before + count <= 65535) + { + osg::DrawElementsUShort *ps = new osg::DrawElementsUShort(de1->getMode(), de1->begin(), de1->end()); + for (size_t j = 0; j < ps->size(); ++j) + { + (*ps)[j] += before; + } + pvt_prim_list.push_back(ps); + } + else + { + osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(de1->getMode(), de1->begin(), de1->end()); + for (size_t j = 0; j < ps->size(); ++j) + { + (*ps)[j] += before; + } + pvt_prim_list.push_back(ps); + } + } + else if ((de2 = dynamic_cast(ii->get())) != 0) + { + if (before + count <= 65535) + { + osg::DrawElementsUShort *ps = new osg::DrawElementsUShort(*de2); + for (size_t j = 0; j < ps->size(); ++j) + { + (*ps)[j] += before; + } + pvt_prim_list.push_back(ps); + } + else + { + osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(de2->getMode(), de2->begin(), de2->end()); + for (size_t j = 0; j < ps->size(); ++j) + { + (*ps)[j] += before; + } + pvt_prim_list.push_back(ps); + } + } + else if ((de3 = dynamic_cast(ii->get())) != 0) + { + osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(*de3); + for (size_t j = 0; j < ps->size(); ++j) + { + (*ps)[j] += before; + } + pvt_prim_list.push_back(ps); + } + } + } + pvt_geom->setVertexArray(pvt_vertices.get()); + + for (unsigned int i=0; inumRunways(); ++i) + { + FGRunway* runway(apt->getRunwayByIndex(i)); + if (runway->isReciprocal()) continue; + + addRunwayVertices(runway, tower_lat, tower_lon, scale, rwy_vertices.get()); + } + osg::Geometry *rwy_geom = dynamic_cast(_geode->getDrawable(2)); + rwy_geom->setVertexArray(rwy_vertices.get()); + osg::DrawArrays* rwy = dynamic_cast(rwy_geom->getPrimitiveSet(0)); + rwy->setCount(rwy_vertices->size()); + + getCamera()->setNodeMask(0xffffffff); +} + +// end of GroundRadar.cxx diff --git a/src/Cockpit/groundradar.hxx b/src/Cockpit/groundradar.hxx new file mode 100644 index 0000000..ec439c2 --- /dev/null +++ b/src/Cockpit/groundradar.hxx @@ -0,0 +1,59 @@ +// groundradar.hxx - Background layer for the ATC radar. +// +// Copyright (C) 2007 Csaba Halasz. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +#ifndef __INST_GROUNDRADAR_HXX +#define __INST_GROUNDRADAR_HXX + +#include +#include + +#include +#include + +#include "od_gauge.hxx" + +// forward decls +class FGRunwayBase; +class FGPavement; + +//////////////////////////////////////////////////////////////////////// +// Built-in layer for the atc radar. +//////////////////////////////////////////////////////////////////////// + +class GroundRadar : public SGSubsystem, public SGPropertyChangeListener, private FGODGauge +{ +public: + static const int TextureHalfSize = 256; + GroundRadar(SGPropertyNode* node); + virtual ~GroundRadar(); + void updateTexture(); + virtual void valueChanged(SGPropertyNode*); + virtual void update (double dt); +protected: + void createTexture(const char* texture_name); + + void addRunwayVertices(const FGRunwayBase* aRunway, double aTowerLat, double aTowerLon, double aScale, osg::Vec3Array* aVertices); + osg::Geometry *addPavementGeometry(const FGPavement* aPavement, double aTowerLat, double aTowerLon, double aScale); + + osg::ref_ptr _geode; + SGPropertyNode_ptr _airport_node; + SGPropertyNode_ptr _range_node; +}; + +#endif // __INST_GROUNDRADAR_HXX diff --git a/src/Cockpit/od_gauge.cxx b/src/Cockpit/od_gauge.cxx new file mode 100644 index 0000000..4f569ac --- /dev/null +++ b/src/Cockpit/od_gauge.cxx @@ -0,0 +1,325 @@ +// Owner Drawn Gauge helper class +// +// Written by Harald JOHNSEN, started May 2005. +// +// Copyright (C) 2005 Harald JOHNSEN +// +// Ported to OSG by Tim Moore - Jun 2007 +// +// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012 +// Supports now multisampling/mipmapping, usage of the stencil buffer and placing +// the texture in the scene by certain filter criteria +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for GL_DEPTH_STENCIL_EXT on Windows + +#include + +#include +#include + +#include +#include
+#include +#include "od_gauge.hxx" + +#include + +static simgear::canvas::SystemAdapterPtr system_adapter( + new canvas::FGCanvasSystemAdapter +); + +//------------------------------------------------------------------------------ +FGODGauge::FGODGauge() +{ + setSystemAdapter(system_adapter); +} + +//------------------------------------------------------------------------------ +FGODGauge::~FGODGauge() +{ + +} + +/** + * Replace a texture in the airplane model with the gauge texture. + */ +class ReplaceStaticTextureVisitor: + public osg::NodeVisitor +{ + public: + + typedef osg::ref_ptr GroupPtr; + typedef osg::ref_ptr MaterialPtr; + + ReplaceStaticTextureVisitor( const char* name, + osg::Texture2D* new_texture ): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _tex_name( osgDB::getSimpleFileName(name) ), + _new_texture(new_texture) + {} + + ReplaceStaticTextureVisitor( SGPropertyNode* placement, + osg::Texture2D* new_texture, + osg::NodeCallback* cull_callback = 0 ): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _tex_name( osgDB::getSimpleFileName( + placement->getStringValue("texture")) + ), + _node_name( placement->getStringValue("node") ), + _parent_name( placement->getStringValue("parent") ), + _node(placement), + _new_texture(new_texture), + _cull_callback(cull_callback) + { + if( _tex_name.empty() + && _node_name.empty() + && _parent_name.empty() ) + SG_LOG + ( + SG_GL, + SG_WARN, + "No filter criterion for replacing texture. " + " Every texture will be replaced!" + ); + } + + /** + * Get a list of groups which have been inserted into the scene graph to + * replace the given texture + */ + simgear::canvas::Placements& getPlacements() + { + return _placements; + } + + virtual void apply(osg::Geode& node) + { + simgear::EffectGeode* eg = dynamic_cast(&node); + if( !eg ) + return; + + osg::StateSet* ss = eg->getEffect()->getDefaultStateSet(); + if( !ss ) + return; + + osg::Group *parent = node.getParent(0); + if( !_node_name.empty() && parent->getName() != _node_name ) + return; + + if( !_parent_name.empty() ) + { + // Traverse nodes upwards starting at the parent node (skip current + // node) + const osg::NodePath& np = getNodePath(); + bool found = false; + for( int i = static_cast(np.size()) - 2; i >= 0; --i ) + { + const osg::Node* path_segment = np[i]; + const osg::Node* path_parent = path_segment->getParent(0); + + // A node without a name is always the parent of the root node of + // the model just containing the file name + if( path_parent && path_parent->getName().empty() ) + return; + + if( path_segment->getName() == _parent_name ) + { + found = true; + break; + } + } + + if( !found ) + return; + } + + for( size_t unit = 0; unit < ss->getNumTextureAttributeLists(); ++unit ) + { + osg::Texture2D* tex = dynamic_cast + ( + ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE) + ); + + if( !tex || !tex->getImage() || tex == _new_texture ) + continue; + + if( !_tex_name.empty() ) + { + std::string tex_name = tex->getImage()->getFileName(); + std::string tex_name_simple = osgDB::getSimpleFileName(tex_name); + if( !osgDB::equalCaseInsensitive(_tex_name, tex_name_simple) ) + continue; + } + + // insert a new group between the geode an it's parent which overrides + // the texture + GroupPtr group = new osg::Group; + group->setName("canvas texture group"); + group->addChild(eg); + parent->removeChild(eg); + parent->addChild(group); + + if( _cull_callback ) + group->setCullCallback(_cull_callback); + + osg::StateSet* stateSet = group->getOrCreateStateSet(); + stateSet->setTextureAttribute( unit, _new_texture, + osg::StateAttribute::OVERRIDE ); + stateSet->setTextureMode( unit, GL_TEXTURE_2D, + osg::StateAttribute::ON ); + + _placements.push_back( simgear::canvas::PlacementPtr( + new ObjectPlacement(_node, group) + )); + + SG_LOG + ( + SG_GL, + SG_INFO, + "Replaced texture '" << _tex_name << "'" + << " for object '" << parent->getName() << "'" + << (!_parent_name.empty() ? " with parent '" + _parent_name + "'" + : "") + ); + return; + } + } + + protected: + + class ObjectPlacement: + public simgear::canvas::Placement + { + public: + + ObjectPlacement( SGPropertyNode* node, + GroupPtr group ): + Placement(node), + _group(group) + { + // TODO make more generic and extendable for more properties + if( node->hasValue("emission") ) + setEmission( node->getFloatValue("emission") ); + } + + virtual bool childChanged(SGPropertyNode* node) + { + if( node->getParent() != _node ) + return false; + + if( node->getNameString() == "emission" ) + setEmission( node->getFloatValue() ); + else + return false; + + return true; + } + + void setEmission(float emit) + { + emit = SGMiscf::clip(emit, 0, 1); + + if( !_material ) + { + _material = new osg::Material; + _material->setColorMode(osg::Material::OFF); + _material->setDataVariance(osg::Object::DYNAMIC); + _group->getOrCreateStateSet() + ->setAttribute(_material, ( osg::StateAttribute::ON + | osg::StateAttribute::OVERRIDE ) ); + } + + _material->setEmission( + osg::Material::FRONT_AND_BACK, + osg::Vec4(emit, emit, emit, emit) + ); + } + + /** + * Remove placement from the scene + */ + virtual ~ObjectPlacement() + { + assert( _group->getNumChildren() == 1 ); + osg::Node *child = _group->getChild(0); + + if( _group->getNumParents() ) + { + osg::Group *parent = _group->getParent(0); + parent->addChild(child); + parent->removeChild(_group); + } + + _group->removeChild(child); + } + + private: + GroupPtr _group; + MaterialPtr _material; + }; + + std::string _tex_name, ///get_scenery()->get_aircraft_branch(); + ReplaceStaticTextureVisitor visitor(name, new_texture); + root->accept(visitor); + return visitor.getPlacements(); +} + +//------------------------------------------------------------------------------ +simgear::canvas::Placements +FGODGauge::set_texture( SGPropertyNode* placement, + osg::Texture2D* new_texture, + osg::NodeCallback* cull_callback ) +{ + osg::Group* root = globals->get_scenery()->get_aircraft_branch(); + ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback); + root->accept(visitor); + return visitor.getPlacements(); +} diff --git a/src/Cockpit/od_gauge.hxx b/src/Cockpit/od_gauge.hxx new file mode 100644 index 0000000..cd22107 --- /dev/null +++ b/src/Cockpit/od_gauge.hxx @@ -0,0 +1,74 @@ +// Owner Drawn Gauge helper class +// +// Moved to SimGear by Thomas Geymayer - October 2012 +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// + +#ifndef _OD_GAUGE_HXX +#define _OD_GAUGE_HXX + +#include + +#include +#include + +class SGPropertyNode; + +/** + * Owner Drawn Gauge helper class + */ +class FGODGauge: + public simgear::canvas::ODGauge +{ + public: + FGODGauge(); + virtual ~FGODGauge(); + + /** + * Replace an opengl texture name inside the aircraft scene graph. + * This is to replace a static texture by a dynamic one + * @param name texture filename + * @param new_texture dynamic texture to replace the old one + * @return A list of groups which override the given texture + */ + static + simgear::canvas::Placements set_texture( const char * name, + osg::Texture2D* new_texture ); + + /** + * Replace an opengl texture name inside the aircraft scene graph. + * This is to replace a static texture by a dynamic one. The replacement + * is base on certain filtering criteria which have to be stored in string + * value childs of the placement node. Recognized nodes are: + * - texture Match the name of the texture + * - node Match the name of the object + * - parent Match any of the object parents names (all the tree upwards) + * @param placement the node containing the replacement criteria + * @param new_texture dynamic texture to replace the old one + * @param an optional cull callback which will be installed on any matching + * object + * @return A list of groups which override the given texture + */ + static + simgear::canvas::Placements + set_texture( SGPropertyNode* placement, + osg::Texture2D* new_texture, + osg::NodeCallback* cull_callback = 0 ); + +}; + +#endif // _OD_GAUGE_HXX diff --git a/src/Cockpit/panel.cxx b/src/Cockpit/panel.cxx index 3ac1562..3fbc199 100644 --- a/src/Cockpit/panel.cxx +++ b/src/Cockpit/panel.cxx @@ -36,6 +36,7 @@ #include // sprintf #include #include +#include #include #include @@ -48,6 +49,7 @@ #include +#include #include #include #include @@ -55,10 +57,10 @@ #include
#include
-#include
+#include +#include #include