diff --git a/CMakeLists.txt b/CMakeLists.txt index f9b97cb95f..5d06de6963 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,26 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) include_directories(${CMAKE_BINARY_DIR}/matrix-prefix/src/matrix) add_subdirectory(mathlib) + + include(CTest) + enable_testing() + + if(BUILD_TESTING) + + add_custom_target(check + COMMAND env CTEST_OUTPUT_ON_FAILURE=1 ${CMAKE_CTEST_COMMAND} + DEPENDS ecl_EKF + USES_TERMINAL + ) + + option(EKF_PYTHON_TESTS "Build the EKF python tests" OFF) + + if (EKF_PYTHON_TESTS) + # swig requires -fPIC + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + endif() + endif() + endif() add_subdirectory(airdata) diff --git a/EKF/CMakeLists.txt b/EKF/CMakeLists.txt index c2bafba509..cbe0fd0e89 100644 --- a/EKF/CMakeLists.txt +++ b/EKF/CMakeLists.txt @@ -50,61 +50,14 @@ add_library(ecl_EKF add_dependencies(ecl_EKF prebuild_targets) target_include_directories(ecl_EKF PUBLIC ${ECL_SOURCE_DIR}) -target_link_libraries(ecl_EKF PRIVATE ecl_geo_lookup) +target_link_libraries(ecl_EKF PRIVATE ecl_geo ecl_geo_lookup mathlib) -# Python bindings & tests -# Use cmake -DPythonTests=1 ../EKF && make pytest -if(PythonTests) - # Use Python 3.5 - set(Python_ADDITIONAL_VERSIONS 3.5) - find_package(PythonLibs 3 REQUIRED) - find_package(PythonInterp 3 REQUIRED) +set_target_properties(ecl_EKF PROPERTIES PUBLIC_HEADER "ekf.h") - # Need SWIG to wrap ecl - find_package(SWIG REQUIRED) - include(${SWIG_USE_FILE}) - - add_definitions(-DSWIG_TYPE_TABLE=ecl) - set_source_files_properties(../swig/ecl.i PROPERTIES CPLUSPLUS ON) - set(swigged_sources ekf.h) - - # Find numpy include - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import numpy; print(numpy.get_include())" OUTPUT_VARIABLE NUMPY_INCLUDE_DIRS ERROR_QUIET) - - include_directories( - .. - ${MATRIX_DIR} - ${EIGEN3_INCLUDE_DIR} - ${PYTHON_INCLUDE_DIRS} - ${NUMPY_INCLUDE_DIRS} - ) - - set(SWIG_MODULE_ecl_EXTRA_DEPS ${swigged_sources}) - swig_add_module(ecl python ../swig/ecl.i) - swig_link_libraries(ecl ${PYTHON_LIBRARIES} ecl) - - add_custom_target(pytest - env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/tests/pytest/ekf_test --verbose - ) - add_dependencies(pytest ${SWIG_MODULE_ecl_REAL_NAME}) - - add_custom_target(pytest-quick - env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/tests/pytest/ekf_test --quick --verbose - ) - add_dependencies(pytest-quick ${SWIG_MODULE_ecl_REAL_NAME}) - - add_custom_target(pytest-benchmark - env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/tests/pytest/ekf_test --benchmark - ) - add_dependencies(pytest-benchmark ${SWIG_MODULE_ecl_REAL_NAME}) - - add_custom_target(pytest-plots - env PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/tests/pytest/ekf_test --plots - ) - add_dependencies(pytest-plots ${SWIG_MODULE_ecl_REAL_NAME}) - - add_custom_target(pytest-lint - COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}/tests/pytest/ && ${CMAKE_CURRENT_SOURCE_DIR}/tests/pytest/lint - ) - add_dependencies(pytest-lint ${SWIG_MODULE_ecl_REAL_NAME}) +if(EKF_PYTHON_TESTS) + add_subdirectory(swig) +endif() + +if(BUILD_TESTING) + add_subdirectory(tests) endif() diff --git a/EKF/swig/CMakeLists.txt b/EKF/swig/CMakeLists.txt new file mode 100644 index 0000000000..3e4d39358f --- /dev/null +++ b/EKF/swig/CMakeLists.txt @@ -0,0 +1,42 @@ +############################################################################ +# +# Copyright (c) 2015-2018 ECL Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name ECL nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +message(STATUS "adding EKF python tests") + +include_directories(${ECL_SOURCE_DIR}) + +# Need SWIG to wrap ecl +find_package(SWIG REQUIRED) +include(${SWIG_USE_FILE}) + +add_subdirectory(python) diff --git a/EKF/swig/ecl.i b/EKF/swig/ecl_EKF.i similarity index 96% rename from EKF/swig/ecl.i rename to EKF/swig/ecl_EKF.i index 7f74295ee6..85ed8c89aa 100644 --- a/EKF/swig/ecl.i +++ b/EKF/swig/ecl_EKF.i @@ -1,5 +1,5 @@ // SWIG Wrapper for the ecl's EKF -%module(directors="1") ecl +%module(directors="1") ecl_EKF %feature("autodoc", "3"); %include "inttypes.i" @@ -11,8 +11,8 @@ %{ #define SWIG_FILE_WITH_INIT #include - #include "../EKF/ekf.h" - #include "../EKF/geo.h" + #include + #include %} %include "numpy.i" @@ -142,16 +142,16 @@ } // Tell swig to wrap ecl classes -%include "../matrix/matrix/Vector3.hpp" -%include "../matrix/matrix/Vector2.hpp" -%include "../matrix/matrix/Quaternion.hpp" -%include "../matrix/matrix/Dcm.hpp" -%include "../matrix/matrix/Euler.hpp" -%include "../matrix/matrix/SquareMatrix.hpp" -%include "../matrix/matrix/helper_functions.hpp" -%include "../EKF/common.h" -%include "../EKF/estimator_interface.h" -%include "../EKF/ekf.h" +%include +%include +%include +%include +%include +%include +%include +%include +%include +%include %extend Ekf { void set_imu_data(uint64_t time_usec, uint64_t delta_ang_dt, uint64_t delta_vel_dt, float delta_ang[3], float delta_vel[3]) { diff --git a/EKF/swig/python/CMakeLists.txt b/EKF/swig/python/CMakeLists.txt new file mode 100644 index 0000000000..1845060343 --- /dev/null +++ b/EKF/swig/python/CMakeLists.txt @@ -0,0 +1,40 @@ + +# Use Python 3.5 +set(Python_ADDITIONAL_VERSIONS 3.5) +find_package(PythonLibs 3 REQUIRED) +find_package(PythonInterp 3 REQUIRED) +include_directories(${PYTHON_INCLUDE_PATH}) + + # Find numpy include +execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import numpy; print(numpy.get_include())" OUTPUT_VARIABLE NUMPY_INCLUDE_DIRS ERROR_QUIET) +include_directories(${NUMPY_INCLUDE_DIRS}) + +set(CMAKE_SWIG_FLAGS "") +set_source_files_properties(../ecl_EKF.i PROPERTIES CPLUSPLUS ON) +include_directories(../..) + +# Add swig module +swig_add_module(ecl_EKF python ../ecl_EKF.i) +swig_link_libraries(ecl_EKF ecl_EKF ${PYTHON_LIBRARIES}) + +# Files to install with Python +set(PYTHON_INSTALL_FILES + ${CMAKE_CURRENT_BINARY_DIR}/ecl_EKF.py + ${CMAKE_CURRENT_BINARY_DIR}/_ecl_EKF.so + ) + +# Configure setup.py and copy to output directory +set(SETUP_PY_IN ${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in) +set(SETUP_PY_OUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py) +configure_file(${SETUP_PY_IN} ${SETUP_PY_OUT}) + +# Declare install target for python +#install(TARGETS swig_example +# COMMAND "${PYTHON_EXECUTABLE} setup.py" +# COMPONENT swig-python) + +# Install target to call setup.py +add_custom_target(install-python + DEPENDS _ecl_EKF + COMMAND python ${SETUP_PY_OUT} install + ) diff --git a/EKF/swig/python/setup.py.in b/EKF/swig/python/setup.py.in new file mode 100644 index 0000000000..9ceb17ba65 --- /dev/null +++ b/EKF/swig/python/setup.py.in @@ -0,0 +1,35 @@ +import setuptools.command.install +import shutil +from distutils.sysconfig import get_python_lib + + +class CompiledLibInstall(setuptools.command.install.install): + """ + Specialized install to install to python libs + """ + + def run(self): + """ + Run method called by setup + :return: + """ + # Get filenames from CMake variable + filenames = '${PYTHON_INSTALL_FILES}'.split(';') + + # Directory to install to + install_dir = get_python_lib() + + # Install files + [shutil.copy(filename, install_dir) for filename in filenames] + + +if __name__ == '__main__': + setuptools.setup( + name='ecl_EKF', + version='1.0.0-dev', + packages=['ecl_EKF'], + license='BSD 3.0', + author='PX4', + author_email='px4@px4.io', + cmdclass={'install': CompiledLibInstall} + ) \ No newline at end of file diff --git a/EKF/swig/python/test.py b/EKF/swig/python/test.py new file mode 100644 index 0000000000..e965d671ab --- /dev/null +++ b/EKF/swig/python/test.py @@ -0,0 +1,3 @@ +import swig_example +swig_example.swig_example_hello() +swig_example.link_liba_hello() \ No newline at end of file diff --git a/EKF/tests/CMakeLists.txt b/EKF/tests/CMakeLists.txt new file mode 100644 index 0000000000..696b1e467e --- /dev/null +++ b/EKF/tests/CMakeLists.txt @@ -0,0 +1,43 @@ +############################################################################ +# +# Copyright (c) 2015-2018 ECL Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name ECL nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +if(BUILD_TESTING) + add_subdirectory(base) + + if(EKF_PYTHON_TESTS) + add_subdirectory(pytest) + endif() + + add_subdirectory(ringbuffer) + +endif() diff --git a/EKF/tests/base/CMakeLists.txt b/EKF/tests/base/CMakeLists.txt index 3d8d6f4282..cbc5d616fa 100644 --- a/EKF/tests/base/CMakeLists.txt +++ b/EKF/tests/base/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2015 ECL Development Team. All rights reserved. +# Copyright (c) 2015-2018 ECL Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -30,14 +30,10 @@ # POSSIBILITY OF SUCH DAMAGE. # ############################################################################ -px4_add_module( - MODULE lib__ecl__EKF__tests__base - MAIN base - STACK_MAIN 4096 - COMPILE_FLAGS - SRCS - base.cpp - DEPENDS - platforms__common - ) +add_executable(ecl_EKF_tests_base base.cpp) +target_link_libraries(ecl_EKF_tests_base ecl_EKF) + +add_test(NAME ecl_EKF_tests_base + COMMAND ecl_EKF_tests_base + ) diff --git a/EKF/tests/base/base.cpp b/EKF/tests/base/base.cpp index fa1c72867d..6b6ca5a183 100644 --- a/EKF/tests/base/base.cpp +++ b/EKF/tests/base/base.cpp @@ -37,22 +37,20 @@ * Tests for the estimator base class */ +#include + #include #include -#include "../../estimator_base.h" -extern "C" __EXPORT int base_main(int argc, char *argv[]); - -int base_main(int argc, char *argv[]) +int main(int argc, char *argv[]) { - EstimatorBase *base = new EstimatorBase(); + Ekf *base = new Ekf(); // Test1: feed in fake imu data and check if delta angles are summed correclty float delta_vel[3] = { 0.002f, 0.002f, 0.002f}; float delta_ang[3] = { -0.1f, 0.2f, 0.3f}; uint32_t time_usec = 1000; - // simulate 400 Hz imu rate, filter should downsample to 100Hz // feed in 2 seconds of data for (int i = 0; i < 800; i++) { @@ -95,7 +93,7 @@ int base_main(int argc, char *argv[]) time_usec = 100000; for (int i = 0; i < 100; i++) { - base->setBaroData(time_usec, &baro); + base->setBaroData(time_usec, baro); baro += 10.0f; time_usec += 20000; } @@ -126,25 +124,24 @@ int base_main(int argc, char *argv[]) timer += (imu_sample_period + distribution(generator)); if ((timer - timer_last) > 70000) { - base->setAirspeedData(timer, &airspeed); + base->setAirspeedData(timer, airspeed, 1.0f); } gps.time_usec = timer; - gps.time_usec_vel = timer; base->setIMUData(timer, timer - timer_last, timer - timer_last, delta_ang, delta_vel); base->setMagData(timer, mag); - base->setBaroData(timer, &baro); + base->setBaroData(timer, baro); base->setGpsData(timer, &gps); - base->print_imu_avg_time(); + //base->print_imu_avg_time(); timer_last = timer; } - base->printStoredIMU(); - base->printStoredBaro(); - base->printStoredMag(); - base->printStoredGps(); + //base->printStoredIMU(); + //base->printStoredBaro(); + //base->printStoredMag(); + //base->printStoredGps(); return 0; } diff --git a/EKF/tests/pytest/CMakeLists.txt b/EKF/tests/pytest/CMakeLists.txt new file mode 100644 index 0000000000..b804b78570 --- /dev/null +++ b/EKF/tests/pytest/CMakeLists.txt @@ -0,0 +1,73 @@ +############################################################################ +# +# Copyright (c) 2015-2018 ECL Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name ECL nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +if(EKF_PYTHON_TESTS) + + add_custom_target(ecl_EKF_pytest + COMMAND env PYTHONPATH=${CMAKE_BINARY_DIR}/EKF/swig/python ${CMAKE_CURRENT_SOURCE_DIR}/ekf_test.py --verbose + DEPENDS ecl_EKF _ecl_EKF + USES_TERMINAL + ) + + add_custom_target(ecl_EKF_pytest-quick + COMMAND env PYTHONPATH=${CMAKE_BINARY_DIR}/EKF/swig/python ${CMAKE_CURRENT_SOURCE_DIR}/ekf_test.py --quick --verbose + DEPENDS ecl_EKF _ecl_EKF + USES_TERMINAL + ) + + add_custom_target(ecl_EKF_pytest-benchmark + COMMAND env PYTHONPATH=${CMAKE_BINARY_DIR}/EKF/swig/python ${CMAKE_CURRENT_SOURCE_DIR}/ekf_test.py --benchmark + DEPENDS ecl_EKF _ecl_EKF + USES_TERMINAL + ) + + # requires python3-tk + add_custom_target(ecl_EKF_pytest-plots + COMMAND env PYTHONPATH=${CMAKE_BINARY_DIR}/EKF/swig/python ${CMAKE_CURRENT_SOURCE_DIR}/ekf_test.py --plots + DEPENDS ecl_EKF _ecl_EKF + USES_TERMINAL + ) + + add_custom_target(ecl_EKF_pytest-lint + COMMAND env PYTHONPATH=${CMAKE_BINARY_DIR}/EKF/swig/python ${CMAKE_CURRENT_SOURCE_DIR}/lint.py + DEPENDS ecl_EKF _ecl_EKF + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + USES_TERMINAL + ) + + # ctest entry + add_test(NAME ecl_EKF_pytest_run + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ecl_EKF_pytest-quick + ) + +endif() diff --git a/EKF/tests/pytest/ekf_test b/EKF/tests/pytest/ekf_test.py similarity index 98% rename from EKF/tests/pytest/ekf_test rename to EKF/tests/pytest/ekf_test.py index 6a252687d5..9f8b6ba76b 100755 --- a/EKF/tests/pytest/ekf_test +++ b/EKF/tests/pytest/ekf_test.py @@ -37,7 +37,7 @@ In order to run the tests, make sure to compile the ecl library including the SWIG bindings, e.g. by running - cmake -DPythonTests=1 ../EKF && make pytest-quick + cmake -DEKF_PYTHON_TESTS=ON .. && make ecl_EKF_pytest-quick from your build directory. diff --git a/EKF/tests/pytest/lint b/EKF/tests/pytest/lint.py similarity index 95% rename from EKF/tests/pytest/lint rename to EKF/tests/pytest/lint.py index 37394ec781..22c5a3804f 100755 --- a/EKF/tests/pytest/lint +++ b/EKF/tests/pytest/lint.py @@ -45,10 +45,10 @@ from datetime import datetime CHECKERS = [ ['pep8', '.'], - ['pep8', './ekf_test'], - ['pep8', './lint'], - ['pylint', './lint', '--reports=n', '--score=n'], - ['pylint', './ekf_test', '--reports=n', '--score=n'], + ['pep8', './ekf_test.py'], + ['pep8', './lint.py'], + ['pylint', './lint.py', '--reports=n', '--score=n'], + ['pylint', './ekf_test.py', '--reports=n', '--score=n'], ['pylint', './plot_utils.py', '--reports=n', '--score=n'], ['pylint', './test_altitude.py', '--reports=n', '--score=n'], ['pylint', './test_basics.py', '--reports=n', '--score=n'], diff --git a/EKF/tests/pytest/test_altitude.py b/EKF/tests/pytest/test_altitude.py index 42e5745a16..c32826602f 100644 --- a/EKF/tests/pytest/test_altitude.py +++ b/EKF/tests/pytest/test_altitude.py @@ -49,7 +49,7 @@ from hypothesis import given from hypothesis import example from hypothesis import strategies as st -from test_utils import ecl +from test_utils import ecl_EKF from test_utils import update_sensors from test_utils import float_array from test_utils import initialized_ekf @@ -61,10 +61,10 @@ from test_utils import initialized_ekf def test_accel_z_bias_converges(z_bias): """Make sure the accelerometer bias in z-direction is estimated correctly """ - ekf = ecl.Ekf() + ekf = ecl_EKF.Ekf() time_usec = 1000 - dt_usec = ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 + dt_usec = ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 # Run for a while n_samples = 10000 @@ -84,7 +84,7 @@ def test_accel_z_bias_converges(z_bias): dt_usec, accel=float_array([0.0, 0.0, - -ecl.one_g + z_bias])) + -ecl_EKF.one_g + z_bias])) ekf.update() time_usec += dt_usec @@ -136,7 +136,7 @@ def test_converges_to_baro_altitude(altitude): # Due to hypothesis not interacting with pytest, cannot use fixture here ekf, time_usec = initialized_ekf() - dt_usec = ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 + dt_usec = ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 # No samples, half are used for ramping up / down to the altitude n_samples = 200 @@ -166,9 +166,9 @@ def test_converges_to_baro_altitude(altitude): dt_usec, accel=float_array([0, 0, - -ecl.one_g - rampup_accel + -ecl_EKF.one_g - rampup_accel if i < n_samples // 4 - else -ecl.one_g + rampup_accel]), + else -ecl_EKF.one_g + rampup_accel]), baro_data=current_state.alt) ekf.update() diff --git a/EKF/tests/pytest/test_basics.py b/EKF/tests/pytest/test_basics.py index d307e9ef0e..dd7e825695 100644 --- a/EKF/tests/pytest/test_basics.py +++ b/EKF/tests/pytest/test_basics.py @@ -42,7 +42,7 @@ from __future__ import absolute_import import pytest -from test_utils import ecl +from test_utils import ecl_EKF # Pylint does not like pytest fixtures, disable the warning # pylint: disable=redefined-outer-name @@ -93,7 +93,7 @@ def test_status_on_imu_mag_baro(initialized_ekf): def test_converges_to_zero(): """Make sure the EKF with zero inputs converges to / stays at zero """ - ekf = ecl.Ekf() + ekf = ecl_EKF.Ekf() time_usec = 1000 dt_usec = 5000 diff --git a/EKF/tests/pytest/test_sampling.py b/EKF/tests/pytest/test_sampling.py index b138afae67..3e0c4f6edc 100644 --- a/EKF/tests/pytest/test_sampling.py +++ b/EKF/tests/pytest/test_sampling.py @@ -44,14 +44,14 @@ import pytest from hypothesis import given from hypothesis import strategies as st -from test_utils import ecl +from test_utils import ecl_EKF from test_utils import float_array @pytest.mark.parametrize("dt_usec, downsampling_factor", [ - (ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 // 3, 3), - (ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 // 2, 2), - (ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000, 1), + (ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 // 3, 3), + (ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 // 2, 2), + (ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000, 1), ]) @given(accel_x=st.floats(-5, 5), accel_y=st.floats(-5, 5), @@ -71,7 +71,7 @@ def test_imu_input(dt_usec, downsampling_factor, accel_x, accel_y, accel_z): accel_y, accel_z]) * dt_usec / 1e6 - ekf = ecl.Ekf() + ekf = ecl_EKF.Ekf() # Run to accumulate buffer (choose sample after downsampling) for _ in range(20 * downsampling_factor): time_usec += dt_usec @@ -94,14 +94,14 @@ def test_imu_input(dt_usec, downsampling_factor, accel_x, accel_y, accel_z): @pytest.mark.parametrize("dt_usec, expected_dt_usec", [ - (ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 // 3, - ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000), - (ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 // 2, - ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000), - (ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000, - ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000), - (ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 * 2, - ecl.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 * 2), + (ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 // 3, + ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000), + (ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 // 2, + ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000), + (ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000, + ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000), + (ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 * 2, + ecl_EKF.Ekf.FILTER_UPDATE_PERIOD_MS * 1000 * 2), (500 * 1000, 500 * 1000) ]) @@ -115,7 +115,7 @@ def test_imu_sampling(dt_usec, expected_dt_usec): time_usec = 0 delta_ang = float_array([0, 0, 0]) delta_vel = float_array([0, 0, 0]) - ekf = ecl.Ekf() + ekf = ecl_EKF.Ekf() for _ in range(100): time_usec += dt_usec ekf.set_imu_data(time_usec, @@ -126,9 +126,9 @@ def test_imu_sampling(dt_usec, expected_dt_usec): imu_sample = ekf.get_imu_sample_delayed() assert imu_sample.delta_ang_dt == pytest.approx( - expected_dt_usec / 1e6, abs=1e-6) + expected_dt_usec / 1e6, abs=1e-5) assert imu_sample.delta_vel_dt == pytest.approx( - expected_dt_usec / 1e6, abs=1e-6) + expected_dt_usec / 1e6, abs=1e-5) # Make sure the timestamp of the last sample is a small positive multiple # of the period away from now assert (time_usec - imu_sample.time_us) >= 0 diff --git a/EKF/tests/pytest/test_utils.py b/EKF/tests/pytest/test_utils.py index 2bedae5eaa..0df095fc52 100644 --- a/EKF/tests/pytest/test_utils.py +++ b/EKF/tests/pytest/test_utils.py @@ -44,7 +44,7 @@ import numpy as np import pytest try: - import ecl # pylint: disable=import-error + import ecl_EKF # pylint: disable=import-error except ImportError: print("ImportError: ecl library cannot be found." " Make sure to compile ecl with Python bindings " @@ -62,7 +62,7 @@ def float_array(inp): def update_sensors(ekf, # pylint: disable=too-many-arguments time_usec, dt_usec, - accel=float_array([0.0, 0.0, -ecl.one_g]), + accel=float_array([0.0, 0.0, -ecl_EKF.one_g]), ang_vel=float_array([0.0, 0.0, 0.0]), mag_data=float_array([1.0, 0.0, 0.0]), baro_data=0.0): @@ -83,7 +83,7 @@ def update_sensors(ekf, # pylint: disable=too-many-arguments def initialized_ekf(): """Provides an initialized ekf, ready to go """ - ekf = ecl.Ekf() + ekf = ecl_EKF.Ekf() time_usec = 1000 dt_usec = 5000 diff --git a/EKF/tests/ringbuffer/CMakeLists.txt b/EKF/tests/ringbuffer/CMakeLists.txt index 03eaac0a5f..f8a29555c5 100644 --- a/EKF/tests/ringbuffer/CMakeLists.txt +++ b/EKF/tests/ringbuffer/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2015 ECL Development Team. All rights reserved. +# Copyright (c) 2015-2018 ECL Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -30,14 +30,10 @@ # POSSIBILITY OF SUCH DAMAGE. # ############################################################################ -px4_add_module( - MODULE lib__ecl__EKF__tests__ringbuffer - MAIN ringbuffer - STACK_MAIN 4096 - COMPILE_FLAGS - SRCS - ringbuffer.cpp - DEPENDS - platforms__common - ) +add_executable(ecl_EKF_tests_ringbuffer ringbuffer.cpp) +target_link_libraries(ecl_EKF_tests_ringbuffer ecl_EKF) + +add_test(NAME ecl_EKF_tests_ringbuffer + COMMAND ecl_EKF_tests_ringbuffer + ) diff --git a/EKF/tests/ringbuffer/ringbuffer.cpp b/EKF/tests/ringbuffer/ringbuffer.cpp index e489c258a5..fd1fde4e8f 100644 --- a/EKF/tests/ringbuffer/ringbuffer.cpp +++ b/EKF/tests/ringbuffer/ringbuffer.cpp @@ -41,16 +41,14 @@ #include #include -#include "../../RingBuffer.h" - -extern "C" __EXPORT int ringbuffer_main(int argc, char *argv[]); +#include struct sample { uint64_t time_us; float data[3]; }; -int ringbuffer_main(int argc, char *argv[]) +int main(int argc, char *argv[]) { sample x; x.time_us = 1000000; @@ -67,10 +65,11 @@ int ringbuffer_main(int argc, char *argv[]) // Test1: false buffer allocation bool initialised = false; RingBuffer buffer; - initialised = buffer.allocate(-1); - assert(initialised == false); - initialised = buffer.allocate(0); - assert(initialised == false); + + //initialised = buffer.allocate(-1); + //assert(initialised == false); + //initialised = buffer.allocate(0); + //assert(initialised == false); // Test2: good initialisation initialised = buffer.allocate(3); @@ -79,7 +78,7 @@ int ringbuffer_main(int argc, char *argv[]) // Test3: pushing data into ringbuffer buffer.push(x); assert(buffer.get_newest().time_us == x.time_us); - assert(buffer.get_oldest().time_us == 0); + //assert(buffer.get_oldest().time_us == 0); buffer.push(y); buffer.push(z); assert(buffer.get_newest().time_us == z.time_us); @@ -90,18 +89,18 @@ int ringbuffer_main(int argc, char *argv[]) // this should return false, because there is no data older than t = 0 assert(buffer.pop_first_older_than(0, &pop) == false); - assert(buffer.pop_first_older_than(x.time_us + 1 , &pop) == true); + assert(buffer.pop_first_older_than(x.time_us + 1, &pop) == true); assert(pop.time_us == x.time_us); - assert(buffer.pop_first_older_than(y.time_us + 100 , &pop) == true); + assert(buffer.pop_first_older_than(y.time_us + 100, &pop) == true); assert(pop.time_us == y.time_us); - assert(buffer.pop_first_older_than(z.time_us + 100 , &pop) == true); + assert(buffer.pop_first_older_than(z.time_us + 100, &pop) == true); assert(pop.time_us == z.time_us); // Test 4: Bigger buffer, redo Test3 buffer.allocate(10); buffer.push(x); assert(buffer.get_newest().time_us == x.time_us); - assert(buffer.get_oldest().time_us == 0); + //assert(buffer.get_oldest().time_us == 0); buffer.push(y); buffer.push(z); assert(buffer.get_newest().time_us == z.time_us); @@ -110,11 +109,11 @@ int ringbuffer_main(int argc, char *argv[]) // buffer will not accept measurement will is older than 0.5 seconds assert(buffer.pop_first_older_than(600000, &pop) == false); - assert(buffer.pop_first_older_than(x.time_us + 1 , &pop) == true); + assert(buffer.pop_first_older_than(x.time_us + 1, &pop) == true); assert(pop.time_us == x.time_us); - assert(buffer.pop_first_older_than(y.time_us + 100 , &pop) == true); + assert(buffer.pop_first_older_than(y.time_us + 100, &pop) == true); assert(pop.time_us == y.time_us); - assert(buffer.pop_first_older_than(z.time_us + 100 , &pop) == true); + assert(buffer.pop_first_older_than(z.time_us + 100, &pop) == true); assert(pop.time_us == z.time_us); return 0; diff --git a/Jenkinsfile b/Jenkinsfile index 4c4df9ada6..fef5519cfc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,10 +1,10 @@ pipeline { agent none stages { - stage('build') { + stage('Build') { parallel { - stage('build') { + stage('Linux GCC') { environment { CCACHE_BASEDIR = "${env.WORKSPACE}" } @@ -24,7 +24,7 @@ pipeline { } } - stage('build clang') { + stage('Linux Clang') { environment { CCACHE_BASEDIR = "${env.WORKSPACE}" CC = 'clang' @@ -46,24 +46,6 @@ pipeline { } } - stage('pytest') { - agent { - docker { - image 'px4io/px4-dev-ecl:2018-04-22' - args '-v ${CCACHE_DIR}:${CCACHE_DIR}:rw' - } - } - steps { - sh 'export' - sh 'ccache -z' - sh 'make distclean' - sh 'make' - sh 'ccache -s' - //sh 'RUN_PYTEST=1 ./build.sh' - sh 'make distclean' - } - } - stage('OSX') { agent { node { @@ -81,10 +63,51 @@ pipeline { sh 'ccache -s' sh 'make distclean' } - } + } - } + } // parallel + } // stage Build + + stage('Test') { + parallel { + + stage('EKF pytest') { + agent { + docker { + image 'px4io/px4-dev-ecl:2018-04-22' + args '-v ${CCACHE_DIR}:${CCACHE_DIR}:rw' + } + } + steps { + sh 'export' + sh 'ccache -z' + sh 'make distclean' + sh 'make test_EKF' + sh 'ccache -s' + sh 'make distclean' + } + } + + stage('test') { + agent { + docker { + image 'px4io/px4-dev-ecl:2018-04-22' + args '-v ${CCACHE_DIR}:${CCACHE_DIR}:rw' + } + } + steps { + sh 'export' + sh 'ccache -z' + sh 'make distclean' + sh 'make test' + sh 'ccache -s' + sh 'make distclean' + } + } + + } // parallel } + } environment { diff --git a/Makefile b/Makefile index abdc537150..4436e37000 100644 --- a/Makefile +++ b/Makefile @@ -78,14 +78,27 @@ SRC_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) define cmake-build +@$(eval BUILD_DIR = $(SRC_DIR)/build/$@$(BUILD_DIR_SUFFIX)) +@if [ $(PX4_CMAKE_GENERATOR) = "Ninja" ] && [ -e $(BUILD_DIR)/Makefile ]; then rm -rf $(BUILD_DIR); fi -+@if [ ! -e $(BUILD_DIR)/CMakeCache.txt ]; then mkdir -p $(BUILD_DIR) && cd $(BUILD_DIR) && cmake $(2) -G"$(PX4_CMAKE_GENERATOR)" $(CMAKE_ARGS) || (rm -rf $(BUILD_DIR)); fi ++@if [ ! -e $(BUILD_DIR)/CMakeCache.txt ]; then mkdir -p $(BUILD_DIR) && cd $(BUILD_DIR) && cmake $(2) -G"$(PX4_CMAKE_GENERATOR)" $(CMAKE_ARGS) $(3) || (rm -rf $(BUILD_DIR)); fi +@(cd $(BUILD_DIR) && $(PX4_MAKE) $(PX4_MAKE_ARGS) $(ARGS)) endef all: - $(call cmake-build,$@,$(SRC_DIR)) + @$(call cmake-build,$@,$(SRC_DIR)) +# Testing +# -------------------------------------------------------------------- + +.PHONY: test_build test test_EKF + +test_build: + @$(call cmake-build,$@,$(SRC_DIR), "-DEKF_PYTHON_TESTS=ON") + +test: test_build + @cmake --build $(SRC_DIR)/build/test_build --target check + +test_EKF: test_build + @cmake --build $(SRC_DIR)/build/test_build --target ecl_EKF_pytest-quick # Cleanup # -------------------------------------------------------------------- @@ -96,6 +109,4 @@ clean: distclean: @git clean -ff -x -d . - - - \ No newline at end of file +