From 6dcb4977a212a6236feb1088d731ad8ac7474ddf Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sat, 3 Feb 2024 11:57:07 +1300 Subject: [PATCH 01/38] Start of head tracker VRx --- html/airplane.obj | 3379 ++++++++++++++++ html/scan.js | 106 +- html/texture.gif | Bin 0 -> 31218 bytes html/vrx_index.html | 18 +- lib/Fusion/CMakeLists.txt | 7 + lib/Fusion/Fusion.h | 32 + lib/Fusion/FusionAhrs.c | 505 +++ lib/Fusion/FusionAhrs.h | 109 + lib/Fusion/FusionAxes.h | 187 + lib/Fusion/FusionCalibration.h | 44 + lib/Fusion/FusionCompass.c | 49 + lib/Fusion/FusionCompass.h | 25 + lib/Fusion/FusionConvention.h | 25 + lib/Fusion/FusionMath.h | 481 +++ lib/Fusion/FusionOffset.c | 77 + lib/Fusion/FusionOffset.h | 40 + lib/HeadTracker/devHeadTracker.cpp | 219 ++ lib/HeadTracker/devHeadTracker.h | 15 + lib/ICM42670P/.piopm | 1 + lib/ICM42670P/LICENSE | 18 + lib/ICM42670P/README.md | 242 ++ .../FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino | 59 + .../examples/Polling_I2C/Polling_I2C.ino | 52 + .../examples/Polling_SPI/Polling_SPI.ino | 52 + lib/ICM42670P/keywords.txt | 5 + lib/ICM42670P/library.properties | 10 + lib/ICM42670P/src/ICM42670P.cpp | 306 ++ lib/ICM42670P/src/ICM42670P.h | 65 + lib/ICM42670P/src/Invn/InvError.h | 64 + lib/ICM42670P/src/imu/inv_imu_defs.h | 991 +++++ lib/ICM42670P/src/imu/inv_imu_driver.c | 1578 ++++++++ lib/ICM42670P/src/imu/inv_imu_driver.h | 457 +++ lib/ICM42670P/src/imu/inv_imu_extfunc.h | 56 + lib/ICM42670P/src/imu/inv_imu_regmap.h | 3391 +++++++++++++++++ lib/ICM42670P/src/imu/inv_imu_transport.c | 273 ++ lib/ICM42670P/src/imu/inv_imu_transport.h | 121 + lib/ICM42670P/src/imu/inv_imu_version.h | 37 + lib/ICM42670P/src/inv_time.c | 36 + lib/QMC5883L/QMC5883LCompass.cpp | 469 +++ lib/QMC5883L/QMC5883LCompass.h | 59 + lib/WIFI/devWIFI.cpp | 40 +- python/build_html.py | 9 + src/Vrx_main.cpp | 4 + targets/common.ini | 7 +- targets/debug.ini | 9 +- targets/rapidfire.ini | 10 + 46 files changed, 13726 insertions(+), 13 deletions(-) create mode 100644 html/airplane.obj create mode 100644 html/texture.gif create mode 100644 lib/Fusion/CMakeLists.txt create mode 100644 lib/Fusion/Fusion.h create mode 100644 lib/Fusion/FusionAhrs.c create mode 100644 lib/Fusion/FusionAhrs.h create mode 100644 lib/Fusion/FusionAxes.h create mode 100644 lib/Fusion/FusionCalibration.h create mode 100644 lib/Fusion/FusionCompass.c create mode 100644 lib/Fusion/FusionCompass.h create mode 100644 lib/Fusion/FusionConvention.h create mode 100644 lib/Fusion/FusionMath.h create mode 100644 lib/Fusion/FusionOffset.c create mode 100644 lib/Fusion/FusionOffset.h create mode 100644 lib/HeadTracker/devHeadTracker.cpp create mode 100644 lib/HeadTracker/devHeadTracker.h create mode 100644 lib/ICM42670P/.piopm create mode 100644 lib/ICM42670P/LICENSE create mode 100644 lib/ICM42670P/README.md create mode 100644 lib/ICM42670P/examples/FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino create mode 100644 lib/ICM42670P/examples/Polling_I2C/Polling_I2C.ino create mode 100644 lib/ICM42670P/examples/Polling_SPI/Polling_SPI.ino create mode 100644 lib/ICM42670P/keywords.txt create mode 100644 lib/ICM42670P/library.properties create mode 100644 lib/ICM42670P/src/ICM42670P.cpp create mode 100644 lib/ICM42670P/src/ICM42670P.h create mode 100644 lib/ICM42670P/src/Invn/InvError.h create mode 100644 lib/ICM42670P/src/imu/inv_imu_defs.h create mode 100644 lib/ICM42670P/src/imu/inv_imu_driver.c create mode 100644 lib/ICM42670P/src/imu/inv_imu_driver.h create mode 100644 lib/ICM42670P/src/imu/inv_imu_extfunc.h create mode 100644 lib/ICM42670P/src/imu/inv_imu_regmap.h create mode 100644 lib/ICM42670P/src/imu/inv_imu_transport.c create mode 100644 lib/ICM42670P/src/imu/inv_imu_transport.h create mode 100644 lib/ICM42670P/src/imu/inv_imu_version.h create mode 100644 lib/ICM42670P/src/inv_time.c create mode 100644 lib/QMC5883L/QMC5883LCompass.cpp create mode 100644 lib/QMC5883L/QMC5883LCompass.h diff --git a/html/airplane.obj b/html/airplane.obj new file mode 100644 index 0000000..915a2ce --- /dev/null +++ b/html/airplane.obj @@ -0,0 +1,3379 @@ +# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware +# File Created: 06.08.2020 19:57:01 + +mtllib airplane.mtl + +# +# object awionetka +# + +v 2.8269 12.5525 -44.1386 +v 2.4376 11.9722 -44.1131 +v 0.7048 14.6280 -67.4302 +v 0.9541 14.9538 -67.4302 +v 1.0103 16.0328 -67.4287 +v 0.9541 16.5154 -67.4302 +v 2.8291 16.9331 -44.3376 +v 3.0230 15.0275 -44.2436 +v 1.5461 17.8633 -44.3602 +v 0.5838 17.1532 -67.4331 +v -5.1009 7.6915 0.8742 +v -5.0922 8.3498 -9.9901 +v 5.0781 8.3498 -9.9901 +v 5.0868 7.6915 0.8742 +v 6.0221 9.5242 -10.0397 +v 6.1351 9.1736 0.7772 +v 6.4610 15.2921 0.1598 +v 6.3006 15.4962 6.0005 +v 6.0243 9.2086 6.2725 +v 4.9439 7.7294 6.3373 +v 5.0781 10.0112 15.5147 +v 3.9605 8.6815 15.5730 +v 5.3544 15.4831 15.2756 +v 4.2025 11.6179 19.1481 +v 3.1834 10.4559 19.0730 +v -4.9581 7.7294 6.3373 +v 5.9536 10.1731 -17.4485 +v 5.0066 9.0861 -17.4069 +v 3.6062 10.3888 -28.2741 +v 4.6749 11.1390 -28.3069 +v 6.6155 16.1924 -10.2729 +v 6.3808 16.9178 3.7508 +v 6.4610 16.6014 0.1394 +v 4.8018 16.6772 15.2238 +v 5.7480 17.5884 5.9218 +v 2.7613 18.6222 7.7042 +v 2.1657 17.8706 15.1714 +v 4.6647 15.4102 19.5111 +v -2.1798 17.8706 15.1714 +v -1.8124 17.1736 19.7167 +v 1.7983 17.1736 19.7167 +v 4.1902 16.3047 19.5993 +v 4.4285 17.4682 -28.6590 +v 4.8419 18.0725 -23.4846 +v 6.0702 16.8463 -20.5278 +v 4.9738 14.9298 -28.4622 +v 6.5594 16.5861 -17.3092 +v 6.5426 12.8631 -17.5658 +v 6.2131 17.1926 -20.0029 +v 2.2401 20.9608 -18.1585 +v -0.0067 22.0280 -14.0477 +v 6.3392 22.0280 -14.0477 +v 6.3392 20.9608 -18.1549 +v 2.1110 18.4953 -28.7035 +v 2.2700 18.6426 -24.6838 +v 6.4114 20.3287 0.6854 +v 6.4858 20.9921 2.4751 +v 6.1825 21.3340 2.9242 +v 3.2235 22.0076 3.5125 +v 4.9673 19.1959 -21.8356 +v 6.1614 18.8627 -20.2303 +v 6.2678 20.4184 -18.8183 +v 5.0416 20.1640 -20.5088 +v 6.4705 19.4532 -18.3444 +v 6.2802 18.3349 -19.6063 +v 6.6155 16.5314 -11.5093 +v 6.5594 19.8811 -17.0060 +v -2.2841 18.6426 -24.6838 +v -2.2841 19.6522 -22.9247 +v 2.2700 19.6522 -22.9247 +v -2.4518 11.9722 -44.1131 +v -0.7189 14.6280 -67.4302 +v 6.6155 17.0380 -9.6249 +v 6.6155 20.3798 -11.6004 +v 6.6155 19.8483 -9.7415 +v 6.6155 16.6451 -9.0781 +v 6.6155 16.9863 -0.9067 +v 6.6155 17.1489 -0.6341 +v 6.6155 20.4840 -1.7582 +v 6.6155 20.0182 -1.0635 +v 6.6155 20.1669 -9.4185 +v -3.3462 23.2273 -0.1172 +v -3.3178 22.8416 2.1099 +v 3.3037 22.8416 2.1099 +v 3.3321 23.2273 -0.1172 +v 6.3392 23.2506 0.0249 +v 6.2642 22.0536 1.9910 +v 2.2700 20.4760 -21.1853 +v -2.2841 20.4760 -21.1853 +v 2.9523 12.2646 20.2379 +v -2.9664 12.2646 20.2379 +v -1.9473 11.0624 19.6248 +v 1.9332 11.0624 19.6248 +v 3.4233 13.4528 20.7591 +v -3.1975 10.4559 19.0730 +v 3.5486 16.1436 20.7985 +v 3.7856 15.3949 20.7730 +v 1.7035 16.8747 20.8291 +v 1.7035 16.1253 21.1222 +v 1.7035 13.7473 21.1543 +v 6.6155 20.9207 0.1044 +v 1.5322 16.1319 20.2190 +v 1.5322 13.7539 20.2510 +v 3.6135 15.4014 19.8705 +v 3.3773 16.1501 19.8953 +v 3.2512 13.4594 19.8566 +v 29.8233 21.5141 0.0789 +v 29.8233 22.5566 1.9692 +v 62.7012 22.7468 -2.0491 +v 62.7012 23.5210 -0.1471 +v 62.7012 24.1268 -2.1096 +v 29.8233 23.6647 -0.0152 +v 29.8233 21.5105 -14.0251 +v 62.7012 22.7928 -10.4858 +v 62.7012 23.4831 -10.5164 +v 29.8233 22.6375 -14.0747 +v 65.0500 22.9262 -10.4916 +v 65.0500 22.4392 -10.4705 +v 65.0500 22.5260 -13.5359 +v 64.3589 22.5704 -10.4763 +v 64.3589 22.6892 -13.8567 +v 64.3589 23.1033 -10.4997 +v 62.7012 22.9874 -13.9515 +v 65.0500 22.9262 -3.2388 +v 65.0500 22.4472 -5.1539 +v 65.0500 23.4401 -5.1969 +v 64.3589 22.6696 -3.3613 +v 64.3589 23.1821 -1.4477 +v 64.3589 23.6960 -3.4058 +v 6.6155 20.7836 -13.9930 +v 29.8925 21.8276 -18.1870 +v -2.2542 20.9608 -18.1585 +v -5.0208 9.0861 -17.4069 +v 4.6815 8.3520 -7.6267 +v 5.7867 7.6558 -7.7346 +v 5.7867 7.6908 -10.1016 +v 4.5430 8.5131 -10.2758 +v 5.3719 7.3402 -10.0863 +v 5.3719 7.3052 -7.7192 +v 1.8515 7.7870 -7.5960 +v 1.8515 7.8060 -10.2379 +v 8.9637 6.2948 -9.6657 +v 8.9637 6.2590 -8.0648 +v 8.5496 6.1213 -8.0590 +v 10.8977 3.4918 -8.1063 +v 11.1740 3.6295 -8.1122 +v 11.1740 3.5749 -9.3690 +v 8.5496 6.1570 -9.6598 +v 10.8977 3.4371 -9.3624 +v 11.1740 1.7808 -9.2902 +v 11.1740 1.8355 -8.0342 +v 10.8977 1.8355 -8.0342 +v 10.8977 1.7808 -9.2902 +v 13.7073 3.3401 -10.6352 +v 13.2516 3.5501 -11.0391 +v 13.2516 4.9381 -8.8609 +v 13.7073 4.4832 -8.8404 +v 11.7317 3.5501 -11.0391 +v 11.7317 4.9381 -8.8609 +v 11.2760 3.3401 -10.6352 +v 11.2760 4.4832 -8.8404 +v 13.7073 1.2144 -10.5419 +v 13.2516 0.9694 -10.9268 +v 11.7317 0.9694 -10.9268 +v 11.2760 1.2144 -10.5419 +v 13.7073 0.2324 -8.6553 +v 13.2516 -0.0432 -8.6356 +v 11.7317 -0.0432 -8.6356 +v 11.2760 0.2324 -8.6553 +v 13.7073 1.3755 -6.8612 +v 13.2516 1.1648 -6.4566 +v 11.7317 1.1648 -6.4566 +v 11.2760 1.3755 -6.8612 +v 13.7073 3.5005 -6.9538 +v 13.2516 3.7455 -6.5689 +v 11.7317 3.7455 -6.5689 +v 11.2760 3.5005 -6.9538 +v 13.7073 2.3574 -8.7479 +v 11.2760 2.3574 -8.7479 +v 2.1949 15.2192 -50.9460 +v 1.0897 14.8576 -59.2267 +v 19.1864 14.9954 -59.2325 +v 19.1864 15.2484 -53.4363 +v 19.1864 15.5604 -52.6205 +v 2.3327 15.6814 -49.8605 +v 1.0897 16.0991 -59.2814 +v 2.1949 16.1851 -50.9883 +v 19.1864 16.0145 -53.4698 +v 19.1864 15.9613 -59.2748 +v 20.1531 15.2301 -53.8503 +v 20.2916 14.9954 -59.2325 +v 20.2916 15.8235 -59.2690 +v 20.1531 15.7827 -53.8744 +v 19.8768 15.5487 -52.8961 +v 19.9322 15.1623 -61.7447 +v 19.1864 15.2141 -62.1463 +v 2.8853 15.0872 -65.0441 +v 29.6439 21.6905 0.6242 +v 6.6272 8.5678 2.5801 +v 7.1681 8.5561 2.3038 +v 30.1848 21.6789 0.3479 +v 29.1030 21.6789 0.3479 +v 6.0761 8.6072 2.3016 +v 29.9086 21.4864 -0.8878 +v 6.8918 8.4956 0.9238 +v 6.3626 8.4956 0.9238 +v 29.3793 21.4864 -0.8878 +v 5.1014 7.7258 0.7612 +v 5.4572 8.1734 0.7423 +v 5.6766 8.5598 2.3081 +v 4.8499 7.5632 2.4707 +v 5.2968 8.0436 2.9672 +v -2.8411 12.5525 -44.1386 +v -0.9682 14.9538 -67.4302 +v -1.0244 16.0328 -67.4287 +v -3.0371 15.0275 -44.2436 +v -2.8432 16.9331 -44.3376 +v -0.9682 16.5154 -67.4302 +v -1.5602 17.8633 -44.3602 +v -0.5979 17.1532 -67.4331 +v -6.1493 9.1736 0.7772 +v -6.0363 9.5242 -10.0397 +v -6.0384 9.2086 6.2725 +v -6.3147 15.4962 6.0005 +v -6.4751 15.2921 0.1598 +v -3.9746 8.6815 15.5730 +v -5.0922 10.0112 15.5147 +v -5.3685 15.4831 15.2756 +v -4.2167 11.6179 19.1481 +v -5.9670 10.1731 -17.4485 +v -4.6891 11.1390 -28.3069 +v -3.6203 10.3888 -28.2741 +v -6.6297 16.1924 -10.2729 +v -6.3949 16.9178 3.7508 +v -6.4751 16.6014 0.1394 +v -4.8159 16.6772 15.2238 +v -5.7622 17.5884 5.9218 +v -2.7754 18.6222 7.7042 +v -4.6789 15.4102 19.5111 +v -4.2043 16.3047 19.5993 +v -4.4427 17.4682 -28.6590 +v -4.9879 14.9298 -28.4622 +v -6.0844 16.8463 -20.5278 +v -4.8560 18.0725 -23.4846 +v -6.5735 16.5861 -17.3092 +v -6.2272 17.1926 -20.0029 +v -6.5560 12.8631 -17.5658 +v -6.3534 20.9608 -18.1549 +v -6.3534 22.0280 -14.0477 +v -2.1252 18.4953 -28.7035 +v -6.4999 20.9921 2.4751 +v -6.4255 20.3287 0.6854 +v -3.2376 22.0076 3.5125 +v -6.1966 21.3340 2.9242 +v -4.9814 19.1959 -21.8356 +v -6.1755 18.8627 -20.2303 +v -5.0557 20.1640 -20.5088 +v -6.2819 20.4184 -18.8183 +v -6.4846 19.4532 -18.3444 +v -6.2943 18.3349 -19.6063 +v -6.6297 16.5314 -11.5093 +v -6.5735 19.8811 -17.0060 +v -6.6297 17.0380 -9.6249 +v -6.6297 19.8483 -9.7415 +v -6.6297 20.3798 -11.6004 +v -6.6297 16.6451 -9.0781 +v -6.6297 16.9863 -0.9067 +v -6.6297 17.1489 -0.6341 +v -6.6297 20.0182 -1.0635 +v -6.6297 20.4840 -1.7582 +v -6.6297 20.1669 -9.4185 +v -6.3534 23.2506 0.0249 +v -6.2783 22.0536 1.9910 +v -1.7177 13.7473 21.1543 +v -3.4374 13.4528 20.7591 +v -3.7997 15.3949 20.7730 +v -1.7177 16.1253 21.1222 +v -3.5628 16.1436 20.7985 +v -1.7177 16.8747 20.8291 +v -6.6297 20.9207 0.1044 +v -1.5463 16.1319 20.2190 +v -3.3914 16.1501 19.8953 +v -3.6276 15.4014 19.8705 +v -1.5463 13.7539 20.2510 +v -3.2653 13.4594 19.8566 +v -29.8374 21.5141 0.0789 +v -29.8374 22.5566 1.9692 +v -62.7153 23.5210 -0.1471 +v -62.7153 22.7468 -2.0491 +v -29.8374 23.6647 -0.0152 +v -62.7153 24.1268 -2.1096 +v -62.7153 22.7928 -10.4858 +v -29.8374 21.5105 -14.0251 +v -62.7153 23.4831 -10.5164 +v -29.8374 22.6375 -14.0747 +v -65.0641 22.9262 -10.4916 +v -65.0641 22.5260 -13.5359 +v -65.0641 22.4392 -10.4705 +v -64.3730 22.5704 -10.4763 +v -64.3730 22.6892 -13.8567 +v -64.3730 23.1033 -10.4997 +v -62.7153 22.9874 -13.9515 +v -65.0641 23.4401 -5.1969 +v -65.0641 22.4472 -5.1539 +v -65.0641 22.9262 -3.2388 +v -64.3730 22.6696 -3.3613 +v -64.3730 23.1821 -1.4477 +v -64.3730 23.6960 -3.4058 +v -6.6297 20.7836 -13.9930 +v -29.9066 21.8276 -18.1870 +v -4.6956 8.3520 -7.6267 +v -4.5571 8.5131 -10.2758 +v -5.8008 7.6908 -10.1016 +v -5.8008 7.6558 -7.7346 +v -5.3860 7.3402 -10.0863 +v -1.8656 7.8060 -10.2379 +v -1.8656 7.7870 -7.5960 +v -5.3860 7.3052 -7.7192 +v -8.9778 6.2948 -9.6657 +v -8.9778 6.2590 -8.0648 +v -11.1881 3.6295 -8.1122 +v -10.9118 3.4918 -8.1063 +v -8.5637 6.1213 -8.0590 +v -11.1881 3.5749 -9.3690 +v -10.9118 3.4371 -9.3624 +v -8.5637 6.1570 -9.6598 +v -11.1881 1.7808 -9.2902 +v -10.9118 1.7808 -9.2902 +v -10.9118 1.8355 -8.0342 +v -11.1881 1.8355 -8.0342 +v -13.2658 4.9381 -8.8609 +v -13.2658 3.5501 -11.0391 +v -13.7214 3.3401 -10.6352 +v -13.7214 4.4832 -8.8404 +v -11.7458 4.9381 -8.8609 +v -11.7458 3.5501 -11.0391 +v -11.2901 4.4832 -8.8404 +v -11.2901 3.3401 -10.6352 +v -13.2658 0.9694 -10.9268 +v -13.7214 1.2144 -10.5419 +v -11.7458 0.9694 -10.9268 +v -11.2901 1.2144 -10.5419 +v -13.2658 -0.0432 -8.6356 +v -13.7214 0.2324 -8.6553 +v -11.7458 -0.0432 -8.6356 +v -11.2901 0.2324 -8.6553 +v -13.2658 1.1648 -6.4566 +v -13.7214 1.3755 -6.8612 +v -11.7458 1.1648 -6.4566 +v -11.2901 1.3755 -6.8612 +v -13.2658 3.7455 -6.5689 +v -13.7214 3.5005 -6.9538 +v -11.7458 3.7455 -6.5689 +v -11.2901 3.5005 -6.9538 +v -13.7214 2.3574 -8.7479 +v -11.2901 2.3574 -8.7479 +v -2.2090 15.2192 -50.9460 +v -19.2005 15.2484 -53.4363 +v -19.2005 14.9954 -59.2325 +v -1.1038 14.8576 -59.2267 +v -19.2005 15.5604 -52.6205 +v -2.3468 15.6814 -49.8605 +v -1.1038 16.0991 -59.2814 +v -19.2005 15.9613 -59.2748 +v -19.2005 16.0145 -53.4698 +v -2.2090 16.1851 -50.9883 +v -20.1672 15.2301 -53.8503 +v -20.1672 15.7827 -53.8744 +v -20.3057 15.8235 -59.2690 +v -20.3057 14.9954 -59.2325 +v -19.8909 15.5487 -52.8961 +v -19.2005 15.2141 -62.1463 +v -19.9463 15.1623 -61.7447 +v -2.8994 15.0872 -65.0441 +v -29.6580 21.6905 0.6242 +v -30.1990 21.6789 0.3479 +v -7.1822 8.5561 2.3038 +v -6.6413 8.5678 2.5801 +v -29.1171 21.6789 0.3479 +v -6.0902 8.6072 2.3016 +v -29.9227 21.4864 -0.8878 +v -29.3934 21.4864 -0.8878 +v -6.3767 8.4956 0.9238 +v -6.9060 8.4956 0.9238 +v -5.4713 8.1734 0.7423 +v -5.1155 7.7258 0.7612 +v -5.6907 8.5598 2.3081 +v -4.8640 7.5632 2.4707 +v -5.3109 8.0436 2.9672 +v -0.5039 7.3693 11.8136 +v -0.5039 6.1774 12.0286 +v -0.0154 6.2131 12.2277 +v -0.0154 7.3977 12.0148 +v -0.0154 6.1643 11.9557 +v -0.3085 6.1424 11.8362 +v -0.0154 4.6356 12.2269 +v -0.3085 4.6144 12.1074 +v -0.0154 4.6604 12.3632 +v -0.4062 4.6312 12.2036 +v -0.0154 4.3884 12.4121 +v -0.4062 4.3593 12.2532 +v -0.7065 7.2826 11.3426 +v -0.7065 6.0907 11.5475 +v -0.4302 6.0907 11.5475 +v -0.4302 4.5619 11.8194 +v -0.5680 4.5619 11.8194 +v -0.5680 4.2900 11.8683 +v -0.5039 7.2031 10.8477 +v -0.5039 6.0039 11.0671 +v -0.3085 6.0382 11.2595 +v -0.3085 4.5102 11.5307 +v -0.4062 4.4927 11.4345 +v -0.4062 4.2208 11.4841 +v -0.0154 7.1965 10.6508 +v -0.0154 5.9674 10.8681 +v -0.0154 6.0170 11.1400 +v -0.0154 4.4883 11.4112 +v -0.0154 4.4635 11.2756 +v -0.0154 4.1923 11.3244 +v 0.4730 7.2031 10.8477 +v 0.4730 6.0039 11.0671 +v 0.2776 6.0382 11.2595 +v 0.2776 4.5102 11.5307 +v 0.3753 4.4927 11.4345 +v 0.3753 4.2208 11.4841 +v 0.6749 7.2826 11.3426 +v 0.6749 6.0907 11.5475 +v 0.3986 6.0907 11.5475 +v 0.3986 4.5619 11.8194 +v 0.5371 4.5619 11.8194 +v 0.5371 4.2900 11.8683 +v 0.4730 7.3693 11.8136 +v 0.4730 6.1774 12.0286 +v 0.2776 6.1424 11.8362 +v 0.2776 4.6144 12.1074 +v 0.3753 4.6312 12.2036 +v 0.3753 4.3593 12.2532 +v -0.0154 4.2900 11.8683 +v -0.0154 7.2877 11.3346 +v -0.8443 3.8708 11.1021 +v -1.1206 4.1428 11.0525 +v 1.0897 4.1428 11.0525 +v 0.8134 3.8708 11.1021 +v 1.3660 3.1666 11.0882 +v 1.0897 3.1666 11.0882 +v -1.3969 3.1666 11.0882 +v -1.1206 3.1666 11.0882 +v 1.2275 1.6088 11.7910 +v 0.9512 1.6088 11.7910 +v -1.2591 1.6088 11.7910 +v -0.9828 1.6088 11.7910 +v 1.0897 3.5100 12.9916 +v 1.3660 3.5100 12.9916 +v 1.0897 4.4373 12.6840 +v 0.8134 4.1654 12.7328 +v -1.1206 4.4373 12.6840 +v -0.8443 4.1654 12.7328 +v -1.1206 3.5100 12.9916 +v -1.3969 3.5100 12.9916 +v 0.9512 1.8056 12.8786 +v 0.9512 1.3237 12.5440 +v 1.2275 1.3237 12.5440 +v 1.2275 1.8056 12.8786 +v 0.9512 1.2749 12.2721 +v 1.2275 1.2749 12.2721 +v -1.2591 1.3237 12.5440 +v -0.9828 1.3237 12.5440 +v -0.9828 1.8056 12.8786 +v -1.2591 1.8056 12.8786 +v -1.2591 1.2749 12.2721 +v -0.9828 1.2749 12.2721 +v -0.4302 3.8825 11.9419 +v -0.4302 2.5441 10.3600 +v -0.8443 2.4508 10.6202 +v -0.8443 3.6106 11.9907 +v 0.3986 3.8825 11.9419 +v 0.3986 2.5441 10.3600 +v 0.8134 3.6106 11.9907 +v 0.8134 2.4508 10.6202 +v -0.4302 0.5051 10.7281 +v -0.8443 0.6829 10.9388 +v 0.3986 0.5051 10.7281 +v 0.8134 0.6829 10.9388 +v -0.4302 -0.1962 12.6782 +v -0.8443 0.0757 12.6293 +v 0.3986 -0.1962 12.6782 +v 0.8134 0.0757 12.6293 +v -0.4302 1.1422 14.2601 +v -0.8443 1.2355 13.9999 +v 0.3986 1.1422 14.2601 +v 0.8134 1.2355 13.9999 +v -0.4302 3.1812 13.8920 +v -0.8443 3.0033 13.6813 +v 0.3986 3.1812 13.8920 +v 0.8134 3.0033 13.6813 +v -0.8443 1.8457 12.3078 +v 0.8134 1.8457 12.3078 +v -0.0257 16.6291 22.4227 +v -0.9792 16.2121 22.4322 +v -0.5542 15.8038 24.2941 +v -0.0249 16.0320 24.2926 +v -1.3940 15.2345 22.4395 +v -0.7809 15.2535 24.3087 +v -0.9792 14.2562 22.4461 +v -0.5542 14.7227 24.3043 +v -0.0257 13.8399 22.4555 +v -0.0249 14.4946 24.3057 +v 0.5233 14.7227 24.3043 +v 0.9490 14.2562 22.4461 +v 0.7500 15.2535 24.3087 +v 1.3638 15.2345 22.4395 +v 0.5233 15.8038 24.2941 +v 0.9490 16.2121 22.4322 +v -0.0249 16.6969 21.0763 +v -0.9515 16.2602 21.0806 +v -1.3291 15.2352 21.0959 +v -0.9515 14.1928 21.0894 +v -0.0249 13.7561 21.0930 +v 0.9206 14.1928 21.0894 +v 1.2982 15.2352 21.0959 +v 0.9206 16.2602 21.0806 +v -0.0154 15.2629 25.2695 +v 5.7407 6.3531 21.9496 +v 5.4951 6.1672 21.7557 +v 4.8521 7.0770 21.5137 +v 5.2115 7.5501 22.2157 +v 4.6822 6.9567 21.4276 +v 5.4003 6.1103 21.7601 +v 4.6633 7.0092 21.6135 +v 5.4003 6.1351 21.8337 +v 5.0416 7.4816 22.3156 +v 5.6270 6.3400 22.0261 +v 5.1925 7.6019 22.4008 +v 5.7218 6.3983 22.0407 +v 3.5290 9.1466 21.5399 +v 4.0772 9.5498 22.3236 +v 3.3212 9.0074 21.4561 +v 3.3212 9.0263 21.7010 +v 3.8694 9.4499 22.5022 +v 4.0961 9.5891 22.5860 +v 0.3717 14.2759 21.8949 +v 0.8069 14.5004 22.1049 +v 0.2769 14.1658 21.9416 +v 0.3527 14.1745 22.0546 +v 0.7879 14.4180 22.2631 +v 0.8820 14.5099 22.2179 +v -5.7716 24.1728 21.9496 +v -5.5260 24.3587 21.7557 +v -4.8837 23.4489 21.5137 +v -5.2424 22.9765 22.2157 +v -4.7131 23.5692 21.4276 +v -5.4319 24.4155 21.7601 +v -4.6942 23.5167 21.6135 +v -5.4319 24.3907 21.8337 +v -5.0725 23.0443 22.3156 +v -5.6586 24.1859 22.0261 +v -5.2234 22.9240 22.4008 +v -5.7534 24.1276 22.0407 +v -3.5598 21.3792 21.5399 +v -4.1080 20.9768 22.3236 +v -3.3521 21.5192 21.4561 +v -3.3521 21.4995 21.7010 +v -3.9003 21.0760 22.5022 +v -4.1270 20.9367 22.5860 +v -0.4033 16.2507 21.8949 +v -0.8377 16.0255 22.1049 +v -0.3085 16.3608 21.9416 +v -0.3843 16.3513 22.0546 +v -0.8188 16.1078 22.2631 +v -0.9136 16.0167 22.2179 +v -0.0176 15.2287 21.0879 +v 0.8134 18.1366 -33.8685 +v 0.6749 20.8245 -51.9294 +v -0.0154 21.1379 -50.8345 +v -0.0154 18.2475 -30.5544 +v 0.6749 22.6718 -54.4809 +v 0.3986 33.2568 -64.2430 +v -0.0154 33.2758 -63.6904 +v -0.0154 22.6958 -53.7906 +v 0.1223 32.6969 -72.5171 +v -0.0154 32.9689 -72.6644 +v -0.0154 33.4303 -64.4172 +v 0.9512 17.4922 -53.0156 +v 0.2609 22.1753 -68.9771 +v 0.2609 16.9943 -67.6460 +v 0.2609 19.9883 -68.2532 +v -0.8443 18.1366 -33.8685 +v -0.7065 20.8245 -51.9294 +v -0.7065 22.6718 -54.4809 +v -0.4302 33.2568 -64.2430 +v -0.1540 32.6969 -72.5171 +v -0.9828 17.4922 -53.0156 +v -0.2917 22.1753 -68.9771 +v -0.2917 16.9943 -67.6460 +v -0.2917 19.9883 -68.2532 +# 595 vertices + +vn 0.9125 -0.1255 0.3893 +vn 0.4066 -0.1324 0.9040 +vn 0.5933 -0.1339 0.7937 +vn 0.9612 -0.1047 0.2552 +vn 0.9950 -0.0846 -0.0535 +vn 0.8617 -0.0693 -0.5027 +vn 0.9035 -0.0912 -0.4188 +vn 0.9944 -0.1046 -0.0132 +vn 0.4362 -0.0578 -0.8980 +vn 0.3262 -0.0427 -0.9443 +vn -0.3911 -0.0156 0.9202 +vn -0.5254 -0.0650 0.8484 +vn 0.3761 -0.0718 0.9238 +vn 0.5461 -0.0098 0.8377 +vn 0.8986 -0.0403 0.4368 +vn 0.9538 0.0004 0.3004 +vn 0.9995 0.0209 0.0228 +vn 0.9915 0.0885 -0.0950 +vn 0.9222 0.0633 0.3814 +vn 0.3776 0.0833 0.9222 +vn 0.8918 0.2437 0.3812 +vn 0.3219 0.3523 0.8788 +vn 0.9699 0.1576 -0.1858 +vn 0.9156 0.2936 0.2746 +vn 0.4847 0.4715 0.7367 +vn -0.5275 0.0920 0.8446 +vn 0.8816 -0.0779 0.4655 +vn 0.4970 -0.1152 0.8601 +vn 0.2844 -0.1325 0.9495 +vn 0.9021 -0.1448 0.4065 +vn 0.9986 0.0079 0.0525 +vn 0.9931 0.0831 -0.0827 +vn 0.9920 0.0915 0.0866 +vn 0.6647 0.1432 -0.7333 +vn 0.8382 0.1635 -0.5203 +vn 0.2716 0.1199 -0.9549 +vn 0.1657 0.1320 -0.9773 +vn 0.9728 0.1613 -0.1662 +vn -0.1657 0.1425 -0.9758 +vn -0.2535 0.1523 -0.9553 +vn 0.1905 0.1531 -0.9697 +vn 0.7521 0.1559 -0.6403 +vn 0.7578 -0.1190 -0.6416 +vn 0.6036 -0.3331 -0.7244 +vn 0.9645 -0.2408 -0.1087 +vn 0.9882 -0.1451 -0.0482 +vn 0.9954 -0.0826 0.0484 +vn 0.9929 -0.0846 0.0834 +vn 0.9722 -0.2269 0.0573 +vn 0.0001 -0.2514 -0.9679 +vn 0.0000 -0.1691 -0.9856 +vn -0.0009 -0.1694 -0.9856 +vn 0.0002 -0.2515 -0.9679 +vn 0.1378 -0.0433 -0.9895 +vn 0.1859 -0.2909 -0.9385 +vn 0.9999 -0.0037 0.0123 +vn 0.9701 0.2377 -0.0494 +vn 0.9810 0.1873 -0.0502 +vn 0.9997 0.0213 -0.0102 +vn 0.7342 0.5562 -0.3893 +vn 0.0974 0.6318 -0.7690 +vn 0.2961 0.6654 -0.6853 +vn 0.8156 0.4943 -0.3010 +vn 0.6834 -0.5180 -0.5145 +vn 0.9037 -0.3761 -0.2046 +vn 0.7909 -0.3232 -0.5196 +vn 0.9001 -0.3291 -0.2854 +vn 0.4182 -0.4575 -0.7848 +vn 0.9828 -0.1590 -0.0944 +vn 0.9755 -0.2195 -0.0174 +vn 0.9999 -0.0033 0.0122 +vn 0.9939 -0.0816 -0.0743 +vn -0.1856 -0.2103 -0.9598 +vn -0.1649 -0.4885 -0.8568 +vn 0.2197 -0.4939 -0.8413 +vn -0.4066 -0.1298 0.9043 +vn -0.4513 -0.1336 0.8823 +vn 1.0000 0.0000 -0.0000 +vn 1.0000 -0.0013 0.0080 +vn 0.9916 0.0104 0.1291 +vn 0.9678 0.0523 0.2463 +vn 0.9942 0.0880 0.0618 +vn 0.0032 0.0426 -0.9991 +vn -0.1263 0.4238 -0.8969 +vn 0.0947 0.4052 -0.9093 +vn -0.0039 0.0167 -0.9999 +vn 0.0767 0.2069 -0.9753 +vn 0.7913 0.3830 -0.4767 +vn 0.1443 0.5118 -0.8469 +vn 0.5017 0.5212 -0.6904 +vn 0.9590 0.1221 -0.2559 +vn 0.0709 -0.3220 -0.9441 +vn -0.0709 -0.2673 -0.9610 +vn 0.9949 -0.1011 0.0056 +vn 0.9975 -0.0706 0.0047 +vn 0.9911 -0.1321 0.0153 +vn 0.9914 -0.1304 0.0129 +vn 0.9992 -0.0394 -0.0036 +vn 0.9999 -0.0101 -0.0004 +vn 1.0000 -0.0097 0.0002 +vn 0.0372 -0.1989 -0.9793 +vn 0.1603 0.8589 0.4864 +vn -0.1602 0.8530 0.4967 +vn -0.0233 0.8274 0.5611 +vn 0.0389 0.8304 0.5558 +vn 0.5747 0.7264 0.3769 +vn 0.5780 0.7447 0.3337 +vn 0.1442 0.7925 0.5926 +vn -0.1924 0.8051 0.5611 +vn 0.6980 0.4744 -0.5364 +vn 0.4793 0.5583 -0.6771 +vn 0.8266 0.5417 -0.1529 +vn 0.8198 0.5725 -0.0154 +vn 0.1772 0.2771 -0.9444 +vn 0.1454 0.6706 -0.7275 +vn 0.0400 0.9593 -0.2794 +vn 0.0267 0.9523 0.3040 +vn 0.9968 0.0798 0.0110 +vn 0.9526 0.3024 0.0316 +vn 0.1711 0.9851 -0.0176 +vn 0.1928 0.9811 0.0170 +vn 0.1861 0.9825 0.0040 +vn 0.1725 0.9848 -0.0218 +vn -0.9369 0.1760 0.3020 +vn -0.9821 0.1874 -0.0193 +vn -0.9721 0.1839 0.1455 +vn -0.9369 0.1755 0.3025 +vn 0.0083 -0.0088 0.9999 +vn 0.9825 -0.1864 0.0025 +vn 0.1635 -0.0239 -0.9863 +vn 0.1635 -0.0238 -0.9863 +vn 0.1634 -0.0240 -0.9863 +vn -0.9662 0.1855 -0.1789 +vn 0.2157 0.9753 0.0472 +vn 0.0352 0.2196 0.9749 +vn 0.0449 0.4479 0.8930 +vn 0.0208 0.5004 0.8656 +vn 0.0249 0.2734 0.9616 +vn 0.0622 0.2517 0.9658 +vn 0.0786 0.3758 0.9233 +vn 0.1376 0.3593 -0.9230 +vn 0.2036 0.1337 -0.9699 +vn -0.0112 0.2328 -0.9725 +vn -0.0062 0.4875 -0.8731 +vn -0.0201 0.1255 -0.9919 +vn -0.0162 0.5036 -0.8638 +vn 0.0380 -0.0418 0.9984 +vn -0.0530 -0.0205 0.9984 +vn 0.1119 -0.1018 -0.9885 +vn -0.0178 -0.1272 -0.9917 +vn 0.8747 -0.0550 -0.4816 +vn 0.6208 -0.0991 -0.7777 +vn -0.1882 -0.0114 0.9821 +vn -0.1738 -0.0415 0.9839 +vn -0.2005 -0.0311 0.9792 +vn -0.1864 -0.0132 0.9824 +vn 0.2287 -0.1259 -0.9653 +vn 0.2291 -0.1035 -0.9679 +vn 0.0547 -0.1596 -0.9857 +vn -0.0281 -0.0625 0.9977 +vn 0.7561 0.0356 -0.6535 +vn -0.0551 0.0854 0.9948 +vn 0.0517 0.1300 0.9902 +vn 0.3005 0.2391 0.9233 +vn 0.1883 0.2939 0.9371 +vn 0.3523 0.0356 -0.9352 +vn 0.5674 0.2187 -0.7939 +vn 0.7127 0.1781 -0.6785 +vn 0.0294 -0.0115 0.9995 +vn -0.0293 -0.1776 -0.9837 +vn 0.0407 -0.0752 0.9963 +vn -0.0238 -0.2129 -0.9768 +vn 0.0339 -0.0600 0.9976 +vn -0.0359 -0.2513 -0.9672 +vn 0.9970 0.0712 -0.0312 +vn 0.9660 0.2390 0.0985 +vn 0.9996 -0.0167 -0.0246 +vn -0.0496 -0.2125 -0.9759 +vn -0.3557 -0.1166 0.9273 +vn 0.5416 0.0173 -0.8405 +vn 0.4473 0.0155 -0.8942 +vn 0.4960 0.0161 -0.8682 +vn 0.5492 0.0221 -0.8354 +vn -0.2059 -0.0119 0.9785 +vn -0.2796 -0.0166 0.9600 +vn -0.1335 -0.0109 0.9910 +vn -0.1309 -0.0071 0.9914 +vn 0.0543 -0.9872 0.1499 +vn 0.0237 -0.9894 0.1432 +vn 0.0428 -0.9906 0.1298 +vn 0.0693 -0.9897 0.1252 +vn 0.0587 0.9955 0.0738 +vn 0.0760 0.9960 0.0465 +vn 0.0483 0.9972 0.0564 +vn 0.0251 0.9971 0.0716 +vn 0.5400 0.0164 -0.8415 +vn 0.6640 0.0022 -0.7477 +vn 0.0313 0.9985 0.0449 +vn 0.0101 0.9997 -0.0221 +vn 0.0090 0.9997 -0.0207 +vn 0.0513 0.9967 0.0625 +vn 0.9739 -0.0099 -0.2267 +vn 0.8937 -0.0049 -0.4486 +vn -0.6345 -0.0022 0.7729 +vn -0.4986 -0.0169 0.8667 +vn -0.8843 0.0051 0.4668 +vn -0.9718 0.0103 0.2358 +vn 0.0426 -0.9904 0.1313 +vn 0.0099 -0.9977 0.0674 +vn 0.0093 -0.9976 0.0681 +vn 0.0360 -0.9880 0.1500 +vn 0.0000 0.0435 0.9991 +vn -1.0000 0.0000 -0.0000 +vn -0.0003 0.9991 -0.0435 +vn 0.0000 0.9991 -0.0434 +vn -0.0009 -0.9990 0.0437 +vn 0.0000 -0.9991 0.0435 +vn 0.7005 -0.5594 -0.4432 +vn 0.7006 -0.6838 -0.2040 +vn 0.7004 -0.1653 -0.6944 +vn 0.7007 0.1042 -0.7058 +vn 0.0000 -0.7838 -0.6210 +vn 0.0000 -0.9583 -0.2859 +vn 0.0000 -0.2318 -0.9728 +vn 0.0000 0.1458 -0.9893 +vn -0.7004 -0.5594 -0.4433 +vn -0.7007 -0.6837 -0.2041 +vn -0.7003 -0.1652 -0.6944 +vn -0.7006 0.1042 -0.7059 +vn 0.7009 -0.6632 0.2626 +vn 0.6471 -0.5300 0.5480 +vn 0.0000 -0.9230 0.3848 +vn 0.0000 -0.6945 0.7195 +vn -0.7024 -0.6558 0.2769 +vn -0.6373 -0.5360 0.5536 +vn 0.5820 -0.0841 0.8088 +vn 0.5713 0.1768 0.8015 +vn 0.0000 -0.4042 0.9147 +vn -0.6050 0.0344 0.7955 +vn -0.5183 0.0357 0.8544 +vn 0.6410 0.5809 0.5017 +vn 0.7025 0.6775 0.2180 +vn 0.0000 0.7558 0.6548 +vn 0.0000 0.9532 0.3024 +vn 0.0000 0.4848 0.8746 +vn -0.7073 0.6151 0.3485 +vn -0.6656 0.6475 0.3710 +vn 0.7011 0.6630 -0.2625 +vn 0.7008 0.5183 -0.4901 +vn 0.0000 0.9298 -0.3682 +vn 0.0000 0.7266 -0.6871 +vn -0.7011 0.6629 -0.2626 +vn -0.7008 0.5183 -0.4902 +vn 0.0320 0.2100 0.9772 +vn 0.0076 0.0028 1.0000 +vn 0.0019 -0.0100 0.9999 +vn 0.0647 0.2325 0.9705 +vn 0.0788 0.3656 0.9274 +vn 0.0564 0.3851 0.9212 +vn 0.0079 -0.0640 -0.9979 +vn 0.0539 0.2933 -0.9545 +vn 0.1491 0.2090 -0.9665 +vn 0.1047 -0.1370 -0.9850 +vn 0.0720 0.3994 -0.9139 +vn 0.1165 0.4450 -0.8879 +vn 0.9940 0.1094 -0.0048 +vn 0.9983 -0.0584 0.0026 +vn 0.9995 -0.0303 0.0013 +vn 0.9885 0.1512 -0.0066 +vn 0.0444 0.1469 0.9881 +vn 0.0000 -0.0114 0.9999 +vn 0.2355 0.1052 -0.9662 +vn 0.1233 -0.1322 -0.9835 +vn 0.2721 0.3877 -0.8807 +vn 0.9615 0.2744 -0.0119 +vn 0.1287 0.3512 0.9274 +vn 0.1597 -0.2598 -0.9523 +vn 0.1184 -0.2501 -0.9610 +vn -0.0144 -0.0706 0.9974 +vn -0.0039 -0.0748 0.9972 +vn 0.9898 -0.1420 0.0063 +vn 0.0143 -0.0560 0.9983 +vn 0.0224 -0.2114 -0.9772 +vn -0.9965 -0.0836 0.0037 +vn -0.9999 -0.0107 0.0005 +vn -0.9553 -0.2954 0.0130 +vn -0.9916 0.1292 -0.0057 +vn -0.9918 0.1278 -0.0056 +vn -0.9923 0.1237 -0.0054 +vn 0.3730 0.7531 0.5420 +vn 0.3866 0.6192 0.6834 +vn 0.4489 0.3861 0.8059 +vn 0.4754 0.4248 0.7704 +vn -0.4595 0.2594 -0.8495 +vn -0.2628 0.3289 -0.9070 +vn -0.2818 0.6164 -0.7353 +vn -0.3403 0.6370 -0.6917 +vn 0.0000 -0.9904 0.1381 +vn 0.0000 -0.9994 -0.0348 +vn 0.0711 -0.9939 0.0844 +vn -0.2545 -0.2041 -0.9453 +vn -0.3562 -0.1795 -0.9170 +vn 0.4923 -0.0176 0.8702 +vn 0.4256 -0.0421 0.9039 +vn 0.1445 -0.9891 0.0278 +vn 0.2779 -0.9247 0.2601 +vn -0.2952 -0.1897 -0.9364 +vn -0.1792 0.1034 -0.9784 +vn 0.4100 0.2647 0.8729 +vn 0.3970 -0.0288 0.9173 +vn 0.4338 0.3937 0.8104 +vn -0.1384 0.5406 -0.8298 +vn 0.0000 -0.1168 -0.9932 +vn 0.0000 -0.0982 -0.9952 +vn 0.0000 -0.1539 -0.9881 +vn 0.0000 -0.0422 -0.9991 +vn -0.7741 -0.0109 -0.6330 +vn -0.7830 -0.0434 -0.6205 +vn -0.7780 -0.0485 -0.6265 +vn -0.7692 0.0053 -0.6390 +vn -0.7629 0.0642 -0.6433 +vn -0.9125 -0.1255 0.3893 +vn -0.9612 -0.1047 0.2552 +vn -0.9950 -0.0846 -0.0535 +vn -0.9944 -0.1046 -0.0132 +vn -0.9035 -0.0912 -0.4188 +vn -0.8617 -0.0693 -0.5027 +vn -0.3278 -0.0530 -0.9432 +vn -0.4983 -0.0474 -0.8657 +vn -0.9538 0.0004 0.3004 +vn -0.8987 -0.0404 0.4368 +vn -0.9222 0.0633 0.3814 +vn -0.9915 0.0885 -0.0950 +vn -0.9995 0.0209 0.0228 +vn -0.3232 0.2762 0.9051 +vn -0.8918 0.2437 0.3812 +vn -0.9699 0.1576 -0.1858 +vn -0.3668 0.4786 0.7978 +vn -0.9156 0.2936 0.2746 +vn -0.8817 -0.0779 0.4654 +vn -0.9021 -0.1447 0.4065 +vn -0.3982 -0.1390 0.9067 +vn -0.9986 0.0079 0.0525 +vn -0.9931 0.0831 -0.0827 +vn -0.9920 0.0915 0.0866 +vn -0.6647 0.1432 -0.7333 +vn -0.8382 0.1635 -0.5203 +vn -0.2042 0.1157 -0.9721 +vn -0.9728 0.1613 -0.1662 +vn -0.7521 0.1559 -0.6403 +vn -0.7578 -0.1190 -0.6416 +vn -0.9882 -0.1451 -0.0482 +vn -0.9645 -0.2408 -0.1087 +vn -0.6036 -0.3331 -0.7244 +vn -0.9954 -0.0826 0.0485 +vn -0.9722 -0.2269 0.0573 +vn -0.9929 -0.0846 0.0835 +vn -0.0001 -0.2514 -0.9679 +vn -0.0002 -0.2515 -0.9679 +vn 0.0009 -0.1694 -0.9856 +vn -0.2073 -0.0455 -0.9772 +vn -0.9810 0.1873 -0.0502 +vn -0.9701 0.2377 -0.0494 +vn -0.9999 -0.0037 0.0123 +vn -0.9997 0.0213 -0.0102 +vn -0.2220 0.6620 -0.7158 +vn -0.1462 0.6313 -0.7617 +vn -0.7342 0.5562 -0.3893 +vn -0.8156 0.4943 -0.3010 +vn -0.6834 -0.5180 -0.5145 +vn -0.9037 -0.3762 -0.2046 +vn -0.7909 -0.3232 -0.5196 +vn -0.4182 -0.4575 -0.7848 +vn -0.9001 -0.3291 -0.2854 +vn -0.9828 -0.1590 -0.0944 +vn -0.9755 -0.2195 -0.0174 +vn -0.9999 -0.0033 0.0122 +vn -0.9939 -0.0816 -0.0743 +vn -1.0000 -0.0013 0.0080 +vn -0.9916 0.0104 0.1291 +vn -0.9678 0.0523 0.2463 +vn -0.9942 0.0880 0.0618 +vn -0.0767 0.2069 -0.9753 +vn -0.7912 0.3830 -0.4767 +vn -0.5017 0.5212 -0.6904 +vn -0.0961 0.5128 -0.8531 +vn -0.9590 0.1221 -0.2559 +vn -0.9949 -0.1011 0.0057 +vn -0.9911 -0.1321 0.0153 +vn -0.9975 -0.0706 0.0047 +vn -0.9914 -0.1304 0.0130 +vn -1.0000 -0.0097 0.0002 +vn -0.9999 -0.0101 -0.0004 +vn -0.9992 -0.0394 -0.0036 +vn -0.0443 0.9435 0.3283 +vn -0.5780 0.7447 0.3337 +vn -0.8266 0.5417 -0.1529 +vn -0.8198 0.5725 -0.0154 +vn -0.5747 0.7264 0.3769 +vn -0.0402 0.9807 -0.1912 +vn -0.6980 0.4744 -0.5364 +vn -0.4793 0.5583 -0.6771 +vn -0.0970 0.6710 -0.7351 +vn -0.2358 0.2810 -0.9303 +vn -0.9968 0.0798 0.0110 +vn -0.9526 0.3024 0.0316 +vn -0.1711 0.9851 -0.0176 +vn -0.1725 0.9848 -0.0218 +vn -0.1861 0.9825 0.0040 +vn -0.1928 0.9811 0.0170 +vn 0.9369 0.1760 0.3020 +vn 0.9369 0.1755 0.3025 +vn 0.9721 0.1840 0.1455 +vn 0.9821 0.1874 -0.0193 +vn -0.0083 -0.0088 0.9999 +vn -0.9825 -0.1864 0.0025 +vn -0.1635 -0.0239 -0.9863 +vn -0.1634 -0.0240 -0.9863 +vn -0.1635 -0.0238 -0.9863 +vn 0.9662 0.1855 -0.1789 +vn -0.2157 0.9753 0.0472 +vn -0.0352 0.2196 0.9749 +vn -0.0249 0.2734 0.9616 +vn -0.0208 0.5004 0.8656 +vn -0.0449 0.4479 0.8930 +vn -0.0786 0.3758 0.9233 +vn -0.0622 0.2517 0.9658 +vn -0.1376 0.3593 -0.9230 +vn 0.0062 0.4875 -0.8731 +vn 0.0112 0.2328 -0.9725 +vn -0.2036 0.1337 -0.9699 +vn 0.0162 0.5036 -0.8638 +vn 0.0201 0.1255 -0.9919 +vn 0.0530 -0.0205 0.9984 +vn -0.0380 -0.0418 0.9984 +vn -0.1119 -0.1018 -0.9885 +vn 0.0178 -0.1272 -0.9917 +vn -0.8747 -0.0550 -0.4816 +vn -0.6208 -0.0991 -0.7777 +vn 0.1882 -0.0114 0.9821 +vn 0.1864 -0.0132 0.9824 +vn 0.2005 -0.0311 0.9792 +vn 0.1738 -0.0415 0.9839 +vn -0.2291 -0.1035 -0.9679 +vn -0.2287 -0.1259 -0.9653 +vn -0.0547 -0.1596 -0.9857 +vn 0.0281 -0.0625 0.9977 +vn -0.7561 0.0356 -0.6535 +vn -0.0517 0.1300 0.9902 +vn 0.0551 0.0854 0.9948 +vn -0.1883 0.2939 0.9371 +vn -0.3005 0.2391 0.9233 +vn -0.3523 0.0356 -0.9352 +vn -0.7127 0.1781 -0.6784 +vn -0.5674 0.2187 -0.7939 +vn -0.0294 -0.0115 0.9995 +vn 0.0293 -0.1776 -0.9837 +vn -0.0407 -0.0752 0.9963 +vn 0.0238 -0.2129 -0.9768 +vn -0.0339 -0.0600 0.9976 +vn 0.0359 -0.2513 -0.9672 +vn -0.9970 0.0712 -0.0312 +vn -0.9660 0.2390 0.0985 +vn -0.9996 -0.0167 -0.0246 +vn -0.0011 -1.0000 0.0012 +vn -0.0033 -1.0000 0.0026 +vn -0.0017 -1.0000 0.0033 +vn 0.0009 -1.0000 0.0022 +vn 0.0033 -1.0000 0.0026 +vn 0.0011 -1.0000 0.0007 +vn 0.0005 -1.0000 -0.0000 +vn -0.0019 -1.0000 -0.0015 +vn -0.0009 -1.0000 -0.0012 +vn 0.0019 -1.0000 -0.0015 +vn -0.5416 0.0173 -0.8405 +vn -0.5492 0.0221 -0.8354 +vn -0.4960 0.0161 -0.8682 +vn -0.4473 0.0155 -0.8942 +vn 0.2059 -0.0119 0.9785 +vn 0.1309 -0.0071 0.9914 +vn 0.1335 -0.0109 0.9910 +vn 0.2796 -0.0166 0.9600 +vn -0.0543 -0.9872 0.1499 +vn -0.0693 -0.9897 0.1252 +vn -0.0428 -0.9906 0.1298 +vn -0.0237 -0.9894 0.1432 +vn -0.0587 0.9955 0.0738 +vn -0.0251 0.9971 0.0716 +vn -0.0483 0.9972 0.0564 +vn -0.0760 0.9960 0.0465 +vn -0.5400 0.0164 -0.8415 +vn -0.6640 0.0022 -0.7477 +vn -0.0090 0.9997 -0.0207 +vn -0.0101 0.9997 -0.0221 +vn -0.0313 0.9985 0.0449 +vn -0.0513 0.9967 0.0624 +vn -0.8937 -0.0049 -0.4486 +vn -0.9739 -0.0099 -0.2267 +vn 0.6345 -0.0022 0.7729 +vn 0.9718 0.0103 0.2358 +vn 0.8843 0.0051 0.4668 +vn 0.4986 -0.0169 0.8667 +vn -0.0093 -0.9976 0.0681 +vn -0.0099 -0.9977 0.0674 +vn -0.0426 -0.9904 0.1313 +vn -0.0360 -0.9880 0.1500 +vn 0.0003 0.9991 -0.0435 +vn 0.0009 -0.9990 0.0437 +vn -0.7004 -0.1653 -0.6944 +vn -0.7006 -0.6838 -0.2040 +vn -0.7005 -0.5594 -0.4432 +vn -0.7007 0.1042 -0.7058 +vn 0.7003 -0.1652 -0.6944 +vn 0.7007 -0.6837 -0.2041 +vn 0.7004 -0.5594 -0.4433 +vn 0.7006 0.1042 -0.7059 +vn -0.6471 -0.5300 0.5480 +vn -0.7009 -0.6632 0.2626 +vn 0.6373 -0.5360 0.5536 +vn 0.7024 -0.6558 0.2769 +vn -0.5713 0.1768 0.8015 +vn -0.5820 -0.0841 0.8088 +vn 0.5183 0.0357 0.8544 +vn 0.6050 0.0344 0.7955 +vn -0.7025 0.6775 0.2180 +vn -0.6410 0.5809 0.5017 +vn 0.6656 0.6475 0.3710 +vn 0.7073 0.6151 0.3485 +vn -0.7008 0.5183 -0.4901 +vn -0.7011 0.6630 -0.2625 +vn 0.7008 0.5183 -0.4902 +vn 0.7011 0.6629 -0.2626 +vn -0.0320 0.2100 0.9772 +vn -0.0647 0.2325 0.9705 +vn -0.0019 -0.0100 0.9999 +vn -0.0076 0.0028 1.0000 +vn -0.0788 0.3656 0.9274 +vn -0.0564 0.3851 0.9212 +vn -0.0079 -0.0640 -0.9979 +vn -0.1047 -0.1370 -0.9850 +vn -0.1491 0.2090 -0.9665 +vn -0.0539 0.2933 -0.9545 +vn -0.1165 0.4450 -0.8879 +vn -0.0720 0.3994 -0.9139 +vn -0.9940 0.1094 -0.0048 +vn -0.9885 0.1512 -0.0066 +vn -0.9995 -0.0303 0.0013 +vn -0.9983 -0.0584 0.0026 +vn -0.0444 0.1469 0.9881 +vn -0.1233 -0.1322 -0.9835 +vn -0.2355 0.1052 -0.9662 +vn -0.2721 0.3877 -0.8807 +vn -0.9615 0.2744 -0.0119 +vn -0.1287 0.3512 0.9274 +vn -0.1184 -0.2501 -0.9610 +vn -0.1597 -0.2598 -0.9523 +vn 0.0144 -0.0706 0.9974 +vn 0.0039 -0.0748 0.9972 +vn -0.9898 -0.1420 0.0063 +vn -0.0143 -0.0560 0.9983 +vn -0.0224 -0.2114 -0.9772 +vn 0.9965 -0.0836 0.0037 +vn 0.9553 -0.2954 0.0130 +vn 0.9999 -0.0107 0.0005 +vn 0.9916 0.1292 -0.0057 +vn 0.9918 0.1278 -0.0056 +vn 0.9923 0.1236 -0.0054 +vn -0.3730 0.7531 0.5420 +vn -0.4754 0.4248 0.7704 +vn -0.4489 0.3861 0.8059 +vn -0.3866 0.6192 0.6834 +vn 0.4595 0.2594 -0.8495 +vn 0.3403 0.6369 -0.6917 +vn 0.2818 0.6164 -0.7353 +vn 0.2628 0.3289 -0.9070 +vn -0.0711 -0.9939 0.0844 +vn 0.3562 -0.1795 -0.9170 +vn 0.2545 -0.2041 -0.9453 +vn -0.4923 -0.0176 0.8702 +vn -0.4256 -0.0421 0.9039 +vn -0.2779 -0.9247 0.2601 +vn -0.1445 -0.9891 0.0278 +vn 0.1792 0.1034 -0.9784 +vn 0.2952 -0.1897 -0.9364 +vn -0.4100 0.2647 0.8729 +vn -0.3970 -0.0288 0.9173 +vn -0.4338 0.3937 0.8104 +vn 0.1384 0.5406 -0.8298 +vn 0.7741 -0.0109 -0.6330 +vn 0.7692 0.0053 -0.6390 +vn 0.7780 -0.0485 -0.6265 +vn 0.7830 -0.0434 -0.6205 +vn 0.7629 0.0642 -0.6433 +vn -0.6022 0.7857 -0.1415 +vn -0.7955 0.5966 -0.1065 +vn -0.3832 0.9090 -0.1637 +vn -0.3839 0.9088 -0.1633 +vn -0.0004 0.1776 0.9841 +vn -0.0002 0.1773 0.9842 +vn -0.0003 0.1779 0.9840 +vn -0.0002 0.1778 0.9841 +vn -0.1369 0.9753 -0.1730 +vn 0.1368 0.9754 -0.1731 +vn -0.3829 0.9096 -0.1614 +vn -0.3828 0.9096 -0.1614 +vn -0.0000 -0.1773 -0.9842 +vn 0.0010 -0.1767 -0.9843 +vn -0.0020 -0.1767 -0.9843 +vn -0.0026 -0.1762 -0.9844 +vn -0.3829 0.9090 -0.1645 +vn -0.3836 0.9090 -0.1633 +vn -0.6038 0.7844 -0.1421 +vn -0.7971 0.5942 -0.1080 +vn -0.9899 0.1398 -0.0252 +vn -0.9910 -0.1318 0.0242 +vn -0.0003 0.1773 0.9841 +vn -0.0010 0.1774 0.9841 +vn -0.9234 0.3778 -0.0671 +vn -0.9236 0.3775 -0.0670 +vn -0.9906 0.1350 -0.0240 +vn -0.9906 -0.1350 0.0240 +vn -0.0018 -0.1770 -0.9842 +vn -0.0004 -0.1772 -0.9842 +vn -0.9239 0.3766 -0.0682 +vn -0.9240 0.3764 -0.0676 +vn -0.9254 -0.3733 0.0662 +vn -0.9236 -0.3771 0.0690 +vn -0.0015 0.1775 0.9841 +vn -0.0007 0.1781 0.9840 +vn -0.9234 -0.3778 0.0671 +vn -0.9236 -0.3775 0.0670 +vn -0.0003 -0.1778 -0.9841 +vn -0.0002 -0.1789 -0.9839 +vn -0.9239 -0.3766 0.0681 +vn -0.9240 -0.3764 0.0676 +vn -0.7971 -0.5942 0.1080 +vn -0.6037 -0.7844 0.1423 +vn -0.3766 -0.9118 0.1640 +vn -0.3827 -0.9098 0.1608 +vn -0.3704 -0.9137 0.1672 +vn 0.0004 0.1785 0.9839 +vn 0.0007 0.1779 0.9841 +vn -0.3829 -0.9096 0.1614 +vn -0.3828 -0.9096 0.1614 +vn -0.3831 -0.9095 0.1613 +vn 0.0002 -0.1796 -0.9837 +vn -0.0001 -0.1797 -0.9837 +vn -0.3827 -0.9091 0.1647 +vn -0.3833 -0.9090 0.1637 +vn 0.3767 -0.9117 0.1640 +vn 0.3827 -0.9088 0.1663 +vn 0.3707 -0.9146 0.1616 +vn 0.0006 0.1771 0.9842 +vn 0.0005 0.1771 0.9842 +vn 0.3829 -0.9096 0.1614 +vn 0.3827 -0.9097 0.1614 +vn 0.3831 -0.9095 0.1614 +vn 0.0004 -0.1788 -0.9839 +vn 0.0004 -0.1779 -0.9841 +vn 0.3827 -0.9091 0.1647 +vn 0.3821 -0.9095 0.1638 +vn 0.3833 -0.9086 0.1656 +vn 0.9913 -0.1292 0.0237 +vn 0.9903 0.1366 -0.0246 +vn 0.9258 -0.3721 0.0660 +vn 0.9276 -0.3676 0.0673 +vn 0.0006 0.1779 0.9840 +vn 0.0006 0.1780 0.9840 +vn 0.9243 -0.3759 0.0668 +vn 0.9241 -0.3762 0.0668 +vn 0.9244 -0.3755 0.0668 +vn 0.0006 -0.1779 -0.9841 +vn 0.0014 -0.1774 -0.9841 +vn 0.9239 -0.3765 0.0681 +vn 0.9240 -0.3762 0.0686 +vn 0.9238 -0.3769 0.0677 +vn 0.7963 0.5955 -0.1063 +vn 0.6043 0.7841 -0.1412 +vn 0.0008 0.1776 0.9841 +vn 0.0004 0.1774 0.9841 +vn 0.9243 0.3759 -0.0668 +vn 0.9244 0.3755 -0.0668 +vn 0.9241 0.3762 -0.0668 +vn 0.0016 -0.1758 -0.9844 +vn 0.0017 -0.1757 -0.9844 +vn 0.9239 0.3765 -0.0681 +vn 0.9238 0.3769 -0.0677 +vn 0.7974 0.5938 -0.1079 +vn 0.6039 0.7843 -0.1421 +vn 0.3832 0.9091 -0.1637 +vn 0.3825 0.9094 -0.1634 +vn 0.3829 0.9096 -0.1614 +vn 0.3831 0.9095 -0.1614 +vn 0.3828 0.9090 -0.1645 +vn 0.3821 0.9096 -0.1634 +vn 0.0000 0.1781 0.9840 +vn 0.0005 0.1776 0.9841 +vn 0.0000 0.1773 0.9841 +vn 0.0000 0.1772 0.9842 +vn 0.0003 0.1771 0.9842 +vn 0.0000 0.1768 0.9842 +vn -0.0003 0.1771 0.9842 +vn -0.0005 0.1776 0.9841 +vn -0.0092 -0.1697 -0.9854 +vn -0.0005 -0.1685 -0.9857 +vn 0.0000 -0.1579 -0.9875 +vn -0.0245 -0.1471 -0.9888 +vn -0.0000 -0.1321 -0.9912 +vn 0.0245 -0.1471 -0.9888 +vn 0.0093 -0.1697 -0.9854 +vn 0.0005 -0.1685 -0.9857 +vn 0.0000 -0.1598 -0.9872 +vn 0.0279 -0.9944 0.1022 +vn 0.0558 -0.9907 0.1239 +vn -0.0372 -0.9891 0.1425 +vn -0.0372 -0.9964 0.0762 +vn -0.0378 -0.9870 0.1562 +vn 0.0000 -0.9619 0.2735 +vn 0.0378 -0.9870 0.1562 +vn 0.0000 -0.7687 0.6396 +vn -0.0478 0.9805 -0.1904 +vn 0.0000 0.9981 -0.0619 +vn -0.0471 0.9620 -0.2690 +vn -0.0471 0.9737 -0.2228 +vn 0.0353 0.9686 -0.2461 +vn 0.0707 0.9668 -0.2455 +vn 0.0478 0.9805 -0.1904 +vn 0.0000 0.9439 0.3303 +vn 0.0000 0.8214 0.5704 +vn 0.0000 -0.2128 0.9771 +vn -0.9743 0.0400 0.2216 +vn -0.9976 0.0124 0.0688 +vn -0.9285 0.0660 0.3655 +vn -0.9284 0.0660 0.3656 +vn -0.9991 -0.0074 -0.0410 +vn 0.9991 0.0074 0.0410 +vn 0.9875 -0.0280 -0.1552 +vn 0.9993 -0.0065 -0.0360 +vn 0.9615 -0.0488 -0.2703 +vn 0.9615 -0.0488 -0.2704 +vn -0.9993 -0.0065 -0.0363 +vn -0.9875 -0.0280 -0.1553 +vn -0.9991 0.0074 0.0408 +vn 0.9991 -0.0074 -0.0408 +vn 0.9743 0.0400 0.2218 +vn 0.9975 0.0124 0.0691 +vn -0.9615 -0.0488 -0.2703 +vn -0.9615 -0.0488 -0.2704 +vn 0.9285 0.0660 0.3655 +vn 0.9284 0.0660 0.3656 +vn 0.0000 0.1777 0.9841 +vn 0.0000 -0.1776 -0.9841 +vn -0.5481 -0.3015 -0.7802 +vn -0.5481 -0.8264 -0.1291 +vn -0.5481 -0.7194 -0.4266 +vn -0.5477 0.0097 -0.8366 +vn 0.0000 -0.3604 -0.9328 +vn 0.0000 -0.9880 -0.1542 +vn 0.0000 -0.8602 -0.5099 +vn 0.0000 0.0117 -0.9999 +vn 0.5474 -0.3017 -0.7806 +vn 0.5473 -0.8269 -0.1291 +vn 0.5475 -0.7198 -0.4267 +vn 0.5471 0.0097 -0.8370 +vn -0.5476 -0.5252 0.6514 +vn -0.5477 -0.7293 0.4100 +vn 0.0000 -0.6277 0.7784 +vn 0.0000 -0.8718 0.4899 +vn 0.5469 -0.5255 0.6517 +vn 0.5470 -0.7298 0.4102 +vn -0.5481 0.3015 0.7802 +vn -0.5477 -0.0097 0.8366 +vn 0.0000 -0.3384 0.9410 +vn 0.5474 0.3017 0.7806 +vn 0.5471 -0.0097 0.8370 +vn -0.5481 0.8264 0.1291 +vn -0.5481 0.7194 0.4266 +vn 0.0000 0.6459 0.7634 +vn 0.0000 0.9880 0.1542 +vn 0.0000 0.8602 0.5099 +vn 0.5473 0.8269 0.1291 +vn 0.5475 0.7198 0.4267 +vn -0.5476 0.5252 -0.6514 +vn -0.5477 0.7293 -0.4100 +vn 0.0000 0.6277 -0.7784 +vn 0.0000 0.8718 -0.4899 +vn 0.5470 0.5255 -0.6517 +vn 0.5470 0.7298 -0.4102 +vn 0.0614 0.1936 -0.9792 +vn -0.4056 0.1174 -0.9065 +vn -0.3649 0.3885 -0.8461 +vn -0.0054 0.4707 -0.8823 +vn -0.9040 0.1897 -0.3831 +vn -0.9899 0.1420 0.0051 +vn -0.8934 0.4420 -0.0803 +vn -0.8252 0.4421 -0.3516 +vn -0.9246 0.0832 0.3718 +vn -0.8434 0.3979 0.3611 +vn -0.3903 0.2181 0.8945 +vn -0.0136 0.1959 0.9805 +vn -0.0871 0.4549 0.8863 +vn -0.3536 0.4551 0.8172 +vn 0.3553 0.4068 0.8416 +vn 0.3992 0.1331 0.9071 +vn 0.8920 0.4446 0.0815 +vn 0.8198 0.4502 0.3539 +vn 0.9021 0.1979 0.3836 +vn 0.9900 0.1408 -0.0065 +vn 0.8466 0.3912 -0.3610 +vn 0.9238 0.0782 -0.3749 +vn 0.3557 0.3879 -0.8503 +vn 0.3874 0.1533 -0.9091 +vn -0.1476 0.0418 -0.9882 +vn -0.4261 0.0235 -0.9044 +vn -0.9295 -0.0250 -0.3678 +vn -0.9901 -0.0329 0.1366 +vn -0.9402 -0.0033 0.3406 +vn -0.4130 0.0450 0.9096 +vn 0.1399 0.0528 0.9888 +vn 0.4191 0.0336 0.9073 +vn 0.9306 -0.0236 0.3654 +vn 0.9896 -0.0347 -0.1399 +vn 0.9384 -0.0074 -0.3456 +vn 0.4059 0.0352 -0.9132 +vn -0.0046 0.6186 -0.7857 +vn -0.7821 0.6231 -0.0003 +vn -0.0047 0.6234 0.7819 +vn 0.7821 0.6231 -0.0003 +vn 0.7653 -0.5329 -0.3610 +vn 0.3710 -0.9284 0.0207 +vn 0.4885 -0.8352 -0.2525 +vn 0.8202 -0.3500 -0.4525 +vn 0.2924 -0.9563 -0.0008 +vn 0.1198 -0.9545 0.2732 +vn -0.7473 0.3985 0.5317 +vn -0.8137 0.0704 0.5770 +vn -0.7764 0.1495 0.6122 +vn -0.6351 0.5927 0.4954 +vn -0.4152 0.8379 0.3543 +vn -0.1925 0.9180 0.3468 +vn -0.1763 0.9381 0.2982 +vn 0.0229 0.9605 0.2773 +vn 0.9133 -0.0161 -0.4070 +vn 0.8829 -0.0687 -0.4646 +vn 0.3864 -0.8782 -0.2820 +vn 0.7566 -0.4637 -0.4610 +vn 0.2687 -0.9404 -0.2085 +vn -0.7546 0.4370 0.4895 +vn -0.8273 0.2051 0.5230 +vn -0.3967 0.8819 0.2547 +vn -0.2712 0.9421 0.1972 +vn 0.8230 -0.2716 -0.4988 +vn 0.2474 -0.9512 -0.1846 +vn 0.6959 -0.5778 -0.4264 +vn -0.0440 -0.9977 -0.0513 +vn -0.7027 0.5498 0.4515 +vn -0.7153 0.5164 0.4708 +vn -0.2684 0.9505 0.1563 +vn -0.0144 0.9988 -0.0476 +vn 0.7312 -0.5226 -0.4385 +vn 0.4239 0.3771 0.8235 +vn 0.5042 0.2760 0.8183 +vn 0.4552 0.3105 0.8345 +vn 0.3809 0.4251 0.8211 +vn 0.4081 0.4724 0.7812 +vn 0.5039 0.2756 0.8186 +vn -0.7640 -0.5348 0.3610 +vn -0.3709 -0.9284 -0.0210 +vn -0.4884 -0.8353 0.2524 +vn -0.8194 -0.3516 0.4527 +vn -0.2920 -0.9564 0.0005 +vn -0.1204 -0.9545 -0.2727 +vn 0.7472 0.3985 -0.5319 +vn 0.8135 0.0711 -0.5772 +vn 0.7761 0.1496 -0.6126 +vn 0.6348 0.5928 -0.4956 +vn 0.4151 0.8379 -0.3544 +vn 0.1924 0.9180 -0.3469 +vn 0.1762 0.9381 -0.2982 +vn -0.0229 0.9605 -0.2773 +vn -0.9126 -0.0212 0.4084 +vn -0.8826 -0.0704 0.4649 +vn -0.3864 -0.8781 0.2821 +vn -0.7563 -0.4643 0.4609 +vn -0.2681 -0.9406 0.2082 +vn 0.7544 0.4374 -0.4894 +vn 0.8269 0.2070 -0.5229 +vn 0.3967 0.8819 -0.2547 +vn 0.2712 0.9421 -0.1972 +vn -0.8227 -0.2730 0.4986 +vn -0.2476 -0.9511 0.1847 +vn -0.6955 -0.5786 0.4260 +vn 0.0443 -0.9977 0.0512 +vn 0.7021 0.5509 -0.4511 +vn 0.7141 0.5186 -0.4702 +vn 0.2686 0.9505 -0.1564 +vn 0.0145 0.9988 0.0475 +vn -0.7305 -0.5241 0.4379 +vn -0.4253 0.3749 -0.8238 +vn -0.5065 0.2745 -0.8174 +vn -0.4572 0.3090 -0.8339 +vn -0.3820 0.4231 -0.8216 +vn -0.4091 0.4698 -0.7822 +vn -0.5068 0.2750 -0.8170 +vn -0.0036 -0.9999 0.0103 +vn -0.0000 -1.0000 0.0079 +vn -0.0000 -1.0000 0.0050 +vn -0.0061 -1.0000 0.0043 +vn -0.0019 -1.0000 -0.0003 +vn 0.0000 -1.0000 0.0035 +vn 0.0019 -1.0000 -0.0003 +vn 0.0061 -1.0000 0.0043 +vn 0.0036 -0.9999 0.0103 +vn 0.7758 0.0868 -0.6250 +vn 0.9254 0.1319 -0.3552 +vn 0.6366 0.1829 -0.7492 +vn 0.5715 0.1158 -0.8124 +vn 0.9334 0.2371 -0.2695 +vn 0.7104 0.1763 -0.6814 +vn 0.5656 0.4530 -0.6892 +vn 0.6343 0.4695 -0.6143 +vn 0.9251 -0.0452 -0.3770 +vn 0.8971 -0.0247 -0.4412 +vn 0.5779 0.0351 -0.8154 +vn 0.9957 -0.0229 -0.0900 +vn 0.9996 -0.0285 0.0029 +vn 0.9982 -0.0377 -0.0469 +vn 0.9995 -0.0257 0.0157 +vn -0.7757 0.0868 -0.6251 +vn -0.5715 0.1158 -0.8124 +vn -0.6364 0.1829 -0.7494 +vn -0.9254 0.1320 -0.3554 +vn -0.9333 0.2373 -0.2696 +vn -0.6339 0.4696 -0.6145 +vn -0.5651 0.4532 -0.6894 +vn -0.7100 0.1765 -0.6818 +vn -0.9248 -0.0452 -0.3777 +vn -0.5770 0.0350 -0.8160 +vn -0.8962 -0.0248 -0.4430 +vn -0.9957 -0.0229 -0.0900 +vn -0.9996 -0.0285 0.0030 +vn -0.9982 -0.0378 -0.0469 +vn -0.9995 -0.0257 0.0157 +vn 0.0000 0.0336 0.9994 +vn 0.0000 0.0335 0.9994 +vn 0.0000 0.0338 0.9994 +vn 0.0000 0.0339 0.9994 +vn 0.0000 0.0334 0.9994 +vn 0.0000 0.0340 0.9994 +vn 0.0000 -0.9800 0.1988 +vn 0.0000 -0.9714 0.2376 +vn 0.0000 -0.9611 0.2762 +vn 0.0000 -0.9483 0.3173 +vn 0.0000 -0.9488 0.3158 +vn 0.0000 -0.9280 0.3725 +vn 0.0000 -0.9169 0.3990 +vn 0.0000 -0.8793 0.4762 +# 957 vertex normals + +vt 0.7378 0.1623 0.0000 +vt 0.7386 0.1548 0.0000 +vt 0.9972 0.1894 0.0000 +vt 0.9963 0.1936 0.0000 +vt 0.9952 0.2057 0.0000 +vt 0.9959 0.2111 0.0000 +vt 0.7388 0.2109 0.0000 +vt 0.7378 0.1898 0.0000 +vt 0.7407 0.2281 0.0000 +vt 0.9967 0.2181 0.0000 +vt 0.2442 0.0685 0.0000 +vt 0.3624 0.0906 0.0000 +vt 0.3607 0.1072 0.0000 +vt 0.2427 0.0887 0.0000 +vt 0.2397 0.1572 0.0000 +vt 0.1745 0.1494 0.0000 +vt 0.1833 0.0800 0.0000 +vt 0.1851 0.0599 0.0000 +vt 0.0823 0.0686 0.0000 +vt 0.0840 0.0503 0.0000 +vt 0.0723 0.1277 0.0000 +vt 0.0398 0.0734 0.0000 +vt 0.0419 0.0537 0.0000 +vt 0.4410 0.1244 0.0000 +vt 0.4425 0.1088 0.0000 +vt 0.5633 0.1272 0.0000 +vt 0.5612 0.1420 0.0000 +vt 0.3541 0.1811 0.0000 +vt 0.1971 0.1681 0.0000 +vt 0.2397 0.1708 0.0000 +vt 0.0693 0.1417 0.0000 +vt 0.1697 0.1719 0.0000 +vt 0.1371 0.1979 0.0000 +vt 0.0586 0.1718 0.0000 +vt 0.0260 0.1174 0.0000 +vt 0.0116 0.1542 0.0000 +vt 0.0234 0.1278 0.0000 +vt 0.5625 0.2138 0.0000 +vt 0.5034 0.2218 0.0000 +vt 0.4693 0.2023 0.0000 +vt 0.5603 0.1849 0.0000 +vt 0.4327 0.1939 0.0000 +vt 0.4392 0.1548 0.0000 +vt 0.4631 0.2042 0.0000 +vt 0.8812 0.8264 0.0000 +vt 0.8359 0.8499 0.0000 +vt 0.8364 0.7822 0.0000 +vt 0.8814 0.7822 0.0000 +vt 0.5671 0.2412 0.0000 +vt 0.5232 0.2484 0.0000 +vt 0.7381 0.9826 0.0000 +vt 0.7180 0.9757 0.0000 +vt 0.7347 0.9347 0.0000 +vt 0.7656 0.9565 0.0000 +vt 0.7100 0.9753 0.0000 +vt 0.6760 0.9755 0.0000 +vt 0.6712 0.9178 0.0000 +vt 0.7106 0.9247 0.0000 +vt 0.4822 0.2324 0.0000 +vt 0.4616 0.2235 0.0000 +vt 0.4360 0.2416 0.0000 +vt 0.4432 0.2380 0.0000 +vt 0.4658 0.2419 0.0000 +vt 0.4394 0.2280 0.0000 +vt 0.4558 0.2177 0.0000 +vt 0.3670 0.1849 0.0000 +vt 0.4237 0.2311 0.0000 +vt 0.5029 0.2576 0.0000 +vt 0.3469 0.1895 0.0000 +vt 0.3628 0.2292 0.0000 +vt 0.3443 0.2211 0.0000 +vt 0.3418 0.1832 0.0000 +vt 0.2493 0.1765 0.0000 +vt 0.2460 0.1790 0.0000 +vt 0.7519 0.0521 0.0000 +vt 0.7548 0.0508 0.0000 +vt 0.7615 0.0876 0.0000 +vt 0.7547 0.0824 0.0000 +vt 0.8443 0.0507 0.0000 +vt 0.8458 0.0880 0.0000 +vt 0.8495 0.0549 0.0000 +vt 0.8497 0.0848 0.0000 +vt 0.6798 0.8142 0.0000 +vt 0.6544 0.8145 0.0000 +vt 0.6789 0.7825 0.0000 +vt 0.6469 0.7809 0.0000 +vt 0.6379 0.8158 0.0000 +vt 0.6397 0.7815 0.0000 +vt 0.6426 0.7746 0.0000 +vt 0.4841 0.2673 0.0000 +vt 0.8738 0.0324 0.0000 +vt 0.8624 0.0038 0.0000 +vt 0.8879 0.0208 0.0000 +vt 0.8914 0.0097 0.0000 +vt 0.8601 0.0379 0.0000 +vt 0.8002 0.0434 0.0000 +vt 0.7992 0.0041 0.0000 +vt 0.4547 0.2816 0.0000 +vt 0.6610 0.0258 0.0000 +vt 0.6475 0.0131 0.0000 +vt 0.6739 0.0129 0.0000 +vt 0.6685 0.0372 0.0000 +vt 0.6602 0.0029 0.0000 +vt 0.6915 0.0664 0.0000 +vt 0.6787 0.0650 0.0000 +vt 0.6789 0.0566 0.0000 +vt 0.6936 0.0527 0.0000 +vt 0.6716 0.0882 0.0000 +vt 0.6634 0.0796 0.0000 +vt 0.6592 0.0728 0.0000 +vt 0.6490 0.0466 0.0000 +vt 0.2342 0.2184 0.0000 +vt 0.2465 0.2117 0.0000 +vt 0.2286 0.2118 0.0000 +vt 0.9277 0.0383 0.0000 +vt 0.9311 0.0125 0.0000 +vt 0.9506 0.0339 0.0000 +vt 0.9470 0.0407 0.0000 +vt 0.9599 0.0352 0.0000 +vt 0.9579 0.0433 0.0000 +vt 0.9457 0.0509 0.0000 +vt 0.9264 0.0479 0.0000 +vt 0.9174 0.0366 0.0000 +vt 0.9210 0.0115 0.0000 +vt 0.9498 0.0132 0.0000 +vt 0.9309 0.0031 0.0000 +vt 0.9498 0.0033 0.0000 +vt 0.9607 0.0134 0.0000 +vt 0.4893 0.5119 0.0000 +vt 0.4898 0.5351 0.0000 +vt 0.2293 0.5359 0.0000 +vt 0.2324 0.5123 0.0000 +vt 0.8554 0.4881 0.0000 +vt 0.8555 0.5105 0.0000 +vt 0.0300 0.2661 0.0000 +vt 0.0518 0.2672 0.0000 +vt 0.0289 0.6325 0.0000 +vt 0.0056 0.6324 0.0000 +vt 0.0291 0.8921 0.0000 +vt 0.0035 0.8930 0.0000 +vt 0.4893 0.3552 0.0000 +vt 0.8551 0.3944 0.0000 +vt 0.1453 0.2670 0.0000 +vt 0.1855 0.6325 0.0000 +vt 0.1454 0.2401 0.0000 +vt 0.1454 0.2359 0.0000 +vt 0.1781 0.2415 0.0000 +vt 0.8737 0.3946 0.0000 +vt 0.8738 0.3574 0.0000 +vt 0.8809 0.3615 0.0000 +vt 0.8807 0.3944 0.0000 +vt 0.1817 0.2489 0.0000 +vt 0.1454 0.2481 0.0000 +vt 0.1831 0.2674 0.0000 +vt 0.8549 0.3572 0.0000 +vt 0.0670 0.2346 0.0000 +vt 0.0871 0.2315 0.0000 +vt 0.0863 0.2406 0.0000 +vt 0.8812 0.4544 0.0000 +vt 0.8735 0.4737 0.0000 +vt 0.8805 0.4764 0.0000 +vt 0.8732 0.4956 0.0000 +vt 0.0663 0.2485 0.0000 +vt 0.0447 0.2465 0.0000 +vt 0.0632 0.2382 0.0000 +vt 0.2322 0.3552 0.0000 +vt 0.1854 0.8927 0.0000 +vt 0.4895 0.3113 0.0000 +vt 0.2304 0.6317 0.0000 +vt 0.2297 0.3110 0.0000 +vt 0.2318 0.8930 0.0000 +vt 0.3390 0.2246 0.0000 +vt 0.2542 0.2180 0.0000 +vt 0.6525 0.7574 0.0000 +vt 0.6567 0.7594 0.0000 +vt 0.3893 0.2360 0.0000 +vt 0.8893 0.1130 0.0000 +vt 0.8917 0.0998 0.0000 +vt 0.9163 0.1007 0.0000 +vt 0.9171 0.1159 0.0000 +vt 0.2415 0.6552 0.0000 +vt 0.2651 0.6546 0.0000 +vt 0.2675 0.6926 0.0000 +vt 0.2408 0.6929 0.0000 +vt 0.9784 0.3763 0.0000 +vt 0.9728 0.3401 0.0000 +vt 0.9871 0.3636 0.0000 +vt 0.9821 0.3784 0.0000 +vt 0.8556 0.5210 0.0000 +vt 0.8588 0.5242 0.0000 +vt 0.8461 0.5297 0.0000 +vt 0.8205 0.5210 0.0000 +vt 0.9111 0.0617 0.0000 +vt 0.8980 0.0611 0.0000 +vt 0.8930 0.5124 0.0000 +vt 0.9249 0.4896 0.0000 +vt 0.9266 0.4908 0.0000 +vt 0.8957 0.5142 0.0000 +vt 0.8995 0.0226 0.0000 +vt 0.9096 0.0221 0.0000 +vt 0.2599 0.6171 0.0000 +vt 0.2445 0.6176 0.0000 +vt 0.2466 0.5775 0.0000 +vt 0.2581 0.5775 0.0000 +vt 0.9777 0.4165 0.0000 +vt 0.9587 0.4512 0.0000 +vt 0.9570 0.4502 0.0000 +vt 0.9756 0.4145 0.0000 +vt 0.9431 0.4624 0.0000 +vt 0.9346 0.4732 0.0000 +vt 0.9332 0.4722 0.0000 +vt 0.9421 0.4610 0.0000 +vt 0.9109 0.0036 0.0000 +vt 0.8978 0.0037 0.0000 +vt 0.2464 0.5600 0.0000 +vt 0.2583 0.5609 0.0000 +vt 0.3141 0.6072 0.0000 +vt 0.3202 0.6107 0.0000 +vt 0.2938 0.6259 0.0000 +vt 0.2938 0.6189 0.0000 +vt 0.1204 0.8991 0.0000 +vt 0.1204 0.9160 0.0000 +vt 0.0907 0.9159 0.0000 +vt 0.0907 0.8990 0.0000 +vt 0.6120 0.3057 0.0000 +vt 0.6181 0.3022 0.0000 +vt 0.6384 0.3139 0.0000 +vt 0.6384 0.3210 0.0000 +vt 0.3141 0.5838 0.0000 +vt 0.3202 0.5802 0.0000 +vt 0.1502 0.8991 0.0000 +vt 0.1502 0.9159 0.0000 +vt 0.6120 0.2753 0.0000 +vt 0.6181 0.2788 0.0000 +vt 0.2938 0.5721 0.0000 +vt 0.2937 0.5663 0.0000 +vt 0.1796 0.8990 0.0000 +vt 0.1796 0.9159 0.0000 +vt 0.6385 0.2614 0.0000 +vt 0.6384 0.2671 0.0000 +vt 0.2736 0.5838 0.0000 +vt 0.2675 0.5802 0.0000 +vt 0.0312 0.8990 0.0000 +vt 0.0312 0.9159 0.0000 +vt 0.0020 0.9159 0.0000 +vt 0.0020 0.8990 0.0000 +vt 0.6647 0.2753 0.0000 +vt 0.6586 0.2788 0.0000 +vt 0.2735 0.6072 0.0000 +vt 0.2674 0.6107 0.0000 +vt 0.0610 0.8990 0.0000 +vt 0.0610 0.9159 0.0000 +vt 0.6648 0.3057 0.0000 +vt 0.6587 0.3022 0.0000 +vt 0.2938 0.5955 0.0000 +vt 0.6384 0.2905 0.0000 +vt 0.4355 0.9628 0.0000 +vt 0.4233 0.8728 0.0000 +vt 0.6230 0.8725 0.0000 +vt 0.6230 0.9370 0.0000 +vt 0.6223 0.9456 0.0000 +vt 0.4372 0.9759 0.0000 +vt 0.8025 0.7410 0.0000 +vt 0.7111 0.7288 0.0000 +vt 0.7388 0.5413 0.0000 +vt 0.8030 0.5415 0.0000 +vt 0.6983 0.7274 0.0000 +vt 0.7295 0.5411 0.0000 +vt 0.7432 0.5259 0.0000 +vt 0.8036 0.5222 0.0000 +vt 0.8031 0.5296 0.0000 +vt 0.7442 0.5317 0.0000 +vt 0.6323 0.9327 0.0000 +vt 0.6343 0.8728 0.0000 +vt 0.7336 0.5329 0.0000 +vt 0.6300 0.9425 0.0000 +vt 0.8310 0.5327 0.0000 +vt 0.8343 0.5411 0.0000 +vt 0.6305 0.8460 0.0000 +vt 0.6222 0.8416 0.0000 +vt 0.4433 0.8096 0.0000 +vt 0.8665 0.7215 0.0000 +vt 0.3410 0.6313 0.0000 +vt 0.3408 0.6423 0.0000 +vt 0.2812 0.6353 0.0000 +vt 0.4326 0.6315 0.0000 +vt 0.4328 0.6394 0.0000 +vt 0.4433 0.6343 0.0000 +vt 0.9693 0.8837 0.0000 +vt 0.6738 0.8836 0.0000 +vt 0.6795 0.8798 0.0000 +vt 0.9742 0.8794 0.0000 +vt 0.9628 0.8881 0.0000 +vt 0.6691 0.8889 0.0000 +vt 0.9715 0.9044 0.0000 +vt 0.6777 0.9056 0.0000 +vt 0.6731 0.9043 0.0000 +vt 0.9671 0.9023 0.0000 +vt 0.9720 0.8660 0.0000 +vt 0.6784 0.8649 0.0000 +vt 0.6592 0.9097 0.0000 +vt 0.6628 0.9060 0.0000 +vt 0.6656 0.8897 0.0000 +vt 0.6526 0.8753 0.0000 +vt 0.6575 0.8597 0.0000 +vt 0.6584 0.8832 0.0000 +vt 0.9730 0.8896 0.0000 +vt 0.9812 0.8952 0.0000 +vt 0.9790 0.8893 0.0000 +vt 0.6402 0.8685 0.0000 +vt 0.6523 0.8564 0.0000 +vt 0.6436 0.8768 0.0000 +vt 0.3337 0.7154 0.0000 +vt 0.3344 0.7206 0.0000 +vt 0.3381 0.7269 0.0000 +vt 0.3367 0.7004 0.0000 +vt 0.3344 0.7036 0.0000 +vt 0.9469 0.0830 0.0000 +vt 0.9604 0.0830 0.0000 +vt 0.9604 0.0889 0.0000 +vt 0.9470 0.0889 0.0000 +vt 0.0663 0.9876 0.0000 +vt 0.0671 0.9896 0.0000 +vt 0.0619 0.9896 0.0000 +vt 0.0628 0.9876 0.0000 +vt 0.3703 0.6616 0.0000 +vt 0.3876 0.6616 0.0000 +vt 0.3876 0.6652 0.0000 +vt 0.3703 0.6652 0.0000 +vt 0.4469 0.6074 0.0000 +vt 0.4475 0.6060 0.0000 +vt 0.4510 0.6060 0.0000 +vt 0.4516 0.6074 0.0000 +vt 0.7455 0.3089 0.0000 +vt 0.7455 0.3120 0.0000 +vt 0.7408 0.3120 0.0000 +vt 0.7408 0.3089 0.0000 +vt 0.9470 0.0772 0.0000 +vt 0.9604 0.0771 0.0000 +vt 0.0582 0.9859 0.0000 +vt 0.0603 0.9851 0.0000 +vt 0.3876 0.6687 0.0000 +vt 0.3703 0.6687 0.0000 +vt 0.4535 0.6035 0.0000 +vt 0.4549 0.6041 0.0000 +vt 0.7361 0.3120 0.0000 +vt 0.7361 0.3089 0.0000 +vt 0.9469 0.0712 0.0000 +vt 0.9604 0.0712 0.0000 +vt 0.0582 0.9807 0.0000 +vt 0.0603 0.9816 0.0000 +vt 0.3876 0.6722 0.0000 +vt 0.3703 0.6722 0.0000 +vt 0.4535 0.6000 0.0000 +vt 0.4549 0.5994 0.0000 +vt 0.7314 0.3120 0.0000 +vt 0.7314 0.3089 0.0000 +vt 0.9466 0.0653 0.0000 +vt 0.9604 0.0654 0.0000 +vt 0.0619 0.9771 0.0000 +vt 0.0628 0.9791 0.0000 +vt 0.3876 0.6757 0.0000 +vt 0.3703 0.6757 0.0000 +vt 0.4510 0.5975 0.0000 +vt 0.4516 0.5960 0.0000 +vt 0.7267 0.3120 0.0000 +vt 0.7267 0.3089 0.0000 +vt 0.9468 0.1065 0.0000 +vt 0.9604 0.1065 0.0000 +vt 0.9604 0.1124 0.0000 +vt 0.9465 0.1124 0.0000 +vt 0.0671 0.9771 0.0000 +vt 0.0663 0.9791 0.0000 +vt 0.3703 0.6475 0.0000 +vt 0.3876 0.6475 0.0000 +vt 0.3876 0.6511 0.0000 +vt 0.3703 0.6511 0.0000 +vt 0.4475 0.5975 0.0000 +vt 0.4469 0.5960 0.0000 +vt 0.7644 0.3091 0.0000 +vt 0.7643 0.3121 0.0000 +vt 0.7596 0.3121 0.0000 +vt 0.7597 0.3090 0.0000 +vt 0.9469 0.1005 0.0000 +vt 0.9604 0.1006 0.0000 +vt 0.0708 0.9807 0.0000 +vt 0.0688 0.9816 0.0000 +vt 0.3876 0.6546 0.0000 +vt 0.3703 0.6546 0.0000 +vt 0.4450 0.6000 0.0000 +vt 0.4436 0.5994 0.0000 +vt 0.7549 0.3120 0.0000 +vt 0.7550 0.3090 0.0000 +vt 0.9469 0.0947 0.0000 +vt 0.9604 0.0948 0.0000 +vt 0.0708 0.9859 0.0000 +vt 0.0688 0.9851 0.0000 +vt 0.3876 0.6581 0.0000 +vt 0.3703 0.6581 0.0000 +vt 0.4450 0.6035 0.0000 +vt 0.4436 0.6041 0.0000 +vt 0.7502 0.3120 0.0000 +vt 0.7502 0.3089 0.0000 +vt 0.3982 0.6572 0.0000 +vt 0.3943 0.6556 0.0000 +vt 0.3982 0.6517 0.0000 +vt 0.3927 0.6517 0.0000 +vt 0.3943 0.6478 0.0000 +vt 0.3982 0.6462 0.0000 +vt 0.4021 0.6478 0.0000 +vt 0.4038 0.6517 0.0000 +vt 0.4021 0.6556 0.0000 +vt 0.1114 0.9319 0.0000 +vt 0.1133 0.9365 0.0000 +vt 0.1067 0.9364 0.0000 +vt 0.1067 0.9298 0.0000 +vt 0.1021 0.9318 0.0000 +vt 0.1001 0.9364 0.0000 +vt 0.1021 0.9411 0.0000 +vt 0.1067 0.9430 0.0000 +vt 0.1113 0.9411 0.0000 +vt 0.2803 0.7277 0.0000 +vt 0.2777 0.7297 0.0000 +vt 0.2775 0.7066 0.0000 +vt 0.2806 0.7080 0.0000 +vt 0.2880 0.7038 0.0000 +vt 0.2881 0.7061 0.0000 +vt 0.2882 0.7329 0.0000 +vt 0.2881 0.7309 0.0000 +vt 0.3071 0.7045 0.0000 +vt 0.3074 0.7059 0.0000 +vt 0.3070 0.7321 0.0000 +vt 0.3073 0.7307 0.0000 +vt 0.1134 0.9235 0.0000 +vt 0.1129 0.9218 0.0000 +vt 0.1227 0.9249 0.0000 +vt 0.1205 0.9257 0.0000 +vt 0.1234 0.9470 0.0000 +vt 0.1208 0.9460 0.0000 +vt 0.1132 0.9489 0.0000 +vt 0.1131 0.9504 0.0000 +vt 0.0935 0.9234 0.0000 +vt 0.0878 0.9237 0.0000 +vt 0.0872 0.9216 0.0000 +vt 0.0937 0.9218 0.0000 +vt 0.3132 0.7064 0.0000 +vt 0.3129 0.7045 0.0000 +vt 0.3160 0.7044 0.0000 +vt 0.3164 0.7067 0.0000 +vt 0.0875 0.9505 0.0000 +vt 0.0874 0.9486 0.0000 +vt 0.0937 0.9482 0.0000 +vt 0.0935 0.9502 0.0000 +vt 0.3161 0.7321 0.0000 +vt 0.3134 0.7320 0.0000 +vt 0.3134 0.7305 0.0000 +vt 0.3162 0.7306 0.0000 +vt 0.7690 0.2714 0.0000 +vt 0.7690 0.2898 0.0000 +vt 0.7607 0.2883 0.0000 +vt 0.7601 0.2720 0.0000 +vt 0.7857 0.2751 0.0000 +vt 0.7869 0.2858 0.0000 +vt 0.7909 0.2816 0.0000 +vt 0.7911 0.2791 0.0000 +vt 0.2774 0.6526 0.0000 +vt 0.2954 0.6488 0.0000 +vt 0.2953 0.6677 0.0000 +vt 0.2774 0.6640 0.0000 +vt 0.2730 0.6597 0.0000 +vt 0.2730 0.6569 0.0000 +vt 0.3060 0.6665 0.0000 +vt 0.3060 0.6497 0.0000 +vt 0.3405 0.6673 0.0000 +vt 0.3405 0.6483 0.0000 +vt 0.3583 0.6526 0.0000 +vt 0.3583 0.6640 0.0000 +vt 0.7167 0.2745 0.0000 +vt 0.7344 0.2710 0.0000 +vt 0.7342 0.2887 0.0000 +vt 0.7161 0.2852 0.0000 +vt 0.3627 0.6597 0.0000 +vt 0.3627 0.6569 0.0000 +vt 0.7115 0.2816 0.0000 +vt 0.7115 0.2785 0.0000 +vt 0.3297 0.6668 0.0000 +vt 0.3297 0.6497 0.0000 +vt 0.7419 0.2883 0.0000 +vt 0.7419 0.2722 0.0000 +vt 0.3464 0.6247 0.0000 +vt 0.3287 0.6070 0.0000 +vt 0.3339 0.6056 0.0000 +vt 0.3478 0.6195 0.0000 +vt 0.7567 0.2952 0.0000 +vt 0.7806 0.2953 0.0000 +vt 0.7805 0.3045 0.0000 +vt 0.7567 0.3044 0.0000 +vt 0.8619 0.1312 0.0000 +vt 0.8670 0.1122 0.0000 +vt 0.8722 0.1108 0.0000 +vt 0.8657 0.1350 0.0000 +vt 0.3352 0.5827 0.0000 +vt 0.3390 0.5865 0.0000 +vt 0.8044 0.2953 0.0000 +vt 0.8044 0.3046 0.0000 +vt 0.8531 0.0983 0.0000 +vt 0.8545 0.0931 0.0000 +vt 0.3594 0.5762 0.0000 +vt 0.3580 0.5814 0.0000 +vt 0.8283 0.2954 0.0000 +vt 0.8283 0.3046 0.0000 +vt 0.8341 0.1034 0.0000 +vt 0.8303 0.0996 0.0000 +vt 0.3772 0.5940 0.0000 +vt 0.3720 0.5954 0.0000 +vt 0.6851 0.2951 0.0000 +vt 0.7090 0.2951 0.0000 +vt 0.7090 0.3043 0.0000 +vt 0.6852 0.3043 0.0000 +vt 0.8290 0.1224 0.0000 +vt 0.8238 0.1238 0.0000 +vt 0.3707 0.6182 0.0000 +vt 0.3669 0.6144 0.0000 +vt 0.7329 0.2951 0.0000 +vt 0.7328 0.3043 0.0000 +vt 0.8429 0.1363 0.0000 +vt 0.8415 0.1415 0.0000 +vt 0.3529 0.6005 0.0000 +vt 0.8480 0.1173 0.0000 +vt 0.9103 0.7368 0.0000 +vt 0.8988 0.7359 0.0000 +vt 0.9041 0.7142 0.0000 +vt 0.9103 0.7155 0.0000 +vt 0.9126 0.6919 0.0000 +vt 0.9241 0.6908 0.0000 +vt 0.9252 0.7120 0.0000 +vt 0.9187 0.7135 0.0000 +vt 0.9358 0.6907 0.0000 +vt 0.9315 0.7129 0.0000 +vt 0.8776 0.7177 0.0000 +vt 0.8891 0.7170 0.0000 +vt 0.8891 0.7383 0.0000 +vt 0.8828 0.7396 0.0000 +vt 0.8955 0.7396 0.0000 +vt 0.9007 0.7177 0.0000 +vt 0.9397 0.7145 0.0000 +vt 0.9460 0.7136 0.0000 +vt 0.9503 0.7358 0.0000 +vt 0.9387 0.7357 0.0000 +vt 0.9332 0.7130 0.0000 +vt 0.9271 0.7346 0.0000 +vt 0.9167 0.7142 0.0000 +vt 0.9220 0.7359 0.0000 +vt 0.9103 0.7516 0.0000 +vt 0.8988 0.7511 0.0000 +vt 0.9110 0.6767 0.0000 +vt 0.9233 0.6761 0.0000 +vt 0.9358 0.6754 0.0000 +vt 0.8776 0.7024 0.0000 +vt 0.8891 0.7020 0.0000 +vt 0.9008 0.7024 0.0000 +vt 0.9503 0.7511 0.0000 +vt 0.9378 0.7504 0.0000 +vt 0.9255 0.7498 0.0000 +vt 0.9220 0.7511 0.0000 +vt 0.9104 0.7018 0.0000 +vt 0.9258 0.7255 0.0000 +vt 0.8892 0.7518 0.0000 +vt 0.9403 0.7010 0.0000 +vt 0.9250 0.5453 0.0000 +vt 0.9290 0.5449 0.0000 +vt 0.9317 0.5572 0.0000 +vt 0.9219 0.5598 0.0000 +vt 0.9342 0.5569 0.0000 +vt 0.9301 0.5447 0.0000 +vt 0.9409 0.5591 0.0000 +vt 0.9389 0.5583 0.0000 +vt 0.9439 0.5464 0.0000 +vt 0.9447 0.5467 0.0000 +vt 0.9508 0.5620 0.0000 +vt 0.9486 0.5476 0.0000 +vt 0.9531 0.5623 0.0000 +vt 0.9498 0.5475 0.0000 +vt 0.9240 0.5458 0.0000 +vt 0.9198 0.5604 0.0000 +vt 0.9325 0.5845 0.0000 +vt 0.9211 0.5854 0.0000 +vt 0.9354 0.5843 0.0000 +vt 0.9394 0.5860 0.0000 +vt 0.9367 0.5856 0.0000 +vt 0.9510 0.5875 0.0000 +vt 0.9541 0.5877 0.0000 +vt 0.9182 0.5857 0.0000 +vt 0.9318 0.6516 0.0000 +vt 0.9259 0.6513 0.0000 +vt 0.9334 0.6511 0.0000 +vt 0.9396 0.6522 0.0000 +vt 0.9381 0.6525 0.0000 +vt 0.9456 0.6524 0.0000 +vt 0.9470 0.6528 0.0000 +vt 0.9244 0.6509 0.0000 +vt 0.9453 0.5455 0.0000 +vt 0.9493 0.5462 0.0000 +vt 0.9498 0.5473 0.0000 +vt 0.9442 0.5460 0.0000 +vt 0.9130 0.6721 0.0000 +vt 0.9091 0.6725 0.0000 +vt 0.9063 0.6602 0.0000 +vt 0.9161 0.6576 0.0000 +vt 0.9038 0.6605 0.0000 +vt 0.9079 0.6727 0.0000 +vt 0.8942 0.6509 0.0000 +vt 0.8962 0.6516 0.0000 +vt 0.8913 0.6635 0.0000 +vt 0.8905 0.6632 0.0000 +vt 0.8844 0.6480 0.0000 +vt 0.8866 0.6624 0.0000 +vt 0.8821 0.6476 0.0000 +vt 0.8853 0.6625 0.0000 +vt 0.9140 0.6715 0.0000 +vt 0.9182 0.6569 0.0000 +vt 0.9055 0.6329 0.0000 +vt 0.9169 0.6320 0.0000 +vt 0.9026 0.6331 0.0000 +vt 0.8957 0.6239 0.0000 +vt 0.8984 0.6243 0.0000 +vt 0.8841 0.6224 0.0000 +vt 0.8811 0.6223 0.0000 +vt 0.9199 0.6317 0.0000 +vt 0.9062 0.5658 0.0000 +vt 0.9121 0.5661 0.0000 +vt 0.9046 0.5663 0.0000 +vt 0.8955 0.5577 0.0000 +vt 0.8970 0.5574 0.0000 +vt 0.8895 0.5576 0.0000 +vt 0.8881 0.5571 0.0000 +vt 0.9136 0.5664 0.0000 +vt 0.8898 0.6645 0.0000 +vt 0.8858 0.6638 0.0000 +vt 0.8854 0.6627 0.0000 +vt 0.8909 0.6640 0.0000 +vt 0.9045 0.1518 0.0000 +vt 0.8936 0.1485 0.0000 +vt 0.9045 0.1363 0.0000 +vt 0.9153 0.1462 0.0000 +vt 0.9200 0.1348 0.0000 +vt 0.9156 0.1242 0.0000 +vt 0.9045 0.1208 0.0000 +vt 0.8936 0.1266 0.0000 +vt 0.8891 0.1378 0.0000 +vt 0.2687 0.5447 0.0000 +vt 0.4646 0.5833 0.0000 +vt 0.4553 0.5880 0.0000 +vt 0.2356 0.5470 0.0000 +vt 0.4926 0.6050 0.0000 +vt 0.5971 0.7265 0.0000 +vt 0.5909 0.7276 0.0000 +vt 0.4869 0.6092 0.0000 +vt 0.6888 0.7224 0.0000 +vt 0.6905 0.7255 0.0000 +vt 0.5977 0.7304 0.0000 +vt 0.4779 0.5493 0.0000 +vt 0.6543 0.6049 0.0000 +vt 0.6403 0.5479 0.0000 +vt 0.6472 0.5801 0.0000 +vt 0.6571 0.7398 0.0000 +vt 0.6879 0.7434 0.0000 +vt 0.4705 0.7838 0.0000 +vt 0.4580 0.7792 0.0000 +vt 0.4300 0.8009 0.0000 +vt 0.4380 0.8036 0.0000 +vt 0.3323 0.9227 0.0000 +vt 0.3254 0.9224 0.0000 +vt 0.2347 0.9190 0.0000 +vt 0.3247 0.9256 0.0000 +vt 0.2329 0.9222 0.0000 +vt 0.4442 0.7426 0.0000 +vt 0.2693 0.8004 0.0000 +vt 0.2824 0.7441 0.0000 +vt 0.2766 0.7758 0.0000 +vt 0.9957 0.7644 0.0000 +vt 0.9794 0.7647 0.0000 +vt 0.9796 0.5508 0.0000 +vt 0.9988 0.5510 0.0000 +vt 0.9874 0.7972 0.0000 +vt 0.9875 0.3898 0.0000 +vt 0.9929 0.3898 0.0000 +vt 0.6463 0.5486 0.0000 +vt 0.6524 0.5790 0.0000 +vt 0.6589 0.6043 0.0000 +vt 0.6918 0.7215 0.0000 +vt 0.6913 0.7252 0.0000 +# 692 texture coords + +o awionetka +g awionetka +usemtl airplane +s off +f 1/1/1 2/2/2 3/3/3 +f 1/1/1 3/3/3 4/4/4 +f 5/5/5 6/6/6 7/7/7 +f 5/5/5 7/7/7 8/8/8 +f 9/9/9 7/7/7 6/6/6 +f 9/9/9 6/6/6 10/10/10 +f 11/11/11 12/12/12 13/12/13 +f 11/11/11 13/12/13 14/11/14 +f 13/12/13 15/13/15 16/14/16 +f 13/12/13 16/14/16 14/11/14 +f 16/14/16 17/15/17 18/16/18 +f 16/14/16 18/16/18 19/17/19 +f 19/17/19 20/18/20 14/11/14 +f 19/17/19 14/11/14 16/14/16 +f 20/18/20 19/17/19 21/19/21 +f 20/18/20 21/19/21 22/20/22 +f 21/19/21 19/17/19 18/16/18 +f 21/19/21 18/16/18 23/21/23 +f 21/19/21 24/22/24 25/23/25 +f 21/19/21 25/23/25 22/20/22 +f 11/11/11 14/11/14 20/18/20 +f 11/11/11 20/18/20 26/18/26 +f 27/24/27 28/25/28 29/26/29 +f 27/24/27 29/26/29 30/27/30 +f 28/25/28 27/24/27 15/13/15 +f 28/25/28 15/13/15 13/12/13 +f 31/28/31 17/15/17 16/14/16 +f 31/28/31 16/14/16 15/13/15 +f 30/27/30 29/26/29 2/2/2 +f 30/27/30 2/2/2 1/1/1 +f 32/29/32 18/16/18 17/15/17 +f 32/29/32 17/15/17 33/30/33 +f 34/31/34 23/21/23 18/16/18 +f 34/31/34 18/16/18 35/32/35 +f 36/33/36 37/34/37 34/31/34 +f 36/33/36 34/31/34 35/32/35 +f 35/32/35 18/16/18 32/29/32 +f 23/21/23 38/35/38 24/22/24 +f 23/21/23 24/22/24 21/19/21 +f 39/34/39 40/36/40 41/36/41 +f 39/34/39 41/36/41 37/34/37 +f 41/36/41 42/37/42 34/31/34 +f 41/36/41 34/31/34 37/34/37 +f 43/38/43 44/39/44 45/40/45 +f 43/38/43 45/40/45 46/41/46 +f 47/42/47 48/43/48 45/40/45 +f 47/42/47 45/40/45 49/44/49 +f 50/45/50 51/46/51 52/47/52 +f 50/45/50 52/47/52 53/48/53 +f 8/8/8 7/7/7 43/38/43 +f 8/8/8 43/38/43 46/41/46 +f 9/9/9 54/49/54 43/38/43 +f 9/9/9 43/38/43 7/7/7 +f 55/50/55 44/39/44 43/38/43 +f 55/50/55 43/38/43 54/49/54 +f 30/27/30 1/1/1 8/8/8 +f 30/27/30 8/8/8 46/41/46 +f 56/51/56 57/52/57 32/53/58 +f 56/51/56 32/53/58 33/54/59 +f 58/55/60 59/56/61 36/57/62 +f 58/55/60 36/57/62 35/58/63 +f 60/59/64 61/60/65 45/40/45 +f 60/59/64 45/40/45 44/39/44 +f 53/61/66 62/62/67 63/63/68 +f 63/63/68 62/62/67 61/60/65 +f 63/63/68 61/60/65 60/59/64 +f 64/64/69 65/65/70 61/60/65 +f 64/64/69 61/60/65 62/62/67 +f 31/28/31 15/13/15 27/24/27 +f 31/28/31 27/24/27 48/43/48 +f 31/28/31 48/43/48 47/42/47 +f 66/66/71 31/28/31 47/42/47 +f 62/62/67 67/67/72 64/64/69 +f 55/50/55 68/50/73 69/68/74 +f 55/50/55 69/68/74 70/68/75 +f 44/39/44 55/50/55 70/68/75 +f 44/39/44 70/68/75 60/59/64 +f 42/37/42 38/35/38 23/21/23 +f 42/37/42 23/21/23 34/31/34 +f 2/2/2 71/2/76 72/3/77 +f 2/2/2 72/3/77 3/3/3 +f 73/69/78 66/66/71 74/70/79 +f 73/69/78 74/70/79 75/71/78 +f 66/66/71 73/69/78 31/28/31 +f 76/72/80 77/73/81 33/30/33 +f 76/72/80 33/30/33 31/28/31 +f 78/74/82 33/30/33 77/73/81 +f 73/69/78 76/72/80 31/28/31 +f 78/75/78 77/76/78 79/77/78 +f 78/75/78 79/77/78 80/78/78 +f 76/79/78 81/80/78 79/77/78 +f 76/79/78 79/77/78 77/76/78 +f 76/79/78 73/81/78 75/82/78 +f 76/79/78 75/82/78 81/80/78 +f 31/28/31 33/30/33 17/15/17 +f 82/83/83 83/84/84 84/84/85 +f 82/83/83 84/84/85 85/83/86 +f 86/85/87 85/83/86 84/84/85 +f 86/85/87 84/84/85 87/86/88 +f 84/84/85 59/87/89 58/88/90 +f 84/84/85 58/88/90 87/86/88 +f 58/55/60 35/58/63 32/53/58 +f 58/55/60 32/53/58 57/52/57 +f 87/86/88 58/88/90 57/89/91 +f 70/68/75 88/90/92 63/63/68 +f 70/68/75 63/63/68 60/59/64 +f 88/90/92 70/68/75 69/68/74 +f 88/90/92 69/68/74 89/90/93 +f 64/91/94 47/92/95 65/93/96 +f 65/93/96 47/92/95 49/94/97 +f 47/92/95 67/95/98 74/96/99 +f 47/92/95 74/96/99 66/97/100 +f 67/95/98 47/92/95 64/91/94 +f 45/40/45 61/60/65 65/65/70 +f 45/40/45 65/65/70 49/44/49 +f 50/98/101 53/61/66 63/63/68 +f 50/98/101 63/63/68 88/90/92 +f 90/99/102 91/99/103 92/100/104 +f 90/99/102 92/100/104 93/100/105 +f 90/99/102 24/101/106 94/102/107 +f 25/103/108 93/100/105 92/100/104 +f 25/103/108 92/100/104 95/103/109 +f 25/103/108 24/101/106 90/99/102 +f 25/103/108 90/99/102 93/100/105 +f 42/104/110 96/105/111 97/106/112 +f 42/104/110 97/106/112 38/107/113 +f 41/108/114 98/109/115 96/105/111 +f 41/108/114 96/105/111 42/104/110 +f 96/105/111 98/109/115 99/110/116 +f 90/99/102 94/102/107 100/111/117 +f 4/4/4 5/5/5 8/8/8 +f 4/4/4 8/8/8 1/1/1 +f 101/112/118 33/30/33 78/74/82 +f 101/112/118 78/74/82 80/113/78 +f 33/30/33 101/112/118 56/114/119 +f 102/115/120 103/116/121 104/117/122 +f 102/115/120 104/117/122 105/118/123 +f 105/118/124 104/117/125 97/119/126 +f 105/118/124 97/119/126 96/120/127 +f 102/115/128 105/118/128 96/121/128 +f 102/115/128 96/121/128 99/122/128 +f 103/116/129 102/115/129 99/123/129 +f 103/116/129 99/123/129 100/124/129 +f 106/125/130 103/116/131 100/126/130 +f 106/125/130 100/126/130 94/127/132 +f 104/117/125 106/125/133 94/128/133 +f 104/117/125 94/128/133 97/119/126 +f 103/116/121 106/125/134 104/117/122 +f 107/129/135 108/130/136 87/131/137 +f 107/129/135 87/131/137 101/132/138 +f 109/133/139 110/134/140 108/130/136 +f 109/133/139 108/130/136 107/129/135 +f 110/135/141 111/136/142 112/137/143 +f 110/135/141 112/137/143 108/138/144 +f 112/137/143 86/139/145 87/140/146 +f 112/137/143 87/140/146 108/138/144 +f 107/129/135 113/141/147 114/142/148 +f 107/129/135 114/142/148 109/133/139 +f 115/143/149 116/144/150 112/137/143 +f 115/143/149 112/137/143 111/136/142 +f 117/145/151 118/146/78 119/147/152 +f 120/148/153 121/149/154 119/150/155 +f 120/148/153 119/150/155 118/151/156 +f 119/147/152 121/152/157 122/153/158 +f 119/147/152 122/153/158 117/145/151 +f 115/143/149 122/153/158 121/152/157 +f 115/143/149 121/152/157 123/154/159 +f 121/149/154 120/148/153 114/142/148 +f 121/149/154 114/142/148 123/155/160 +f 124/156/78 125/157/78 126/158/161 +f 120/148/153 118/151/156 125/159/162 +f 120/148/153 125/159/162 127/160/163 +f 127/160/163 125/159/162 124/161/164 +f 127/160/163 124/161/164 128/162/165 +f 126/158/161 117/145/151 122/153/158 +f 126/158/161 122/153/158 129/163/166 +f 128/164/167 124/165/168 126/158/161 +f 128/164/167 126/158/161 129/163/166 +f 115/143/149 111/136/142 129/163/166 +f 115/143/149 129/163/166 122/153/158 +f 114/142/148 120/148/153 127/160/163 +f 114/142/148 127/160/163 109/133/139 +f 128/164/167 129/163/166 111/136/142 +f 128/164/167 111/136/142 110/135/141 +f 109/133/139 127/160/163 128/162/165 +f 109/133/139 128/162/165 110/134/140 +f 125/157/78 118/146/78 117/145/151 +f 125/157/78 117/145/151 126/158/161 +f 130/166/169 113/141/147 107/129/135 +f 130/166/169 107/129/135 101/132/138 +f 86/139/145 112/137/143 116/144/150 +f 86/139/145 116/144/150 52/167/170 +f 123/155/160 114/142/148 113/141/147 +f 123/155/160 113/141/147 131/168/171 +f 116/144/150 115/143/149 123/154/159 +f 116/144/150 123/154/159 131/169/172 +f 113/141/147 130/166/169 53/170/173 +f 113/141/147 53/170/173 131/168/171 +f 131/169/172 53/171/174 52/167/170 +f 131/169/172 52/167/170 116/144/150 +f 75/71/78 74/70/79 81/172/78 +f 80/113/78 79/173/78 101/112/118 +f 87/86/88 57/89/91 56/174/175 +f 87/86/88 56/174/175 101/175/176 +f 62/62/67 53/61/66 67/67/72 +f 130/176/177 74/70/79 67/67/72 +f 130/176/177 67/67/72 53/61/66 +f 81/172/78 101/112/118 79/173/78 +f 130/176/177 101/112/118 81/172/78 +f 130/176/177 81/172/78 74/70/79 +f 30/27/30 46/41/46 48/43/48 +f 30/27/30 48/43/48 27/24/27 +f 50/98/101 88/90/92 89/90/93 +f 50/98/101 89/90/93 132/98/178 +f 52/47/52 51/46/51 85/83/86 +f 52/47/52 85/83/86 86/85/87 +f 13/12/13 12/12/12 133/25/179 +f 13/12/13 133/25/179 28/25/28 +f 48/43/48 46/41/46 45/40/45 +f 134/177/180 135/178/181 136/179/182 +f 134/177/180 136/179/182 137/180/183 +f 138/181/184 139/182/185 140/183/186 +f 138/181/184 140/183/186 141/184/187 +f 138/185/188 141/186/189 137/187/190 +f 138/185/188 137/187/190 136/188/191 +f 139/189/192 135/190/193 134/191/194 +f 139/189/192 134/191/194 140/192/195 +f 142/193/196 136/179/182 135/178/181 +f 142/193/196 135/178/181 143/194/197 +f 144/195/198 145/196/199 146/197/200 +f 144/195/198 146/197/200 143/198/201 +f 143/194/197 146/199/202 147/200/203 +f 143/194/197 147/200/203 142/193/196 +f 144/201/204 148/202/205 149/203/206 +f 144/201/204 149/203/206 145/204/207 +f 142/205/208 147/206/209 149/207/210 +f 142/205/208 149/207/210 148/208/211 +f 150/209/212 151/210/212 152/211/212 +f 150/209/212 152/211/212 153/212/212 +f 150/213/78 147/200/203 146/199/202 +f 150/213/78 146/199/202 151/214/78 +f 145/204/207 149/203/206 153/215/213 +f 145/204/207 153/215/213 152/216/213 +f 146/197/200 145/196/199 152/211/214 +f 146/197/200 152/211/214 151/210/215 +f 150/209/216 153/212/217 149/207/210 +f 150/209/216 149/207/210 147/206/209 +f 148/202/205 144/201/204 139/182/185 +f 148/202/205 139/182/185 138/181/184 +f 139/189/192 144/195/198 143/198/201 +f 139/189/192 143/198/201 135/190/193 +f 138/185/188 136/188/191 142/205/208 +f 138/185/188 142/205/208 148/208/211 +f 154/217/218 155/218/219 156/219/220 +f 154/217/218 156/219/220 157/220/221 +f 155/221/222 158/222/223 159/223/224 +f 155/221/222 159/223/224 156/224/225 +f 158/225/226 160/226/227 161/227/228 +f 158/225/226 161/227/228 159/228/229 +f 162/229/230 163/230/231 155/218/219 +f 162/229/230 155/218/219 154/217/218 +f 163/231/232 164/232/233 158/222/223 +f 163/231/232 158/222/223 155/221/222 +f 164/233/234 165/234/235 160/226/227 +f 164/233/234 160/226/227 158/225/226 +f 166/235/236 167/236/237 163/230/231 +f 166/235/236 163/230/231 162/229/230 +f 167/237/238 168/238/238 164/232/233 +f 167/237/238 164/232/233 163/231/232 +f 168/239/239 169/240/240 165/234/235 +f 168/239/239 165/234/235 164/233/234 +f 170/241/241 171/242/242 167/236/237 +f 170/241/241 167/236/237 166/235/236 +f 171/243/243 172/244/244 168/245/245 +f 171/243/243 168/245/245 167/246/245 +f 168/239/239 172/247/246 173/248/247 +f 168/239/239 173/248/247 169/240/240 +f 174/249/248 175/250/249 171/242/242 +f 174/249/248 171/242/242 170/241/241 +f 175/251/250 176/252/251 172/244/244 +f 175/251/250 172/244/244 171/243/243 +f 176/253/252 177/254/253 173/248/247 +f 176/253/252 173/248/247 172/247/246 +f 157/220/221 156/219/220 175/250/249 +f 157/220/221 175/250/249 174/249/248 +f 156/224/225 159/223/224 176/252/251 +f 156/224/225 176/252/251 175/251/250 +f 159/228/229 161/227/228 177/254/253 +f 159/228/229 177/254/253 176/253/252 +f 157/220/78 178/255/78 154/217/78 +f 161/227/213 160/226/213 179/256/213 +f 154/217/78 178/255/78 162/229/78 +f 160/226/213 165/234/213 179/256/213 +f 162/229/78 178/255/78 166/235/78 +f 165/234/213 169/240/213 179/256/213 +f 166/235/78 178/255/78 170/241/78 +f 169/240/213 173/248/213 179/256/213 +f 170/241/78 178/255/78 174/249/78 +f 173/248/213 177/254/213 179/256/213 +f 174/249/78 178/255/78 157/220/78 +f 177/254/213 161/227/213 179/256/213 +f 180/257/254 181/258/255 182/259/256 +f 180/257/254 182/259/256 183/260/257 +f 184/261/258 185/262/259 180/257/254 +f 184/261/258 180/257/254 183/260/257 +f 186/263/260 187/264/261 188/265/262 +f 186/263/260 188/265/262 189/266/263 +f 187/264/261 185/267/264 184/268/265 +f 187/264/261 184/268/265 188/265/262 +f 190/269/266 191/270/267 192/271/268 +f 190/269/266 192/271/268 193/272/269 +f 190/273/270 183/260/257 182/259/256 +f 190/273/270 182/259/256 191/274/271 +f 189/266/263 188/265/262 193/272/272 +f 189/266/263 193/272/272 192/271/273 +f 188/265/262 184/268/265 194/275/274 +f 188/265/262 194/275/274 193/272/272 +f 193/272/269 194/275/275 190/269/266 +f 194/276/276 184/261/258 183/260/257 +f 194/276/276 183/260/257 190/273/270 +f 189/266/263 192/271/273 195/277/277 +f 189/266/263 195/277/277 196/278/278 +f 195/279/279 191/274/271 182/259/256 +f 195/279/279 182/259/256 196/280/280 +f 192/271/268 191/270/267 195/277/281 +f 197/281/282 196/280/280 182/259/256 +f 197/281/282 182/259/256 181/258/255 +f 189/266/263 196/278/278 197/282/283 +f 189/266/263 197/282/283 186/263/260 +f 181/283/284 186/284/285 197/285/286 +f 180/286/287 187/287/288 186/284/285 +f 180/286/287 186/284/285 181/283/284 +f 180/286/287 185/288/289 187/287/288 +f 198/289/290 199/290/291 200/291/292 +f 198/289/290 200/291/292 201/292/293 +f 202/293/294 203/294/295 199/290/296 +f 202/293/294 199/290/296 198/289/297 +f 204/295/298 205/296/299 206/297/300 +f 204/295/298 206/297/300 207/298/298 +f 202/293/294 207/298/301 206/297/302 +f 202/293/294 206/297/302 203/294/295 +f 204/299/303 201/292/293 200/291/292 +f 204/299/303 200/291/292 205/300/304 +f 206/297/300 205/296/299 208/301/305 +f 206/297/300 208/301/305 209/302/306 +f 206/297/302 209/302/307 210/303/308 +f 206/297/302 210/303/308 203/294/295 +f 211/304/309 208/305/310 205/300/304 +f 211/304/309 205/300/304 200/291/292 +f 199/290/291 212/306/311 211/304/309 +f 199/290/291 211/304/309 200/291/292 +f 203/294/295 210/303/308 212/306/312 +f 203/294/295 212/306/312 199/290/296 +f 202/307/313 201/308/314 204/295/315 +f 202/307/313 204/295/315 207/298/301 +f 201/308/314 202/307/313 198/309/316 +f 210/310/317 209/311/318 208/305/319 +f 210/310/317 208/305/319 211/304/320 +f 211/304/320 212/312/321 210/310/317 +f 213/1/322 214/4/323 72/3/77 +f 213/1/322 72/3/77 71/2/76 +f 215/5/324 216/8/325 217/7/326 +f 215/5/324 217/7/326 218/6/327 +f 219/9/328 220/10/329 218/6/327 +f 219/9/328 218/6/327 217/7/326 +f 12/12/12 11/11/11 221/14/330 +f 12/12/12 221/14/330 222/13/331 +f 221/14/330 223/17/332 224/16/333 +f 221/14/330 224/16/333 225/15/334 +f 223/17/332 221/14/330 11/11/11 +f 223/17/332 11/11/11 26/18/26 +f 26/18/26 226/20/335 227/19/336 +f 26/18/26 227/19/336 223/17/332 +f 224/16/333 223/17/332 227/19/336 +f 224/16/333 227/19/336 228/21/337 +f 226/20/335 26/18/26 20/18/20 +f 226/20/335 20/18/20 22/20/22 +f 227/19/336 226/20/335 95/23/338 +f 227/19/336 95/23/338 229/22/339 +f 230/24/340 231/27/341 232/26/342 +f 230/24/340 232/26/342 133/25/179 +f 133/25/179 12/12/12 222/13/331 +f 133/25/179 222/13/331 230/24/340 +f 221/14/330 225/15/334 233/28/343 +f 221/14/330 233/28/343 222/13/331 +f 71/2/76 2/2/2 29/26/29 +f 71/2/76 29/26/29 232/26/342 +f 231/27/341 213/1/322 71/2/76 +f 231/27/341 71/2/76 232/26/342 +f 234/29/344 235/30/345 225/15/334 +f 234/29/344 225/15/334 224/16/333 +f 224/16/333 228/21/337 236/31/346 +f 224/16/333 236/31/346 237/32/347 +f 236/31/346 39/34/39 238/33/348 +f 236/31/346 238/33/348 237/32/347 +f 237/32/347 234/29/344 224/16/333 +f 37/34/37 36/33/36 238/33/348 +f 37/34/37 238/33/348 39/34/39 +f 228/21/337 227/19/336 229/22/339 +f 228/21/337 229/22/339 239/35/349 +f 40/36/40 39/34/39 236/31/346 +f 40/36/40 236/31/346 240/37/350 +f 241/38/351 242/41/352 243/40/353 +f 241/38/351 243/40/353 244/39/354 +f 245/42/355 246/44/356 243/40/353 +f 245/42/355 243/40/353 247/43/357 +f 51/46/51 82/83/83 85/83/86 +f 132/45/358 248/48/359 249/47/360 +f 132/45/358 249/47/360 51/46/51 +f 219/9/328 250/49/361 54/49/54 +f 219/9/328 54/49/54 9/9/9 +f 216/8/325 242/41/352 241/38/351 +f 216/8/325 241/38/351 217/7/326 +f 219/9/328 217/7/326 241/38/351 +f 219/9/328 241/38/351 250/49/361 +f 68/50/73 250/49/361 241/38/351 +f 68/50/73 241/38/351 244/39/354 +f 231/27/341 242/41/352 216/8/325 +f 231/27/341 216/8/325 213/1/322 +f 234/53/362 251/52/363 252/51/364 +f 234/53/362 252/51/364 235/54/365 +f 238/57/366 253/56/367 254/55/368 +f 238/57/366 254/55/368 237/58/369 +f 238/57/366 36/57/62 59/56/61 +f 238/57/366 59/56/61 253/56/367 +f 255/59/370 244/39/354 243/40/353 +f 255/59/370 243/40/353 256/60/371 +f 248/61/372 257/63/373 258/62/374 +f 257/63/373 255/59/370 256/60/371 +f 257/63/373 256/60/371 258/62/374 +f 259/64/375 258/62/374 256/60/371 +f 259/64/375 256/60/371 260/65/376 +f 233/28/343 245/42/355 247/43/357 +f 233/28/343 247/43/357 230/24/340 +f 233/28/343 230/24/340 222/13/331 +f 261/66/377 245/42/355 233/28/343 +f 258/62/374 259/64/375 262/67/378 +f 244/39/354 255/59/370 69/68/74 +f 244/39/354 69/68/74 68/50/73 +f 240/37/350 236/31/346 228/21/337 +f 240/37/350 228/21/337 239/35/349 +f 22/20/22 25/23/25 95/23/338 +f 22/20/22 95/23/338 226/20/335 +f 29/26/29 28/25/28 133/25/179 +f 29/26/29 133/25/179 232/26/342 +f 219/9/328 9/9/9 10/10/10 +f 219/9/328 10/10/10 220/10/329 +f 68/50/73 55/50/55 54/49/54 +f 68/50/73 54/49/54 250/49/361 +f 263/69/213 264/71/213 265/70/379 +f 263/69/213 265/70/379 261/66/377 +f 261/66/377 233/28/343 263/69/213 +f 266/72/380 233/28/343 235/30/345 +f 266/72/380 235/30/345 267/73/381 +f 268/74/382 267/73/381 235/30/345 +f 263/69/213 233/28/343 266/72/380 +f 268/75/213 269/78/213 270/77/213 +f 268/75/213 270/77/213 267/76/213 +f 266/79/213 267/76/213 270/77/213 +f 266/79/213 270/77/213 271/80/213 +f 266/79/213 271/80/213 264/82/213 +f 266/79/213 264/82/213 263/81/213 +f 233/28/343 225/15/334 235/30/345 +f 83/84/84 82/83/83 272/85/383 +f 83/84/84 272/85/383 273/86/384 +f 83/84/84 273/86/384 254/88/385 +f 83/84/84 254/88/385 253/87/386 +f 234/53/362 237/58/369 254/55/368 +f 234/53/362 254/55/368 251/52/363 +f 273/86/384 251/89/387 254/88/385 +f 253/87/386 59/87/89 84/84/85 +f 253/87/386 84/84/85 83/84/84 +f 69/68/74 255/59/370 257/63/373 +f 69/68/74 257/63/373 89/90/93 +f 259/91/388 260/93/389 245/92/390 +f 260/93/389 246/94/391 245/92/390 +f 245/92/390 261/97/392 265/96/393 +f 245/92/390 265/96/393 262/95/394 +f 262/95/394 259/91/388 245/92/390 +f 243/40/353 246/44/356 260/65/376 +f 243/40/353 260/65/376 256/60/371 +f 132/98/178 89/90/93 257/63/373 +f 132/98/178 257/63/373 248/61/372 +f 91/99/103 90/99/102 100/111/117 +f 91/99/103 100/111/117 274/111/395 +f 275/102/396 276/106/397 239/107/398 +f 275/102/396 239/107/398 229/101/399 +f 95/103/109 92/100/104 91/99/103 +f 95/103/109 91/99/103 229/101/399 +f 100/111/117 99/110/116 277/110/400 +f 100/111/117 277/110/400 274/111/395 +f 240/104/401 239/107/398 276/106/397 +f 240/104/401 276/106/397 278/105/402 +f 279/109/403 98/109/115 41/108/114 +f 279/109/403 41/108/114 40/108/404 +f 40/108/404 240/104/401 278/105/402 +f 40/108/404 278/105/402 279/109/403 +f 278/105/402 277/110/400 279/109/403 +f 99/110/116 98/109/115 279/109/403 +f 99/110/116 279/109/403 277/110/400 +f 91/99/103 274/111/395 275/102/396 +f 214/4/323 213/1/322 216/8/325 +f 214/4/323 216/8/325 215/5/324 +f 280/112/405 269/113/213 268/74/382 +f 280/112/405 268/74/382 235/30/345 +f 252/114/406 280/112/405 235/30/345 +f 132/45/358 51/46/51 50/45/50 +f 281/115/407 282/118/408 283/117/409 +f 281/115/407 283/117/409 284/116/410 +f 282/118/411 278/120/412 276/119/413 +f 282/118/411 276/119/413 283/117/414 +f 281/115/415 277/122/415 278/121/415 +f 281/115/415 278/121/415 282/118/415 +f 284/116/416 274/124/416 277/123/416 +f 284/116/416 277/123/416 281/115/416 +f 285/125/417 275/127/418 274/126/417 +f 285/125/417 274/126/417 284/116/419 +f 283/117/414 276/119/413 275/128/420 +f 283/117/414 275/128/420 285/125/420 +f 284/116/410 283/117/409 285/125/421 +f 286/129/422 280/132/423 273/131/424 +f 286/129/422 273/131/424 287/130/425 +f 287/130/425 288/134/426 289/133/427 +f 287/130/425 289/133/427 286/129/422 +f 288/135/428 287/138/429 290/137/430 +f 288/135/428 290/137/430 291/136/431 +f 273/140/432 272/139/433 290/137/430 +f 273/140/432 290/137/430 287/138/429 +f 286/129/422 289/133/427 292/142/434 +f 286/129/422 292/142/434 293/141/435 +f 294/143/436 291/136/431 290/137/430 +f 294/143/436 290/137/430 295/144/437 +f 296/145/438 297/147/439 298/146/213 +f 299/148/440 298/151/441 297/150/442 +f 299/148/440 297/150/442 300/149/443 +f 297/147/439 296/145/438 301/153/444 +f 297/147/439 301/153/444 300/152/445 +f 294/143/436 302/154/446 300/152/445 +f 294/143/436 300/152/445 301/153/444 +f 300/149/443 302/155/447 292/142/434 +f 300/149/443 292/142/434 299/148/440 +f 303/158/448 304/157/213 305/156/213 +f 299/148/440 306/160/449 304/159/450 +f 299/148/440 304/159/450 298/151/441 +f 306/160/449 307/162/451 305/161/452 +f 306/160/449 305/161/452 304/159/450 +f 303/158/448 308/163/453 301/153/444 +f 303/158/448 301/153/444 296/145/438 +f 303/158/448 305/165/454 307/164/455 +f 303/158/448 307/164/455 308/163/453 +f 294/143/436 301/153/444 308/163/453 +f 294/143/436 308/163/453 291/136/431 +f 292/142/434 289/133/427 306/160/449 +f 292/142/434 306/160/449 299/148/440 +f 291/136/431 308/163/453 307/164/455 +f 291/136/431 307/164/455 288/135/428 +f 289/133/427 288/134/426 307/162/451 +f 289/133/427 307/162/451 306/160/449 +f 304/157/213 303/158/448 296/145/438 +f 304/157/213 296/145/438 298/146/213 +f 309/166/456 280/132/423 286/129/422 +f 309/166/456 286/129/422 293/141/435 +f 272/139/433 249/167/457 295/144/437 +f 272/139/433 295/144/437 290/137/430 +f 302/155/447 310/168/458 293/141/435 +f 302/155/447 293/141/435 292/142/434 +f 295/144/437 310/169/459 302/154/446 +f 295/144/437 302/154/446 294/143/436 +f 293/141/435 310/168/458 248/170/460 +f 293/141/435 248/170/460 309/166/456 +f 310/169/459 295/144/437 249/167/457 +f 310/169/459 249/167/457 248/171/461 +f 264/71/213 271/172/213 265/70/379 +f 269/113/213 280/112/405 270/173/213 +f 252/174/462 251/89/387 273/86/384 +f 252/174/462 273/86/384 280/175/463 +f 258/62/374 262/67/378 248/61/372 +f 309/176/464 248/61/372 262/67/378 +f 309/176/464 262/67/378 265/70/379 +f 271/172/213 270/173/213 280/112/405 +f 309/176/464 265/70/379 271/172/213 +f 309/176/464 271/172/213 280/112/405 +f 231/27/341 230/24/340 247/43/357 +f 231/27/341 247/43/357 242/41/352 +f 215/313/465 218/314/466 220/315/467 +f 215/313/465 220/315/467 10/315/468 +f 10/315/468 6/314/469 5/313/470 +f 10/315/468 5/313/470 72/316/471 +f 5/313/470 4/317/472 3/316/473 +f 5/313/470 3/316/473 72/316/471 +f 215/313/465 10/315/468 72/316/471 +f 215/313/465 72/316/471 214/317/474 +f 249/47/360 272/85/383 82/83/83 +f 249/47/360 82/83/83 51/46/51 +f 247/43/357 243/40/353 242/41/352 +f 311/177/475 312/180/476 313/179/477 +f 311/177/475 313/179/477 314/178/478 +f 315/181/479 316/184/480 317/183/481 +f 315/181/479 317/183/481 318/182/482 +f 315/185/483 313/188/484 312/187/485 +f 315/185/483 312/187/485 316/186/486 +f 318/189/487 317/192/488 311/191/489 +f 318/189/487 311/191/489 314/190/490 +f 319/193/491 320/194/492 314/178/478 +f 319/193/491 314/178/478 313/179/477 +f 321/197/493 322/196/494 323/195/495 +f 321/197/493 323/195/495 320/198/496 +f 324/200/497 321/199/498 320/194/492 +f 324/200/497 320/194/492 319/193/491 +f 323/201/499 322/204/500 325/203/501 +f 323/201/499 325/203/501 326/202/502 +f 325/207/503 324/206/504 319/205/505 +f 325/207/503 319/205/505 326/208/506 +f 327/209/212 328/212/212 329/211/212 +f 327/209/212 329/211/212 330/210/212 +f 327/213/213 330/214/213 321/199/498 +f 327/213/213 321/199/498 324/200/497 +f 322/204/500 329/216/78 328/215/78 +f 322/204/500 328/215/78 325/203/501 +f 321/197/493 330/210/215 329/211/507 +f 321/197/493 329/211/507 322/196/494 +f 327/209/508 324/206/504 325/207/503 +f 327/209/508 325/207/503 328/212/217 +f 326/202/502 315/181/479 318/182/482 +f 326/202/502 318/182/482 323/201/499 +f 318/189/487 314/190/490 320/198/496 +f 318/189/487 320/198/496 323/195/495 +f 315/185/483 326/208/506 319/205/505 +f 315/185/483 319/205/505 313/188/484 +f 331/219/509 332/218/510 333/217/511 +f 331/219/509 333/217/511 334/220/512 +f 335/223/224 336/222/223 332/221/222 +f 335/223/224 332/221/222 331/224/225 +f 337/227/513 338/226/514 336/225/515 +f 337/227/513 336/225/515 335/228/516 +f 332/218/510 339/230/517 340/229/518 +f 332/218/510 340/229/518 333/217/511 +f 336/222/223 341/232/233 339/231/232 +f 336/222/223 339/231/232 332/221/222 +f 338/226/514 342/234/519 341/233/520 +f 338/226/514 341/233/520 336/225/515 +f 339/230/517 343/236/521 344/235/522 +f 339/230/517 344/235/522 340/229/518 +f 341/232/233 345/238/238 343/237/238 +f 341/232/233 343/237/238 339/231/232 +f 342/234/519 346/240/523 345/239/524 +f 342/234/519 345/239/524 341/233/520 +f 343/236/521 347/242/525 348/241/526 +f 343/236/521 348/241/526 344/235/522 +f 345/245/245 349/244/244 347/243/243 +f 345/245/245 347/243/243 343/246/245 +f 345/239/524 346/240/523 350/248/527 +f 345/239/524 350/248/527 349/247/528 +f 347/242/525 351/250/529 352/249/530 +f 347/242/525 352/249/530 348/241/526 +f 349/244/244 353/252/251 351/251/250 +f 349/244/244 351/251/250 347/243/243 +f 350/248/527 354/254/531 353/253/532 +f 350/248/527 353/253/532 349/247/528 +f 351/250/529 331/219/509 334/220/512 +f 351/250/529 334/220/512 352/249/530 +f 353/252/251 335/223/224 331/224/225 +f 353/252/251 331/224/225 351/251/250 +f 354/254/531 337/227/513 335/228/516 +f 354/254/531 335/228/516 353/253/532 +f 333/217/213 355/255/213 334/220/213 +f 337/227/78 356/256/78 338/226/78 +f 340/229/213 355/255/213 333/217/213 +f 338/226/78 356/256/78 342/234/78 +f 344/235/213 355/255/213 340/229/213 +f 342/234/78 356/256/78 346/240/78 +f 348/241/213 355/255/213 344/235/213 +f 346/240/78 356/256/78 350/248/78 +f 352/249/213 355/255/213 348/241/213 +f 350/248/78 356/256/78 354/254/78 +f 334/220/213 355/255/213 352/249/213 +f 354/254/78 356/256/78 337/227/78 +f 357/257/533 358/260/534 359/259/535 +f 357/257/533 359/259/535 360/258/536 +f 361/261/537 358/260/534 357/257/533 +f 361/261/537 357/257/533 362/262/538 +f 363/263/539 364/266/540 365/265/541 +f 363/263/539 365/265/541 366/264/542 +f 366/264/542 365/265/541 361/268/543 +f 366/264/542 361/268/543 362/267/544 +f 367/269/545 368/272/546 369/271/547 +f 367/269/545 369/271/547 370/270/548 +f 367/273/549 370/274/271 359/259/535 +f 367/273/549 359/259/535 358/260/534 +f 364/266/540 369/271/550 368/272/551 +f 364/266/540 368/272/551 365/265/541 +f 365/265/541 368/272/551 371/275/552 +f 365/265/541 371/275/552 361/268/543 +f 368/272/546 367/269/545 371/275/553 +f 371/276/554 367/273/549 358/260/534 +f 371/276/554 358/260/534 361/261/537 +f 364/266/540 372/278/555 373/277/556 +f 364/266/540 373/277/556 369/271/550 +f 373/279/557 372/280/558 359/259/535 +f 373/279/557 359/259/535 370/274/271 +f 369/271/547 373/277/559 370/270/548 +f 374/281/560 360/258/536 359/259/535 +f 374/281/560 359/259/535 372/280/558 +f 364/266/540 363/263/539 374/282/561 +f 364/266/540 374/282/561 372/278/555 +f 360/283/562 374/285/563 363/284/564 +f 357/286/565 360/283/562 363/284/564 +f 357/286/565 363/284/564 366/287/566 +f 357/286/565 366/287/566 362/288/567 +f 375/289/568 376/292/569 377/291/570 +f 375/289/568 377/291/570 378/290/571 +f 379/293/572 375/289/573 378/290/574 +f 379/293/572 378/290/574 380/294/575 +f 381/295/298 382/298/298 383/297/576 +f 381/295/298 383/297/576 384/296/299 +f 379/293/572 380/294/575 383/297/577 +f 379/293/572 383/297/577 382/298/578 +f 381/299/579 384/300/580 377/291/570 +f 381/299/579 377/291/570 376/292/569 +f 383/297/576 385/302/581 386/301/582 +f 383/297/576 386/301/582 384/296/299 +f 383/297/577 380/294/575 387/303/583 +f 383/297/577 387/303/583 385/302/584 +f 388/304/585 377/291/570 384/300/580 +f 388/304/585 384/300/580 386/305/586 +f 378/290/571 377/291/570 388/304/585 +f 378/290/571 388/304/585 389/306/587 +f 380/294/575 378/290/574 389/306/588 +f 380/294/575 389/306/588 387/303/583 +f 379/307/313 382/298/578 381/295/315 +f 379/307/313 381/295/315 376/308/314 +f 376/308/314 375/309/316 379/307/313 +f 387/310/589 388/304/590 386/305/591 +f 387/310/589 386/305/591 385/311/592 +f 388/304/590 387/310/589 389/312/593 +f 38/107/113 97/106/112 94/102/107 +f 38/107/113 94/102/107 24/101/106 +f 229/101/399 91/99/103 275/102/396 +f 390/318/594 391/319/595 392/320/596 +f 390/318/594 392/320/596 393/321/597 +f 394/322/598 392/323/599 391/324/600 +f 394/322/598 391/324/600 395/325/601 +f 396/326/602 394/327/603 395/328/604 +f 396/326/602 395/328/604 397/329/605 +f 398/330/606 396/331/607 397/332/608 +f 398/330/606 397/332/608 399/333/609 +f 400/334/610 398/335/611 399/336/612 +f 400/334/610 399/336/612 401/337/613 +f 391/319/595 390/318/594 402/338/614 +f 391/319/595 402/338/614 403/339/615 +f 395/325/601 391/324/600 403/340/616 +f 395/325/601 403/340/616 404/341/617 +f 397/329/618 395/328/619 404/342/620 +f 397/329/618 404/342/620 405/343/621 +f 399/333/609 397/332/608 405/344/622 +f 399/333/609 405/344/622 406/345/623 +f 401/337/613 399/336/612 406/346/624 +f 401/337/613 406/346/624 407/347/625 +f 403/339/615 402/338/614 408/348/626 +f 403/339/615 408/348/626 409/349/627 +f 404/341/617 403/340/616 409/350/628 +f 404/341/617 409/350/628 410/351/629 +f 405/343/621 404/342/620 410/352/630 +f 405/343/621 410/352/630 411/353/631 +f 406/345/623 405/344/622 411/354/632 +f 406/345/623 411/354/632 412/355/633 +f 407/347/634 406/346/635 412/356/636 +f 407/347/634 412/356/636 413/357/637 +f 414/358/638 415/359/639 409/349/638 +f 414/358/638 409/349/638 408/348/640 +f 410/351/629 409/350/628 415/360/641 +f 410/351/629 415/360/641 416/361/642 +f 411/353/643 410/352/644 416/362/643 +f 411/353/643 416/362/643 417/363/645 +f 412/355/633 411/354/632 417/364/646 +f 412/355/633 417/364/646 418/365/647 +f 413/357/637 412/356/636 418/366/648 +f 413/357/637 418/366/648 419/367/649 +f 420/368/650 421/369/651 415/370/650 +f 420/368/650 415/370/650 414/371/652 +f 416/361/642 415/360/641 421/372/653 +f 416/361/642 421/372/653 422/373/654 +f 417/374/655 416/375/656 422/376/655 +f 417/374/655 422/376/655 423/377/657 +f 418/365/647 417/364/646 423/378/658 +f 418/365/647 423/378/658 424/379/659 +f 419/380/660 418/381/661 424/382/660 +f 419/380/660 424/382/660 425/383/662 +f 426/384/663 427/385/664 421/369/665 +f 426/384/663 421/369/665 420/368/666 +f 422/373/654 421/372/653 427/386/667 +f 422/373/654 427/386/667 428/387/668 +f 423/377/669 422/376/670 428/388/669 +f 423/377/669 428/388/669 429/389/671 +f 424/379/659 423/378/658 429/390/672 +f 424/379/659 429/390/672 430/391/673 +f 425/383/674 424/382/675 430/392/674 +f 425/383/674 430/392/674 431/393/676 +f 432/394/677 433/395/678 427/385/664 +f 432/394/677 427/385/664 426/384/663 +f 428/387/668 427/386/667 433/396/679 +f 428/387/668 433/396/679 434/397/680 +f 429/389/681 428/388/682 434/398/681 +f 429/389/681 434/398/681 435/399/683 +f 430/391/673 429/390/672 435/400/684 +f 430/391/673 435/400/684 436/401/685 +f 431/393/686 430/392/687 436/402/688 +f 431/393/686 436/402/688 437/403/689 +f 433/395/678 432/394/677 393/321/690 +f 433/395/678 393/321/690 392/320/691 +f 434/397/680 433/396/679 392/323/599 +f 434/397/680 392/323/599 394/322/598 +f 435/399/692 434/398/693 394/327/603 +f 435/399/692 394/327/603 396/326/602 +f 436/401/685 435/400/684 396/331/607 +f 436/401/685 396/331/607 398/330/606 +f 437/403/689 436/402/688 398/335/694 +f 437/403/689 398/335/694 400/334/695 +f 400/404/696 401/405/697 438/406/698 +f 401/405/697 407/407/699 438/406/698 +f 407/407/699 413/408/700 438/406/698 +f 413/408/700 419/409/701 438/406/698 +f 419/409/701 425/410/702 438/406/698 +f 425/410/702 431/411/699 438/406/698 +f 431/411/699 437/412/703 438/406/698 +f 437/412/703 400/404/696 438/406/698 +f 402/413/704 390/414/705 439/415/706 +f 408/416/707 402/413/704 439/415/706 +f 414/417/708 408/416/707 439/415/706 +f 420/418/709 414/417/708 439/415/706 +f 426/419/710 420/418/709 439/415/706 +f 432/420/711 426/419/710 439/415/706 +f 390/414/705 393/421/712 439/415/706 +f 393/421/712 432/420/711 439/415/706 +f 440/422/713 441/423/714 442/424/715 +f 440/422/713 442/424/715 443/425/716 +f 443/425/716 442/424/715 444/426/717 +f 443/425/716 444/426/717 445/427/718 +f 446/428/719 441/423/714 440/422/713 +f 446/428/719 440/422/713 447/429/718 +f 445/427/718 444/426/717 448/430/720 +f 445/427/718 448/430/720 449/431/720 +f 450/432/720 446/428/719 447/429/718 +f 450/432/720 447/429/718 451/433/720 +f 452/434/721 453/435/722 454/436/723 +f 452/434/721 454/436/723 455/437/724 +f 456/438/725 457/439/726 455/437/724 +f 456/438/725 455/437/724 454/436/723 +f 458/440/727 457/439/726 456/438/725 +f 458/440/727 456/438/725 459/441/722 +f 460/442/728 461/443/729 462/444/729 +f 460/442/728 462/444/729 463/445/728 +f 464/446/730 465/447/730 462/448/701 +f 464/446/730 462/448/701 461/449/701 +f 466/450/729 467/451/729 468/452/728 +f 466/450/729 468/452/728 469/453/728 +f 466/454/701 470/455/730 471/456/730 +f 466/454/701 471/456/730 467/457/701 +f 460/442/728 463/445/728 453/435/722 +f 460/442/728 453/435/722 452/434/721 +f 459/441/722 469/453/728 468/452/728 +f 459/441/722 468/452/728 458/440/727 +f 445/458/731 452/459/732 455/460/733 +f 445/458/731 455/460/733 443/461/734 +f 452/459/732 445/458/731 449/462/735 +f 452/459/732 449/462/735 460/463/735 +f 461/464/213 460/463/735 449/462/735 +f 461/464/213 449/462/735 464/465/213 +f 448/466/736 444/467/737 453/468/738 +f 448/466/736 453/468/738 463/469/736 +f 448/466/736 463/469/736 462/470/78 +f 448/466/736 462/470/78 465/471/78 +f 454/472/739 453/468/738 444/467/737 +f 454/472/739 444/467/737 442/473/740 +f 464/446/730 449/431/720 448/430/720 +f 464/446/730 448/430/720 465/447/730 +f 459/474/741 446/475/742 450/476/743 +f 459/474/741 450/476/743 469/477/743 +f 451/478/744 447/479/745 458/480/746 +f 451/478/744 458/480/746 468/481/744 +f 450/432/720 451/433/720 471/456/730 +f 450/432/720 471/456/730 470/455/730 +f 466/482/213 469/477/743 450/476/743 +f 466/482/213 450/476/743 470/483/213 +f 451/478/744 468/481/744 467/484/78 +f 451/478/744 467/484/78 471/485/78 +f 446/475/742 459/474/741 456/486/747 +f 446/475/742 456/486/747 441/487/748 +f 457/488/749 458/480/746 447/479/745 +f 457/488/749 447/479/745 440/489/750 +f 440/489/751 443/461/751 455/460/751 +f 440/489/751 455/460/751 457/488/751 +f 456/486/752 454/472/752 442/473/752 +f 456/486/752 442/473/752 441/487/752 +f 472/490/753 473/491/754 474/492/755 +f 472/490/753 474/492/755 475/493/756 +f 476/494/757 477/495/758 473/496/759 +f 476/494/757 473/496/759 472/497/760 +f 478/498/761 479/499/762 477/500/763 +f 478/498/761 477/500/763 476/501/764 +f 473/491/754 480/502/765 481/503/766 +f 473/491/754 481/503/766 474/492/755 +f 477/495/758 482/504/767 480/505/768 +f 477/495/758 480/505/768 473/496/759 +f 479/499/762 483/506/769 482/507/770 +f 479/499/762 482/507/770 477/500/763 +f 480/502/765 484/508/771 485/509/772 +f 480/502/765 485/509/772 481/503/766 +f 482/504/767 486/510/773 484/511/773 +f 482/504/767 484/511/773 480/505/768 +f 483/506/769 487/512/774 486/513/775 +f 483/506/769 486/513/775 482/507/770 +f 484/508/771 488/514/776 489/515/777 +f 484/508/771 489/515/777 485/509/772 +f 486/516/778 490/517/779 488/518/780 +f 486/516/778 488/518/780 484/519/778 +f 487/512/774 491/520/781 490/521/782 +f 487/512/774 490/521/782 486/513/775 +f 488/514/776 492/522/783 493/523/784 +f 488/514/776 493/523/784 489/515/777 +f 490/517/779 494/524/785 492/525/786 +f 490/517/779 492/525/786 488/518/780 +f 491/520/781 495/526/787 494/527/788 +f 491/520/781 494/527/788 490/521/782 +f 492/522/783 472/490/753 475/493/756 +f 492/522/783 475/493/756 493/523/784 +f 494/524/785 476/494/757 472/497/760 +f 494/524/785 472/497/760 492/525/786 +f 495/526/787 478/498/761 476/501/764 +f 495/526/787 476/501/764 494/527/788 +f 475/493/213 474/492/213 496/528/213 +f 478/498/78 497/529/78 479/499/78 +f 474/492/213 481/503/213 496/528/213 +f 479/499/78 497/529/78 483/506/78 +f 481/503/213 485/509/213 496/528/213 +f 483/506/78 497/529/78 487/512/78 +f 485/509/213 489/515/213 496/528/213 +f 487/512/78 497/529/78 491/520/78 +f 489/515/213 493/523/213 496/528/213 +f 491/520/78 497/529/78 495/526/78 +f 493/523/213 475/493/213 496/528/213 +f 495/526/78 497/529/78 478/498/78 +f 498/530/789 499/531/790 500/532/791 +f 498/530/789 500/532/791 501/533/792 +f 499/534/793 502/535/794 503/536/795 +f 499/534/793 503/536/795 500/537/796 +f 502/535/794 504/538/797 505/539/798 +f 502/535/794 505/539/798 503/536/795 +f 504/540/799 506/541/800 507/542/801 +f 504/540/799 507/542/801 505/543/802 +f 508/544/803 507/542/801 506/541/800 +f 508/544/803 506/541/800 509/545/804 +f 510/546/805 508/547/806 509/548/807 +f 510/546/805 509/548/807 511/549/808 +f 512/550/809 510/546/805 511/549/808 +f 512/550/809 511/549/808 513/551/810 +f 512/552/811 513/553/812 498/530/789 +f 512/552/811 498/530/789 501/533/792 +f 499/531/790 498/530/789 514/554/813 +f 499/531/790 514/554/813 515/555/814 +f 502/535/794 499/534/793 515/556/815 +f 502/535/794 515/556/815 516/557/816 +f 504/538/797 502/535/794 516/557/816 +f 504/538/797 516/557/816 517/558/817 +f 506/541/800 504/540/799 517/559/818 +f 506/541/800 517/559/818 518/560/819 +f 518/560/819 519/561/820 509/545/804 +f 518/560/819 509/545/804 506/541/800 +f 519/562/821 520/563/822 511/549/808 +f 519/562/821 511/549/808 509/548/807 +f 520/563/822 521/564/823 513/551/810 +f 520/563/822 513/551/810 511/549/808 +f 498/530/789 513/553/812 521/565/824 +f 498/530/789 521/565/824 514/554/813 +f 501/533/792 500/532/791 522/566/825 +f 500/537/796 503/536/795 522/567/826 +f 503/536/795 505/539/798 522/567/826 +f 505/543/802 507/542/801 522/568/827 +f 507/542/801 508/544/803 522/568/827 +f 508/547/806 510/546/805 522/569/828 +f 510/546/805 512/550/809 522/569/828 +f 512/552/811 501/533/792 522/566/825 +f 523/570/829 524/571/830 525/572/831 +f 523/570/829 525/572/831 526/573/832 +f 527/574/833 525/572/831 524/571/830 +f 527/574/833 524/571/830 528/575/834 +f 529/576/835 527/577/836 528/578/837 +f 529/576/835 528/578/837 530/579/838 +f 531/580/839 529/576/835 530/579/838 +f 531/580/839 530/579/838 532/581/840 +f 533/582/841 531/580/839 532/581/840 +f 533/582/841 532/581/840 534/583/842 +f 534/584/843 523/570/829 526/573/832 +f 534/584/843 526/573/832 533/585/844 +f 526/573/832 525/572/831 535/586/845 +f 526/573/832 535/586/845 536/587/846 +f 525/572/831 527/574/833 537/588/847 +f 525/572/831 537/588/847 535/586/845 +f 527/577/836 529/576/835 538/589/848 +f 527/577/836 538/589/848 537/590/849 +f 539/591/850 538/589/848 529/576/835 +f 539/591/850 529/576/835 531/580/839 +f 531/580/839 533/582/841 540/592/851 +f 531/580/839 540/592/851 539/591/850 +f 533/585/844 526/573/832 536/587/846 +f 533/585/844 536/587/846 540/593/852 +f 541/594/853 542/595/854 536/587/846 +f 541/594/853 536/587/846 535/586/845 +f 535/586/845 537/588/847 543/596/855 +f 535/586/845 543/596/855 541/594/853 +f 544/597/856 543/598/857 537/590/849 +f 544/597/856 537/590/849 538/589/848 +f 538/589/848 539/591/850 545/599/858 +f 538/589/848 545/599/858 544/597/856 +f 546/600/859 545/599/858 539/591/850 +f 546/600/859 539/591/850 540/592/851 +f 540/593/852 536/587/846 542/595/854 +f 540/593/852 542/595/854 546/601/860 +f 532/581/861 530/579/862 524/602/863 +f 532/581/861 524/602/863 523/603/864 +f 534/604/865 532/581/861 523/603/864 +f 528/605/866 524/602/863 530/579/862 +f 547/606/867 548/607/868 549/608/869 +f 547/606/867 549/608/869 550/609/870 +f 551/610/871 549/608/869 548/607/868 +f 551/610/871 548/607/868 552/611/872 +f 553/612/873 551/613/874 552/614/875 +f 553/612/873 552/614/875 554/615/876 +f 555/616/877 553/612/873 554/615/876 +f 555/616/877 554/615/876 556/617/878 +f 557/618/879 555/616/877 556/617/878 +f 557/618/879 556/617/878 558/619/880 +f 558/620/881 547/606/867 550/609/870 +f 558/620/881 550/609/870 557/621/882 +f 550/609/870 549/608/869 559/622/883 +f 550/609/870 559/622/883 560/623/884 +f 549/608/869 551/610/871 561/624/885 +f 549/608/869 561/624/885 559/622/883 +f 551/613/874 553/612/873 562/625/886 +f 551/613/874 562/625/886 561/626/887 +f 563/627/888 562/625/886 553/612/873 +f 563/627/888 553/612/873 555/616/877 +f 555/616/877 557/618/879 564/628/889 +f 555/616/877 564/628/889 563/627/888 +f 557/621/882 550/609/870 560/623/884 +f 557/621/882 560/623/884 564/629/890 +f 565/630/891 566/631/892 560/623/884 +f 565/630/891 560/623/884 559/622/883 +f 559/622/883 561/624/885 567/632/893 +f 559/622/883 567/632/893 565/630/891 +f 568/633/894 567/634/895 561/626/887 +f 568/633/894 561/626/887 562/625/886 +f 562/625/886 563/627/888 569/635/896 +f 562/625/886 569/635/896 568/633/894 +f 570/636/897 569/635/896 563/627/888 +f 570/636/897 563/627/888 564/628/889 +f 564/629/890 560/623/884 566/631/892 +f 564/629/890 566/631/892 570/637/898 +f 556/617/899 554/615/900 548/638/901 +f 556/617/899 548/638/901 547/639/902 +f 558/640/903 556/617/899 547/639/902 +f 552/641/904 548/638/901 554/615/900 +f 515/642/905 514/643/906 571/644/907 +f 516/645/908 515/642/905 571/644/907 +f 517/646/909 516/645/908 571/644/907 +f 518/647/910 517/646/909 571/644/907 +f 519/648/911 518/647/910 571/644/907 +f 520/649/912 519/648/911 571/644/907 +f 521/650/913 520/649/912 571/644/907 +f 514/643/906 521/650/913 571/644/907 +f 572/651/914 573/652/915 574/653/916 +f 572/651/914 574/653/916 575/654/917 +f 576/655/918 577/656/919 578/657/920 +f 576/655/918 578/657/920 579/658/921 +f 573/652/915 576/655/918 579/658/921 +f 573/652/915 579/658/921 574/653/916 +f 580/659/922 581/660/923 582/661/924 +f 580/659/922 582/661/924 577/656/919 +f 578/657/920 577/656/919 582/661/924 +f 573/652/915 572/651/914 583/662/925 +f 576/655/918 584/663/926 580/659/922 +f 576/655/918 580/659/922 577/656/919 +f 573/652/915 583/662/925 585/664/927 +f 573/652/915 585/664/927 586/665/928 +f 576/655/918 573/652/915 586/665/928 +f 576/655/918 586/665/928 584/663/926 +f 587/666/929 575/667/930 574/668/931 +f 587/666/929 574/668/931 588/669/932 +f 589/670/933 579/671/934 578/672/935 +f 589/670/933 578/672/935 590/673/936 +f 588/669/932 574/668/931 579/671/934 +f 588/669/932 579/671/934 589/670/933 +f 591/674/937 590/673/936 582/675/938 +f 591/674/937 582/675/938 581/676/939 +f 578/672/935 582/675/938 590/673/936 +f 588/669/932 592/677/940 587/666/929 +f 591/674/937 593/678/941 589/670/933 +f 591/674/937 589/670/933 590/673/936 +f 594/679/942 592/677/940 588/669/932 +f 594/679/942 588/669/932 595/680/943 +f 595/680/943 588/669/932 589/670/933 +f 595/680/943 589/670/933 593/678/941 +f 572/681/944 587/682/945 592/683/946 +f 572/681/944 592/683/946 583/684/947 +f 575/685/948 587/682/945 572/681/944 +f 594/686/949 585/687/949 583/684/947 +f 594/686/949 583/684/947 592/683/946 +f 585/664/950 594/688/950 595/689/951 +f 585/664/950 595/689/951 586/665/952 +f 584/663/953 593/690/954 591/691/955 +f 584/663/953 591/691/955 580/659/956 +f 586/665/952 595/689/951 593/690/954 +f 586/665/952 593/690/954 584/663/953 +f 591/691/955 581/692/957 580/659/956 +# 0 polygons - 1114 triangles + diff --git a/html/scan.js b/html/scan.js index 89ae10e..472b087 100644 --- a/html/scan.js +++ b/html/scan.js @@ -7,13 +7,18 @@ function _(el) { function get_mode() { var json_url = 'mode.json'; + // putting this on the HTML causes rendering issues! + if (_('rtctab')) _('rtctab').style.display = 'none'; + if (_('httab')) _('httab').style.display = 'none'; + xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { var data = JSON.parse(this.responseText); if (data.mode==="STA") { _('stamode').style.display = 'block'; - if (_('rtctab')) _('rtctab').style.display = 'block'; + if (_('rtctab')) _('rtctab').removeAttribute('style'); + if (_('httab')) _('httab').removeAttribute('style'); _('ssid').textContent = data.ssid; } else { _('apmode').style.display = 'block'; @@ -28,7 +33,7 @@ function get_mode() { mui.tabs.activate('pane-justified-2'); _('tx_tab').style.display = 'none'; } - if(data['product-name']) _('product-name').textContent = data['product-name']; + if(data['product-name'] && _('product-name')) _('product-name').textContent = data['product-name']; } }; xmlhttp.open("POST", json_url, true); @@ -459,3 +464,100 @@ function cuteAlert({ alertFrame.addEventListener("click", stopProp); }); } + +//========================================================= + +if (_('httab')) _('httab').addEventListener('mui.tabs.showstart', start); + +var websock; +var Euler = {heading: 0.0, pitch: 0.0, roll: 0.0}; + +const loadScript = (FILE_URL, async = true, type = "text/javascript") => { + return new Promise((resolve, reject) => { + try { + const scriptEle = document.createElement("script"); + scriptEle.type = type; + scriptEle.async = async; + scriptEle.src = FILE_URL; + + scriptEle.addEventListener("load", (ev) => { + resolve({ status: true }); + }); + + scriptEle.addEventListener("error", (ev) => { + reject({ + status: false, + message: `Failed to load the script ${FILE_URL}` + }); + }); + + document.body.appendChild(scriptEle); + } catch (error) { + reject(error); + } + }); +}; + +let ht_loaded = false; +function start() { + if (!ht_loaded) { + loadScript('https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.12/p5.min.js').then(()=>{ + ht_loaded = true; + websock = new WebSocket('ws://' + window.location.hostname + '/ws'); + // websock.onopen = function(evt) { + // console.log('websock open'); + // var e = document.getElementById('webSockStatus'); + // e.style.backgroundColor = 'green'; + // }; + // websock.onclose = function(evt) { + // console.log('websock close'); + // var e = document.getElementById('webSockStatus'); + // e.style.backgroundColor = 'red'; + // }; + websock.onerror = function(evt) { console.log(evt); }; + websock.onmessage = function(evt) { + Euler = JSON.parse(evt.data); + }; + }) + } +} + +let plane; +let tex; + +function preload() { + plane = loadModel('airplane.obj', true); + tex = loadImage('texture.gif'); +} + +function setup() { + var canvas = createCanvas(500, 500, WEBGL); + canvas.parent('canvas-holder'); +} + +function draw() { + background(192); + + rotateY(radians(Euler.heading+180)); + rotateX(radians(Euler.pitch)); + rotateZ(radians(Euler.roll)); + + push(); + stroke('#CCC'); + strokeWeight(0.5); + for (let x=-width/2; x <= width/2; x +=20) { + line(x, 0, -height/2, x, 0, height/2); + } + for (let z=-height/2; z <= height/2; z +=20) { + line(-width/2, 0, z, width/2, 0, z); + } + pop(); + + push(); + noStroke(); + scale(2); + translate(0,-26,0); + texture(tex); + model(plane); + pop(); +} diff --git a/html/texture.gif b/html/texture.gif new file mode 100644 index 0000000000000000000000000000000000000000..946f55de2db649aaa9cadbe4cefb07c0b2c5fc91 GIT binary patch literal 31218 zcmW(*cOcaN|9{`MomIB8x9;ri?2&O+S?TN@Dl+Rh95Sw`kYtC*h(glYTW2JhXLdAD zl1e_mzQ5<6uRmU|=j-))K3~tr>+yJ+TbgNVc>y2*^zjt@t1l!h^jAlUdBIiO5PPC*s~9I3_5!N$zV$A#qP<^& zF)OAh$|=bxD9I_v$tejUl@j?BBV-iJHPBM>7>9EzYO2br7*&kC>ScK~cQI{qHBGdF zMxeH)j)t~|y0)&AW}dLlqf!w$WxXqM`bjGKzUK_0WK5m(47H35jP(rlbq$Tx46mx1 z#OoO+>X_cJG_%q)ziDb=YGPq!VQHvtnWkq!(y>Z5w8+u2&d@oZt8H`J($37#`nI*5 zwT@kukuAv@=b(DQ-`>ta-#*veo?zl|+rh!T+Q8K5qKnG~H~b|pkIQ%`&s@X6xC&cG zZ~uT$pP)d$Aisd<;9&2duyCuW4FAyF@X(0RDS}yqTua#1 zn+b&2n}my`f|#3+E@qUv<RuwWqE1kqw>nvg^x8kvtL-1^pFMrr*wXy0rLnPNsPl8 z*<0VB)4zS9f7$-@^>AnZ%jchapAYu-et+9L{QCXJ_iulHeEa$5;ODoaUq62Qcl_h` z&!hkT`*Cvo^S|FePfq{*cl`IizyF>5_x~pP3kqTa{sR>LmkIyN34q}g05J&4paFuo z+rMEl0Rl}%idz+1G?x$D=2Zz^8g8x_%0(Gg1N(J+1TJQnEyGAl)z|~H&uFo$J%JMW z=a7LVPZmiasS=rltgeGnVX_%=dLZn(?!;jrMfM%T{mFY)&#yS)u%8CN;NDE3^U?KF zl}xYUiewPwMdHzXPv9XVrP3N$s9TnwqP=B7DPvuz_s)$;-cpzVVauAfzLfdsEos&Ar7EE`Y}!bKxL~^eZSjiEaPXa(RJ3nq-dmH zgb@>p@C0wZ@%PSHhD>?-`AoT%C&J;czeJ~F0~L>*-52&rY+B33O)OeFT@|)k*#NaP z+bGJ}P-6@wZlk_PaJ1F_TUNoQIn52ZEs>Blw?;$(_Sxxdx;)Q1 zGeA(W)jb#{o@L?jZ2~b+=L$m1@9;U~RLsZte6FNd&g9(seE2g*r@%khPG=3b5hRtE z{@bs%R#tOGG_jEKh ze}BIckbPs%GWG2Bvn)2XFIQ=^}0Xk-}CkN$VArD@j{mMuUKI)cJFoInU(d}FB42pB+tkEjY?*; z`j#P8WZ_MhKWKuy)u;QSEu`-HPN$3J%_mPN0r(%srsA*-8_aCzcN@*Xc=y_2$(M>h z`>wDTh3X#8SL80s4-swEhx*5dutcsa;je?i4aW};wYs%U@3P3f)nCBQn#(d+flGJa zJND>PRf;?pzq)iyTAux6|9lv`Ue9WHuHLufXUYbD0GdvxedTt?Xnq>y+#i8WS3L*x{B zEC0X;6>BlvStbsal3M~f&Pw&S4RNuz3Uf;**I3Sf5x9|zy~cmXG!%;B7y_^`kGp2z zdW^Uibi|lW%?UKA5Lhj^JETf7j~Vt6-d8zW4YeE=nyU~oP^d)5YNjjmT`|>Pd>_BE zkzt_FpOM#Z0peH9zB%hB0nswL1E4aLs?#Lfcd;yk9Yis1QAQgJFwa~p>|FbICYyOf z1XP)j+iJ>ra2mm>Fovm(nA#WmUhq%}wlic6Y9{CNRgZEe!6aJhJgu@yyy2WZjXV@o z-AIiY2#+-J6BCdMHIc>IXWCihoSmyN-Blf(&T{LQVof*X>MDh+Q$TE?!?J?A5i=#* z`K(tcwfr?`J8(>j>|-)1uRKLPs=Y5U#>92+=t0XLS*J3_h9Pdokz8xl!!o&K;rN(q z>4WbOMN{v?VT-8&k>01M_W*-uf_O=2831z!!pL3l$6 zooWT?-~@t)1p0l)xStX?nE>TR&vj)j6P_cf1eN-==f&BL2`bEP=QIhe)i4TXA&G_Y zAkV5n=)t1uyn*l5xu++?a_l_Ti;GM&Br*q`k|d8#FP1b^oInpGG%YEHma+K6^zjbT zygu^xFbvxqMBbuY+Nsv;up9189|G3-a+8?dYe0ySfqCIQUcf1W$TthD{^I9*9)RoP zxzctA?o}Z;L{HO{+G-%1-n&U;5?=a7!olCaAivzAW5tjh6dta>m^}h>+XYrb2-;bNXOF>lf7J7G;hoQN)fYPl3rv}Jljo+fCv-!=%OK#_xhz&?_^}!*>XJ|ZXYz}$jo|J&W#eOl{&?i9b{b-xD4PlN2f7+?-S5_xH1yY@4S%l;O|ck=+i(NjV>p~hUj6!XgEa_r5c z^MPotl8~GG>mSK8Mq+|%>D1)}uJrKOF-zfZj>Te|sEeta(sr@VmF3)Zfdd<&Z(_-h zlPe`}#12MwGZqG{b5QB#n#k*w@ANLP>{ZGpOyQ5jpBl0T48P`&rXJmnxh^uD4*%ls zI(GGo!=C51PS{_z<7e2i{TsEK>lz0cITzn-RnfaXoRuM9H6w^czgo$eVdTnM^EFnj%u#aLi3OW|Nwf^A^me$4pJg zRP(^tS}Ifa4JN@Skvtc*?i%5-|)g#bS`Qh8l+b=b`ViCXchaOe}`Kv3(*40;QmfXxDTL?#Sy?v3}#jURDXxp};#n!aY&& z5OVwjKi}`w@bm)TQ2ga%9*%!6@Cu53Y@hY%7MqHMhR){s5e=f6rG=UW@-D#sD(}9! zsyny_K1zqw^Vj$u=(e)YP`O`FEF-*}uc{P9 zin|-+<0!sGt9amFOyGY3ukKK5m=bD8Iqj?et=_}q9#A9GWerX^rDu%t&07KPy~>rX=Kw* z35}SSI?cb(NltjfS>qS6&ap!Vup^xV!r5NQ;uMp-Td~D#ZpB|SaKq*7WfEY342_70 zw-18nNj?f7!`sbs#I(zk3w&Q9%CAtOAq4rJX7Oo&ZHK@%5P*-rn5*}zb zcRqNXukeQ~DBcXtb9VGcK3bN2^zvtZ#(efB9geMvzFSq^uFN@vW1FTr#YkxUp!iku zm7ONk;!a8(h5>eE)gn;8*4lmznZNcfQ|)>a>mpW^+q-t5l(lHI_Tg{V=?E59t-4CL zx(a@lE<#=FF!O6l-Ru3j9vriEGe0J%IEBBuy(q7jn|*?h$66FPs4nxw#xH9qgS@xuiwF||_C<^eMIm72C za!yN;$>C{dT%LB&(~jM$S4=!Heoi*zvf$A&WB>92H%F=6vLNh}AUDUChz7_Mj~$YA z5s>)0p$;QGY=6;U)LD8fC~YX6WtPmSuYn}M0txp)e89yC2M`K?oC6>Vi>ED0e_9mA zqV&145$=&W^m5|MDZu zfCn+i-*hp)&BWC)eK>t8E~_43+oXv+t~CG^MR1I=)2WcH4Zd@Cl3DqyBcHai$Q9Up zdio;;0;h|#zPkSV;tt~#@ z?_ztq{@Sc#JeWkZc2LSm_A=Npxu5wIRB@!9qUz= zJyghxS%a{VE|)fg{uy>tes&4~JZ@6%gJyH;_Ds4^nBo45wAW}5bKUvw`iDLQ$N;v<-N5fUC3V!gk*I=uf_^M4lt0}zGw{n2HimWAJtf3}%zcNVUq?E###&Z4WxMF+1K~f=nSWR z?spq!@0LUU9&LO%R1jwMBz&R%K)&$qk6ypfk&x2zFhkaRkqJMJxtY4yzY*Bxa4fYn z21{!smy;Y4XPMbGY*X*-U_BrVVqOm!yZUtC57ltq$qp0IlrdJ8;y#GIH*y8}<{+dV z9v>h2UhqXJdu4IRg-+$q`>az)m3V>;_d6kJGH6Y-!Rn-{yLf=4J0PH2d{8vmDA4-Y+^-ftKKcd#i!GtkU`rZI0lbgEbTgsp;{4HhxVEB z_M4#?(6X1D0LCg2$^P>9R93w*|D)lj?k}xF$C%pK#aTg)p-#qHkGxI>@p*;olh{J2)$kG zWM3hlUAe?%ec^pk_WKv_40!;yzojH``|$z(`S*{e6+Ijy$xv3pLVOYt0NDEYYI^gQ z@Y>e&Qypa3gW5FtLc$gUKw;1{>XEQoSJ7{q}Ch8=)9j zR{A=$soZ1=Ri2R0E8dur(9G-ZzF*3&*1f`M*rm14HW&le^IX|u@n`j5_G@QX)Z%os zd8eAXc|NDDu-LIP0DfwNc7tNf9BsESQ)i}7xw6JH&PDXho0Xce8rqi5CsfwmFgwyR zJL?IUyL(vpQgK{u208(9QjK2BNWpTWHvA`P(J7@*}{P8hKi7txa9HX#r zfw7gAJzXnV$*vVAKFZ(lta)EK#J!mRqIS-bx7~A=4#u~CPq7Q6yaXa(LYj}}a(0u$ zM%Eb+LFD%RR){?9<1JycqbUyS9_B+D+dS@C5se`M$?#2o@6dDa=;q#W$==~4;zXE1 zTXdTf!#P739qap?7_{~y&iN0OGDKxxoH3OJ+Ct=_zgDg;=&xVNOmdol6Kmk!$gdaz z=mjfXzUVZZaBY5z#@d6oM@{jdQ7eCNOaOi#d-|DbnuF*gLm{N^fKmnm+}`}~SpDJoCLlZtv^m2OWa6y=`VDAGI3w=CkR3bx%@hD4S*s6 z45NUW%_IF(e&=1MI-89qFaD3RMymc@MOxpT>*t>ZjEl|IZI>3Qcs^5&oIemt0`Z++ zQHr=@LWT49mlb@oQtaeRE?py*|^I}U}sTKTv|hs>Xm8Q*J+6nCYBqP+n3O5 zU=k}5?%Q5^cGH6Qi9@X2kH53WPE~`C4eSuu)FFBIg?PT_!rR5)cP`GIa8aHl9vC5H z@BpB_-#`IRP!$bfXMLM`pd8$N*wtdAVOHjcy`x6zDUJ|P_37YUqbCH&r>707zK|R> zw&2@kyn+BlDVGUZ3NR?m1Fv8fou%L}n*6nJ994x~PyDv7DH{-NJ@R@&wQ#)qPjzLu z{$s*unLa`SkQiYb_u z>UhBwrhC}W#jf|GAf;aNW@&z|)ukIPZ$~GzL;8jP%UV|+RF$_76$jn-d*K@J_>#I! z(33|Tm5j<1TTm{P;GJut`QtjT?|oN)kE^*_{tp~DlZ+~0bvi*3&h9Ywx&1D8$vKI) zq32rME4nPDWN$P~m;5^VKoao;=7~bFr+M6fwiRRbnG06GBsT1Fka#lF{A~-@K=ZU0 zN}yfk!VkAEcoqVk@Q4q7_g$q>;t20~QAW8Mdbz!xz-i|-SlPoF3rcoQsW{nE-@fVr zo|@`k=wFoKU+9x23or0~G04y7o5Q%xW7#_&NMw#q5M6beWjn{?Jm;VK&c)H5b3*Fx zz`d5=4Bg|x@rQ;nGS|Bqht6btDEnxj+E$wOz;Ki7!>%v&5dtWaBf)@iu&9q^umR!{ z7IAl9Y(R~MO&s8H_7FGKv{e!B;6bfJmhhQ(wE2UIlBx?MUrRmkSQ>%dENsX%7>se( z|6pte+|-2Kf6?a?|HauQXz^9&pEk9saV}vC<4{nmO{1wD`tI#RzB`;sR*gAjZXHkiqZCU1*<%W0wvg)@hs-LnWF1!N_ zX;6}KVgOqarrYUibg$ei3XC>!*g-~abP}w931*hga5e`7;14&15&;SftT{+7W&xZm zEG|w5!ePSu8gE?tga9f-unUp@M&+PtO{5|7HxB+=#cq3b>TT?9UwFkm%=Q%0v!n@l zo+g5X!ndl-PnY(>&@8!k1KT)>f8{S~&5p2vNKG-N;_1fMhd6#=xZM?5fPwvoY5*k( z3Sim#<_SJ#-ypayZ_czpI79d8ln6TWPS((pg#$oMsTQObUQzAi@5jCg!b1c;45tY- zl$q-uHRpD|qKU+7sMUFqiG)giwfjlQ{Pl`=@3sm{J$aDn`eQ7-WL7{X{BBcR6H#zS z+*~KU#VRqd`W}ausqQ+46$mZ8f0b?xQU;Q`Oni}5nrUlkvbo-3GZg?XGonPxp{3Z1 z)d?VQR5r8TzhW%#p?_QGrB=57GV^CU*H@}*0CZ^s(I4kHSAzL7#e%v#KzIj+sY0qrA zHEO2PBnsznP1A5&IAslM<$m~~y+T-8W?uJee)URg>DPTt*03%xU>>unZaB4BT#vC}0hG@hR>fpp4Ao6p2hq)6sPluOUdwnk^4VWxWERvOuX3{z7vdltYmO#;_C++W z( zY!`Pj6+8%`1cD0);MT2UUd9^6E2Sr(59Cj3`7+<-oAfUO`*Ur{_~#b&r`KLVuC>G!?w0XR-VGCS8IS(sAuMDIC=@-kx#n1p{2*H)9a7 zHKZ6CqSOM!W|8EPnGP@{q0ok0+{tIigmhdI_b{L;3c>R}z}HB-6Xh>4zkwPNHJfy+ z_cJeZk+V=;)`5uYXrt>E2#ExPV=hvpj5_Ce^-wariCO3G`BM>p0a7Tf;XkoP^V^kf zO{o^;Fo*)yr8MS=>6|5G5S|JW*(BBp3VgT z`V0**3_gM!SHB4a!~H;zoYF^}l0C79t-z3K2s zN}3-IhQsr_(^LIXi0E|ERhP7j=#+FCF&u}8*6erH?tBR*@Sru#Jcu6W+n>)T9YP3h zSChlBh$tNCypvJ7I4Rzv*A_}rs7#B%zQCOpKPPY_qUKYF_Cd~sN;h6(WnT-oQrIOK z3bc#UeOxpiAVn>lB|CLg>G|MnS&@=)^ zvOS+cT+&PRHAOpFG%8S>>tPuZnQo!j^Up2h3j4hGUp;(EvW$VW32#M4RHci^EdaI|u=NCb;7CmD`}Jg1H{W7}1)5rZ=Cr_;M)=w?xxR&lz$ zSFwoWKi27vn7iX|Yy}Yw#P(A&UR@=q{C^AF<@ zF)@hvh!iv(9)lij_yZ4fDRH8uF7lDe(jVV%O=B%3dXrH>^AII0OADiUaF3dD{CFdl zyIGT{8kp=h!(iB&dV@Yl<{pWewVpg27k6rYsxvW@(oRtL7vv9r)h5w7`WjPgnw=~b zLeDMgj#byS?t@5)w28zTto!~1O9F|Xndrz14+cnA>F^L4n56~OI40Gz2_CCieq%q) zM+U}IPQ<6{Y14^WwSsIe1TZ!;v*^sLcJMy)TeH@#MWn2Ti+M$}^}oU{VuXkeBwc41 zi6#$Kejfi}c-k9245t#`q`w-9w?>4b8DlZ27c5dQl%`&sw<0CjkXYJ2Sa&Sc4-BKa zVH2<3av~6~**D~2|J-5`MCi|^>qVsK)4-})_tMeH*g&YlI)e-^L^+TJV1S~(r86@; zdqvO{-pXFaB1h%9JFU{I1KGr21jkmBXwv2?uzoUe`jEyh55!CL}4eU zU#ZAlpXYvF5tNQv2}<(>C|{Qts(`oFQOE>IS+UY!khzBH5fj)nxwrYIN}x%@iL^| zOD~0rIt)Wex;!#>bU@ZSL~{a^LQb(Hrt^GRjkauE=@K`*lG0INb)a5$bS?bcXgthx*mq`>u3R?TA zHQBvG4d#eqpR7wQOP07aQSX|c_Leg7XS6JljEIzZ+%K7F%S^0|Lqyk*oNxv?!vyaA zB=HEagay<+!oF_p5lt(N(=CnFD9s!DU=vfaVVLGcPMIv@AfukGP+6Z_AQQ96OX+0z zidr*0`H?llt!pL!kcE3XX+`p#L)3IM0pY0TRiSG1+RdTL8ga^jP^hzKzvdZmk|KJ? zbC2pi3D%#qs+01nx=DtgqFhVRigKV;t!%CJ#edvWQpE8oK6o5jA`c+5F1oGjKWS*= z2^@zmuwssr;d9yNJjqQi$eTYhjqIO$UL)3iXXyQt!X8c1w7u*o|-^z-X(r*4< z7_H40K28$HCOhaS3t~Zl;eDoH{d%4Fi&Kfb7&i_bNrFKbr&bLe~Nobil|agk8%jSck@+hs8vPv-`* zY=#SP=uKn&R0rz2>}*f-7f+7wz>VAG5`PQtpAKy1OEO(Y%il{U4N8*64U#KVG1@S1F~M zT^Tw8R*y5%^wIY62MUEXeG$Bu$LJYUnj*diAvT?c`wJ4^2XWKDS}3A47A%7gVZ#T> zpp&`U0q&*{abBV#PV{vtb2gbE$CXU6ZG%}5L@-4qCxK*tvbR5yA#K@4==NoSPChU8 zuw&Yd^q}C$nUydHhbU469e#Slg4E3F8Qt{SQTJ)tWQrZ{rX6~hzE!`N8TRCvq+wQC zZ(@Nz1LQyKh2&b}i1}clb%eo%GJF&1dJG~>&QGK!DNGG&j)rQb`@JyZD*6lJmhqFI zCASF^rK$G;P(SCAMxvwct&naq$V85+HsBwA#}f%P%XryV*wJY=(su|WJ6$-*s@WkZG90I=(9sq}mMi+3YpUWD!c$n`%4T~E)rJF!c9 z!9uBbR&cWuYk91QO%{pyszLprf9xnR50+_~SHyqPI8G9{1vZX>DDe^n@k7Xnw*Ey8 zm+u0qRt(IW!QIIA74F3#&cy5OP6Tb5gP#$GfMA6b=baBg}Bc?cYIEhNV*z< zxO2)JrB@2kWq^v)L0Swcc65jgHj=wE^55;b8G((dDW+Sz0G@zixcbd3%DFKKjeH8Y zB%MR4FhpNH*Z#x3F)H`v4^dGX9sa!6*46`>EnWxb5G$y}wsTjftdNoDmMM zf5piR2N}qllT$|TrBIN*Vdftl7*ZFL_rsrD(?QtAkuL;m-q2^ihJ3^Fj+@64ZIsz2 zC_#lN15QXmq6rmh3PedDkEJQDdf4mQ*kcV8M4^RQ?pn-6fva5k1Oytah$X-S!JC{L zm)^5pV$YY?AOLk&Vq<>8B&cieN zaS=C4!fVWqe~s#~^ziyJ7{tw8jQio9mIzG7%%^DwWE{7smTQV2-@ZDIMM!e=n-Dak zYf>BR{^&=5|J7SlLJF1_qNJJ32pnS=pnAM1h73>}G*pR@%#UXD%S?xp3CNljB>L8O zIKib6y?PAxlG?`ZBr7p+pG&`my>rXsO_Zz%w`x538D*Iaq=T7(z^6aigu94)XF5aZ zO`@-2HoFIW5Y3MU(lP*F04N0j?leKmWQm~ZO6!Lg(t>xs%{;O30VIG3Fr_8&%fiSE zywv4Q-0w6_#o$93hw>lS?{5U0+p&-TGb#&EK!mc$t?Ad`Q|$H#udx@mo-((Ey_B*? z=?Q@I8RONSK2Yhx?}8~ zyr+8j>3Tcn-dpc8PcbS00qCi7egZ^{qk89=S34diBFp+Ccm%qA-s^a3$*AfjZ<6U# z6?bJMYdtz@x1Qi0ItBj<KGOgK&N#`;?yo&~A^vhtb~cXo;P(<0L)MJk>b(1* z#q?Q;ew=XFt_pmVO9ntvxWc34dWFn zIu6Pxs-pAqTud-Dtu4IdHOK~-1Xkctz@T z%e!~c!o!&W{F(z&6RiP`e<~99T>mLgMb#R-*FR$3i&T`{Tzj@4u`bxZ)J$sEKc}Ic zH_iJxO|HH!%Gbi^U|2;Uw-YRD*Z7U%_%Q5{VpJc+M@~4}3l{?y!}!zKZroCzX)+HPaGNy2o{RUY3MZ?-1+Y zoiyfa#kITv3Gl(QB^T0md1`)txv|P)R~;O{Pb}a~^QRWL4GX_JOwoQhD&C=cZ=>^@ zCat&_m%BeN&Iq({4%ws9$oE3?r484YTBNk5hq>n3US#R*mJ->f?|ycSDzqb+D`{_9 zH*oKn-qEq7N5IRo)xV^%ol?N=zIOr=-F5AW!7w}$QhBze2i_Y{kI^66dazfpjAUN< zxq9pAf_~jSuln-9K!xo=9+Zad5_WO^nmiyM&>^JQ@24>1k?Swc9z_KKNThWnyexf2 ztCvG6=+Y_$wb3u*#0Jgar0x z9ZB;le(CcCx_ERq%Su!RiNb|Cg$@3W?2(nXBj?r#&`0_8tlJ7io+Z3^^0wjoejmZY z?{I*QVoy9iWlOaLmCW|PYf$?_6nf`Mf&4;c`&(CVYk1f|WM)&Sp~I{}5$^o$GA{>8 zdv0DbSr62gDC#myGf8+Vrqpo!axBo_^symC(9KjF<2iKZ1n;98p`xWOyziU$bV+Bo zM`$Wr36f-yB1TRv_SFM(=E;nx=T$MRv3%p@@);P`D!ZQ(+gY2*r z=&8`xgaD>+jjqEQlE_j=W-u=*Nhn|LegIa`$hENht#0#E;lH~+35*wbBCAdK{w~VA zEvmac(TEZ3qgedZP*y1lz51j5-KEQJxt@P>vrqOnZBT(c52yW-hvH0ZIsSAq%9Rr} zerM40_qXky*(aVaxaAicGB@$ zRMJD+2qKTk+zp`~7gLnz`Dc;lnv~}3pzHkt1$tKE?^-`xS5Qms0sxi>98?ll#(e4x zK=>(VNuUZ43taynl+41*xs>&=UI3^36j&3(wKEvEqthbsV4(;XgcK5#Zfp(ur#1CU z>0>hgD_`gFDTOG01+p#)Lhf+hxCI7L)DNdTcGty)zXv|kOFl86M{fvuPTQJHix$^0 zM1$k$5D_>gefnOG(sj#}oyqca!~BbG00Ufy+OhJ`#n|1#X6?c*GtbqekfJXYxX-RMEYRpa7%yoz+KeY9OvK$gB6=E79U`LxjvR zZ2~!uWtvV*zWxONw#btL7RD>d!FPCl5yYEDPgyDP$fP<>Yao<2VRwmsKM z9jAt>0X9w*j(fPe_v<=FNJ1ikl3a+n=Rc5FofRlCm&DeNH;#`i#}x&V@$r$5aS?}K!HN}i`&d(OZ$9bG#dhV({O^ZC(psTEL9?5Q(vWZr$;xmDGjURR=K-M1TwR}X|BuJr9TtfMr#dO*Flq`ta^GqsR@5-~iWsAhj{3Y~QQ`x?j{wiB-P+grBHOA~4DJCRK@q$G( zL9f5q!9E*G&Gur*Wpb*$95MayrnS?E3W-XhW?-K>wbxk{ES^22Gr_Mt_})x;TK_t% z0xEuvA`UfL#aPoYDW0M^C3-93yODmQUJ$hinxtpn4$tO}X`d?DLsg~{Dkpw!zE{2` zSytBwJ=-^eI-S|1EqB|nl`rFEm9ov?)p#XIp14i9uR`KK;93Ntx^nK6a4TEgw1ON-L;3QR^GJ;}&4T#=zGV#*Zp3qM)61i`5&ZJ5SDrRX* zQ^w&%=PE9id$>eP?!rVzd3@Ca@jHgbSMF}2WhxXYMsgM;(PASbnHgnQHURUUBD-M9 zCUqnFiCMHSkDJ#o$<<4*`!t3b7(%f&3}(Euy{gw?Kz)arhIX) z^r^Lxhy^i62C3X*?8)(%(N9`-SKwQ$be6w__>Q|Q*rU8`TfekV$OB}-f(83hN@AfR z+{P*dw!_ct#rsLPs$w}^TkXmW(C;Vp2Ys3nRWTN+I)WReSU)GYH%6upli;cPxf)i! zCAPA$8@V-7=;6X9p?^kV#I=;38uT%$Mv+?`l#SMO zS3MTmrVBMCouv*xRlwpc!KzE|BT};F^7}MuJY@+`aA}6{)$`(a&fnvt*C+MgpY(U? z_4$}2d&LM+-zzs_N-umNi>MYhII3`guj${H+_i+%Unr$-Rl{r{>Iud=5;Lbc)O(M? zuH|}9$!hPH)tt@1+hv|l8>wyecP?KPIRFnLdjcF*?V*Xsh)rzWv%R07E|Z-6^lk`l8L4= zDPg4?DjiNMkEXJ}El&U1>V6$q=KYUmp*zrPf_jp8S>6Xj=agdZsxJ&jRB%b$+T*!t2D|e^^X;%~atuxPej+gngBQ|K7fs>GvtjSi-CvtF6eOA2vHlEhZRG zatMV3mddYZTP8~f*WXcUW!^N%n7KV2j+U6tBsZwVA zdl}R(mBu>zX`@iw^AacWh;gfbK2%<5wPKcXaFuU*R;vUH2sj(#0OUX2NckkC zt`Mp`s(ff%qdqY+UPSZUdADRiHIqa`Asfiw z9Iv`K`B#t{gb1f(J?><&vU%VA#ihIO3%}x{>}ki#qhCLl8w)ix1SO7_x|&XR;g+76 z1!J-5gg*UbHkOl=gSgwlUyW(ytM^vi=(>AUCb26yWw2AJFFTj|#H18{D%)u!?0xF+ z81E^@B@%y?glm@)+pd@Yo-;FQ4}V^CWvf7u{6nDZ+0@^`XQ%wT3paE5DIi|fnc&%e zVT#l;f0K<1QL|o{-kPS^TK!7@uAX9J!vI8tg0n%@IDsLDVNLYHmEoFXPMd8nQwZ0#3TDnz;uCLdZx~i#RGXcBxev5WRrQe|6Z^z zQs%k`mod`9#8``=QYr-sR2qS-f|wOc&losJ!Y0582WwX6rs6z_{*ogcPj*=))ax<0 zo3x)y&f~F=iBzJ}$8W#N!|B4V!a61cr?vdB!qc5+GM>M-GN*}uSMG^ET#puRk%iNp zgNFGihNrY=5D-C_@-Z%Rd?kmf?%V5=2u|{BuD5K`#YMa%kLfW|77tc+tim@&edrC6 zM*8)XQy+=sb4(R^iXU4hK-Zc5!|2{AQ#>P$GD6C0mzKSxRTisuR|54+RA-j0PLKA> zEx+HmYpmvOm6gI#?=c`=+)L>CY+iX=H6QX`vsZd&^1v)Yv;G>)&X~?kdELEn=&+}Z zHKxDwF--H5^i)#J4Oe=Jj~giV9U(Ea(WpK9%zOPtedA#)BIF5L`OZ6$Ra80V`6vEw zD1+}G;25>%4ansy8`o2$T0clL!Zp-SjkQ^v#S%V?=@|mhCEB`eC(eKaGJ~xDh znk@>M`$(ug1|1i-S@c=y+8N10vmeKrgm4>s-0t(}8xijByJaS9Z(-_rImlp`yCR{P z+Omt0fn4$SyRcH>azP2#!mOkcJuy)1LxRvxzfnK1nY9y5j|b<<$4ADELCgEjKPAnE z!j})wDnGYS2+RW9-?h)iLcBTTB1~kqk_wF52pa3m7oTT0;cYiw{jF}E*>{kP=%8#f zE*0oJlhc)Y2~GBFn%wPeSwWnxl;C!*#_@4R|6Fo3_@Nz$F#8N`@`b*4djjZP^jt=f zzews~gl3kP1HylNmy&!(Oa z!0IA4lq3Q_Dr$t6{9i@);ZODd#R2?t?{)XKch?BXUfErHB%AE6ktB4DB&2JvaILbc zYmbymNRh5RlB^1;u8|}}Nm{-?zyIO$cz@pS*E#2T${sjxjkPdO0BVsx!yGOSpoOrG z?(LJC##bEL{7?CPO1d9i7oyy$S>C=CGkwVcx_%a1=0N$9eb+#6IPKcu(|Qjal)$R5 z7S!yrr4$Y23w`wbMA6iJ?AVof{&#Wcna8`}L|v7{M8mbuu8E_{a;p_2;nY6OeGjy0 zyY`H-V}6vOgh0ku%N(UIZ}yyUMqdIP?u9>%x^d>rjeBPgvhv!@{(Kc&dC53|JF(TH z7S#V|r&mYL{|LrBhFB+Y8e09`NL!5w?hijc7OqUF)YFlHeOnffY}$BQHdEN7keMiU z|2IlN4I?3vzN{RDR=t0u|8v8PvFf~$?=EYH$@~Lk8e{{Or+gpZT^Ma!5PCVX+1A;4;25?>(`UH(erb^6q=$wi~Mf{a{lyRU-dpnUrAJKpM&Ij zfnlHT)lL!hv;9-NkLPi%AHF#InA_;KM6jDG&7 z&d%qz7FUCqrQ+YPdd+80*Q9mJkZH2C^do~1?HfjXCwXlhujS*RukttQ_8^OK#;0RD zcef%!)}_Q@>M4&r=A_j%J9OraA}6pSZ{0IX`mQPQ(fvywtr&lq%F-D2S9_)x;WDIo z=kA_;k`WJK$AEZHicR8;f~*x7nxyrqtBfjHVYS1bujCBrkByxUxOSLze+vDrqLesd z1W_*%9VPxiSf5Ds-5f7W)Y@-ZhrH9Oc>LnO(HxfzEM)zm{+o=O@*5fI|DE-V?UW&J z-#bC47$pAK*hHU6&Jt8VzhDa@17wp-K4mu!&;i9o87Nt5lQ64ULITYFx#>rF)!os@ z&$bsJ!=*F}Ro&;8Ymux$FcPRjX{fm3fevyPigW9LVR>yrb8*cOYsS z)WKl&?x74d0&+y3#^>R^2Frd?dXs||>Xa)@#RqQGD*6Kx!*5e8zAfY~+jElZuPMS? z#s?@>|iNs7bh{S)J=MeDSv@k*-(k+=C&0}5!6o1 znzFq|pe+M*DK71AVCm!x{l6<3i?I@+M`OChl*J0Mq*;B4xw;Ov|K;+o3`y1|ua5NZM>nhu@Mp=^ z3<`xV)h*27Mvl#&f*65$wFS;IlH>NsD3efOe zP{33`#}HN=8X6%h6v!Nbm2R}Vo^ZI5$&J5#^_Jtk8L85URK3W$#@{0U5E`?iecWDh zWW|?$^qyOL$6}6L8#5IOe)wzgQ_!t-W%sQ|Z&OMc*M_}^vsA~52y@vEe})Bty3gnN zGwIV6x349XB2U~pn_bKY9C&Rz+nEZR>bARm=B;2 z8c!CpzEG-#)_X_M1C!V*$xSh(Q+L*>MUr8S19D%yUwZ5s9+|VdLE5Ilei?>$KdZ(A zJOE3yH8kFPx00Lp*a3?J0I~}-w`_hsOx1i3%MUzi%=zy3-5VJd4{ysu>@^##*Ah$% zx#MzlOthz>>T0!X3O_m&77vlE zK1WM?;R|d&b$La)FYV+>5lPYUv_!$Z8sltbbfoI{Q~ z)YJ-B$us-+yAnum)%o^k&ZeXR9q0f=FjO-!fRvyw1qQT^F<~}c=W_8DzL_14cAAkE zbgmtUh$;Kiff64NIdm?~Ln{HLp|ozr?PX$ahDM9ttCtK7NEEr@2iK0(Y-|6UDe@nf z{{&ackqDGOx$qd_oc454RQ841TswTx639JCh@%A#o`K)05n&x_1eS z2&ug_xC%f6ANLB88790D6yv$=2gB+(6aB6%Ty|ftH_BOkylNp-+!Zwp?Qkr`zeY%m z&naIadkO_Lf$tsn+$rdrIhr;by^cxM z50QaudkNNq`$H-kSUCfLyV3J%N1qN)v(W@KMXl>XCyi^B)M$|9#q6jD2$Rbb2k6Iw zIlR)^zxuPqEV*BWJvcThdC!UF_!h`KSE4f`?>4WT720YfvKCMuz1Gm+{1jEQu#3OW z?Ogny+M_d5ryYFeJn-k4NL-M4qEN}T&g)s@4V%z zcY~~1$fLIxPI_Uha_2z^U22*)=WJRJ`V0fi;{(v{n>wr4GPt9b&*whZ@F|cF_h@nL z4LbjU|N0-Q89zX6F@9dBH5LE^jX>SCe5!Q-+;dImTFHWpKEZ8s8_WDh2V2)4`}P2p zHk%$M9;tRwYuy*6Pye`9Y~i-im!zK7i0f@{|HUc7E;3aP+DSLvFn|lpsW&AeX=j2Y zxYF9wxPB3q>+hVHyAzx~csI*8uE5tO4HW!JM~hQhHLo6gQLx$?tr;9$J0US}ux57>9)1gX^$zv(9ZVap$ik za@k4%b9mYw<=KQ?me1UF2?p?v8X7gdaN^~If159|b*%OmvzBH(7ge9f2%g|OmYIUn zdDk%`{P5>(shm=;HO8Q((q@&-_sn;j?en2AUe!pnp7OoHN4NZ6c^tejdPFjwNqzg< zyPZz=6&hxn$V!P3S^;%l64Z+XwI@MHfEMrgp6Dnd8&s5pl%Ovoro4lwy(E?G%?0me z)cqqRJsYCyh^U^SiY5;VI@x|$Xr|he1zGK{_%kCeGTgIZKHykU^Ec^Y+QMnY?FRiZ zB$GF>EM(M2*673fvhvbk@R+~zuz^sO7{8Yo7fcKTW~-q94r?~l8sLzkfF4YMmW_)r zixY==RkkH)I6f}nQnNY77OC>-?{Zh!p$ve9w6v|r9tn?=3Mi{jUR?|5Uank)K2ka@ z+)`ep4~75ys8?rf*!<<_&7%-g%+ z_q!z)hK;<$hrO8P!bP>08B!fJe$Mm3if$hjC9P`uZ>Y#9TIn_i?HY(qEDF`db3fYR zE;QkB064grKmd_9_~q8tq`RZ95Dh;crcBC}51H@b(^3c--NGF2OJ!E_>DKg2_cukA8Gl*>Ux~H-2Q#8P32NHy6KyqJQ%FY&WQz0}{BW`1 z+z0lojk<+;eE2aU@wqVq?EYS+X%*;@W}KH5QQhM!MX@>=g0hKX!~WFLv4 zoD8@yVwk;9NFWVuM}fFCgPwl$YXosCEctCTYyTvw98rBZUz;}{a)L^fx)T<+ZYDh% zf7i>dh_n1&Sjzb-39}^sgMbIVp5vq9(6N#!Z^6dg1&060<=#NjB>Zekko6^zW5>S; z#vj&Q)inSp9YAI?;BgF?WC7%XgM@x>Yb;qTol{4vKqE-3xxemM=(0{sx(FsuiZ_tz zxGbJjPs}KnC```PYm^ z{%r3o>jz&;4#c#}#EYep9qxk!v&DoO#R8O2l?@324&{jdgWLWsXSkH zMA4K}*BCGr_Niwx@D6ScS+x~sqj}OIx8)+YHHcfqfKzf2iy5#MbLc+6@O@2f<@8C& zwvm26WGmmRRsO>rL^^zHfov!c5*gGxVMzE0@7RUX+1N4`;&4!Vw(yi9+71Oky%;cm z+L?<0AWAmOcQC|;gK_x~O#tdofpIuf1a6*bmT`G@Swaf5KbE;4x#MQTt%?txRWJm2 zDIji=9}mUPs2yzE4(2AEJ5B+6@Pci5{lzH$c6(qK-g9R(KpxKLF1|i@EfTE21|O)P zc^kb4008F225XR@wlp3-mb(tjj+2d~v!M(!agcTTIvcwSAnSNx87iPb3g$f}Xo!Mt zr(wof=YSiZa)~Eb|z8AzpMgvUJ^Q?KU zgbKW|I@c6>pG&3}G+5=$DGLT$Q!Z6%baw#AKr()i7MR0^50Z)RSr>Bw8Cn=-9=NpW z9Q=hh_(P$gV|-SD^J>9_ggC_O5^wX}LGiN_8e4ciqI@p<&`E;6OGKV?eaMV$)lQY+)g@*#`(Z4cWDaWYA9i;(?Z} ziEA=o*{t9nKankLuaLNjt9 zd*wp=wUXkRErjHdTnc)Hh8|`B8f3uoC(-ZGR^Cx)FAc|FAp~cx_fhceB$@nkG%ao| z%Hi~OM^x!l!%|KBIYa4gv6TpygyVh^l8dvbilE*_ZgCt;Tk{&>Xi_c>y-P#Cr&(!{ zalctaXU(qFR`K^vcvv}6#oJXn-c{9rOQW0!14aCJ9hzyM?u*wlJ{4~k5&tG+cXa7y z_N^mT1{^#58~T2NRa2zV8sR{(hyH`gAh@0F0f74Z2>+gjgO%g=*;wIn{5&smsn$x- zIG;<5$Ssz2w45kfPV^Aqqn2t2=E#x2Za!o8W%w>Rrb^ z!hT1NGQUW)_szgJ9zS{Qq=2~38iZXkZkLP~HMrr+-^Enw1{)Ay-UO7`O;0T%MvOS_ zdJ}Fyj8jQ3jFPD9@$r=pSxvq}^f?k?)tyoN#>!A|+x7H*yhuj8th^84OyrOyc`EyO zPCBCp8F=m+3I1JkzU0X7Mfq>|{DTO=|Ei{a{|HbW`-p$#*Z0LltK#|8;&C#zJWk4o z$Egg2jU1$5RDNMwe>Xg4BV}t1s~^K#DR}PqLf@+da1X)MjkrcZ$Fb{pDS##k(n{sA zp&6zM8UqZzVFQ7`znefhOsEd?_9CC(BJ*51ANL zoa8Mv7>S2S4|?p3Z+#N7nww+N2%49Gw15V3w-t2OL zZcd@b@?<9Dc)P)Y?8b5asB&ImF&Tm1^kWr>ElT(2&hE`W-8(wZJ_$ieUOsZ?2eRWs zvwxr3-%QW?6SUN0+$r|>mQcZqpK7{)IG${y@0V&g#@id3RIe-tN?DYzzz*xkVJIN* zXp#7#=3y!~;1oOH+BNWshMk~2i+TIZZrN{zCQ_D}wqQXHlDz|DBhQ=k2>j*pz$@7i z8*G}n+d;zfVu}QD%J!1{{3iT+@R8r;wjQB%SAv1xg9Ao=13j_*vn-5ELjQ#>aoJkF z*kfyqkln$tGwXAGz9quqpc zxL{zsZ0JG9-7EJYGr)jTm)I-?tL8cVj$P2JJ21yWbB~W@KGx;}z*6M%AR1=I`@KI@ zwAbRZW)u0fmBBphG{#7DdvrwoLd|ni(fOXY+a52yUzMwXVZ9`R6o}g^o=>KYKXQ?W z>|J=`kE&SAR6S!#+0%F1V*J5#>@53^%Id%oA1wJccAbRGuuR9B01@r}P8t#l$NBer zB1bn61N|z$`yzi!j6?gqSE_|!p{bPf$jfdu#Y#QT7lriO_@fMX^m~Z=G_>OzLtX%^ z?auREqLwqfdw=Z>}0KMT#C%67u0&VB~?`xLBVG*z_u|S90T$EW28G-q3k`^3Amp zp-C~}C*Fc3J&R7>^Y`bF;tJNX6pxV~9JVw_)O#M+`7c%wNF?d)#TT(w2OrgMpSB*s z^pi0U7|2KxOq&IIa#_YK7K97`Bu#ShW+7K-_?dk!M=?8Z(06ZdeukK^nJloaFAH?YeJwa@aA@#El6SmduOj{y>dwXbZwAIX&dO6pDew$Kyv zbF17SX-9VcAz#y-!7O2&=!F2A<$*-PT=S`0As$*(_LJb}rt&rF#dEE}-(e3t9!G4x zcbciYba$fs?{9&Rk8TK%J;OdC`~-D)UvbUYTD;;q>_2UFv`q7R zb?$?Q$7OmF;lpk?1~@kS%NY#p+7?M>`Pq*iYjBCOJUDhYHZNT=Pf`VOBqC|CpQz*r z07vR|HO7<@0a`Bymj`vdoSpwda+8RcXq}^HqmCMoyo;Ip>my)p%{iHLbQ~ zo%lj+t)}#a#!B>Z)+2V!cO--Qc)$tcSY`*ux2!>INdNu%^6D(mm0+dJ+_ryCiXJAW z5Y_zv8B8_V*-8SiVt)loQ`=5aAFpRzv?2a#r8hn6U3D|uJ;YS?Uhc`{Wos_|budLc zNfI+&J*dgnuKsL1uDK-h(=q&AY~t7OcL9k9mtVWNpAI>(A1!gT`@`yPt9HIpEUz1& z$vjjKtGovV&KK=qPhLA1Q98p1*jlPSXSQU@n-=iU0JN2@0rZ|VlLZEI6IhIDjDwZZ zfFBruU%iVVpV($p-lIMi#FiQcIZLXVOj$m!Y~ec=Bec=-bNzSr2{hKGdAg!b{DQTi zNPGCoeASEXW&8TAo30Ii%5I&wTDqWFU3l}}^{|?2=ed0P7gHAei-=}~%Gvzw-m4ey zWT-jF`y%RZobV$5#eMj32AqGU+v=I{A9ogq8Nfht)kBW1yI?bHzY|hejbiY_nRox) zl9`!Rb+q5`IyYDn!->9(kB2kO6Ay%s)gPCdq^GD=5VQR2sL9U*%F$Ro$Ss(Frd?FEM;hLVS24w>Q~Rl2acr?(C55r_q|(16lOtalb6qtgA+{+YM`RMW8szeXFvf9zv6^T+2ubs4NPA2czz*33VwnIeB^GIt%G^M}n~ z2o<{30~U$cHf$O?5ubP9{#jOIX5RA!$1!}=ZeR`u>8Gp2$yt6s`*e`ud2My^s-mnF zQc$#jaamqgSghHRzZkmcTM&t_GAXihpGHb3=Y@6UhDkimyoa56Ha^sIf=}Sj zB`bO`SJ*u=dy7~tP2~(RaaZ}qo=krkOwm<|VyTqok#idrnaCobQ9}uoXZeuW&VXsc z>heD~k4a(+u!d6N!-nmq5ob-)(m3Y<=}JI#qqZ@6y10XC9g!T|p` zZ?^w9CvHZZP|Y+^u4u+m=@KHMD-t1+%F;R@3e-`iLhzvBu;b1<#C$sMrNwVD;u_B-(^)qKb+e{xc2iP*O!zH(lMWeCfJgNU7)ry%yx;A3JI1I z$muof!(aDe@I*qORMLLrk$>x!1gM&^Y+XDy405P@7TKvX7BK;Jih>P{8`nCPd4R)d z{0KMRyiEhaKKh{LWASGP#eoo^j$>!dOORf2i2}ucK>&jVH}f+z8lVlTHF9a(q9wM4 z%fA3lC*I=^eb`rUm#a&L}(@z?r2zRq^JAHf(BoT6JQXM5bR z6*&+PAZEJo=Y-xk$FNP7+Ni?V?0d_8rbmBW8`h`v6p|vVg9FX`&c4}xvxkceNvZxC zyDW89mHkHaD*|?ATZ8e81_<=f2M^=<1dOhxv5=-Af3Gd7_z6K!)DVWPbucpXkuQ)K zM;3d1ndb|37$CGtWGRWuPv6eJ>sV>t)uYdRo_`m>AF$r7w7JYIZxX zz*HC)suUdk*=6p5=4tn@RVg|5DiMw^(}~ea=t!;T+I8DUJq)p1ZnLs4H1DWXui2&w zf)vZu;{J^YMqUtl&zWjj;LpkXSX0tsE>(ZY4)Z{hgygxsCvdP1W8pMj1ejKsF1w%Q zy&*3AZ5DQM6>3@+Av`ZV`MlC@Ab$ zIraGU4}~jQQ-QN1BbwG__jo#IrGYHC7Q-8Dkpu$xNzqfe`X_$M>b*Fy$x)P>q^6Sg z7x|=5nvjNBQ@=LE_%|9B)x7jS`cuLLoQwx6K6{yfb)JkWT+z2OJ$a$2IjK$Cm9x*z z{vD4VAv^n}Z0TMa@#?PTd49eX8>Xx(wjw0^*l6MEnF?Xb64n8tfqWx5Af~MVAwF0X zy1$5PSLXgk#!XM4?=s+L?SSK?VDnnorOKF&pI#q)UAp;Bg_2+f1P(76Vw{Z&0pn&T zE@f9Bt7(_o>5&5f<{Io;T=+%hh>OQsFNQxdykm^*VP12R!j7jN{qaqBDZwaiNR5`t zDbuc!bjBT>>n;WlJT(w#sA^jqe*K8FT1F?hDa zW#o-tV`BR#vE{GM9;1SVZ9&&Vq0=yz`@8WX!!T;3vGpzEPy+lsfkTo2uiQp|0Pve^ zx9(-h58{VZdRPzj+)+o9%KfnL^BE`8l04+snlyMFl0`5ugmNG)m^x?uNNZ=%!g#_pDh>AK^Pq zd&nhp+T|*HCb^=^FYgm88?NRcdeTale;@=h^B1h83W^)cXC49G@>n@}1jkNxHLU{(6zFnQMfU z##v2CPA8tOT1+Y9HQ@A%Qk>2#{K-{sWL=jpr~2AmLtIA-%&BX7z4p5{Z+vJ!!uAIZ zXR?osrN^QtAhiHW{9*ptc-})*^_5hC$o?62pT9p7MrEg+C8Rsi5G`!XiY+lj#WU!! z+OK_X2d$fPECq|%^Dl1|N~vMnc&RD}+*}rjL!r+*>bydMpQ6^xmCf=3z zQ7uHXuo)ln64z2vUi*%!CQP0*Dd&7yi0106C0?ZeP5ZSB_iq;N?V(x7%n}DfMmV6g zbfh>JPIyag3@D>Q*yLtNd zqh>ra{LYhdsjs%Ui9=zK0X>@whO!Y|Wb7f1)M5y4Vj}a&D*MH}hiGa*O@w(BEYueM z!4zDRUb3bXxG*UqACP-+aku2xn*36)IqFyJ3^OB~kJ_e1ohC+a8DfV4u2q$rUQ@!F zAO&l~Os=^CMrCH_8X@e0`JS;Fdb09ic%^oj&Oj)I468i3EXm)T0TKMO2Lg~LX1E+#dqdB?vnRo?8DSXT;o7d! z&|-nUCxQVgYMmhxKZL8~M0HV7cbQP6ka$2dID4OYgM^+wgfBaipDh~Te^Pbh(|@{T zE;A*En}t7+6~6SAQ{6D*Zly0SV=llkLp02s8FtTJVRFYi{#3<~g*qbjQJc#{bBK0H zjn6}|)cayiWb_lpac(77+!O_!Pqp;S1s%4B*aB@QZ6WG3unZmS^qfbM3(pN+Tf`SfJhupEW|SgcFR_juZpOMls7w96O3#;^_}sCihW7Jye-ANbFVei zFq#U*G%BIeC~!jx0AqpAP~dSiL?H{+>#F{iiJ78s;jU68YVKpBGuso6CrKMTPW1Rd zd;%4&uk5VM96@LO;vsC;3($cb2wHQdQxdw6+6|)u@+6KkG(WQJ` zLaVmE4=a45(xm+C2P&qTjm?yZl6VmgR3bA1GU>B!UR(;u=t{87eG2PL>|>lz^OK3p0OuI<n9w+d}t&&>;U1v7s6Zq9?$Y2r=X)9`HBW&3`JnU2- zG3o0&kV`a6pw?MaDnu z80aWILowZ%iWFds{=J!zF17BVjPP^LonkQB{jI3hLfTxfq zqW|%E5D@v>NWmq3w{4EsOiT}9HqJ-3uhhzqg`@C?O;wCzhokrnaCx4{P!`0Hbk>Ol z9{%%Unt+pS=A4SP=-|aVw;XG6j9H+wx=->BR6T%r$`?!Zz=?`)`T!m3D%L9tF-^nCz` zp<~y3(C!p;Y|0r$3WuH&@0D_`Ir{9E2oDJ}N&F`qcYM~U5-O6wVMv2TvY~P;@PVx6 z^HIQ3$a;Tvs^PYlG_G3t zTyBMgZJI!6(Jm{}!34%j`T_6to+V!;*fr8}$~Fvqtqr>E;=w$SX`rJTCQ$!WkZgwj z0Gp%U){sSObIdmp04Tz$2oI6#Qz{=jjSP~`coK;?XX`ny?u|EPFW~HN@QK5c8}zHr z^Jk>8{Fs}gJB;SdtM2wDR6dNxjv7~SIJYFRfDiwjbiEte>JQ~U)k8%5#YM%UPOAAE zF+f7}h2J>i>pL8mfV~J{ujyXX^+`y2Ie$HXE%?!Hgagr~t93~6S3Dp(Sb_$5m(TILdU1!wx%By>PHeR~X|2FPR&P{!Me(U#c^UR~1 z58csgRdXdP94s4v%f76G!~j6N=eL-k>BB`+47+M;c@ky))jg1Dhq;|^?@KV54*<@6 zd3F3uyCi7BQK8gOy1m2>0wcLt1RMYqV81^w5Y_$L>duWbvrLOV>65*G_M0WlA_W$k zh3c$Dw8uUUCv)Ax3M#r(TRv@34=(MXE{_&=j%}$tp?+vRoymJzQ2oM-$lG}x_gqvj zPIn`X`F`z~^-32GxsPzJe*d%=^z9xzq`1MD7Voba4>p{=OAFRg$!X7^aX!}S)q{(@AF-*tB)styEjB9|8~)K(4}tJO<>Vtj9@F4>!_6$zvujSp{D!<6{F ziYEX~zyGRqnPpTO1gGw7d+BLiqZ^xGB}a^~JtZTyYip?d3qotIihf9ZD6SVypRIF+ z8I(N%kxH&D84O7Sn31FgWPNjOGybIU2D6tezSGZT*Z-}5*d@qJB(XPLs_?X+zzWgf zi9o9ArIrzxP;iZ3t@5Z+<|cpe(l2v~$k|+fp?JAW_2<2o8MSv-yLZF1uXZ6#IVJj| z4PKbO(E$N|>%>}-s5oRVR`+&7#>=~nPeA#n2t4}}v}2D~2&T2aEpy!hn?HK%1G5qu zK^b$7R+j*J&GeN6aTuA?=>fR$R*N^K7cYpNO1}2tD zFUf{%*W5U9pp<)8AGz9q)xUYOzsJ$vNvqfRH}9Q>d$=dcgN?D$rJ;MxwW8PL{ zPo_*?7`GkGJh;2D8s4a-Jr{H7**};j`x!Ln*>*jr=G(>8;CMsB;3OlVA%LZ_?~Olg zQh8*1VyJFk<;K0P3xqRaspgJ659aRsMeGsYJ+i-5wouF=Ng5s2Sux0YIe;?hy63Dj z6)zALVI;W0f5&VIl)Y?`Wn6cv_|Y9{sr95veg@sh(VCusrf0?2gW?DG7wdxxz(AtQ zpyDV5esT>s8CyY%(Iq}R)n#)7NIc6gKZ*Qnaa2w}3aS{?lX+%_a&m}d>sgh1xRJ)k z!Da<669>+C;W`}pF=}W_KM7A&9}?ZAW{dX>2p1Clon*YL^j5s_k%#M^$fp*c`+dwc zd@ZYb@S2?3N*2h8h%!pOA!cP^*=w#Cd^&iB30p|@cRas*{&+-pG2NOLdSg2Ftl*!dBTgN;d}1-TWJf2n8~_?Vakh`B@hT^3qmt+3 zF%wetQf;tbRYJ;VP79ZcVN2~)C${B^_-F679ANXDc=W4wM}$DdVusmWUjf|-kR?}k zmHW*u5vz(TeLj9m*U1(JsP2c)Tb>^0*(AUFUKyN|98{j?vi6%^!Ft7cg8m< z+v5E8Wwy|0?@Rz-gL&#`KZLn+;#@F5=Pb5X1#XfCRLkd(8 z15iqK`r|xMK>M(%vnBvs44g{pnavni<_Ao zYPDC9{@wUe+@Jg=as@xP=qrxHk6uE!2Vpjg2i{+Hk5fVbO*E3zVdfiLry1Y`A<~5s zt8EhU3PoB4E5ExrTf{LRW8R5fPS(gP-GGQLY7QO)ht6_!kz?%q+w9? z&>kzhyTu4i_Iz2|6qcw}_6xSP$;C0tQE)=bXMBK~bdZGFPLx<-!1dM^IQG6R?!+xi zH`Glyasz;Lfp6#VPOC0T@InN>)jikBbxkG9gHPIj`0S?b?m~GMB^Dgy?Z8EImp{kx z7%KQ&Gey0fh|%V*beIxj-})sX5mM- zg>_>O`t%H+42n?N^xunhrXx!zstTqSn4cAPBQfPs-$K%X-p!MgUfY`pNB$qHkxyNh z@OT)RFJ=Vpwc^P7OJ>ppUB<*yV%YVorys0MntEE-}Ap*>_`2+4#Bn$NK8r zA(oNRjlGG?yLD=&Q3cuy18g2XoK7pPw_HEhwM8;^V*`GX5%q`b0BibzLo0r5E% zdQf4x_F-fgf-{f_3o7Ewo`oL^({wiKyz{%U%uwHvoUGL$2Sg>FrYBkPLx73yBQ!8? zY=Q)%JHi4=cqj+Zl59INJ|r9@ONSlBsXU|4-W=`Y@rcijC#BW7Ruhh4V$C1q*Hx{0 zr}uPJ?JU7%tKg9cc)(h+)dU1H1~j_@*WFpr*jt`#_+=S5m5fOIlR^b*qHI$m#5DS+ zSQiYH%36-Coa)AVa0C2_rsf3-3G03OU*77M_ol~b(sLg7)HCl;)>5zdwz`ye zy_vv-4fk8cmh<&?p{8^#z z_k}2NhLO^u*;4aHIzJcdC)VX4(|!1|WRpcO*^1*X87#R5GKv5jCTVor8ov`v0*W&} z2*Mn9>1km%k`7vKyh6aKN@n3Jxp%MDt<*Ctf^dXTNyi)Tu%Z+%AlYCI^iNZqqrk}R zuNm%7MliG2eGU_t<0$z;57vgFW!5EQbK7VN|`92q#F3@l(&FLyfb^HD7d9G7u}f9^(I zI?P9xwTZAMfJT7+7|uA zU3Q`kyPYBf^9vZeP?WO3pH1z+UfLev7f8F$k;*Q9=rF-y#7O+`1hruS;z+WbF0+CB zE_B P=5UgM`7s6p0qFb>f8T?c literal 0 HcmV?d00001 diff --git a/html/vrx_index.html b/html/vrx_index.html index 2e35d51..ad58899 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -27,7 +27,8 @@

Welcome to your ExpressLRS
update page
@@ -101,10 +102,23 @@

RTC Update via NTP

Daylight Saving
- + + +
+
+

Head Tracking

+
+ + + +
+
+ +
+
diff --git a/lib/Fusion/CMakeLists.txt b/lib/Fusion/CMakeLists.txt new file mode 100644 index 0000000..1c2836b --- /dev/null +++ b/lib/Fusion/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB_RECURSE files "*.c") + +add_library(Fusion ${files}) + +if(UNIX AND NOT APPLE) + target_link_libraries(Fusion m) # link math library for Linux +endif() diff --git a/lib/Fusion/Fusion.h b/lib/Fusion/Fusion.h new file mode 100644 index 0000000..48f5198 --- /dev/null +++ b/lib/Fusion/Fusion.h @@ -0,0 +1,32 @@ +/** + * @file Fusion.h + * @author Seb Madgwick + * @brief Main header file for the Fusion library. This is the only file that + * needs to be included when using the library. + */ + +#ifndef FUSION_H +#define FUSION_H + +//------------------------------------------------------------------------------ +// Includes + +#ifdef __cplusplus +extern "C" { +#endif + +#include "FusionAhrs.h" +#include "FusionAxes.h" +#include "FusionCalibration.h" +#include "FusionCompass.h" +#include "FusionConvention.h" +#include "FusionMath.h" +#include "FusionOffset.h" + +#ifdef __cplusplus +} +#endif + +#endif +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionAhrs.c b/lib/Fusion/FusionAhrs.c new file mode 100644 index 0000000..0417b76 --- /dev/null +++ b/lib/Fusion/FusionAhrs.c @@ -0,0 +1,505 @@ +/** + * @file FusionAhrs.c + * @author Seb Madgwick + * @brief AHRS algorithm to combine gyroscope, accelerometer, and magnetometer + * measurements into a single measurement of orientation relative to the Earth. + */ + +//------------------------------------------------------------------------------ +// Includes + +#include // FLT_MAX +#include "FusionAhrs.h" +#include // atan2f, cosf, powf, sinf + +//------------------------------------------------------------------------------ +// Definitions + +/** + * @brief Initial gain used during the initialisation. + */ +#define INITIAL_GAIN (10.0f) + +/** + * @brief Initialisation period in seconds. + */ +#define INITIALISATION_PERIOD (3.0f) + +//------------------------------------------------------------------------------ +// Function declarations + +static inline FusionVector HalfGravity(const FusionAhrs *const ahrs); + +static inline FusionVector HalfMagnetic(const FusionAhrs *const ahrs); + +static inline FusionVector Feedback(const FusionVector sensor, const FusionVector reference); + +static inline int Clamp(const int value, const int min, const int max); + +//------------------------------------------------------------------------------ +// Functions + +/** + * @brief Initialises the AHRS algorithm structure. + * @param ahrs AHRS algorithm structure. + */ +void FusionAhrsInitialise(FusionAhrs *const ahrs) { + const FusionAhrsSettings settings = { + .convention = FusionConventionNwu, + .gain = 0.5f, + .gyroscopeRange = 0.0f, + .accelerationRejection = 90.0f, + .magneticRejection = 90.0f, + .recoveryTriggerPeriod = 0, + }; + FusionAhrsSetSettings(ahrs, &settings); + FusionAhrsReset(ahrs); +} + +/** + * @brief Resets the AHRS algorithm. This is equivalent to reinitialising the + * algorithm while maintaining the current settings. + * @param ahrs AHRS algorithm structure. + */ +void FusionAhrsReset(FusionAhrs *const ahrs) { + ahrs->quaternion = FUSION_IDENTITY_QUATERNION; + ahrs->accelerometer = FUSION_VECTOR_ZERO; + ahrs->initialising = true; + ahrs->rampedGain = INITIAL_GAIN; + ahrs->angularRateRecovery = false; + ahrs->halfAccelerometerFeedback = FUSION_VECTOR_ZERO; + ahrs->halfMagnetometerFeedback = FUSION_VECTOR_ZERO; + ahrs->accelerometerIgnored = false; + ahrs->accelerationRecoveryTrigger = 0; + ahrs->accelerationRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod; + ahrs->magnetometerIgnored = false; + ahrs->magneticRecoveryTrigger = 0; + ahrs->magneticRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod; +} + +/** + * @brief Sets the AHRS algorithm settings. + * @param ahrs AHRS algorithm structure. + * @param settings Settings. + */ +void FusionAhrsSetSettings(FusionAhrs *const ahrs, const FusionAhrsSettings *const settings) { + ahrs->settings.convention = settings->convention; + ahrs->settings.gain = settings->gain; + ahrs->settings.gyroscopeRange = settings->gyroscopeRange == 0.0f ? FLT_MAX : 0.98f * settings->gyroscopeRange; + ahrs->settings.accelerationRejection = settings->accelerationRejection == 0.0f ? FLT_MAX : powf(0.5f * sinf(FusionDegreesToRadians(settings->accelerationRejection)), 2); + ahrs->settings.magneticRejection = settings->magneticRejection == 0.0f ? FLT_MAX : powf(0.5f * sinf(FusionDegreesToRadians(settings->magneticRejection)), 2); + ahrs->settings.recoveryTriggerPeriod = settings->recoveryTriggerPeriod; + ahrs->accelerationRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod; + ahrs->magneticRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod; + if ((settings->gain == 0.0f) || (settings->recoveryTriggerPeriod == 0)) { // disable acceleration and magnetic rejection features if gain is zero + ahrs->settings.accelerationRejection = FLT_MAX; + ahrs->settings.magneticRejection = FLT_MAX; + } + if (ahrs->initialising == false) { + ahrs->rampedGain = ahrs->settings.gain; + } + ahrs->rampedGainStep = (INITIAL_GAIN - ahrs->settings.gain) / INITIALISATION_PERIOD; +} + +/** + * @brief Updates the AHRS algorithm using the gyroscope, accelerometer, and + * magnetometer measurements. + * @param ahrs AHRS algorithm structure. + * @param gyroscope Gyroscope measurement in degrees per second. + * @param accelerometer Accelerometer measurement in g. + * @param magnetometer Magnetometer measurement in arbitrary units. + * @param deltaTime Delta time in seconds. + */ +void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const FusionVector magnetometer, const float deltaTime) { +#define Q ahrs->quaternion.element + + // Store accelerometer + ahrs->accelerometer = accelerometer; + + // Reinitialise if gyroscope range exceeded + if ((fabs(gyroscope.axis.x) > ahrs->settings.gyroscopeRange) || (fabs(gyroscope.axis.y) > ahrs->settings.gyroscopeRange) || (fabs(gyroscope.axis.z) > ahrs->settings.gyroscopeRange)) { + const FusionQuaternion quaternion = ahrs->quaternion; + FusionAhrsReset(ahrs); + ahrs->quaternion = quaternion; + ahrs->angularRateRecovery = true; + } + + // Ramp down gain during initialisation + if (ahrs->initialising == true) { + ahrs->rampedGain -= ahrs->rampedGainStep * deltaTime; + if ((ahrs->rampedGain < ahrs->settings.gain) || (ahrs->settings.gain == 0.0f)) { + ahrs->rampedGain = ahrs->settings.gain; + ahrs->initialising = false; + ahrs->angularRateRecovery = false; + } + } + + // Calculate direction of gravity indicated by algorithm + const FusionVector halfGravity = HalfGravity(ahrs); + + // Calculate accelerometer feedback + FusionVector halfAccelerometerFeedback = FUSION_VECTOR_ZERO; + ahrs->accelerometerIgnored = true; + if (FusionVectorIsZero(accelerometer) == false) { + + // Calculate accelerometer feedback scaled by 0.5 + ahrs->halfAccelerometerFeedback = Feedback(FusionVectorNormalise(accelerometer), halfGravity); + + // Don't ignore accelerometer if acceleration error below threshold + if ((ahrs->initialising == true) || ((FusionVectorMagnitudeSquared(ahrs->halfAccelerometerFeedback) <= ahrs->settings.accelerationRejection))) { + ahrs->accelerometerIgnored = false; + ahrs->accelerationRecoveryTrigger -= 9; + } else { + ahrs->accelerationRecoveryTrigger += 1; + } + + // Don't ignore accelerometer during acceleration recovery + if (ahrs->accelerationRecoveryTrigger > ahrs->accelerationRecoveryTimeout) { + ahrs->accelerationRecoveryTimeout = 0; + ahrs->accelerometerIgnored = false; + } else { + ahrs->accelerationRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod; + } + ahrs->accelerationRecoveryTrigger = Clamp(ahrs->accelerationRecoveryTrigger, 0, ahrs->settings.recoveryTriggerPeriod); + + // Apply accelerometer feedback + if (ahrs->accelerometerIgnored == false) { + halfAccelerometerFeedback = ahrs->halfAccelerometerFeedback; + } + } + + // Calculate magnetometer feedback + FusionVector halfMagnetometerFeedback = FUSION_VECTOR_ZERO; + ahrs->magnetometerIgnored = true; + if (FusionVectorIsZero(magnetometer) == false) { + + // Calculate direction of magnetic field indicated by algorithm + const FusionVector halfMagnetic = HalfMagnetic(ahrs); + + // Calculate magnetometer feedback scaled by 0.5 + ahrs->halfMagnetometerFeedback = Feedback(FusionVectorNormalise(FusionVectorCrossProduct(halfGravity, magnetometer)), halfMagnetic); + + // Don't ignore magnetometer if magnetic error below threshold + if ((ahrs->initialising == true) || ((FusionVectorMagnitudeSquared(ahrs->halfMagnetometerFeedback) <= ahrs->settings.magneticRejection))) { + ahrs->magnetometerIgnored = false; + ahrs->magneticRecoveryTrigger -= 9; + } else { + ahrs->magneticRecoveryTrigger += 1; + } + + // Don't ignore magnetometer during magnetic recovery + if (ahrs->magneticRecoveryTrigger > ahrs->magneticRecoveryTimeout) { + ahrs->magneticRecoveryTimeout = 0; + ahrs->magnetometerIgnored = false; + } else { + ahrs->magneticRecoveryTimeout = ahrs->settings.recoveryTriggerPeriod; + } + ahrs->magneticRecoveryTrigger = Clamp(ahrs->magneticRecoveryTrigger, 0, ahrs->settings.recoveryTriggerPeriod); + + // Apply magnetometer feedback + if (ahrs->magnetometerIgnored == false) { + halfMagnetometerFeedback = ahrs->halfMagnetometerFeedback; + } + } + + // Convert gyroscope to radians per second scaled by 0.5 + const FusionVector halfGyroscope = FusionVectorMultiplyScalar(gyroscope, FusionDegreesToRadians(0.5f)); + + // Apply feedback to gyroscope + const FusionVector adjustedHalfGyroscope = FusionVectorAdd(halfGyroscope, FusionVectorMultiplyScalar(FusionVectorAdd(halfAccelerometerFeedback, halfMagnetometerFeedback), ahrs->rampedGain)); + + // Integrate rate of change of quaternion + ahrs->quaternion = FusionQuaternionAdd(ahrs->quaternion, FusionQuaternionMultiplyVector(ahrs->quaternion, FusionVectorMultiplyScalar(adjustedHalfGyroscope, deltaTime))); + + // Normalise quaternion + ahrs->quaternion = FusionQuaternionNormalise(ahrs->quaternion); +#undef Q +} + +/** + * @brief Returns the direction of gravity scaled by 0.5. + * @param ahrs AHRS algorithm structure. + * @return Direction of gravity scaled by 0.5. + */ +static inline FusionVector HalfGravity(const FusionAhrs *const ahrs) { +#define Q ahrs->quaternion.element + switch (ahrs->settings.convention) { + case FusionConventionNwu: + case FusionConventionEnu: { + const FusionVector halfGravity = {.axis = { + .x = Q.x * Q.z - Q.w * Q.y, + .y = Q.y * Q.z + Q.w * Q.x, + .z = Q.w * Q.w - 0.5f + Q.z * Q.z, + }}; // third column of transposed rotation matrix scaled by 0.5 + return halfGravity; + } + case FusionConventionNed: { + const FusionVector halfGravity = {.axis = { + .x = Q.w * Q.y - Q.x * Q.z, + .y = -1.0f * (Q.y * Q.z + Q.w * Q.x), + .z = 0.5f - Q.w * Q.w - Q.z * Q.z, + }}; // third column of transposed rotation matrix scaled by -0.5 + return halfGravity; + } + } + return FUSION_VECTOR_ZERO; // avoid compiler warning +#undef Q +} + +/** + * @brief Returns the direction of the magnetic field scaled by 0.5. + * @param ahrs AHRS algorithm structure. + * @return Direction of the magnetic field scaled by 0.5. + */ +static inline FusionVector HalfMagnetic(const FusionAhrs *const ahrs) { +#define Q ahrs->quaternion.element + switch (ahrs->settings.convention) { + case FusionConventionNwu: { + const FusionVector halfMagnetic = {.axis = { + .x = Q.x * Q.y + Q.w * Q.z, + .y = Q.w * Q.w - 0.5f + Q.y * Q.y, + .z = Q.y * Q.z - Q.w * Q.x, + }}; // second column of transposed rotation matrix scaled by 0.5 + return halfMagnetic; + } + case FusionConventionEnu: { + const FusionVector halfMagnetic = {.axis = { + .x = 0.5f - Q.w * Q.w - Q.x * Q.x, + .y = Q.w * Q.z - Q.x * Q.y, + .z = -1.0f * (Q.x * Q.z + Q.w * Q.y), + }}; // first column of transposed rotation matrix scaled by -0.5 + return halfMagnetic; + } + case FusionConventionNed: { + const FusionVector halfMagnetic = {.axis = { + .x = -1.0f * (Q.x * Q.y + Q.w * Q.z), + .y = 0.5f - Q.w * Q.w - Q.y * Q.y, + .z = Q.w * Q.x - Q.y * Q.z, + }}; // second column of transposed rotation matrix scaled by -0.5 + return halfMagnetic; + } + } + return FUSION_VECTOR_ZERO; // avoid compiler warning +#undef Q +} + +/** + * @brief Returns the feedback. + * @param sensor Sensor. + * @param reference Reference. + * @return Feedback. + */ +static inline FusionVector Feedback(const FusionVector sensor, const FusionVector reference) { + if (FusionVectorDotProduct(sensor, reference) < 0.0f) { // if error is >90 degrees + return FusionVectorNormalise(FusionVectorCrossProduct(sensor, reference)); + } + return FusionVectorCrossProduct(sensor, reference); +} + +/** + * @brief Returns a value limited to maximum and minimum. + * @param value Value. + * @param min Minimum value. + * @param max Maximum value. + * @return Value limited to maximum and minimum. + */ +static inline int Clamp(const int value, const int min, const int max) { + if (value < min) { + return min; + } + if (value > max) { + return max; + } + return value; +} + +/** + * @brief Updates the AHRS algorithm using the gyroscope and accelerometer + * measurements only. + * @param ahrs AHRS algorithm structure. + * @param gyroscope Gyroscope measurement in degrees per second. + * @param accelerometer Accelerometer measurement in g. + * @param deltaTime Delta time in seconds. + */ +void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const float deltaTime) { + + // Update AHRS algorithm + FusionAhrsUpdate(ahrs, gyroscope, accelerometer, FUSION_VECTOR_ZERO, deltaTime); + + // Zero heading during initialisation + if (ahrs->initialising == true) { + FusionAhrsSetHeading(ahrs, 0.0f); + } +} + +/** + * @brief Updates the AHRS algorithm using the gyroscope, accelerometer, and + * heading measurements. + * @param ahrs AHRS algorithm structure. + * @param gyroscope Gyroscope measurement in degrees per second. + * @param accelerometer Accelerometer measurement in g. + * @param heading Heading measurement in degrees. + * @param deltaTime Delta time in seconds. + */ +void FusionAhrsUpdateExternalHeading(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const float heading, const float deltaTime) { +#define Q ahrs->quaternion.element + + // Calculate roll + const float roll = atan2f(Q.w * Q.x + Q.y * Q.z, 0.5f - Q.y * Q.y - Q.x * Q.x); + + // Calculate magnetometer + const float headingRadians = FusionDegreesToRadians(heading); + const float sinHeadingRadians = sinf(headingRadians); + const FusionVector magnetometer = {.axis = { + .x = cosf(headingRadians), + .y = -1.0f * cosf(roll) * sinHeadingRadians, + .z = sinHeadingRadians * sinf(roll), + }}; + + // Update AHRS algorithm + FusionAhrsUpdate(ahrs, gyroscope, accelerometer, magnetometer, deltaTime); +#undef Q +} + +/** + * @brief Returns the quaternion describing the sensor relative to the Earth. + * @param ahrs AHRS algorithm structure. + * @return Quaternion describing the sensor relative to the Earth. + */ +FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs *const ahrs) { + return ahrs->quaternion; +} + +/** + * @brief Sets the quaternion describing the sensor relative to the Earth. + * @param ahrs AHRS algorithm structure. + * @param quaternion Quaternion describing the sensor relative to the Earth. + */ +void FusionAhrsSetQuaternion(FusionAhrs *const ahrs, const FusionQuaternion quaternion) { + ahrs->quaternion = quaternion; +} + +/** + * @brief Returns the linear acceleration measurement equal to the accelerometer + * measurement with the 1 g of gravity removed. + * @param ahrs AHRS algorithm structure. + * @return Linear acceleration measurement in g. + */ +FusionVector FusionAhrsGetLinearAcceleration(const FusionAhrs *const ahrs) { +#define Q ahrs->quaternion.element + + // Calculate gravity in the sensor coordinate frame + const FusionVector gravity = {.axis = { + .x = 2.0f * (Q.x * Q.z - Q.w * Q.y), + .y = 2.0f * (Q.y * Q.z + Q.w * Q.x), + .z = 2.0f * (Q.w * Q.w - 0.5f + Q.z * Q.z), + }}; // third column of transposed rotation matrix + + // Remove gravity from accelerometer measurement + switch (ahrs->settings.convention) { + case FusionConventionNwu: + case FusionConventionEnu: { + return FusionVectorSubtract(ahrs->accelerometer, gravity); + } + case FusionConventionNed: { + return FusionVectorAdd(ahrs->accelerometer, gravity); + } + } + return FUSION_VECTOR_ZERO; // avoid compiler warning +#undef Q +} + +/** + * @brief Returns the Earth acceleration measurement equal to accelerometer + * measurement in the Earth coordinate frame with the 1 g of gravity removed. + * @param ahrs AHRS algorithm structure. + * @return Earth acceleration measurement in g. + */ +FusionVector FusionAhrsGetEarthAcceleration(const FusionAhrs *const ahrs) { +#define Q ahrs->quaternion.element +#define A ahrs->accelerometer.axis + + // Calculate accelerometer measurement in the Earth coordinate frame + const float qwqw = Q.w * Q.w; // calculate common terms to avoid repeated operations + const float qwqx = Q.w * Q.x; + const float qwqy = Q.w * Q.y; + const float qwqz = Q.w * Q.z; + const float qxqy = Q.x * Q.y; + const float qxqz = Q.x * Q.z; + const float qyqz = Q.y * Q.z; + FusionVector accelerometer = {.axis = { + .x = 2.0f * ((qwqw - 0.5f + Q.x * Q.x) * A.x + (qxqy - qwqz) * A.y + (qxqz + qwqy) * A.z), + .y = 2.0f * ((qxqy + qwqz) * A.x + (qwqw - 0.5f + Q.y * Q.y) * A.y + (qyqz - qwqx) * A.z), + .z = 2.0f * ((qxqz - qwqy) * A.x + (qyqz + qwqx) * A.y + (qwqw - 0.5f + Q.z * Q.z) * A.z), + }}; // rotation matrix multiplied with the accelerometer + + // Remove gravity from accelerometer measurement + switch (ahrs->settings.convention) { + case FusionConventionNwu: + case FusionConventionEnu: + accelerometer.axis.z -= 1.0f; + break; + case FusionConventionNed: + accelerometer.axis.z += 1.0f; + break; + } + return accelerometer; +#undef Q +#undef A +} + +/** + * @brief Returns the AHRS algorithm internal states. + * @param ahrs AHRS algorithm structure. + * @return AHRS algorithm internal states. + */ +FusionAhrsInternalStates FusionAhrsGetInternalStates(const FusionAhrs *const ahrs) { + const FusionAhrsInternalStates internalStates = { + .accelerationError = FusionRadiansToDegrees(FusionAsin(2.0f * FusionVectorMagnitude(ahrs->halfAccelerometerFeedback))), + .accelerometerIgnored = ahrs->accelerometerIgnored, + .accelerationRecoveryTrigger = ahrs->settings.recoveryTriggerPeriod == 0 ? 0.0f : (float) ahrs->accelerationRecoveryTrigger / (float) ahrs->settings.recoveryTriggerPeriod, + .magneticError = FusionRadiansToDegrees(FusionAsin(2.0f * FusionVectorMagnitude(ahrs->halfMagnetometerFeedback))), + .magnetometerIgnored = ahrs->magnetometerIgnored, + .magneticRecoveryTrigger = ahrs->settings.recoveryTriggerPeriod == 0 ? 0.0f : (float) ahrs->magneticRecoveryTrigger / (float) ahrs->settings.recoveryTriggerPeriod, + }; + return internalStates; +} + +/** + * @brief Returns the AHRS algorithm flags. + * @param ahrs AHRS algorithm structure. + * @return AHRS algorithm flags. + */ +FusionAhrsFlags FusionAhrsGetFlags(const FusionAhrs *const ahrs) { + const FusionAhrsFlags flags = { + .initialising = ahrs->initialising, + .angularRateRecovery = ahrs->angularRateRecovery, + .accelerationRecovery = ahrs->accelerationRecoveryTrigger > ahrs->accelerationRecoveryTimeout, + .magneticRecovery= ahrs->magneticRecoveryTrigger > ahrs->magneticRecoveryTimeout, + }; + return flags; +} + +/** + * @brief Sets the heading of the orientation measurement provided by the AHRS + * algorithm. This function can be used to reset drift in heading when the AHRS + * algorithm is being used without a magnetometer. + * @param ahrs AHRS algorithm structure. + * @param heading Heading angle in degrees. + */ +void FusionAhrsSetHeading(FusionAhrs *const ahrs, const float heading) { +#define Q ahrs->quaternion.element + const float yaw = atan2f(Q.w * Q.z + Q.x * Q.y, 0.5f - Q.y * Q.y - Q.z * Q.z); + const float halfYawMinusHeading = 0.5f * (yaw - FusionDegreesToRadians(heading)); + const FusionQuaternion rotation = {.element = { + .w = cosf(halfYawMinusHeading), + .x = 0.0f, + .y = 0.0f, + .z = -1.0f * sinf(halfYawMinusHeading), + }}; + ahrs->quaternion = FusionQuaternionMultiply(rotation, ahrs->quaternion); +#undef Q +} + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionAhrs.h b/lib/Fusion/FusionAhrs.h new file mode 100644 index 0000000..15473ad --- /dev/null +++ b/lib/Fusion/FusionAhrs.h @@ -0,0 +1,109 @@ +/** + * @file FusionAhrs.h + * @author Seb Madgwick + * @brief AHRS algorithm to combine gyroscope, accelerometer, and magnetometer + * measurements into a single measurement of orientation relative to the Earth. + */ + +#ifndef FUSION_AHRS_H +#define FUSION_AHRS_H + +//------------------------------------------------------------------------------ +// Includes + +#include "FusionConvention.h" +#include "FusionMath.h" +#include + +//------------------------------------------------------------------------------ +// Definitions + +/** + * @brief AHRS algorithm settings. + */ +typedef struct { + FusionConvention convention; + float gain; + float gyroscopeRange; + float accelerationRejection; + float magneticRejection; + unsigned int recoveryTriggerPeriod; +} FusionAhrsSettings; + +/** + * @brief AHRS algorithm structure. Structure members are used internally and + * must not be accessed by the application. + */ +typedef struct { + FusionAhrsSettings settings; + FusionQuaternion quaternion; + FusionVector accelerometer; + bool initialising; + float rampedGain; + float rampedGainStep; + bool angularRateRecovery; + FusionVector halfAccelerometerFeedback; + FusionVector halfMagnetometerFeedback; + bool accelerometerIgnored; + int accelerationRecoveryTrigger; + int accelerationRecoveryTimeout; + bool magnetometerIgnored; + int magneticRecoveryTrigger; + int magneticRecoveryTimeout; +} FusionAhrs; + +/** + * @brief AHRS algorithm internal states. + */ +typedef struct { + float accelerationError; + bool accelerometerIgnored; + float accelerationRecoveryTrigger; + float magneticError; + bool magnetometerIgnored; + float magneticRecoveryTrigger; +} FusionAhrsInternalStates; + +/** + * @brief AHRS algorithm flags. + */ +typedef struct { + bool initialising; + bool angularRateRecovery; + bool accelerationRecovery; + bool magneticRecovery; +} FusionAhrsFlags; + +//------------------------------------------------------------------------------ +// Function declarations + +void FusionAhrsInitialise(FusionAhrs *const ahrs); + +void FusionAhrsReset(FusionAhrs *const ahrs); + +void FusionAhrsSetSettings(FusionAhrs *const ahrs, const FusionAhrsSettings *const settings); + +void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const FusionVector magnetometer, const float deltaTime); + +void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const float deltaTime); + +void FusionAhrsUpdateExternalHeading(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const float heading, const float deltaTime); + +FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs *const ahrs); + +void FusionAhrsSetQuaternion(FusionAhrs *const ahrs, const FusionQuaternion quaternion); + +FusionVector FusionAhrsGetLinearAcceleration(const FusionAhrs *const ahrs); + +FusionVector FusionAhrsGetEarthAcceleration(const FusionAhrs *const ahrs); + +FusionAhrsInternalStates FusionAhrsGetInternalStates(const FusionAhrs *const ahrs); + +FusionAhrsFlags FusionAhrsGetFlags(const FusionAhrs *const ahrs); + +void FusionAhrsSetHeading(FusionAhrs *const ahrs, const float heading); + +#endif + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionAxes.h b/lib/Fusion/FusionAxes.h new file mode 100644 index 0000000..32c07db --- /dev/null +++ b/lib/Fusion/FusionAxes.h @@ -0,0 +1,187 @@ +/** + * @file FusionAxes.h + * @author Seb Madgwick + * @brief Swaps sensor axes for alignment with the body axes. + */ + +#ifndef FUSION_AXES_H +#define FUSION_AXES_H + +//------------------------------------------------------------------------------ +// Includes + +#include "FusionMath.h" + +//------------------------------------------------------------------------------ +// Definitions + +/** + * @brief Axes alignment describing the sensor axes relative to the body axes. + * For example, if the body X axis is aligned with the sensor Y axis and the + * body Y axis is aligned with sensor X axis but pointing the opposite direction + * then alignment is +Y-X+Z. + */ +typedef enum { + FusionAxesAlignmentPXPYPZ, /* +X+Y+Z */ + FusionAxesAlignmentPXNZPY, /* +X-Z+Y */ + FusionAxesAlignmentPXNYNZ, /* +X-Y-Z */ + FusionAxesAlignmentPXPZNY, /* +X+Z-Y */ + FusionAxesAlignmentNXPYNZ, /* -X+Y-Z */ + FusionAxesAlignmentNXPZPY, /* -X+Z+Y */ + FusionAxesAlignmentNXNYPZ, /* -X-Y+Z */ + FusionAxesAlignmentNXNZNY, /* -X-Z-Y */ + FusionAxesAlignmentPYNXPZ, /* +Y-X+Z */ + FusionAxesAlignmentPYNZNX, /* +Y-Z-X */ + FusionAxesAlignmentPYPXNZ, /* +Y+X-Z */ + FusionAxesAlignmentPYPZPX, /* +Y+Z+X */ + FusionAxesAlignmentNYPXPZ, /* -Y+X+Z */ + FusionAxesAlignmentNYNZPX, /* -Y-Z+X */ + FusionAxesAlignmentNYNXNZ, /* -Y-X-Z */ + FusionAxesAlignmentNYPZNX, /* -Y+Z-X */ + FusionAxesAlignmentPZPYNX, /* +Z+Y-X */ + FusionAxesAlignmentPZPXPY, /* +Z+X+Y */ + FusionAxesAlignmentPZNYPX, /* +Z-Y+X */ + FusionAxesAlignmentPZNXNY, /* +Z-X-Y */ + FusionAxesAlignmentNZPYPX, /* -Z+Y+X */ + FusionAxesAlignmentNZNXPY, /* -Z-X+Y */ + FusionAxesAlignmentNZNYNX, /* -Z-Y-X */ + FusionAxesAlignmentNZPXNY, /* -Z+X-Y */ +} FusionAxesAlignment; + +//------------------------------------------------------------------------------ +// Inline functions + +/** + * @brief Swaps sensor axes for alignment with the body axes. + * @param sensor Sensor axes. + * @param alignment Axes alignment. + * @return Sensor axes aligned with the body axes. + */ +static inline FusionVector FusionAxesSwap(const FusionVector sensor, const FusionAxesAlignment alignment) { + FusionVector result; + switch (alignment) { + case FusionAxesAlignmentPXPYPZ: + break; + case FusionAxesAlignmentPXNZPY: + result.axis.x = +sensor.axis.x; + result.axis.y = -sensor.axis.z; + result.axis.z = +sensor.axis.y; + return result; + case FusionAxesAlignmentPXNYNZ: + result.axis.x = +sensor.axis.x; + result.axis.y = -sensor.axis.y; + result.axis.z = -sensor.axis.z; + return result; + case FusionAxesAlignmentPXPZNY: + result.axis.x = +sensor.axis.x; + result.axis.y = +sensor.axis.z; + result.axis.z = -sensor.axis.y; + return result; + case FusionAxesAlignmentNXPYNZ: + result.axis.x = -sensor.axis.x; + result.axis.y = +sensor.axis.y; + result.axis.z = -sensor.axis.z; + return result; + case FusionAxesAlignmentNXPZPY: + result.axis.x = -sensor.axis.x; + result.axis.y = +sensor.axis.z; + result.axis.z = +sensor.axis.y; + return result; + case FusionAxesAlignmentNXNYPZ: + result.axis.x = -sensor.axis.x; + result.axis.y = -sensor.axis.y; + result.axis.z = +sensor.axis.z; + return result; + case FusionAxesAlignmentNXNZNY: + result.axis.x = -sensor.axis.x; + result.axis.y = -sensor.axis.z; + result.axis.z = -sensor.axis.y; + return result; + case FusionAxesAlignmentPYNXPZ: + result.axis.x = +sensor.axis.y; + result.axis.y = -sensor.axis.x; + result.axis.z = +sensor.axis.z; + return result; + case FusionAxesAlignmentPYNZNX: + result.axis.x = +sensor.axis.y; + result.axis.y = -sensor.axis.z; + result.axis.z = -sensor.axis.x; + return result; + case FusionAxesAlignmentPYPXNZ: + result.axis.x = +sensor.axis.y; + result.axis.y = +sensor.axis.x; + result.axis.z = -sensor.axis.z; + return result; + case FusionAxesAlignmentPYPZPX: + result.axis.x = +sensor.axis.y; + result.axis.y = +sensor.axis.z; + result.axis.z = +sensor.axis.x; + return result; + case FusionAxesAlignmentNYPXPZ: + result.axis.x = -sensor.axis.y; + result.axis.y = +sensor.axis.x; + result.axis.z = +sensor.axis.z; + return result; + case FusionAxesAlignmentNYNZPX: + result.axis.x = -sensor.axis.y; + result.axis.y = -sensor.axis.z; + result.axis.z = +sensor.axis.x; + return result; + case FusionAxesAlignmentNYNXNZ: + result.axis.x = -sensor.axis.y; + result.axis.y = -sensor.axis.x; + result.axis.z = -sensor.axis.z; + return result; + case FusionAxesAlignmentNYPZNX: + result.axis.x = -sensor.axis.y; + result.axis.y = +sensor.axis.z; + result.axis.z = -sensor.axis.x; + return result; + case FusionAxesAlignmentPZPYNX: + result.axis.x = +sensor.axis.z; + result.axis.y = +sensor.axis.y; + result.axis.z = -sensor.axis.x; + return result; + case FusionAxesAlignmentPZPXPY: + result.axis.x = +sensor.axis.z; + result.axis.y = +sensor.axis.x; + result.axis.z = +sensor.axis.y; + return result; + case FusionAxesAlignmentPZNYPX: + result.axis.x = +sensor.axis.z; + result.axis.y = -sensor.axis.y; + result.axis.z = +sensor.axis.x; + return result; + case FusionAxesAlignmentPZNXNY: + result.axis.x = +sensor.axis.z; + result.axis.y = -sensor.axis.x; + result.axis.z = -sensor.axis.y; + return result; + case FusionAxesAlignmentNZPYPX: + result.axis.x = -sensor.axis.z; + result.axis.y = +sensor.axis.y; + result.axis.z = +sensor.axis.x; + return result; + case FusionAxesAlignmentNZNXPY: + result.axis.x = -sensor.axis.z; + result.axis.y = -sensor.axis.x; + result.axis.z = +sensor.axis.y; + return result; + case FusionAxesAlignmentNZNYNX: + result.axis.x = -sensor.axis.z; + result.axis.y = -sensor.axis.y; + result.axis.z = -sensor.axis.x; + return result; + case FusionAxesAlignmentNZPXNY: + result.axis.x = -sensor.axis.z; + result.axis.y = +sensor.axis.x; + result.axis.z = -sensor.axis.y; + return result; + } + return sensor; // avoid compiler warning +} + +#endif + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionCalibration.h b/lib/Fusion/FusionCalibration.h new file mode 100644 index 0000000..733f572 --- /dev/null +++ b/lib/Fusion/FusionCalibration.h @@ -0,0 +1,44 @@ +/** + * @file FusionCalibration.h + * @author Seb Madgwick + * @brief Gyroscope, accelerometer, and magnetometer calibration models. + */ + +#ifndef FUSION_CALIBRATION_H +#define FUSION_CALIBRATION_H + +//------------------------------------------------------------------------------ +// Includes + +#include "FusionMath.h" + +//------------------------------------------------------------------------------ +// Inline functions + +/** + * @brief Gyroscope and accelerometer calibration model. + * @param uncalibrated Uncalibrated measurement. + * @param misalignment Misalignment matrix. + * @param sensitivity Sensitivity. + * @param offset Offset. + * @return Calibrated measurement. + */ +static inline FusionVector FusionCalibrationInertial(const FusionVector uncalibrated, const FusionMatrix misalignment, const FusionVector sensitivity, const FusionVector offset) { + return FusionMatrixMultiplyVector(misalignment, FusionVectorHadamardProduct(FusionVectorSubtract(uncalibrated, offset), sensitivity)); +} + +/** + * @brief Magnetometer calibration model. + * @param uncalibrated Uncalibrated measurement. + * @param softIronMatrix Soft-iron matrix. + * @param hardIronOffset Hard-iron offset. + * @return Calibrated measurement. + */ +static inline FusionVector FusionCalibrationMagnetic(const FusionVector uncalibrated, const FusionMatrix softIronMatrix, const FusionVector hardIronOffset) { + return FusionMatrixMultiplyVector(softIronMatrix, FusionVectorSubtract(uncalibrated, hardIronOffset)); +} + +#endif + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionCompass.c b/lib/Fusion/FusionCompass.c new file mode 100644 index 0000000..0ec99d1 --- /dev/null +++ b/lib/Fusion/FusionCompass.c @@ -0,0 +1,49 @@ +/** + * @file FusionCompass.c + * @author Seb Madgwick + * @brief Tilt-compensated compass to calculate the magnetic heading using + * accelerometer and magnetometer measurements. + */ + +//------------------------------------------------------------------------------ +// Includes + +#include "FusionAxes.h" +#include "FusionCompass.h" +#include // atan2f + +//------------------------------------------------------------------------------ +// Functions + +/** + * @brief Calculates the magnetic heading. + * @param convention Earth axes convention. + * @param accelerometer Accelerometer measurement in any calibrated units. + * @param magnetometer Magnetometer measurement in any calibrated units. + * @return Heading angle in degrees. + */ +float FusionCompassCalculateHeading(const FusionConvention convention, const FusionVector accelerometer, const FusionVector magnetometer) { + switch (convention) { + case FusionConventionNwu: { + const FusionVector west = FusionVectorNormalise(FusionVectorCrossProduct(accelerometer, magnetometer)); + const FusionVector north = FusionVectorNormalise(FusionVectorCrossProduct(west, accelerometer)); + return FusionRadiansToDegrees(atan2f(west.axis.x, north.axis.x)); + } + case FusionConventionEnu: { + const FusionVector west = FusionVectorNormalise(FusionVectorCrossProduct(accelerometer, magnetometer)); + const FusionVector north = FusionVectorNormalise(FusionVectorCrossProduct(west, accelerometer)); + const FusionVector east = FusionVectorMultiplyScalar(west, -1.0f); + return FusionRadiansToDegrees(atan2f(north.axis.x, east.axis.x)); + } + case FusionConventionNed: { + const FusionVector up = FusionVectorMultiplyScalar(accelerometer, -1.0f); + const FusionVector west = FusionVectorNormalise(FusionVectorCrossProduct(up, magnetometer)); + const FusionVector north = FusionVectorNormalise(FusionVectorCrossProduct(west, up)); + return FusionRadiansToDegrees(atan2f(west.axis.x, north.axis.x)); + } + } + return 0; // avoid compiler warning +} + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionCompass.h b/lib/Fusion/FusionCompass.h new file mode 100644 index 0000000..78326c0 --- /dev/null +++ b/lib/Fusion/FusionCompass.h @@ -0,0 +1,25 @@ +/** + * @file FusionCompass.h + * @author Seb Madgwick + * @brief Tilt-compensated compass to calculate the magnetic heading using + * accelerometer and magnetometer measurements. + */ + +#ifndef FUSION_COMPASS_H +#define FUSION_COMPASS_H + +//------------------------------------------------------------------------------ +// Includes + +#include "FusionConvention.h" +#include "FusionMath.h" + +//------------------------------------------------------------------------------ +// Function declarations + +float FusionCompassCalculateHeading(const FusionConvention convention, const FusionVector accelerometer, const FusionVector magnetometer); + +#endif + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionConvention.h b/lib/Fusion/FusionConvention.h new file mode 100644 index 0000000..0b0d43a --- /dev/null +++ b/lib/Fusion/FusionConvention.h @@ -0,0 +1,25 @@ +/** + * @file FusionConvention.h + * @author Seb Madgwick + * @brief Earth axes convention. + */ + +#ifndef FUSION_CONVENTION_H +#define FUSION_CONVENTION_H + +//------------------------------------------------------------------------------ +// Definitions + +/** + * @brief Earth axes convention. + */ +typedef enum { + FusionConventionNwu, /* North-West-Up */ + FusionConventionEnu, /* East-North-Up */ + FusionConventionNed, /* North-East-Down */ +} FusionConvention; + +#endif + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionMath.h b/lib/Fusion/FusionMath.h new file mode 100644 index 0000000..6765d29 --- /dev/null +++ b/lib/Fusion/FusionMath.h @@ -0,0 +1,481 @@ +/** + * @file FusionMath.h + * @author Seb Madgwick + * @brief Math library. + */ + +#ifndef FUSION_MATH_H +#define FUSION_MATH_H + +//------------------------------------------------------------------------------ +// Includes + +#include // M_PI, sqrtf, atan2f, asinf +#include +#include + +//------------------------------------------------------------------------------ +// Definitions + +/** + * @brief 3D vector. + */ +typedef union { + float array[3]; + + struct { + float x; + float y; + float z; + } axis; +} FusionVector; + +/** + * @brief Quaternion. + */ +typedef union { + float array[4]; + + struct { + float w; + float x; + float y; + float z; + } element; +} FusionQuaternion; + +/** + * @brief 3x3 matrix in row-major order. + * See http://en.wikipedia.org/wiki/Row-major_order + */ +typedef union { + float array[3][3]; + + struct { + float xx; + float xy; + float xz; + float yx; + float yy; + float yz; + float zx; + float zy; + float zz; + } element; +} FusionMatrix; + +/** + * @brief Euler angles. Roll, pitch, and yaw correspond to rotations around + * X, Y, and Z respectively. + */ +typedef union { + float array[3]; + + struct { + float roll; + float pitch; + float yaw; + } angle; +} FusionEuler; + +/** + * @brief Vector of zeros. + */ +#define FUSION_VECTOR_ZERO ((FusionVector){ .array = {0.0f, 0.0f, 0.0f} }) + +/** + * @brief Vector of ones. + */ +#define FUSION_VECTOR_ONES ((FusionVector){ .array = {1.0f, 1.0f, 1.0f} }) + +/** + * @brief Identity quaternion. + */ +#define FUSION_IDENTITY_QUATERNION ((FusionQuaternion){ .array = {1.0f, 0.0f, 0.0f, 0.0f} }) + +/** + * @brief Identity matrix. + */ +#define FUSION_IDENTITY_MATRIX ((FusionMatrix){ .array = {{1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}} }) + +/** + * @brief Euler angles of zero. + */ +#define FUSION_EULER_ZERO ((FusionEuler){ .array = {0.0f, 0.0f, 0.0f} }) + +/** + * @brief Pi. May not be defined in math.h. + */ +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +/** + * @brief Include this definition or add as a preprocessor definition to use + * normal square root operations. + */ +//#define FUSION_USE_NORMAL_SQRT + +//------------------------------------------------------------------------------ +// Inline functions - Degrees and radians conversion + +/** + * @brief Converts degrees to radians. + * @param degrees Degrees. + * @return Radians. + */ +static inline float FusionDegreesToRadians(const float degrees) { + return degrees * ((float) M_PI / 180.0f); +} + +/** + * @brief Converts radians to degrees. + * @param radians Radians. + * @return Degrees. + */ +static inline float FusionRadiansToDegrees(const float radians) { + return radians * (180.0f / (float) M_PI); +} + +//------------------------------------------------------------------------------ +// Inline functions - Arc sine + +/** + * @brief Returns the arc sine of the value. + * @param value Value. + * @return Arc sine of the value. + */ +static inline float FusionAsin(const float value) { + if (value <= -1.0f) { + return (float) M_PI / -2.0f; + } + if (value >= 1.0f) { + return (float) M_PI / 2.0f; + } + return asinf(value); +} + +//------------------------------------------------------------------------------ +// Inline functions - Fast inverse square root + +#ifndef FUSION_USE_NORMAL_SQRT + +/** + * @brief Calculates the reciprocal of the square root. + * See https://pizer.wordpress.com/2008/10/12/fast-inverse-square-root/ + * @param x Operand. + * @return Reciprocal of the square root of x. + */ +static inline float FusionFastInverseSqrt(const float x) { + + typedef union { + float f; + int32_t i; + } Union32; + + Union32 union32 = {.f = x}; + union32.i = 0x5F1F1412 - (union32.i >> 1); + return union32.f * (1.69000231f - 0.714158168f * x * union32.f * union32.f); +} + +#endif + +//------------------------------------------------------------------------------ +// Inline functions - Vector operations + +/** + * @brief Returns true if the vector is zero. + * @param vector Vector. + * @return True if the vector is zero. + */ +static inline bool FusionVectorIsZero(const FusionVector vector) { + return (vector.axis.x == 0.0f) && (vector.axis.y == 0.0f) && (vector.axis.z == 0.0f); +} + +/** + * @brief Returns the sum of two vectors. + * @param vectorA Vector A. + * @param vectorB Vector B. + * @return Sum of two vectors. + */ +static inline FusionVector FusionVectorAdd(const FusionVector vectorA, const FusionVector vectorB) { + const FusionVector result = {.axis = { + .x = vectorA.axis.x + vectorB.axis.x, + .y = vectorA.axis.y + vectorB.axis.y, + .z = vectorA.axis.z + vectorB.axis.z, + }}; + return result; +} + +/** + * @brief Returns vector B subtracted from vector A. + * @param vectorA Vector A. + * @param vectorB Vector B. + * @return Vector B subtracted from vector A. + */ +static inline FusionVector FusionVectorSubtract(const FusionVector vectorA, const FusionVector vectorB) { + const FusionVector result = {.axis = { + .x = vectorA.axis.x - vectorB.axis.x, + .y = vectorA.axis.y - vectorB.axis.y, + .z = vectorA.axis.z - vectorB.axis.z, + }}; + return result; +} + +/** + * @brief Returns the sum of the elements. + * @param vector Vector. + * @return Sum of the elements. + */ +static inline float FusionVectorSum(const FusionVector vector) { + return vector.axis.x + vector.axis.y + vector.axis.z; +} + +/** + * @brief Returns the multiplication of a vector by a scalar. + * @param vector Vector. + * @param scalar Scalar. + * @return Multiplication of a vector by a scalar. + */ +static inline FusionVector FusionVectorMultiplyScalar(const FusionVector vector, const float scalar) { + const FusionVector result = {.axis = { + .x = vector.axis.x * scalar, + .y = vector.axis.y * scalar, + .z = vector.axis.z * scalar, + }}; + return result; +} + +/** + * @brief Calculates the Hadamard product (element-wise multiplication). + * @param vectorA Vector A. + * @param vectorB Vector B. + * @return Hadamard product. + */ +static inline FusionVector FusionVectorHadamardProduct(const FusionVector vectorA, const FusionVector vectorB) { + const FusionVector result = {.axis = { + .x = vectorA.axis.x * vectorB.axis.x, + .y = vectorA.axis.y * vectorB.axis.y, + .z = vectorA.axis.z * vectorB.axis.z, + }}; + return result; +} + +/** + * @brief Returns the cross product. + * @param vectorA Vector A. + * @param vectorB Vector B. + * @return Cross product. + */ +static inline FusionVector FusionVectorCrossProduct(const FusionVector vectorA, const FusionVector vectorB) { +#define A vectorA.axis +#define B vectorB.axis + const FusionVector result = {.axis = { + .x = A.y * B.z - A.z * B.y, + .y = A.z * B.x - A.x * B.z, + .z = A.x * B.y - A.y * B.x, + }}; + return result; +#undef A +#undef B +} + +/** + * @brief Returns the dot product. + * @param vectorA Vector A. + * @param vectorB Vector B. + * @return Dot product. + */ +static inline float FusionVectorDotProduct(const FusionVector vectorA, const FusionVector vectorB) { + return FusionVectorSum(FusionVectorHadamardProduct(vectorA, vectorB)); +} + +/** + * @brief Returns the vector magnitude squared. + * @param vector Vector. + * @return Vector magnitude squared. + */ +static inline float FusionVectorMagnitudeSquared(const FusionVector vector) { + return FusionVectorSum(FusionVectorHadamardProduct(vector, vector)); +} + +/** + * @brief Returns the vector magnitude. + * @param vector Vector. + * @return Vector magnitude. + */ +static inline float FusionVectorMagnitude(const FusionVector vector) { + return sqrtf(FusionVectorMagnitudeSquared(vector)); +} + +/** + * @brief Returns the normalised vector. + * @param vector Vector. + * @return Normalised vector. + */ +static inline FusionVector FusionVectorNormalise(const FusionVector vector) { +#ifdef FUSION_USE_NORMAL_SQRT + const float magnitudeReciprocal = 1.0f / sqrtf(FusionVectorMagnitudeSquared(vector)); +#else + const float magnitudeReciprocal = FusionFastInverseSqrt(FusionVectorMagnitudeSquared(vector)); +#endif + return FusionVectorMultiplyScalar(vector, magnitudeReciprocal); +} + +//------------------------------------------------------------------------------ +// Inline functions - Quaternion operations + +/** + * @brief Returns the sum of two quaternions. + * @param quaternionA Quaternion A. + * @param quaternionB Quaternion B. + * @return Sum of two quaternions. + */ +static inline FusionQuaternion FusionQuaternionAdd(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB) { + const FusionQuaternion result = {.element = { + .w = quaternionA.element.w + quaternionB.element.w, + .x = quaternionA.element.x + quaternionB.element.x, + .y = quaternionA.element.y + quaternionB.element.y, + .z = quaternionA.element.z + quaternionB.element.z, + }}; + return result; +} + +/** + * @brief Returns the multiplication of two quaternions. + * @param quaternionA Quaternion A (to be post-multiplied). + * @param quaternionB Quaternion B (to be pre-multiplied). + * @return Multiplication of two quaternions. + */ +static inline FusionQuaternion FusionQuaternionMultiply(const FusionQuaternion quaternionA, const FusionQuaternion quaternionB) { +#define A quaternionA.element +#define B quaternionB.element + const FusionQuaternion result = {.element = { + .w = A.w * B.w - A.x * B.x - A.y * B.y - A.z * B.z, + .x = A.w * B.x + A.x * B.w + A.y * B.z - A.z * B.y, + .y = A.w * B.y - A.x * B.z + A.y * B.w + A.z * B.x, + .z = A.w * B.z + A.x * B.y - A.y * B.x + A.z * B.w, + }}; + return result; +#undef A +#undef B +} + +/** + * @brief Returns the multiplication of a quaternion with a vector. This is a + * normal quaternion multiplication where the vector is treated a + * quaternion with a W element value of zero. The quaternion is post- + * multiplied by the vector. + * @param quaternion Quaternion. + * @param vector Vector. + * @return Multiplication of a quaternion with a vector. + */ +static inline FusionQuaternion FusionQuaternionMultiplyVector(const FusionQuaternion quaternion, const FusionVector vector) { +#define Q quaternion.element +#define V vector.axis + const FusionQuaternion result = {.element = { + .w = -Q.x * V.x - Q.y * V.y - Q.z * V.z, + .x = Q.w * V.x + Q.y * V.z - Q.z * V.y, + .y = Q.w * V.y - Q.x * V.z + Q.z * V.x, + .z = Q.w * V.z + Q.x * V.y - Q.y * V.x, + }}; + return result; +#undef Q +#undef V +} + +/** + * @brief Returns the normalised quaternion. + * @param quaternion Quaternion. + * @return Normalised quaternion. + */ +static inline FusionQuaternion FusionQuaternionNormalise(const FusionQuaternion quaternion) { +#define Q quaternion.element +#ifdef FUSION_USE_NORMAL_SQRT + const float magnitudeReciprocal = 1.0f / sqrtf(Q.w * Q.w + Q.x * Q.x + Q.y * Q.y + Q.z * Q.z); +#else + const float magnitudeReciprocal = FusionFastInverseSqrt(Q.w * Q.w + Q.x * Q.x + Q.y * Q.y + Q.z * Q.z); +#endif + const FusionQuaternion result = {.element = { + .w = Q.w * magnitudeReciprocal, + .x = Q.x * magnitudeReciprocal, + .y = Q.y * magnitudeReciprocal, + .z = Q.z * magnitudeReciprocal, + }}; + return result; +#undef Q +} + +//------------------------------------------------------------------------------ +// Inline functions - Matrix operations + +/** + * @brief Returns the multiplication of a matrix with a vector. + * @param matrix Matrix. + * @param vector Vector. + * @return Multiplication of a matrix with a vector. + */ +static inline FusionVector FusionMatrixMultiplyVector(const FusionMatrix matrix, const FusionVector vector) { +#define R matrix.element + const FusionVector result = {.axis = { + .x = R.xx * vector.axis.x + R.xy * vector.axis.y + R.xz * vector.axis.z, + .y = R.yx * vector.axis.x + R.yy * vector.axis.y + R.yz * vector.axis.z, + .z = R.zx * vector.axis.x + R.zy * vector.axis.y + R.zz * vector.axis.z, + }}; + return result; +#undef R +} + +//------------------------------------------------------------------------------ +// Inline functions - Conversion operations + +/** + * @brief Converts a quaternion to a rotation matrix. + * @param quaternion Quaternion. + * @return Rotation matrix. + */ +static inline FusionMatrix FusionQuaternionToMatrix(const FusionQuaternion quaternion) { +#define Q quaternion.element + const float qwqw = Q.w * Q.w; // calculate common terms to avoid repeated operations + const float qwqx = Q.w * Q.x; + const float qwqy = Q.w * Q.y; + const float qwqz = Q.w * Q.z; + const float qxqy = Q.x * Q.y; + const float qxqz = Q.x * Q.z; + const float qyqz = Q.y * Q.z; + const FusionMatrix matrix = {.element = { + .xx = 2.0f * (qwqw - 0.5f + Q.x * Q.x), + .xy = 2.0f * (qxqy - qwqz), + .xz = 2.0f * (qxqz + qwqy), + .yx = 2.0f * (qxqy + qwqz), + .yy = 2.0f * (qwqw - 0.5f + Q.y * Q.y), + .yz = 2.0f * (qyqz - qwqx), + .zx = 2.0f * (qxqz - qwqy), + .zy = 2.0f * (qyqz + qwqx), + .zz = 2.0f * (qwqw - 0.5f + Q.z * Q.z), + }}; + return matrix; +#undef Q +} + +/** + * @brief Converts a quaternion to ZYX Euler angles in degrees. + * @param quaternion Quaternion. + * @return Euler angles in degrees. + */ +static inline FusionEuler FusionQuaternionToEuler(const FusionQuaternion quaternion) { +#define Q quaternion.element + const float halfMinusQySquared = 0.5f - Q.y * Q.y; // calculate common terms to avoid repeated operations + const FusionEuler euler = {.angle = { + .roll = FusionRadiansToDegrees(atan2f(Q.w * Q.x + Q.y * Q.z, halfMinusQySquared - Q.x * Q.x)), + .pitch = FusionRadiansToDegrees(FusionAsin(2.0f * (Q.w * Q.y - Q.z * Q.x))), + .yaw = FusionRadiansToDegrees(atan2f(Q.w * Q.z + Q.x * Q.y, halfMinusQySquared - Q.z * Q.z)), + }}; + return euler; +#undef Q +} + +#endif + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionOffset.c b/lib/Fusion/FusionOffset.c new file mode 100644 index 0000000..b21794d --- /dev/null +++ b/lib/Fusion/FusionOffset.c @@ -0,0 +1,77 @@ +/** + * @file FusionOffset.c + * @author Seb Madgwick + * @brief Gyroscope offset correction algorithm for run-time calibration of the + * gyroscope offset. + */ + +//------------------------------------------------------------------------------ +// Includes + +#include "FusionOffset.h" +#include // fabs + +//------------------------------------------------------------------------------ +// Definitions + +/** + * @brief Cutoff frequency in Hz. + */ +#define CUTOFF_FREQUENCY (0.02f) + +/** + * @brief Timeout in seconds. + */ +#define TIMEOUT (5) + +/** + * @brief Threshold in degrees per second. + */ +#define THRESHOLD (3.0f) + +//------------------------------------------------------------------------------ +// Functions + +/** + * @brief Initialises the gyroscope offset algorithm. + * @param offset Gyroscope offset algorithm structure. + * @param sampleRate Sample rate in Hz. + */ +void FusionOffsetInitialise(FusionOffset *const offset, const unsigned int sampleRate) { + offset->filterCoefficient = 2.0f * (float) M_PI * CUTOFF_FREQUENCY * (1.0f / (float) sampleRate); + offset->timeout = TIMEOUT * sampleRate; + offset->timer = 0; + offset->gyroscopeOffset = FUSION_VECTOR_ZERO; +} + +/** + * @brief Updates the gyroscope offset algorithm and returns the corrected + * gyroscope measurement. + * @param offset Gyroscope offset algorithm structure. + * @param gyroscope Gyroscope measurement in degrees per second. + * @return Corrected gyroscope measurement in degrees per second. + */ +FusionVector FusionOffsetUpdate(FusionOffset *const offset, FusionVector gyroscope) { + + // Subtract offset from gyroscope measurement + gyroscope = FusionVectorSubtract(gyroscope, offset->gyroscopeOffset); + + // Reset timer if gyroscope not stationary + if ((fabs(gyroscope.axis.x) > THRESHOLD) || (fabs(gyroscope.axis.y) > THRESHOLD) || (fabs(gyroscope.axis.z) > THRESHOLD)) { + offset->timer = 0; + return gyroscope; + } + + // Increment timer while gyroscope stationary + if (offset->timer < offset->timeout) { + offset->timer++; + return gyroscope; + } + + // Adjust offset if timer has elapsed + offset->gyroscopeOffset = FusionVectorAdd(offset->gyroscopeOffset, FusionVectorMultiplyScalar(gyroscope, offset->filterCoefficient)); + return gyroscope; +} + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/Fusion/FusionOffset.h b/lib/Fusion/FusionOffset.h new file mode 100644 index 0000000..51ae4a8 --- /dev/null +++ b/lib/Fusion/FusionOffset.h @@ -0,0 +1,40 @@ +/** + * @file FusionOffset.h + * @author Seb Madgwick + * @brief Gyroscope offset correction algorithm for run-time calibration of the + * gyroscope offset. + */ + +#ifndef FUSION_OFFSET_H +#define FUSION_OFFSET_H + +//------------------------------------------------------------------------------ +// Includes + +#include "FusionMath.h" + +//------------------------------------------------------------------------------ +// Definitions + +/** + * @brief Gyroscope offset algorithm structure. Structure members are used + * internally and must not be accessed by the application. + */ +typedef struct { + float filterCoefficient; + unsigned int timeout; + unsigned int timer; + FusionVector gyroscopeOffset; +} FusionOffset; + +//------------------------------------------------------------------------------ +// Function declarations + +void FusionOffsetInitialise(FusionOffset *const offset, const unsigned int sampleRate); + +FusionVector FusionOffsetUpdate(FusionOffset *const offset, FusionVector gyroscope); + +#endif + +//------------------------------------------------------------------------------ +// End of file diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp new file mode 100644 index 0000000..e347f72 --- /dev/null +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -0,0 +1,219 @@ +#include +#include "common.h" + +#if defined(PIN_SCL) +#include + +#include "devHeadTracker.h" + +#include "logging.h" + +#include "ICM42670P.h" +#include "QMC5883LCompass.h" + +#include "Fusion.h" + +static ICM42670P IMU(Wire,0); +static QMC5883LCompass compass; +static FusionAhrs ahrs; + +static float aRes; +static float gRes; +static float mRes; +static volatile uint8_t irq_received = 0; + +static IRAM_ATTR void irq_handler(void) { + irq_received = 1; +} + +static void initialize() +{ + Wire.begin(PIN_SDA, PIN_SCL); + + // Compass init first + compass.init(); + compass.setMode(0x01,0x08,0x10,0X00); // continuous, 100Hz, 8G, 512 over sample + + // Initializing the ICM42607C + int ret = IMU.begin(); + if (ret != 0) { + DBGLN("ICM42607C initialization failed: %d", ret); + while(1); + } + if ((ret = IMU.enableDataInterrupt(9, irq_handler))) { + DBGLN("Interrupt enable failed: %d"); + while(1); + } + // Accel ODR = 100 Hz and Full Scale Range = 16G + IMU.startAccel(100, 16); + aRes = 16.0/32768; + // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps + IMU.startGyro(100, 2000); + gRes = 2000.0/32768; + + FusionAhrsInitialise(&ahrs); + // Set AHRS algorithm settings + const FusionAhrsSettings settings = { + .convention = FusionConventionNwu, + .gain = 0.5f, + .gyroscopeRange = 2000.0f, /* replace this with actual gyroscope range in degrees/s */ + .accelerationRejection = 10.0f, + .magneticRejection = 10.0f, + .recoveryTriggerPeriod = 5 * 100, /* 5 seconds */ + }; + FusionAhrsSetSettings(&ahrs, &settings); +} + +static int start() +{ + //TODO: load compass calibration settings from config + compass.setCalibrationOffsets(0,0,0); + compass.setCalibrationScales(0,0,0); + //TODO: load gyro/accel calibration from settings + //TODO: load orientation from settings + return DURATION_IMMEDIATELY; +} + +// Normalizes any number to an arbitrary range +// by assuming the range wraps around when going below min or above max +static float normalize(float value, float start, float end) { + float width = end - start; // + float offsetValue = value - start; // value relative to 0 + + return (offsetValue - (floor(offsetValue / width) * width)) + start; + // + start to reset back to start of original range +} + +// Rotate a point (pn) in space in Order X -> Y -> Z +static void rotate(float pn[3], const float rot[3]) { + float out[3]; + + // X-axis Rotation + if (rot[0] != 0) { + out[0] = pn[0] * 1 + pn[1] * 0 + pn[2] * 0; + out[1] = pn[0] * 0 + pn[1] * cos(rot[0]) - pn[2] * sin(rot[0]); + out[2] = pn[0] * 0 + pn[1] * sin(rot[0]) + pn[2] * cos(rot[0]); + memcpy(pn, out, sizeof(out[0]) * 3); + } + + // Y-axis Rotation + if (rot[1] != 0) { + out[0] = pn[0] * cos(rot[1]) - pn[1] * 0 + pn[2] * sin(rot[1]); + out[1] = pn[0] * 0 + pn[1] * 1 + pn[2] * 0; + out[2] = -pn[0] * sin(rot[1]) + pn[1] * 0 + pn[2] * cos(rot[1]); + memcpy(pn, out, sizeof(out[0]) * 3); + } + + // Z-axis Rotation + if (rot[2] != 0) { + out[0] = pn[0] * cos(rot[2]) - pn[1] * sin(rot[2]) + pn[2] * 0; + out[1] = pn[0] * sin(rot[2]) + pn[1] * cos(rot[2]) + pn[2] * 0; + out[2] = pn[0] * 0 + pn[1] * 0 + pn[2] * 1; + memcpy(pn, out, sizeof(out[0]) * 3); + } +} + +static float orientation[3] = {0.0, 0.0, 0.0}; +static FusionEuler euler; +static float rollHome = 0, pitchHome = 0, yawHome = 0; + +static int timeout() +{ + static boolean running = true; + static int counter = 0; + + if(irq_received) { + irq_received = 0; + + inv_imu_sensor_event_t imu_event; + IMU.getDataFromRegisters(&imu_event); + + FusionVector a; + a.axis.x = imu_event.accel[0] * aRes; + a.axis.y = imu_event.accel[1] * aRes; + a.axis.z = imu_event.accel[2] * aRes; + rotate(a.array, orientation); + + FusionVector g; + g.axis.x = imu_event.gyro[0] * gRes; + g.axis.y = imu_event.gyro[1] * gRes; + g.axis.z = imu_event.gyro[2] * gRes; + rotate(g.array, orientation); + + compass.read(); + + FusionVector m; + m.axis.x = compass.getX(); + m.axis.y = compass.getY(); + m.axis.z = compass.getZ(); + rotate(m.array, orientation); + + // Calculate delta time (in seconds) to account for gyroscope sample clock error + const clock_t timestamp = micros(); + static clock_t previousTimestamp; + const float deltaTime = (float) (timestamp - previousTimestamp) / (float) 1000000; + previousTimestamp = timestamp; + + FusionAhrsUpdate(&ahrs, g, a, m, deltaTime); + + euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs)); + euler.angle.roll -= rollHome; + euler.angle.pitch -= pitchHome; + euler.angle.yaw -= yawHome; + } + return DURATION_IMMEDIATELY; +} + +void resetCenter() +{ + rollHome += euler.angle.roll; + pitchHome += euler.angle.pitch; + yawHome += euler.angle.yaw; + rollHome = normalize(rollHome, -180.0, 180.0); + pitchHome = normalize(pitchHome, -180.0, 180.0); + yawHome = normalize(yawHome, -180.0, 180.0); +} + +void setupBoardOrientation(OrientationPhase phase) +{ + static FusionEuler vrx_flat; + + switch(phase) + { + case PHASE_BEGIN: + orientation[0] = 0; + orientation[1] = 0; + orientation[2] = 0; + rollHome = 0; + pitchHome = 0; + yawHome = 0; + FusionAhrsReset(&ahrs); + break; + case PHASE_VRX_FLAT: + vrx_flat = euler; + break; + case PHASE_BOARD_FLAT: + orientation[0] = (vrx_flat.angle.roll - euler.angle.roll)*DEG_TO_RAD; + orientation[1] = (vrx_flat.angle.pitch - euler.angle.pitch)*DEG_TO_RAD; + orientation[2] = (vrx_flat.angle.yaw - euler.angle.yaw)*DEG_TO_RAD; + FusionAhrsReset(&ahrs); + // TODO: save orientation in settings + break; + } +} + +void getEuler(float *yaw, float *pitch, float *roll) +{ + *yaw = euler.angle.yaw; + *pitch = -euler.angle.pitch; + *roll = -euler.angle.roll; +} + +device_t HeadTracker_device = { + .initialize = initialize, + .start = start, + .event = NULL, + .timeout = timeout +}; + +#endif \ No newline at end of file diff --git a/lib/HeadTracker/devHeadTracker.h b/lib/HeadTracker/devHeadTracker.h new file mode 100644 index 0000000..c8f956c --- /dev/null +++ b/lib/HeadTracker/devHeadTracker.h @@ -0,0 +1,15 @@ +#pragma once + +#include "device.h" + +extern device_t HeadTracker_device; + +typedef enum { + PHASE_BEGIN, + PHASE_VRX_FLAT, + PHASE_BOARD_FLAT +} OrientationPhase; + +void resetCenter(); +void setupBoardOrientation(OrientationPhase phase); +void getEuler(float *yaw, float *pitch, float *roll); diff --git a/lib/ICM42670P/.piopm b/lib/ICM42670P/.piopm new file mode 100644 index 0000000..b1418e7 --- /dev/null +++ b/lib/ICM42670P/.piopm @@ -0,0 +1 @@ +{"type": "library", "name": "ICM42670P", "version": "1.0.0", "spec": {"owner": "invensenseinc", "id": 14400, "name": "ICM42670P", "requirements": null, "uri": null}} \ No newline at end of file diff --git a/lib/ICM42670P/LICENSE b/lib/ICM42670P/LICENSE new file mode 100644 index 0000000..809d9ae --- /dev/null +++ b/lib/ICM42670P/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2022 InvenSense, Inc All rights reserved. + +This software, related documentation and any modifications thereto (collectively "Software") is subject +to InvenSense, Inc and its licencors' intellectual property rights under U.S. and international copyright +and other intellectual property rights laws. + +InvenSense, Inc and its licencors retain all intellectual property and proprietary rights in and to the Software +and any use, reproduction, disclosure or distribution of the Software without an express license agreement +from InvenSense, Inc is strictly prohibited. + +EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS +PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL +InvenSense, Inc BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +OF THE SOFTWARE. diff --git a/lib/ICM42670P/README.md b/lib/ICM42670P/README.md new file mode 100644 index 0000000..0ac731a --- /dev/null +++ b/lib/ICM42670P/README.md @@ -0,0 +1,242 @@ +![TDKInvensense](doc/pictures/TDKInvensense.jpg) + +# ICM42670P Arduino library +This arduino library for the [TDK/Invensense ICM42670P High Performance 6-Axis MotionTracking(TM) IMU](https://invensense.tdk.com/products/motion-tracking/6-axis/icm-42670-p). +The ICM-42670-P is a high performance 6-axis MEMS MotionTracking device that combines a 3-axis gyroscope and a 3-axis accelerometer. It has a configurable host interface that supports I3CSM, I2C, and SPI serial communication, features up to 2.25 Kbytes FIFO and 2 programmable interrupts with ultra-low-power wake-on-motion support to minimize system power consumption. +This library supports both I2C and SPI commmunication with the ICM42670P. + +# Software setup +Use Arduino Library manager to find and install the ICM42670P library. + +# Hardware setup +There is currenlty no Arduino shield for the ICM42670P. +The wiring must be done manually between the Arduino motherboard and the ICM42670P daughter board. +The below wiring description is given for an Arduino Zero board, it depends on the interface to be used: +* I2C + +|Arduino Zero|ICM42670P daughter board| +| --- | --- | +| 3V3 | CN4.9 & CN5.10 | +| GND | CN5.9 | +| SDA | CN5.4 | +| SCL | CN5.6 | + +* SPI + +|Arduino Zero|ICM42670P daughter board| +| --- | --- | +| 3V3 | CN4.9 & CN5.10 | +| GND | CN5.9 | +| MISO=SPI.1 | CN4.7 | +| MOSI=SPI.4 | CN5.4 | +| SCK=SPI.3 | CN5.6 | +| CS=DIG.8 | CN5.8 | + +Note: SPI Chip Select can be mapped on any free digital IO, updating the sketches accordingly + +* Interrupt + +|Arduino Zero|ICM42670P daughter board| +| --- | --- | +| DIG.2 | CN4.1 | + +Note: Interrupt pin can be mapped on any free interruptable IO, updating the sketches accordingly + +## Orientation of axes + +The diagram below shows the orientation of the axes of sensitivity and the polarity of rotation. Note the pin 1 identifier (•) in the +figure. + +![Orientation of Axes](doc/pictures/OrientationOfAxes.jpg) + +# Library API + +## Create ICM42670P instance + +**ICM42670P(TwoWire &i2c,bool lsb)** +Create an instance of the ICM42670P that will be accessed using the specified I2C. The LSB of the I2C address can be set to 0 or 1. + +```C++ +ICM42670P IMU(Wire,0); +``` + +**ICM42670P(SPIClass &spi,uint8_t cs_id)** +Create an instance of the ICM42670P that will be accessed using the specified SPI. The IO number to be used as chip select must be specified. + +```C++ +ICM42670P IMU(SPI,10); +``` + +**/!\ This library does NOT support multiple instances of ICM42670P.** + + +## Initialize the ICM42670P +Call the begin method to execute the ICM42670P initialization routine. + +**int begin()** + +Initializes all the required parameters in order to communicate and use the ICM42670P. + +```C++ +IMU.begin(); +``` + +## Log sensor data + +**int startAccel(uint16_t odr, uint16_t fsr)** + +This method starts logging with the accelerometer, using the specified full scale range and output data rate. +Supported ODR are: 12, 25, 50, 100, 200, 400, 800, 1600 Hz (any other value defaults to 100 Hz). +Supported full scale ranges are: 2, 4, 8, 16 G (any other value defaults to 16 G). + +```C++ +IMU.startAccel(100,16); +``` + +**int startGyro(uint16_t odr, uint16_t fsr)** + +This method starts logging with the gyroscope, using the specified full scale range and output data rate. +Supported ODR are: 12, 25, 50, 100, 200, 400, 800, 1600 Hz (any other value defaults to 100 Hz). +Supported full scale ranges are: 250, 500, 1000, 2000 dps (any other value defaults to 2000 dps). + +```C++ +IMU.startGyro(100,2000); +``` + +**int getDataFromRegisters(inv_imu_sensor_event_t* evt)** + +This method reads the ICM42670P sensor data from registers and fill the provided event structure with sensor raw data. +Raw data can be translated to International System using the configured FSR for each sensor. Temperature in Degrees Centigrade = (TEMP_DATA / 128) + 25 + +```C++ +inv_imu_sensor_event_t event; +IMU.getDataFromRegisters(&event); +Serial.print("Accel X = "); +Serial.println(imu_event.accel[0]); +Serial.print("Accel Y = "); +Serial.print(imu_event.accel[1]); +Serial.print("Accel Z = "); +Serial.print(imu_event.accel[2]); +Serial.print("Gyro X = "); +Serial.print(imu_event.gyro[0]); +Serial.print("Gyro Y = "); +Serial.print(imu_event.gyro[1]); +Serial.print("Gyro Z = "); +Serial.print(imu_event.gyro[2]); +Serial.print("Temperature = "); +Serial.println(imu_event.temperature); +``` + +**int enableFifoInterrupt(uint8_t intpin, ICM42670P_irq_handler handler, uint8_t fifo_watermark)** + +This method initializes the fifo and the interrupt of the ICM42670P. The interrupt is triggered each time there is enough samples in the fifo (as specified by fifo_watermark), and the provided handler is called. +Any interuptable pin of the Arduino can be used for intpin. + +```C++ +uint8_t irq_received = 0; + +void irq_handler(void) +{ + irq_received = 1; +} +... + +// Enable interrupt on pin 2 +IMU.enableFifoInterrupt(2,irq_handler); +``` + +**int getDataFromFifo(ICM42670P_sensor_event_cb event_cb)** + +This method reads the ICM42670P sensor data samples stored in the FIFO and call the provided event handler with the sample event as parameter. +Raw data can be translated to International System using the configured FSR for each sensor. Temperature in Degrees Centigrade = (TEMP_DATA / 128) + 25 + +```C++ +void event_cb(inv_imu_sensor_event_t *evt) +{ + Serial.print(evt->accel[0]); + Serial.print(","); + Serial.print(evt->accel[1]); + Serial.print(","); + Serial.print(evt->accel[2]); + Serial.print(","); + Serial.print(evt->gyro[0]); + Serial.print(","); + Serial.print(evt->gyro[1]); + Serial.print(","); + Serial.print(evt->gyro[2]); + Serial.print(","); + Serial.println(evt->temperature); +} + +void loop() { + // Wait for interrupt to read data from fifo + if(irq_received) + { + irq_received = 0; + IMU.getDataFromFifo(event_cb); + } +} +``` + +**bool isAccelDataValid(inv_imu_sensor_event_t *evt)** + +This method checks if the accelerometer data in the FIFO sample is valid. + +```C++ +void event_cb(inv_imu_sensor_event_t *evt) +{ + if(IMU.isAccelDataValid(evt)) { + ... + } +} + +``` + +**bool isGyroDataValid(inv_imu_sensor_event_t *evt)** + +This method checks if the gyroscope data in the FIFO sample is valid. + +```C++ +void event_cb(inv_imu_sensor_event_t *evt) +{ + if(IMU.isGyroDataValid(evt)) { + ... + } +} + +``` + +**inv_imu_sensor_event_t** + +This structure is used by the ICM42670P driver to return raw sensor data. Available data is: +|Field name|description| +| --- | --- | +| sensor_mask | Mask describing available data | +| timestamp_fsync | Timestamp in us | +| accel[3] | 3-axis accel raw data | +| gyro[3] | 3-axis gyro raw data | +| temperature | Temperature raw data (on 1 or 2 bytes) | +| accel_high_res[3] | 3- axis accel lsb in high resolution | +| gyro_high_res[3] | 3- axis gyro LSB in high resolution | + + +# Available Sketches + +**Polling_I2C** + +This sketch initializes the ICM42670P with the I2C interface, and starts logging raw sensor data from IMU registers. Sensor data can be monitored on Serial monitor or Serial plotter + +**Polling_SPI** + +This sketch initializes the ICM42670P with the SPI interface, and starts logging raw sensor data from IMU registers. Sensor data can be monitored on Serial monitor or Serial plotter + +**FIFO_Interrupt** + +This sketch initializes the ICM42670P with the SPI interface and interrupt PIN2, and starts logging raw sensor data from IMU FIFO. Sensor data can be monitored on Serial monitor or Serial plotter + +**IMU data monitoring** + +When the ICM42670P IMU is logging, the Accelerometer, Gyroscope and Temperature raw data can be monitored with the Arduino Serial Plotter (Tools->Serial Plotter). + +![Serial Plotter](doc/pictures/SerialPlotter.jpg) + diff --git a/lib/ICM42670P/examples/FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino b/lib/ICM42670P/examples/FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino new file mode 100644 index 0000000..0dac9c9 --- /dev/null +++ b/lib/ICM42670P/examples/FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino @@ -0,0 +1,59 @@ +#include "ICM42670P.h" + +// Instantiate an ICM42670P with SPI interface and CS on pin 8 +ICM42670P IMU(SPI,8); + +uint8_t irq_received = 0; + +void irq_handler(void) { + irq_received = 1; +} + +void event_cb(inv_imu_sensor_event_t *evt) { + // Format data for Serial Plotter + if(IMU.isAccelDataValid(evt)&&IMU.isGyroDataValid(evt)) { + Serial.print(evt->accel[0]); + Serial.print(","); + Serial.print(evt->accel[1]); + Serial.print(","); + Serial.print(evt->accel[2]); + Serial.print(","); + Serial.print(evt->gyro[0]); + Serial.print(","); + Serial.print(evt->gyro[1]); + Serial.print(","); + Serial.print(evt->gyro[2]); + Serial.print(","); + Serial.println(evt->temperature); + } +} + +void setup() { + int ret; + Serial.begin(115200); + while(!Serial) {} + + // Initializing the ICM42670P + ret = IMU.begin(); + if (ret != 0) { + Serial.print("ICM42670P initialization failed: "); + Serial.println(ret); + while(1); + } + // Enable interrupt on pin 2, Fifo watermark=10 + IMU.enableFifoInterrupt(2,irq_handler,10); + // Accel ODR = 100 Hz and Full Scale Range = 16G + IMU.startAccel(100,16); + // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps + IMU.startGyro(100,2000); + // Plotter axis header + Serial.println("AccelX,AccelY,AccelZ,GyroX,GyroY,GyroZ,Temperature"); +} + +void loop() { + // Wait for interrupt to read data from fifo + if(irq_received) { + irq_received = 0; + IMU.getDataFromFifo(event_cb); + } +} diff --git a/lib/ICM42670P/examples/Polling_I2C/Polling_I2C.ino b/lib/ICM42670P/examples/Polling_I2C/Polling_I2C.ino new file mode 100644 index 0000000..31d25d3 --- /dev/null +++ b/lib/ICM42670P/examples/Polling_I2C/Polling_I2C.ino @@ -0,0 +1,52 @@ +#include "ICM42670P.h" + +// Instantiate an ICM42670P with LSB address set to 0 +ICM42670P IMU(Wire,0); + +void setup() { + int ret; + Serial.begin(115200); + while(!Serial) {} + + // Initializing the ICM42670P + ret = IMU.begin(); + if (ret != 0) { + Serial.print("ICM42670P initialization failed: "); + Serial.println(ret); + while(1); + } + // Accel ODR = 100 Hz and Full Scale Range = 16G + IMU.startAccel(100,16); + // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps + IMU.startGyro(100,2000); + // Wait IMU to start + delay(100); + // Plotter axis header + Serial.println("AccelX,AccelY,AccelZ,GyroX,GyroY,GyroZ,Temperature"); +} + +void loop() { + + inv_imu_sensor_event_t imu_event; + + // Get last event + IMU.getDataFromRegisters(&imu_event); + + // Format data for Serial Plotter + Serial.print(imu_event.accel[0]); + Serial.print(","); + Serial.print(imu_event.accel[1]); + Serial.print(","); + Serial.print(imu_event.accel[2]); + Serial.print(","); + Serial.print(imu_event.gyro[0]); + Serial.print(","); + Serial.print(imu_event.gyro[1]); + Serial.print(","); + Serial.print(imu_event.gyro[2]); + Serial.print(","); + Serial.println(imu_event.temperature); + + // Run @ ODR 100Hz + delay(10); +} diff --git a/lib/ICM42670P/examples/Polling_SPI/Polling_SPI.ino b/lib/ICM42670P/examples/Polling_SPI/Polling_SPI.ino new file mode 100644 index 0000000..479ea23 --- /dev/null +++ b/lib/ICM42670P/examples/Polling_SPI/Polling_SPI.ino @@ -0,0 +1,52 @@ +#include "ICM42670P.h" + +// Instantiate an ICM42670P with SPI interface and CS on pin 8 +ICM42670P IMU(SPI,8); + +void setup() { + int ret; + Serial.begin(115200); + while(!Serial) {} + + // Initializing the ICM42670P + ret = IMU.begin(); + if (ret != 0) { + Serial.print("ICM42670P initialization failed: "); + Serial.println(ret); + while(1); + } + // Accel ODR = 100 Hz and Full Scale Range = 16G + IMU.startAccel(100,16); + // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps + IMU.startGyro(100,2000); + // Wait IMU to start + delay(100); + // Plotter axis header + Serial.println("AccelX,AccelY,AccelZ,GyroX,GyroY,GyroZ,Temperature"); +} + +void loop() { + + inv_imu_sensor_event_t imu_event; + + // Get last event + IMU.getDataFromRegisters(&imu_event); + + // Format data for Serial Plotter + Serial.print(imu_event.accel[0]); + Serial.print(","); + Serial.print(imu_event.accel[1]); + Serial.print(","); + Serial.print(imu_event.accel[2]); + Serial.print(","); + Serial.print(imu_event.gyro[0]); + Serial.print(","); + Serial.print(imu_event.gyro[1]); + Serial.print(","); + Serial.print(imu_event.gyro[2]); + Serial.print(","); + Serial.println(imu_event.temperature); + + // Run @ ODR 100Hz + delay(10); +} diff --git a/lib/ICM42670P/keywords.txt b/lib/ICM42670P/keywords.txt new file mode 100644 index 0000000..ba431e1 --- /dev/null +++ b/lib/ICM42670P/keywords.txt @@ -0,0 +1,5 @@ +ICM42670P KEYWORD1 +begin KEYWORD2 +startAccel KEYWORD2 +startGyro KEYWORD2 +getDataFromRegisters KEYWORD2 diff --git a/lib/ICM42670P/library.properties b/lib/ICM42670P/library.properties new file mode 100644 index 0000000..c50bf07 --- /dev/null +++ b/lib/ICM42670P/library.properties @@ -0,0 +1,10 @@ +name=ICM42670P +version=1.0.0 +author=TDK/Invensense +maintainer=TDK/Invensense +sentence=The ICM42670P arduino library manages the communication with an ICM42670P Invensence high performance 6-axis MEMS MotionTracking device. +paragraph=This library allows to easily configure and log accelerometer, gyroscope and temperature data from an ICM42670P device, using the SPI or the I2C interface. +category=Sensors +url=https://github.com/InvenSenseInc/arduino.ICM42670P +architectures=* +includes=ICM42670P.h diff --git a/lib/ICM42670P/src/ICM42670P.cpp b/lib/ICM42670P/src/ICM42670P.cpp new file mode 100644 index 0000000..51b997f --- /dev/null +++ b/lib/ICM42670P/src/ICM42670P.cpp @@ -0,0 +1,306 @@ +/* + * + * ------------------------------------------------------------------------------------------------------------ + * Copyright (c) 2022 InvenSense, Inc All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense, Inc and its licencors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense, Inc and its licencors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense, Inc is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * InvenSense, Inc BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "Arduino.h" +#include "ICM42670P.h" + +static int i2c_write(struct inv_imu_serif * serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen); +static int i2c_read(struct inv_imu_serif * serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen); +static int spi_write(struct inv_imu_serif * serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen); +static int spi_read(struct inv_imu_serif * serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen); +static void event_cb(inv_imu_sensor_event_t *event); + +// i2c and SPI interfaces are used from C driver callbacks, without any knowledge of the object +// As they are declared as static, they will be overriden each time a new ICM42670P object is created +// i2c +uint8_t i2c_address = 0; +static TwoWire *i2c = NULL; +#define ICM42670P_I2C_SPEED 1000000 +#define ICM42670P_I2C_ADDRESS 0x68 +// spi +static SPIClass *spi = NULL; +static uint8_t chip_select_id = 0; +bool _useSPI = false; +#define SPI_READ 0x80 +#define SPI_CLOCK 16000000 + +// This is used by the event callback (not object aware), declared static +static inv_imu_sensor_event_t* event; + +// This is used by the getDataFromFifo callback (not object aware), declared static +static struct inv_imu_device *icm_driver_ptr = NULL; + +// ICM42670P constructor for I2c interface +ICM42670P::ICM42670P(TwoWire &i2c_ref,bool lsb) { + i2c = &i2c_ref; + i2c_address = ICM42670P_I2C_ADDRESS | (lsb ? 0x1 : 0); +} + +// ICM42670P constructor for spi interface +ICM42670P::ICM42670P(SPIClass &spi_ref,uint8_t cs_id) { + spi = &spi_ref; + chip_select_id = cs_id; +} + +/* starts communication with the ICM42670P */ +int ICM42670P::begin() { + struct inv_imu_serif icm_serif; + int rc = 0; + uint8_t who_am_i; + + if (i2c != NULL) { + i2c->begin(); + i2c->setClock(ICM42670P_I2C_SPEED); + icm_serif.serif_type = UI_I2C; + icm_serif.read_reg = i2c_read; + icm_serif.write_reg = i2c_write; + } else { + spi->begin(); + pinMode(chip_select_id,OUTPUT); + digitalWrite(chip_select_id,HIGH); + icm_serif.serif_type = UI_SPI4; + icm_serif.read_reg = spi_read; + icm_serif.write_reg = spi_write; + } + /* Initialize serial interface between MCU and Icm43xxx */ + icm_serif.context = 0; /* no need */ + icm_serif.max_read = 2048; /* maximum number of bytes allowed per serial read */ + icm_serif.max_write = 2048; /* maximum number of bytes allowed per serial write */ + rc = inv_imu_init(&icm_driver, &icm_serif, NULL); + if (rc != INV_ERROR_SUCCESS) { + return rc; + } + icm_driver.sensor_event_cb = event_cb; + + /* Check WHOAMI */ + rc = inv_imu_get_who_am_i(&icm_driver, &who_am_i); + if(rc != 0) { + return -2; + } + if ((who_am_i != ICM42607P_WHOAMI) && (who_am_i != ICM42607C_WHOAMI) && (who_am_i != ICM42670P_WHOAMI) && (who_am_i != ICM42670S_WHOAMI) && (who_am_i != ICM42670T_WHOAMI)) { + return -3; + } + + // successful init, return 0 + return 0; +} + +int ICM42670P::startAccel(uint16_t odr, uint16_t fsr) { + int rc = 0; + rc |= inv_imu_set_accel_fsr(&icm_driver, accel_fsr_g_to_param(fsr)); + rc |= inv_imu_set_accel_frequency(&icm_driver, accel_freq_to_param(odr)); + rc |= inv_imu_enable_accel_low_noise_mode(&icm_driver); + return rc; +} + +int ICM42670P::startGyro(uint16_t odr, uint16_t fsr) { + int rc = 0; + rc |= inv_imu_set_gyro_fsr(&icm_driver, gyro_fsr_dps_to_param(fsr)); + rc |= inv_imu_set_gyro_frequency(&icm_driver, gyro_freq_to_param(odr)); + rc |= inv_imu_enable_gyro_low_noise_mode(&icm_driver); + return rc; +} + +int ICM42670P::getDataFromRegisters(inv_imu_sensor_event_t* evt) { + if(evt != NULL) { + // Set event buffer to be used by the callback + event = evt; + return inv_imu_get_data_from_registers(&icm_driver); + } else { + return -1; + } +} + +int ICM42670P::enableFifoInterrupt(uint8_t intpin, ICM42670P_irq_handler handler, uint8_t fifo_watermark) { + int rc = 0; + uint8_t data; + + if(handler == NULL) { + return -1; + } + pinMode(intpin, INPUT); + attachInterrupt(intpin, handler, RISING); + rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_ENABLED); + rc |= inv_imu_write_reg(&icm_driver, FIFO_CONFIG2, 1, &fifo_watermark); + // Set fifo_wm_int_w generating condition : fifo_wm_int_w generated when counter == threshold + rc |= inv_imu_read_reg(&icm_driver, FIFO_CONFIG5_MREG1, 1, &data); + data &= (uint8_t)~FIFO_CONFIG5_WM_GT_TH_EN; + rc |= inv_imu_write_reg(&icm_driver, FIFO_CONFIG5_MREG1, 1, &data); + // Disable APEX to use 2.25kB of fifo for raw data + data = SENSOR_CONFIG3_APEX_DISABLE_MASK; + rc |= inv_imu_write_reg(&icm_driver, SENSOR_CONFIG3_MREG1, 1, &data); + return rc; +} + +int ICM42670P::enableDataInterrupt(uint8_t intpin, ICM42670P_irq_handler handler) { + int rc = 0; + uint8_t data; + + if(handler == NULL) { + return -1; + } + pinMode(intpin, INPUT); + attachInterrupt(intpin, handler, RISING); + rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_DISABLED); + return rc; +} + +int ICM42670P::getDataFromFifo(ICM42670P_sensor_event_cb event_cb) { + if(event_cb == NULL) { + return -1; + } + icm_driver.sensor_event_cb = event_cb; + return inv_imu_get_data_from_fifo(&icm_driver); +} + +bool ICM42670P::isAccelDataValid(inv_imu_sensor_event_t *evt) { + return (evt->sensor_mask & (1<sensor_mask & (1<beginTransmission(i2c_address); + i2c->write(reg); + for(uint8_t i = 0; i < wlen; i++) { + i2c->write(wbuffer[i]); + } + i2c->endTransmission(); + return 0; +} + +static int i2c_read(struct inv_imu_serif * serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen) { + uint16_t rx_bytes = 0; + + i2c->beginTransmission(i2c_address); + i2c->write(reg); + i2c->endTransmission(false); + rx_bytes = i2c->requestFrom(i2c_address, rlen); + if (rlen == rx_bytes) { + for(uint8_t i = 0; i < rx_bytes; i++) { + rbuffer[i] = i2c->read(); + } + return 0; + } else { + return -1; + } +} + +static int spi_write(struct inv_imu_serif * serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen) { + spi->beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE1)); + digitalWrite(chip_select_id,LOW); + spi->transfer(reg); + for(uint8_t i = 0; i < wlen; i++) { + spi->transfer(wbuffer[i]); + } + digitalWrite(chip_select_id,HIGH); + spi->endTransaction(); + return 0; +} + +static int spi_read(struct inv_imu_serif * serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen) { + spi->beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE1)); + digitalWrite(chip_select_id,LOW); + spi->transfer(reg | SPI_READ); + spi->transfer(rbuffer,rlen); + digitalWrite(chip_select_id,HIGH); + spi->endTransaction(); + return 0; +} + +ACCEL_CONFIG0_FS_SEL_t ICM42670P::accel_fsr_g_to_param(uint16_t accel_fsr_g) { + ACCEL_CONFIG0_FS_SEL_t ret = ACCEL_CONFIG0_FS_SEL_16g; + + switch(accel_fsr_g) { + case 2: ret = ACCEL_CONFIG0_FS_SEL_2g; break; + case 4: ret = ACCEL_CONFIG0_FS_SEL_4g; break; + case 8: ret = ACCEL_CONFIG0_FS_SEL_8g; break; + case 16: ret = ACCEL_CONFIG0_FS_SEL_16g; break; + default: + /* Unknown accel FSR. Set to default 16G */ + break; + } + return ret; +} + +GYRO_CONFIG0_FS_SEL_t ICM42670P::gyro_fsr_dps_to_param(uint16_t gyro_fsr_dps) { + GYRO_CONFIG0_FS_SEL_t ret = GYRO_CONFIG0_FS_SEL_2000dps; + + switch(gyro_fsr_dps) { + case 250: ret = GYRO_CONFIG0_FS_SEL_250dps; break; + case 500: ret = GYRO_CONFIG0_FS_SEL_500dps; break; + case 1000: ret = GYRO_CONFIG0_FS_SEL_1000dps; break; + case 2000: ret = GYRO_CONFIG0_FS_SEL_2000dps; break; + default: + /* Unknown gyro FSR. Set to default 2000dps" */ + break; + } + return ret; +} + +ACCEL_CONFIG0_ODR_t ICM42670P::accel_freq_to_param(uint16_t accel_freq_hz) { + ACCEL_CONFIG0_ODR_t ret = ACCEL_CONFIG0_ODR_100_HZ; + + switch(accel_freq_hz) { + case 12: ret = ACCEL_CONFIG0_ODR_12_5_HZ; break; + case 25: ret = ACCEL_CONFIG0_ODR_25_HZ; break; + case 50: ret = ACCEL_CONFIG0_ODR_50_HZ; break; + case 100: ret = ACCEL_CONFIG0_ODR_100_HZ; break; + case 200: ret = ACCEL_CONFIG0_ODR_200_HZ; break; + case 400: ret = ACCEL_CONFIG0_ODR_400_HZ; break; + case 800: ret = ACCEL_CONFIG0_ODR_800_HZ; break; + case 1600: ret = ACCEL_CONFIG0_ODR_1600_HZ; break; + default: + /* Unknown accel frequency. Set to default 100Hz */ + break; + } + return ret; +} + +GYRO_CONFIG0_ODR_t ICM42670P::gyro_freq_to_param(uint16_t gyro_freq_hz) { + GYRO_CONFIG0_ODR_t ret = GYRO_CONFIG0_ODR_100_HZ; + + switch(gyro_freq_hz) { + case 12: ret = GYRO_CONFIG0_ODR_12_5_HZ; break; + case 25: ret = GYRO_CONFIG0_ODR_25_HZ; break; + case 50: ret = GYRO_CONFIG0_ODR_50_HZ; break; + case 100: ret = GYRO_CONFIG0_ODR_100_HZ; break; + case 200: ret = GYRO_CONFIG0_ODR_200_HZ; break; + case 400: ret = GYRO_CONFIG0_ODR_400_HZ; break; + case 800: ret = GYRO_CONFIG0_ODR_800_HZ; break; + case 1600: ret = GYRO_CONFIG0_ODR_1600_HZ; break; + default: + /* Unknown gyro ODR. Set to default 100Hz */ + break; + } + return ret; +} + + +static void event_cb(inv_imu_sensor_event_t *evt) { + memcpy(event,evt,sizeof(inv_imu_sensor_event_t)); +} diff --git a/lib/ICM42670P/src/ICM42670P.h b/lib/ICM42670P/src/ICM42670P.h new file mode 100644 index 0000000..90cca38 --- /dev/null +++ b/lib/ICM42670P/src/ICM42670P.h @@ -0,0 +1,65 @@ +/* + * + * ------------------------------------------------------------------------------------------------------------ + * Copyright (c) 2022 InvenSense, Inc All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense, Inc and its licencors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense, Inc and its licencors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense, Inc is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * InvenSense, Inc BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * + * ------------------------------------------------------------------------------------------------------------ + */ + +#ifndef ICM42670P_H +#define ICM42670P_H + +#include "Arduino.h" +#include "SPI.h" +#include "Wire.h" + +extern "C" { +#include "imu/inv_imu_driver.h" +#undef ICM42670P +} + +// This defines the handler called when retrieving a sample from the FIFO +typedef void (*ICM42670P_sensor_event_cb)(inv_imu_sensor_event_t *event); +// This defines the handler called when receiving an irq +typedef void (*ICM42670P_irq_handler)(void); + +class ICM42670P { + public: + ICM42670P(TwoWire &i2c,bool address_lsb); + ICM42670P(SPIClass &spi,uint8_t chip_select_id); + int begin(); + int startAccel(uint16_t odr, uint16_t fsr); + int startGyro(uint16_t odr, uint16_t fsr); + int getDataFromRegisters(inv_imu_sensor_event_t* evt); + int enableFifoInterrupt(uint8_t intpin, ICM42670P_irq_handler handler, uint8_t fifo_watermark); + int enableDataInterrupt(uint8_t intpin, ICM42670P_irq_handler handler); + int getDataFromFifo(ICM42670P_sensor_event_cb event_cb); + bool isAccelDataValid(inv_imu_sensor_event_t *evt); + bool isGyroDataValid(inv_imu_sensor_event_t *evt); + + protected: + struct inv_imu_device icm_driver; + ACCEL_CONFIG0_ODR_t accel_freq_to_param(uint16_t accel_freq_hz); + GYRO_CONFIG0_ODR_t gyro_freq_to_param(uint16_t gyro_freq_hz); + ACCEL_CONFIG0_FS_SEL_t accel_fsr_g_to_param(uint16_t accel_fsr_g); + GYRO_CONFIG0_FS_SEL_t gyro_fsr_dps_to_param(uint16_t gyro_fsr_dps); +}; + +#endif // ICM42670P_H diff --git a/lib/ICM42670P/src/Invn/InvError.h b/lib/ICM42670P/src/Invn/InvError.h new file mode 100644 index 0000000..9fe49a2 --- /dev/null +++ b/lib/ICM42670P/src/Invn/InvError.h @@ -0,0 +1,64 @@ +/* + * ________________________________________________________________________________________________________ + * Copyright (c) 2015-2015 InvenSense Inc. All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively “Software”) is subject + * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * ________________________________________________________________________________________________________ + */ + +/** @defgroup InvError Error code + * @brief Common error code + * + * @ingroup EmbUtils + * @{ + */ + +#ifndef _INV_ERROR_H_ +#define _INV_ERROR_H_ + +/** @brief Common error code definition + */ +enum inv_error +{ + INV_ERROR_SUCCESS = 0, /**< no error */ + INV_ERROR = -1, /**< unspecified error */ + INV_ERROR_NIMPL = -2, /**< function not implemented for given + arguments */ + INV_ERROR_TRANSPORT = -3, /**< error occured at transport level */ + INV_ERROR_TIMEOUT = -4, /**< action did not complete in the expected + time window */ + INV_ERROR_SIZE = -5, /**< size/length of given arguments is not + suitable to complete requested action */ + INV_ERROR_OS = -6, /**< error related to OS */ + INV_ERROR_IO = -7, /**< error related to IO operation */ + INV_ERROR_MEM = -9, /**< not enough memory to complete requested + action */ + INV_ERROR_HW = -10, /**< error at HW level */ + INV_ERROR_BAD_ARG = -11, /**< provided arguments are not good to + perform requestion action */ + INV_ERROR_UNEXPECTED = -12, /**< something unexpected happened */ + INV_ERROR_FILE = -13, /**< cannot access file or unexpected format */ + INV_ERROR_PATH = -14, /**< invalid file path */ + INV_ERROR_IMAGE_TYPE = -15, /**< error when image type is not managed */ + INV_ERROR_WATCHDOG = -16, /**< error when device doesn't respond + to ping */ +}; + +#endif /* _INV_ERROR_H_ */ + +/** @} */ diff --git a/lib/ICM42670P/src/imu/inv_imu_defs.h b/lib/ICM42670P/src/imu/inv_imu_defs.h new file mode 100644 index 0000000..7ad503a --- /dev/null +++ b/lib/ICM42670P/src/imu/inv_imu_defs.h @@ -0,0 +1,991 @@ +/* + * ________________________________________________________________________________________________________ + * Copyright (c) 2017 InvenSense Inc. All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * ________________________________________________________________________________________________________ + */ + +#ifndef _INV_IMU_DEFS_H_ +#define _INV_IMU_DEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file inv_imu_defs.h + * File exposing the device register map + */ + +#include +/* List whoami values for all device variants*/ +#define T1000_WHOAMI 0x30 +#define ICM42607P_WHOAMI 0x60 +#define ICM42607C_WHOAMI 0x61 +#define ICM42670P_WHOAMI 0x67 +#define ICM42670T_WHOAMI 0x64 +#define ICM42670S_WHOAMI 0x69 +#define ICM42680_WHOAMI 0x80 + + +#define ICM42670P + +/* Define whoami value for the targeted product and make sure the target is valid */ +#if defined(T1000) +#define ICM_WHOAMI T1000_WHOAMI +#elif defined(ICM42607P) +#define ICM_WHOAMI ICM42607P_WHOAMI +#elif defined(ICM42670P) +#define ICM_WHOAMI ICM42670P_WHOAMI +#elif defined(ICM42670T) +#define ICM_WHOAMI ICM42670T_WHOAMI +#elif defined(ICM42670S) +#define ICM_WHOAMI ICM42670S_WHOAMI +#elif defined(ICM42680) +#define ICM_WHOAMI ICM42680_WHOAMI +#else +#error "Please define which IMU variant is targeted." +#endif + +#include "imu/inv_imu_regmap.h" + +/* ---------------------------------------------------------------------------- + * Device features + * + * Next macros define some of the device features such as FIFO, sensor data + * size or whoami value. + * ---------------------------------------------------------------------------- */ + +#define ACCEL_DATA_SIZE 6 +#define GYRO_DATA_SIZE 6 +#define TEMP_DATA_SIZE 2 + +#define FIFO_HEADER_SIZE 1 +#define FIFO_ACCEL_DATA_SIZE ACCEL_DATA_SIZE +#define FIFO_GYRO_DATA_SIZE GYRO_DATA_SIZE +#define FIFO_TEMP_DATA_SIZE 1 +#define FIFO_TS_FSYNC_SIZE 2 +#define FIFO_TEMP_HIGH_RES_SIZE 1 +#define FIFO_ACCEL_GYRO_HIGH_RES_SIZE 3 + +#define FIFO_16BYTES_PACKET_SIZE \ + (FIFO_HEADER_SIZE + FIFO_ACCEL_DATA_SIZE + FIFO_GYRO_DATA_SIZE + FIFO_TEMP_DATA_SIZE + \ + FIFO_TS_FSYNC_SIZE) +#define FIFO_20BYTES_PACKET_SIZE \ + (FIFO_HEADER_SIZE + FIFO_ACCEL_DATA_SIZE + FIFO_GYRO_DATA_SIZE + FIFO_TEMP_DATA_SIZE + \ + FIFO_TS_FSYNC_SIZE + FIFO_TEMP_HIGH_RES_SIZE + FIFO_ACCEL_GYRO_HIGH_RES_SIZE) + +#define FIFO_HEADER_ODR_ACCEL 0x01 +#define FIFO_HEADER_ODR_GYRO 0x02 +#define FIFO_HEADER_FSYNC 0x04 +#define FIFO_HEADER_TMST 0x08 +#define FIFO_HEADER_HEADER_20 0x10 +#define FIFO_HEADER_GYRO 0x20 +#define FIFO_HEADER_ACC 0x40 +#define FIFO_HEADER_MSG 0x80 + +#define INVALID_VALUE_FIFO ((int16_t)0x8000) +#define INVALID_VALUE_FIFO_1B ((int8_t)0x80) +#define OUT_OF_BOUND_TEMPERATURE_NEG_FIFO_1B ((int8_t)0x81) +#define OUT_OF_BOUND_TEMPERATURE_POS_FIFO_1B ((int8_t)0x7F) + +/** Describe the content of the FIFO header */ +typedef union { + unsigned char Byte; + struct { + unsigned char gyro_odr_different : 1; + unsigned char accel_odr_different : 1; + unsigned char fsync_bit : 1; + unsigned char timestamp_bit : 1; + unsigned char twentybits_bit : 1; + unsigned char gyro_bit : 1; + unsigned char accel_bit : 1; + unsigned char msg_bit : 1; + } bits; +} fifo_header_t; + +/* ---------------------------------------------------------------------------- + * Device registers description + * + * Next section defines some of the registers bitfield and declare corresponding + * accessors. + * Note that descriptors and accessors are not provided for all the registers + * but only for the most useful ones. + * For all details on registers and bitfields functionalities please refer to + * the device datasheet. + * ---------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------- + * register bank 0 + * ---------------------------------------------------------------------------- */ + +/* + * DEVICE_CONFIG + * Register Name : DEVICE_CONFIG + */ + +/* SPI_MODE */ +typedef enum { + DEVICE_CONFIG_SPI_MODE_1_2 = (0x1 << DEVICE_CONFIG_SPI_MODE_POS), + DEVICE_CONFIG_SPI_MODE_0_3 = (0x0 << DEVICE_CONFIG_SPI_MODE_POS), +} DEVICE_CONFIG_SPI_MODE_t; + +/* SPI_AP_4WIRE */ +typedef enum { + DEVICE_CONFIG_SPI_4WIRE = (0x1 << DEVICE_CONFIG_SPI_AP_4WIRE_POS), + DEVICE_CONFIG_SPI_3WIRE = (0x0 << DEVICE_CONFIG_SPI_AP_4WIRE_POS), +} DEVICE_CONFIG_SPI_AP_4WIRE_t; + +/* + * SIGNAL_PATH_RESET + * Register Name: SIGNAL_PATH_RESET + */ + +/* SOFT_RESET_DEVICE_CONFIG */ +typedef enum { + SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_EN = + (0x01 << SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_POS), + SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_DIS = + (0x00 << SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_POS), +} SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_t; + +/* FIFO_FLUSH */ +typedef enum { + SIGNAL_PATH_RESET_FIFO_FLUSH_EN = (0x01 << SIGNAL_PATH_RESET_FIFO_FLUSH_POS), + SIGNAL_PATH_RESET_FIFO_FLUSH_DIS = (0x00 << SIGNAL_PATH_RESET_FIFO_FLUSH_POS), +} SIGNAL_PATH_RESET_FIFO_FLUSH_t; + +/* + * INT_CONFIG + * Register Name: INT_CONFIG + */ + +/* INT2_MODE */ +typedef enum { + INT_CONFIG_INT2_MODE_LATCHED = (0x01 << INT_CONFIG_INT2_MODE_POS), + INT_CONFIG_INT2_MODE_PULSED = (0x00 << INT_CONFIG_INT2_MODE_POS), +} INT_CONFIG_INT2_MODE_t; + +/* INT2_DRIVE_CIRCUIT */ +typedef enum { + INT_CONFIG_INT2_DRIVE_CIRCUIT_PP = (0x01 << INT_CONFIG_INT2_DRIVE_CIRCUIT_POS), + INT_CONFIG_INT2_DRIVE_CIRCUIT_OD = (0x00 << INT_CONFIG_INT2_DRIVE_CIRCUIT_POS), +} INT_CONFIG_INT2_DRIVE_CIRCUIT_t; + +/* INT2_POLARITY */ +typedef enum { + INT_CONFIG_INT2_POLARITY_HIGH = (0x01 << INT_CONFIG_INT2_POLARITY_POS), + INT_CONFIG_INT2_POLARITY_LOW = (0x00 << INT_CONFIG_INT2_POLARITY_POS), +} INT_CONFIG_INT2_POLARITY_t; + +/* INT1_MODE */ +typedef enum { + INT_CONFIG_INT1_MODE_LATCHED = (0x01 << INT_CONFIG_INT1_MODE_POS), + INT_CONFIG_INT1_MODE_PULSED = (0x00 << INT_CONFIG_INT1_MODE_POS), +} INT_CONFIG_INT1_MODE_t; + +/* INT1_DRIVE_CIRCUIT */ +typedef enum { + INT_CONFIG_INT1_DRIVE_CIRCUIT_PP = (0x01 << INT_CONFIG_INT1_DRIVE_CIRCUIT_POS), + INT_CONFIG_INT1_DRIVE_CIRCUIT_OD = (0x00 << INT_CONFIG_INT1_DRIVE_CIRCUIT_POS), +} INT_CONFIG_INT1_DRIVE_CIRCUIT_t; + +/* INT1_POLARITY */ +typedef enum { + INT_CONFIG_INT1_POLARITY_HIGH = 0x01, + INT_CONFIG_INT1_POLARITY_LOW = 0x00, +} INT_CONFIG_INT1_POLARITY_t; + +/* + * PWR_MGMT0 + * Register Name: PWR_MGMT0 + */ + +/* ACCEL_LP_CLK_SEL */ +typedef enum { + PWR_MGMT0_ACCEL_LP_CLK_WUOSC = (0x00 << PWR_MGMT0_ACCEL_LP_CLK_SEL_POS), + PWR_MGMT0_ACCEL_LP_CLK_RCOSC = (0x01 << PWR_MGMT0_ACCEL_LP_CLK_SEL_POS), +} PWR_MGMT0_ACCEL_LP_CLK_t; + +/* IDLE */ +typedef enum { + PWR_MGMT0_IDLE_DIS = (0x01 << PWR_MGMT0_IDLE_POS), + PWR_MGMT0_IDLE_EN = (0x00 << PWR_MGMT0_IDLE_POS), +} PWR_MGMT0_IDLE_t; + +/* GYRO_MODE */ +typedef enum { + PWR_MGMT0_GYRO_MODE_LN = (0x03 << PWR_MGMT0_GYRO_MODE_POS), + PWR_MGMT0_GYRO_MODE_LP = (0x02 << PWR_MGMT0_GYRO_MODE_POS), + PWR_MGMT0_GYRO_MODE_STANDBY = (0x01 << PWR_MGMT0_GYRO_MODE_POS), + PWR_MGMT0_GYRO_MODE_OFF = (0x00 << PWR_MGMT0_GYRO_MODE_POS), +} PWR_MGMT0_GYRO_MODE_t; + +/* ACCEL_MODE */ +typedef enum { + PWR_MGMT0_ACCEL_MODE_LN = 0x03, + PWR_MGMT0_ACCEL_MODE_LP = 0x02, + PWR_MGMT0_ACCEL_MODE_OFF = 0x00, +} PWR_MGMT0_ACCEL_MODE_t; + +/* + * GYRO_CONFIG0 + * Register Name: GYRO_CONFIG0 + */ + +/* GYRO_FS_SEL*/ +typedef enum { + GYRO_CONFIG0_FS_SEL_250dps = (3 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS), + GYRO_CONFIG0_FS_SEL_500dps = (2 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS), + GYRO_CONFIG0_FS_SEL_1000dps = (1 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS), + GYRO_CONFIG0_FS_SEL_2000dps = (0 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS), +} GYRO_CONFIG0_FS_SEL_t; + +/* GYRO_ODR */ +typedef enum { + GYRO_CONFIG0_ODR_1_5625_HZ = 0xF, + GYRO_CONFIG0_ODR_3_125_HZ = 0xE, + GYRO_CONFIG0_ODR_6_25_HZ = 0xD, + GYRO_CONFIG0_ODR_12_5_HZ = 0xC, + GYRO_CONFIG0_ODR_25_HZ = 0xB, + GYRO_CONFIG0_ODR_50_HZ = 0xA, + GYRO_CONFIG0_ODR_100_HZ = 0x9, + GYRO_CONFIG0_ODR_200_HZ = 0x8, + GYRO_CONFIG0_ODR_400_HZ = 0x7, + GYRO_CONFIG0_ODR_800_HZ = 0x6, + GYRO_CONFIG0_ODR_1600_HZ = 0x5, +} GYRO_CONFIG0_ODR_t; + +/* + * ACCEL_CONFIG0 + * Register Name: ACCEL_CONFIG0 + */ + +/* ACCEL_FS_SEL */ +typedef enum { + ACCEL_CONFIG0_FS_SEL_2g = (0x3 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS), + ACCEL_CONFIG0_FS_SEL_4g = (0x2 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS), + ACCEL_CONFIG0_FS_SEL_8g = (0x1 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS), + ACCEL_CONFIG0_FS_SEL_16g = (0x0 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS), +} ACCEL_CONFIG0_FS_SEL_t; + +/* ACCEL_ODR */ +typedef enum { + ACCEL_CONFIG0_ODR_1_5625_HZ = 0xF, + ACCEL_CONFIG0_ODR_3_125_HZ = 0xE, + ACCEL_CONFIG0_ODR_6_25_HZ = 0xD, + ACCEL_CONFIG0_ODR_12_5_HZ = 0xC, + ACCEL_CONFIG0_ODR_25_HZ = 0xB, + ACCEL_CONFIG0_ODR_50_HZ = 0xA, + ACCEL_CONFIG0_ODR_100_HZ = 0x9, + ACCEL_CONFIG0_ODR_200_HZ = 0x8, + ACCEL_CONFIG0_ODR_400_HZ = 0x7, + ACCEL_CONFIG0_ODR_800_HZ = 0x6, + ACCEL_CONFIG0_ODR_1600_HZ = 0x5, +} ACCEL_CONFIG0_ODR_t; + +/* + * GYRO_CONFIG1 + * Register Name: GYRO_CONFIG1 + */ + +/* GYRO_UI_FILT_BW_IND */ +typedef enum { + GYRO_CONFIG1_GYRO_FILT_BW_16 = (0x07 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), + GYRO_CONFIG1_GYRO_FILT_BW_25 = (0x06 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), + GYRO_CONFIG1_GYRO_FILT_BW_34 = (0x05 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), + GYRO_CONFIG1_GYRO_FILT_BW_53 = (0x04 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), + GYRO_CONFIG1_GYRO_FILT_BW_73 = (0x03 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), + GYRO_CONFIG1_GYRO_FILT_BW_121 = (0x02 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), + GYRO_CONFIG1_GYRO_FILT_BW_180 = (0x01 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), + GYRO_CONFIG1_GYRO_FILT_BW_NO_FILTER = (0x00 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), +} GYRO_CONFIG1_GYRO_FILT_BW_t; + +/* + * ACCEL_CONFIG1 + * Register Name: ACCEL_CONFIG1 + */ + +/* ACCEL_UI_AVG_IND */ +typedef enum { + ACCEL_CONFIG1_ACCEL_FILT_AVG_64 = (0x5 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), + ACCEL_CONFIG1_ACCEL_FILT_AVG_32 = (0x4 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), + ACCEL_CONFIG1_ACCEL_FILT_AVG_16 = (0x3 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), + ACCEL_CONFIG1_ACCEL_FILT_AVG_8 = (0x2 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), + ACCEL_CONFIG1_ACCEL_FILT_AVG_4 = (0x1 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), + ACCEL_CONFIG1_ACCEL_FILT_AVG_2 = (0x0 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), +} ACCEL_CONFIG1_ACCEL_FILT_AVG_t; + +/* ACCEL_UI_FILT_BW_IND */ +typedef enum { + ACCEL_CONFIG1_ACCEL_FILT_BW_16 = (0x7 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), + ACCEL_CONFIG1_ACCEL_FILT_BW_25 = (0x6 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), + ACCEL_CONFIG1_ACCEL_FILT_BW_34 = (0x5 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), + ACCEL_CONFIG1_ACCEL_FILT_BW_53 = (0x4 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), + ACCEL_CONFIG1_ACCEL_FILT_BW_73 = (0x3 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), + ACCEL_CONFIG1_ACCEL_FILT_BW_121 = (0x2 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), + ACCEL_CONFIG1_ACCEL_FILT_BW_180 = (0x1 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), + ACCEL_CONFIG1_ACCEL_FILT_BW_NO_FILTER = (0x0 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), +} ACCEL_CONFIG1_ACCEL_FILT_BW_t; + +/* + * APEX_CONFIG0 + * Register Name: APEX_CONFIG0 + */ + +/* DMP_POWER_SAVE_EN */ +typedef enum { + APEX_CONFIG0_DMP_POWER_SAVE_EN = (0x1 << APEX_CONFIG0_DMP_POWER_SAVE_EN_POS), + APEX_CONFIG0_DMP_POWER_SAVE_DIS = (0x0 << APEX_CONFIG0_DMP_POWER_SAVE_EN_POS), +} APEX_CONFIG0_DMP_POWER_SAVE_t; + +/* DMP_INIT_EN */ +typedef enum { + APEX_CONFIG0_DMP_INIT_EN = (0x01 << APEX_CONFIG0_DMP_INIT_EN_POS), + APEX_CONFIG0_DMP_INIT_DIS = (0x00 << APEX_CONFIG0_DMP_INIT_EN_POS), +} APEX_CONFIG0_DMP_INIT_t; + +/* DMP_MEM_RESET */ +typedef enum { + APEX_CONFIG0_DMP_MEM_RESET_APEX_ST_EN = (0x01 << APEX_CONFIG0_DMP_MEM_RESET_EN_POS), + APEX_CONFIG0_DMP_MEM_RESET_DIS = (0x00 << APEX_CONFIG0_DMP_MEM_RESET_EN_POS), +} APEX_CONFIG0_DMP_MEM_RESET_t; + +/* + * APEX_CONFIG1 + * Register Name: APEX_CONFIG1 + */ + +/* SMD_ENABLE */ +typedef enum { + APEX_CONFIG1_SMD_ENABLE_DIS = (0x00 << APEX_CONFIG1_SMD_ENABLE_POS), + APEX_CONFIG1_SMD_ENABLE_EN = (0x01 << APEX_CONFIG1_SMD_ENABLE_POS), +} APEX_CONFIG1_SMD_ENABLE_t; + +/* FF_ENABLE */ +typedef enum { + APEX_CONFIG1_FF_ENABLE_DIS = (0x00 << APEX_CONFIG1_FF_ENABLE_POS), + APEX_CONFIG1_FF_ENABLE_EN = (0x01 << APEX_CONFIG1_FF_ENABLE_POS), +} APEX_CONFIG1_FF_ENABLE_t; + +/* TILT_ENABLE */ +typedef enum { + APEX_CONFIG1_TILT_ENABLE_DIS = (0x0 << APEX_CONFIG1_TILT_ENABLE_POS), + APEX_CONFIG1_TILT_ENABLE_EN = (0x1 << APEX_CONFIG1_TILT_ENABLE_POS), +} APEX_CONFIG1_TILT_ENABLE_t; + +/* PED_ENABLE */ +typedef enum { + APEX_CONFIG1_PED_ENABLE_DIS = (0x0 << APEX_CONFIG1_PED_ENABLE_POS), + APEX_CONFIG1_PED_ENABLE_EN = (0x1 << APEX_CONFIG1_PED_ENABLE_POS), +} APEX_CONFIG1_PED_ENABLE_t; + +/* DMP_ODR */ +typedef enum { + APEX_CONFIG1_DMP_ODR_25Hz = (0x0 << APEX_CONFIG1_DMP_ODR_POS), + APEX_CONFIG1_DMP_ODR_50Hz = (0x2 << APEX_CONFIG1_DMP_ODR_POS), + APEX_CONFIG1_DMP_ODR_100Hz = (0x3 << APEX_CONFIG1_DMP_ODR_POS), + APEX_CONFIG1_DMP_ODR_400Hz = (0x1 << APEX_CONFIG1_DMP_ODR_POS), +} APEX_CONFIG1_DMP_ODR_t; + +/* + * WOM_CONFIG + * Register Name: WOM_CONFIG + */ + +/* WOM_INT_DUR */ +typedef enum { + WOM_CONFIG_WOM_INT_DUR_1_SMPL = (0x00 << WOM_CONFIG_WOM_INT_DUR_POS), + WOM_CONFIG_WOM_INT_DUR_2_SMPL = (0x01 << WOM_CONFIG_WOM_INT_DUR_POS), + WOM_CONFIG_WOM_INT_DUR_3_SMPL = (0x02 << WOM_CONFIG_WOM_INT_DUR_POS), + WOM_CONFIG_WOM_INT_DUR_4_SMPL = (0x03 << WOM_CONFIG_WOM_INT_DUR_POS), +} WOM_CONFIG_WOM_INT_DUR_t; + +/* WOM_INT_MODE */ +typedef enum { + WOM_CONFIG_WOM_INT_MODE_ANDED = (0x01 << WOM_CONFIG_WOM_INT_MODE_POS), + WOM_CONFIG_WOM_INT_MODE_ORED = (0x00 << WOM_CONFIG_WOM_INT_MODE_POS), +} WOM_CONFIG_WOM_INT_MODE_t; + +/* WOM_MODE */ +typedef enum { + WOM_CONFIG_WOM_MODE_CMP_PREV = (0x01 << WOM_CONFIG_WOM_MODE_POS), + WOM_CONFIG_WOM_MODE_CMP_INIT = (0x00 << WOM_CONFIG_WOM_MODE_POS), +} WOM_CONFIG_WOM_MODE_t; + +/* WOM_ENABLE */ +typedef enum { + WOM_CONFIG_WOM_EN_ENABLE = (0x01 << WOM_CONFIG_WOM_EN_POS), + WOM_CONFIG_WOM_EN_DISABLE = (0x00 << WOM_CONFIG_WOM_EN_POS), +} WOM_CONFIG_WOM_EN_t; + +/* + * FIFO_CONFIG1 + * Register Name: FIFO_CONFIG + */ + +/* FIFO_MODE */ +typedef enum { + FIFO_CONFIG1_FIFO_MODE_SNAPSHOT = (0x01 << FIFO_CONFIG1_FIFO_MODE_POS), + FIFO_CONFIG1_FIFO_MODE_STREAM = (0x00 << FIFO_CONFIG1_FIFO_MODE_POS) +} FIFO_CONFIG1_FIFO_MODE_t; + +/* FIFO_BYPASS */ +typedef enum { + FIFO_CONFIG1_FIFO_BYPASS_ON = (0x01 << FIFO_CONFIG1_FIFO_BYPASS_POS), + FIFO_CONFIG1_FIFO_BYPASS_OFF = (0x00 << FIFO_CONFIG1_FIFO_BYPASS_POS), +} FIFO_CONFIG1_FIFO_BYPASS_t; + +/* + * APEX_DATA0 and APEX_DATA1 + * Register Name: APEX_DATA0 and APEX_DATA1 + */ + +/* Pedometer output */ +typedef struct APEX_DATA_STEP_ACTIVITY { + uint16_t step_cnt; /* Number of steps taken */ + uint8_t step_cadence; /* Walk/run cadence in number of samples. Format is u6.2.*/ + uint8_t activity_class; /* Detected activity unknown (0), walk (1) or run (2) */ +} APEX_DATA_STEP_ACTIVITY_t; + +/* + * APEX_DATA3 + * Register Name: APEX_DATA3 + */ + +/* DMP_IDLE */ +typedef enum { + APEX_DATA3_DMP_IDLE_ON = (0x01 << APEX_DATA3_DMP_IDLE_POS), + APEX_DATA3_DMP_IDLE_OFF = (0x00 << APEX_DATA3_DMP_IDLE_POS), +} APEX_DATA3_DMP_IDLE_OFF_t; + +/* ACTIVITY_CLASS */ +typedef enum { + APEX_DATA3_ACTIVITY_CLASS_OTHER = 0x0, + APEX_DATA3_ACTIVITY_CLASS_WALK = 0x1, + APEX_DATA3_ACTIVITY_CLASS_RUN = 0x2, +} APEX_DATA3_ACTIVITY_CLASS_t; + +/* + * INTF_CONFIG0 + * Register Name: INTF_CONFIG0 + */ + +/* FIFO_COUNT_REC */ +typedef enum { + INTF_CONFIG0_FIFO_COUNT_REC_RECORD = (0x01 << INTF_CONFIG0_FIFO_COUNT_FORMAT_POS), + INTF_CONFIG0_FIFO_COUNT_REC_BYTE = (0x00 << INTF_CONFIG0_FIFO_COUNT_FORMAT_POS), +} INTF_CONFIG0_FIFO_COUNT_REC_t; + +/* FIFO_COUNT_ENDIAN */ +typedef enum { + INTF_CONFIG0_FIFO_COUNT_BIG_ENDIAN = (0x01 << INTF_CONFIG0_FIFO_COUNT_ENDIAN_POS), + INTF_CONFIG0_FIFO_COUNT_LITTLE_ENDIAN = (0x00 << INTF_CONFIG0_FIFO_COUNT_ENDIAN_POS), +} INTF_CONFIG0_FIFO_COUNT_ENDIAN_t; + +/* DATA_ENDIAN */ +typedef enum { + INTF_CONFIG0_DATA_BIG_ENDIAN = (0x01 << INTF_CONFIG0_SENSOR_DATA_ENDIAN_POS), + INTF_CONFIG0_DATA_LITTLE_ENDIAN = (0x00 << INTF_CONFIG0_SENSOR_DATA_ENDIAN_POS), +} INTF_CONFIG0_DATA_ENDIAN_t; + +/* --------------------------------------------------------------------------- + * register bank MREG1 + * ---------------------------------------------------------------------------- */ + +/* + * TMST_CONFIG1_MREG1 + * Register Name: TMST_CONFIG1 + */ + +/* TMST_RES */ +typedef enum { + TMST_CONFIG1_RESOL_16us = (0x01 << TMST_CONFIG1_TMST_RES_POS), + TMST_CONFIG1_RESOL_1us = (0x00 << TMST_CONFIG1_TMST_RES_POS), +} TMST_CONFIG1_RESOL_t; + +/* TMST_FSYNC */ +typedef enum { + TMST_CONFIG1_TMST_FSYNC_EN = (0x01 << TMST_CONFIG1_TMST_FSYNC_EN_POS), + TMST_CONFIG1_TMST_FSYNC_DIS = (0x00 << TMST_CONFIG1_TMST_FSYNC_EN_POS), +} TMST_CONFIG1_TMST_FSYNC_EN_t; + +/* TMST_EN */ +typedef enum { + TMST_CONFIG1_TMST_EN = 0x01, + TMST_CONFIG1_TMST_DIS = 0x00, +} TMST_CONFIG1_TMST_EN_t; + +/* + * FIFO_CONFIG5_MREG1 + * Register Name: FIFO_CONFIG5 + */ +/* FIFO_WM_GT_TH */ +typedef enum { + FIFO_CONFIG5_WM_GT_TH_EN = (0x1 << FIFO_CONFIG5_FIFO_WM_GT_TH_POS), + FIFO_CONFIG5_WM_GT_TH_DIS = (0x0 << FIFO_CONFIG5_FIFO_WM_GT_TH_POS), +} FIFO_CONFIG5_WM_GT_t; + +/* FIFO_HIRES_EN */ +typedef enum { + FIFO_CONFIG5_HIRES_EN = (0x1 << FIFO_CONFIG5_FIFO_HIRES_EN_POS), + FIFO_CONFIG5_HIRES_DIS = (0x0 << FIFO_CONFIG5_FIFO_HIRES_EN_POS), +} FIFO_CONFIG5_HIRES_t; + +/* FIFO_TMST_FSYNC_EN */ +typedef enum { + FIFO_CONFIG5_TMST_FSYNC_EN = (0x1 << FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_POS), + FIFO_CONFIG5_TMST_FSYNC_DIS = (0x0 << FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_POS), +} FIFO_CONFIG5_TMST_FSYNC_t; + +/* FIFO_GYRO_EN */ +typedef enum { + FIFO_CONFIG5_GYRO_EN = (0x1 << FIFO_CONFIG5_FIFO_GYRO_EN_POS), + FIFO_CONFIG5_GYRO_DIS = (0x0 << FIFO_CONFIG5_FIFO_GYRO_EN_POS), +} FIFO_CONFIG5_GYRO_t; + +/* FIFO_ACCEL_EN*/ +typedef enum { + FIFO_CONFIG5_ACCEL_EN = 0x01, + FIFO_CONFIG5_ACCEL_DIS = 0x00, +} FIFO_CONFIG5_ACCEL_t; + +/* + * FSYNC_CONFIG_MREG1 + * Register Name: FSYNC_CONFIG + */ + +/* FSYNC_UI_SEL */ +typedef enum { + FSYNC_CONFIG_UI_SEL_NO = (0x0 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), + FSYNC_CONFIG_UI_SEL_TEMP = (0x1 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), + FSYNC_CONFIG_UI_SEL_GYRO_X = (0x2 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), + FSYNC_CONFIG_UI_SEL_GYRO_Y = (0x3 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), + FSYNC_CONFIG_UI_SEL_GYRO_Z = (0x4 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), + FSYNC_CONFIG_UI_SEL_ACCEL_X = (0x5 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), + FSYNC_CONFIG_UI_SEL_ACCEL_Y = (0x6 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), + FSYNC_CONFIG_UI_SEL_ACCEL_Z = (0x7 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), +} FSYNC_CONFIG_UI_SEL_t; + +/* + * ST_CONFIG_MREG1 + * Register Name: ST_CONFIG + */ +typedef enum { + ST_CONFIG_16_SAMPLES = (0 << ST_CONFIG_ST_NUMBER_SAMPLE_POS), + ST_CONFIG_200_SAMPLES = (1 << ST_CONFIG_ST_NUMBER_SAMPLE_POS), +} ST_CONFIG_NUM_SAMPLES_t; + +typedef enum { + ST_CONFIG_ACCEL_ST_LIM_50 = (7 << ST_CONFIG_ACCEL_ST_LIM_POS), +} ST_CONFIG_ACCEL_ST_LIM_t; + +typedef enum { + ST_CONFIG_GYRO_ST_LIM_50 = (7 << ST_CONFIG_GYRO_ST_LIM_POS), +} ST_CONFIG_GYRO_ST_LIM_t; + +/* + * SELFTEST_MREG1 + * Register Name: SELFTEST + */ + +/* GYRO_ST_EN and ACCEL_ST_EN */ +typedef enum { + SELFTEST_DIS = 0, + SELFTEST_ACCEL_EN = SELFTEST_ACCEL_ST_EN_MASK, + SELFTEST_GYRO_EN = SELFTEST_GYRO_ST_EN_MASK, + SELFTEST_EN = (SELFTEST_ACCEL_ST_EN_MASK | SELFTEST_GYRO_ST_EN_MASK) +} SELFTEST_ACCEL_GYRO_ST_EN_t; + +/* + * OTP_CONFIG_MREG1 + * Register Name: OTP_CONFIG + */ + +/* OTP_CONFIG */ +typedef enum { + OTP_CONFIG_OTP_COPY_TRIM = (1 << OTP_CONFIG_OTP_COPY_MODE_POS), + OTP_CONFIG_OTP_COPY_ST_DATA = (3 << OTP_CONFIG_OTP_COPY_MODE_POS), +} OTP_CONFIG_COPY_MODE_t; + +/* + * APEX_CONFIG2_MREG1 + * Register Name: APEX_CONFIG2 +*/ + +/* LOW_ENERGY_AMP_TH_SEL */ +typedef enum { + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_30_MG = (0 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_35_MG = (1 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_40_MG = (2 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_45_MG = (3 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_50_MG = (4 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_55_MG = (5 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_60_MG = (6 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_65_MG = (7 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_70_MG = (8 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_75_MG = (9 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_80_MG = (10 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_85_MG = (11 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_90_MG = (12 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_95_MG = (13 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_100_MG = (14 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), + APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_105_MG = (15 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), +} APEX_CONFIG2_LOW_ENERGY_AMP_TH_t; + +/* DMP_POWER_SAVE_TIME_SEL */ +typedef enum { + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_0_S = 0x0, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_4_S = 0x1, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_8_S = 0x2, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_12_S = 0x3, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_16_S = 0x4, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_20_S = 0x5, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_24_S = 0x6, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_28_S = 0x7, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_32_S = 0x8, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_36_S = 0x9, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_40_S = 0xA, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_44_S = 0xB, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_48_S = 0xC, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_52_S = 0xD, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_56_S = 0xE, + APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_60_S = 0xF, +} APEX_CONFIG2_DMP_POWER_SAVE_TIME_t; + +/* + * APEX_CONFIG3_MREG1 + * Register Name: APEX_CONFIG3 +*/ + +/* PEDO_AMP_TH_SEL */ +typedef enum { + APEX_CONFIG3_PEDO_AMP_TH_30_MG = (0 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_34_MG = (1 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_38_MG = (2 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_42_MG = (3 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_46_MG = (4 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_50_MG = (5 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_54_MG = (6 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_58_MG = (7 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_62_MG = (8 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_66_MG = (9 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_70_MG = (10 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_74_MG = (11 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_78_MG = (12 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_82_MG = (13 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_86_MG = (14 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), + APEX_CONFIG3_PEDO_AMP_TH_90_MG = (15 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), +} APEX_CONFIG3_PEDO_AMP_TH_t; + +/* + * APEX_CONFIG4_MREG1 + * Register Name: APEX_CONFIG4 +*/ + +/* PEDO_SB_TIMER_TH_SEL */ +typedef enum { + APEX_CONFIG4_PEDO_SB_TIMER_TH_50_SAMPLES = (0 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), + APEX_CONFIG4_PEDO_SB_TIMER_TH_75_SAMPLES = (1 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), + APEX_CONFIG4_PEDO_SB_TIMER_TH_100_SAMPLES = (2 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), + APEX_CONFIG4_PEDO_SB_TIMER_TH_125_SAMPLES = (3 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), + APEX_CONFIG4_PEDO_SB_TIMER_TH_150_SAMPLES = (4 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), + APEX_CONFIG4_PEDO_SB_TIMER_TH_175_SAMPLES = (5 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), + APEX_CONFIG4_PEDO_SB_TIMER_TH_200_SAMPLES = (6 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), + APEX_CONFIG4_PEDO_SB_TIMER_TH_225_SAMPLES = (7 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), +} APEX_CONFIG4_PEDO_SB_TIMER_TH_t; + +/* PEDO_HI_ENRGY_TH_SEL */ +typedef enum { + APEX_CONFIG4_PEDO_HI_ENRGY_TH_88_MG = (0 << APEX_CONFIG4_PED_HI_EN_TH_SEL_POS), + APEX_CONFIG4_PEDO_HI_ENRGY_TH_104_MG = (1 << APEX_CONFIG4_PED_HI_EN_TH_SEL_POS), + APEX_CONFIG4_PEDO_HI_ENRGY_TH_133_MG = (2 << APEX_CONFIG4_PED_HI_EN_TH_SEL_POS), + APEX_CONFIG4_PEDO_HI_ENRGY_TH_155_MG = (3 << APEX_CONFIG4_PED_HI_EN_TH_SEL_POS), +} APEX_CONFIG4_PEDO_HI_ENRGY_TH_t; + +/* + * APEX_CONFIG5_MREG1 + * Register Name: APEX_CONFIG5 +*/ + +/* TILT_WAIT_TIME_SEL */ +typedef enum { + APEX_CONFIG5_TILT_WAIT_TIME_0_S = (0 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS), + APEX_CONFIG5_TILT_WAIT_TIME_2_S = (1 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS), + APEX_CONFIG5_TILT_WAIT_TIME_4_S = (2 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS), + APEX_CONFIG5_TILT_WAIT_TIME_6_S = (3 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS), +} APEX_CONFIG5_TILT_WAIT_TIME_t; + +/* LOWG_PEAK_TH_HYST_SEL */ +typedef enum { + APEX_CONFIG5_LOWG_PEAK_TH_HYST_31_MG = (0 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_LOWG_PEAK_TH_HYST_63_MG = (1 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_LOWG_PEAK_TH_HYST_94_MG = (2 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_LOWG_PEAK_TH_HYST_125_MG = (3 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_LOWG_PEAK_TH_HYST_156_MG = (4 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_LOWG_PEAK_TH_HYST_188_MG = (5 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_LOWG_PEAK_TH_HYST_219_MG = (6 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_LOWG_PEAK_TH_HYST_250_MG = (7 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), +} APEX_CONFIG5_LOWG_PEAK_TH_HYST_t; + +/* HIGHG_PEAK_TH_HYST_SEL */ +typedef enum { + APEX_CONFIG5_HIGHG_PEAK_TH_HYST_31_MG = (0 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_HIGHG_PEAK_TH_HYST_63_MG = (1 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_HIGHG_PEAK_TH_HYST_94_MG = (2 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_HIGHG_PEAK_TH_HYST_125_MG = (3 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_HIGHG_PEAK_TH_HYST_156_MG = (4 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_HIGHG_PEAK_TH_HYST_188_MG = (5 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_HIGHG_PEAK_TH_HYST_219_MG = (6 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), + APEX_CONFIG5_HIGHG_PEAK_TH_HYST_250_MG = (7 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), +} APEX_CONFIG5_HIGHG_PEAK_TH_HYST_t; + +/* + * APEX_CONFIG9_MREG1 + * Register Name: APEX_CONFIG9 +*/ + +/* FF_DEBOUNCE_DURATION_SEL */ +typedef enum { + APEX_CONFIG9_FF_DEBOUNCE_DURATION_0_MS = (0 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_1250_MS = (1 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_1375_MS = (2 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_1500_MS = (3 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_1625_MS = (4 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_1750_MS = (5 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_1875_MS = (6 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_2000_MS = (7 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_2125_MS = (8 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_2250_MS = (9 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_2375_MS = (10 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_2500_MS = (11 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_2625_MS = (12 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_2750_MS = (13 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_2875_MS = (14 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), + APEX_CONFIG9_FF_DEBOUNCE_DURATION_3000_MS = (15 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), +} APEX_CONFIG9_FF_DEBOUNCE_DURATION_t; + +/* SMD_SENSITIVITY_SEL */ +typedef enum { + APEX_CONFIG9_SMD_SENSITIVITY_0 = (0 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), + APEX_CONFIG9_SMD_SENSITIVITY_1 = (1 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), + APEX_CONFIG9_SMD_SENSITIVITY_2 = (2 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), + APEX_CONFIG9_SMD_SENSITIVITY_3 = (3 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), + APEX_CONFIG9_SMD_SENSITIVITY_4 = (4 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), +} APEX_CONFIG9_SMD_SENSITIVITY_t; + +/* SMD_SENSITIVITY_MODE */ +typedef enum { + APEX_CONFIG9_SENSITIVITY_MODE_NORMAL = (0 << APEX_CONFIG9_SENSITIVITY_MODE_POS), + APEX_CONFIG9_SENSITIVITY_MODE_SLOW_WALK = (1 << APEX_CONFIG9_SENSITIVITY_MODE_POS), +} APEX_CONFIG9_SENSITIVITY_MODE_t; + +/* + * APEX_CONFIG10_MREG1 + * Register Name: APEX_CONFIG10 +*/ + +/* LOWG_PEAK_TH_SEL */ +typedef enum { + APEX_CONFIG10_LOWG_PEAK_TH_31_MG = (0x00 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_63_MG = (0x01 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_94_MG = (0x02 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_125_MG = (0x03 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_156_MG = (0x04 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_188_MG = (0x05 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_219_MG = (0x06 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_250_MG = (0x07 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_281_MG = (0x08 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_313_MG = (0x09 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_344_MG = (0x0A << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_375_MG = (0x0B << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_406_MG = (0x0C << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_438_MG = (0x0D << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_469_MG = (0x0E << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_500_MG = (0x0F << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_531_MG = (0x10 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_563_MG = (0x11 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_594_MG = (0x12 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_625_MG = (0x13 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_656_MG = (0x14 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_688_MG = (0x15 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_719_MG = (0x16 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_750_MG = (0x17 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_781_MG = (0x18 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_813_MG = (0x19 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_844_MG = (0x1A << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_875_MG = (0x1B << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_906_MG = (0x1C << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_938_MG = (0x1D << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_969_MG = (0x1E << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), + APEX_CONFIG10_LOWG_PEAK_TH_1000_MG = (0x1F << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), +} APEX_CONFIG10_LOWG_PEAK_TH_t; + +/* LOWG_TIME_TH_SEL */ +typedef enum { + APEX_CONFIG10_LOWG_TIME_TH_1_SAMPLE = (0x00 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), + APEX_CONFIG10_LOWG_TIME_TH_2_SAMPLES = (0x01 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), + APEX_CONFIG10_LOWG_TIME_TH_3_SAMPLES = (0x02 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), + APEX_CONFIG10_LOWG_TIME_TH_4_SAMPLES = (0x03 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), + APEX_CONFIG10_LOWG_TIME_TH_5_SAMPLES = (0x04 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), + APEX_CONFIG10_LOWG_TIME_TH_6_SAMPLES = (0x05 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), + APEX_CONFIG10_LOWG_TIME_TH_7_SAMPLES = (0x06 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), + APEX_CONFIG10_LOWG_TIME_TH_8_SAMPLES = (0x07 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), +} APEX_CONFIG10_LOWG_TIME_TH_SAMPLES_t; + +/* + * APEX_CONFIG11_MREG1 + * Register Name: APEX_CONFIG11 +*/ + +/* HIGHG_PEAK_TH_SEL */ +typedef enum { + APEX_CONFIG11_HIGHG_PEAK_TH_250_MG = (0x00 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_500_MG = (0x01 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_750_MG = (0x02 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_1000MG = (0x03 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_1250_MG = (0x04 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_1500_MG = (0x05 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_1750_MG = (0x06 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_2000_MG = (0x07 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_2250_MG = (0x08 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_2500_MG = (0x09 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_2750_MG = (0x0A << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_3000_MG = (0x0B << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_3250_MG = (0x0C << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_3500_MG = (0x0D << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_3750_MG = (0x0E << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_4000_MG = (0x0F << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_4250_MG = (0x10 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_4500_MG = (0x11 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_4750_MG = (0x12 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_5000_MG = (0x13 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_5250_MG = (0x14 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_5500_MG = (0x15 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_5750_MG = (0x16 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_6000_MG = (0x17 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_6250_MG = (0x18 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_6500_MG = (0x19 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_6750_MG = (0x1A << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_7000_MG = (0x1B << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_7250_MG = (0x1C << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_7500_MG = (0x1D << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_7750_MG = (0x1E << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), + APEX_CONFIG11_HIGHG_PEAK_TH_8000_MG = (0x1F << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), +} APEX_CONFIG11_HIGHG_PEAK_TH_t; + +/* HIGHG_TIME_TH_SEL */ +typedef enum { + APEX_CONFIG11_HIGHG_TIME_TH_1_SAMPLE = (0x00 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), + APEX_CONFIG11_HIGHG_TIME_TH_2_SAMPLES = (0x01 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), + APEX_CONFIG11_HIGHG_TIME_TH_3_SAMPLES = (0x02 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), + APEX_CONFIG11_HIGHG_TIME_TH_4_SAMPLES = (0x03 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), + APEX_CONFIG11_HIGHG_TIME_TH_5_SAMPLES = (0x04 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), + APEX_CONFIG11_HIGHG_TIME_TH_6_SAMPLES = (0x05 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), + APEX_CONFIG11_HIGHG_TIME_TH_7_SAMPLES = (0x06 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), + APEX_CONFIG11_HIGHG_TIME_TH_8_SAMPLES = (0x07 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), +} APEX_CONFIG11_HIGHG_TIME_TH_SAMPLES_t; + +/* + * FDR_CONFIG_MREG1 + * Register Name: FDR_CONFIG + */ + +/* FDR_SEL */ +typedef enum { + FDR_CONFIG_FDR_SEL_DIS = (0x0 << FDR_CONFIG_FDR_SEL_POS), + FDR_CONFIG_FDR_SEL_FACTOR_2 = (0x8 << FDR_CONFIG_FDR_SEL_POS), + FDR_CONFIG_FDR_SEL_FACTOR_4 = (0x9 << FDR_CONFIG_FDR_SEL_POS), + FDR_CONFIG_FDR_SEL_FACTOR_8 = (0xA << FDR_CONFIG_FDR_SEL_POS), + FDR_CONFIG_FDR_SEL_FACTOR_16 = (0xB << FDR_CONFIG_FDR_SEL_POS), + FDR_CONFIG_FDR_SEL_FACTOR_32 = (0xC << FDR_CONFIG_FDR_SEL_POS), + FDR_CONFIG_FDR_SEL_FACTOR_64 = (0xD << FDR_CONFIG_FDR_SEL_POS), + FDR_CONFIG_FDR_SEL_FACTOR_128 = (0xE << FDR_CONFIG_FDR_SEL_POS), + FDR_CONFIG_FDR_SEL_FACTOR_256 = (0xF << FDR_CONFIG_FDR_SEL_POS), +} FDR_CONFIG_FDR_SEL_t; + +/* + * APEX_CONFIG12_MREG1 + * Register Name: APEX_CONFIG12 +*/ +/* FF_MAX_DURATION_SEL */ +typedef enum { + APEX_CONFIG12_FF_MAX_DURATION_102_CM = (0 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_120_CM = (1 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_139_CM = (2 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_159_CM = (3 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_181_CM = (4 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_204_CM = (5 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_228_CM = (6 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_254_CM = (7 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_281_CM = (8 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_310_CM = (9 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_339_CM = (10 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_371_CM = (11 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_403_CM = (12 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_438_CM = (13 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_473_CM = (14 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), + APEX_CONFIG12_FF_MAX_DURATION_510_CM = (15 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), +} APEX_CONFIG12_FF_MAX_DURATION_t; + +/* FF_MIN_DURATION_SEL */ +typedef enum { + APEX_CONFIG12_FF_MIN_DURATION_10_CM = (0 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_12_CM = (1 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_13_CM = (2 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_16_CM = (3 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_18_CM = (4 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_20_CM = (5 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_23_CM = (6 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_25_CM = (7 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_28_CM = (8 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_31_CM = (9 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_34_CM = (10 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_38_CM = (11 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_41_CM = (12 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_45_CM = (13 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_48_CM = (14 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), + APEX_CONFIG12_FF_MIN_DURATION_52_CM = (15 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), +} APEX_CONFIG12_FF_MIN_DURATION_t; + +/* --------------------------------------------------------------------------- + * register bank MREG2 + * ---------------------------------------------------------------------------- */ + +/* + * OTP_CTRL7_MREG2 + * Register Name: OTP_CTRL7 +*/ + +/* OTP_CTRL7 */ +typedef enum { + OTP_CTRL7_OTP_RELOAD_EN = (1 << OTP_CTRL7_OTP_RELOAD_POS), + OTP_CTRL7_OTP_RELOAD_DIS = (0 << OTP_CTRL7_OTP_RELOAD_POS), +} OTP_CTRL7_OTP_RELOAD_t; + +/* OTP_PWR_DOWN */ +typedef enum { + OTP_CTRL7_PWR_DOWN_EN = (1 << OTP_CTRL7_OTP_PWR_DOWN_POS), + OTP_CTRL7_PWR_DOWN_DIS = (0 << OTP_CTRL7_OTP_PWR_DOWN_POS), +} OTP_CTRL7_PWR_DOWN_t; + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _INV_IMU_DEFS_H_ */ diff --git a/lib/ICM42670P/src/imu/inv_imu_driver.c b/lib/ICM42670P/src/imu/inv_imu_driver.c new file mode 100644 index 0000000..6f64e47 --- /dev/null +++ b/lib/ICM42670P/src/imu/inv_imu_driver.c @@ -0,0 +1,1578 @@ +/* + * ________________________________________________________________________________________________________ + * Copyright (c) 2017 InvenSense Inc. All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * ________________________________________________________________________________________________________ + */ + +#include "imu/inv_imu_defs.h" +#include "imu/inv_imu_extfunc.h" +#include "imu/inv_imu_driver.h" +#include "imu/inv_imu_version.h" + +static int select_rcosc(struct inv_imu_device *s); +static int select_wuosc(struct inv_imu_device *s); +static int configure_serial_interface(struct inv_imu_device *s); +static int init_hardware_from_ui(struct inv_imu_device *s); +static int resume_dmp(struct inv_imu_device *s); + +int inv_imu_init(struct inv_imu_device *s, struct inv_imu_serif *serif, + void (*sensor_event_cb)(inv_imu_sensor_event_t *event)) +{ + int status = 0; + + memset(s, 0, sizeof(*s)); + + s->transport.serif = *serif; + + /* Supply ramp time max is 3 ms */ + inv_imu_sleep_us(3000); + + /* Configure serial interface */ + status |= configure_serial_interface(s); + if (status) + return status; + + /* Register sensor event callback */ + s->sensor_event_cb = sensor_event_cb; + + /* Make sure `need_mclk_cnt` is cleared */ + s->transport.need_mclk_cnt = 0; + + /* Reset device */ + status |= inv_imu_device_reset(s); + + /* Init transport layer */ + status |= inv_imu_init_transport(s); + + /* Read and set endianness for further processing */ + status |= inv_imu_get_endianness(s); + + /* Initialize hardware */ + status |= init_hardware_from_ui(s); + + /* Set default value for sensor start/stop time */ + s->gyro_start_time_us = UINT32_MAX; + s->accel_start_time_us = UINT32_MAX; + s->gyro_power_off_tmst = UINT32_MAX; + + return status; +} + +int inv_imu_device_reset(struct inv_imu_device *s) +{ + int status = INV_ERROR_SUCCESS; + uint8_t device_config_backup; + uint8_t intf_config1_backup; + uint8_t data; + + /* Ensure BLK_SEL_R and BLK_SEL_W are set to 0 */ + data = 0; + status |= inv_imu_write_reg(s, BLK_SEL_R, 1, &data); + status |= inv_imu_write_reg(s, BLK_SEL_W, 1, &data); + + /* Backup registers to configure serial interface */ + status |= inv_imu_read_reg(s, DEVICE_CONFIG, 1, &device_config_backup); + status |= inv_imu_read_reg(s, INTF_CONFIG1, 1, &intf_config1_backup); + + /* Trigger soft reset */ + data = (uint8_t)SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_EN; + status |= inv_imu_write_reg(s, SIGNAL_PATH_RESET, 1, &data); + + /* Wait 1ms for soft reset to be effective */ + inv_imu_sleep_us(1000); + + /* Restore registers to configure serial interface */ + status |= inv_imu_write_reg(s, DEVICE_CONFIG, 1, &device_config_backup); + status |= inv_imu_write_reg(s, INTF_CONFIG1, 1, &intf_config1_backup); + + /* Clear the reset done interrupt */ + status |= inv_imu_read_reg(s, INT_STATUS, 1, &data); + if (data != INT_STATUS_RESET_DONE_INT_MASK) + status |= INV_ERROR_UNEXPECTED; + + return status; +} + +int inv_imu_get_who_am_i(struct inv_imu_device *s, uint8_t *who_am_i) +{ + return inv_imu_read_reg(s, WHO_AM_I, 1, who_am_i); +} + +static int select_rcosc(struct inv_imu_device *s) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); + data &= ~PWR_MGMT0_ACCEL_LP_CLK_SEL_MASK; + data |= PWR_MGMT0_ACCEL_LP_CLK_RCOSC; + status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data); + + return status; +} + +static int select_wuosc(struct inv_imu_device *s) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); + data &= ~PWR_MGMT0_ACCEL_LP_CLK_SEL_MASK; + data |= PWR_MGMT0_ACCEL_LP_CLK_WUOSC; + status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data); + + return status; +} + +int inv_imu_enable_accel_low_power_mode(struct inv_imu_device *s) +{ + int status = 0; + PWR_MGMT0_ACCEL_MODE_t accel_mode; + PWR_MGMT0_GYRO_MODE_t gyro_mode; + ACCEL_CONFIG0_ODR_t acc_odr_bitfield; + uint32_t accel_odr_us = 0; + uint8_t pwr_mgmt0_reg; + uint8_t accel_config0_reg; + uint8_t value; + + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + accel_mode = (PWR_MGMT0_ACCEL_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_ACCEL_MODE_MASK); + gyro_mode = (PWR_MGMT0_GYRO_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_GYRO_MODE_MASK); + + /* Check if the accelerometer is the only one enabled */ + if ((accel_mode != PWR_MGMT0_ACCEL_MODE_LP) && + ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) || (gyro_mode == PWR_MGMT0_GYRO_MODE_STANDBY))) { + /* Get accelerometer's ODR for next required wait */ + status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); + acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); + accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); + /* Select the RC OSC as clock source for the accelerometer */ + status |= select_rcosc(s); + } + + /* Enable/Switch the accelerometer in/to low power mode */ + /* Read a new time because select_rcosc() modified it */ + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + pwr_mgmt0_reg &= ~PWR_MGMT0_ACCEL_MODE_MASK; + pwr_mgmt0_reg |= PWR_MGMT0_ACCEL_MODE_LP; + status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + inv_imu_sleep_us(200); + + if ((accel_mode != PWR_MGMT0_ACCEL_MODE_LP) && + ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) || (gyro_mode == PWR_MGMT0_GYRO_MODE_STANDBY))) { + /* Wait one accelerometer ODR before switching to the WU OSC */ + if (accel_odr_us > + 200) /* if ODR is smaller than 200 us, we already waited for one ODR above. */ + inv_imu_sleep_us(accel_odr_us - 200); + status |= select_wuosc(s); + } + + if (accel_mode == PWR_MGMT0_ACCEL_MODE_OFF) { + /* First data are wrong after accel enable using IIR filter + There is no signal that says accel start-up has completed and data are stable using FIR filter + So keep track of the time at start-up to discard the invalid data, about 20ms after enable + */ + if (s->fifo_is_used) + s->accel_start_time_us = inv_imu_get_time_us(); + } + + /* Enable the automatic RCOSC power on so that FIFO is entirely powered on */ + status |= inv_imu_read_reg(s, FIFO_CONFIG6_MREG1, 1, &value); + value &= ~FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK; + status |= inv_imu_write_reg(s, FIFO_CONFIG6_MREG1, 1, &value); + + return status; +} + +int inv_imu_enable_accel_low_noise_mode(struct inv_imu_device *s) +{ + int status = 0; + PWR_MGMT0_ACCEL_MODE_t accel_mode; + PWR_MGMT0_GYRO_MODE_t gyro_mode; + ACCEL_CONFIG0_ODR_t acc_odr_bitfield; + uint32_t accel_odr_us; + uint8_t pwr_mgmt0_reg; + uint8_t accel_config0_reg; + + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + accel_mode = (PWR_MGMT0_ACCEL_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_ACCEL_MODE_MASK); + gyro_mode = (PWR_MGMT0_GYRO_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_GYRO_MODE_MASK); + /* Check if the accelerometer is the only one enabled */ + if ((accel_mode == PWR_MGMT0_ACCEL_MODE_LP) && + ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) || (gyro_mode == PWR_MGMT0_GYRO_MODE_STANDBY))) { + /* Get accelerometer's ODR for next required wait */ + status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); + acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); + accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); + /* Select the RC OSC as clock source for the accelerometer */ + status |= select_rcosc(s); + /* Wait one accel ODR before switching to low noise mode */ + inv_imu_sleep_us(accel_odr_us); + } + + /* Enable/Switch the accelerometer in/to low noise mode */ + /* Read a new time because select_rcosc() modified it */ + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + pwr_mgmt0_reg &= ~PWR_MGMT0_ACCEL_MODE_MASK; + pwr_mgmt0_reg |= PWR_MGMT0_ACCEL_MODE_LN; + status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + inv_imu_sleep_us(200); + + if (accel_mode == PWR_MGMT0_ACCEL_MODE_OFF) { + /* First data are wrong after accel enable using IIR filter + There is no signal that says accel start-up has completed and data are stable using FIR filter + So keep track of the time at start-up to discard the invalid data, about 20ms after enable + */ + if (s->fifo_is_used) + s->accel_start_time_us = inv_imu_get_time_us(); + } + + return status; +} + +int inv_imu_disable_accel(struct inv_imu_device *s) +{ + int status = 0; + int stop_fifo_usage = 0; + uint32_t accel_odr_us; + PWR_MGMT0_GYRO_MODE_t gyro_mode; + ACCEL_CONFIG0_ODR_t acc_odr_bitfield; + uint8_t pwr_mgmt0_reg; + uint8_t accel_config0_reg, fifo_cfg_6_reg; + + /* Get accelerometer's ODR for next required wait */ + status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); + acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); + accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); + + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + gyro_mode = (PWR_MGMT0_GYRO_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_GYRO_MODE_MASK); + if ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) && s->fifo_is_used) { + /* + * Accel is off and gyro is about to be turned off. + * Flush FIFO so that there is no old data at next enable time + */ + stop_fifo_usage = 1; + } + + /* Check if accel is the last sensor enabled and bit rcosc dis is not set */ + status |= inv_imu_read_reg(s, FIFO_CONFIG6_MREG1, 1, &fifo_cfg_6_reg); + if ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) && + ((fifo_cfg_6_reg & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) == 0)) { + /* + * Disable the automatic RCOSC power on to avoid extra power consumption + * in sleep mode (all sensors and clocks off) + */ + fifo_cfg_6_reg |= ((1 & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) + << FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_POS); + status |= inv_imu_write_reg(s, FIFO_CONFIG6_MREG1, 1, &fifo_cfg_6_reg); + inv_imu_sleep_us(accel_odr_us); + } + + pwr_mgmt0_reg &= ~PWR_MGMT0_ACCEL_MODE_MASK; + pwr_mgmt0_reg |= PWR_MGMT0_ACCEL_MODE_OFF; + status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + + if (stop_fifo_usage && s->fifo_is_used) { + /* Reset FIFO explicitly so there is no data left in FIFO once all sensors are off */ + status |= inv_imu_reset_fifo(s); + } + + return status; +} + +int inv_imu_enable_gyro_low_noise_mode(struct inv_imu_device *s) +{ + int status = 0; + PWR_MGMT0_ACCEL_MODE_t accel_mode; + PWR_MGMT0_GYRO_MODE_t gyro_mode; + ACCEL_CONFIG0_ODR_t acc_odr_bitfield; + uint32_t accel_odr_us; + uint8_t pwr_mgmt0_reg; + uint8_t accel_config0_reg; + uint64_t current_time; + + /* + * Powering the gyroscope on immediately after powering it off can result in device failure. + * The gyroscope proof mass can continue vibrating after it has been powered off, + * and powering it back on immediately can result in unpredictable proof mass movement. + * After powering the gyroscope off, a period of > 20 ms should be allowed + * to elapse before it is powered back on. + */ + if (s->gyro_power_off_tmst != UINT32_MAX) { + current_time = inv_imu_get_time_us(); + /* Handle rollover */ + if (current_time <= s->gyro_power_off_tmst) + current_time += UINT32_MAX; + /* If 20 ms are not elapsed since power-off error is returned */ + if ((current_time - s->gyro_power_off_tmst) <= GYR_POWER_OFF_DUR_US) + return INV_ERROR_HW; + } + + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + accel_mode = (PWR_MGMT0_ACCEL_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_ACCEL_MODE_MASK); + gyro_mode = (PWR_MGMT0_GYRO_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_GYRO_MODE_MASK); + /* Check if the accelerometer is the only one enabled */ + if ((accel_mode == PWR_MGMT0_ACCEL_MODE_LP) && + ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) || (gyro_mode == PWR_MGMT0_GYRO_MODE_STANDBY))) { + /* Get accelerometer's ODR for next required wait */ + status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); + acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); + accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); + /* Select the RC OSC as clock source for the accelerometer */ + status |= select_rcosc(s); + /* Wait one accel ODR before enabling the gyroscope */ + inv_imu_sleep_us(accel_odr_us); + } + + /* Enable/Switch the gyroscope in/to low noise mode */ + /* Read a new time because select_rcosc() modified it */ + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + pwr_mgmt0_reg &= ~PWR_MGMT0_GYRO_MODE_MASK; + pwr_mgmt0_reg |= (uint8_t)PWR_MGMT0_GYRO_MODE_LN; + status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + inv_imu_sleep_us(200); + + if (gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) { + /* First data are wrong after gyro enable using IIR filter + There is no signal that says Gyro start-up has completed and data are stable using FIR filter + and the Gyro max start-up time is 40ms + So keep track of the time at start-up to discard the invalid data, about 60ms after enable + */ + if (s->fifo_is_used) + s->gyro_start_time_us = inv_imu_get_time_us(); + } + + return status; +} + +int inv_imu_disable_gyro(struct inv_imu_device *s) +{ + int status = 0; + int stop_fifo_usage = 0; + ACCEL_CONFIG0_ODR_t acc_odr_bitfield; + uint32_t accel_odr_us; + PWR_MGMT0_ACCEL_MODE_t accel_mode; + uint8_t pwr_mgmt0_reg; + uint8_t accel_config0_reg, fifo_cfg_6_reg; + + /* Get accelerometer's ODR for next required wait */ + status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); + acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); + accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); + + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + accel_mode = (PWR_MGMT0_ACCEL_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_ACCEL_MODE_MASK); + + if ((accel_mode == PWR_MGMT0_ACCEL_MODE_OFF) && s->fifo_is_used) { + /* + * Accel is off and gyro is about to be turned off. + * Flush FIFO so that there is no old data at next enable time + */ + stop_fifo_usage = 1; + } + + /* Check if the accelerometer is enabled in low power mode */ + if (accel_mode == PWR_MGMT0_ACCEL_MODE_LP) { + /* Select the RC OSC as clock source for the accelerometer */ + status |= select_rcosc(s); + } + + /* Check if gyro is the last sensor enabled and bit rcosc dis is not set */ + status |= inv_imu_read_reg(s, FIFO_CONFIG6_MREG1, 1, &fifo_cfg_6_reg); + if ((accel_mode == PWR_MGMT0_ACCEL_MODE_OFF) && + ((fifo_cfg_6_reg & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) == 0)) { + GYRO_CONFIG0_ODR_t gyro_odr_bitfield; + uint32_t gyro_odr_us; + uint8_t gyro_config0_reg; + + /* Read gyro odr to apply it to the sleep */ + status |= inv_imu_read_reg(s, GYRO_CONFIG0, 1, &gyro_config0_reg); + gyro_odr_bitfield = (GYRO_CONFIG0_ODR_t)(gyro_config0_reg & GYRO_CONFIG0_GYRO_ODR_MASK); + gyro_odr_us = inv_imu_convert_odr_bitfield_to_us(gyro_odr_bitfield); + + /* + * Disable the automatic RCOSC power on to avoid extra power consumption + * in sleep mode (all sensors and clocks off) + */ + fifo_cfg_6_reg |= ((1 & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) + << FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_POS); + status |= inv_imu_write_reg(s, FIFO_CONFIG6_MREG1, 1, &fifo_cfg_6_reg); + inv_imu_sleep_us(gyro_odr_us); + } + + /* Read a new time because select_rcosc() modified it */ + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + pwr_mgmt0_reg &= ~PWR_MGMT0_GYRO_MODE_MASK; + pwr_mgmt0_reg |= PWR_MGMT0_GYRO_MODE_OFF; + status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); + /* keep track of gyro power-off time */ + s->gyro_power_off_tmst = inv_imu_get_time_us(); + + if (accel_mode == PWR_MGMT0_ACCEL_MODE_LP) { + /* Wait based on accelerometer ODR */ + inv_imu_sleep_us(2 * accel_odr_us); + /* Select the WU OSC as clock source for the accelerometer */ + status |= select_wuosc(s); + } + + if (stop_fifo_usage && s->fifo_is_used) { + /* Reset FIFO explicitly so there is no data left in FIFO once all sensors are off */ + status |= inv_imu_reset_fifo(s); + } + + return status; +} + +int inv_imu_enable_fsync(struct inv_imu_device *s) +{ + int status = 0; + uint8_t value; + + status |= inv_imu_switch_on_mclk(s); + + //Enable Fsync + status |= inv_imu_read_reg(s, FSYNC_CONFIG_MREG1, 1, &value); + value &= ~FSYNC_CONFIG_FSYNC_UI_SEL_MASK; + value |= (uint8_t)FSYNC_CONFIG_UI_SEL_TEMP; + status |= inv_imu_write_reg(s, FSYNC_CONFIG_MREG1, 1, &value); + + status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &value); + value &= ~TMST_CONFIG1_TMST_FSYNC_EN_MASK; + value |= TMST_CONFIG1_TMST_FSYNC_EN; + status |= inv_imu_write_reg(s, TMST_CONFIG1_MREG1, 1, &value); + + status |= inv_imu_switch_off_mclk(s); + + return status; +} + +int inv_imu_disable_fsync(struct inv_imu_device *s) +{ + int status = 0; + uint8_t value; + + status |= inv_imu_switch_on_mclk(s); + + // Disable Fsync + status |= inv_imu_read_reg(s, FSYNC_CONFIG_MREG1, 1, &value); + value &= ~FSYNC_CONFIG_FSYNC_UI_SEL_MASK; + value |= (uint8_t)FSYNC_CONFIG_UI_SEL_NO; + status |= inv_imu_write_reg(s, FSYNC_CONFIG_MREG1, 1, &value); + + status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &value); + value &= ~TMST_CONFIG1_TMST_FSYNC_EN_MASK; + value |= TMST_CONFIG1_TMST_FSYNC_DIS; + status |= inv_imu_write_reg(s, TMST_CONFIG1_MREG1, 1, &value); + + status |= inv_imu_switch_off_mclk(s); + + return status; +} + +int inv_imu_configure_wom(struct inv_imu_device *s, const uint8_t wom_x_th, const uint8_t wom_y_th, + const uint8_t wom_z_th, WOM_CONFIG_WOM_INT_MODE_t wom_int, + WOM_CONFIG_WOM_INT_DUR_t wom_dur) +{ + int status = 0; + uint8_t data[3]; + uint8_t value; + + data[0] = wom_x_th; // Set X threshold + data[1] = wom_y_th; // Set Y threshold + data[2] = wom_z_th; // Set Z threshold + status |= inv_imu_write_reg(s, ACCEL_WOM_X_THR_MREG1, sizeof(data), &data[0]); + + // Compare current sample with the previous sample and WOM from the 3 axis are ORed or ANDed to produce WOM signal. + status |= inv_imu_read_reg(s, WOM_CONFIG, 1, &value); + value &= ~WOM_CONFIG_WOM_INT_MODE_MASK; + value |= (uint8_t)WOM_CONFIG_WOM_MODE_CMP_PREV | (uint8_t)wom_int; + + // Configure the number of overthreshold event to wait before producing the WOM signal. + value &= ~WOM_CONFIG_WOM_INT_DUR_MASK; + value |= (uint8_t)wom_dur; + status |= inv_imu_write_reg(s, WOM_CONFIG, 1, &value); + + return status; +} + +int inv_imu_enable_wom(struct inv_imu_device *s) +{ + int status = 0; + uint8_t value; + inv_imu_interrupt_parameter_t config_int = { (inv_imu_interrupt_value)0 }; + + /* Disable fifo threshold int1 */ + status |= inv_imu_get_config_int1(s, &config_int); + config_int.INV_FIFO_THS = INV_IMU_DISABLE; + status |= inv_imu_set_config_int1(s, &config_int); + + /* Enable WOM */ + status |= inv_imu_read_reg(s, WOM_CONFIG, 1, &value); + value &= ~WOM_CONFIG_WOM_EN_MASK; + value |= (uint8_t)WOM_CONFIG_WOM_EN_ENABLE; + status |= inv_imu_write_reg(s, WOM_CONFIG, 1, &value); + + return status; +} + +int inv_imu_disable_wom(struct inv_imu_device *s) +{ + int status = 0; + uint8_t value; + inv_imu_interrupt_parameter_t config_int = { (inv_imu_interrupt_value)0 }; + + /* Disable WOM */ + status |= inv_imu_read_reg(s, WOM_CONFIG, 1, &value); + value &= ~WOM_CONFIG_WOM_EN_MASK; + value |= WOM_CONFIG_WOM_EN_DISABLE; + status |= inv_imu_write_reg(s, WOM_CONFIG, 1, &value); + + /* Enable fifo threshold int1 */ + status |= inv_imu_get_config_int1(s, &config_int); + config_int.INV_FIFO_THS = INV_IMU_ENABLE; + status |= inv_imu_set_config_int1(s, &config_int); + + return status; +} + +int inv_imu_get_config_int1(struct inv_imu_device *s, inv_imu_interrupt_parameter_t *it) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, INT_SOURCE0, 1, &data); + it->INV_UI_FSYNC = (inv_imu_interrupt_value)((data & INT_SOURCE0_FSYNC_INT1_EN_MASK) >> + INT_SOURCE0_FSYNC_INT1_EN_POS); + it->INV_UI_DRDY = (inv_imu_interrupt_value)((data & INT_SOURCE0_DRDY_INT1_EN_MASK) >> + INT_SOURCE0_DRDY_INT1_EN_POS); + it->INV_FIFO_THS = (inv_imu_interrupt_value)((data & INT_SOURCE0_FIFO_THS_INT1_EN_MASK) >> + INT_SOURCE0_FIFO_THS_INT1_EN_POS); + it->INV_FIFO_FULL = (inv_imu_interrupt_value)((data & INT_SOURCE0_FIFO_FULL_INT1_EN_MASK) >> + INT_SOURCE0_FIFO_FULL_INT1_EN_POS); + + status |= inv_imu_read_reg(s, INT_SOURCE1, 1, &data); + it->INV_SMD = (inv_imu_interrupt_value)((data & INT_SOURCE1_SMD_INT1_EN_MASK) >> + INT_SOURCE1_SMD_INT1_EN_POS); + it->INV_WOM_X = (inv_imu_interrupt_value)((data & INT_SOURCE1_WOM_X_INT1_EN_MASK) >> + INT_SOURCE1_WOM_X_INT1_EN_POS); + it->INV_WOM_Y = (inv_imu_interrupt_value)((data & INT_SOURCE1_WOM_Y_INT1_EN_MASK) >> + INT_SOURCE1_WOM_Y_INT1_EN_POS); + it->INV_WOM_Z = (inv_imu_interrupt_value)((data & INT_SOURCE1_WOM_Z_INT1_EN_MASK) >> + INT_SOURCE1_WOM_Z_INT1_EN_POS); + + status |= inv_imu_read_reg(s, INT_SOURCE6_MREG1, 1, &data); + it->INV_FF = (inv_imu_interrupt_value)((data & INT_SOURCE6_FF_INT1_EN_MASK) >> + INT_SOURCE6_FF_INT1_EN_POS); + it->INV_LOWG = (inv_imu_interrupt_value)((data & INT_SOURCE6_LOWG_INT1_EN_MASK) >> + INT_SOURCE6_LOWG_INT1_EN_POS); + it->INV_STEP_DET = (inv_imu_interrupt_value)((data & INT_SOURCE6_STEP_DET_INT1_EN_MASK) >> + INT_SOURCE6_STEP_DET_INT1_EN_POS); + it->INV_STEP_CNT_OVFL = (inv_imu_interrupt_value)( + (data & INT_SOURCE6_STEP_CNT_OFL_INT1_EN_MASK) >> INT_SOURCE6_STEP_CNT_OFL_INT1_EN_POS); + it->INV_TILT_DET = (inv_imu_interrupt_value)((data & INT_SOURCE6_TILT_DET_INT1_EN_MASK) >> + INT_SOURCE6_TILT_DET_INT1_EN_POS); + + return status; +} + +int inv_imu_get_config_int2(struct inv_imu_device *s, inv_imu_interrupt_parameter_t *it) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, INT_SOURCE3, 1, &data); + it->INV_UI_FSYNC = (inv_imu_interrupt_value)((data & INT_SOURCE3_FSYNC_INT2_EN_MASK) >> + INT_SOURCE3_FSYNC_INT2_EN_POS); + it->INV_UI_DRDY = (inv_imu_interrupt_value)((data & INT_SOURCE3_DRDY_INT2_EN_MASK) >> + INT_SOURCE3_DRDY_INT2_EN_POS); + it->INV_FIFO_THS = (inv_imu_interrupt_value)((data & INT_SOURCE3_FIFO_THS_INT2_EN_MASK) >> + INT_SOURCE3_FIFO_THS_INT2_EN_POS); + it->INV_FIFO_FULL = (inv_imu_interrupt_value)((data & INT_SOURCE3_FIFO_FULL_INT2_EN_MASK) >> + INT_SOURCE3_FIFO_FULL_INT2_EN_POS); + + status |= inv_imu_read_reg(s, INT_SOURCE4, 1, &data); + it->INV_SMD = (inv_imu_interrupt_value)((data & INT_SOURCE4_SMD_INT2_EN_MASK) >> + INT_SOURCE4_SMD_INT2_EN_POS); + it->INV_WOM_X = (inv_imu_interrupt_value)((data & INT_SOURCE4_WOM_X_INT2_EN_MASK) >> + INT_SOURCE4_WOM_X_INT2_EN_POS); + it->INV_WOM_Y = (inv_imu_interrupt_value)((data & INT_SOURCE4_WOM_Y_INT2_EN_MASK) >> + INT_SOURCE4_WOM_Y_INT2_EN_POS); + it->INV_WOM_Z = (inv_imu_interrupt_value)((data & INT_SOURCE4_WOM_Z_INT2_EN_MASK) >> + INT_SOURCE4_WOM_Z_INT2_EN_POS); + + status |= inv_imu_read_reg(s, INT_SOURCE7_MREG1, 1, &data); + it->INV_FF = (inv_imu_interrupt_value)((data & INT_SOURCE7_FF_INT2_EN_MASK) >> + INT_SOURCE7_FF_INT2_EN_POS); + it->INV_LOWG = (inv_imu_interrupt_value)((data & INT_SOURCE7_LOWG_INT2_EN_MASK) >> + INT_SOURCE7_LOWG_INT2_EN_POS); + it->INV_STEP_DET = (inv_imu_interrupt_value)((data & INT_SOURCE7_STEP_DET_INT2_EN_MASK) >> + INT_SOURCE7_STEP_DET_INT2_EN_POS); + it->INV_STEP_CNT_OVFL = (inv_imu_interrupt_value)( + (data & INT_SOURCE7_STEP_CNT_OFL_INT2_EN_MASK) >> INT_SOURCE7_STEP_CNT_OFL_INT2_EN_POS); + it->INV_TILT_DET = (inv_imu_interrupt_value)((data & INT_SOURCE7_TILT_DET_INT2_EN_MASK) >> + INT_SOURCE7_TILT_DET_INT2_EN_POS); + + return status; +} + +int inv_imu_set_config_int1(struct inv_imu_device *s, inv_imu_interrupt_parameter_t *it) +{ + int status = 0; + uint8_t data[2]; + + status |= inv_imu_read_reg(s, INT_SOURCE0, 2, &data[0]); + + data[0] &= ~(INT_SOURCE0_FSYNC_INT1_EN_MASK | INT_SOURCE0_DRDY_INT1_EN_MASK | + INT_SOURCE0_FIFO_THS_INT1_EN_MASK | INT_SOURCE0_FIFO_FULL_INT1_EN_MASK); + data[0] |= ((it->INV_UI_FSYNC != 0) << INT_SOURCE0_FSYNC_INT1_EN_POS); + data[0] |= ((it->INV_UI_DRDY != 0) << INT_SOURCE0_DRDY_INT1_EN_POS); + data[0] |= ((it->INV_FIFO_THS != 0) << INT_SOURCE0_FIFO_THS_INT1_EN_POS); + data[0] |= ((it->INV_FIFO_FULL != 0) << INT_SOURCE0_FIFO_FULL_INT1_EN_POS); + + data[1] &= ~(INT_SOURCE1_SMD_INT1_EN_MASK | INT_SOURCE1_WOM_X_INT1_EN_MASK | + INT_SOURCE1_WOM_Y_INT1_EN_MASK | INT_SOURCE1_WOM_Z_INT1_EN_MASK); + data[1] |= ((it->INV_SMD != 0) << INT_SOURCE1_SMD_INT1_EN_POS); + data[1] |= ((it->INV_WOM_X != 0) << INT_SOURCE1_WOM_X_INT1_EN_POS); + data[1] |= ((it->INV_WOM_Y != 0) << INT_SOURCE1_WOM_Y_INT1_EN_POS); + data[1] |= ((it->INV_WOM_Z != 0) << INT_SOURCE1_WOM_Z_INT1_EN_POS); + + status |= inv_imu_write_reg(s, INT_SOURCE0, 2, &data[0]); + + status |= inv_imu_read_reg(s, INT_SOURCE6_MREG1, 1, &data[0]); + + data[0] &= ~(INT_SOURCE6_FF_INT1_EN_MASK | INT_SOURCE6_LOWG_INT1_EN_MASK | + INT_SOURCE6_STEP_DET_INT1_EN_MASK | INT_SOURCE6_STEP_CNT_OFL_INT1_EN_MASK | + INT_SOURCE6_TILT_DET_INT1_EN_MASK); + data[0] |= ((it->INV_FF != 0) << INT_SOURCE6_FF_INT1_EN_POS); + data[0] |= ((it->INV_LOWG != 0) << INT_SOURCE6_LOWG_INT1_EN_POS); + data[0] |= ((it->INV_STEP_DET != 0) << INT_SOURCE6_STEP_DET_INT1_EN_POS); + data[0] |= ((it->INV_STEP_CNT_OVFL != 0) << INT_SOURCE6_STEP_CNT_OFL_INT1_EN_POS); + data[0] |= ((it->INV_TILT_DET != 0) << INT_SOURCE6_TILT_DET_INT1_EN_POS); + status |= inv_imu_write_reg(s, INT_SOURCE6_MREG1, 1, &data[0]); + + return status; +} + +int inv_imu_set_config_int2(struct inv_imu_device *s, inv_imu_interrupt_parameter_t *it) +{ + int status = 0; + uint8_t data[2]; + + status |= inv_imu_read_reg(s, INT_SOURCE3, 2, &data[0]); + + data[0] &= ~(INT_SOURCE3_FSYNC_INT2_EN_MASK | INT_SOURCE3_DRDY_INT2_EN_MASK | + INT_SOURCE3_FIFO_THS_INT2_EN_MASK | INT_SOURCE3_FIFO_FULL_INT2_EN_MASK); + data[0] |= ((it->INV_UI_FSYNC != 0) << INT_SOURCE3_FSYNC_INT2_EN_POS); + data[0] |= ((it->INV_UI_DRDY != 0) << INT_SOURCE3_DRDY_INT2_EN_POS); + data[0] |= ((it->INV_FIFO_THS != 0) << INT_SOURCE3_FIFO_THS_INT2_EN_POS); + data[0] |= ((it->INV_FIFO_FULL != 0) << INT_SOURCE3_FIFO_FULL_INT2_EN_POS); + + data[1] &= ~(INT_SOURCE4_SMD_INT2_EN_MASK | INT_SOURCE4_WOM_X_INT2_EN_MASK | + INT_SOURCE4_WOM_Y_INT2_EN_MASK | INT_SOURCE4_WOM_Z_INT2_EN_MASK); + data[1] |= ((it->INV_SMD != 0) << INT_SOURCE4_SMD_INT2_EN_POS); + data[1] |= ((it->INV_WOM_X != 0) << INT_SOURCE4_WOM_X_INT2_EN_POS); + data[1] |= ((it->INV_WOM_Y != 0) << INT_SOURCE4_WOM_Y_INT2_EN_POS); + data[1] |= ((it->INV_WOM_Z != 0) << INT_SOURCE4_WOM_Z_INT2_EN_POS); + + status |= inv_imu_write_reg(s, INT_SOURCE3, 2, &data[0]); + + status |= inv_imu_read_reg(s, INT_SOURCE7_MREG1, 1, &data[0]); + + data[0] &= ~(INT_SOURCE7_FF_INT2_EN_MASK | INT_SOURCE7_LOWG_INT2_EN_MASK | + INT_SOURCE7_STEP_DET_INT2_EN_MASK | INT_SOURCE7_STEP_CNT_OFL_INT2_EN_MASK | + INT_SOURCE7_TILT_DET_INT2_EN_MASK); + data[0] |= ((it->INV_FF != 0) << INT_SOURCE7_FF_INT2_EN_POS); + data[0] |= ((it->INV_LOWG != 0) << INT_SOURCE7_LOWG_INT2_EN_POS); + data[0] |= ((it->INV_STEP_DET != 0) << INT_SOURCE7_STEP_DET_INT2_EN_POS); + data[0] |= ((it->INV_STEP_CNT_OVFL != 0) << INT_SOURCE7_STEP_CNT_OFL_INT2_EN_POS); + data[0] |= ((it->INV_TILT_DET != 0) << INT_SOURCE7_TILT_DET_INT2_EN_POS); + + status |= inv_imu_write_reg(s, INT_SOURCE7_MREG1, 1, &data[0]); + + return status; +} + +int inv_imu_get_data_from_registers(struct inv_imu_device *s) +{ + int status = 0; + uint8_t int_status; + uint8_t temperature[2]; + uint8_t accel[ACCEL_DATA_SIZE]; + uint8_t gyro[GYRO_DATA_SIZE]; + inv_imu_sensor_event_t event; + + /* Ensure data ready status bit is set */ + if ((status |= inv_imu_read_reg(s, INT_STATUS_DRDY, 1, &int_status))) + return status; + + if (int_status & INT_STATUS_DRDY_DATA_RDY_INT_MASK) { + status |= inv_imu_read_reg(s, TEMP_DATA1, 2, &temperature[0]); + + if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { + event.temperature = (((int16_t)temperature[0]) << 8) | temperature[1]; + } else { + event.temperature = (((int16_t)temperature[1]) << 8) | temperature[0]; + } + + status |= inv_imu_read_reg(s, ACCEL_DATA_X1, ACCEL_DATA_SIZE, &accel[0]); + + if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { + event.accel[0] = (accel[0] << 8) | accel[1]; + event.accel[1] = (accel[2] << 8) | accel[3]; + event.accel[2] = (accel[4] << 8) | accel[5]; + } else { + event.accel[0] = (accel[1] << 8) | accel[0]; + event.accel[1] = (accel[3] << 8) | accel[2]; + event.accel[2] = (accel[5] << 8) | accel[4]; + } + + status |= inv_imu_read_reg(s, GYRO_DATA_X1, GYRO_DATA_SIZE, &gyro[0]); + + if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { + event.gyro[0] = (gyro[0] << 8) | gyro[1]; + event.gyro[1] = (gyro[2] << 8) | gyro[3]; + event.gyro[2] = (gyro[4] << 8) | gyro[5]; + } else { + event.gyro[0] = (gyro[1] << 8) | gyro[0]; + event.gyro[1] = (gyro[3] << 8) | gyro[2]; + event.gyro[2] = (gyro[5] << 8) | gyro[4]; + } + + /* call sensor event callback */ + if (s->sensor_event_cb) + s->sensor_event_cb(&event); + } + /*else: Data Ready was not reached*/ + + return status; +} + +int inv_imu_get_data_from_fifo(struct inv_imu_device *s) +{ + int status = 0; + uint8_t int_status; + uint16_t packet_count_i, packet_count, total_packet_count = 0; + uint16_t packet_size = FIFO_HEADER_SIZE + FIFO_ACCEL_DATA_SIZE + FIFO_GYRO_DATA_SIZE + + FIFO_TEMP_DATA_SIZE + FIFO_TS_FSYNC_SIZE; + fifo_header_t *header; + + /* Ensure data ready status bit is set */ + if ((status |= inv_imu_read_reg(s, INT_STATUS, 1, &int_status))) + return status; + + if ((int_status & INT_STATUS_FIFO_THS_INT_MASK) || + (int_status & INT_STATUS_FIFO_FULL_INT_MASK)) { + uint8_t data[2]; + + /* + * Make sure RCOSC is enabled to guarrantee FIFO read. + * For power optimization, this call can be ommited under specific conditions: + * - If using WM interrupt and you can guarrantee entire FIFO will be read at once. + * - If gyro is enabled or accel is in LN or LP+RCOSC mode. + * - In accel LP+WUOSC mode, if you wait 100 us after reading FIFO_COUNT and + * you can guarrantee that the FIFO will be read within 1 ms. + * Please refer to the AN-000324 for more information. + */ + status |= inv_imu_switch_on_mclk(s); + + /* FIFO record mode configured at driver init, so we read packet number, not byte count */ + if ((status |= inv_imu_read_reg(s, FIFO_COUNTH, 2, &data[0])) != INV_ERROR_SUCCESS) { + status |= inv_imu_switch_off_mclk(s); + return status; + } + + total_packet_count = (uint16_t)(data[0] | (data[1] << 8)); + packet_count = total_packet_count; + while (packet_count > 0) { + uint16_t invalid_frame_cnt = 0; + /* Read FIFO only when data is expected in FIFO */ + /* fifo_idx type variable must be large enough to parse the FIFO_MIRRORING_SIZE */ + uint16_t fifo_idx = 0; + + if (s->fifo_highres_enabled) + packet_size = FIFO_20BYTES_PACKET_SIZE; + + if ((status |= + inv_imu_read_reg(s, FIFO_DATA, packet_size * packet_count, s->fifo_data))) { + /* + * Sensor data is in FIFO according to FIFO_COUNT but failed to read FIFO, + * reset FIFO and try next chance + */ + status |= inv_imu_reset_fifo(s); + status |= inv_imu_switch_off_mclk(s); + return status; + } + + for (packet_count_i = 0; packet_count_i < packet_count; packet_count_i++) { + inv_imu_sensor_event_t event; + event.sensor_mask = 0; + header = (fifo_header_t *)&s->fifo_data[fifo_idx]; + fifo_idx += FIFO_HEADER_SIZE; + + /* Decode invalid frame, this typically happens if packet_count is greater + than 2 in case of WOM event since FIFO_THS IRQ is disabled if WOM is enabled, + and we wake up only upon a WOM event so we can have more than 1 ACC packet in FIFO + and we do not wait the oscillator wake-up time so we will receive 1 invalid packet, + which we will read again upon next FIFO read operation thanks to while() loop*/ + if (header->Byte == 0x80) { + uint8_t is_invalid_frame = 1; + /* Check N-FIFO_HEADER_SIZE remaining bytes are all 0 to be invalid frame */ + for (uint8_t i = 0; i < (packet_size - FIFO_HEADER_SIZE); i++) { + if (s->fifo_data[fifo_idx + i]) { + is_invalid_frame = 0; + break; + } + } + /* In case of invalid frame read FIFO will be retried for this packet */ + invalid_frame_cnt += is_invalid_frame; + fifo_idx += packet_size - FIFO_HEADER_SIZE; + } else { + /* Decode packet */ + if (header->bits.msg_bit) { + /* MSG BIT set in FIFO header, Resetting FIFO */ + status |= inv_imu_reset_fifo(s); + status |= inv_imu_switch_off_mclk(s); + return INV_ERROR; + } + + if (header->bits.accel_bit) { + if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { + event.accel[0] = + (s->fifo_data[0 + fifo_idx] << 8) | s->fifo_data[1 + fifo_idx]; + event.accel[1] = + (s->fifo_data[2 + fifo_idx] << 8) | s->fifo_data[3 + fifo_idx]; + event.accel[2] = + (s->fifo_data[4 + fifo_idx] << 8) | s->fifo_data[5 + fifo_idx]; + } else { + event.accel[0] = + (s->fifo_data[1 + fifo_idx] << 8) | s->fifo_data[0 + fifo_idx]; + event.accel[1] = + (s->fifo_data[3 + fifo_idx] << 8) | s->fifo_data[2 + fifo_idx]; + event.accel[2] = + (s->fifo_data[5 + fifo_idx] << 8) | s->fifo_data[4 + fifo_idx]; + } + fifo_idx += FIFO_ACCEL_DATA_SIZE; + } + + if (header->bits.gyro_bit) { + if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { + event.gyro[0] = + (s->fifo_data[0 + fifo_idx] << 8) | s->fifo_data[1 + fifo_idx]; + event.gyro[1] = + (s->fifo_data[2 + fifo_idx] << 8) | s->fifo_data[3 + fifo_idx]; + event.gyro[2] = + (s->fifo_data[4 + fifo_idx] << 8) | s->fifo_data[5 + fifo_idx]; + } else { + event.gyro[0] = + (s->fifo_data[1 + fifo_idx] << 8) | s->fifo_data[0 + fifo_idx]; + event.gyro[1] = + (s->fifo_data[3 + fifo_idx] << 8) | s->fifo_data[2 + fifo_idx]; + event.gyro[2] = + (s->fifo_data[5 + fifo_idx] << 8) | s->fifo_data[4 + fifo_idx]; + } + fifo_idx += FIFO_GYRO_DATA_SIZE; + } + + if ((header->bits.accel_bit) || (header->bits.gyro_bit)) { + /* + * The coarse temperature (8 or 16B FIFO packet format) + * range is ± 64 degrees with 0.5°C resolution. + * but the fine temperature range (2 bytes) (20B FIFO packet format) is + * ± 256 degrees with (1/128)°C resolution + */ + if (header->bits.twentybits_bit) { + if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { + event.temperature = (((int16_t)s->fifo_data[0 + fifo_idx]) << 8) | + s->fifo_data[1 + fifo_idx]; + } else { + event.temperature = (((int16_t)s->fifo_data[1 + fifo_idx]) << 8) | + s->fifo_data[0 + fifo_idx]; + } + fifo_idx += FIFO_TEMP_DATA_SIZE + FIFO_TEMP_HIGH_RES_SIZE; + + /* new temperature data */ + if (event.temperature != INVALID_VALUE_FIFO) + event.sensor_mask |= (1 << INV_SENSOR_TEMPERATURE); + } else { + /* cast to int8_t since FIFO is in 16 bits mode (temperature on 8 bits) */ + event.temperature = (int8_t)s->fifo_data[0 + fifo_idx]; + fifo_idx += FIFO_TEMP_DATA_SIZE; + + /* new temperature data */ + if (event.temperature != INVALID_VALUE_FIFO_1B) + event.sensor_mask |= (1 << INV_SENSOR_TEMPERATURE); + } + } + + if ((header->bits.timestamp_bit) || (header->bits.fsync_bit)) { + if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) + event.timestamp_fsync = + (s->fifo_data[0 + fifo_idx] << 8) | s->fifo_data[1 + fifo_idx]; + else + event.timestamp_fsync = + (s->fifo_data[1 + fifo_idx] << 8) | s->fifo_data[0 + fifo_idx]; + fifo_idx += FIFO_TS_FSYNC_SIZE; + /* new fsync event */ + if (header->bits.fsync_bit) + event.sensor_mask |= (1 << INV_SENSOR_FSYNC_EVENT); + } + + if (header->bits.accel_bit) { + if ((event.accel[0] != INVALID_VALUE_FIFO) && + (event.accel[1] != INVALID_VALUE_FIFO) && + (event.accel[2] != INVALID_VALUE_FIFO)) { + if (s->accel_start_time_us == UINT32_MAX) { + event.sensor_mask |= (1 << INV_SENSOR_ACCEL); + } else { + if (!header->bits.fsync_bit) { + /* Discard first data after startup to let output to settle */ + if ((inv_imu_get_time_us() - s->accel_start_time_us) >= + ACC_STARTUP_TIME_US) { + s->accel_start_time_us = UINT32_MAX; + event.sensor_mask |= (1 << INV_SENSOR_ACCEL); + } + } + } + + if ((event.sensor_mask & (1 << INV_SENSOR_ACCEL)) && + (header->bits.twentybits_bit)) { + event.accel_high_res[0] = (s->fifo_data[0 + fifo_idx] >> 4) & 0xF; + event.accel_high_res[1] = (s->fifo_data[1 + fifo_idx] >> 4) & 0xF; + event.accel_high_res[2] = (s->fifo_data[2 + fifo_idx] >> 4) & 0xF; + } + } + } + + if (header->bits.gyro_bit) { + if ((event.gyro[0] != INVALID_VALUE_FIFO) && + (event.gyro[1] != INVALID_VALUE_FIFO) && + (event.gyro[2] != INVALID_VALUE_FIFO)) { + if (s->gyro_start_time_us == UINT32_MAX) { + event.sensor_mask |= (1 << INV_SENSOR_GYRO); + } else { + if (!header->bits.fsync_bit) { + /* Discard first data after startup to let output to settle */ + if ((inv_imu_get_time_us() - s->gyro_start_time_us) >= + GYR_STARTUP_TIME_US) { + s->gyro_start_time_us = UINT32_MAX; + event.sensor_mask |= (1 << INV_SENSOR_GYRO); + } + } + } + + if ((event.sensor_mask & (1 << INV_SENSOR_GYRO)) && + (header->bits.twentybits_bit)) { + event.gyro_high_res[0] = (s->fifo_data[0 + fifo_idx]) & 0xF; + event.gyro_high_res[1] = (s->fifo_data[1 + fifo_idx]) & 0xF; + event.gyro_high_res[2] = (s->fifo_data[2 + fifo_idx]) & 0xF; + } + } + } + + if (header->bits.twentybits_bit) + fifo_idx += FIFO_ACCEL_GYRO_HIGH_RES_SIZE; + + /* call sensor event callback */ + if (s->sensor_event_cb) + s->sensor_event_cb(&event); + + } /* end of else invalid frame */ + } /* end of FIFO read for loop */ + packet_count = invalid_frame_cnt; + } /*end of while: packet_count > 0*/ + + status |= inv_imu_switch_off_mclk(s); + + } /*else: FIFO threshold was not reached and FIFO was not full*/ + + return total_packet_count; +} + +uint32_t inv_imu_convert_odr_bitfield_to_us(uint32_t odr_bitfield) +{ + /* + odr bitfield - frequency : odr ms + 0 - N/A + 1 - N/A + 2 - N/A + 3 - N/A + 4 - N/A + 5 - 1.6k : 0.625ms + (default) 6 - 800 : 1.25ms + 7 - 400 : 2.5 ms + 8 - 200 : 5 ms + 9 - 100 : 10 ms + 10 - 50 : 20 ms + 11 - 25 : 40 ms + 12 - 12.5 : 80 ms + 13 - 6.25 : 160 ms + 14 - 3.125 : 320 ms + 15 - 1.5625 : 640 ms + */ + + switch (odr_bitfield) { + case ACCEL_CONFIG0_ODR_1600_HZ: + return 625; + case ACCEL_CONFIG0_ODR_800_HZ: + return 1250; + case ACCEL_CONFIG0_ODR_400_HZ: + return 2500; + case ACCEL_CONFIG0_ODR_200_HZ: + return 5000; + case ACCEL_CONFIG0_ODR_100_HZ: + return 10000; + case ACCEL_CONFIG0_ODR_50_HZ: + return 20000; + case ACCEL_CONFIG0_ODR_25_HZ: + return 40000; + case ACCEL_CONFIG0_ODR_12_5_HZ: + return 80000; + case ACCEL_CONFIG0_ODR_6_25_HZ: + return 160000; + case ACCEL_CONFIG0_ODR_3_125_HZ: + return 320000; + case ACCEL_CONFIG0_ODR_1_5625_HZ: + default: + return 640000; + } +} + +int inv_imu_set_accel_frequency(struct inv_imu_device *s, const ACCEL_CONFIG0_ODR_t frequency) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &data); + data &= ~ACCEL_CONFIG0_ACCEL_ODR_MASK; + data |= frequency; + status |= inv_imu_write_reg(s, ACCEL_CONFIG0, 1, &data); + + return status; +} + +int inv_imu_set_gyro_frequency(struct inv_imu_device *s, const GYRO_CONFIG0_ODR_t frequency) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, GYRO_CONFIG0, 1, &data); + data &= ~GYRO_CONFIG0_GYRO_ODR_MASK; + data |= frequency; + status |= inv_imu_write_reg(s, GYRO_CONFIG0, 1, &data); + + return status; +} + +int inv_imu_set_accel_fsr(struct inv_imu_device *s, ACCEL_CONFIG0_FS_SEL_t accel_fsr_g) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &data); + data &= ~ACCEL_CONFIG0_ACCEL_UI_FS_SEL_MASK; + data |= accel_fsr_g; + status |= inv_imu_write_reg(s, ACCEL_CONFIG0, 1, &data); + + return status; +} + +int inv_imu_set_gyro_fsr(struct inv_imu_device *s, GYRO_CONFIG0_FS_SEL_t gyro_fsr_dps) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, GYRO_CONFIG0, 1, &data); + data &= ~GYRO_CONFIG0_GYRO_UI_FS_SEL_MASK; + data |= gyro_fsr_dps; + status |= inv_imu_write_reg(s, GYRO_CONFIG0, 1, &data); + + return status; +} + +int inv_imu_get_accel_fsr(struct inv_imu_device *s, ACCEL_CONFIG0_FS_SEL_t *accel_fsr_g) +{ + int status = 0; + uint8_t accel_config0_reg; + + if ((s->fifo_highres_enabled) && (s->fifo_is_used == INV_IMU_FIFO_ENABLED)) + *accel_fsr_g = ACCEL_CONFIG0_FS_SEL_MAX; + else { + status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); + *accel_fsr_g = + (ACCEL_CONFIG0_FS_SEL_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_UI_FS_SEL_MASK); + } + + return status; +} + +int inv_imu_get_gyro_fsr(struct inv_imu_device *s, GYRO_CONFIG0_FS_SEL_t *gyro_fsr_dps) +{ + int status = 0; + uint8_t gyro_config0_reg; + + if ((s->fifo_highres_enabled) && (s->fifo_is_used == INV_IMU_FIFO_ENABLED)) + *gyro_fsr_dps = GYRO_CONFIG0_FS_SEL_MAX; + else { + status |= inv_imu_read_reg(s, GYRO_CONFIG0, 1, &gyro_config0_reg); + *gyro_fsr_dps = + (GYRO_CONFIG0_FS_SEL_t)(gyro_config0_reg & GYRO_CONFIG0_GYRO_UI_FS_SEL_MASK); + } + + return status; +} + +int inv_imu_set_accel_lp_avg(struct inv_imu_device *s, ACCEL_CONFIG1_ACCEL_FILT_AVG_t acc_avg) +{ + uint8_t value; + int status = 0; + + status |= inv_imu_read_reg(s, ACCEL_CONFIG1, 1, &value); + if (status) + return status; + + value &= ~ACCEL_CONFIG1_ACCEL_UI_AVG_MASK; + value |= acc_avg; + + status |= inv_imu_write_reg(s, ACCEL_CONFIG1, 1, &value); + + return status; +} + +int inv_imu_set_accel_ln_bw(struct inv_imu_device *s, ACCEL_CONFIG1_ACCEL_FILT_BW_t acc_bw) +{ + uint8_t value; + int status = 0; + + status |= inv_imu_read_reg(s, ACCEL_CONFIG1, 1, &value); + if (status) + return status; + + value &= ~ACCEL_CONFIG1_ACCEL_UI_FILT_BW_MASK; + value |= acc_bw; + + status |= inv_imu_write_reg(s, ACCEL_CONFIG1, 1, &value); + + return status; +} + +int inv_imu_set_gyro_ln_bw(struct inv_imu_device *s, GYRO_CONFIG1_GYRO_FILT_BW_t gyr_bw) +{ + uint8_t value; + int status = 0; + + status |= inv_imu_read_reg(s, GYRO_CONFIG1, 1, &value); + if (status) + return status; + + value &= ~GYRO_CONFIG1_GYRO_UI_FILT_BW_MASK; + value |= gyr_bw; + + status |= inv_imu_write_reg(s, GYRO_CONFIG1, 1, &value); + + return status; +} + +int inv_imu_set_timestamp_resolution(struct inv_imu_device * s, + const TMST_CONFIG1_RESOL_t timestamp_resol) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &data); + data &= ~TMST_CONFIG1_TMST_RES_MASK; + data |= timestamp_resol; + status |= inv_imu_write_reg(s, TMST_CONFIG1_MREG1, 1, &data); + + return status; +} + +int inv_imu_reset_fifo(struct inv_imu_device *s) +{ + int status = 0; + uint8_t fifo_flush_status = (uint8_t)SIGNAL_PATH_RESET_FIFO_FLUSH_EN; + + status |= inv_imu_switch_on_mclk(s); + + status |= inv_imu_write_reg(s, SIGNAL_PATH_RESET, 1, &fifo_flush_status); + inv_imu_sleep_us(10); + + /* Wait for FIFO flush (idle bit will go high at appropriate time and unlock flush) */ + while ((status == 0) && ((fifo_flush_status & SIGNAL_PATH_RESET_FIFO_FLUSH_MASK) == + (uint8_t)SIGNAL_PATH_RESET_FIFO_FLUSH_EN)) { + status |= inv_imu_read_reg(s, SIGNAL_PATH_RESET, 1, &fifo_flush_status); + } + + status |= inv_imu_switch_off_mclk(s); + + return status; +} + +int inv_imu_enable_high_resolution_fifo(struct inv_imu_device *s) +{ + uint8_t value; + int status = 0; + + /* set FIFO packets to 20bit format (i.e. high res is enabled) */ + s->fifo_highres_enabled = 1; + + status |= inv_imu_read_reg(s, FIFO_CONFIG5_MREG1, 1, &value); + value &= ~FIFO_CONFIG5_FIFO_HIRES_EN_MASK; + value |= FIFO_CONFIG5_HIRES_EN; + status |= inv_imu_write_reg(s, FIFO_CONFIG5_MREG1, 1, &value); + + return status; +} + +int inv_imu_disable_high_resolution_fifo(struct inv_imu_device *s) +{ + uint8_t value; + int status = 0; + + /* set FIFO packets to 16bit format (i.e. high res is disabled) */ + s->fifo_highres_enabled = 0; + + status |= inv_imu_read_reg(s, FIFO_CONFIG5_MREG1, 1, &value); + value &= ~FIFO_CONFIG5_FIFO_HIRES_EN_MASK; + value |= FIFO_CONFIG5_HIRES_DIS; + status |= inv_imu_write_reg(s, FIFO_CONFIG5_MREG1, 1, &value); + + return status; +} + +int inv_imu_configure_fifo(struct inv_imu_device *s, INV_IMU_FIFO_CONFIG_t fifo_config) +{ + int status = 0; + uint8_t data; + inv_imu_interrupt_parameter_t config_int = { (inv_imu_interrupt_value)0 }; + + s->fifo_is_used = fifo_config; + + inv_imu_switch_on_mclk(s); + + switch (fifo_config) { + case INV_IMU_FIFO_ENABLED: + /* Configure: + - FIFO record mode i.e FIFO count unit is packet + - FIFO snapshot mode i.e drop the data when the FIFO overflows + - Timestamp is logged in FIFO + - Little Endian fifo_count + */ + + status |= inv_imu_read_reg(s, INTF_CONFIG0, 1, &data); + data &= ~(INTF_CONFIG0_FIFO_COUNT_FORMAT_MASK | INTF_CONFIG0_FIFO_COUNT_ENDIAN_MASK); + data |= (uint8_t)INTF_CONFIG0_FIFO_COUNT_REC_RECORD | + (uint8_t)INTF_CONFIG0_FIFO_COUNT_LITTLE_ENDIAN; + status |= inv_imu_write_reg(s, INTF_CONFIG0, 1, &data); + + status |= inv_imu_read_reg(s, FIFO_CONFIG1, 1, &data); + data &= ~(FIFO_CONFIG1_FIFO_MODE_MASK | FIFO_CONFIG1_FIFO_BYPASS_MASK); + data |= (uint8_t)FIFO_CONFIG1_FIFO_MODE_SNAPSHOT | (uint8_t)FIFO_CONFIG1_FIFO_BYPASS_OFF; + status |= inv_imu_write_reg(s, FIFO_CONFIG1, 1, &data); + + status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &data); + data &= ~TMST_CONFIG1_TMST_EN_MASK; + data |= TMST_CONFIG1_TMST_EN; + status |= inv_imu_write_reg(s, TMST_CONFIG1_MREG1, 1, &data); + + /* restart and reset FIFO configuration */ + status |= inv_imu_read_reg(s, FIFO_CONFIG5_MREG1, 1, &data); + data &= ~(FIFO_CONFIG5_FIFO_GYRO_EN_MASK | FIFO_CONFIG5_FIFO_ACCEL_EN_MASK | + FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_MASK | FIFO_CONFIG5_FIFO_HIRES_EN_MASK); + data |= ((uint8_t)FIFO_CONFIG5_GYRO_EN | (uint8_t)FIFO_CONFIG5_ACCEL_EN | + (uint8_t)FIFO_CONFIG5_TMST_FSYNC_EN | (uint8_t)FIFO_CONFIG5_WM_GT_TH_EN); + status |= inv_imu_write_reg(s, FIFO_CONFIG5_MREG1, 1, &data); + + // Configure FIFO WM so that INT is triggered for each packet + data = 0x1; + status |= inv_imu_write_reg(s, FIFO_CONFIG2, 1, &data); + + /* Disable Data Ready Interrupt */ + status |= inv_imu_get_config_int1(s, &config_int); + config_int.INV_UI_DRDY = INV_IMU_DISABLE; + status |= inv_imu_set_config_int1(s, &config_int); + break; + + case INV_IMU_FIFO_DISABLED: + /* make sure FIFO is disabled */ + status |= inv_imu_read_reg(s, FIFO_CONFIG1, 1, &data); + data &= ~FIFO_CONFIG1_FIFO_BYPASS_MASK; + data |= (uint8_t)FIFO_CONFIG1_FIFO_BYPASS_ON; + status |= inv_imu_write_reg(s, FIFO_CONFIG1, 1, &data); + + /* restart and reset FIFO configuration */ + status |= inv_imu_read_reg(s, FIFO_CONFIG5_MREG1, 1, &data); + data &= ~(FIFO_CONFIG5_FIFO_GYRO_EN_MASK | FIFO_CONFIG5_FIFO_ACCEL_EN_MASK | + FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_MASK); + data |= ((uint8_t)FIFO_CONFIG5_GYRO_DIS | (uint8_t)FIFO_CONFIG5_ACCEL_DIS | + (uint8_t)FIFO_CONFIG5_TMST_FSYNC_EN); + status |= inv_imu_write_reg(s, FIFO_CONFIG5_MREG1, 1, &data); + + /* Enable Data Ready Interrupt */ + status |= inv_imu_get_config_int1(s, &config_int); + config_int.INV_UI_DRDY = INV_IMU_ENABLE; + status |= inv_imu_set_config_int1(s, &config_int); + break; + + default: + status = -1; + } + + status |= inv_imu_switch_off_mclk(s); + + return status; +} + +uint32_t inv_imu_get_timestamp_resolution_us(struct inv_imu_device *s) +{ + int status = 0; + uint8_t tmst_config1_reg; + TMST_CONFIG1_RESOL_t tmst_resol; + + status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &tmst_config1_reg); + if (status < 0) + return INV_ERROR; + + tmst_resol = (TMST_CONFIG1_RESOL_t)(tmst_config1_reg & TMST_CONFIG1_TMST_RES_MASK); + + if (tmst_resol == TMST_CONFIG1_RESOL_16us) + return 16; + else if (tmst_resol == TMST_CONFIG1_RESOL_1us) + return 1; + + // Should not happen, return 0 + return 0; +} + +const char *inv_imu_get_version(void) +{ + return INV_IMU_VERSION_STRING; +} + +/* + * Static functions definition + */ +static int configure_serial_interface(struct inv_imu_device *s) +{ + uint8_t value; + int status = 0; + + /* Ensure BLK_SEL_R and BLK_SEL_W are set to 0 */ + value = 0; + status |= inv_imu_write_reg(s, BLK_SEL_R, 1, &value); + status |= inv_imu_write_reg(s, BLK_SEL_W, 1, &value); + + if (s->transport.serif.serif_type == UI_I2C) { + /* Enable I2C 50ns spike filtering */ + status |= inv_imu_read_reg(s, INTF_CONFIG1, 1, &value); + value &= ~(INTF_CONFIG1_I3C_SDR_EN_MASK | INTF_CONFIG1_I3C_DDR_EN_MASK); + status |= inv_imu_write_reg(s, INTF_CONFIG1, 1, &value); + } else { + /* Configure SPI */ + if (s->transport.serif.serif_type == UI_SPI4) + value = (uint8_t)DEVICE_CONFIG_SPI_4WIRE | (uint8_t)DEVICE_CONFIG_SPI_MODE_0_3; + else if (s->transport.serif.serif_type == UI_SPI3) + value = (uint8_t)DEVICE_CONFIG_SPI_3WIRE | (uint8_t)DEVICE_CONFIG_SPI_MODE_0_3; + else + return INV_ERROR_BAD_ARG; /* Not supported */ + status |= inv_imu_write_reg(s, DEVICE_CONFIG, 1, &value); + + /* Device operation in shared spi bus configuration (AN-000352) */ + status |= inv_imu_read_reg(s, INTF_CONFIG0, 1, &value); + value |= 0x3; + status |= inv_imu_write_reg(s, INTF_CONFIG0, 1, &value); + } + + return status; +} + +static int init_hardware_from_ui(struct inv_imu_device *s) +{ + int status = 0; + uint8_t value; + inv_imu_interrupt_parameter_t config_int = {(inv_imu_interrupt_value)0}; + + /* Deactivate FSYNC by default */ + status |= inv_imu_disable_fsync(s); + + /* Set default timestamp resolution 16us (Mobile use cases) */ + status |= inv_imu_set_timestamp_resolution(s, TMST_CONFIG1_RESOL_16us); + + /* Enable push pull on INT1 to avoid moving in Test Mode after a soft reset */ + status |= inv_imu_read_reg(s, INT_CONFIG, 1, &value); + value &= ~INT_CONFIG_INT1_DRIVE_CIRCUIT_MASK; + value |= (uint8_t)INT_CONFIG_INT1_DRIVE_CIRCUIT_PP; + status |= inv_imu_write_reg(s, INT_CONFIG, 1, &value); + + /* Configure the INT1 interrupt pulse as active high */ + status |= inv_imu_read_reg(s, INT_CONFIG, 1, &value); + value &= ~INT_CONFIG_INT1_POLARITY_MASK; + value |= (uint8_t)INT_CONFIG_INT1_POLARITY_HIGH; + status |= inv_imu_write_reg(s, INT_CONFIG, 1, &value); + + /* Set interrupt config */ + config_int.INV_UI_FSYNC = INV_IMU_DISABLE; + config_int.INV_UI_DRDY = INV_IMU_DISABLE; + config_int.INV_FIFO_THS = INV_IMU_ENABLE; + config_int.INV_FIFO_FULL = INV_IMU_DISABLE; + config_int.INV_SMD = INV_IMU_ENABLE; + config_int.INV_WOM_X = INV_IMU_ENABLE; + config_int.INV_WOM_Y = INV_IMU_ENABLE; + config_int.INV_WOM_Z = INV_IMU_ENABLE; + config_int.INV_FF = INV_IMU_ENABLE; + config_int.INV_LOWG = INV_IMU_ENABLE; + config_int.INV_STEP_DET = INV_IMU_ENABLE; + config_int.INV_STEP_CNT_OVFL = INV_IMU_ENABLE; + config_int.INV_TILT_DET = INV_IMU_ENABLE; + status |= inv_imu_set_config_int1(s, &config_int); + + /* Enable FIFO: use 16-bit format by default (i.e. high res is disabled) */ + status |= inv_imu_configure_fifo(s, INV_IMU_FIFO_ENABLED); + + /* + * Disable the automatic RCOSC power on to avoid + * extra power consumption in sleep mode (all sensors and clocks off) + */ + status |= inv_imu_read_reg(s, FIFO_CONFIG6_MREG1, 1, &value); + value |= ((1 & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) + << FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_POS); + status |= inv_imu_write_reg(s, FIFO_CONFIG6_MREG1, 1, &value); + + return status; +} + +int inv_imu_configure_fifo_data_rate(struct inv_imu_device *s, FDR_CONFIG_FDR_SEL_t dec_factor) +{ + int status = 0; + uint8_t data; + + status |= inv_imu_read_reg(s, FDR_CONFIG_MREG1, 1, &data); + data &= (uint8_t)~FDR_CONFIG_FDR_SEL_MASK; + data |= (uint8_t)dec_factor; + status |= inv_imu_write_reg(s, FDR_CONFIG_MREG1, 1, &data); + + return status; +} + +static int resume_dmp(struct inv_imu_device *s) +{ + int status = 0; + uint8_t value; + uint64_t start; + + status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &value); + value &= ~APEX_CONFIG0_DMP_INIT_EN_MASK; + value |= (uint8_t)APEX_CONFIG0_DMP_INIT_EN; + status |= inv_imu_write_reg(s, APEX_CONFIG0, 1, &value); + + /* wait to make sure dmp_init_en = 0 */ + start = inv_imu_get_time_us(); + do { + inv_imu_read_reg(s, APEX_CONFIG0, 1, &value); + inv_imu_sleep_us(100); + + if ((value & APEX_CONFIG0_DMP_INIT_EN_MASK) == 0) + break; + + } while (inv_imu_get_time_us() - start < 50000); + + return status; +} + +int inv_imu_start_dmp(struct inv_imu_device *s) +{ + int status = 0; + + // On first enabling of DMP, reset internal state + if (!s->dmp_is_on) { + // Reset SRAM to 0's + status |= inv_imu_reset_dmp(s, APEX_CONFIG0_DMP_MEM_RESET_APEX_ST_EN); + if (status) + return status; + s->dmp_is_on = 1; + } + + // Initialize DMP + status |= resume_dmp(s); + + return status; +} + +int inv_imu_reset_dmp(struct inv_imu_device *s, const APEX_CONFIG0_DMP_MEM_RESET_t sram_reset) +{ + const int ref_timeout = 5000; /*50 ms*/ + int status = 0; + int timeout = ref_timeout; + uint8_t data_dmp_reset; + uint8_t value = 0; + + status |= inv_imu_switch_on_mclk(s); + + // Reset DMP internal memories + status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &value); + value &= ~APEX_CONFIG0_DMP_MEM_RESET_EN_MASK; + value |= (sram_reset & APEX_CONFIG0_DMP_MEM_RESET_EN_MASK); + status |= inv_imu_write_reg(s, APEX_CONFIG0, 1, &value); + + inv_imu_sleep_us(1000); + + // Make sure reset procedure has finished by reading back mem_reset_en bit + do { + inv_imu_sleep_us(10); + status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &data_dmp_reset); + } while ( + ((data_dmp_reset & APEX_CONFIG0_DMP_MEM_RESET_EN_MASK) != APEX_CONFIG0_DMP_MEM_RESET_DIS) && + timeout-- && !status); + + status |= inv_imu_switch_off_mclk(s); + + if (timeout <= 0) + return INV_ERROR_TIMEOUT; + + return status; +} + +int inv_imu_set_endianness(struct inv_imu_device *s, INTF_CONFIG0_DATA_ENDIAN_t endianness) +{ + int status = 0; + uint8_t value; + + status |= inv_imu_read_reg(s, INTF_CONFIG0, 1, &value); + value &= ~INTF_CONFIG0_SENSOR_DATA_ENDIAN_MASK; + value |= (uint8_t)endianness; + status |= inv_imu_write_reg(s, INTF_CONFIG0, 1, &value); + + if (!status) + s->endianness_data = (uint8_t)endianness; + + return status; +} + +int inv_imu_get_endianness(struct inv_imu_device *s) +{ + int status = 0; + uint8_t value; + + status |= inv_imu_read_reg(s, INTF_CONFIG0, 1, &value); + if (!status) + s->endianness_data = value & INTF_CONFIG0_SENSOR_DATA_ENDIAN_MASK; + + return status; +} diff --git a/lib/ICM42670P/src/imu/inv_imu_driver.h b/lib/ICM42670P/src/imu/inv_imu_driver.h new file mode 100644 index 0000000..1522c72 --- /dev/null +++ b/lib/ICM42670P/src/imu/inv_imu_driver.h @@ -0,0 +1,457 @@ +/* + * ________________________________________________________________________________________________________ + * Copyright (c) 2017 InvenSense Inc. All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * ________________________________________________________________________________________________________ + */ + +/** @defgroup Driver Driver + * @brief High-level functions to drive the device + * @{ + */ + +/** @file inv_imu_driver.h */ + +#ifndef _INV_IMU_DRIVER_H_ +#define _INV_IMU_DRIVER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "imu/inv_imu_defs.h" +#include "imu/inv_imu_transport.h" + +#include "Invn/InvError.h" + +#include +#include + +/** Max FSR values for accel */ +#define ACCEL_CONFIG0_FS_SEL_MAX ACCEL_CONFIG0_FS_SEL_16g + +/** Max FSR values for gyro */ +#define GYRO_CONFIG0_FS_SEL_MAX GYRO_CONFIG0_FS_SEL_2000dps + +/** Max user offset value for accel (mg) */ +#define ACCEL_OFFUSER_MAX_MG 1000 + +/** Max user offset value for gyro (dps) */ +#define GYRO_OFFUSER_MAX_DPS 64 + +/** Max buffer size mirrored from FIFO at polling time */ +#define FIFO_MIRRORING_SIZE 16 * 258 // packet size * max_count = 4kB + +/** Accel start-up time */ +#define ACC_STARTUP_TIME_US 10000 + +/** Gyro start-up time */ +#define GYR_STARTUP_TIME_US 70000 + +/** Gyro power-off to power-on duration */ +#define GYR_POWER_OFF_DUR_US 20000 + +/** Sensor identifier for UI control function */ +enum inv_imu_sensor { + INV_SENSOR_ACCEL, /**< Accelerometer */ + INV_SENSOR_GYRO, /**< Gyroscope */ + INV_SENSOR_FSYNC_EVENT, /**< FSYNC */ + INV_SENSOR_TEMPERATURE, /**< Chip temperature */ + INV_SENSOR_DMP_PEDOMETER_EVENT, /**< Pedometer: step detected */ + INV_SENSOR_DMP_PEDOMETER_COUNT, /**< Pedometer: step counter */ + INV_SENSOR_DMP_TILT, /**< Tilt */ + INV_SENSOR_DMP_FF, /**< FreeFall */ + INV_SENSOR_DMP_LOWG, /**< Low G */ + INV_SENSOR_DMP_SMD, /**< Significant Motion Detection */ + INV_SENSOR_MAX +}; + +/** Configure Fifo usage */ +typedef enum { + INV_IMU_FIFO_DISABLED = 0, /**< Fifo is disabled and data source is sensors registers */ + INV_IMU_FIFO_ENABLED = 1, /**< Fifo is used as data source */ +} INV_IMU_FIFO_CONFIG_t; + +/** Sensor event structure definition */ +typedef struct { + int sensor_mask; + uint16_t timestamp_fsync; + int16_t accel[3]; + int16_t gyro[3]; + int16_t temperature; + int8_t accel_high_res[3]; + int8_t gyro_high_res[3]; +} inv_imu_sensor_event_t; + +/** IMU driver states definition */ +struct inv_imu_device { + /** Transport layer. + * @warning Must be the first one of struct inv_imu_device + */ + struct inv_imu_transport transport; + + /** callback executed by: + * * inv_imu_get_data_from_fifo (if FIFO is used). + * * inv_imu_get_data_from_registers (if FIFO isn't used). + * May be NULL if above API are not used by application + */ + void (*sensor_event_cb)(inv_imu_sensor_event_t *event); + + + uint8_t fifo_data[FIFO_MIRRORING_SIZE]; /**< FIFO mirroring memory area */ + uint8_t dmp_is_on; /**< DMP started status */ + uint8_t endianness_data; /**< Data endianness configuration */ + uint8_t fifo_highres_enabled; /**< Highres mode configuration */ + INV_IMU_FIFO_CONFIG_t fifo_is_used; /**< FIFO configuration */ + uint64_t gyro_start_time_us; /**< Gyro start time used to discard first samples */ + uint64_t accel_start_time_us; /**< Accel start time used to discard first samples */ + uint64_t gyro_power_off_tmst; /**< Gyro power off time */ +}; + +/** Interrupt enum state for INT1, INT2, and IBI */ +typedef enum { + INV_IMU_DISABLE = 0, + INV_IMU_ENABLE +} inv_imu_interrupt_value; + +/** Interrupt definition */ +typedef struct { + inv_imu_interrupt_value INV_UI_FSYNC; + inv_imu_interrupt_value INV_UI_DRDY; + inv_imu_interrupt_value INV_FIFO_THS; + inv_imu_interrupt_value INV_FIFO_FULL; + inv_imu_interrupt_value INV_SMD; + inv_imu_interrupt_value INV_WOM_X; + inv_imu_interrupt_value INV_WOM_Y; + inv_imu_interrupt_value INV_WOM_Z; + inv_imu_interrupt_value INV_FF; + inv_imu_interrupt_value INV_LOWG; + inv_imu_interrupt_value INV_STEP_DET; + inv_imu_interrupt_value INV_STEP_CNT_OVFL; + inv_imu_interrupt_value INV_TILT_DET; +} inv_imu_interrupt_parameter_t; + +/** @brief Initializes device. + * @param[in] s Pointer to device. + * @param[in] serif Pointer on serial interface structure. + * @param[in] sensor_event_cb Callback executed when a new sensor event is available. * + * @return 0 on success, negative value on error. + */ +int inv_imu_init(struct inv_imu_device *s, struct inv_imu_serif *serif, + void (*sensor_event_cb)(inv_imu_sensor_event_t *event)); + +/** @brief Reset device by reloading OTPs. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_device_reset(struct inv_imu_device *s); + +/** @brief return WHOAMI value. + * @param[in] s Pointer to device. + * @param[out] who_am_i WHOAMI for device. + * @return 0 on success, negative value on error. + */ +int inv_imu_get_who_am_i(struct inv_imu_device *s, uint8_t *who_am_i); + +/** @brief Enable/put accel in low power mode. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_enable_accel_low_power_mode(struct inv_imu_device *s); + +/** @brief Enable/put accel in low noise mode. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_enable_accel_low_noise_mode(struct inv_imu_device *s); + +/** @brief Disable all 3 axes of accel. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_disable_accel(struct inv_imu_device *s); + +/** @brief Enable/put gyro in low noise mode. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_enable_gyro_low_noise_mode(struct inv_imu_device *s); + +/** @brief Disable all 3 axes of gyro. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_disable_gyro(struct inv_imu_device *s); + +/** @brief Enable fsync tagging functionality. + * * Enables fsync. + * * Enables timestamp to registers. Once fsync is enabled fsync counter is pushed to + * fifo instead of timestamp. So timestamp is made available in registers. Note that + * this increase power consumption. + * * Enables fsync related interrupt. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_enable_fsync(struct inv_imu_device *s); + +/** @brief Disable fsync tagging functionality. + * * Disables fsync. + * * Disables timestamp to registers. Once fsync is disabled timestamp is pushed to fifo + * instead of fsync counter. So in order to decrease power consumption, timestamp is no + * more available in registers. + * * Disables fsync related interrupt. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_disable_fsync(struct inv_imu_device *s); + +/** @brief Configure which interrupt source can trigger INT1. + * @param[in] s Pointer to device. + * @param[in] interrupt_to_configure Structure with the corresponding state to manage INT1. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_config_int1(struct inv_imu_device * s, + inv_imu_interrupt_parameter_t *interrupt_to_configure); + +/** @brief Retrieve interrupts configuration. + * @param[in] s Pointer to device. + * @param[in] interrupt_to_configure Structure with the corresponding state to manage INT1. + * @return 0 on success, negative value on error. + */ +int inv_imu_get_config_int1(struct inv_imu_device * s, + inv_imu_interrupt_parameter_t *interrupt_to_configure); + +/** @brief Configure which interrupt source can trigger INT2. + * @param[in] s Pointer to device. + * @param[in] interrupt_to_configure Structure with the corresponding state to INT2. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_config_int2(struct inv_imu_device * s, + inv_imu_interrupt_parameter_t *interrupt_to_configure); + +/** @brief Retrieve interrupts configuration. + * @param[in] s Pointer to device. + * @param[in] interrupt_to_configure Structure with the corresponding state to manage INT2. + * @return 0 on success, negative value on error. + */ +int inv_imu_get_config_int2(struct inv_imu_device * s, + inv_imu_interrupt_parameter_t *interrupt_to_configure); + +/** @brief Read all registers containing data (temperature, accelerometer and gyroscope). + * Then it calls sensor_event_cb function passed at init for each packet. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_get_data_from_registers(struct inv_imu_device *s); + +/** @brief Read all available packets from the FIFO. + * For each packet function builds a sensor event containing packet data + * and validity information. Then it calls sensor_event_cb funtion passed + * at init for each packet. + * @param[in] s Pointer to device. + * @return Number of valid packets read on success, negative value on error. + */ +int inv_imu_get_data_from_fifo(struct inv_imu_device *s); + +/** @brief Converts ACCEL_CONFIG0_ODR_t or GYRO_CONFIG0_ODR_t enums to period expressed in us. + * @param[in] odr_bitfield An ACCEL_CONFIG0_ODR_t or GYRO_CONFIG0_ODR_t enum. + * @return The corresponding period expressed in us. + */ +uint32_t inv_imu_convert_odr_bitfield_to_us(uint32_t odr_bitfield); + +/** @brief Configure accel Output Data Rate. + * @param[in] s Pointer to device. + * @param[in] frequency The requested frequency. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_accel_frequency(struct inv_imu_device *s, const ACCEL_CONFIG0_ODR_t frequency); + +/** @brief Configure gyro Output Data Rate. + * @param[in] s Pointer to device. + * @param[in] frequency The requested frequency. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_gyro_frequency(struct inv_imu_device *s, const GYRO_CONFIG0_ODR_t frequency); + +/** @brief Set accel full scale range. + * @param[in] s Pointer to device. + * @param[in] accel_fsr_g Requested full scale range. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_accel_fsr(struct inv_imu_device *s, ACCEL_CONFIG0_FS_SEL_t accel_fsr_g); + +/** @brief Access accel full scale range. + * @param[in] s Pointer to device. + * @param[out] accel_fsr_g Current full scale range. + * @return 0 on success, negative value on error. + */ +int inv_imu_get_accel_fsr(struct inv_imu_device *s, ACCEL_CONFIG0_FS_SEL_t *accel_fsr_g); + +/** @brief Set gyro full scale range. + * @param[in] s Pointer to device. + * @param[in] gyro_fsr_dps Requested full scale range. +* @return 0 on success, negative value on error. + */ +int inv_imu_set_gyro_fsr(struct inv_imu_device *s, GYRO_CONFIG0_FS_SEL_t gyro_fsr_dps); + +/** @brief Access gyro full scale range. + * @param[in] s Pointer to device. + * @param[out] gyro_fsr_dps Current full scale range. + * @return 0 on success, negative value on error. + */ +int inv_imu_get_gyro_fsr(struct inv_imu_device *s, GYRO_CONFIG0_FS_SEL_t *gyro_fsr_dps); + +/** @brief Set accel Low-Power averaging value. + * @param[in] s Pointer to device. + * @param[in] acc_avg Requested averaging value. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_accel_lp_avg(struct inv_imu_device *s, ACCEL_CONFIG1_ACCEL_FILT_AVG_t acc_avg); + +/** @brief Set accel Low-Noise bandwidth value. + * @param[in] s Pointer to device. + * @param[in] acc_bw Requested averaging value. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_accel_ln_bw(struct inv_imu_device *s, ACCEL_CONFIG1_ACCEL_FILT_BW_t acc_bw); + +/** @brief Set gyro Low-Noise bandwidth value. + * @param[in] s Pointer to device. + * @param[in] gyr_bw Requested averaging value. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_gyro_ln_bw(struct inv_imu_device *s, GYRO_CONFIG1_GYRO_FILT_BW_t gyr_bw); + +/** @brief Set timestamp resolution. + * @param[in] s Pointer to device. + * @param[in] timestamp_resol Requested timestamp resolution. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_timestamp_resolution(struct inv_imu_device * s, + const TMST_CONFIG1_RESOL_t timestamp_resol); + +/** @brief Reset IMU fifo. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_reset_fifo(struct inv_imu_device *s); + +/** @brief Enable 20 bits raw acc and raw gyr data in fifo. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_enable_high_resolution_fifo(struct inv_imu_device *s); + +/** @brief Disable 20 bits raw acc and raw gyr data in fifo. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_disable_high_resolution_fifo(struct inv_imu_device *s); + +/** @brief Configure Fifo. + * @param[in] s Pointer to device. + * @param[in] fifo_config Fifo configuration method. + * Enabled: data are pushed to FIFO and FIFO THS interrupt is set. + * Disabled: data are not pused to FIFO and DRDY interrupt is set. + * @return 0 on success, negative value on error. + */ +int inv_imu_configure_fifo(struct inv_imu_device *s, INV_IMU_FIFO_CONFIG_t fifo_config); + +/** @brief Get timestamp resolution + * @param[in] s Pointer to device. + * @return The timestamp resolution in us or 0 in case of error + */ +uint32_t inv_imu_get_timestamp_resolution_us(struct inv_imu_device *s); + +/** @brief Enable Wake On Motion. + * @param[in] s Pointer to device. + * @param[in] wom_x_th Threshold value for the Wake on Motion Interrupt for X-axis accel. + * @param[in] wom_y_th Threshold value for the Wake on Motion Interrupt for Y-axis accel. + * @param[in] wom_z_th Threshold value for the Wake on Motion Interrupt for Z-axis accel. + * @param[in] wom_int Select which mode between AND/OR is used to generate interrupt. + * @param[in] wom_dur Select the number of overthreshold event to wait + * before generating interrupt. + * @return 0 on success, negative value on error. + */ +int inv_imu_configure_wom(struct inv_imu_device *s, const uint8_t wom_x_th, const uint8_t wom_y_th, + const uint8_t wom_z_th, WOM_CONFIG_WOM_INT_MODE_t wom_int, + WOM_CONFIG_WOM_INT_DUR_t wom_dur); + +/** @brief Enable Wake On Motion. + * WoM requests to have the accelerometer enabled to work. + * As a consequence Fifo water-mark interrupt is disabled to only trigger WoM interrupts. + * To have good performance, it's recommended to set accel ODR to 20ms + * and in Low Power Mode. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_enable_wom(struct inv_imu_device *s); + +/** @brief Disable Wake On Motion. + * Fifo water-mark interrupt is re-enabled when WoM is disabled. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_disable_wom(struct inv_imu_device *s); + +/** @brief Start DMP for APEX algorithms and selftest. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_start_dmp(struct inv_imu_device *s); + +/** @brief Reset DMP for APEX algorithms and selftest. + * @param[in] s Pointer to device. + * @param[in] sram_reset Reset mode for the SRAM. + * @return 0 on success, negative value on error. + */ +int inv_imu_reset_dmp(struct inv_imu_device *s, const APEX_CONFIG0_DMP_MEM_RESET_t sram_reset); + +/** @brief Set the UI endianness and set the inv_device endianness field. + * @param[in] s Pointer to device. + * @param[in] endianness Endianness to be set. + * @return 0 on success, negative value on error. + */ +int inv_imu_set_endianness(struct inv_imu_device *s, INTF_CONFIG0_DATA_ENDIAN_t endianness); + +/** @brief Read the UI endianness and set the inv_device endianness field. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_get_endianness(struct inv_imu_device *s); + +/** @brief Configure Fifo decimation. + * @param[in] s Pointer to device. + * @param[in] dec_factor Requested decimation factor value from 2 to 256. + * @return 0 on success, negative value on error. + */ +int inv_imu_configure_fifo_data_rate(struct inv_imu_device *s, FDR_CONFIG_FDR_SEL_t dec_factor); + +/** @brief Return driver version x.y.z-suffix as a char array + * @return driver version a char array "x.y.z-suffix" + */ +const char *inv_imu_get_version(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _INV_IMU_DRIVER_H_ */ + +/** @} */ diff --git a/lib/ICM42670P/src/imu/inv_imu_extfunc.h b/lib/ICM42670P/src/imu/inv_imu_extfunc.h new file mode 100644 index 0000000..f10e608 --- /dev/null +++ b/lib/ICM42670P/src/imu/inv_imu_extfunc.h @@ -0,0 +1,56 @@ +/* + * ________________________________________________________________________________________________________ + * Copyright (c) 2017 InvenSense Inc. All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * ________________________________________________________________________________________________________ + */ + +/** @defgroup ExtFunc ExtFunc + * @brief Extern functions (to be implemented in application) required by the driver. + * @{ + */ + +/** @file inv_imu_extfunc.h */ + +#ifndef _INV_IMU_EXTFUNC_H_ +#define _INV_IMU_EXTFUNC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Sleep function. + * @param[in] us Sleep duration in microseconds (us). + */ +extern void inv_imu_sleep_us(uint32_t us); + +/** @brief Get time function. Value is expected to be on 64bit with a 1 us resolution. + * @return The current time in us. + */ +extern uint64_t inv_imu_get_time_us(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _INV_IMU_EXTFUNC_H_ */ + +/** @} */ diff --git a/lib/ICM42670P/src/imu/inv_imu_regmap.h b/lib/ICM42670P/src/imu/inv_imu_regmap.h new file mode 100644 index 0000000..954fd6b --- /dev/null +++ b/lib/ICM42670P/src/imu/inv_imu_regmap.h @@ -0,0 +1,3391 @@ +/* + *________________________________________________________________________________________________________ + * Copyright (c) 2017 InvenSense Inc. All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense and its licensors intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + OF THE SOFTWARE. + * ________________________________________________________________________________________________________ + */ +#ifndef _INV_IMU_REGMAP_H_ +#define _INV_IMU_REGMAP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file inv_imu_regmap.h + * File exposing the device register map + */ + +#include + +/* forward declaration */ +struct inv_imu_device; + + + +/* ---------------------------------------------------------------------------- + * Device Register map + * + * Next macros defines address for all registers as listed by device + * datasheet. + * Macros name is with REGISTER_NAME being the name of + * the corresponding register in datasheet. + * Note that macro name is _Bx with x being the bank + * number for registers that are in bank 1 and further (suffix is ommitted for + * bank 0 registers) + * ---------------------------------------------------------------------------- */ + +/* BANK0 */ +#define MCLK_RDY 0x10000 +#define DEVICE_CONFIG 0x10001 +#define SIGNAL_PATH_RESET 0x10002 +#define DRIVE_CONFIG1 0x10003 +#define DRIVE_CONFIG2 0x10004 +#define DRIVE_CONFIG3 0x10005 +#define INT_CONFIG 0x10006 +#define TEMP_DATA1 0x10009 +#define TEMP_DATA0 0x1000a +#define ACCEL_DATA_X1 0x1000b +#define ACCEL_DATA_X0 0x1000c +#define ACCEL_DATA_Y1 0x1000d +#define ACCEL_DATA_Y0 0x1000e +#define ACCEL_DATA_Z1 0x1000f +#define ACCEL_DATA_Z0 0x10010 +#define GYRO_DATA_X1 0x10011 +#define GYRO_DATA_X0 0x10012 +#define GYRO_DATA_Y1 0x10013 +#define GYRO_DATA_Y0 0x10014 +#define GYRO_DATA_Z1 0x10015 +#define GYRO_DATA_Z0 0x10016 +#define TMST_FSYNCH 0x10017 +#define TMST_FSYNCL 0x10018 +#define APEX_DATA4 0x1001d +#define APEX_DATA5 0x1001e +#define PWR_MGMT0 0x1001f +#define GYRO_CONFIG0 0x10020 +#define ACCEL_CONFIG0 0x10021 +#define TEMP_CONFIG0 0x10022 +#define GYRO_CONFIG1 0x10023 +#define ACCEL_CONFIG1 0x10024 +#define APEX_CONFIG0 0x10025 +#define APEX_CONFIG1 0x10026 +#define WOM_CONFIG 0x10027 +#define FIFO_CONFIG1 0x10028 +#define FIFO_CONFIG2 0x10029 +#define FIFO_CONFIG3 0x1002a +#define INT_SOURCE0 0x1002b +#define INT_SOURCE1 0x1002c +#define INT_SOURCE3 0x1002d +#define INT_SOURCE4 0x1002e +#define FIFO_LOST_PKT0 0x1002f +#define FIFO_LOST_PKT1 0x10030 +#define APEX_DATA0 0x10031 +#define APEX_DATA1 0x10032 +#define APEX_DATA2 0x10033 +#define APEX_DATA3 0x10034 +#define INTF_CONFIG0 0x10035 +#define INTF_CONFIG1 0x10036 +#define INT_STATUS_DRDY 0x10039 +#define INT_STATUS 0x1003a +#define INT_STATUS2 0x1003b +#define INT_STATUS3 0x1003c +#define FIFO_COUNTH 0x1003d +#define FIFO_COUNTL 0x1003e +#define FIFO_DATA 0x1003f +#define WHO_AM_I 0x10075 +#define BLK_SEL_W 0x10079 +#define MADDR_W 0x1007a +#define M_W 0x1007b +#define BLK_SEL_R 0x1007c +#define MADDR_R 0x1007d +#define M_R 0x1007e + +/* MREG1 */ +#define TMST_CONFIG1_MREG1 0x00 +#define FIFO_CONFIG5_MREG1 0x01 +#define FIFO_CONFIG6_MREG1 0x02 +#define FSYNC_CONFIG_MREG1 0x03 +#define INT_CONFIG0_MREG1 0x04 +#define INT_CONFIG1_MREG1 0x05 +#define SENSOR_CONFIG3_MREG1 0x06 +#define ST_CONFIG_MREG1 0x13 +#define SELFTEST_MREG1 0x14 +#define INTF_CONFIG6_MREG1 0x23 +#define INTF_CONFIG10_MREG1 0x25 +#define INTF_CONFIG7_MREG1 0x28 +#define OTP_CONFIG_MREG1 0x2b +#define INT_SOURCE6_MREG1 0x2f +#define INT_SOURCE7_MREG1 0x30 +#define INT_SOURCE8_MREG1 0x31 +#define INT_SOURCE9_MREG1 0x32 +#define INT_SOURCE10_MREG1 0x33 +#define APEX_CONFIG2_MREG1 0x44 +#define APEX_CONFIG3_MREG1 0x45 +#define APEX_CONFIG4_MREG1 0x46 +#define APEX_CONFIG5_MREG1 0x47 +#define APEX_CONFIG9_MREG1 0x48 +#define APEX_CONFIG10_MREG1 0x49 +#define APEX_CONFIG11_MREG1 0x4a +#define ACCEL_WOM_X_THR_MREG1 0x4b +#define ACCEL_WOM_Y_THR_MREG1 0x4c +#define ACCEL_WOM_Z_THR_MREG1 0x4d +#define OFFSET_USER0_MREG1 0x4e +#define OFFSET_USER1_MREG1 0x4f +#define OFFSET_USER2_MREG1 0x50 +#define OFFSET_USER3_MREG1 0x51 +#define OFFSET_USER4_MREG1 0x52 +#define OFFSET_USER5_MREG1 0x53 +#define OFFSET_USER6_MREG1 0x54 +#define OFFSET_USER7_MREG1 0x55 +#define OFFSET_USER8_MREG1 0x56 +#define ST_STATUS1_MREG1 0x63 +#define ST_STATUS2_MREG1 0x64 +#define FDR_CONFIG_MREG1 0x66 +#define APEX_CONFIG12_MREG1 0x67 + +/* MREG3 */ +#define XA_ST_DATA_MREG3 0x5000 +#define YA_ST_DATA_MREG3 0x5001 +#define ZA_ST_DATA_MREG3 0x5002 +#define XG_ST_DATA_MREG3 0x5003 +#define YG_ST_DATA_MREG3 0x5004 +#define ZG_ST_DATA_MREG3 0x5005 + +/* MREG2 */ +#define OTP_CTRL7_MREG2 0x2806 + + +/* --------------------------------------------------------------------------- + * register BANK0 + * ---------------------------------------------------------------------------*/ + +/* + * MCLK_RDY + * Register Name : MCLK_RDY + */ + +/* + * mclk_rdy + * 0: Indicates internal clock is currently not running + * 1: Indicates internal clock is currently running + */ +#define MCLK_RDY_MCLK_RDY_POS 0x03 +#define MCLK_RDY_MCLK_RDY_MASK (0x01 << MCLK_RDY_MCLK_RDY_POS) + +/* + * DEVICE_CONFIG + * Register Name : DEVICE_CONFIG + */ + +/* + * spi_ap_4wire + * 0: AP interface uses 3-wire SPI mode + * 1: AP interface uses 4-wire SPI mode + */ +#define DEVICE_CONFIG_SPI_AP_4WIRE_POS 0x02 +#define DEVICE_CONFIG_SPI_AP_4WIRE_MASK (0x01 << DEVICE_CONFIG_SPI_AP_4WIRE_POS) + +/* + * spi_mode + * SPI mode selection + * + * 0: Mode 0 and Mode 3 + * 1: Mode 1 and Mode 2 + * + * If device is operating in non-SPI mode, user is not allowed to change the power-on default setting of this register. Change of this register setting will not take effect till AP_CS = 1. + */ +#define DEVICE_CONFIG_SPI_MODE_POS 0x00 +#define DEVICE_CONFIG_SPI_MODE_MASK 0x01 + + + +/* + * SIGNAL_PATH_RESET + * Register Name : SIGNAL_PATH_RESET + */ + +/* + * soft_reset_device_config + * Software Reset (auto clear bit) + * + * 0: Software reset not enabled + * 1: Software reset enabled + */ +#define SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_POS 0x04 +#define SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_MASK (0x01 << SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_POS) + +/* + * fifo_flush + * When set to 1, FIFO will get flushed. + * FIFO flush requires the following programming sequence: + * • Write FIFO_FLUSH =1 + * • Wait for 1.5 µs + * • Read FIFO_FLUSH, it should now be 0 + * Host can only program this register bit to 1. + */ +#define SIGNAL_PATH_RESET_FIFO_FLUSH_POS 0x02 +#define SIGNAL_PATH_RESET_FIFO_FLUSH_MASK (0x01 << SIGNAL_PATH_RESET_FIFO_FLUSH_POS) + + + +/* + * DRIVE_CONFIG1 + * Register Name : DRIVE_CONFIG1 + */ + +/* + * i3c_ddr_slew_rate + * Controls slew rate for output pin 14 when device is in I3CSM DDR protocol. + * While in I3CSM operation, the device automatically switches to use I3C_DDR_SLEW_RATE after receiving ENTHDR0 ccc command from the host. The device automatically switches back to I3C_SDR_SLEW_RATE after the host issues HDR_EXIT pattern. + * + * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns + * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns + * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns + * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns + * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns + * 101: MAX: 2 ns + * 110: Reserved + * 111: Reserved + * + * This register field should not be programmed in I3C/DDR mode. + */ +#define DRIVE_CONFIG1_I3C_DDR_SLEW_RATE_POS 0x03 +#define DRIVE_CONFIG1_I3C_DDR_SLEW_RATE_MASK (0x07 << DRIVE_CONFIG1_I3C_DDR_SLEW_RATE_POS) + +/* + * i3c_sdr_slew_rate + * Controls slew rate for output pin 14 in I3CSM SDR protocol. + * After device reset, I2C_SLEW_RATE is used by default. If I3CSM feature is enabled, the device automatically switches to use I3C_SDR_SLEW_RATE after receiving 0x7E+W message (an I3CSM broadcast message). + * + * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns + * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns + * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns + * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns + * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns + * 101: MAX: 2 ns + * 110: Reserved + * 111: Reserved + * + * This register field should not be programmed in I3C/DDR mode + */ +#define DRIVE_CONFIG1_I3C_SDR_SLEW_RATE_POS 0x00 +#define DRIVE_CONFIG1_I3C_SDR_SLEW_RATE_MASK 0x07 + + + +/* + * DRIVE_CONFIG2 + * Register Name : DRIVE_CONFIG2 + */ + +/* + * i2c_slew_rate + * Controls slew rate for output pin 14 in I2C mode. + * After device reset, the I2C_SLEW_RATE is used by default. If the 1st write operation from host is an SPI transaction, the device automatically switches to SPI_SLEW_RATE. If I3CSM feature is enabled, the device automatically switches to I3C_SDR_SLEW_RATE after receiving 0x7E+W message (an I3C broadcast message). + * + * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns + * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns + * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns + * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns + * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns + * 101: MAX: 2 ns + * 110: Reserved + * 111: Reserved + * + * This register field should not be programmed in I3C/DDR mode + */ +#define DRIVE_CONFIG2_I2C_SLEW_RATE_POS 0x03 +#define DRIVE_CONFIG2_I2C_SLEW_RATE_MASK (0x07 << DRIVE_CONFIG2_I2C_SLEW_RATE_POS) + +/* + * all_slew_rate + * Configure drive strength for all output pins in all modes (SPI3, SPI4, I2C, I3CSM) excluding pin 14. + * + * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns + * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns + * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns + * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns + * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns + * 101: MAX: 2 ns + * 110: Reserved + * 111: Reserved + * + * This register field should not be programmed in I3C/DDR mode + */ +#define DRIVE_CONFIG2_ALL_SLEW_RATE_POS 0x00 +#define DRIVE_CONFIG2_ALL_SLEW_RATE_MASK 0x07 + + + +/* + * DRIVE_CONFIG3 + * Register Name : DRIVE_CONFIG3 + */ + +/* + * spi_slew_rate + * Controls slew rate for output pin 14 in SPI 3-wire mode. In SPI 4-wire mode this register controls the slew rate of pin 1 as it is used as an output in SPI 4-wire mode only. After chip reset, the I2C_SLEW_RATE is used by default for pin 14 pin. If the 1st write operation from the host is an SPI3/4 transaction, the device automatically switches to SPI_SLEW_RATE. + * + * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns + * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns + * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns + * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns + * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns + * 101: MAX: 2 ns + * 110: Reserved + * 111: Reserved + * + * This register field should not be programmed in I3C/DDR mode + */ +#define DRIVE_CONFIG3_SPI_SLEW_RATE_POS 0x00 +#define DRIVE_CONFIG3_SPI_SLEW_RATE_MASK 0x07 + + + +/* + * INT_CONFIG + * Register Name : INT_CONFIG + */ + +/* + * int2_mode + * Interrupt mode and drive circuit shall be configurable by register. + * Interrupt Mode + * 1: Latched Mode + * 0: Pulsed Mode + */ +#define INT_CONFIG_INT2_MODE_POS 0x05 +#define INT_CONFIG_INT2_MODE_MASK (0x01 << INT_CONFIG_INT2_MODE_POS) + +/* + * int2_drive_circuit + * Interrupt mode and drive circuit shall be configurable by register. + * Drive Circuit + * 1: Push-Pull + * 0: Open drain + */ +#define INT_CONFIG_INT2_DRIVE_CIRCUIT_POS 0x04 +#define INT_CONFIG_INT2_DRIVE_CIRCUIT_MASK (0x01 << INT_CONFIG_INT2_DRIVE_CIRCUIT_POS) + +/* + * int2_polarity + * Interrupt mode and drive circuit shall be configurable by register. + * Interrupt Polarity + * 1: Active High + * 0: Active Low + */ +#define INT_CONFIG_INT2_POLARITY_POS 0x03 +#define INT_CONFIG_INT2_POLARITY_MASK (0x01 << INT_CONFIG_INT2_POLARITY_POS) + +/* + * int1_mode + * Interrupt mode and drive circuit shall be configurable by register. + * Interrupt Mode + * 1: Latched Mode + * 0: Pulsed Mode + */ +#define INT_CONFIG_INT1_MODE_POS 0x02 +#define INT_CONFIG_INT1_MODE_MASK (0x01 << INT_CONFIG_INT1_MODE_POS) + +/* + * int1_drive_circuit + * Interrupt mode and drive circuit shall be configurable by register. + * Drive Circuit + * 1: Push-Pull + * 0: Open drain + */ +#define INT_CONFIG_INT1_DRIVE_CIRCUIT_POS 0x01 +#define INT_CONFIG_INT1_DRIVE_CIRCUIT_MASK (0x01 << INT_CONFIG_INT1_DRIVE_CIRCUIT_POS) + +/* + * int1_polarity + * Interrupt mode and drive circuit shall be configurable by register. + * Interrupt Polarity + * 1: Active High + * 0: Active Low + */ +#define INT_CONFIG_INT1_POLARITY_POS 0x00 +#define INT_CONFIG_INT1_POLARITY_MASK 0x01 + + + +/* + * TEMP_DATA1 + * Register Name : TEMP_DATA1 + */ + +/* + * temp_data + * Temperature data + */ +#define TEMP_DATA1_TEMP_DATA_POS 0x00 +#define TEMP_DATA1_TEMP_DATA_MASK 0xff + + + +/* + * TEMP_DATA0 + * Register Name : TEMP_DATA0 + */ + +/* + * temp_data + * Temperature data + */ +#define TEMP_DATA0_TEMP_DATA_POS 0x00 +#define TEMP_DATA0_TEMP_DATA_MASK 0xff + + + +/* + * ACCEL_DATA_X1 + * Register Name : ACCEL_DATA_X1 + */ + +/* + * accel_data_x + * Accel X axis data + */ +#define ACCEL_DATA_X1_ACCEL_DATA_X_POS 0x00 +#define ACCEL_DATA_X1_ACCEL_DATA_X_MASK 0xff + + + +/* + * ACCEL_DATA_X0 + * Register Name : ACCEL_DATA_X0 + */ + +/* + * accel_data_x + * Accel X axis data + */ +#define ACCEL_DATA_X0_ACCEL_DATA_X_POS 0x00 +#define ACCEL_DATA_X0_ACCEL_DATA_X_MASK 0xff + + + +/* + * ACCEL_DATA_Y1 + * Register Name : ACCEL_DATA_Y1 + */ + +/* + * accel_data_y + * Accel Y axis data + */ +#define ACCEL_DATA_Y1_ACCEL_DATA_Y_POS 0x00 +#define ACCEL_DATA_Y1_ACCEL_DATA_Y_MASK 0xff + + + +/* + * ACCEL_DATA_Y0 + * Register Name : ACCEL_DATA_Y0 + */ + +/* + * accel_data_y + * Accel Y axis data + */ +#define ACCEL_DATA_Y0_ACCEL_DATA_Y_POS 0x00 +#define ACCEL_DATA_Y0_ACCEL_DATA_Y_MASK 0xff + + + +/* + * ACCEL_DATA_Z1 + * Register Name : ACCEL_DATA_Z1 + */ + +/* + * accel_data_z + * Accel Z axis data + */ +#define ACCEL_DATA_Z1_ACCEL_DATA_Z_POS 0x00 +#define ACCEL_DATA_Z1_ACCEL_DATA_Z_MASK 0xff + + + +/* + * ACCEL_DATA_Z0 + * Register Name : ACCEL_DATA_Z0 + */ + +/* + * accel_data_z + * Accel Z axis data + */ +#define ACCEL_DATA_Z0_ACCEL_DATA_Z_POS 0x00 +#define ACCEL_DATA_Z0_ACCEL_DATA_Z_MASK 0xff + + + +/* + * GYRO_DATA_X1 + * Register Name : GYRO_DATA_X1 + */ + +/* + * gyro_data_x + * Gyro X axis data + */ +#define GYRO_DATA_X1_GYRO_DATA_X_POS 0x00 +#define GYRO_DATA_X1_GYRO_DATA_X_MASK 0xff + + + +/* + * GYRO_DATA_X0 + * Register Name : GYRO_DATA_X0 + */ + +/* + * gyro_data_x + * Gyro X axis data + */ +#define GYRO_DATA_X0_GYRO_DATA_X_POS 0x00 +#define GYRO_DATA_X0_GYRO_DATA_X_MASK 0xff + + + +/* + * GYRO_DATA_Y1 + * Register Name : GYRO_DATA_Y1 + */ + +/* + * gyro_data_y + * Gyro Y axis data + */ +#define GYRO_DATA_Y1_GYRO_DATA_Y_POS 0x00 +#define GYRO_DATA_Y1_GYRO_DATA_Y_MASK 0xff + + + +/* + * GYRO_DATA_Y0 + * Register Name : GYRO_DATA_Y0 + */ + +/* + * gyro_data_y + * Gyro Y axis data + */ +#define GYRO_DATA_Y0_GYRO_DATA_Y_POS 0x00 +#define GYRO_DATA_Y0_GYRO_DATA_Y_MASK 0xff + + + +/* + * GYRO_DATA_Z1 + * Register Name : GYRO_DATA_Z1 + */ + +/* + * gyro_data_z + * Gyro Z axis data + */ +#define GYRO_DATA_Z1_GYRO_DATA_Z_POS 0x00 +#define GYRO_DATA_Z1_GYRO_DATA_Z_MASK 0xff + + + +/* + * GYRO_DATA_Z0 + * Register Name : GYRO_DATA_Z0 + */ + +/* + * gyro_data_z + * Gyro Z axis data + */ +#define GYRO_DATA_Z0_GYRO_DATA_Z_POS 0x00 +#define GYRO_DATA_Z0_GYRO_DATA_Z_MASK 0xff + + + +/* + * TMST_FSYNCH + * Register Name : TMST_FSYNCH + */ + +/* + * tmst_fsync_data + * Stores the time delta from the rising edge of FSYNC to the latest ODR until the UI Interface reads the FSYNC tag in the status register + */ +#define TMST_FSYNCH_TMST_FSYNC_DATA_POS 0x00 +#define TMST_FSYNCH_TMST_FSYNC_DATA_MASK 0xff + + + +/* + * TMST_FSYNCL + * Register Name : TMST_FSYNCL + */ + +/* + * tmst_fsync_data + * Stores the time delta from the rising edge of FSYNC to the latest ODR until the UI Interface reads the FSYNC tag in the status register + */ +#define TMST_FSYNCL_TMST_FSYNC_DATA_POS 0x00 +#define TMST_FSYNCL_TMST_FSYNC_DATA_MASK 0xff + + + +/* + * APEX_DATA4 + * Register Name : APEX_DATA4 + */ + +/* + * ff_dur + * Free Fall duration. The duration is given in number of samples and it can be converted to freefall distance by applying the following formula: + * ff_distance = 0.5*9.81*(ff_duration*dmp_odr_s)^2) + * Note: dmp_odr_s in the duration of DMP_ODR expressed in seconds. + */ +#define APEX_DATA4_FF_DUR_POS 0x00 +#define APEX_DATA4_FF_DUR_MASK 0xff + + + +/* + * APEX_DATA5 + * Register Name : APEX_DATA5 + */ + +/* + * ff_dur + * Free Fall duration. The duration is given in number of samples and it can be converted to freefall distance by applying the following formula: + * ff_distance = 0.5*9.81*(ff_duration*dmp_odr_s)^2) + * Note: dmp_odr_s in the duration of DMP_ODR expressed in seconds. + */ +#define APEX_DATA5_FF_DUR_POS 0x00 +#define APEX_DATA5_FF_DUR_MASK 0xff + + + +/* + * PWR_MGMT0 + * Register Name : PWR_MGMT0 + */ + +/* + * accel_lp_clk_sel + * 0: Accelerometer LP mode uses Wake Up oscillator clock. This is the lowest power consumption mode and it is the recommended setting. + * 1: Accelerometer LP mode uses RC oscillator clock. + * + * This field can be changed on-the-fly even if accel sensor is on. + */ +#define PWR_MGMT0_ACCEL_LP_CLK_SEL_POS 0x07 +#define PWR_MGMT0_ACCEL_LP_CLK_SEL_MASK (0x01 << PWR_MGMT0_ACCEL_LP_CLK_SEL_POS) + +/* + * idle + * If this bit is set to 1, the RC oscillator is powered on even if Accel and Gyro are powered off. + * Nominally this bit is set to 0, so when Accel and Gyro are powered off, + * the chip will go to OFF state , since the RC oscillator will also be powered off. + * + * This field can be changed on-the-fly even if a sensor is already on + */ +#define PWR_MGMT0_IDLE_POS 0x04 +#define PWR_MGMT0_IDLE_MASK (0x01 << PWR_MGMT0_IDLE_POS) + +/* + * gyro_mode + * 00: Turns gyroscope off + * 01: Places gyroscope in Standby Mode + * 10: Reserved + * 11: Places gyroscope in Low Noise (LN) Mode + * + * Gyroscope needs to be kept ON for a minimum of 45ms. When transitioning from OFF to any of the other modes, do not issue any register writes for 200 µs. + * + * This field can be changed on-the-fly even if gyro sensor is on + */ +#define PWR_MGMT0_GYRO_MODE_POS 0x02 +#define PWR_MGMT0_GYRO_MODE_MASK (0x03 << PWR_MGMT0_GYRO_MODE_POS) + +/* + * accel_mode + * 00: Turns accelerometer off + * 01: Turns accelerometer off + * 10: Places accelerometer in Low Power (LP) Mode + * 11: Places accelerometer in Low Noise (LN) Mode + * + * When selecting LP Mode please refer to ACCEL_LP_CLK_SEL setting, bit[7] of this register. + * + * Before entering LP mode and during LP Mode the following combinations of ODR and averaging are not permitted: + * 1) ODR=1600 Hz or ODR=800 Hz: any averaging. + * 2) ODR=400 Hz: averaging=16x, 32x or 64x. + * 3) ODR=200 Hz: averaging=64x. + * + * When transitioning from OFF to any of the other modes, do not issue any register writes for 200 µs. + * + * This field can be changed on-the-fly even if accel sensor is on + */ +#define PWR_MGMT0_ACCEL_MODE_POS 0x00 +#define PWR_MGMT0_ACCEL_MODE_MASK 0x03 + + + +/* + * GYRO_CONFIG0 + * Register Name : GYRO_CONFIG0 + */ + +/* + * gyro_ui_fs_sel + * Full scale select for gyroscope UI interface output + * + * 00: ±2000 dps + * 01: ±1000 dps + * 10: ±500 dps + * 11: ±250 dps + * + * This field can be changed on-the-fly even if gyro sensor is on + */ +#define GYRO_CONFIG0_GYRO_UI_FS_SEL_POS 0x05 +#define GYRO_CONFIG0_GYRO_UI_FS_SEL_MASK (0x03 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS) + +/* + * gyro_odr + * Gyroscope ODR selection for UI interface output + * + * 0000: Reserved + * 0001: Reserved + * 0010: Reserved + * 0011: Reserved + * 0100: Reserved + * 0101: 1.6k Hz + * 0110: 800 Hz + * 0111: 400 Hz + * 1000: 200 Hz + * 1001: 100 Hz + * 1010: 50 Hz + * 1011: 25 Hz + * 1100: 12.5 Hz + * 1101: Reserved + * 1110: Reserved + * 1111: Reserved + * + * This field can be changed on-the-fly even if gyro sensor is on + */ +#define GYRO_CONFIG0_GYRO_ODR_POS 0x00 +#define GYRO_CONFIG0_GYRO_ODR_MASK 0x0f + + + +/* + * ACCEL_CONFIG0 + * Register Name : ACCEL_CONFIG0 + */ + +/* + * accel_ui_fs_sel + * Full scale select for accelerometer UI interface output + * + * 00: ±16g + * 01: ±8g + * 10: ±4g + * 11: ±2g + * + * This field can be changed on-the-fly even if accel sensor is on + */ +#define ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS 0x05 +#define ACCEL_CONFIG0_ACCEL_UI_FS_SEL_MASK (0x03 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS) + +/* + * accel_odr + * Accelerometer ODR selection for UI interface output + * + * 0000: Reserved + * 0001: Reserved + * 0010: Reserved + * 0011: Reserved + * 0100: Reserved + * 0101: 1.6 kHz (LN mode) + * 0110: 800 Hz (LN mode) + * 0111: 400 Hz (LP or LN mode) + * 1000: 200 Hz (LP or LN mode) + * 1001: 100 Hz (LP or LN mode) + * 1010: 50 Hz (LP or LN mode) + * 1011: 25 Hz (LP or LN mode) + * 1100: 12.5 Hz (LP or LN mode) + * 1101: 6.25 Hz (LP mode) + * 1110: 3.125 Hz (LP mode) + * 1111: 1.5625 Hz (LP mode) + * + * This field can be changed on-the-fly when accel sensor is on + */ +#define ACCEL_CONFIG0_ACCEL_ODR_POS 0x00 +#define ACCEL_CONFIG0_ACCEL_ODR_MASK 0x0f + + + +/* + * TEMP_CONFIG0 + * Register Name : TEMP_CONFIG0 + */ + +/* + * temp_filt_bw + * Sets the bandwidth of the temperature signal DLPF + * + * 000: DLPF bypassed + * 001: DLPF BW = 180 Hz + * 010: DLPF BW = 72 Hz + * 011: DLPF BW = 34 Hz + * 100: DLPF BW = 16 Hz + * 101: DLPF BW = 8 Hz + * 110: DLPF BW = 4 Hz + * 111: DLPF BW = 4 Hz + * + * This field can be changed on-the-fly even if sensor is on + */ +#define TEMP_CONFIG0_TEMP_FILT_BW_POS 0x04 +#define TEMP_CONFIG0_TEMP_FILT_BW_MASK (0x07 << TEMP_CONFIG0_TEMP_FILT_BW_POS) + + + +/* + * GYRO_CONFIG1 + * Register Name : GYRO_CONFIG1 + */ + +/* + * gyro_ui_filt_bw + * Selects GYRO UI low pass filter bandwidth + * + * 000: Low pass filter bypassed + * 001: 180 Hz + * 010: 121 Hz + * 011: 73 Hz + * 100: 53 Hz + * 101: 34 Hz + * 110: 25 Hz + * 111: 16 Hz + * + * This field can be changed on-the-fly even if gyro sensor is on + */ +#define GYRO_CONFIG1_GYRO_UI_FILT_BW_POS 0x00 +#define GYRO_CONFIG1_GYRO_UI_FILT_BW_MASK 0x07 + + + +/* + * ACCEL_CONFIG1 + * Register Name : ACCEL_CONFIG1 + */ + +/* + * accel_ui_avg + * Selects averaging filter setting to create accelerometer output in accelerometer low power mode (LPM) + * + * 000: 2x average + * 001: 4x average + * 010: 8x average + * 011: 16x average + * 100: 32x average + * 101: 64x average + * 110: 64x average + * 111: 64x average + * + * This field cannot be changed when the accel sensor is in LPM + */ +#define ACCEL_CONFIG1_ACCEL_UI_AVG_POS 0x04 +#define ACCEL_CONFIG1_ACCEL_UI_AVG_MASK (0x07 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS) + +/* + * accel_ui_filt_bw + * Selects ACCEL UI low pass filter bandwidth + * + * 000: Low pass filter bypassed + * 001: 180 Hz + * 010: 121 Hz + * 011: 73 Hz + * 100: 53 Hz + * 101: 34 Hz + * 110: 25 Hz + * 111: 16 Hz + * + * This field can be changed on-the-fly even if accel sensor is on + */ +#define ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS 0x00 +#define ACCEL_CONFIG1_ACCEL_UI_FILT_BW_MASK 0x07 + + + +/* + * APEX_CONFIG0 + * Register Name : APEX_CONFIG0 + */ + +/* + * dmp_power_save_en + * When this bit is set to 1, power saving is enabled for DMP algorithms + */ +#define APEX_CONFIG0_DMP_POWER_SAVE_EN_POS 0x03 +#define APEX_CONFIG0_DMP_POWER_SAVE_EN_MASK (0x01 << APEX_CONFIG0_DMP_POWER_SAVE_EN_POS) + +/* + * dmp_init_en + * When this bit is set to 1, DMP runs DMP SW initialization procedure. Bit is reset by hardware when the procedure is finished. All other APEX features are ignored as long as DMP_INIT_EN is set. + * + * This field can be changed on-the-fly even if accel sensor is on. + */ +#define APEX_CONFIG0_DMP_INIT_EN_POS 0x02 +#define APEX_CONFIG0_DMP_INIT_EN_MASK (0x01 << APEX_CONFIG0_DMP_INIT_EN_POS) + +/* + * dmp_mem_reset_en + * When this bit is set to 1, it clears DMP SRAM for APEX operation or Self-test operation. + */ +#define APEX_CONFIG0_DMP_MEM_RESET_EN_POS 0x00 +#define APEX_CONFIG0_DMP_MEM_RESET_EN_MASK 0x03 + + + +/* + * APEX_CONFIG1 + * Register Name : APEX_CONFIG1 + */ + +/* + * smd_enable + * 0: Significant Motion Detection not enabled + * 1: Significant Motion Detection enabled + * + * This field can be changed on-the-fly even if accel sensor is on + */ +#define APEX_CONFIG1_SMD_ENABLE_POS 0x06 +#define APEX_CONFIG1_SMD_ENABLE_MASK (0x01 << APEX_CONFIG1_SMD_ENABLE_POS) + +/* + * ff_enable + * 0: Freefall Detection not enabled + * 1: Freefall Detection enabled + * + * This field can be changed on-the-fly even if accel sensor is on + */ +#define APEX_CONFIG1_FF_ENABLE_POS 0x05 +#define APEX_CONFIG1_FF_ENABLE_MASK (0x01 << APEX_CONFIG1_FF_ENABLE_POS) + +/* + * tilt_enable + * 0: Tilt Detection not enabled + * 1: Tilt Detection enabled + * + * This field can be changed on-the-fly even if accel sensor is on + */ +#define APEX_CONFIG1_TILT_ENABLE_POS 0x04 +#define APEX_CONFIG1_TILT_ENABLE_MASK (0x01 << APEX_CONFIG1_TILT_ENABLE_POS) + +/* + * ped_enable + * 0: Pedometer not enabled + * 1: Pedometer enabled + * + * This field can be changed on-the-fly even if accel sensor is on + */ +#define APEX_CONFIG1_PED_ENABLE_POS 0x03 +#define APEX_CONFIG1_PED_ENABLE_MASK (0x01 << APEX_CONFIG1_PED_ENABLE_POS) + +/* + * dmp_odr + * 00: 25 Hz + * 01: 400 Hz + * 10: 50 Hz + * 11: 100 Hz + * + * The ACCEL_ODR field must be configured to an ODR equal or greater to the DMP_ODR field, for correct device operation. + * + * This field can be changed on-the-fly even if accel sensor is on + */ +#define APEX_CONFIG1_DMP_ODR_POS 0x00 +#define APEX_CONFIG1_DMP_ODR_MASK 0x03 + + + +/* + * WOM_CONFIG + * Register Name : WOM_CONFIG + */ + +/* + * wom_int_dur + * Selects Wake on Motion interrupt assertion from among the following options + * + * 00: WoM interrupt asserted at first overthreshold event + * 01: WoM interrupt asserted at second overthreshold event + * 10: WoM interrupt asserted at third overthreshold event + * 11: WoM interrupt asserted at fourth overthreshold event + * + * This field can be changed on-the-fly even if accel sensor is on, but it cannot be changed if WOM_EN is already enabled + */ +#define WOM_CONFIG_WOM_INT_DUR_POS 0x03 +#define WOM_CONFIG_WOM_INT_DUR_MASK (0x03 << WOM_CONFIG_WOM_INT_DUR_POS) + +/* + * wom_int_mode + * 0: Set WoM interrupt on the OR of all enabled accelerometer thresholds + * 1: Set WoM interrupt on the AND of all enabled accelerometer thresholds + * + * This field can be changed on-the-fly even if accel sensor is on, but it cannot be changed if WOM_EN is already enabled + */ +#define WOM_CONFIG_WOM_INT_MODE_POS 0x02 +#define WOM_CONFIG_WOM_INT_MODE_MASK (0x01 << WOM_CONFIG_WOM_INT_MODE_POS) + +/* + * wom_mode + * 0 - Initial sample is stored. Future samples are compared to initial sample + * 1 - Compare current sample to previous sample + * + * This field can be changed on-the-fly even if accel sensor is already on, but it cannot be changed if wom_en is already enabled. + */ +#define WOM_CONFIG_WOM_MODE_POS 0x01 +#define WOM_CONFIG_WOM_MODE_MASK (0x01 << WOM_CONFIG_WOM_MODE_POS) + +/* + * wom_en + * 1: enable wake-on-motion detection. + * 0: disable wake-on-motion detection. + * + * This field can be changed on-the-fly even if accel sensor is already on. + */ +#define WOM_CONFIG_WOM_EN_POS 0x00 +#define WOM_CONFIG_WOM_EN_MASK 0x01 + + + +/* + * FIFO_CONFIG1 + * Register Name : FIFO_CONFIG1 + */ + +/* + * fifo_mode + * FIFO mode control + * + * 0: Stream-to-FIFO Mode + * 1: STOP-on-FULL Mode + */ +#define FIFO_CONFIG1_FIFO_MODE_POS 0x01 +#define FIFO_CONFIG1_FIFO_MODE_MASK (0x01 << FIFO_CONFIG1_FIFO_MODE_POS) + +/* + * fifo_bypass + * FIFO bypass control + * 0: FIFO is not bypassed + * 1: FIFO is bypassed + */ +#define FIFO_CONFIG1_FIFO_BYPASS_POS 0x00 +#define FIFO_CONFIG1_FIFO_BYPASS_MASK 0x01 + + + +/* + * FIFO_CONFIG2 + * Register Name : FIFO_CONFIG2 + */ + +/* + * fifo_wm + * FIFO watermark. Generate interrupt when the FIFO reaches or exceeds FIFO_WM size in bytes or records according to FIFO_COUNT_FORMAT setting. FIFO_WM_EN must be zero before writing this register. Interrupt only fires once. This register should be set to non-zero value, before choosing this interrupt source. + * + * This field should be changed when FIFO is empty to avoid spurious interrupts. + */ +#define FIFO_CONFIG2_FIFO_WM_POS 0x00 +#define FIFO_CONFIG2_FIFO_WM_MASK 0xff + + + +/* + * FIFO_CONFIG3 + * Register Name : FIFO_CONFIG3 + */ + +/* + * fifo_wm + * FIFO watermark. Generate interrupt when the FIFO reaches or exceeds FIFO_WM size in bytes or records according to FIFO_COUNT_FORMAT setting. FIFO_WM_EN must be zero before writing this register. Interrupt only fires once. This register should be set to non-zero value, before choosing this interrupt source. + * + * This field should be changed when FIFO is empty to avoid spurious interrupts. + */ +#define FIFO_CONFIG3_FIFO_WM_POS 0x00 +#define FIFO_CONFIG3_FIFO_WM_MASK 0x0f + + + +/* + * INT_SOURCE0 + * Register Name : INT_SOURCE0 + */ + +/* + * st_int1_en + * 0: Self-Test Done interrupt not routed to INT1 + * 1: Self-Test Done interrupt routed to INT1 + */ +#define INT_SOURCE0_ST_INT1_EN_POS 0x07 +#define INT_SOURCE0_ST_INT1_EN_MASK (0x01 << INT_SOURCE0_ST_INT1_EN_POS) + +/* + * fsync_int1_en + * 0: FSYNC interrupt not routed to INT1 + * 1: FSYNC interrupt routed to INT1 + */ +#define INT_SOURCE0_FSYNC_INT1_EN_POS 0x06 +#define INT_SOURCE0_FSYNC_INT1_EN_MASK (0x01 << INT_SOURCE0_FSYNC_INT1_EN_POS) + +/* + * pll_rdy_int1_en + * 0: PLL ready interrupt not routed to INT1 + * 1: PLL ready interrupt routed to INT1 + */ +#define INT_SOURCE0_PLL_RDY_INT1_EN_POS 0x05 +#define INT_SOURCE0_PLL_RDY_INT1_EN_MASK (0x01 << INT_SOURCE0_PLL_RDY_INT1_EN_POS) + +/* + * reset_done_int1_en + * 0: Reset done interrupt not routed to INT1 + * 1: Reset done interrupt routed to INT1 + */ +#define INT_SOURCE0_RESET_DONE_INT1_EN_POS 0x04 +#define INT_SOURCE0_RESET_DONE_INT1_EN_MASK (0x01 << INT_SOURCE0_RESET_DONE_INT1_EN_POS) + +/* + * drdy_int1_en + * 0: Data Ready interrupt not routed to INT1 + * 1: Data Ready interrupt routed to INT1 + */ +#define INT_SOURCE0_DRDY_INT1_EN_POS 0x03 +#define INT_SOURCE0_DRDY_INT1_EN_MASK (0x01 << INT_SOURCE0_DRDY_INT1_EN_POS) + +/* + * fifo_ths_int1_en + * 0: FIFO threshold interrupt not routed to INT1 + * 1: FIFO threshold interrupt routed to INT1 + */ +#define INT_SOURCE0_FIFO_THS_INT1_EN_POS 0x02 +#define INT_SOURCE0_FIFO_THS_INT1_EN_MASK (0x01 << INT_SOURCE0_FIFO_THS_INT1_EN_POS) + +/* + * fifo_full_int1_en + * 0: FIFO full interrupt not routed to INT1 + * 1: FIFO full interrupt routed to INT1 + * To avoid FIFO FULL interrupts while reading FIFO, this bit should be disabled while reading FIFO + */ +#define INT_SOURCE0_FIFO_FULL_INT1_EN_POS 0x01 +#define INT_SOURCE0_FIFO_FULL_INT1_EN_MASK (0x01 << INT_SOURCE0_FIFO_FULL_INT1_EN_POS) + +/* + * agc_rdy_int1_en + * 0: UI AGC ready interrupt not routed to INT1 + * 1: UI AGC ready interrupt routed to INT1 + */ +#define INT_SOURCE0_AGC_RDY_INT1_EN_POS 0x00 +#define INT_SOURCE0_AGC_RDY_INT1_EN_MASK 0x01 + + + +/* + * INT_SOURCE1 + * Register Name : INT_SOURCE1 + */ + +/* + * i3c_protocol_error_int1_en + * 0: I3CSM protocol error interrupt not routed to INT1 + * 1: I3CSM protocol error interrupt routed to INT1 + */ +#define INT_SOURCE1_I3C_PROTOCOL_ERROR_INT1_EN_POS 0x06 +#define INT_SOURCE1_I3C_PROTOCOL_ERROR_INT1_EN_MASK (0x01 << INT_SOURCE1_I3C_PROTOCOL_ERROR_INT1_EN_POS) + +/* + * smd_int1_en + * 0: SMD interrupt not routed to INT1 + * 1: SMD interrupt routed to INT1 + */ +#define INT_SOURCE1_SMD_INT1_EN_POS 0x03 +#define INT_SOURCE1_SMD_INT1_EN_MASK (0x01 << INT_SOURCE1_SMD_INT1_EN_POS) + +/* + * wom_z_int1_en + * 0: Z-axis WOM interrupt not routed to INT1 + * 1: Z-axis WOM interrupt routed to INT1 + */ +#define INT_SOURCE1_WOM_Z_INT1_EN_POS 0x02 +#define INT_SOURCE1_WOM_Z_INT1_EN_MASK (0x01 << INT_SOURCE1_WOM_Z_INT1_EN_POS) + +/* + * wom_y_int1_en + * 0: Y-axis WOM interrupt not routed to INT1 + * 1: Y-axis WOM interrupt routed to INT1 + */ +#define INT_SOURCE1_WOM_Y_INT1_EN_POS 0x01 +#define INT_SOURCE1_WOM_Y_INT1_EN_MASK (0x01 << INT_SOURCE1_WOM_Y_INT1_EN_POS) + +/* + * wom_x_int1_en + * 0: X-axis WOM interrupt not routed to INT1 + * 1: X-axis WOM interrupt routed to INT1 + */ +#define INT_SOURCE1_WOM_X_INT1_EN_POS 0x00 +#define INT_SOURCE1_WOM_X_INT1_EN_MASK 0x01 + + + +/* + * INT_SOURCE3 + * Register Name : INT_SOURCE3 + */ + +/* + * st_int2_en + * 0: Self-Test Done interrupt not routed to INT2 + * 1: Self-Test Done interrupt routed to INT2 + */ +#define INT_SOURCE3_ST_INT2_EN_POS 0x07 +#define INT_SOURCE3_ST_INT2_EN_MASK (0x01 << INT_SOURCE3_ST_INT2_EN_POS) + +/* + * fsync_int2_en + * 0: FSYNC interrupt not routed to INT2 + * 1: FSYNC interrupt routed to INT2 + */ +#define INT_SOURCE3_FSYNC_INT2_EN_POS 0x06 +#define INT_SOURCE3_FSYNC_INT2_EN_MASK (0x01 << INT_SOURCE3_FSYNC_INT2_EN_POS) + +/* + * pll_rdy_int2_en + * 0: PLL ready interrupt not routed to INT2 + * 1: PLL ready interrupt routed to INT2 + */ +#define INT_SOURCE3_PLL_RDY_INT2_EN_POS 0x05 +#define INT_SOURCE3_PLL_RDY_INT2_EN_MASK (0x01 << INT_SOURCE3_PLL_RDY_INT2_EN_POS) + +/* + * reset_done_int2_en + * 0: Reset done interrupt not routed to INT2 + * 1: Reset done interrupt routed to INT2 + */ +#define INT_SOURCE3_RESET_DONE_INT2_EN_POS 0x04 +#define INT_SOURCE3_RESET_DONE_INT2_EN_MASK (0x01 << INT_SOURCE3_RESET_DONE_INT2_EN_POS) + +/* + * drdy_int2_en + * 0: Data Ready interrupt not routed to INT2 + * 1: Data Ready interrupt routed to INT2 + */ +#define INT_SOURCE3_DRDY_INT2_EN_POS 0x03 +#define INT_SOURCE3_DRDY_INT2_EN_MASK (0x01 << INT_SOURCE3_DRDY_INT2_EN_POS) + +/* + * fifo_ths_int2_en + * 0: FIFO threshold interrupt not routed to INT2 + * 1: FIFO threshold interrupt routed to INT2 + */ +#define INT_SOURCE3_FIFO_THS_INT2_EN_POS 0x02 +#define INT_SOURCE3_FIFO_THS_INT2_EN_MASK (0x01 << INT_SOURCE3_FIFO_THS_INT2_EN_POS) + +/* + * fifo_full_int2_en + * 0: FIFO full interrupt not routed to INT2 + * 1: FIFO full interrupt routed to INT2 + */ +#define INT_SOURCE3_FIFO_FULL_INT2_EN_POS 0x01 +#define INT_SOURCE3_FIFO_FULL_INT2_EN_MASK (0x01 << INT_SOURCE3_FIFO_FULL_INT2_EN_POS) + +/* + * agc_rdy_int2_en + * 0: AGC ready interrupt not routed to INT2 + * 1: AGC ready interrupt routed to INT2 + */ +#define INT_SOURCE3_AGC_RDY_INT2_EN_POS 0x00 +#define INT_SOURCE3_AGC_RDY_INT2_EN_MASK 0x01 + + + +/* + * INT_SOURCE4 + * Register Name : INT_SOURCE4 + */ + +/* + * i3c_protocol_error_int2_en + * 0: I3CSM protocol error interrupt not routed to INT2 + * 1: I3CSM protocol error interrupt routed to INT2 + */ +#define INT_SOURCE4_I3C_PROTOCOL_ERROR_INT2_EN_POS 0x06 +#define INT_SOURCE4_I3C_PROTOCOL_ERROR_INT2_EN_MASK (0x01 << INT_SOURCE4_I3C_PROTOCOL_ERROR_INT2_EN_POS) + +/* + * smd_int2_en + * 0: SMD interrupt not routed to INT2 + * 1: SMD interrupt routed to INT2 + */ +#define INT_SOURCE4_SMD_INT2_EN_POS 0x03 +#define INT_SOURCE4_SMD_INT2_EN_MASK (0x01 << INT_SOURCE4_SMD_INT2_EN_POS) + +/* + * wom_z_int2_en + * 0: Z-axis WOM interrupt not routed to INT2 + * 1: Z-axis WOM interrupt routed to INT2 + */ +#define INT_SOURCE4_WOM_Z_INT2_EN_POS 0x02 +#define INT_SOURCE4_WOM_Z_INT2_EN_MASK (0x01 << INT_SOURCE4_WOM_Z_INT2_EN_POS) + +/* + * wom_y_int2_en + * 0: Y-axis WOM interrupt not routed to INT2 + * 1: Y-axis WOM interrupt routed to INT2 + */ +#define INT_SOURCE4_WOM_Y_INT2_EN_POS 0x01 +#define INT_SOURCE4_WOM_Y_INT2_EN_MASK (0x01 << INT_SOURCE4_WOM_Y_INT2_EN_POS) + +/* + * wom_x_int2_en + * 0: X-axis WOM interrupt not routed to INT2 + * 1: X-axis WOM interrupt routed to INT2 + */ +#define INT_SOURCE4_WOM_X_INT2_EN_POS 0x00 +#define INT_SOURCE4_WOM_X_INT2_EN_MASK 0x01 + + + +/* + * FIFO_LOST_PKT0 + * Register Name : FIFO_LOST_PKT0 + */ + +/* + * fifo_lost_pkt_cnt + * Stores the number of packets lost in the FIFO + */ +#define FIFO_LOST_PKT0_FIFO_LOST_PKT_CNT_POS 0x00 +#define FIFO_LOST_PKT0_FIFO_LOST_PKT_CNT_MASK 0xff + + + +/* + * FIFO_LOST_PKT1 + * Register Name : FIFO_LOST_PKT1 + */ + +/* + * fifo_lost_pkt_cnt + * Stores the number of packets lost in the FIFO + */ +#define FIFO_LOST_PKT1_FIFO_LOST_PKT_CNT_POS 0x00 +#define FIFO_LOST_PKT1_FIFO_LOST_PKT_CNT_MASK 0xff + + + +/* + * APEX_DATA0 + * Register Name : APEX_DATA0 + */ + +/* + * step_cnt + * This status register indicates number of step taken. + */ +#define APEX_DATA0_STEP_CNT_POS 0x00 +#define APEX_DATA0_STEP_CNT_MASK 0xff + + + +/* + * APEX_DATA1 + * Register Name : APEX_DATA1 + */ + +/* + * step_cnt + * This status register indicates number of step taken. + */ +#define APEX_DATA1_STEP_CNT_POS 0x00 +#define APEX_DATA1_STEP_CNT_MASK 0xff + + + +/* + * APEX_DATA2 + * Register Name : APEX_DATA2 + */ + +/* + * step_cadence + * Pedometer step cadence.Walk/run cadency in number of samples. Format is u6.2. + * E.g, At 50Hz and 2Hz walk frequency, the cadency is 25 samples. The register will output 100. + */ +#define APEX_DATA2_STEP_CADENCE_POS 0x00 +#define APEX_DATA2_STEP_CADENCE_MASK 0xff + + + +/* + * APEX_DATA3 + * Register Name : APEX_DATA3 + */ + +/* + * dmp_idle + * 0: Indicates DMP is running + * 1: Indicates DMP is idle + */ +#define APEX_DATA3_DMP_IDLE_POS 0x02 +#define APEX_DATA3_DMP_IDLE_MASK (0x01 << APEX_DATA3_DMP_IDLE_POS) + +/* + * activity_class + * Pedometer Output: Detected activity + * + * 00: Unknown + * 01: Walk + * 10: Run + * 11: Reserved + */ +#define APEX_DATA3_ACTIVITY_CLASS_POS 0x00 +#define APEX_DATA3_ACTIVITY_CLASS_MASK 0x03 + + + +/* + * INTF_CONFIG0 + * Register Name : INTF_CONFIG0 + */ + +/* + * fifo_count_format + * 0: FIFO count is reported in bytes + * 1: FIFO count is reported in records (1 record = 16 bytes for header + gyro + accel + temp sensor data + time stamp, or 8 bytes for header + gyro/accel + temp sensor data) + */ +#define INTF_CONFIG0_FIFO_COUNT_FORMAT_POS 0x06 +#define INTF_CONFIG0_FIFO_COUNT_FORMAT_MASK (0x01 << INTF_CONFIG0_FIFO_COUNT_FORMAT_POS) + +/* + * fifo_count_endian + * This bit applies to both fifo_count and lost_pkt_count + * 0 : Little Endian (The LSByte data is read first, followed by MSByte data). + * 1 : Big Endian (The MSByte data is read first, followed by LSByte data). + */ +#define INTF_CONFIG0_FIFO_COUNT_ENDIAN_POS 0x05 +#define INTF_CONFIG0_FIFO_COUNT_ENDIAN_MASK (0x01 << INTF_CONFIG0_FIFO_COUNT_ENDIAN_POS) + +/* + * sensor_data_endian + * This bit applies to sensor data to AP, and fifo data. + * 0 : Little Endian (The LSByte data is read first, followed by MSByte data). + * 1 : Big Endian (The MSByte data is read first, followed by LSByte data). + */ +#define INTF_CONFIG0_SENSOR_DATA_ENDIAN_POS 0x04 +#define INTF_CONFIG0_SENSOR_DATA_ENDIAN_MASK (0x01 << INTF_CONFIG0_SENSOR_DATA_ENDIAN_POS) + +/* + * INTF_CONFIG1 + * Register Name : INTF_CONFIG1 + */ + +/* + * i3c_sdr_en + * 0: I3CSM SDR mode not enabled + * 1: I3CSM SDR mode enabled + * + * Device will be in pure I2C mode if {I3C_SDR_EN, I3C_DDR_EN} = 00 + */ +#define INTF_CONFIG1_I3C_SDR_EN_POS 0x03 +#define INTF_CONFIG1_I3C_SDR_EN_MASK (0x01 << INTF_CONFIG1_I3C_SDR_EN_POS) + +/* + * i3c_ddr_en + * 0: I3CSM DDR mode not enabled + * 1: I3CSM DDR mode enabled + * + * This bit will not take effect unless I3C_SDR_EN = 1. + */ +#define INTF_CONFIG1_I3C_DDR_EN_POS 0x02 +#define INTF_CONFIG1_I3C_DDR_EN_MASK (0x01 << INTF_CONFIG1_I3C_DDR_EN_POS) + +/* + * clksel + * 00 Alway select internal RC oscillator + * 01 Select PLL when available, else select RC oscillator (default) + * 10 (Reserved) + * 11 Disable all clocks + */ +#define INTF_CONFIG1_CLKSEL_POS 0x00 +#define INTF_CONFIG1_CLKSEL_MASK 0x03 + + + +/* + * INT_STATUS_DRDY + * Register Name : INT_STATUS_DRDY + */ + +/* + * data_rdy_int + * This bit automatically sets to 1 when a Data Ready interrupt is generated. The bit clears to 0 after the register has been read. + */ +#define INT_STATUS_DRDY_DATA_RDY_INT_POS 0x00 +#define INT_STATUS_DRDY_DATA_RDY_INT_MASK 0x01 + + + +/* + * INT_STATUS + * Register Name : INT_STATUS + */ + +/* + * st_int + * This bit automatically sets to 1 when a Self Test done interrupt is generated. The bit clears to 0 after the register has been read. + */ +#define INT_STATUS_ST_INT_POS 0x07 +#define INT_STATUS_ST_INT_MASK (0x01 << INT_STATUS_ST_INT_POS) + +/* + * fsync_int + * This bit automatically sets to 1 when an FSYNC interrupt is generated. The bit clears to 0 after the register has been read. + */ +#define INT_STATUS_FSYNC_INT_POS 0x06 +#define INT_STATUS_FSYNC_INT_MASK (0x01 << INT_STATUS_FSYNC_INT_POS) + +/* + * pll_rdy_int + * This bit automatically sets to 1 when a PLL Ready interrupt is generated. The bit clears to 0 after the register has been read. + */ +#define INT_STATUS_PLL_RDY_INT_POS 0x05 +#define INT_STATUS_PLL_RDY_INT_MASK (0x01 << INT_STATUS_PLL_RDY_INT_POS) + +/* + * reset_done_int + * This bit automatically sets to 1 when software reset is complete. The bit clears to 0 after the register has been read. + */ +#define INT_STATUS_RESET_DONE_INT_POS 0x04 +#define INT_STATUS_RESET_DONE_INT_MASK (0x01 << INT_STATUS_RESET_DONE_INT_POS) + +/* + * fifo_ths_int + * This bit automatically sets to 1 when the FIFO buffer reaches the threshold value. The bit clears to 0 after the register has been read. + */ +#define INT_STATUS_FIFO_THS_INT_POS 0x02 +#define INT_STATUS_FIFO_THS_INT_MASK (0x01 << INT_STATUS_FIFO_THS_INT_POS) + +/* + * fifo_full_int + * This bit automatically sets to 1 when the FIFO buffer is full. The bit clears to 0 after the register has been read. + */ +#define INT_STATUS_FIFO_FULL_INT_POS 0x01 +#define INT_STATUS_FIFO_FULL_INT_MASK (0x01 << INT_STATUS_FIFO_FULL_INT_POS) + +/* + * agc_rdy_int + * This bit automatically sets to 1 when an AGC Ready interrupt is generated. The bit clears to 0 after the register has been read. + */ +#define INT_STATUS_AGC_RDY_INT_POS 0x00 +#define INT_STATUS_AGC_RDY_INT_MASK 0x01 + + + +/* + * INT_STATUS2 + * Register Name : INT_STATUS2 + */ + +/* + * smd_int + * Significant Motion Detection Interrupt, clears on read + */ +#define INT_STATUS2_SMD_INT_POS 0x03 +#define INT_STATUS2_SMD_INT_MASK (0x01 << INT_STATUS2_SMD_INT_POS) + +/* + * wom_x_int + * Wake on Motion Interrupt on X-axis, clears on read + */ +#define INT_STATUS2_WOM_X_INT_POS 0x02 +#define INT_STATUS2_WOM_X_INT_MASK (0x01 << INT_STATUS2_WOM_X_INT_POS) + +/* + * wom_y_int + * Wake on Motion Interrupt on Y-axis, clears on read + */ +#define INT_STATUS2_WOM_Y_INT_POS 0x01 +#define INT_STATUS2_WOM_Y_INT_MASK (0x01 << INT_STATUS2_WOM_Y_INT_POS) + +/* + * wom_z_int + * Wake on Motion Interrupt on Z-axis, clears on read + */ +#define INT_STATUS2_WOM_Z_INT_POS 0x00 +#define INT_STATUS2_WOM_Z_INT_MASK 0x01 + + + +/* + * INT_STATUS3 + * Register Name : INT_STATUS3 + */ + +/* + * step_det_int + * Step Detection Interrupt, clears on read + */ +#define INT_STATUS3_STEP_DET_INT_POS 0x05 +#define INT_STATUS3_STEP_DET_INT_MASK (0x01 << INT_STATUS3_STEP_DET_INT_POS) + +/* + * step_cnt_ovf_int + * Step Count Overflow Interrupt, clears on read + */ +#define INT_STATUS3_STEP_CNT_OVF_INT_POS 0x04 +#define INT_STATUS3_STEP_CNT_OVF_INT_MASK (0x01 << INT_STATUS3_STEP_CNT_OVF_INT_POS) + +/* + * tilt_det_int + * Tilt Detection Interrupt, clears on read + */ +#define INT_STATUS3_TILT_DET_INT_POS 0x03 +#define INT_STATUS3_TILT_DET_INT_MASK (0x01 << INT_STATUS3_TILT_DET_INT_POS) + +/* + * ff_det_int + * Freefall Interrupt, clears on read + */ +#define INT_STATUS3_FF_DET_INT_POS 0x02 +#define INT_STATUS3_FF_DET_INT_MASK (0x01 << INT_STATUS3_FF_DET_INT_POS) + +/* + * lowg_det_int + * LowG Interrupt, clears on read + */ +#define INT_STATUS3_LOWG_DET_INT_POS 0x01 +#define INT_STATUS3_LOWG_DET_INT_MASK (0x01 << INT_STATUS3_LOWG_DET_INT_POS) + + + +/* + * FIFO_COUNTH + * Register Name : FIFO_COUNTH + */ + +/* + * fifo_count + * Number of bytes in FIFO when fifo_count_format=0. + * Number of records in FIFO when fifo_count_format=1. + */ +#define FIFO_COUNTH_FIFO_COUNT_POS 0x00 +#define FIFO_COUNTH_FIFO_COUNT_MASK 0xff + + + +/* + * FIFO_COUNTL + * Register Name : FIFO_COUNTL + */ + +/* + * fifo_count + * Number of bytes in FIFO when fifo_count_format=0. + * Number of records in FIFO when fifo_count_format=1. + */ +#define FIFO_COUNTL_FIFO_COUNT_POS 0x00 +#define FIFO_COUNTL_FIFO_COUNT_MASK 0xff + + + +/* + * FIFO_DATA + * Register Name : FIFO_DATA + */ + +/* + * fifo_data + * FIFO data port + */ +#define FIFO_DATA_FIFO_DATA_POS 0x00 +#define FIFO_DATA_FIFO_DATA_MASK 0xff + + + +/* + * WHO_AM_I + * Register Name : WHO_AM_I + */ + +/* + * whoami + * Register to indicate to user which device is being accessed + */ +#define WHO_AM_I_WHOAMI_POS 0x00 +#define WHO_AM_I_WHOAMI_MASK 0xff + + + +/* + * BLK_SEL_W + * Register Name : BLK_SEL_W + */ + +/* + * blk_sel_w + * For write operation, select a 256-byte MCLK space, or 128-byte SCLK space. + * Automatically reset when OTP copy operation is triggered. + */ +#define BLK_SEL_W_BLK_SEL_W_POS 0x00 +#define BLK_SEL_W_BLK_SEL_W_MASK 0xff + + + +/* + * MADDR_W + * Register Name : MADDR_W + */ + +/* + * maddr_w + * For MREG write operation, the lower 8-bit address for accessing MCLK domain registers. + */ +#define MADDR_W_MADDR_W_POS 0x00 +#define MADDR_W_MADDR_W_MASK 0xff + + + +/* + * M_W + * Register Name : M_W + */ + +/* + * m_w + * For MREG write operation, the write port for accessing MCLK domain registers. + */ +#define M_W_M_W_POS 0x00 +#define M_W_M_W_MASK 0xff + + + +/* + * BLK_SEL_R + * Register Name : BLK_SEL_R + */ + +/* + * blk_sel_r + * For read operation, select a 256-byte MCLK space, or 128-byte SCLK space. + * Automatically reset when OTP copy operation is triggered. + */ +#define BLK_SEL_R_BLK_SEL_R_POS 0x00 +#define BLK_SEL_R_BLK_SEL_R_MASK 0xff + + + +/* + * MADDR_R + * Register Name : MADDR_R + */ + +/* + * maddr_r + * For MREG read operation, the lower 8-bit address for accessing MCLK domain registers. + */ +#define MADDR_R_MADDR_R_POS 0x00 +#define MADDR_R_MADDR_R_MASK 0xff + + + +/* + * M_R + * Register Name : M_R + */ + +/* + * m_r + * For MREG read operation, the read port for accessing MCLK domain registers. + */ +#define M_R_M_R_POS 0x00 +#define M_R_M_R_MASK 0xff + + +/* --------------------------------------------------------------------------- + * register MREG1 + * ---------------------------------------------------------------------------*/ + +/* + * TMST_CONFIG1 + * Register Name : TMST_CONFIG1 + */ + +/* + * tmst_res + * Time Stamp resolution; When set to 0 (default), time stamp resolution is 1 us. When set to 1, resolution is 16us + */ +#define TMST_CONFIG1_TMST_RES_POS 0x03 +#define TMST_CONFIG1_TMST_RES_MASK (0x01 << TMST_CONFIG1_TMST_RES_POS) + +/* + * tmst_delta_en + * Time Stamp delta Enable : When set to 1, the Time stamp field contains the measurement of time since the last occurrence of ODR. + */ +#define TMST_CONFIG1_TMST_DELTA_EN_POS 0x02 +#define TMST_CONFIG1_TMST_DELTA_EN_MASK (0x01 << TMST_CONFIG1_TMST_DELTA_EN_POS) + +/* + * tmst_fsync_en + * Time Stamp register Fsync Enable . When set to 1, the contents of the Timestamp feature of FSYNC is enabled. The user also needs to select fifo_tmst_fsync_en in order to propagate the timestamp value to the FIFO + */ +#define TMST_CONFIG1_TMST_FSYNC_EN_POS 0x01 +#define TMST_CONFIG1_TMST_FSYNC_EN_MASK (0x01 << TMST_CONFIG1_TMST_FSYNC_EN_POS) + +/* + * tmst_en + * Time Stamp register Enable + */ +#define TMST_CONFIG1_TMST_EN_POS 0x00 +#define TMST_CONFIG1_TMST_EN_MASK 0x01 + + + +/* + * FIFO_CONFIG5 + * Register Name : FIFO_CONFIG5 + */ + +/* + * fifo_wm_gt_th + * 1: trigger FIFO-Watermark interrupt on every ODR(DMA Write) if FIFO_COUNT: =FIFO_WM + * + * 0: Trigger FIFO-Watermark interrupt when FIFO_COUNT == FIFO_WM + */ +#define FIFO_CONFIG5_FIFO_WM_GT_TH_POS 0x05 +#define FIFO_CONFIG5_FIFO_WM_GT_TH_MASK (0x01 << FIFO_CONFIG5_FIFO_WM_GT_TH_POS) + +/* + * fifo_resume_partial_rd + * 0: FIFO is read in packets. If a partial packet is read, then the subsequent read will start from the beginning of the un-read packet. + * 1: FIFO can be read partially. When read is resumed, FIFO bytes will continue from last read point. The SW driver is responsible for cascading previous read and present read and maintain frame boundaries. + */ +#define FIFO_CONFIG5_FIFO_RESUME_PARTIAL_RD_POS 0x04 +#define FIFO_CONFIG5_FIFO_RESUME_PARTIAL_RD_MASK (0x01 << FIFO_CONFIG5_FIFO_RESUME_PARTIAL_RD_POS) + +/* + * fifo_hires_en + * Allows 20 bit resolution in the FIFO packet readout + */ +#define FIFO_CONFIG5_FIFO_HIRES_EN_POS 0x03 +#define FIFO_CONFIG5_FIFO_HIRES_EN_MASK (0x01 << FIFO_CONFIG5_FIFO_HIRES_EN_POS) + +/* + * fifo_tmst_fsync_en + * Allows the TMST in the FIFO to be replaced by the FSYNC timestamp + */ +#define FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_POS 0x02 +#define FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_MASK (0x01 << FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_POS) + +/* + * fifo_gyro_en + * Enables Gyro Packets to go to FIFO + */ +#define FIFO_CONFIG5_FIFO_GYRO_EN_POS 0x01 +#define FIFO_CONFIG5_FIFO_GYRO_EN_MASK (0x01 << FIFO_CONFIG5_FIFO_GYRO_EN_POS) + +/* + * fifo_accel_en + * Enable Accel Packets to go to FIFO + */ +#define FIFO_CONFIG5_FIFO_ACCEL_EN_POS 0x00 +#define FIFO_CONFIG5_FIFO_ACCEL_EN_MASK 0x01 + + + +/* + * FIFO_CONFIG6 + * Register Name : FIFO_CONFIG6 + */ + +/* + * fifo_empty_indicator_dis + * 0: xFF is sent out as FIFO data when FIFO is empty. + * 1: The last FIFO data is sent out when FIFO is empty. + */ +#define FIFO_CONFIG6_FIFO_EMPTY_INDICATOR_DIS_POS 0x04 +#define FIFO_CONFIG6_FIFO_EMPTY_INDICATOR_DIS_MASK (0x01 << FIFO_CONFIG6_FIFO_EMPTY_INDICATOR_DIS_POS) + +/* + * rcosc_req_on_fifo_ths_dis + * 0: When the FIFO is operating in ALP+WUOSC mode and the watermark (WM) interrupt is enabled, the FIFO wakes up the system oscillator (RCOSC) as soon as the watermark level is reached. The system oscillator remains enabled until a Host FIFO read operation happens. This will temporarily cause a small increase in the power consumption due to the enabling of the system oscillator. + * 1: The system oscillator is not automatically woken-up by the FIFO/INT when the WM interrupt is triggered. The side effect is that the host can receive invalid packets until the system oscillator is off after it has been turned on for other reasons not related to a WM interrupt. + * + * The recommended setting of this bit is ‘1’ before entering and during all power modes excluding ALP with WUOSC. This is in order to avoid having to do a FIFO access/flush before entering sleep mode. During ALP with WUOSC it is recommended to set this bit to ‘0’. It is recommended to reset this bit back to ‘1’ before exiting ALP+WUOSC with a wait time of 1 ODR or higher. + */ +#define FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_POS 0x00 +#define FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK 0x01 + + + +/* + * FSYNC_CONFIG + * Register Name : FSYNC_CONFIG + */ + +/* + * fsync_ui_sel + * this register was called (ext_sync_sel) + * 0 Do not tag Fsync flag + * 1 Tag Fsync flag to TEMP_OUT’s LSB + * 2 Tag Fsync flag to GYRO_XOUT’s LSB + * 3 Tag Fsync flag to GYRO_YOUT’s LSB + * 4 Tag Fsync flag to GYRO_ZOUT’s LSB + * 5 Tag Fsync flag to ACCEL_XOUT’s LSB + * 6 Tag Fsync flag to ACCEL_YOUT’s LSB + * 7 Tag Fsync flag to ACCEL_ZOUT’s LSB + */ +#define FSYNC_CONFIG_FSYNC_UI_SEL_POS 0x04 +#define FSYNC_CONFIG_FSYNC_UI_SEL_MASK (0x07 << FSYNC_CONFIG_FSYNC_UI_SEL_POS) + +/* + * fsync_ui_flag_clear_sel + * 0 means the FSYNC flag is cleared when UI sensor reg is updated + * 1 means the FSYNC flag is cleared when UI interface reads the sensor register LSB of FSYNC tagged axis + */ +#define FSYNC_CONFIG_FSYNC_UI_FLAG_CLEAR_SEL_POS 0x01 +#define FSYNC_CONFIG_FSYNC_UI_FLAG_CLEAR_SEL_MASK (0x01 << FSYNC_CONFIG_FSYNC_UI_FLAG_CLEAR_SEL_POS) + +/* + * fsync_polarity + * 0: Start from Rising edge of FSYNC pulse to measure FSYNC interval + * 1: Start from Falling edge of FSYNC pulse to measure FSYNC interval + */ +#define FSYNC_CONFIG_FSYNC_POLARITY_POS 0x00 +#define FSYNC_CONFIG_FSYNC_POLARITY_MASK 0x01 + + + +/* + * INT_CONFIG0 + * Register Name : INT_CONFIG0 + */ + +/* + * ui_drdy_int_clear + * Data Ready Interrupt Clear Option (latched mode) + * 00: Clear on Status Bit Read + * 01: Clear on Status Bit Read + * 10: Clear on Sensor Register Read + * 11: Clear on Status Bit Read OR on Sensor Register read + */ +#define INT_CONFIG0_UI_DRDY_INT_CLEAR_POS 0x04 +#define INT_CONFIG0_UI_DRDY_INT_CLEAR_MASK (0x03 << INT_CONFIG0_UI_DRDY_INT_CLEAR_POS) + +/* + * fifo_ths_int_clear + * FIFO Threshold Interrupt Clear Option (latched mode) + * 00: Clear on Status Bit Read + * 01: Clear on Status Bit Read + * 10: Clear on FIFO data 1Byte Read + * 11: Clear on Status Bit Read OR on FIFO data 1 byte read + */ +#define INT_CONFIG0_FIFO_THS_INT_CLEAR_POS 0x02 +#define INT_CONFIG0_FIFO_THS_INT_CLEAR_MASK (0x03 << INT_CONFIG0_FIFO_THS_INT_CLEAR_POS) + +/* + * fifo_full_int_clear + * FIFO Full Interrupt Clear Option (latched mode) + * 00: Clear on Status Bit Read + * 01: Clear on Status Bit Read + * 10: Clear on FIFO data 1Byte Read + * 11: Clear on Status Bit Read OR on FIFO data 1 byte read + */ +#define INT_CONFIG0_FIFO_FULL_INT_CLEAR_POS 0x00 +#define INT_CONFIG0_FIFO_FULL_INT_CLEAR_MASK 0x03 + + + +/* + * INT_CONFIG1 + * Register Name : INT_CONFIG1 + */ + +/* + * int_tpulse_duration + * 0 - (Default) Interrupt pulse duration is 100us + * 1- Interrupt pulse duration is 8 us + */ +#define INT_CONFIG1_INT_TPULSE_DURATION_POS 0x06 +#define INT_CONFIG1_INT_TPULSE_DURATION_MASK (0x01 << INT_CONFIG1_INT_TPULSE_DURATION_POS) + +/* + * int_async_reset + * 0: The interrupt pulse is reset as soon as the interrupt status register is read if the pulse is still active. + * 1: The interrupt pulse remains high for the intended duration independent of when the interrupt status register is read. This is the default and recommended setting. In this case, when in ALP with the WUOSC clock, the clearing of the interrupt status register requires up to one ODR period after reading. + */ +#define INT_CONFIG1_INT_ASYNC_RESET_POS 0x04 +#define INT_CONFIG1_INT_ASYNC_RESET_MASK (0x01 << INT_CONFIG1_INT_ASYNC_RESET_POS) + + + +/* + * SENSOR_CONFIG3 + * Register Name : SENSOR_CONFIG3 + */ + +/* + * apex_disable + * 1: Disable APEX features to extend FIFO size to 2.25 Kbytes + */ +#define SENSOR_CONFIG3_APEX_DISABLE_POS 0x06 +#define SENSOR_CONFIG3_APEX_DISABLE_MASK (0x01 << SENSOR_CONFIG3_APEX_DISABLE_POS) + +/* + * ST_CONFIG + * Register Name : ST_CONFIG + */ + +/* + * accel_st_reg + * User must set this bit to 1 when enabling accelerometer self-test and clear it to 0 when self-test procedure has completed. + */ +#define ST_CONFIG_ACCEL_ST_REG_POS 0x07 +#define ST_CONFIG_ACCEL_ST_REG_MASK (0x01 << ST_CONFIG_ACCEL_ST_REG_POS) + +/* + * st_number_sample + * This bit selects the number of sensor samples that should be used to process self-test + * 0: 16 samples + * 1: 200 samples + */ +#define ST_CONFIG_ST_NUMBER_SAMPLE_POS 0x06 +#define ST_CONFIG_ST_NUMBER_SAMPLE_MASK (0x01 << ST_CONFIG_ST_NUMBER_SAMPLE_POS) + +/* + * accel_st_lim + * These bits control the tolerated ratio between self-test processed values and reference (fused) ones for accelerometer. + * 0 : 5% + * 1: 10% + * 2: 15% + * 3: 20% + * 4: 25% + * 5: 30% + * 6: 40% + * 7: 50% + */ +#define ST_CONFIG_ACCEL_ST_LIM_POS 0x03 +#define ST_CONFIG_ACCEL_ST_LIM_MASK (0x07 << ST_CONFIG_ACCEL_ST_LIM_POS) + +/* + * gyro_st_lim + * These bits control the tolerated ratio between self-test processed values and reference (fused) ones for gyro. + * 0 : 5% + * 1: 10% + * 2: 15% + * 3: 20% + * 4: 25% + * 5: 30% + * 6: 40% + * 7: 50% + */ +#define ST_CONFIG_GYRO_ST_LIM_POS 0x00 +#define ST_CONFIG_GYRO_ST_LIM_MASK 0x07 + + + +/* + * SELFTEST + * Register Name : SELFTEST + */ + +/* + * gyro_st_en + * 1: enable gyro self test operation. Host needs to program this bit to 0 to move chip out of self test mode. If host programs this bit to 0 while st_busy = 1 and st_done =0, the current running self-test operation is terminated by host. + */ +#define SELFTEST_GYRO_ST_EN_POS 0x07 +#define SELFTEST_GYRO_ST_EN_MASK (0x01 << SELFTEST_GYRO_ST_EN_POS) + +/* + * accel_st_en + * 1: enable accel self test operation. Host needs to program this bit to 0 to move chip out of self test mode. If host programs this bit to 0 while st_busy = 1 and st_done =0, the current running self-test operation is terminated by host. + */ +#define SELFTEST_ACCEL_ST_EN_POS 0x06 +#define SELFTEST_ACCEL_ST_EN_MASK (0x01 << SELFTEST_ACCEL_ST_EN_POS) + +/* + * en_gz_st + * Enable Gyro Z-axis self test + */ +#define SELFTEST_EN_GZ_ST_POS 0x05 +#define SELFTEST_EN_GZ_ST_MASK (0x01 << SELFTEST_EN_GZ_ST_POS) + +/* + * en_gy_st + * Enable Gyro Y-axis self test + */ +#define SELFTEST_EN_GY_ST_POS 0x04 +#define SELFTEST_EN_GY_ST_MASK (0x01 << SELFTEST_EN_GY_ST_POS) + +/* + * en_gx_st + * Enable Gyro X-axis self test + */ +#define SELFTEST_EN_GX_ST_POS 0x03 +#define SELFTEST_EN_GX_ST_MASK (0x01 << SELFTEST_EN_GX_ST_POS) + +/* + * en_az_st + * Enable Accel Z-axis self test + */ +#define SELFTEST_EN_AZ_ST_POS 0x02 +#define SELFTEST_EN_AZ_ST_MASK (0x01 << SELFTEST_EN_AZ_ST_POS) + +/* + * en_ay_st + * Enable Accel Y-axis self test + */ +#define SELFTEST_EN_AY_ST_POS 0x01 +#define SELFTEST_EN_AY_ST_MASK (0x01 << SELFTEST_EN_AY_ST_POS) + +/* + * en_ax_st + * Enable Accel X-axis self test + */ +#define SELFTEST_EN_AX_ST_POS 0x00 +#define SELFTEST_EN_AX_ST_MASK 0x01 + + + +/* + * INTF_CONFIG6 + * Register Name : INTF_CONFIG6 + */ + +/* + * i3c_timeout_en + * Value of 1 to enable i2c/i3c timeout function + */ +#define INTF_CONFIG6_I3C_TIMEOUT_EN_POS 0x04 +#define INTF_CONFIG6_I3C_TIMEOUT_EN_MASK (0x01 << INTF_CONFIG6_I3C_TIMEOUT_EN_POS) + +/* + * i3c_ibi_byte_en + * I3C Enable IBI-payload function. + */ +#define INTF_CONFIG6_I3C_IBI_BYTE_EN_POS 0x03 +#define INTF_CONFIG6_I3C_IBI_BYTE_EN_MASK (0x01 << INTF_CONFIG6_I3C_IBI_BYTE_EN_POS) + +/* + * i3c_ibi_en + * I3C Enable IBI function. + */ +#define INTF_CONFIG6_I3C_IBI_EN_POS 0x02 +#define INTF_CONFIG6_I3C_IBI_EN_MASK (0x01 << INTF_CONFIG6_I3C_IBI_EN_POS) + + + +/* + * INTF_CONFIG10 + * Register Name : INTF_CONFIG10 + */ + +/* + * asynctime0_dis + * 1: Disable asynchronous timing control mode 0 operation. + */ +#define INTF_CONFIG10_ASYNCTIME0_DIS_POS 0x07 +#define INTF_CONFIG10_ASYNCTIME0_DIS_MASK (0x01 << INTF_CONFIG10_ASYNCTIME0_DIS_POS) + +/* + * INTF_CONFIG7 + * Register Name : INTF_CONFIG7 + */ + +/* + * i3c_ddr_wr_mode + * This bit controls how I3C slave treats the 1st 2-byte data from + * host in a DDR write operation. + * + * 0: (a) The 1st-byte in DDR-WR configures the starting register + * address where the write operation should occur. + * (b) The 2nd-byte in DDR-WR is ignored and dropped. + * (c) The 3rd-byte in DDR-WR will be written into the register + * with address specified by the 1st-byte. + * Or, the next DDR-RD will be starting from the address + * specified by the 1st-byte of previous DDR-WR. + * + * 1: (a) The 1st-byte in DDR-WR configures the starting register + * address where the write operation should occur. + * (b) The 2nd-byte in DDR-WR will be written into the register + * with address specified by the 1st-byte. + */ +#define INTF_CONFIG7_I3C_DDR_WR_MODE_POS 0x03 +#define INTF_CONFIG7_I3C_DDR_WR_MODE_MASK (0x01 << INTF_CONFIG7_I3C_DDR_WR_MODE_POS) + +/* + * OTP_CONFIG + * Register Name : OTP_CONFIG + */ + +/* + * otp_copy_mode + * 00: Reserved + * 01: Enable copying OTP block to SRAM + * 10: Reserved + * 11: Enable copying self-test data from OTP memory to SRAM + */ +#define OTP_CONFIG_OTP_COPY_MODE_POS 0x02 +#define OTP_CONFIG_OTP_COPY_MODE_MASK (0x03 << OTP_CONFIG_OTP_COPY_MODE_POS) + +/* + * INT_SOURCE6 + * Register Name : INT_SOURCE6 + */ + +/* + * ff_int1_en + * 0: Freefall interrupt not routed to INT1 + * 1: Freefall interrupt routed to INT1 + */ +#define INT_SOURCE6_FF_INT1_EN_POS 0x07 +#define INT_SOURCE6_FF_INT1_EN_MASK (0x01 << INT_SOURCE6_FF_INT1_EN_POS) + +/* + * lowg_int1_en + * 0: Low-g interrupt not routed to INT1 + * 1: Low-g interrupt routed to INT1 + */ +#define INT_SOURCE6_LOWG_INT1_EN_POS 0x06 +#define INT_SOURCE6_LOWG_INT1_EN_MASK (0x01 << INT_SOURCE6_LOWG_INT1_EN_POS) + +/* + * step_det_int1_en + * 0: Step detect interrupt not routed to INT1 + * 1: Step detect interrupt routed to INT1 + */ +#define INT_SOURCE6_STEP_DET_INT1_EN_POS 0x05 +#define INT_SOURCE6_STEP_DET_INT1_EN_MASK (0x01 << INT_SOURCE6_STEP_DET_INT1_EN_POS) + +/* + * step_cnt_ofl_int1_en + * 0: Step count overflow interrupt not routed to INT1 + * 1: Step count overflow interrupt routed to INT1 + */ +#define INT_SOURCE6_STEP_CNT_OFL_INT1_EN_POS 0x04 +#define INT_SOURCE6_STEP_CNT_OFL_INT1_EN_MASK (0x01 << INT_SOURCE6_STEP_CNT_OFL_INT1_EN_POS) + +/* + * tilt_det_int1_en + * 0: Tilt detect interrupt not routed to INT1 + * 1: Tile detect interrupt routed to INT1 + */ +#define INT_SOURCE6_TILT_DET_INT1_EN_POS 0x03 +#define INT_SOURCE6_TILT_DET_INT1_EN_MASK (0x01 << INT_SOURCE6_TILT_DET_INT1_EN_POS) + + + +/* + * INT_SOURCE7 + * Register Name : INT_SOURCE7 + */ + +/* + * ff_int2_en + * 0: Freefall interrupt not routed to INT2 + * 1: Freefall interrupt routed to INT2 + */ +#define INT_SOURCE7_FF_INT2_EN_POS 0x07 +#define INT_SOURCE7_FF_INT2_EN_MASK (0x01 << INT_SOURCE7_FF_INT2_EN_POS) + +/* + * lowg_int2_en + * 0: Low-g interrupt not routed to INT2 + * 1: Low-g interrupt routed to INT2 + */ +#define INT_SOURCE7_LOWG_INT2_EN_POS 0x06 +#define INT_SOURCE7_LOWG_INT2_EN_MASK (0x01 << INT_SOURCE7_LOWG_INT2_EN_POS) + +/* + * step_det_int2_en + * 0: Step detect interrupt not routed to INT2 + * 1: Step detect interrupt routed to INT2 + */ +#define INT_SOURCE7_STEP_DET_INT2_EN_POS 0x05 +#define INT_SOURCE7_STEP_DET_INT2_EN_MASK (0x01 << INT_SOURCE7_STEP_DET_INT2_EN_POS) + +/* + * step_cnt_ofl_int2_en + * 0: Step count overflow interrupt not routed to INT2 + * 1: Step count overflow interrupt routed to INT2 + */ +#define INT_SOURCE7_STEP_CNT_OFL_INT2_EN_POS 0x04 +#define INT_SOURCE7_STEP_CNT_OFL_INT2_EN_MASK (0x01 << INT_SOURCE7_STEP_CNT_OFL_INT2_EN_POS) + +/* + * tilt_det_int2_en + * 0: Tilt detect interrupt not routed to INT2 + * 1: Tile detect interrupt routed to INT2 + */ +#define INT_SOURCE7_TILT_DET_INT2_EN_POS 0x03 +#define INT_SOURCE7_TILT_DET_INT2_EN_MASK (0x01 << INT_SOURCE7_TILT_DET_INT2_EN_POS) + + + +/* + * INT_SOURCE8 + * Register Name : INT_SOURCE8 + */ + +/* + * fsync_ibi_en + * 0: FSYNC interrupt not routed to IBI + * 1: FSYNC interrupt routed to IBI + */ +#define INT_SOURCE8_FSYNC_IBI_EN_POS 0x05 +#define INT_SOURCE8_FSYNC_IBI_EN_MASK (0x01 << INT_SOURCE8_FSYNC_IBI_EN_POS) + +/* + * pll_rdy_ibi_en + * 0: PLL ready interrupt not routed to IBI + * 1: PLL ready interrupt routed to IBI + */ +#define INT_SOURCE8_PLL_RDY_IBI_EN_POS 0x04 +#define INT_SOURCE8_PLL_RDY_IBI_EN_MASK (0x01 << INT_SOURCE8_PLL_RDY_IBI_EN_POS) + +/* + * ui_drdy_ibi_en + * 0: UI data ready interrupt not routed to IBI + * 1: UI data ready interrupt routed to IBI + */ +#define INT_SOURCE8_UI_DRDY_IBI_EN_POS 0x03 +#define INT_SOURCE8_UI_DRDY_IBI_EN_MASK (0x01 << INT_SOURCE8_UI_DRDY_IBI_EN_POS) + +/* + * fifo_ths_ibi_en + * 0: FIFO threshold interrupt not routed to IBI + * 1: FIFO threshold interrupt routed to IBI + */ +#define INT_SOURCE8_FIFO_THS_IBI_EN_POS 0x02 +#define INT_SOURCE8_FIFO_THS_IBI_EN_MASK (0x01 << INT_SOURCE8_FIFO_THS_IBI_EN_POS) + +/* + * fifo_full_ibi_en + * 0: FIFO full interrupt not routed to IBI + * 1: FIFO full interrupt routed to IBI + */ +#define INT_SOURCE8_FIFO_FULL_IBI_EN_POS 0x01 +#define INT_SOURCE8_FIFO_FULL_IBI_EN_MASK (0x01 << INT_SOURCE8_FIFO_FULL_IBI_EN_POS) + +/* + * agc_rdy_ibi_en + * 0: AGC ready interrupt not routed to IBI + * 1: AGC ready interrupt routed to IBI + */ +#define INT_SOURCE8_AGC_RDY_IBI_EN_POS 0x00 +#define INT_SOURCE8_AGC_RDY_IBI_EN_MASK 0x01 + + + +/* + * INT_SOURCE9 + * Register Name : INT_SOURCE9 + */ + +/* + * i3c_protocol_error_ibi_en + * 0: I3CSM protocol error interrupt not routed to IBI + * 1: I3CSM protocol error interrupt routed to IBI + */ +#define INT_SOURCE9_I3C_PROTOCOL_ERROR_IBI_EN_POS 0x07 +#define INT_SOURCE9_I3C_PROTOCOL_ERROR_IBI_EN_MASK (0x01 << INT_SOURCE9_I3C_PROTOCOL_ERROR_IBI_EN_POS) + +/* + * ff_ibi_en + * 0: Freefall interrupt not routed to IBI + * 1: Freefall interrupt routed to IBI + */ +#define INT_SOURCE9_FF_IBI_EN_POS 0x06 +#define INT_SOURCE9_FF_IBI_EN_MASK (0x01 << INT_SOURCE9_FF_IBI_EN_POS) + +/* + * lowg_ibi_en + * 0: Low-g interrupt not routed to IBI + * 1: Low-g interrupt routed to IBI + */ +#define INT_SOURCE9_LOWG_IBI_EN_POS 0x05 +#define INT_SOURCE9_LOWG_IBI_EN_MASK (0x01 << INT_SOURCE9_LOWG_IBI_EN_POS) + +/* + * smd_ibi_en + * 0: SMD interrupt not routed to IBI + * 1: SMD interrupt routed to IBI + */ +#define INT_SOURCE9_SMD_IBI_EN_POS 0x04 +#define INT_SOURCE9_SMD_IBI_EN_MASK (0x01 << INT_SOURCE9_SMD_IBI_EN_POS) + +/* + * wom_z_ibi_en + * 0: Z-axis WOM interrupt not routed to IBI + * 1: Z-axis WOM interrupt routed to IBI + */ +#define INT_SOURCE9_WOM_Z_IBI_EN_POS 0x03 +#define INT_SOURCE9_WOM_Z_IBI_EN_MASK (0x01 << INT_SOURCE9_WOM_Z_IBI_EN_POS) + +/* + * wom_y_ibi_en + * 0: Y-axis WOM interrupt not routed to IBI + * 1: Y-axis WOM interrupt routed to IBI + */ +#define INT_SOURCE9_WOM_Y_IBI_EN_POS 0x02 +#define INT_SOURCE9_WOM_Y_IBI_EN_MASK (0x01 << INT_SOURCE9_WOM_Y_IBI_EN_POS) + +/* + * wom_x_ibi_en + * 0: X-axis WOM interrupt not routed to IBI + * 1: X-axis WOM interrupt routed to IBI + */ +#define INT_SOURCE9_WOM_X_IBI_EN_POS 0x01 +#define INT_SOURCE9_WOM_X_IBI_EN_MASK (0x01 << INT_SOURCE9_WOM_X_IBI_EN_POS) + +/* + * st_done_ibi_en + * 0: Self-test done interrupt not routed to IBI + * 1: Self-test done interrupt routed to IBI + */ +#define INT_SOURCE9_ST_DONE_IBI_EN_POS 0x00 +#define INT_SOURCE9_ST_DONE_IBI_EN_MASK 0x01 + + + +/* + * INT_SOURCE10 + * Register Name : INT_SOURCE10 + */ + +/* + * step_det_ibi_en + * 0: Step detect interrupt not routed to IBI + * 1: Step detect interrupt routed to IBI + */ +#define INT_SOURCE10_STEP_DET_IBI_EN_POS 0x05 +#define INT_SOURCE10_STEP_DET_IBI_EN_MASK (0x01 << INT_SOURCE10_STEP_DET_IBI_EN_POS) + +/* + * step_cnt_ofl_ibi_en + * 0: Step count overflow interrupt not routed to IBI + * 1: Step count overflow interrupt routed to IBI + */ +#define INT_SOURCE10_STEP_CNT_OFL_IBI_EN_POS 0x04 +#define INT_SOURCE10_STEP_CNT_OFL_IBI_EN_MASK (0x01 << INT_SOURCE10_STEP_CNT_OFL_IBI_EN_POS) + +/* + * tilt_det_ibi_en + * 0: Tilt detect interrupt not routed to IBI + * 1: Tile detect interrupt routed to IBI + */ +#define INT_SOURCE10_TILT_DET_IBI_EN_POS 0x03 +#define INT_SOURCE10_TILT_DET_IBI_EN_MASK (0x01 << INT_SOURCE10_TILT_DET_IBI_EN_POS) + + + +/* + * APEX_CONFIG2 + * Register Name : APEX_CONFIG2 + */ + +/* + * low_energy_amp_th_sel + * Threshold to select a valid step. Used to increase step detection for slow walk use case. + * + * 0000: 30 mg + * 0001: 35 mg + * 0010: 40 mg + * 0011: 45 mg + * 0100: 50 mg + * 0101: 55 mg + * 0110: 60 mg + * 0111: 65 mg + * 1000: 70 mg + * 1001: 75 mg + * 1010: 80 mg (default) + * 1011: 85 mg + * 1100: 90 mg + * 1101: 95 mg + * 1110: 100 mg + * 1111: 105 mg + */ +#define APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS 0x04 +#define APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_MASK (0x0f << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS) + +/* + * dmp_power_save_time_sel + * Duration of the period while the DMP stays awake after receiving a WOM event. + * + * 0000: 0 seconds + * 0001: 4 seconds + * 0010: 8 seconds (default) + * 0011: 12 seconds + * 0100: 16 seconds + * 0101: 20 seconds + * 0110: 24 seconds + * 0111: 28 seconds + * 1000: 32 seconds + * 1001: 36 seconds + * 1010: 40 seconds + * 1011: 44 seconds + * 1100: 48 seconds + * 1101: 52 seconds + * 1110: 56 seconds + * 1111: 60 seconds + */ +#define APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_POS 0x00 +#define APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_MASK 0x0f + + + +/* + * APEX_CONFIG3 + * Register Name : APEX_CONFIG3 + */ + +/* + * ped_amp_th_sel + * Threshold of step detection sensitivity. + * + * Low values increase detection sensitivity: reduce miss-detection. + * High values reduce detection sensitivity: reduce false-positive. + * + * 0000: 30 mg + * 0001: 34 mg + * 0010: 38 mg + * 0011: 42 mg + * 0100: 46 mg + * 0101: 50 mg + * 0110: 54 mg + * 0111: 58 mg + * 1000: 62 mg (default) + * 1001: 66 mg + * 1010: 70 mg + * 1011: 74 mg + * 1100: 78 mg + * 1101: 82 mg + * 1110: 86 mg + * 1111: 90 mg + */ +#define APEX_CONFIG3_PED_AMP_TH_SEL_POS 0x04 +#define APEX_CONFIG3_PED_AMP_TH_SEL_MASK (0x0f << APEX_CONFIG3_PED_AMP_TH_SEL_POS) + +/* + * ped_step_cnt_th_sel + * Minimum number of steps that must be detected before step count is incremented. + * + * Low values reduce latency but increase false positives. + * High values increase step count accuracy but increase latency. + * + * 0000: 0 steps + * 0001: 1 step + * 0010: 2 steps + * 0011: 3 steps + * 0100: 4 steps + * 0101: 5 steps (default) + * 0110: 6 steps + * 0111: 7 steps + * 1000: 8 steps + * 1001: 9 steps + * 1010: 10 steps + * 1011: 11 steps + * 1100: 12 steps + * 1101: 13 steps + * 1110: 14 steps + * 1111: 15 steps + */ +#define APEX_CONFIG3_PED_STEP_CNT_TH_SEL_POS 0x00 +#define APEX_CONFIG3_PED_STEP_CNT_TH_SEL_MASK 0x0f + + + +/* + * APEX_CONFIG4 + * Register Name : APEX_CONFIG4 + */ + +/* + * ped_step_det_th_sel + * Minimum number of steps that must be detected before step event is signaled. + * + * Low values reduce latency but increase false positives. + * High values increase step event validity but increase latency. + * + * 000: 0 steps + * 001: 1 step + * 010: 2 steps (default) + * 011: 3 steps + * 100: 4 steps + * 101: 5 steps + * 110: 6 steps + * 111: 7 steps + */ +#define APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS 0x05 +#define APEX_CONFIG4_PED_STEP_DET_TH_SEL_MASK (0x07 << APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS) + +/* + * ped_sb_timer_th_sel + * Duration before algorithm considers that user has stopped taking steps. + * + * 000: 50 samples + * 001: 75 sample + * 010: 100 samples + * 011: 125 samples + * 100: 150 samples (default) + * 101: 175 samples + * 110: 200 samples + * 111: 225 samples + */ +#define APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS 0x02 +#define APEX_CONFIG4_PED_SB_TIMER_TH_SEL_MASK (0x07 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS) + +/* + * ped_hi_en_th_sel + * Threshold to classify acceleration signal as motion not due to steps. + * + * High values improve vibration rejection. + * Low values improve detection. + * + * 00: 87.89 mg + * 01: 104.49 mg (default) + * 10: 132.81 mg + * 11: 155.27 mg + */ +#define APEX_CONFIG4_PED_HI_EN_TH_SEL_POS 0x00 +#define APEX_CONFIG4_PED_HI_EN_TH_SEL_MASK 0x03 + + + +/* + * APEX_CONFIG5 + * Register Name : APEX_CONFIG5 + */ + +/* + * tilt_wait_time_sel + * Minimum duration for which the device should be tilted before signaling event. + * + * 00: 0s + * 01: 2s + * 10: 4s (default) + * 11: 6s + */ +#define APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS 0x06 +#define APEX_CONFIG5_TILT_WAIT_TIME_SEL_MASK (0x03 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS) + +/* + * lowg_peak_th_hyst_sel + * Hysteresis value added to the low-g threshold after exceeding it. + * + * 000: 31 mg (default) + * 001: 63 mg + * 010: 94 mg + * 011: 125 mg + * 100: 156 mg + * 101: 188 mg + * 110: 219 mg + * 111: 250 mg + */ +#define APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS 0x03 +#define APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_MASK (0x07 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS) + +/* + * highg_peak_th_hyst_sel + * Hysteresis value subtracted from the high-g threshold after exceeding it. + * + * 000: 31 mg (default) + * 001: 63 mg + * 010: 94 mg + * 011: 125 mg + * 100: 156 mg + * 101: 188 mg + * 110: 219 mg + * 111: 250 mg + */ +#define APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS 0x00 +#define APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_MASK 0x07 + + + +/* + * APEX_CONFIG9 + * Register Name : APEX_CONFIG9 + */ + +/* + * ff_debounce_duration_sel + * Period after a freefall is signaled during which a new freefall will not be detected. Prevents false detection due to bounces. + * + * 0000: 0 ms + * 0001: 1250 ms + * 0010: 1375 ms + * 0011: 1500 ms + * 0100: 1625 ms + * 0101: 1750 ms + * 0110: 1875 ms + * 0111: 2000 ms + * 1000: 2125 ms (default) + * 1001: 2250 ms + * 1010: 2375 ms + * 1011: 2500 ms + * 1100: 2625 ms + * 1101: 2750 ms + * 1110: 2875 ms + * 1111: 3000 ms + */ +#define APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS 0x04 +#define APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_MASK (0x0f << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS) + +/* + * smd_sensitivity_sel + * Parameter to tune SMD algorithm robustness to rejection, ranging from 0 to 4 (values higher than 4 are reserved). + * + * Low values increase detection rate but increase false positives. + * High values reduce false positives but reduce detection rate (especially for transport use cases). + * + * Default value is 0. + */ +#define APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS 0x01 +#define APEX_CONFIG9_SMD_SENSITIVITY_SEL_MASK (0x07 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS) + +/* + * sensitivity_mode + * Pedometer sensitivity mode + * 0: Normal (default) + * 1: Slow walk + * + * Slow walk mode improves slow walk detection (<1Hz) but the number of false positives may increase. + */ +#define APEX_CONFIG9_SENSITIVITY_MODE_POS 0x00 +#define APEX_CONFIG9_SENSITIVITY_MODE_MASK 0x01 + + + +/* + * APEX_CONFIG10 + * Register Name : APEX_CONFIG10 + */ + +/* + * lowg_peak_th_sel + * Threshold for accel values below which low-g state is detected. + * + * 00000: 31 mg (default) + * 00001: 63 mg + * 00010: 94 mg + * 00011: 125 mg + * 00100: 156 mg + * 00101: 188 mg + * 00110: 219 mg + * 00111: 250 mg + * 01000: 281 mg + * 01001: 313 mg + * 01010: 344 mg + * 01011: 375 mg + * 01100: 406 mg + * 01101: 438 mg + * 01110: 469 mg + * 01111: 500 mg + * 10000: 531 mg + * 10001: 563 mg + * 10010: 594 mg + * 10011: 625 mg + * 10100: 656 mg + * 10101: 688 mg + * 10110: 719 mg + * 10111: 750 mg + * 11000: 781 mg + * 11001: 813 mg + * 11010: 844 mg + * 11011: 875 mg + * 11100: 906 mg + * 11101: 938 mg + * 11110: 969 mg + * 11111: 1000 mg + */ +#define APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS 0x03 +#define APEX_CONFIG10_LOWG_PEAK_TH_SEL_MASK (0x1f << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS) + +/* + * lowg_time_th_sel + * Number of samples required to enter low-g state. + * + * 000: 1 sample (default) + * 001: 2 samples + * 010: 3 samples + * 011: 4 samples + * 100: 5 samples + * 101: 6 samples + * 110: 7 samples + * 111: 8 samples + */ +#define APEX_CONFIG10_LOWG_TIME_TH_SEL_POS 0x00 +#define APEX_CONFIG10_LOWG_TIME_TH_SEL_MASK 0x07 + + + +/* + * APEX_CONFIG11 + * Register Name : APEX_CONFIG11 + */ + +/* + * highg_peak_th_sel + * Threshold for accel values above which high-g state is detected. + * + * 00000: 250 mg (default) + * 00001: 500 mg + * 00010: 750 mg + * 00011: 1000 mg + * 00100: 1250 mg + * 00101: 1500 mg + * 00110: 1750 mg + * 00111: 2000 mg + * 01000: 2250 mg + * 01001: 2500 mg + * 01010: 2750 mg + * 01011: 3000 mg + * 01100: 3250 mg + * 01101: 3500 mg + * 01110: 3750 mg + * 01111: 4000 mg + * 10000: 4250 mg + * 10001: 4500 mg + * 10010: 4750 mg + * 10011: 5000 mg + * 10100: 5250 mg + * 10101: 5500 mg + * 10110: 5750 mg + * 10111: 6000 mg + * 11000: 6250 mg + * 11001: 6500 mg + * 11010: 6750 mg + * 11011: 7000 mg + * 11100: 7250 mg + * 11101: 7500 mg + * 11110: 7750 mg + * 11111: 8000 mg + */ +#define APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS 0x03 +#define APEX_CONFIG11_HIGHG_PEAK_TH_SEL_MASK (0x1f << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS) + +/* + * highg_time_th_sel + * Number of samples required to enter high-g state. + * + * 000: 1 sample (default) + * 001: 2 samples + * 010: 3 samples + * 011: 4 samples + * 100: 5 samples + * 101: 6 samples + * 110: 7 samples + * 111: 8 samples + */ +#define APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS 0x00 +#define APEX_CONFIG11_HIGHG_TIME_TH_SEL_MASK 0x07 + + + +/* + * ACCEL_WOM_X_THR + * Register Name : ACCEL_WOM_X_THR + */ + +/* + * wom_x_th + * Threshold value for the Wake on Motion Interrupt for X-axis accelerometer + * WoM thresholds are expressed in fixed “mg” independent of the selected Range [0g : 1g]; Resolution 1g/256=~3.9mg + */ +#define ACCEL_WOM_X_THR_WOM_X_TH_POS 0x00 +#define ACCEL_WOM_X_THR_WOM_X_TH_MASK 0xff + + + +/* + * ACCEL_WOM_Y_THR + * Register Name : ACCEL_WOM_Y_THR + */ + +/* + * wom_y_th + * Threshold value for the Wake on Motion Interrupt for Y-axis accelerometer + * WoM thresholds are expressed in fixed “mg” independent of the selected Range [0g : 1g]; Resolution 1g/256=~3.9mg + */ +#define ACCEL_WOM_Y_THR_WOM_Y_TH_POS 0x00 +#define ACCEL_WOM_Y_THR_WOM_Y_TH_MASK 0xff + + + +/* + * ACCEL_WOM_Z_THR + * Register Name : ACCEL_WOM_Z_THR + */ + +/* + * wom_z_th + * Threshold value for the Wake on Motion Interrupt for Z-axis accelerometer + * WoM thresholds are expressed in fixed “mg” independent of the selected Range [0g : 1g]; Resolution 1g/256=~3.9mg + */ +#define ACCEL_WOM_Z_THR_WOM_Z_TH_POS 0x00 +#define ACCEL_WOM_Z_THR_WOM_Z_TH_MASK 0xff + + + +/* + * OFFSET_USER0 + * Register Name : OFFSET_USER0 + */ + +/* + * gyro_x_offuser + * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps + */ +#define OFFSET_USER0_GYRO_X_OFFUSER_POS 0x00 +#define OFFSET_USER0_GYRO_X_OFFUSER_MASK 0xff + + + +/* + * OFFSET_USER1 + * Register Name : OFFSET_USER1 + */ + +/* + * gyro_x_offuser + * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps + */ +#define OFFSET_USER1_GYRO_X_OFFUSER_POS 0x00 +#define OFFSET_USER1_GYRO_X_OFFUSER_MASK 0x0f + +/* + * gyro_y_offuser + * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps + */ +#define OFFSET_USER1_GYRO_Y_OFFUSER_POS 0x04 +#define OFFSET_USER1_GYRO_Y_OFFUSER_MASK (0x0f << OFFSET_USER1_GYRO_Y_OFFUSER_POS) + + + +/* + * OFFSET_USER2 + * Register Name : OFFSET_USER2 + */ + +/* + * gyro_y_offuser + * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps + */ +#define OFFSET_USER2_GYRO_Y_OFFUSER_POS 0x00 +#define OFFSET_USER2_GYRO_Y_OFFUSER_MASK 0xff + + + +/* + * OFFSET_USER3 + * Register Name : OFFSET_USER3 + */ + +/* + * gyro_z_offuser + * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps + */ +#define OFFSET_USER3_GYRO_Z_OFFUSER_POS 0x00 +#define OFFSET_USER3_GYRO_Z_OFFUSER_MASK 0xff + + + +/* + * OFFSET_USER4 + * Register Name : OFFSET_USER4 + */ + +/* + * gyro_z_offuser + * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps + */ +#define OFFSET_USER4_GYRO_Z_OFFUSER_POS 0x00 +#define OFFSET_USER4_GYRO_Z_OFFUSER_MASK 0x0f + +/* + * accel_x_offuser + * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee + */ +#define OFFSET_USER4_ACCEL_X_OFFUSER_POS 0x04 +#define OFFSET_USER4_ACCEL_X_OFFUSER_MASK (0x0f << OFFSET_USER4_ACCEL_X_OFFUSER_POS) + + + +/* + * OFFSET_USER5 + * Register Name : OFFSET_USER5 + */ + +/* + * accel_x_offuser + * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee + */ +#define OFFSET_USER5_ACCEL_X_OFFUSER_POS 0x00 +#define OFFSET_USER5_ACCEL_X_OFFUSER_MASK 0xff + + + +/* + * OFFSET_USER6 + * Register Name : OFFSET_USER6 + */ + +/* + * accel_y_offuser + * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee + */ +#define OFFSET_USER6_ACCEL_Y_OFFUSER_POS 0x00 +#define OFFSET_USER6_ACCEL_Y_OFFUSER_MASK 0xff + + + +/* + * OFFSET_USER7 + * Register Name : OFFSET_USER7 + */ + +/* + * accel_y_offuser + * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee + */ +#define OFFSET_USER7_ACCEL_Y_OFFUSER_POS 0x00 +#define OFFSET_USER7_ACCEL_Y_OFFUSER_MASK 0x0f + +/* + * accel_z_offuser + * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee + */ +#define OFFSET_USER7_ACCEL_Z_OFFUSER_POS 0x04 +#define OFFSET_USER7_ACCEL_Z_OFFUSER_MASK (0x0f << OFFSET_USER7_ACCEL_Z_OFFUSER_POS) + + + +/* + * OFFSET_USER8 + * Register Name : OFFSET_USER8 + */ + +/* + * accel_z_offuser + * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee + */ +#define OFFSET_USER8_ACCEL_Z_OFFUSER_POS 0x00 +#define OFFSET_USER8_ACCEL_Z_OFFUSER_MASK 0xff + + + +/* + * ST_STATUS1 + * Register Name : ST_STATUS1 + */ + +/* + * accel_st_pass + * 1: Accel self-test passed for all the 3 axes + */ +#define ST_STATUS1_ACCEL_ST_PASS_POS 0x05 +#define ST_STATUS1_ACCEL_ST_PASS_MASK (0x01 << ST_STATUS1_ACCEL_ST_PASS_POS) + +/* + * accel_st_done + * 1: Accel self-test done for all the 3 axes + */ +#define ST_STATUS1_ACCEL_ST_DONE_POS 0x04 +#define ST_STATUS1_ACCEL_ST_DONE_MASK (0x01 << ST_STATUS1_ACCEL_ST_DONE_POS) + +/* + * az_st_pass + * 1: Accel Z-axis self-test passed + */ +#define ST_STATUS1_AZ_ST_PASS_POS 0x03 +#define ST_STATUS1_AZ_ST_PASS_MASK (0x01 << ST_STATUS1_AZ_ST_PASS_POS) + +/* + * ay_st_pass + * 1: Accel Y-axis self-test passed + */ +#define ST_STATUS1_AY_ST_PASS_POS 0x02 +#define ST_STATUS1_AY_ST_PASS_MASK (0x01 << ST_STATUS1_AY_ST_PASS_POS) + +/* + * ax_st_pass + * 1: Accel X-axis self-test passed + */ +#define ST_STATUS1_AX_ST_PASS_POS 0x01 +#define ST_STATUS1_AX_ST_PASS_MASK (0x01 << ST_STATUS1_AX_ST_PASS_POS) + + + +/* + * ST_STATUS2 + * Register Name : ST_STATUS2 + */ + +/* + * st_incomplete + * 1: Self-test is incomplete. + * This bit is set to 1 if the self-test was aborted. + * One possible cause of aborting the self-test may be the detection of significant movement in the gyro when the self-test for gyro and/or accel is being executed. + */ +#define ST_STATUS2_ST_INCOMPLETE_POS 0x06 +#define ST_STATUS2_ST_INCOMPLETE_MASK (0x01 << ST_STATUS2_ST_INCOMPLETE_POS) + +/* + * gyro_st_pass + * 1: Gyro self-test passed for all the 3 axes + */ +#define ST_STATUS2_GYRO_ST_PASS_POS 0x05 +#define ST_STATUS2_GYRO_ST_PASS_MASK (0x01 << ST_STATUS2_GYRO_ST_PASS_POS) + +/* + * gyro_st_done + * 1: Gyro self-test done for all the 3 axes + */ +#define ST_STATUS2_GYRO_ST_DONE_POS 0x04 +#define ST_STATUS2_GYRO_ST_DONE_MASK (0x01 << ST_STATUS2_GYRO_ST_DONE_POS) + +/* + * gz_st_pass + * 1: Gyro Z-axis self-test passed + */ +#define ST_STATUS2_GZ_ST_PASS_POS 0x03 +#define ST_STATUS2_GZ_ST_PASS_MASK (0x01 << ST_STATUS2_GZ_ST_PASS_POS) + +/* + * gy_st_pass + * 1: Gyro Y-axis self-test passed + */ +#define ST_STATUS2_GY_ST_PASS_POS 0x02 +#define ST_STATUS2_GY_ST_PASS_MASK (0x01 << ST_STATUS2_GY_ST_PASS_POS) + +/* + * gx_st_pass + * 1: Gyro X-axis self-test passed + */ +#define ST_STATUS2_GX_ST_PASS_POS 0x01 +#define ST_STATUS2_GX_ST_PASS_MASK (0x01 << ST_STATUS2_GX_ST_PASS_POS) + + + +/* + * FDR_CONFIG + * Register Name : FDR_CONFIG + */ + +/* + * fdr_sel + * [7:4] Reserved + * [3:0] FIFO packet rate decimation factor. Sets the number of discarded FIFO packets. Valid range is 0 to 127. User must disable sensors when initializing FDR_SEL value or making changes to it. + * + * 0xxx: Decimation is disabled, all packets are sent to FIFO + * 1000: 1 packet out of 2 is sent to FIFO + * 1001: 1 packet out of 4 is sent to FIFO + * 1010: 1 packet out of 8 is sent to FIFO + * 1011: 1 packet out of 16 is sent to FIFO + * 1100: 1 packet out of 32 is sent to FIFO + * 1101: 1 packet out of 64 is sent to FIFO + * 1110: 1 packet out of 128 is sent to FIFO + * 1111: 1 packet out of 256 is sent to FIFO + */ +#define FDR_CONFIG_FDR_SEL_POS 0x00 +#define FDR_CONFIG_FDR_SEL_MASK 0xff + + + +/* + * APEX_CONFIG12 + * Register Name : APEX_CONFIG12 + */ + +/* + * ff_max_duration_sel + * Maximum freefall length. Longer freefalls are ignored. + * + * 0000: 102 cm (default) + * 0001: 120 cm + * 0010: 139 cm + * 0011: 159 cm + * 0100: 181 cm + * 0101: 204 cm + * 0110: 228 cm + * 0111: 254 cm + * 1000: 281 cm + * 1001: 310 cm + * 1010: 339 cm + * 1011: 371 cm + * 1100: 403 cm + * 1101: 438 cm + * 1110: 473 cm + * 1111: 510 cm + */ +#define APEX_CONFIG12_FF_MAX_DURATION_SEL_POS 0x04 +#define APEX_CONFIG12_FF_MAX_DURATION_SEL_MASK (0x0f << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS) + +/* + * ff_min_duration_sel + * Minimum freefall length. Shorter freefalls are ignored. + * + * 0000: 10 cm (default) + * 0001: 12 cm + * 0010: 13 cm + * 0011: 16 cm + * 0100: 18 cm + * 0101: 20 cm + * 0110: 23 cm + * 0111: 25 cm + * 1000: 28 cm + * 1001: 31 cm + * 1010: 34 cm + * 1011: 38 cm + * 1100: 41 cm + * 1101: 45 cm + * 1110: 48 cm + * 1111: 52 cm + */ +#define APEX_CONFIG12_FF_MIN_DURATION_SEL_POS 0x00 +#define APEX_CONFIG12_FF_MIN_DURATION_SEL_MASK 0x0f + + +/* --------------------------------------------------------------------------- + * register MREG3 + * ---------------------------------------------------------------------------*/ + +/* + * XA_ST_DATA + * Register Name : XA_ST_DATA + */ + +/* + * xa_st_data + * Accel X-axis self test data converted to 8 bit code. + */ +#define XA_ST_DATA_XA_ST_DATA_POS 0x00 +#define XA_ST_DATA_XA_ST_DATA_MASK 0xff + + + +/* + * YA_ST_DATA + * Register Name : YA_ST_DATA + */ + +/* + * ya_st_data + * Accel Y-axis self test data converted to 8 bit code. + */ +#define YA_ST_DATA_YA_ST_DATA_POS 0x00 +#define YA_ST_DATA_YA_ST_DATA_MASK 0xff + + + +/* + * ZA_ST_DATA + * Register Name : ZA_ST_DATA + */ + +/* + * za_st_data + * Accel Z-axis self test data converted to 8 bit code. + */ +#define ZA_ST_DATA_ZA_ST_DATA_POS 0x00 +#define ZA_ST_DATA_ZA_ST_DATA_MASK 0xff + + + +/* + * XG_ST_DATA + * Register Name : XG_ST_DATA + */ + +/* + * xg_st_data + * Gyro X-axis self test data converted to 8 bit code. + */ +#define XG_ST_DATA_XG_ST_DATA_POS 0x00 +#define XG_ST_DATA_XG_ST_DATA_MASK 0xff + + + +/* + * YG_ST_DATA + * Register Name : YG_ST_DATA + */ + +/* + * yg_st_data + * Gyro Y-axis self test data converted to 8 bit code. + */ +#define YG_ST_DATA_YG_ST_DATA_POS 0x00 +#define YG_ST_DATA_YG_ST_DATA_MASK 0xff + + + +/* + * ZG_ST_DATA + * Register Name : ZG_ST_DATA + */ + +/* + * zg_st_data + * Gyro Z-axis self test data converted to 8 bit code. + */ +#define ZG_ST_DATA_ZG_ST_DATA_POS 0x00 +#define ZG_ST_DATA_ZG_ST_DATA_MASK 0xff + + +/* --------------------------------------------------------------------------- + * register MREG2 + * ---------------------------------------------------------------------------*/ + +/* + * OTP_CTRL7 + * Register Name : OTP_CTRL7 + */ + +/* + * otp_reload + * 1: to trigger OTP copy operation. This bit is cleared to 0 after OTP copy is done. + * + * With otp_copy_mode[1:0] = 2'b01, it takes 280us to complete the OTP reloading operation. + * With otp_copy_mode[1:0] = 2'b11, it takes 20us to complete the OTP reloading operation. + */ +#define OTP_CTRL7_OTP_RELOAD_POS 0x03 +#define OTP_CTRL7_OTP_RELOAD_MASK (0x01 << OTP_CTRL7_OTP_RELOAD_POS) + +/* + * otp_pwr_down + * 0: Power up OTP to copy from OTP to SRAM + * 1: Power down OTP + * + * This bit is automatically set to 1 when OTP copy operation is complete. + */ +#define OTP_CTRL7_OTP_PWR_DOWN_POS 0x01 +#define OTP_CTRL7_OTP_PWR_DOWN_MASK (0x01 << OTP_CTRL7_OTP_PWR_DOWN_POS) + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef _INV_IMU_REGMAP_H_*/ diff --git a/lib/ICM42670P/src/imu/inv_imu_transport.c b/lib/ICM42670P/src/imu/inv_imu_transport.c new file mode 100644 index 0000000..2b16ac1 --- /dev/null +++ b/lib/ICM42670P/src/imu/inv_imu_transport.c @@ -0,0 +1,273 @@ +/* + * ________________________________________________________________________________________________________ + * Copyright (c) 2015-2015 InvenSense Inc. All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * ________________________________________________________________________________________________________ + */ + +#include "imu/inv_imu_extfunc.h" +#include "imu/inv_imu_transport.h" +#include "imu/inv_imu_regmap.h" + +#include "Invn/InvError.h" + +/* Function definition */ +static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg); +static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf); +static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf); +static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, + const uint8_t *buf); +static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf); + +int inv_imu_init_transport(struct inv_imu_device *s) +{ + int status = 0; + struct inv_imu_transport *t = (struct inv_imu_transport *)s; + + status |= read_sreg(s, (uint8_t)PWR_MGMT0, 1, &(t->register_cache.pwr_mgmt0_reg)); + status |= read_sreg(s, (uint8_t)GYRO_CONFIG0, 1, &(t->register_cache.gyro_config0_reg)); + status |= read_sreg(s, (uint8_t)ACCEL_CONFIG0, 1, &(t->register_cache.accel_config0_reg)); + + status |= + read_mclk_reg(s, (TMST_CONFIG1_MREG1 & 0xFFFF), 1, &(t->register_cache.tmst_config1_reg)); + + t->need_mclk_cnt = 0; + + return status; +} + +int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf) +{ + uint32_t i; + int rc = 0; + + for (i = 0; i < len; i++) { + uint8_t *cache_addr = get_register_cache_addr(s, reg + i); + + if (cache_addr) { + buf[i] = *cache_addr; + } else { + if (!(reg & 0x10000)) { + rc |= read_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]); + } else { + rc |= read_sreg(s, (uint8_t)reg + i, len - i, &buf[i]); + break; + } + } + } + + return rc; +} + +int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf) +{ + uint32_t i; + int rc = 0; + + for (i = 0; i < len; i++) { + uint8_t *cache_addr = get_register_cache_addr(s, reg + i); + + if (cache_addr) + *cache_addr = buf[i]; + + if (!(reg & 0x10000)) + rc |= write_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]); + } + + if (reg & 0x10000) + rc |= write_sreg(s, (uint8_t)reg, len, buf); + + return rc; +} + +int inv_imu_switch_on_mclk(struct inv_imu_device *s) +{ + int status = 0; + uint8_t data; + struct inv_imu_transport *t = (struct inv_imu_transport *)s; + uint64_t timeout_us = 1000000; /* 1 sec */ + uint64_t start; + uint64_t current; + + /* set IDLE bit only if it is not set yet */ + if (t->need_mclk_cnt == 0) { + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); + data |= PWR_MGMT0_IDLE_MASK; + status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data); + + if (status) + return status; + + /* Check if MCLK is ready */ + start = inv_imu_get_time_us(); + do { + status = inv_imu_read_reg(s, MCLK_RDY, 1, &data); + + if (status) + return status; + + /* Timeout */ + current = inv_imu_get_time_us(); + if (current - start > timeout_us) + return INV_ERROR_TIMEOUT; + + } while (!(data & MCLK_RDY_MCLK_RDY_MASK)); + } else { + /* Make sure it is already on */ + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); + if (0 == (data &= PWR_MGMT0_IDLE_MASK)) + status |= INV_ERROR; + } + + /* Increment the counter to keep track of number of MCLK requesters */ + t->need_mclk_cnt++; + + return status; +} + +int inv_imu_switch_off_mclk(struct inv_imu_device *s) +{ + int status = 0; + uint8_t data; + struct inv_imu_transport *t = (struct inv_imu_transport *)s; + + /* Reset the IDLE but only if there is one requester left */ + if (t->need_mclk_cnt == 1) { + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); + data &= ~PWR_MGMT0_IDLE_MASK; + status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data); + } else { + /* Make sure it is still on */ + status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); + if (0 == (data &= PWR_MGMT0_IDLE_MASK)) + status |= INV_ERROR; + } + + /* Decrement the counter */ + t->need_mclk_cnt--; + + return status; +} + +/* Static function */ + +static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg) +{ + struct inv_imu_transport *t = (struct inv_imu_transport *)s; + + switch (reg) { + case PWR_MGMT0: + return &(t->register_cache.pwr_mgmt0_reg); + case GYRO_CONFIG0: + return &(t->register_cache.gyro_config0_reg); + case ACCEL_CONFIG0: + return &(t->register_cache.accel_config0_reg); + case TMST_CONFIG1_MREG1: + return &(t->register_cache.tmst_config1_reg); + default: + return (uint8_t *)0; // Not found + } +} + +static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf) +{ + struct inv_imu_serif *serif = (struct inv_imu_serif *)s; + + if (len > serif->max_read) + return INV_ERROR_SIZE; + + if (serif->read_reg(serif, reg, buf, len) != 0) + return INV_ERROR_TRANSPORT; + + return 0; +} + +static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf) +{ + struct inv_imu_serif *serif = (struct inv_imu_serif *)s; + + if (len > serif->max_write) + return INV_ERROR_SIZE; + + if (serif->write_reg(serif, reg, buf, len) != 0) + return INV_ERROR_TRANSPORT; + + return 0; +} + +static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf) +{ + uint8_t data; + uint8_t blk_sel = (regaddr & 0xFF00) >> 8; + int status = 0; + + // Have IMU not in IDLE mode to access MCLK domain + status |= inv_imu_switch_on_mclk(s); + + // optimize by changing BLK_SEL only if not NULL + if (blk_sel) + status |= write_sreg(s, (uint8_t)BLK_SEL_R & 0xff, 1, &blk_sel); + + data = (regaddr & 0x00FF); + status |= write_sreg(s, (uint8_t)MADDR_R, 1, &data); + inv_imu_sleep_us(10); + status |= read_sreg(s, (uint8_t)M_R, rd_cnt, buf); + inv_imu_sleep_us(10); + + if (blk_sel) { + data = 0; + status |= write_sreg(s, (uint8_t)BLK_SEL_R, 1, &data); + } + + // switch OFF MCLK if needed + status |= inv_imu_switch_off_mclk(s); + + return status; +} + +static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, + const uint8_t *buf) +{ + uint8_t data; + uint8_t blk_sel = (regaddr & 0xFF00) >> 8; + int status = 0; + + // Have IMU not in IDLE mode to access MCLK domain + status |= inv_imu_switch_on_mclk(s); + + // optimize by changing BLK_SEL only if not NULL + if (blk_sel) + status |= write_sreg(s, (uint8_t)BLK_SEL_W, 1, &blk_sel); + + data = (regaddr & 0x00FF); + status |= write_sreg(s, (uint8_t)MADDR_W, 1, &data); + for (uint8_t i = 0; i < wr_cnt; i++) { + status |= write_sreg(s, (uint8_t)M_W, 1, &buf[i]); + inv_imu_sleep_us(10); + } + + if (blk_sel) { + data = 0; + status = write_sreg(s, (uint8_t)BLK_SEL_W, 1, &data); + } + + status |= inv_imu_switch_off_mclk(s); + + return status; +} diff --git a/lib/ICM42670P/src/imu/inv_imu_transport.h b/lib/ICM42670P/src/imu/inv_imu_transport.h new file mode 100644 index 0000000..dd0845b --- /dev/null +++ b/lib/ICM42670P/src/imu/inv_imu_transport.h @@ -0,0 +1,121 @@ +/* + * ________________________________________________________________________________________________________ + * Copyright (c) 2015-2015 InvenSense Inc. All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * ________________________________________________________________________________________________________ + */ + +/** @defgroup Transport Transport + * @brief Abstraction layer to access device's registers + * @{ + */ + +/** @file inv_imu_transport.h */ + +#ifndef _INV_IMU_TRANSPORT_H_ +#define _INV_IMU_TRANSPORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* forward declaration */ +struct inv_imu_device; + +/** Available serial interface type. */ +typedef enum { + UI_I2C, + UI_SPI4, + UI_SPI3 +} SERIAL_IF_TYPE_t; + +/** Serial interface definition */ +struct inv_imu_serif { + void *context; + int (*read_reg)(struct inv_imu_serif *serif, uint8_t reg, uint8_t *buf, uint32_t len); + int (*write_reg)(struct inv_imu_serif *serif, uint8_t reg, const uint8_t *buf, uint32_t len); + uint32_t max_read; + uint32_t max_write; + SERIAL_IF_TYPE_t serif_type; +}; + +/** Transport interface definition. */ +struct inv_imu_transport { + /** Serial interface object. + * @warning Must be the first object in this structure. + */ + struct inv_imu_serif serif; + + /** Contains mirrored values of some IP registers. */ + struct register_cache { + uint8_t pwr_mgmt0_reg; + uint8_t gyro_config0_reg; + uint8_t accel_config0_reg; + uint8_t tmst_config1_reg; + } register_cache; + + /** Internal counter for MCLK requests. */ + uint8_t need_mclk_cnt; +}; + +/** @brief Init cache variable. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_init_transport(struct inv_imu_device *s); + +/** @brief Reads data from a register on IMU. + * @param[in] s Pointer to device. + * @param[in] reg Register address to be read. + * @param[in] len Number of byte to be read. + * @param[out] buf Output data from the register. + * @return 0 on success, negative value on error. + */ +int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf); + +/** @brief Writes data to a register on IMU. + * @param[in] s Pointer to device. + * @param[in] reg Register address to be written. + * @param[in] len Number of byte to be written. + * @param[in] buf Input data to write. + * @return 0 on success, negative value on error. + */ +int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf); + +/** @brief Enable MCLK. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_switch_on_mclk(struct inv_imu_device *s); + +/** @brief Disable MCLK. + * @param[in] s Pointer to device. + * @return 0 on success, negative value on error. + */ +int inv_imu_switch_off_mclk(struct inv_imu_device *s); + +#ifdef __cplusplus +} +#endif + +#endif /* _INV_IMU_TRANSPORT_H_ */ + +/** @} */ diff --git a/lib/ICM42670P/src/imu/inv_imu_version.h b/lib/ICM42670P/src/imu/inv_imu_version.h new file mode 100644 index 0000000..a3fe291 --- /dev/null +++ b/lib/ICM42670P/src/imu/inv_imu_version.h @@ -0,0 +1,37 @@ +/* + * ________________________________________________________________________________________________________ + * Copyright (c) 2019 InvenSense Inc. All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively “Software”) is subject + * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * ________________________________________________________________________________________________________ + */ + +#ifndef _INV_IMU_VERSION_H_ +#define _INV_IMU_VERSION_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define INV_IMU_VERSION_STRING "2.1.1" + +#ifdef __cplusplus +} +#endif + +#endif /* _INV_IMU_VERSION_H_ */ diff --git a/lib/ICM42670P/src/inv_time.c b/lib/ICM42670P/src/inv_time.c new file mode 100644 index 0000000..9f87bad --- /dev/null +++ b/lib/ICM42670P/src/inv_time.c @@ -0,0 +1,36 @@ +/* + * + * ------------------------------------------------------------------------------------------------------------ + * Copyright (c) 2022 InvenSense, Inc All rights reserved. + * + * This software, related documentation and any modifications thereto (collectively "Software") is subject + * to InvenSense, Inc and its licencors' intellectual property rights under U.S. and international copyright + * and other intellectual property rights laws. + * + * InvenSense, Inc and its licencors retain all intellectual property and proprietary rights in and to the Software + * and any use, reproduction, disclosure or distribution of the Software without an express license agreement + * from InvenSense, Inc is strictly prohibited. + * + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS + * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL + * InvenSense, Inc BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THE SOFTWARE. + * + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "Arduino.h" + +void inv_imu_sleep_us(uint32_t us) +{ + delayMicroseconds(us); +} + +uint64_t inv_imu_get_time_us(void) +{ + return (uint64_t)micros(); +} \ No newline at end of file diff --git a/lib/QMC5883L/QMC5883LCompass.cpp b/lib/QMC5883L/QMC5883LCompass.cpp new file mode 100644 index 0000000..357dc5d --- /dev/null +++ b/lib/QMC5883L/QMC5883LCompass.cpp @@ -0,0 +1,469 @@ +/* +=============================================================================================================== +QMC5883LCompass.h +Library for using QMC5583L series chip boards as a compass. +Learn more at [https://github.com/mprograms/QMC5883LCompass] + +Supports: + +- Getting values of XYZ axis. +- Calculating Azimuth. +- Getting 16 point Azimuth bearing direction (0 - 15). +- Getting 16 point Azimuth bearing Names (N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW) +- Smoothing of XYZ readings via rolling averaging and min / max removal. +- Optional chipset modes (see below) + +=============================================================================================================== + +v1.0 - June 13, 2019 +Written by MPrograms +Github: [https://github.com/mprograms/] + +Release under the GNU General Public License v3 +[https://www.gnu.org/licenses/gpl-3.0.en.html] + +=============================================================================================================== + + + +FROM QST QMC5883L Datasheet [https://nettigo.pl/attachments/440] +----------------------------------------------- + MODE CONTROL (MODE) + Standby 0x00 + Continuous 0x01 + +OUTPUT DATA RATE (ODR) + 10Hz 0x00 + 50Hz 0x04 + 100Hz 0x08 + 200Hz 0x0C + +FULL SCALE (RNG) + 2G 0x00 + 8G 0x10 + +OVER SAMPLE RATIO (OSR) + 512 0x00 + 256 0x40 + 128 0x80 + 64 0xC0 + +*/ + + + +#include "Arduino.h" +#include "QMC5883LCompass.h" +#include + +QMC5883LCompass::QMC5883LCompass() { +} + + +/** + INIT + Initialize Chip - This needs to be called in the sketch setup() function. + + @since v0.1; +**/ +void QMC5883LCompass::init(){ + Wire.begin(); + _writeReg(0x0B,0x01); + setMode(0x01,0x0C,0x10,0X00); +} + + +/** + SET ADDRESS + Set the I2C Address of the chip. This needs to be called in the sketch setup() function. + + @since v0.1; +**/ +// Set I2C Address if different then default. +void QMC5883LCompass::setADDR(byte b){ + _ADDR = b; +} + + + + +/** + REGISTER + Write the register to the chip. + + @since v0.1; +**/ +// Write register values to chip +void QMC5883LCompass::_writeReg(byte r, byte v){ + Wire.beginTransmission(_ADDR); + Wire.write(r); + Wire.write(v); + Wire.endTransmission(); +} + + +/** + CHIP MODE + Set the chip mode. + + @since v0.1; +**/ +// Set chip mode +void QMC5883LCompass::setMode(byte mode, byte odr, byte rng, byte osr){ + _writeReg(0x09,mode|odr|rng|osr); +} + + +/** + * Define the magnetic declination for accurate degrees. + * https://www.magnetic-declination.com/ + * + * @example + * For: Londrina, PR, Brazil at date 2022-12-05 + * The magnetic declination is: -19º 43' + * + * then: setMagneticDeclination(-19, 43); + */ +void QMC5883LCompass::setMagneticDeclination(int degrees, uint8_t minutes) { + _magneticDeclinationDegrees = degrees + minutes / 60; +} + + +/** + RESET + Reset the chip. + + @since v0.1; +**/ +// Reset the chip +void QMC5883LCompass::setReset(){ + _writeReg(0x0A,0x80); +} + +// 1 = Basic 2 = Advanced +void QMC5883LCompass::setSmoothing(byte steps, bool adv){ + _smoothUse = true; + _smoothSteps = ( steps > 10) ? 10 : steps; + _smoothAdvanced = (adv == true) ? true : false; +} + +void QMC5883LCompass::calibrate() { + clearCalibration(); + long calibrationData[3][2] = {{65000, -65000}, {65000, -65000}, {65000, -65000}}; + long x = calibrationData[0][0] = calibrationData[0][1] = getX(); + long y = calibrationData[1][0] = calibrationData[1][1] = getY(); + long z = calibrationData[2][0] = calibrationData[2][1] = getZ(); + + unsigned long startTime = millis(); + + while((millis() - startTime) < 10000) { + read(); + + x = getX(); + y = getY(); + z = getZ(); + + if(x < calibrationData[0][0]) { + calibrationData[0][0] = x; + } + if(x > calibrationData[0][1]) { + calibrationData[0][1] = x; + } + + if(y < calibrationData[1][0]) { + calibrationData[1][0] = y; + } + if(y > calibrationData[1][1]) { + calibrationData[1][1] = y; + } + + if(z < calibrationData[2][0]) { + calibrationData[2][0] = z; + } + if(z > calibrationData[2][1]) { + calibrationData[2][1] = z; + } + ESP.wdtFeed(); + } + + setCalibration( + calibrationData[0][0], + calibrationData[0][1], + calibrationData[1][0], + calibrationData[1][1], + calibrationData[2][0], + calibrationData[2][1] + ); +} + +/** + SET CALIBRATION + Set calibration values for more accurate readings + + @author Claus Näveke - TheNitek [https://github.com/TheNitek] + + @since v1.1.0 + + @deprecated Instead of setCalibration, use the calibration offset and scale methods. +**/ +void QMC5883LCompass::setCalibration(int x_min, int x_max, int y_min, int y_max, int z_min, int z_max){ + setCalibrationOffsets( + (x_min + x_max)/2, + (y_min + y_max)/2, + (z_min + z_max)/2 + ); + + float x_avg_delta = (x_max - x_min)/2; + float y_avg_delta = (y_max - y_min)/2; + float z_avg_delta = (z_max - z_min)/2; + + float avg_delta = (x_avg_delta + y_avg_delta + z_avg_delta) / 3; + + setCalibrationScales( + avg_delta / x_avg_delta, + avg_delta / y_avg_delta, + avg_delta / z_avg_delta + ); +} + +void QMC5883LCompass::setCalibrationOffsets(float x_offset, float y_offset, float z_offset) { + _offset[0] = x_offset; + _offset[1] = y_offset; + _offset[2] = z_offset; +} + +void QMC5883LCompass::setCalibrationScales(float x_scale, float y_scale, float z_scale) { + _scale[0] = x_scale; + _scale[1] = y_scale; + _scale[2] = z_scale; +} + +float QMC5883LCompass::getCalibrationOffset(uint8_t index) { + return _offset[index]; +} + +float QMC5883LCompass::getCalibrationScale(uint8_t index) { + return _scale[index]; +} + +void QMC5883LCompass::clearCalibration(){ + setCalibrationOffsets(0., 0., 0.); + setCalibrationScales(1., 1., 1.); +} + +/** + READ + Read the XYZ axis and save the values in an array. + + @since v0.1; +**/ +void QMC5883LCompass::read(){ + Wire.beginTransmission(_ADDR); + Wire.write(0x00); + int err = Wire.endTransmission(); + if (!err) { + Wire.requestFrom(_ADDR, (byte)6); + _vRaw[0] = (int)(int16_t)(Wire.read() | Wire.read() << 8); + _vRaw[1] = (int)(int16_t)(Wire.read() | Wire.read() << 8); + _vRaw[2] = (int)(int16_t)(Wire.read() | Wire.read() << 8); + + _applyCalibration(); + + if ( _smoothUse ) { + _smoothing(); + } + + //byte overflow = Wire.read() & 0x02; + //return overflow << 2; + } +} + +/** + APPLY CALIBRATION + This function uses the calibration data provided via @see setCalibration() to calculate more + accurate readings + + @author Claus Näveke - TheNitek [https://github.com/TheNitek] + + Based on this awesome article: + https://appelsiini.net/2018/calibrate-magnetometer/ + + @since v1.1.0 + +**/ +void QMC5883LCompass::_applyCalibration(){ + _vCalibrated[0] = (_vRaw[0] - _offset[0]) * _scale[0]; + _vCalibrated[1] = (_vRaw[1] - _offset[1]) * _scale[1]; + _vCalibrated[2] = (_vRaw[2] - _offset[2]) * _scale[2]; +} + + +/** + SMOOTH OUTPUT + This function smooths the output for the XYZ axis. Depending on the options set in + @see setSmoothing(), we can run multiple methods of smoothing the sensor readings. + + First we store (n) samples of sensor readings for each axis and store them in a rolling array. + As each new sensor reading comes in we replace it with a new reading. Then we average the total + of all (n) readings. + + Advanced Smoothing + If you turn advanced smoothing on, we will select the min and max values from our array + of (n) samples. We then subtract both the min and max from the total and average the total of all + (n - 2) readings. + + NOTE: This function does several calculations and can cause your sketch to run slower. + + @since v0.3; +**/ +void QMC5883LCompass::_smoothing(){ + byte max = 0; + byte min = 0; + + if ( _vScan > _smoothSteps - 1 ) { _vScan = 0; } + + for ( int i = 0; i < 3; i++ ) { + if ( _vTotals[i] != 0 ) { + _vTotals[i] = _vTotals[i] - _vHistory[_vScan][i]; + } + _vHistory[_vScan][i] = _vCalibrated[i]; + _vTotals[i] = _vTotals[i] + _vHistory[_vScan][i]; + + if ( _smoothAdvanced ) { + max = 0; + for (int j = 0; j < _smoothSteps - 1; j++) { + max = ( _vHistory[j][i] > _vHistory[max][i] ) ? j : max; + } + + min = 0; + for (int k = 0; k < _smoothSteps - 1; k++) { + min = ( _vHistory[k][i] < _vHistory[min][i] ) ? k : min; + } + + _vSmooth[i] = ( _vTotals[i] - (_vHistory[max][i] + _vHistory[min][i]) ) / (_smoothSteps - 2); + } else { + _vSmooth[i] = _vTotals[i] / _smoothSteps; + } + } + + _vScan++; +} + + +/** + GET X AXIS + Read the X axis + + @since v0.1; + @return int x axis +**/ +int QMC5883LCompass::getX(){ + return _get(0); +} + + +/** + GET Y AXIS + Read the Y axis + + @since v0.1; + @return int y axis +**/ +int QMC5883LCompass::getY(){ + return _get(1); +} + + +/** + GET Z AXIS + Read the Z axis + + @since v0.1; + @return int z axis +**/ +int QMC5883LCompass::getZ(){ + return _get(2); +} + +/** + GET SENSOR AXIS READING + Get the smoothed, calibration, or raw data from a given sensor axis + + @since v1.1.0 + @return int sensor axis value +**/ +int QMC5883LCompass::_get(int i){ + if ( _smoothUse ) + return _vSmooth[i]; + + return _vCalibrated[i]; +} + + + +/** + GET AZIMUTH + Calculate the azimuth (in degrees); + Correct the value with magnetic declination if defined. + + @since v0.1; + @return int azimuth +**/ +int QMC5883LCompass::getAzimuth(){ + float heading = atan2( getY(), getX() ) * 180.0 / PI; + heading += _magneticDeclinationDegrees; + return (int)heading % 360; +} + + +/** + GET BEARING + Divide the 360 degree circle into 16 equal parts and then return the a value of 0-15 + based on where the azimuth is currently pointing. + + + @since v1.2.1 - function takes into account negative azimuth values. Credit: https://github.com/prospark + @since v1.0.1 - function now requires azimuth parameter. + @since v0.2.0 - initial creation + + @return byte direction of bearing +*/ +byte QMC5883LCompass::getBearing(int azimuth){ + unsigned long a = ( azimuth > -0.5 ) ? azimuth / 22.5 : (azimuth+360)/22.5; + unsigned long r = a - (int)a; + byte sexdec = 0; + sexdec = ( r >= .5 ) ? ceil(a) : floor(a); + return sexdec; +} + + +/** + This will take the location of the azimuth as calculated in getBearing() and then + produce an array of chars as a text representation of the direction. + + NOTE: This function does not return anything since it is not possible to return an array. + Values must be passed by reference back to your sketch. + + Example: + + ( if direction is in 1 / NNE) + + char myArray[3]; + compass.getDirection(myArray, azimuth); + + Serial.print(myArray[0]); // N + Serial.print(myArray[1]); // N + Serial.print(myArray[2]); // E + + + @see getBearing(); + + @since v1.0.1 - function now requires azimuth parameter. + @since v0.2.0 - initial creation +*/ +void QMC5883LCompass::getDirection(char* myArray, int azimuth){ + int d = getBearing(azimuth); + myArray[0] = _bearings[d][0]; + myArray[1] = _bearings[d][1]; + myArray[2] = _bearings[d][2]; +} \ No newline at end of file diff --git a/lib/QMC5883L/QMC5883LCompass.h b/lib/QMC5883L/QMC5883LCompass.h new file mode 100644 index 0000000..9345a1a --- /dev/null +++ b/lib/QMC5883L/QMC5883LCompass.h @@ -0,0 +1,59 @@ +#ifndef QMC5883L_Compass +#define QMC5883L_Compass + +#include "Arduino.h" +#include "Wire.h" + +class QMC5883LCompass { + +public: + QMC5883LCompass(); + void init(); + void setADDR(byte b); + void setMode(byte mode, byte odr, byte rng, byte osr); + void setMagneticDeclination(int degrees, uint8_t minutes); + void setSmoothing(byte steps, bool adv); + void calibrate(); + void setCalibration(int x_min, int x_max, int y_min, int y_max, int z_min, + int z_max); + void setCalibrationOffsets(float x_offset, float y_offset, float z_offset); + void setCalibrationScales(float x_scale, float y_scale, float z_scale); + float getCalibrationOffset(uint8_t index); + float getCalibrationScale(uint8_t index); + void clearCalibration(); + void setReset(); + void read(); + int getX(); + int getY(); + int getZ(); + int getAzimuth(); + byte getBearing(int azimuth); + void getDirection(char *myArray, int azimuth); + +private: + void _writeReg(byte reg, byte val); + int _get(int index); + float _magneticDeclinationDegrees = 0; + bool _smoothUse = false; + byte _smoothSteps = 5; + bool _smoothAdvanced = false; + byte _ADDR = 0x0D; + int _vRaw[3] = {0, 0, 0}; + int _vHistory[10][3]; + int _vScan = 0; + long _vTotals[3] = {0, 0, 0}; + int _vSmooth[3] = {0, 0, 0}; + void _smoothing(); + float _offset[3] = {0., 0., 0.}; + float _scale[3] = {1., 1., 1.}; + int _vCalibrated[3]; + void _applyCalibration(); + const char _bearings[16][3] = { + {' ', ' ', 'N'}, {'N', 'N', 'E'}, {' ', 'N', 'E'}, {'E', 'N', 'E'}, + {' ', ' ', 'E'}, {'E', 'S', 'E'}, {' ', 'S', 'E'}, {'S', 'S', 'E'}, + {' ', ' ', 'S'}, {'S', 'S', 'W'}, {' ', 'S', 'W'}, {'W', 'S', 'W'}, + {' ', ' ', 'W'}, {'W', 'N', 'W'}, {' ', 'N', 'W'}, {'N', 'N', 'W'}, + }; +}; + +#endif \ No newline at end of file diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index 8619c25..94d2e8d 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -31,6 +31,9 @@ #include "config.h" #if defined(TARGET_VRX_BACKPACK) +#if defined(PIN_SCL) +#include "devHeadTracker.h" +#endif extern VrxBackpackConfig config; extern bool sendRTCChangesToVrx; #else @@ -67,6 +70,9 @@ static IPAddress netMsk(255, 255, 255, 0); static DNSServer dnsServer; static AsyncWebServer server(80); +#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) +static AsyncWebSocket ws("/ws"); +#endif static bool servicesStarted = false; static bool target_seen = false; @@ -134,14 +140,24 @@ static struct { {"/logo.svg", "image/svg+xml", (uint8_t *)LOGO_SVG, sizeof(LOGO_SVG)}, {"/log.js", "text/javascript", (uint8_t *)LOG_JS, sizeof(LOG_JS)}, {"/log.html", "text/html", (uint8_t *)LOG_HTML, sizeof(LOG_HTML)}, +#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) + {"/airplane.obj", "text/plain", (uint8_t *)PLANE_OBJ, sizeof(PLANE_OBJ)}, + {"/texture.gif", "image/gif", (uint8_t *)TEXTURE_GIF, sizeof(TEXTURE_GIF)}, +#endif }; +static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) +{ +} + static void WebUpdateSendContent(AsyncWebServerRequest *request) { for (size_t i=0 ; iurl().equals(files[i].url)) { AsyncWebServerResponse *response = request->beginResponse_P(200, files[i].contentType, files[i].content, files[i].size); - response->addHeader("Content-Encoding", "gzip"); + if (pgm_read_byte(files[i].content) == 0x1F) { + response->addHeader("Content-Encoding", "gzip"); + } request->send(response); return; } @@ -568,6 +584,13 @@ static void startServices() server.onNotFound(WebUpdateHandleNotFound); + #if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) + server.on("/airplane.obj", WebUpdateSendContent); + server.on("/texture.gif", WebUpdateSendContent); + ws.onEvent(onWsEvent); + server.addHandler(&ws); + #endif + server.begin(); dnsServer.start(DNS_PORT, "*", apIP); @@ -668,6 +691,21 @@ static void HandleWebUpdate() } rebootTime = millis() + 200; } + +#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) + static long lastCall = 0; + static const char IMU_JSON[] PROGMEM = R"=====({"heading":%f,"pitch":%f,"roll":%f})====="; + + if (now - lastCall > 10) { + // Send JSON over websocket + char payload[80]; + float yaw, pitch, roll; + getEuler(&yaw, &pitch, &roll); + snprintf_P(payload, sizeof(payload), IMU_JSON, yaw, pitch, roll); + ws.textAll(payload, strlen(payload)); + lastCall = now; + } +#endif } } diff --git a/python/build_html.py b/python/build_html.py index a57178e..ebc23a1 100644 --- a/python/build_html.py +++ b/python/build_html.py @@ -39,6 +39,13 @@ def build_html(mainfile, var, out, env): out.write(','.join("0x{:02x}".format(c) for c in compress(data.encode('utf-8')))) out.write('\n};\n\n') +def build_binary(mainfile, var, out, env): + with open('html/%s' % mainfile, 'rb') as file: + data = file.read() + out.write('static const char PROGMEM %s[] = {\n' % var) + out.write(','.join("0x{:02x}".format(c) for c in data)) + out.write('\n};\n\n') + def build_common(env, mainfile): fd, path = tempfile.mkstemp() try: @@ -52,6 +59,8 @@ def build_common(env, mainfile): build_html("logo.svg", "LOGO_SVG", out, env) build_html("log.html", "LOG_HTML", out, env) build_html("log.js", "LOG_JS", out, env) + build_html("airplane.obj", "PLANE_OBJ", out, env) + build_binary("texture.gif", "TEXTURE_GIF", out, env) finally: if not os.path.exists("include/WebContent.h") or not filecmp.cmp(path, "include/WebContent.h"): shutil.copyfile(path, "include/WebContent.h") diff --git a/src/Vrx_main.cpp b/src/Vrx_main.cpp index 67406de..21344be 100644 --- a/src/Vrx_main.cpp +++ b/src/Vrx_main.cpp @@ -23,6 +23,7 @@ #include "devWIFI.h" #include "devButton.h" #include "devLED.h" +#include "devHeadTracker.h" #ifdef RAPIDFIRE_BACKPACK #include "rapidfire.h" @@ -78,6 +79,9 @@ device_t *ui_devices[] = { &Button_device, #endif &WIFI_device, +#ifdef PIN_SCL + &HeadTracker_device +#endif }; #if defined(PLATFORM_ESP32) diff --git a/targets/common.ini b/targets/common.ini index 3fa89f2..becc2a3 100644 --- a/targets/common.ini +++ b/targets/common.ini @@ -10,6 +10,7 @@ lib_deps = ottowinter/ESPAsyncWebServer-esphome @ 3.0.0 esphome/AsyncTCP-esphome @ 2.0.1 # use specific version - an update to this library breaks the build bblanchon/ArduinoJson @ 6.19.4 +monitor_filters = esp8266_exception_decoder [common_env_data] build_src_filter = +<*> -<.git/> - - - - - -<*.py> -<*test*.*> @@ -18,8 +19,8 @@ build_flags = -Wall -Iinclude # ------------------------- COMMON ESP8285 DEFINITIONS ----------------- [env_common_esp8285] board = esp8285 -board_build.ldscript = eagle.flash.1m144.ld -upload_speed = 921600 +board_build.ldscript = eagle.flash.1m.ld +upload_speed = 460800 monitor_speed = 460800 board_build.f_cpu = 160000000L build_flags = @@ -28,7 +29,7 @@ build_flags = # ------------------------- COMMON ESP12E DEFINITIONS ----------------- [env_common_esp12e] board = esp12e -board_build.ldscript = eagle.flash.1m144.ld +board_build.ldscript = eagle.flash.1m.ld upload_speed = 921600 monitor_speed = 460800 board_build.f_cpu = 160000000L diff --git a/targets/debug.ini b/targets/debug.ini index 55b8a17..40791d1 100644 --- a/targets/debug.ini +++ b/targets/debug.ini @@ -3,16 +3,16 @@ # ******************************** [env:DEBUG_ESP_RX_Backpack_via_UART] -extends = env_common_esp8285, steadyview_vrx_backpack_common +extends = env_common_esp12e, steadyview_vrx_backpack_common build_flags = ${env_common_esp8285.build_flags} ${steadyview_vrx_backpack_common.build_flags} -D DEBUG_LOG - -D DEBUG_ELRS_WIFI -D PIN_LED=16 -D PIN_MOSI=12 ;Some pin (not a UART) -D PIN_CLK=0 ;Boot pad -D PIN_CS=15 ;Some other pin (not a UART) +reset_method=nodemcu [env:DEBUG_ESP_RX_Backpack_via_WIFI] extends = env:DEBUG_ESP_RX_Backpack_via_UART @@ -22,16 +22,15 @@ extends = env:DEBUG_ESP_RX_Backpack_via_UART # ******************************** [env:DEBUG_TX_Backpack_via_UART] -extends = env_common_esp8285, tx_backpack_common +extends = env_common_esp12e, tx_backpack_common build_flags = ${env_common_esp8285.build_flags} ${tx_backpack_common.build_flags} -D LOGGING_UART=Serial -D DEBUG_LOG - -D DEBUG_ELRS_WIFI - -D STM32_TX_BACKPACK -D PIN_BUTTON=0 -D PIN_LED=16 +reset_method=nodemcu [env:DEBUG_TX_Backpack_via_WIFI] extends = env:DEBUG_TX_Backpack_via_UART diff --git a/targets/rapidfire.ini b/targets/rapidfire.ini index 1607a7c..c1c7792 100644 --- a/targets/rapidfire.ini +++ b/targets/rapidfire.ini @@ -52,3 +52,13 @@ build_flags = [env:Rapidfire_ESP12F_Backpack_via_WIFI] extends = env:Rapidfire_ESP12F_Backpack_via_UART + +[env:Rapidfire_RM_HT_Backpack_via_UART] +extends = env:Rapidfire_ESP_RX_Backpack_via_UART +build_flags = + ${env:Rapidfire_ESP01F_Backpack_via_UART.build_flags} + -D PIN_SDA=2 + -D PIN_SCL=4 + +[env:Rapidfire_RM_HT_Backpack_via_WIFI] +extends = env:Rapidfire_RM_HT_Backpack_via_UART From bbc87c16d3d637878c1311539f8b414de7a1b1d5 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Mon, 5 Feb 2024 19:48:44 +1300 Subject: [PATCH 02/38] Calibration for compass --- html/elrs.css | 77 ++++++++- html/scan.js | 62 ++++++- html/vrx_index.html | 251 +++++++++++++++-------------- lib/HeadTracker/devHeadTracker.cpp | 182 +++++++++++++++------ lib/HeadTracker/devHeadTracker.h | 13 +- lib/WIFI/devWIFI.cpp | 43 ++++- lib/config/config.cpp | 8 + lib/config/config.h | 9 +- 8 files changed, 462 insertions(+), 183 deletions(-) diff --git a/html/elrs.css b/html/elrs.css index a684327..15e526e 100644 --- a/html/elrs.css +++ b/html/elrs.css @@ -289,4 +289,79 @@ body, input, select, textarea { background: #88cef7; } - /*==========================*/ \ No newline at end of file + /*==========================*/ + +.loading { + filter:blur(20px); +} +.ring { + position:absolute; + top:50%; + left:50%; + transform:translate(-50%,-50%); + width:250px; + height:250px; + background:transparent; + border:transparent; + border-radius:50%; + text-align:center; + line-height:250px; + font-family:sans-serif; + font-size:20px; + color:#4361c2; + letter-spacing:4px; + text-transform:uppercase; + text-shadow:0 0 10px #88cef7; + box-shadow:0 0 20px #4fa49a88; +} +.ring:before { + content:''; + position:absolute; + top:-3px; + left:-3px; + width:100%; + height:100%; + border:3px solid transparent; + border-top:3px solid #9dc66b; + border-right:3px solid #9dc66b; + border-radius:50%; + animation:animateC 2s linear infinite; +} +.ring span { + display:block; + position:absolute; + top:calc(50% - 2px); + left:50%; + width:50%; + height:4px; + background:transparent; + transform-origin:left; + animation:animate 2s linear infinite; +} +.ring span:before { + content:''; + position:absolute; + width:16px; + height:16px; + border-radius:50%; + background:#9dc66b; + top:-6px; + right:-8px; + box-shadow:0 0 20px #9dc66b; +} +@keyframes animateC { + 0% { + transform:rotate(0deg); + } + 100% { + transform:rotate(360deg); + } +} +@keyframes animate { + 0% { + transform:rotate(45deg); + } + 100% { + transform:rotate(405deg); + } +} \ No newline at end of file diff --git a/html/scan.js b/html/scan.js index 472b087..fe092a0 100644 --- a/html/scan.js +++ b/html/scan.js @@ -351,7 +351,7 @@ _('sethome').addEventListener('submit', callback("Set Home Network", "An error o _('connect').addEventListener('click', callback("Connect to Home Network", "An error occurred connecting to the Home network", "/connect", null)); _('access').addEventListener('click', callback("Access Point", "An error occurred starting the Access Point", "/access", null)); _('forget').addEventListener('click', callback("Forget Home Network", "An error occurred forgetting the home network", "/forget", null)); -if (_('setrtc')) _('setrtc').addEventListener('submit', callback("Set RTC Time", "An error occured setting the RTC time", "/setrtc", function() { +if (_('setrtc')) _('setrtc').addEventListener('submit', callback("Set RTC Time", "An error occurred setting the RTC time", "/setrtc", function() { return new FormData(_('setrtc')); })); @@ -516,7 +516,17 @@ function start() { // }; websock.onerror = function(evt) { console.log(evt); }; websock.onmessage = function(evt) { - Euler = JSON.parse(evt.data); + d = JSON.parse(evt.data); + if (d['done']) { + calibrationOff(); + cuteAlert({ + type: 'info', + title: "Calibration", + message: "Calibration successful", + confirmText: "OK", + }); + } + if (d['heading']) Euler = d; }; }) } @@ -561,3 +571,51 @@ function draw() { model(plane); pop(); } + +if (_('cal-compass')) _('cal-compass').addEventListener('click', calibrateCompass); +if (_('cal-gyro')) _('cal-gyro').addEventListener('click', calibrateIMU); +if (_('orient-board')) _('orient-board').addEventListener('click', orientBoard); + +function calibrateCompass() { + cuteAlert({ + type: 'info', + title: "Calibrate Compass", + message: "Rotate the board in all directions for 10 seconds until the succeeded popup appears", + confirmText: "Calibrate", + cancelText: "Cancel" + }).then((e)=>{ + websock.send('cc'); + calibrationOn(); + }); +} + +function calibrateIMU() { + cuteAlert({ + type: 'info', + title: "Calibrate IMU", + message: "Place the board flat on the table and wait until the succeeded popup appears", + confirmText: "Calibrate", + cancelText: "Cancel" + }).then((e)=>{ + websock.send('ci'); + }); +} + +function orientBoard() { + +} + +function calibrationOn() { + _('main').classList.add('loading'); + _('calibrating').style.display='block'; + mui.overlay('on', { + 'keyboard': false, + 'static': true + }, _('calibrating')); +} + +function calibrationOff() +{ + _('main').classList.remove('loading'); + mui.overlay('off'); +} diff --git a/html/vrx_index.html b/html/vrx_index.html index ad58899..be1e69d 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -10,150 +10,155 @@ -
- -

Welcome to your ExpressLRS
update page
-

-

From here you can update your VRx Backpack module with @PLATFORM@ firmware
-

- Firmware Rev. @VERSION@ -

+

+
+
+ +

Welcome to your ExpressLRS
update page
+

+

From here you can update your VRx Backpack module with @PLATFORM@ firmware
+

+ Firmware Rev. @VERSION@ +

+

-
-
-
+
+
+
- + -
-

Backpack Firmware Update

- Here you can update your backpack firmware, - be careful to upload the correct file otherwise a bad flash may occur. If this happens you will need - to reflash via USB/Serial. -
-
-
-
- - -
+
+

Backpack Firmware Update

+ Here you can update your backpack firmware, + be careful to upload the correct file otherwise a bad flash may occur. If this happens you will need + to reflash via USB/Serial.
-

- - -
+
+
+
+ + +
+
+

+ +
+
-
-
+ diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index e347f72..88ee9f2 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -6,6 +6,7 @@ #include "devHeadTracker.h" +#include "config.h" #include "logging.h" #include "ICM42670P.h" @@ -13,6 +14,7 @@ #include "Fusion.h" +static HeadTrackerState ht_state = STATE_ERROR; static ICM42670P IMU(Wire,0); static QMC5883LCompass compass; static FusionAhrs ahrs; @@ -38,11 +40,11 @@ static void initialize() int ret = IMU.begin(); if (ret != 0) { DBGLN("ICM42607C initialization failed: %d", ret); - while(1); + return; } if ((ret = IMU.enableDataInterrupt(9, irq_handler))) { DBGLN("Interrupt enable failed: %d"); - while(1); + return; } // Accel ODR = 100 Hz and Full Scale Range = 16G IMU.startAccel(100, 16); @@ -62,13 +64,17 @@ static void initialize() .recoveryTriggerPeriod = 5 * 100, /* 5 seconds */ }; FusionAhrsSetSettings(&ahrs, &settings); + ht_state = STATE_RUNNING; } static int start() { - //TODO: load compass calibration settings from config - compass.setCalibrationOffsets(0,0,0); - compass.setCalibrationScales(0,0,0); + if (ht_state == STATE_ERROR) + { + return DURATION_NEVER; + } + int (*cal)[3][2] = config.GetCompassCalibration(); + compass.setCalibration((*cal)[0][0],(*cal)[0][1],(*cal)[1][0],(*cal)[1][1],(*cal)[2][0],(*cal)[2][1]); //TODO: load gyro/accel calibration from settings //TODO: load orientation from settings return DURATION_IMMEDIATELY; @@ -116,62 +122,138 @@ static void rotate(float pn[3], const float rot[3]) { static float orientation[3] = {0.0, 0.0, 0.0}; static FusionEuler euler; static float rollHome = 0, pitchHome = 0, yawHome = 0; +static int calibrationData[3][2]; +static uint32_t cal_started; static int timeout() { static boolean running = true; static int counter = 0; - if(irq_received) { - irq_received = 0; - - inv_imu_sensor_event_t imu_event; - IMU.getDataFromRegisters(&imu_event); + if(!irq_received) + { + return DURATION_IMMEDIATELY; + } + irq_received = 0; - FusionVector a; - a.axis.x = imu_event.accel[0] * aRes; - a.axis.y = imu_event.accel[1] * aRes; - a.axis.z = imu_event.accel[2] * aRes; - rotate(a.array, orientation); + inv_imu_sensor_event_t imu_event; + IMU.getDataFromRegisters(&imu_event); - FusionVector g; - g.axis.x = imu_event.gyro[0] * gRes; - g.axis.y = imu_event.gyro[1] * gRes; - g.axis.z = imu_event.gyro[2] * gRes; - rotate(g.array, orientation); + switch(ht_state) + { + case STATE_RUNNING: + { + FusionVector a; + a.axis.x = imu_event.accel[0] * aRes; + a.axis.y = imu_event.accel[1] * aRes; + a.axis.z = imu_event.accel[2] * aRes; + rotate(a.array, orientation); + + FusionVector g; + g.axis.x = imu_event.gyro[0] * gRes; + g.axis.y = imu_event.gyro[1] * gRes; + g.axis.z = imu_event.gyro[2] * gRes; + rotate(g.array, orientation); + + compass.read(); + + FusionVector m; + m.axis.x = compass.getX(); + m.axis.y = compass.getY(); + m.axis.z = compass.getZ(); + rotate(m.array, orientation); + + // Calculate delta time (in seconds) to account for gyroscope sample clock error + const clock_t timestamp = micros(); + static clock_t previousTimestamp; + const float deltaTime = (float) (timestamp - previousTimestamp) / (float) 1000000; + previousTimestamp = timestamp; + + FusionAhrsUpdate(&ahrs, g, a, m, deltaTime); + + euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs)); + euler.angle.roll -= rollHome; + euler.angle.pitch -= pitchHome; + euler.angle.yaw -= yawHome; + } + break; + + case STATE_COMPASS_CALIBRATING: + { + if ((millis() - cal_started) < 10000) + { + compass.read(); + + int x = compass.getX(); + int y = compass.getY(); + int z = compass.getZ(); + + if(x < calibrationData[0][0]) { + calibrationData[0][0] = x; + } + if(x > calibrationData[0][1]) { + calibrationData[0][1] = x; + } + + if(y < calibrationData[1][0]) { + calibrationData[1][0] = y; + } + if(y > calibrationData[1][1]) { + calibrationData[1][1] = y; + } + + if(z < calibrationData[2][0]) { + calibrationData[2][0] = z; + } + if(z > calibrationData[2][1]) { + calibrationData[2][1] = z; + } + } + else + { + compass.setCalibration( + calibrationData[0][0], + calibrationData[0][1], + calibrationData[1][0], + calibrationData[1][1], + calibrationData[2][0], + calibrationData[2][1] + ); + config.SetCompassCalibration(calibrationData); + ht_state = STATE_RUNNING; + } + } + break; + + case STATE_IMU_CALIBRATING: + { + + } + break; + } - compass.read(); + return DURATION_IMMEDIATELY; +} - FusionVector m; - m.axis.x = compass.getX(); - m.axis.y = compass.getY(); - m.axis.z = compass.getZ(); - rotate(m.array, orientation); +void startCompassCalibration() +{ + compass.clearCalibration(); + calibrationData[0][0] = calibrationData[0][1] = compass.getX(); + calibrationData[1][0] = calibrationData[1][1] = compass.getY(); + calibrationData[2][0] = calibrationData[2][1] = compass.getZ(); - // Calculate delta time (in seconds) to account for gyroscope sample clock error - const clock_t timestamp = micros(); - static clock_t previousTimestamp; - const float deltaTime = (float) (timestamp - previousTimestamp) / (float) 1000000; - previousTimestamp = timestamp; + cal_started = millis(); + ht_state = STATE_COMPASS_CALIBRATING; +} - FusionAhrsUpdate(&ahrs, g, a, m, deltaTime); +void startIMUCalibration() +{ - euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs)); - euler.angle.roll -= rollHome; - euler.angle.pitch -= pitchHome; - euler.angle.yaw -= yawHome; - } - return DURATION_IMMEDIATELY; } -void resetCenter() +HeadTrackerState getHeadTrackerState() { - rollHome += euler.angle.roll; - pitchHome += euler.angle.pitch; - yawHome += euler.angle.yaw; - rollHome = normalize(rollHome, -180.0, 180.0); - pitchHome = normalize(pitchHome, -180.0, 180.0); - yawHome = normalize(yawHome, -180.0, 180.0); + return ht_state; } void setupBoardOrientation(OrientationPhase phase) @@ -202,6 +284,16 @@ void setupBoardOrientation(OrientationPhase phase) } } +void resetCenter() +{ + rollHome += euler.angle.roll; + pitchHome += euler.angle.pitch; + yawHome += euler.angle.yaw; + rollHome = normalize(rollHome, -180.0, 180.0); + pitchHome = normalize(pitchHome, -180.0, 180.0); + yawHome = normalize(yawHome, -180.0, 180.0); +} + void getEuler(float *yaw, float *pitch, float *roll) { *yaw = euler.angle.yaw; diff --git a/lib/HeadTracker/devHeadTracker.h b/lib/HeadTracker/devHeadTracker.h index c8f956c..f9585af 100644 --- a/lib/HeadTracker/devHeadTracker.h +++ b/lib/HeadTracker/devHeadTracker.h @@ -4,12 +4,23 @@ extern device_t HeadTracker_device; +typedef enum { + STATE_ERROR, + STATE_RUNNING, + STATE_COMPASS_CALIBRATING, + STATE_IMU_CALIBRATING +} HeadTrackerState; + typedef enum { PHASE_BEGIN, PHASE_VRX_FLAT, PHASE_BOARD_FLAT } OrientationPhase; -void resetCenter(); +void startCompassCalibration(); +void startIMUCalibration(); void setupBoardOrientation(OrientationPhase phase); +HeadTrackerState getHeadTrackerState(); + +void resetCenter(); void getEuler(float *yaw, float *pitch, float *roll); diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index 94d2e8d..01c835f 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -148,6 +148,13 @@ static struct { static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { + if (type == WS_EVT_DATA) { + if (memcmp(data, "cc", 2) == 0) { + startCompassCalibration(); + } else if (memcmp(data, "ci", 2) == 0) { + startIMUCalibration(); + } + } } static void WebUpdateSendContent(AsyncWebServerRequest *request) @@ -694,16 +701,36 @@ static void HandleWebUpdate() #if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) static long lastCall = 0; - static const char IMU_JSON[] PROGMEM = R"=====({"heading":%f,"pitch":%f,"roll":%f})====="; - + static HeadTrackerState last_state = STATE_ERROR; if (now - lastCall > 10) { - // Send JSON over websocket - char payload[80]; - float yaw, pitch, roll; - getEuler(&yaw, &pitch, &roll); - snprintf_P(payload, sizeof(payload), IMU_JSON, yaw, pitch, roll); - ws.textAll(payload, strlen(payload)); + auto current_state = getHeadTrackerState(); + switch(current_state) + { + case STATE_RUNNING: + if (last_state == STATE_IMU_CALIBRATING || last_state == STATE_COMPASS_CALIBRATING) + { + ws.textAll("{\"done\": true}"); + } + else + { + static const char IMU_JSON[] PROGMEM = R"=====({"heading":%f,"pitch":%f,"roll":%f})====="; + // Send JSON over websocket + char payload[80]; + float yaw, pitch, roll; + getEuler(&yaw, &pitch, &roll); + snprintf_P(payload, sizeof(payload), IMU_JSON, yaw, pitch, roll); + ws.textAll(payload, strlen(payload)); + } + break; + + case STATE_COMPASS_CALIBRATING: + break; + + case STATE_IMU_CALIBRATING: + break; + } lastCall = now; + last_state = current_state; } #endif } diff --git a/lib/config/config.cpp b/lib/config/config.cpp index 0fc1266..f57f9ca 100644 --- a/lib/config/config.cpp +++ b/lib/config/config.cpp @@ -141,6 +141,7 @@ VrxBackpackConfig::SetDefaults() m_config.ssid[0] = 0; m_config.password[0] = 0; memset(m_config.address, 0, 6); + memset(m_config.compassCalibration, 0, sizeof(m_config.compassCalibration)); m_modified = true; Commit(); } @@ -180,4 +181,11 @@ VrxBackpackConfig::SetStartWiFiOnBoot(bool startWifi) m_modified = true; } +void +VrxBackpackConfig::SetCompassCalibration(const int calibrationData[3][2]) +{ + memcpy(m_config.compassCalibration, calibrationData, sizeof(m_config.compassCalibration)); + m_modified = true; +} + #endif diff --git a/lib/config/config.h b/lib/config/config.h index ae8f9e1..4814f0f 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -3,11 +3,11 @@ #include "elrs_eeprom.h" // CONFIG_MAGIC is ORed with CONFIG_VERSION in the version field -#define TX_BACKPACK_CONFIG_MAGIC (0b01 << 30) -#define VRX_BACKPACK_CONFIG_MAGIC (0b10 << 30) +#define TX_BACKPACK_CONFIG_MAGIC (0b01U << 30) +#define VRX_BACKPACK_CONFIG_MAGIC (0b10U << 30) #define TX_BACKPACK_CONFIG_VERSION 3 -#define VRX_BACKPACK_CONFIG_VERSION 3 +#define VRX_BACKPACK_CONFIG_VERSION 4 #if defined(TARGET_TX_BACKPACK) typedef struct { @@ -59,6 +59,7 @@ typedef struct { char ssid[33]; char password[65]; uint8_t address[6]; + int compassCalibration[3][2]; } vrx_backpack_config_t; class VrxBackpackConfig @@ -74,6 +75,7 @@ class VrxBackpackConfig char *GetSSID() { return m_config.ssid; } char *GetPassword() { return m_config.password; } uint8_t *GetGroupAddress() { return m_config.address; } + int (*GetCompassCalibration())[3][2] { return &m_config.compassCalibration; }; // Setters void SetStorageProvider(ELRS_EEPROM *eeprom); @@ -83,6 +85,7 @@ class VrxBackpackConfig void SetSSID(const char *ssid); void SetPassword(const char *ssid); void SetGroupAddress(const uint8_t address[6]); + void SetCompassCalibration(const int calibration[3][2]); private: vrx_backpack_config_t m_config; From fbe84818ca9eef530512136ee9b30b143fb75bdd Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Tue, 6 Feb 2024 13:20:07 +1300 Subject: [PATCH 03/38] Add reset center command/button --- html/scan.js | 5 +++++ html/vrx_index.html | 1 + lib/HeadTracker/devHeadTracker.cpp | 5 +---- lib/WIFI/devWIFI.cpp | 2 ++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/html/scan.js b/html/scan.js index fe092a0..0b128d4 100644 --- a/html/scan.js +++ b/html/scan.js @@ -572,6 +572,7 @@ function draw() { pop(); } +if (_('set-center')) _('set-center').addEventListener('click', setCenter); if (_('cal-compass')) _('cal-compass').addEventListener('click', calibrateCompass); if (_('cal-gyro')) _('cal-gyro').addEventListener('click', calibrateIMU); if (_('orient-board')) _('orient-board').addEventListener('click', orientBoard); @@ -601,6 +602,10 @@ function calibrateIMU() { }); } +function setCenter() { + websock.send('sc'); +} + function orientBoard() { } diff --git a/html/vrx_index.html b/html/vrx_index.html index be1e69d..e287a5a 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -115,6 +115,7 @@

RTC Update via NTP

Head Tracking

+ Reset Center Calibrate Compass Calibrate Gyro/Accel Set Board Orientation diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index 88ee9f2..f6babec 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -21,7 +21,6 @@ static FusionAhrs ahrs; static float aRes; static float gRes; -static float mRes; static volatile uint8_t irq_received = 0; static IRAM_ATTR void irq_handler(void) { @@ -127,9 +126,6 @@ static uint32_t cal_started; static int timeout() { - static boolean running = true; - static int counter = 0; - if(!irq_received) { return DURATION_IMMEDIATELY; @@ -292,6 +288,7 @@ void resetCenter() rollHome = normalize(rollHome, -180.0, 180.0); pitchHome = normalize(pitchHome, -180.0, 180.0); yawHome = normalize(yawHome, -180.0, 180.0); + DBGLN("%f %f %f", rollHome, pitchHome, yawHome); } void getEuler(float *yaw, float *pitch, float *roll) diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index 01c835f..6de2850 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -153,6 +153,8 @@ static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, Aw startCompassCalibration(); } else if (memcmp(data, "ci", 2) == 0) { startIMUCalibration(); + } else if (memcmp(data, "sc", 2) == 0) { + resetCenter(); } } } From 2a49796a22d65588086022229c24b511096ea899 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Tue, 6 Feb 2024 16:48:23 +1300 Subject: [PATCH 04/38] Board orientation setup --- html/elrs.css | 4 ++ html/scan.js | 41 ++++++++++++++++---- html/vrx_index.html | 30 ++++++++++++--- lib/HeadTracker/devHeadTracker.cpp | 62 +++++++++++++++--------------- lib/HeadTracker/devHeadTracker.h | 10 ++--- lib/WIFI/devWIFI.cpp | 24 ++++++++++++ lib/config/config.cpp | 9 +++++ lib/config/config.h | 3 ++ 8 files changed, 132 insertions(+), 51 deletions(-) diff --git a/html/elrs.css b/html/elrs.css index 15e526e..b290165 100644 --- a/html/elrs.css +++ b/html/elrs.css @@ -21,6 +21,10 @@ body, input, select, textarea { line-height: 1.65em; } +.slider { + width: 100%; +} + /*==========================*/ /* Autocomplete */ diff --git a/html/scan.js b/html/scan.js index 0b128d4..ed668f1 100644 --- a/html/scan.js +++ b/html/scan.js @@ -526,7 +526,20 @@ function start() { confirmText: "OK", }); } - if (d['heading']) Euler = d; + if (d['orientation']) { + _('x-angle').value = d.pitch; + _('y-angle').value = d.roll; + _('z-angle').value = d.heading; + _('label-x').textContent = d.pitch; + _('label-y').textContent = d.roll; + _('label-z').textContent = d.heading; + } + if (d['heading']) { + Euler = d; + _('angle-x').textContent = Euler.pitch; + _('angle-y').textContent = Euler.roll; + _('angle-z').textContent = Euler.heading; + } }; }) } @@ -572,10 +585,14 @@ function draw() { pop(); } -if (_('set-center')) _('set-center').addEventListener('click', setCenter); +if (_('set-center')) _('set-center').addEventListener('click', () => {websock.send('sc');}); if (_('cal-compass')) _('cal-compass').addEventListener('click', calibrateCompass); if (_('cal-gyro')) _('cal-gyro').addEventListener('click', calibrateIMU); -if (_('orient-board')) _('orient-board').addEventListener('click', orientBoard); +if (_('reset-board')) _('reset-board').addEventListener('click', () => {websock.send('ro');}); +if (_('save-orientation')) _('save-orientation').addEventListener('click', saveOrientation); +if (_('x-angle')) _('x-angle').addEventListener('input', setOrientation); +if (_('y-angle')) _('y-angle').addEventListener('input', setOrientation); +if (_('z-angle')) _('z-angle').addEventListener('input', setOrientation); function calibrateCompass() { cuteAlert({ @@ -599,15 +616,25 @@ function calibrateIMU() { cancelText: "Cancel" }).then((e)=>{ websock.send('ci'); + calibrationOn(); }); } -function setCenter() { - websock.send('sc'); +function setOrientation(e) { + _('label-x').textContent = _('x-angle').value; + _('label-y').textContent = _('y-angle').value; + _('label-z').textContent = _('z-angle').value; + websock.send('o:' + _('x-angle').value + ':' + _('y-angle').value + ':' + _('z-angle').value); } -function orientBoard() { - +function saveOrientation() { + websock.send('sv'); + cuteAlert({ + type: 'info', + title: "Save Board Orientation", + message: "Board orientation has been saved to configuration", + confirmText: "OK" + }); } function calibrationOn() { diff --git a/html/vrx_index.html b/html/vrx_index.html index e287a5a..8207d8c 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -115,13 +115,33 @@

RTC Update via NTP

Head Tracking

- Reset Center - Calibrate Compass - Calibrate Gyro/Accel - Set Board Orientation + + +
- +
+ + +
+
+ +
+
+ +
+
+ +
diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index f6babec..fcb4a11 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -23,6 +23,13 @@ static float aRes; static float gRes; static volatile uint8_t irq_received = 0; +static float orientation[3] = {0.0, 0.0, 0.0}; +static FusionEuler euler; +static float rollHome = 0, pitchHome = 0, yawHome = 0; +static int calibrationData[3][2]; +static uint32_t cal_started; + + static IRAM_ATTR void irq_handler(void) { irq_received = 1; } @@ -75,7 +82,7 @@ static int start() int (*cal)[3][2] = config.GetCompassCalibration(); compass.setCalibration((*cal)[0][0],(*cal)[0][1],(*cal)[1][0],(*cal)[1][1],(*cal)[2][0],(*cal)[2][1]); //TODO: load gyro/accel calibration from settings - //TODO: load orientation from settings + memcpy(orientation, *config.GetBoardOrientation(), sizeof(orientation)); return DURATION_IMMEDIATELY; } @@ -118,12 +125,6 @@ static void rotate(float pn[3], const float rot[3]) { } } -static float orientation[3] = {0.0, 0.0, 0.0}; -static FusionEuler euler; -static float rollHome = 0, pitchHome = 0, yawHome = 0; -static int calibrationData[3][2]; -static uint32_t cal_started; - static int timeout() { if(!irq_received) @@ -252,32 +253,30 @@ HeadTrackerState getHeadTrackerState() return ht_state; } -void setupBoardOrientation(OrientationPhase phase) +void resetBoardOrientation() { - static FusionEuler vrx_flat; + orientation[0] = 0; + orientation[1] = 0; + orientation[2] = 0; + rollHome = 0; + pitchHome = 0; + yawHome = 0; + FusionAhrsReset(&ahrs); +} - switch(phase) - { - case PHASE_BEGIN: - orientation[0] = 0; - orientation[1] = 0; - orientation[2] = 0; - rollHome = 0; - pitchHome = 0; - yawHome = 0; - FusionAhrsReset(&ahrs); - break; - case PHASE_VRX_FLAT: - vrx_flat = euler; - break; - case PHASE_BOARD_FLAT: - orientation[0] = (vrx_flat.angle.roll - euler.angle.roll)*DEG_TO_RAD; - orientation[1] = (vrx_flat.angle.pitch - euler.angle.pitch)*DEG_TO_RAD; - orientation[2] = (vrx_flat.angle.yaw - euler.angle.yaw)*DEG_TO_RAD; - FusionAhrsReset(&ahrs); - // TODO: save orientation in settings - break; - } +void saveBoardOrientation() +{ + config.SetBoardOrientation(orientation); + config.Commit(); +} + +void setBoardOrientation(int xAngle, int yAngle, int zAngle) +{ + orientation[0] = yAngle * DEG_TO_RAD; + orientation[1] = xAngle * DEG_TO_RAD; + orientation[2] = zAngle * DEG_TO_RAD; + ahrs.initialising = true; + ahrs.rampedGain = 10.0f; } void resetCenter() @@ -288,7 +287,6 @@ void resetCenter() rollHome = normalize(rollHome, -180.0, 180.0); pitchHome = normalize(pitchHome, -180.0, 180.0); yawHome = normalize(yawHome, -180.0, 180.0); - DBGLN("%f %f %f", rollHome, pitchHome, yawHome); } void getEuler(float *yaw, float *pitch, float *roll) diff --git a/lib/HeadTracker/devHeadTracker.h b/lib/HeadTracker/devHeadTracker.h index f9585af..3a35345 100644 --- a/lib/HeadTracker/devHeadTracker.h +++ b/lib/HeadTracker/devHeadTracker.h @@ -11,15 +11,11 @@ typedef enum { STATE_IMU_CALIBRATING } HeadTrackerState; -typedef enum { - PHASE_BEGIN, - PHASE_VRX_FLAT, - PHASE_BOARD_FLAT -} OrientationPhase; - void startCompassCalibration(); void startIMUCalibration(); -void setupBoardOrientation(OrientationPhase phase); +void resetBoardOrientation(); +void saveBoardOrientation(); +void setBoardOrientation(int xAngle, int yAngle, int zAngle); HeadTrackerState getHeadTrackerState(); void resetCenter(); diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index 6de2850..fa5bd7f 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -148,6 +148,14 @@ static struct { static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { + if (type == WS_EVT_CONNECT) { + static const char IMU_JSON[] PROGMEM = R"=====({"orientation":true,"heading":%f,"pitch":%f,"roll":%f})====="; + // Send JSON over websocket + char payload[80]; + float (*o)[3] = config.GetBoardOrientation(); + snprintf_P(payload, sizeof(payload), IMU_JSON, (*o)[2], (*o)[1], (*o)[0]); + ws.text(client->id(), payload, strlen(payload)); + } if (type == WS_EVT_DATA) { if (memcmp(data, "cc", 2) == 0) { startCompassCalibration(); @@ -155,6 +163,22 @@ static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, Aw startIMUCalibration(); } else if (memcmp(data, "sc", 2) == 0) { resetCenter(); + } else if (memcmp(data, "ro", 2) == 0) { + resetBoardOrientation(); + } else if (memcmp(data, "sv", 2) == 0) { + saveBoardOrientation(); + } else if (memcmp(data, "o:", 2) == 0) { + char buf[64]; + memcpy(buf, data+2, len-2); + buf[len-2] = 0; + char * colon = strchr(buf, ':'); + *colon = 0; + int x = atoi(buf); + char *colon2 = strchr(colon+1, ':'); + *colon2 = 0; + int y = atoi(colon+1); + int z = atoi(colon2+1); + setBoardOrientation(x, y, z); } } } diff --git a/lib/config/config.cpp b/lib/config/config.cpp index f57f9ca..32e6aca 100644 --- a/lib/config/config.cpp +++ b/lib/config/config.cpp @@ -142,6 +142,9 @@ VrxBackpackConfig::SetDefaults() m_config.password[0] = 0; memset(m_config.address, 0, 6); memset(m_config.compassCalibration, 0, sizeof(m_config.compassCalibration)); + m_config.boardOrientation[0] = 0; + m_config.boardOrientation[1] = 0; + m_config.boardOrientation[2] = 0; m_modified = true; Commit(); } @@ -188,4 +191,10 @@ VrxBackpackConfig::SetCompassCalibration(const int calibrationData[3][2]) m_modified = true; } +void +VrxBackpackConfig::SetBoardOrientation(const float orientation[3]) +{ + memcpy(m_config.boardOrientation, orientation, sizeof(m_config.boardOrientation)); + m_modified = true; +} #endif diff --git a/lib/config/config.h b/lib/config/config.h index 4814f0f..34f5a87 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -60,6 +60,7 @@ typedef struct { char password[65]; uint8_t address[6]; int compassCalibration[3][2]; + float boardOrientation[3]; } vrx_backpack_config_t; class VrxBackpackConfig @@ -76,6 +77,7 @@ class VrxBackpackConfig char *GetPassword() { return m_config.password; } uint8_t *GetGroupAddress() { return m_config.address; } int (*GetCompassCalibration())[3][2] { return &m_config.compassCalibration; }; + float (*GetBoardOrientation())[3] { return &m_config.boardOrientation; }; // Setters void SetStorageProvider(ELRS_EEPROM *eeprom); @@ -86,6 +88,7 @@ class VrxBackpackConfig void SetPassword(const char *ssid); void SetGroupAddress(const uint8_t address[6]); void SetCompassCalibration(const int calibration[3][2]); + void SetBoardOrientation(const float orientation[3]); private: vrx_backpack_config_t m_config; From f703eb340eac3a11133995fcbc846d923ce1439b Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Fri, 9 Feb 2024 10:05:53 +1300 Subject: [PATCH 05/38] Local cut-down p5 --- html/p5.js | 18546 +++++++++++++++++++++++++++++++++++++++++ html/scan.js | 4 +- lib/WIFI/devWIFI.cpp | 17 +- python/build_html.py | 1 + 4 files changed, 18555 insertions(+), 13 deletions(-) create mode 100644 html/p5.js diff --git a/html/p5.js b/html/p5.js new file mode 100644 index 0000000..97aac34 --- /dev/null +++ b/html/p5.js @@ -0,0 +1,18546 @@ +/*! p5.js v0.5.12 August 11, 2017 */ +/* This is a manually cut-down version for ExpressLRS use, + We use this older version of p5 because it's smaller and it works for our use. + */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.p5 = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o. + * + * In these functions, hue is always in the range [0,1); all other components + * are in the range [0,1]. 'Brightness' and 'value' are used interchangeably. + */ + +var p5 = _dereq_('../core/core'); +p5.ColorConversion = {}; + +/** + * Convert an HSBA array to HSLA. + */ +p5.ColorConversion._hsbaToHSLA = function(hsba) { + var hue = hsba[0]; + var sat = hsba[1]; + var val = hsba[2]; + + // Calculate lightness. + var li = (2 - sat) * val / 2; + + // Convert saturation. + if (li !== 0) { + if (li === 1) { + sat = 0; + } else if (li < 0.5) { + sat = sat / (2 - sat); + } else { + sat = sat * val / (2 - li * 2); + } + } + + // Hue and alpha stay the same. + return [hue, sat, li, hsba[3]]; +}; + +/** + * Convert an HSBA array to RGBA. + */ +p5.ColorConversion._hsbaToRGBA = function(hsba) { + var hue = hsba[0] * 6; // We will split hue into 6 sectors. + var sat = hsba[1]; + var val = hsba[2]; + + var RGBA = []; + + if (sat === 0) { + RGBA = [val, val, val, hsba[3]]; // Return early if grayscale. + } else { + var sector = Math.floor(hue); + var tint1 = val * (1 - sat); + var tint2 = val * (1 - sat * (hue - sector)); + var tint3 = val * (1 - sat * (1 + sector - hue)); + var red, green, blue; + if (sector === 1) { // Yellow to green. + red = tint2; + green = val; + blue = tint1; + } else if (sector === 2) { // Green to cyan. + red = tint1; + green = val; + blue = tint3; + } else if (sector === 3) { // Cyan to blue. + red = tint1; + green = tint2; + blue = val; + } else if (sector === 4) { // Blue to magenta. + red = tint3; + green = tint1; + blue = val; + } else if (sector === 5) { // Magenta to red. + red = val; + green = tint1; + blue = tint2; + } else { // Red to yellow (sector could be 0 or 6). + red = val; + green = tint3; + blue = tint1; + } + RGBA = [red, green, blue, hsba[3]]; + } + + return RGBA; +}; + +/** + * Convert an HSLA array to HSBA. + */ +p5.ColorConversion._hslaToHSBA = function(hsla) { + var hue = hsla[0]; + var sat = hsla[1]; + var li = hsla[2]; + + // Calculate brightness. + var val; + if (li < 0.5) { + val = (1 + sat) * li; + } else { + val = li + sat - li * sat; + } + + // Convert saturation. + sat = 2 * (val - li) / val; + + // Hue and alpha stay the same. + return [hue, sat, val, hsla[3]]; +}; + +/** + * Convert an HSLA array to RGBA. + * + * We need to change basis from HSLA to something that can be more easily be + * projected onto RGBA. We will choose hue and brightness as our first two + * components, and pick a convenient third one ('zest') so that we don't need + * to calculate formal HSBA saturation. + */ +p5.ColorConversion._hslaToRGBA = function(hsla){ + var hue = hsla[0] * 6; // We will split hue into 6 sectors. + var sat = hsla[1]; + var li = hsla[2]; + + var RGBA = []; + + if (sat === 0) { + RGBA = [li, li, li, hsla[3]]; // Return early if grayscale. + } else { + + // Calculate brightness. + var val; + if (li < 0.5) { + val = (1 + sat) * li; + } else { + val = li + sat - li * sat; + } + + // Define zest. + var zest = 2 * li - val; + + // Implement projection (project onto green by default). + var hzvToRGB = function(hue, zest, val) { + if (hue < 0) { // Hue must wrap to allow projection onto red and blue. + hue += 6; + } else if (hue >= 6) { + hue -= 6; + } + if (hue < 1) { // Red to yellow (increasing green). + return (zest + (val - zest) * hue); + } else if (hue < 3) { // Yellow to cyan (greatest green). + return val; + } else if (hue < 4) { // Cyan to blue (decreasing green). + return (zest + (val - zest) * (4 - hue)); + } else { // Blue to red (least green). + return zest; + } + }; + + // Perform projections, offsetting hue as necessary. + RGBA = [hzvToRGB(hue + 2, zest, val), + hzvToRGB(hue , zest, val), + hzvToRGB(hue - 2, zest, val), + hsla[3]]; + } + + return RGBA; +}; + +/** + * Convert an RGBA array to HSBA. + */ +p5.ColorConversion._rgbaToHSBA = function(rgba) { + var red = rgba[0]; + var green = rgba[1]; + var blue = rgba[2]; + + var val = Math.max(red, green, blue); + var chroma = val - Math.min(red, green, blue); + + var hue, sat; + if (chroma === 0) { // Return early if grayscale. + hue = 0; + sat = 0; + } + else { + sat = chroma / val; + if (red === val) { // Magenta to yellow. + hue = (green - blue) / chroma; + } else if (green === val) { // Yellow to cyan. + hue = 2 + (blue - red) / chroma; + } else if (blue === val) { // Cyan to magenta. + hue = 4 + (red - green) / chroma; + } + if (hue < 0) { // Confine hue to the interval [0, 1). + hue += 6; + } else if (hue >= 6) { + hue -= 6; + } + } + + return [hue / 6, sat, val, rgba[3]]; +}; + +/** + * Convert an RGBA array to HSLA. + */ +p5.ColorConversion._rgbaToHSLA = function(rgba) { + var red = rgba[0]; + var green = rgba[1]; + var blue = rgba[2]; + + var val = Math.max(red, green, blue); + var min = Math.min(red, green, blue); + var li = val + min; // We will halve this later. + var chroma = val - min; + + var hue, sat; + if (chroma === 0) { // Return early if grayscale. + hue = 0; + sat = 0; + } else { + if (li < 1) { + sat = chroma / li; + } else { + sat = chroma / (2 - li); + } + if (red === val) { // Magenta to yellow. + hue = (green - blue) / chroma; + } else if (green === val) { // Yellow to cyan. + hue = 2 + (blue - red) / chroma; + } else if (blue === val) { // Cyan to magenta. + hue = 4 + (red - green) / chroma; + } + if (hue < 0) { // Confine hue to the interval [0, 1). + hue += 6; + } else if (hue >= 6) { + hue -= 6; + } + } + + return [hue / 6, sat, li / 2, rgba[3]]; +}; + +module.exports = p5.ColorConversion; + +},{"../core/core":54}],47:[function(_dereq_,module,exports){ +/** + * @module Color + * @submodule Creating & Reading + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); +_dereq_('./p5.Color'); + +/** + * Extracts the alpha value from a color or pixel array. + * + * @method alpha + * @param {p5.Color|Number[]} obj p5.Color object or pixel array + * @return {Number} the alpha value + * @example + *
+ * + * noStroke(); + * c = color(0, 126, 255, 102); + * fill(c); + * rect(15, 15, 35, 70); + * value = alpha(c); // Sets 'value' to 102 + * fill(value); + * rect(50, 15, 35, 70); + * + *
+ * + * @alt + * Left half of canvas light blue and right half light charcoal grey. + * Left half of canvas light purple and right half a royal blue. + * Left half of canvas salmon pink and the right half white. + * Yellow rect in middle right of canvas, with 55 pixel width and height. + * Yellow ellipse in top left canvas, black ellipse in bottom right,both 80x80. + * Bright fuschia rect in middle of canvas, 60 pixel width and height. + * Two bright green rects on opposite sides of the canvas, both 45x80. + * Four blue rects in each corner of the canvas, each are 35x35. + * Bright sea green rect on left and darker rect on right of canvas, both 45x80. + * Dark green rect on left and light green rect on right of canvas, both 45x80. + * Dark blue rect on left and light teal rect on right of canvas, both 45x80. + * blue rect on left and green on right, both with black outlines & 35x60. + * salmon pink rect on left and black on right, both 35x60. + * 4 rects, tan, brown, brownish purple and purple, with white outlines & 20x60. + * light pastel green rect on left and dark grey rect on right, both 35x60. + * yellow rect on left and red rect on right, both with black outlines & 35x60. + * grey canvas + * deep pink rect on left and grey rect on right, both 35x60. + */ +p5.prototype.alpha = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getAlpha(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the blue value from a color or pixel array. + * + * @method blue + * @param {p5.Color|Number[]} obj p5.Color object or pixel array + * @return {Number} the blue value + * @example + *
+ * + * c = color(175, 100, 220); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * rect(15, 20, 35, 60); // Draw left rectangle + * + * blueValue = blue(c); // Get blue in 'c' + * print(blueValue); // Prints "220.0" + * fill(0, 0, blueValue); // Use 'blueValue' in new fill + * rect(50, 20, 35, 60); // Draw right rectangle + * + *
+ * + * @alt + * Left half of canvas light purple and right half a royal blue. + * + */ +p5.prototype.blue = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getBlue(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the HSB brightness value from a color or pixel array. + * + * @method brightness + * @param {p5.Color|Number[]} color p5.Color object or pixel array + * @return {Number} the brightness value + * @example + *
+ * + * noStroke(); + * colorMode(HSB, 255); + * c = color(0, 126, 255); + * fill(c); + * rect(15, 20, 35, 60); + * value = brightness(c); // Sets 'value' to 255 + * fill(value); + * rect(50, 20, 35, 60); + * + *
+ * + * @alt + * Left half of canvas salmon pink and the right half white. + * + */ +p5.prototype.brightness = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getBrightness(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Creates colors for storing in variables of the color datatype. The + * parameters are interpreted as RGB or HSB values depending on the + * current colorMode(). The default mode is RGB values from 0 to 255 + * and, therefore, the function call color(255, 204, 0) will return a + * bright yellow color. + *

+ * Note that if only one value is provided to color(), it will be interpreted + * as a grayscale value. Add a second value, and it will be used for alpha + * transparency. When three values are specified, they are interpreted as + * either RGB or HSB values. Adding a fourth value applies alpha + * transparency. + *

+ * If a single string argument is provided, RGB, RGBA and Hex CSS color + * strings and all named color strings are supported. In this case, an alpha + * number value as a second argument is not supported, the RGBA form should be + * used. + * + * @method color + * @param {Number} gray number specifying value between white + * and black. + * @param {Number} [alpha] alpha value relative to current color range + * (default is 0-255) + * @return {p5.Color} resulting color + * + * @example + *
+ * + * var c = color(255, 204, 0); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * noStroke(); // Don't draw a stroke around shapes + * rect(30, 20, 55, 55); // Draw rectangle + * + *
+ * + *
+ * + * var c = color(255, 204, 0); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * noStroke(); // Don't draw a stroke around shapes + * ellipse(25, 25, 80, 80); // Draw left circle + * + * // Using only one value with color() + * // generates a grayscale value. + * var c = color(65); // Update 'c' with grayscale value + * fill(c); // Use updated 'c' as fill color + * ellipse(75, 75, 80, 80); // Draw right circle + * + *
+ * + *
+ * + * // Named SVG & CSS colors may be used, + * var c = color('magenta'); + * fill(c); // Use 'c' as fill color + * noStroke(); // Don't draw a stroke around shapes + * rect(20, 20, 60, 60); // Draw rectangle + * + *
+ * + *
+ * + * // as can hex color codes: + * noStroke(); // Don't draw a stroke around shapes + * var c = color('#0f0'); + * fill(c); // Use 'c' as fill color + * rect(0, 10, 45, 80); // Draw rectangle + * + * c = color('#00ff00'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 45, 80); // Draw rectangle + * + *
+ * + *
+ * + * // RGB and RGBA color strings are also supported: + * // these all set to the same color (solid blue) + * var c; + * noStroke(); // Don't draw a stroke around shapes + * c = color('rgb(0,0,255)'); + * fill(c); // Use 'c' as fill color + * rect(10, 10, 35, 35); // Draw rectangle + * + * c = color('rgb(0%, 0%, 100%)'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 35, 35); // Draw rectangle + * + * c = color('rgba(0, 0, 255, 1)'); + * fill(c); // Use updated 'c' as fill color + * rect(10, 55, 35, 35); // Draw rectangle + * + * c = color('rgba(0%, 0%, 100%, 1)'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 55, 35, 35); // Draw rectangle + * + *
+ * + *
+ * + * // HSL color is also supported and can be specified + * // by value + * var c; + * noStroke(); // Don't draw a stroke around shapes + * c = color('hsl(160, 100%, 50%)'); + * fill(c); // Use 'c' as fill color + * rect(0, 10, 45, 80); // Draw rectangle + * + * c = color('hsla(160, 100%, 50%, 0.5)'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 45, 80); // Draw rectangle + * + *
+ * + *
+ * + * // HSB color is also supported and can be specified + * // by value + * var c; + * noStroke(); // Don't draw a stroke around shapes + * c = color('hsb(160, 100%, 50%)'); + * fill(c); // Use 'c' as fill color + * rect(0, 10, 45, 80); // Draw rectangle + * + * c = color('hsba(160, 100%, 50%, 0.5)'); + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 45, 80); // Draw rectangle + * + *
+ * + *
+ * + * var c; // Declare color 'c' + * noStroke(); // Don't draw a stroke around shapes + * + * // If no colorMode is specified, then the + * // default of RGB with scale of 0-255 is used. + * c = color(50, 55, 100); // Create a color for 'c' + * fill(c); // Use color variable 'c' as fill color + * rect(0, 10, 45, 80); // Draw left rect + * + * colorMode(HSB, 100); // Use HSB with scale of 0-100 + * c = color(50, 55, 100); // Update 'c' with new color + * fill(c); // Use updated 'c' as fill color + * rect(55, 10, 45, 80); // Draw right rect + * + *
+ * + * @alt + * Yellow rect in middle right of canvas, with 55 pixel width and height. + * Yellow ellipse in top left of canvas, black ellipse in bottom right,both 80x80. + * Bright fuschia rect in middle of canvas, 60 pixel width and height. + * Two bright green rects on opposite sides of the canvas, both 45x80. + * Four blue rects in each corner of the canvas, each are 35x35. + * Bright sea green rect on left and darker rect on right of canvas, both 45x80. + * Dark green rect on left and lighter green rect on right of canvas, both 45x80. + * Dark blue rect on left and light teal rect on right of canvas, both 45x80. + * + */ + +/** + * @method color + * @param {Number} v1 red or hue value relative to + * the current color range + * @param {Number} v2 green or saturation value + * relative to the current color range + * @param {Number} v3 blue or brightness value + * relative to the current color range + * @param {Number} [alpha] + * @return {p5.Color} + */ + +/** + * @method color + * @param {String} value a color string + * @param {Number} [alpha] + * @return {p5.Color} + */ + +/** + * @method color + * @param {Number[]} values an array containing the red,green,blue & + * and alpha components of the color + * @return {p5.Color} + */ + +p5.prototype.color = function() { + if (arguments[0] instanceof p5.Color) { + return arguments[0]; // Do nothing if argument is already a color object. + } else if (arguments[0] instanceof Array) { + if (this instanceof p5.Renderer) { + return new p5.Color(this, arguments[0]); + } else { + return new p5.Color(this._renderer, arguments[0]); + } + } else { + if (this instanceof p5.Renderer) { + return new p5.Color(this, arguments); + } else { + return new p5.Color(this._renderer, arguments); + } + } +}; + +/** + * Extracts the green value from a color or pixel array. + * + * @method green + * @param {p5.Color|Number[]} color p5.Color object or pixel array + * @return {Number} the green value + * @example + *
+ * + * c = color(20, 75, 200); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * rect(15, 20, 35, 60); // Draw left rectangle + * + * greenValue = green(c); // Get green in 'c' + * print(greenValue); // Print "75.0" + * fill(0, greenValue, 0); // Use 'greenValue' in new fill + * rect(50, 20, 35, 60); // Draw right rectangle + * + *
+ * + * @alt + * blue rect on left and green on right, both with black outlines & 35x60. + * + */ + +p5.prototype.green = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getGreen(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the hue value from a color or pixel array. + * + * Hue exists in both HSB and HSL. This function will return the + * HSB-normalized hue when supplied with an HSB color object (or when supplied + * with a pixel array while the color mode is HSB), but will default to the + * HSL-normalized hue otherwise. (The values will only be different if the + * maximum hue setting for each system is different.) + * + * @method hue + * @param {p5.Color|Number[]} color p5.Color object or pixel array + * @return {Number} the hue + * @example + *
+ * + * noStroke(); + * colorMode(HSB, 255); + * c = color(0, 126, 255); + * fill(c); + * rect(15, 20, 35, 60); + * value = hue(c); // Sets 'value' to "0" + * fill(value); + * rect(50, 20, 35, 60); + * + *
+ * + * @alt + * salmon pink rect on left and black on right, both 35x60. + * + */ + +p5.prototype.hue = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getHue(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Blends two colors to find a third color somewhere between them. The amt + * parameter is the amount to interpolate between the two values where 0.0 + * equal to the first color, 0.1 is very near the first color, 0.5 is halfway + * in between, etc. An amount below 0 will be treated as 0. Likewise, amounts + * above 1 will be capped at 1. This is different from the behavior of lerp(), + * but necessary because otherwise numbers outside the range will produce + * strange and unexpected colors. + *

+ * The way that colours are interpolated depends on the current color mode. + * + * @method lerpColor + * @param {p5.Color} c1 interpolate from this color + * @param {p5.Color} c2 interpolate to this color + * @param {Number} amt number between 0 and 1 + * @return {p5.Color} interpolated color + * @example + *
+ * + * colorMode(RGB); + * stroke(255); + * background(51); + * from = color(218, 165, 32); + * to = color(72, 61, 139); + * colorMode(RGB); // Try changing to HSB. + * interA = lerpColor(from, to, .33); + * interB = lerpColor(from, to, .66); + * fill(from); + * rect(10, 20, 20, 60); + * fill(interA); + * rect(30, 20, 20, 60); + * fill(interB); + * rect(50, 20, 20, 60); + * fill(to); + * rect(70, 20, 20, 60); + * + *
+ * + * @alt + * 4 rects one tan, brown, brownish purple, purple, with white outlines & 20x60 + * + */ + +p5.prototype.lerpColor = function(c1, c2, amt) { + var mode = this._renderer._colorMode; + var maxes = this._renderer._colorMaxes; + var l0, l1, l2, l3; + var fromArray, toArray; + + if (mode === constants.RGB) { + fromArray = c1.levels.map(function(level) { + return level / 255; + }); + toArray = c2.levels.map(function(level) { + return level / 255; + }); + } else if (mode === constants.HSB) { + c1._getBrightness(); // Cache hsba so it definitely exists. + c2._getBrightness(); + fromArray = c1.hsba; + toArray = c2.hsba; + } else if (mode === constants.HSL) { + c1._getLightness(); // Cache hsla so it definitely exists. + c2._getLightness(); + fromArray = c1.hsla; + toArray = c2.hsla; + } else { + throw new Error (mode + 'cannot be used for interpolation.'); + } + + // Prevent extrapolation. + amt = Math.max(Math.min(amt, 1), 0); + + // Define lerp here itself if user isn't using math module. + // Maintains the definition as found in math/calculation.js + if(typeof this.lerp === 'undefined') { + this.lerp = function (start, stop, amt) { + return amt*(stop-start)+start; + }; + } + + // Perform interpolation. + l0 = this.lerp(fromArray[0], toArray[0], amt); + l1 = this.lerp(fromArray[1], toArray[1], amt); + l2 = this.lerp(fromArray[2], toArray[2], amt); + l3 = this.lerp(fromArray[3], toArray[3], amt); + + // Scale components. + l0 *= maxes[mode][0]; + l1 *= maxes[mode][1]; + l2 *= maxes[mode][2]; + l3 *= maxes[mode][3]; + + return this.color(l0, l1, l2, l3); +}; + +/** + * Extracts the HSL lightness value from a color or pixel array. + * + * @method lightness + * @param {p5.Color|Number[]} color p5.Color object or pixel array + * @return {Number} the lightness + * @example + *
+ * + * noStroke(); + * colorMode(HSL); + * c = color(156, 100, 50, 1); + * fill(c); + * rect(15, 20, 35, 60); + * value = lightness(c); // Sets 'value' to 50 + * fill(value); + * rect(50, 20, 35, 60); + * + *
+ * + * @alt + * light pastel green rect on left and dark grey rect on right, both 35x60. + * + */ +p5.prototype.lightness = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getLightness(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the red value from a color or pixel array. + * + * @method red + * @param {p5.Color|Number[]} obj p5.Color object or pixel array + * @return {Number} the red value + * @example + *
+ * + * c = color(255, 204, 0); // Define color 'c' + * fill(c); // Use color variable 'c' as fill color + * rect(15, 20, 35, 60); // Draw left rectangle + * + * redValue = red(c); // Get red in 'c' + * print(redValue); // Print "255.0" + * fill(redValue, 0, 0); // Use 'redValue' in new fill + * rect(50, 20, 35, 60); // Draw right rectangle + * + *
+ * + *
+ * + * colorMode(RGB, 255); + * var c = color(127, 255, 0); + * colorMode(RGB, 1); + * var myColor = red(c); + * print(myColor); + * + *
+ * + * @alt + * yellow rect on left and red rect on right, both with black outlines and 35x60. + * grey canvas + */ +p5.prototype.red = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getRed(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +/** + * Extracts the saturation value from a color or pixel array. + * + * Saturation is scaled differently in HSB and HSL. This function will return + * the HSB saturation when supplied with an HSB color object (or when supplied + * with a pixel array while the color mode is HSB), but will default to the + * HSL saturation otherwise. + * + * @method saturation + * @param {p5.Color|Number[]} color p5.Color object or pixel array + * @return {Number} the saturation + * @example + *
+ * + * noStroke(); + * colorMode(HSB, 255); + * c = color(0, 126, 255); + * fill(c); + * rect(15, 20, 35, 60); + * value = saturation(c); // Sets 'value' to 126 + * fill(value); + * rect(50, 20, 35, 60); + * + *
+ * + * @alt + *deep pink rect on left and grey rect on right, both 35x60. + * + */ + +p5.prototype.saturation = function(c) { + if (c instanceof p5.Color || c instanceof Array) { + return this.color(c)._getSaturation(); + } else { + throw new Error('Needs p5.Color or pixel array as argument.'); + } +}; + +module.exports = p5; + +},{"../core/constants":53,"../core/core":54,"./p5.Color":48}],48:[function(_dereq_,module,exports){ +/** + * @module Color + * @submodule Creating & Reading + * @for p5 + * @requires core + * @requires constants + * @requires color_conversion + */ + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); +var color_conversion = _dereq_('./color_conversion'); + +/** + * We define colors to be immutable objects. Each color stores the color mode + * and level maxes that applied at the time of its construction. These are + * used to interpret the input arguments and to format the output e.g. when + * saturation() is requested. + * + * Internally we store an array representing the ideal RGBA values in floating + * point form, normalized from 0 to 1. From this we calculate the closest + * screen color (RGBA levels from 0 to 255) and expose this to the renderer. + * + * We also cache normalized, floating point components of the color in various + * representations as they are calculated. This is done to prevent repeating a + * conversion that has already been performed. + * + * @class p5.Color + * @constructor + */ +p5.Color = function(renderer, vals) { + + // Record color mode and maxes at time of construction. + this.mode = renderer._colorMode; + this.maxes = renderer._colorMaxes; + + // Calculate normalized RGBA values. + if (this.mode !== constants.RGB && + this.mode !== constants.HSL && + this.mode !== constants.HSB) { + throw new Error(this.mode + ' is an invalid colorMode.'); + } else { + this._array = p5.Color._parseInputs.apply(renderer, vals); + } + + // Expose closest screen color. + this.levels = this._array.map(function(level) { + return Math.round(level * 255); + }); + + return this; +}; + +p5.Color.prototype.toString = function() { + var a = this.levels; + var alpha = this._array[3]; // String representation uses normalized alpha. + return 'rgba('+a[0]+','+a[1]+','+a[2]+','+ alpha +')'; +}; + +p5.Color.prototype._getAlpha = function() { + return this._array[3] * this.maxes[this.mode][3]; +}; + +p5.Color.prototype._getBlue = function() { + return this._array[2] * this.maxes[constants.RGB][2]; +}; + +p5.Color.prototype._getBrightness = function() { + if (!this.hsba) { + this.hsba = color_conversion._rgbaToHSBA(this._array); + } + return this.hsba[2] * this.maxes[constants.HSB][2]; +}; + +p5.Color.prototype._getGreen = function() { + return this._array[1] * this.maxes[constants.RGB][1]; +}; + +/** + * Hue is the same in HSB and HSL, but the maximum value may be different. + * This function will return the HSB-normalized saturation when supplied with + * an HSB color object, but will default to the HSL-normalized saturation + * otherwise. + */ +p5.Color.prototype._getHue = function() { + if (this.mode === constants.HSB) { + if (!this.hsba) { + this.hsba = color_conversion._rgbaToHSBA(this._array); + } + return this.hsba[0] * this.maxes[constants.HSB][0]; + } else { + if (!this.hsla) { + this.hsla = color_conversion._rgbaToHSLA(this._array); + } + return this.hsla[0] * this.maxes[constants.HSL][0]; + } +}; + +p5.Color.prototype._getLightness = function() { + if (!this.hsla) { + this.hsla = color_conversion._rgbaToHSLA(this._array); + } + return this.hsla[2] * this.maxes[constants.HSL][2]; +}; + +p5.Color.prototype._getRed = function() { + return this._array[0] * this.maxes[constants.RGB][0]; +}; + +/** + * Saturation is scaled differently in HSB and HSL. This function will return + * the HSB saturation when supplied with an HSB color object, but will default + * to the HSL saturation otherwise. + */ +p5.Color.prototype._getSaturation = function() { + if (this.mode === constants.HSB) { + if (!this.hsba) { + this.hsba = color_conversion._rgbaToHSBA(this._array); + } + return this.hsba[1] * this.maxes[constants.HSB][1]; + } else { + if (!this.hsla) { + this.hsla = color_conversion._rgbaToHSLA(this._array); + } + return this.hsla[1] * this.maxes[constants.HSL][1]; + } +}; + +/** + * CSS named colors. + */ +var namedColors = { + aliceblue: '#f0f8ff', + antiquewhite: '#faebd7', + aqua: '#00ffff', + aquamarine: '#7fffd4', + azure: '#f0ffff', + beige: '#f5f5dc', + bisque: '#ffe4c4', + black: '#000000', + blanchedalmond: '#ffebcd', + blue: '#0000ff', + blueviolet: '#8a2be2', + brown: '#a52a2a', + burlywood: '#deb887', + cadetblue: '#5f9ea0', + chartreuse: '#7fff00', + chocolate: '#d2691e', + coral: '#ff7f50', + cornflowerblue: '#6495ed', + cornsilk: '#fff8dc', + crimson: '#dc143c', + cyan: '#00ffff', + darkblue: '#00008b', + darkcyan: '#008b8b', + darkgoldenrod: '#b8860b', + darkgray: '#a9a9a9', + darkgreen: '#006400', + darkgrey: '#a9a9a9', + darkkhaki: '#bdb76b', + darkmagenta: '#8b008b', + darkolivegreen: '#556b2f', + darkorange: '#ff8c00', + darkorchid: '#9932cc', + darkred: '#8b0000', + darksalmon: '#e9967a', + darkseagreen: '#8fbc8f', + darkslateblue: '#483d8b', + darkslategray: '#2f4f4f', + darkslategrey: '#2f4f4f', + darkturquoise: '#00ced1', + darkviolet: '#9400d3', + deeppink: '#ff1493', + deepskyblue: '#00bfff', + dimgray: '#696969', + dimgrey: '#696969', + dodgerblue: '#1e90ff', + firebrick: '#b22222', + floralwhite: '#fffaf0', + forestgreen: '#228b22', + fuchsia: '#ff00ff', + gainsboro: '#dcdcdc', + ghostwhite: '#f8f8ff', + gold: '#ffd700', + goldenrod: '#daa520', + gray: '#808080', + green: '#008000', + greenyellow: '#adff2f', + grey: '#808080', + honeydew: '#f0fff0', + hotpink: '#ff69b4', + indianred: '#cd5c5c', + indigo: '#4b0082', + ivory: '#fffff0', + khaki: '#f0e68c', + lavender: '#e6e6fa', + lavenderblush: '#fff0f5', + lawngreen: '#7cfc00', + lemonchiffon: '#fffacd', + lightblue: '#add8e6', + lightcoral: '#f08080', + lightcyan: '#e0ffff', + lightgoldenrodyellow: '#fafad2', + lightgray: '#d3d3d3', + lightgreen: '#90ee90', + lightgrey: '#d3d3d3', + lightpink: '#ffb6c1', + lightsalmon: '#ffa07a', + lightseagreen: '#20b2aa', + lightskyblue: '#87cefa', + lightslategray: '#778899', + lightslategrey: '#778899', + lightsteelblue: '#b0c4de', + lightyellow: '#ffffe0', + lime: '#00ff00', + limegreen: '#32cd32', + linen: '#faf0e6', + magenta: '#ff00ff', + maroon: '#800000', + mediumaquamarine: '#66cdaa', + mediumblue: '#0000cd', + mediumorchid: '#ba55d3', + mediumpurple: '#9370db', + mediumseagreen: '#3cb371', + mediumslateblue: '#7b68ee', + mediumspringgreen: '#00fa9a', + mediumturquoise: '#48d1cc', + mediumvioletred: '#c71585', + midnightblue: '#191970', + mintcream: '#f5fffa', + mistyrose: '#ffe4e1', + moccasin: '#ffe4b5', + navajowhite: '#ffdead', + navy: '#000080', + oldlace: '#fdf5e6', + olive: '#808000', + olivedrab: '#6b8e23', + orange: '#ffa500', + orangered: '#ff4500', + orchid: '#da70d6', + palegoldenrod: '#eee8aa', + palegreen: '#98fb98', + paleturquoise: '#afeeee', + palevioletred: '#db7093', + papayawhip: '#ffefd5', + peachpuff: '#ffdab9', + peru: '#cd853f', + pink: '#ffc0cb', + plum: '#dda0dd', + powderblue: '#b0e0e6', + purple: '#800080', + red: '#ff0000', + rosybrown: '#bc8f8f', + royalblue: '#4169e1', + saddlebrown: '#8b4513', + salmon: '#fa8072', + sandybrown: '#f4a460', + seagreen: '#2e8b57', + seashell: '#fff5ee', + sienna: '#a0522d', + silver: '#c0c0c0', + skyblue: '#87ceeb', + slateblue: '#6a5acd', + slategray: '#708090', + slategrey: '#708090', + snow: '#fffafa', + springgreen: '#00ff7f', + steelblue: '#4682b4', + tan: '#d2b48c', + teal: '#008080', + thistle: '#d8bfd8', + tomato: '#ff6347', + turquoise: '#40e0d0', + violet: '#ee82ee', + wheat: '#f5deb3', + white: '#ffffff', + whitesmoke: '#f5f5f5', + yellow: '#ffff00', + yellowgreen: '#9acd32' +}; + +/** + * These regular expressions are used to build up the patterns for matching + * viable CSS color strings: fragmenting the regexes in this way increases the + * legibility and comprehensibility of the code. + * + * Note that RGB values of .9 are not parsed by IE, but are supported here for + * color string consistency. + */ +var WHITESPACE = /\s*/; // Match zero or more whitespace characters. +var INTEGER = /(\d{1,3})/; // Match integers: 79, 255, etc. +var DECIMAL = /((?:\d+(?:\.\d+)?)|(?:\.\d+))/; // Match 129.6, 79, .9, etc. +var PERCENT = new RegExp(DECIMAL.source + '%'); // Match 12.9%, 79%, .9%, etc. + +/** + * Full color string patterns. The capture groups are necessary. + */ +var colorPatterns = { + // Match colors in format #XXX, e.g. #416. + HEX3: /^#([a-f0-9])([a-f0-9])([a-f0-9])$/i, + + // Match colors in format #XXXX, e.g. #5123. + HEX4: /^#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])$/i, + + // Match colors in format #XXXXXX, e.g. #b4d455. + HEX6: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i, + + // Match colors in format #XXXXXXXX, e.g. #b4d45535. + HEX8: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i, + + // Match colors in format rgb(R, G, B), e.g. rgb(255, 0, 128). + RGB: new RegExp([ + '^rgb\\(', + INTEGER.source, + ',', + INTEGER.source, + ',', + INTEGER.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format rgb(R%, G%, B%), e.g. rgb(100%, 0%, 28.9%). + RGB_PERCENT: new RegExp([ + '^rgb\\(', + PERCENT.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format rgb(R, G, B, A), e.g. rgb(255, 0, 128, 0.25). + RGBA: new RegExp([ + '^rgba\\(', + INTEGER.source, + ',', + INTEGER.source, + ',', + INTEGER.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format rgb(R%, G%, B%, A), e.g. rgb(100%, 0%, 28.9%, 0.5). + RGBA_PERCENT: new RegExp([ + '^rgba\\(', + PERCENT.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format hsla(H, S%, L%), e.g. hsl(100, 40%, 28.9%). + HSL: new RegExp([ + '^hsl\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format hsla(H, S%, L%, A), e.g. hsla(100, 40%, 28.9%, 0.5). + HSLA: new RegExp([ + '^hsla\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format hsb(H, S%, B%), e.g. hsb(100, 40%, 28.9%). + HSB: new RegExp([ + '^hsb\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + '\\)$' + ].join(WHITESPACE.source), 'i'), + + // Match colors in format hsba(H, S%, B%, A), e.g. hsba(100, 40%, 28.9%, 0.5). + HSBA: new RegExp([ + '^hsba\\(', + INTEGER.source, + ',', + PERCENT.source, + ',', + PERCENT.source, + ',', + DECIMAL.source, + '\\)$' + ].join(WHITESPACE.source), 'i') +}; + +/** + * For a number of different inputs, returns a color formatted as [r, g, b, a] + * arrays, with each component normalized between 0 and 1. + * + * @param {Array} [...args] An 'array-like' object that represents a list of + * arguments + * @return {Number[]} a color formatted as [r, g, b, a] + * Example: + * input ==> output + * g ==> [g, g, g, 255] + * g,a ==> [g, g, g, a] + * r, g, b ==> [r, g, b, 255] + * r, g, b, a ==> [r, g, b, a] + * [g] ==> [g, g, g, 255] + * [g, a] ==> [g, g, g, a] + * [r, g, b] ==> [r, g, b, 255] + * [r, g, b, a] ==> [r, g, b, a] + * @example + *
+ * + * // todo + * + *
+ * + * @alt + * //todo + * + */ +p5.Color._parseInputs = function() { + var numArgs = arguments.length; + var mode = this._colorMode; + var maxes = this._colorMaxes; + var results = []; + + if (numArgs >= 3) { // Argument is a list of component values. + + results[0] = arguments[0] / maxes[mode][0]; + results[1] = arguments[1] / maxes[mode][1]; + results[2] = arguments[2] / maxes[mode][2]; + + // Alpha may be undefined, so default it to 100%. + if (typeof arguments[3] === 'number') { + results[3] = arguments[3] / maxes[mode][3]; + } else { + results[3] = 1; + } + + // Constrain components to the range [0,1]. + results = results.map(function(value) { + return Math.max(Math.min(value, 1), 0); + }); + + // Convert to RGBA and return. + if (mode === constants.HSL) { + return color_conversion._hslaToRGBA(results); + } else if (mode === constants.HSB) { + return color_conversion._hsbaToRGBA(results); + } else { + return results; + } + + } else if (numArgs === 1 && typeof arguments[0] === 'string') { + + var str = arguments[0].trim().toLowerCase(); + + // Return if string is a named colour. + if (namedColors[str]) { + return p5.Color._parseInputs.apply(this, [namedColors[str]]); + } + + // Try RGBA pattern matching. + if (colorPatterns.HEX3.test(str)) { // #rgb + results = colorPatterns.HEX3.exec(str).slice(1).map(function(color) { + return parseInt(color + color, 16) / 255; + }); + results[3] = 1; + return results; + } else if (colorPatterns.HEX6.test(str)) { // #rrggbb + results = colorPatterns.HEX6.exec(str).slice(1).map(function(color) { + return parseInt(color, 16) / 255; + }); + results[3] = 1; + return results; + } else if (colorPatterns.HEX4.test(str)) { // #rgba + results = colorPatterns.HEX4.exec(str).slice(1).map(function(color) { + return parseInt(color + color, 16) / 255; + }); + return results; + } else if (colorPatterns.HEX8.test(str)) { // #rrggbbaa + results = colorPatterns.HEX8.exec(str).slice(1).map(function(color) { + return parseInt(color, 16) / 255; + }); + return results; + } else if (colorPatterns.RGB.test(str)) { // rgb(R,G,B) + results = colorPatterns.RGB.exec(str).slice(1).map(function(color) { + return color / 255; + }); + results[3] = 1; + return results; + } else if (colorPatterns.RGB_PERCENT.test(str)) { // rgb(R%,G%,B%) + results = colorPatterns.RGB_PERCENT.exec(str).slice(1) + .map(function(color) { + return parseFloat(color) / 100; + }); + results[3] = 1; + return results; + } else if (colorPatterns.RGBA.test(str)) { // rgba(R,G,B,A) + results = colorPatterns.RGBA.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 3) { + return parseFloat(color); + } + return color / 255; + }); + return results; + } else if (colorPatterns.RGBA_PERCENT.test(str)) { // rgba(R%,G%,B%,A%) + results = colorPatterns.RGBA_PERCENT.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 3) { + return parseFloat(color); + } + return parseFloat(color) / 100; + }); + return results; + } + + // Try HSLA pattern matching. + if (colorPatterns.HSL.test(str)) { // hsl(H,S,L) + results = colorPatterns.HSL.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + return parseInt(color, 10) / 100; + }); + results[3] = 1; + } else if (colorPatterns.HSLA.test(str)) { // hsla(H,S,L,A) + results = colorPatterns.HSLA.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + else if (idx === 3) { + return parseFloat(color); + } + return parseInt(color, 10) / 100; + }); + } + results = results.map(function(value) { + return Math.max(Math.min(value, 1), 0); + }); + if (results.length) { + return color_conversion._hslaToRGBA(results); + } + + // Try HSBA pattern matching. + if (colorPatterns.HSB.test(str)) { // hsb(H,S,B) + results = colorPatterns.HSB.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + return parseInt(color, 10) / 100; + }); + results[3] = 1; + } else if (colorPatterns.HSBA.test(str)) { // hsba(H,S,B,A) + results = colorPatterns.HSBA.exec(str).slice(1) + .map(function(color, idx) { + if (idx === 0) { + return parseInt(color, 10) / 360; + } + else if (idx === 3) { + return parseFloat(color); + } + return parseInt(color, 10) / 100; + }); + } + results = results.map(function(value) { + return Math.max(Math.min(value, 1), 0); + }); + + if (results.length) { + return color_conversion._hsbaToRGBA(results); + } + + // Input did not match any CSS color pattern: default to white. + results = [1, 1, 1, 1]; + + } else if ((numArgs === 1 || numArgs === 2) && + typeof arguments[0] === 'number') { // 'Grayscale' mode. + + /** + * For HSB and HSL, interpret the gray level as a brightness/lightness + * value (they are equivalent when chroma is zero). For RGB, normalize the + * gray level according to the blue maximum. + */ + results[0] = arguments[0] / maxes[mode][2]; + results[1] = arguments[0] / maxes[mode][2]; + results[2] = arguments[0] / maxes[mode][2]; + + // Alpha may be undefined, so default it to 100%. + if (typeof arguments[1] === 'number') { + results[3] = arguments[1] / maxes[mode][3]; + } else { + results[3] = 1; + } + + // Constrain components to the range [0,1]. + results = results.map(function(value) { + return Math.max(Math.min(value, 1), 0); + }); + + } else { + throw new Error (arguments + 'is not a valid color representation.'); + } + + return results; +}; + +module.exports = p5.Color; + +},{"../core/constants":53,"../core/core":54,"./color_conversion":46}],49:[function(_dereq_,module,exports){ +/** + * @module Color + * @submodule Setting + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); +_dereq_('./p5.Color'); + +/** + * The background() function sets the color used for the background of the + * p5.js canvas. The default background is light gray. This function is + * typically used within draw() to clear the display window at the beginning + * of each frame, but it can be used inside setup() to set the background on + * the first frame of animation or if the background need only be set once. + *

+ * The color is either specified in terms of the RGB, HSB, or HSL color + * depending on the current colorMode. (The default color space is RGB, with + * each value in the range from 0 to 255). + *

+ * If a single string argument is provided, RGB, RGBA and Hex CSS color strings + * and all named color strings are supported. In this case, an alpha number + * value as a second argument is not supported, the RGBA form should be used. + *

+ * A p5.Color object can also be provided to set the background color. + *

+ * A p5.Image can also be provided to set the background iamge. + * + * @method background + * @param {p5.Color} color any value created by the color() function + * @param {Number} [a] opacity of the background relative to current + * color range (default is 0-100) + * @chainable + * + * @example + *
+ * + * // Grayscale integer value + * background(51); + * + *
+ * + *
+ * + * // R, G & B integer values + * background(255, 204, 0); + * + *
+ * + *
+ * + * // H, S & B integer values + * colorMode(HSB); + * background(255, 204, 100); + * + *
+ * + *
+ * + * // Named SVG/CSS color string + * background('red'); + * + *
+ * + *
+ * + * // three-digit hexadecimal RGB notation + * background('#fae'); + * + *
+ * + *
+ * + * // six-digit hexadecimal RGB notation + * background('#222222'); + * + *
+ * + *
+ * + * // integer RGB notation + * background('rgb(0,255,0)'); + * + *
+ * + *
+ * + * // integer RGBA notation + * background('rgba(0,255,0, 0.25)'); + * + *
+ * + *
+ * + * // percentage RGB notation + * background('rgb(100%,0%,10%)'); + * + *
+ * + *
+ * + * // percentage RGBA notation + * background('rgba(100%,0%,100%,0.5)'); + * + *
+ * + *
+ * + * // p5 Color object + * background(color(0, 0, 255)); + * + *
+ * + * @alt + * canvas with darkest charcoal grey background. + * canvas with yellow background. + * canvas with royal blue background. + * canvas with red background. + * canvas with pink background. + * canvas with black background. + * canvas with bright green background. + * canvas with soft green background. + * canvas with red background. + * canvas with light purple background. + * canvas with blue background. + */ + +/** + * @method background + * @param {String} colorstring color string, possible formats include: integer + * rgb() or rgba(), percentage rgb() or rgba(), + * 3-digit hex, 6-digit hex + * @param {Number} [a] + * @chainable + */ + +/** + * @method background + * @param {Number} gray specifies a value between white and black + * @param {Number} [a] + * @chainable + */ + +/** + * @method background + * @param {Number} v1 red or hue value (depending on the current color + * mode) + * @param {Number} v2 green or saturation value (depending on the current + * color mode) + * @param {Number} v3 blue or brightness value (depending on the current + * color mode) + * @param {Number} [a] + * @chainable + */ + +/** + * @method background + * @param {p5.Image} image image created with loadImage() or createImage(), + * to set as background + * (must be same size as the sketch window) + * @param {Number} [a] + * @chainable + */ +p5.prototype.background = function() { + if (arguments[0] instanceof p5.Image) { + this.image(arguments[0], 0, 0, this.width, this.height); + } else { + this._renderer.background.apply(this._renderer, arguments); + } + return this; +}; + +/** + * Clears the pixels within a buffer. This function only works on p5.Canvas + * objects created with the createCanvas() function; it won't work with the + * main display window. Unlike the main graphics context, pixels in + * additional graphics areas created with createGraphics() can be entirely + * or partially transparent. This function clears everything to make all of + * the pixels 100% transparent. + * + * @method clear + * @chainable + * @example + *
+ * + * // Clear the screen on mouse press. + * function setup() { + * createCanvas(100, 100); + * } + * + * function draw() { + * ellipse(mouseX, mouseY, 20, 20); + * } + * + * function mousePressed() { + * clear(); + * } + * + *
+ * + * @alt + * 20x20 white ellipses are continually drawn at mouse x and y coordinates. + * + */ + +p5.prototype.clear = function() { + this._renderer.clear(); + return this; +}; + +/** + * colorMode() changes the way p5.js interprets color data. By default, the + * parameters for fill(), stroke(), background(), and color() are defined by + * values between 0 and 255 using the RGB color model. This is equivalent to + * setting colorMode(RGB, 255). Setting colorMode(HSB) lets you use the HSB + * system instead. By default, this is colorMode(HSB, 360, 100, 100, 1). You + * can also use HSL. + *

+ * Note: existing color objects remember the mode that they were created in, + * so you can change modes as you like without affecting their appearance. + * + * + * @method colorMode + * @param {Constant} mode either RGB, HSB or HSL, corresponding to + * Red/Green/Blue and Hue/Saturation/Brightness + * (or Lightness) + * @param {Number} [max] range for all values + * @chainable + */ +/** + * @method colorMode + * @param {Constant} mode + * @param {Number} max1 range for the red or hue depending on the + * current color mode + * @param {Number} max2 range for the green or saturation depending + * on the current color mode + * @param {Number} max3 range for the blue or brightness/lighntess + * depending on the current color mode + * @param {Number} [maxA] range for the alpha + * @chainable + * + * @example + *
+ * + * noStroke(); + * colorMode(RGB, 100); + * for (i = 0; i < 100; i++) { + * for (j = 0; j < 100; j++) { + * stroke(i, j, 0); + * point(i, j); + * } + * } + * + *
+ * + *
+ * + * noStroke(); + * colorMode(HSB, 100); + * for (i = 0; i < 100; i++) { + * for (j = 0; j < 100; j++) { + * stroke(i, j, 100); + * point(i, j); + * } + * } + * + *
+ * + *
+ * + * colorMode(RGB, 255); + * var c = color(127, 255, 0); + * + * colorMode(RGB, 1); + * var myColor = c._getRed(); + * text(myColor, 10, 10, 80, 80); + * + *
+ * + *
+ * + * noFill(); + * colorMode(RGB, 255, 255, 255, 1); + * background(255); + * + * strokeWeight(4); + * stroke(255, 0 , 10, 0.3); + * ellipse(40, 40, 50, 50); + * ellipse(50, 50, 40, 40); + * + *
+ * + * @alt + *Green to red gradient from bottom L to top R. shading originates from top left. + *Rainbow gradient from left to right. Brightness increasing to white at top. + *unknown image. + *50x50 ellipse at middle L & 40x40 ellipse at center. Transluscent pink outlines. + * + */ +p5.prototype.colorMode = function() { + if (arguments[0] === constants.RGB || + arguments[0] === constants.HSB || + arguments[0] === constants.HSL) { + + // Set color mode. + this._renderer._colorMode = arguments[0]; + + // Set color maxes. + var maxes = this._renderer._colorMaxes[this._renderer._colorMode]; + if (arguments.length === 2) { + maxes[0] = arguments[1]; // Red + maxes[1] = arguments[1]; // Green + maxes[2] = arguments[1]; // Blue + maxes[3] = arguments[1]; // Alpha + } else if (arguments.length === 4) { + maxes[0] = arguments[1]; // Red + maxes[1] = arguments[2]; // Green + maxes[2] = arguments[3]; // Blue + } else if (arguments.length === 5) { + maxes[0] = arguments[1]; // Red + maxes[1] = arguments[2]; // Green + maxes[2] = arguments[3]; // Blue + maxes[3] = arguments[4]; // Alpha + } + } + + return this; +}; + +/** + * Sets the color used to fill shapes. For example, if you run + * fill(204, 102, 0), all subsequent shapes will be filled with orange. This + * color is either specified in terms of the RGB or HSB color depending on + * the current colorMode(). (The default color space is RGB, with each value + * in the range from 0 to 255). + *

+ * If a single string argument is provided, RGB, RGBA and Hex CSS color strings + * and all named color strings are supported. In this case, an alpha number + * value as a second argument is not supported, the RGBA form should be used. + *

+ * A p5 Color object can also be provided to set the fill color. + * + * @method fill + * @param {Number} v1 red or hue value relative to + * the current color range + * @param {Number} v2 green or saturation value + * relative to the current color range + * @param {Number} v3 blue or brightness value + * relative to the current color range + * @param {Number} [alpha] + * @chainable + */ + +/** + * @method fill + * @param {String} value a color string + * @param {Number} [alpha] + * @chainable + */ + +/** + * @method fill + * @param {Number[]} values an array containing the red,green,blue & + * and alpha components of the color + * @chainable + */ + +/** + * @method fill + * @param {p5.Color} color the fill color + * @param {Number} [alpha] + * @chainable + * + * @example + *
+ * + * // Grayscale integer value + * fill(51); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // R, G & B integer values + * fill(255, 204, 0); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // H, S & B integer values + * colorMode(HSB); + * fill(255, 204, 100); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // Named SVG/CSS color string + * fill('red'); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // three-digit hexadecimal RGB notation + * fill('#fae'); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // six-digit hexadecimal RGB notation + * fill('#222222'); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // integer RGB notation + * fill('rgb(0,255,0)'); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // integer RGBA notation + * fill('rgba(0,255,0, 0.25)'); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // percentage RGB notation + * fill('rgb(100%,0%,10%)'); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // percentage RGBA notation + * fill('rgba(100%,0%,100%,0.5)'); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // p5 Color object + * fill(color(0, 0, 255)); + * rect(20, 20, 60, 60); + * + *
+ * @alt + * 60x60 dark charcoal grey rect with black outline in center of canvas. + * 60x60 yellow rect with black outline in center of canvas. + * 60x60 royal blue rect with black outline in center of canvas. + * 60x60 red rect with black outline in center of canvas. + * 60x60 pink rect with black outline in center of canvas. + * 60x60 black rect with black outline in center of canvas. + * 60x60 light green rect with black outline in center of canvas. + * 60x60 soft green rect with black outline in center of canvas. + * 60x60 red rect with black outline in center of canvas. + * 60x60 dark fushcia rect with black outline in center of canvas. + * 60x60 blue rect with black outline in center of canvas. + */ + +p5.prototype.fill = function() { + this._renderer._setProperty('_fillSet', true); + this._renderer._setProperty('_doFill', true); + this._renderer.fill.apply(this._renderer, arguments); + return this; +}; + +/** + * Disables filling geometry. If both noStroke() and noFill() are called, + * nothing will be drawn to the screen. + * + * @method noFill + * @chainable + * @example + *
+ * + * rect(15, 10, 55, 55); + * noFill(); + * rect(20, 20, 60, 60); + * + *
+ * @alt + * white rect top middle and noFill rect center. Both 60x60 with black outlines. + */ +p5.prototype.noFill = function() { + this._renderer._setProperty('_doFill', false); + return this; +}; + +/** + * Disables drawing the stroke (outline). If both noStroke() and noFill() + * are called, nothing will be drawn to the screen. + * + * @method noStroke + * @chainable + * @example + *
+ * + * noStroke(); + * rect(20, 20, 60, 60); + * + *
+ * + * + * @alt + *60x60 white rect at center. no outline. + * + */ + +p5.prototype.noStroke = function() { + this._renderer._setProperty('_doStroke', false); + return this; +}; + +/** + * Sets the color used to draw lines and borders around shapes. This color + * is either specified in terms of the RGB or HSB color depending on the + * current colorMode() (the default color space is RGB, with each value in + * the range from 0 to 255). + *

+ * If a single string argument is provided, RGB, RGBA and Hex CSS color + * strings and all named color strings are supported. In this case, an alpha + * number value as a second argument is not supported, the RGBA form should be + * used. + *

+ * A p5 Color object can also be provided to set the stroke color. + * + * + * @method stroke + * @param {Number} v1 red or hue value relative to + * the current color range + * @param {Number} v2 green or saturation value + * relative to the current color range + * @param {Number} v3 blue or brightness value + * relative to the current color range + * @param {Number} [alpha] + * @chainable + */ + +/** + * @method stroke + * @param {String} value a color string + * @param {Number} [alpha] + * @chainable + */ + +/** + * @method stroke + * @param {Number[]} values an array containing the red,green,blue & + * and alpha components of the color + * @chainable + */ + +/** + * @method stroke + * @param {p5.Color} color the stroke color + * @param {Number} [alpha] + * @chainable + * + * @example + *
+ * + * // Grayscale integer value + * strokeWeight(4); + * stroke(51); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // R, G & B integer values + * stroke(255, 204, 0); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // H, S & B integer values + * colorMode(HSB); + * strokeWeight(4); + * stroke(255, 204, 100); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // Named SVG/CSS color string + * stroke('red'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // three-digit hexadecimal RGB notation + * stroke('#fae'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // six-digit hexadecimal RGB notation + * stroke('#222222'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // integer RGB notation + * stroke('rgb(0,255,0)'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // integer RGBA notation + * stroke('rgba(0,255,0,0.25)'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // percentage RGB notation + * stroke('rgb(100%,0%,10%)'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // percentage RGBA notation + * stroke('rgba(100%,0%,100%,0.5)'); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * + *
+ * + *
+ * + * // p5 Color object + * stroke(color(0, 0, 255)); + * strokeWeight(4); + * rect(20, 20, 60, 60); + * + *
+ * + * @alt + * 60x60 white rect at center. Dark charcoal grey outline. + * 60x60 white rect at center. Yellow outline. + * 60x60 white rect at center. Royal blue outline. + * 60x60 white rect at center. Red outline. + * 60x60 white rect at center. Pink outline. + * 60x60 white rect at center. Black outline. + * 60x60 white rect at center. Bright green outline. + * 60x60 white rect at center. Soft green outline. + * 60x60 white rect at center. Red outline. + * 60x60 white rect at center. Dark fushcia outline. + * 60x60 white rect at center. Blue outline. + */ + +p5.prototype.stroke = function() { + this._renderer._setProperty('_strokeSet', true); + this._renderer._setProperty('_doStroke', true); + this._renderer.stroke.apply(this._renderer, arguments); + return this; +}; + +module.exports = p5; + +},{"../core/constants":53,"../core/core":54,"./p5.Color":48}],50:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule 2D Primitives + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); +var canvas = _dereq_('./canvas'); +_dereq_('./error_helpers'); + +/** + * Draw an arc to the screen. If called with only a, b, c, d, start, and + * stop, the arc will be drawn as an open pie. If mode is provided, the arc + * will be drawn either open, as a chord, or as a pie as specified. The + * origin may be changed with the ellipseMode() function.

+ * Note that drawing a full circle (ex: 0 to TWO_PI) will appear blank + * because 0 and TWO_PI are the same position on the unit circle. The + * best way to handle this is by using the ellipse() function instead + * to create a closed ellipse, and to use the arc() function + * only to draw parts of an ellipse. + * + * @method arc + * @param {Number} a x-coordinate of the arc's ellipse + * @param {Number} b y-coordinate of the arc's ellipse + * @param {Number} c width of the arc's ellipse by default + * @param {Number} d height of the arc's ellipse by default + * @param {Number} start angle to start the arc, specified in radians + * @param {Number} stop angle to stop the arc, specified in radians + * @param {Constant} [mode] optional parameter to determine the way of drawing + * the arc. either CHORD or PIE + * @chainable + * @example + *
+ * + * arc(50, 55, 50, 50, 0, HALF_PI); + * noFill(); + * arc(50, 55, 60, 60, HALF_PI, PI); + * arc(50, 55, 70, 70, PI, PI+QUARTER_PI); + * arc(50, 55, 80, 80, PI+QUARTER_PI, TWO_PI); + * + *
+ * + *
+ * + * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, OPEN); + * + *
+ * + *
+ * + * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, CHORD); + * + *
+ * + *
+ * + * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, PIE); + * + *
+ * + * @alt + *shattered outline of an ellipse with a quarter of a white circle bottom-right. + *white ellipse with black outline with top right missing. + *white ellipse with top right missing with black outline around shape. + *white ellipse with top right quarter missing with black outline around the shape. + * + */ +p5.prototype.arc = function(x, y, w, h, start, stop, mode) { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + if (this._angleMode === constants.DEGREES) { + start = this.radians(start); + stop = this.radians(stop); + } + + // Make all angles positive... + while (start < 0) { + start += constants.TWO_PI; + } + while (stop < 0) { + stop += constants.TWO_PI; + } + // ...and confine them to the interval [0,TWO_PI). + start %= constants.TWO_PI; + stop %= constants.TWO_PI; + + // account for full circle + if (stop === start) { + stop += constants.TWO_PI; + } + + // Adjust angles to counter linear scaling. + if (start <= constants.HALF_PI) { + start = Math.atan(w / h * Math.tan(start)); + } else if (start > constants.HALF_PI && start <= 3 * constants.HALF_PI) { + start = Math.atan(w / h * Math.tan(start)) + constants.PI; + } else { + start = Math.atan(w / h * Math.tan(start)) + constants.TWO_PI; + } + if (stop <= constants.HALF_PI) { + stop = Math.atan(w / h * Math.tan(stop)); + } else if (stop > constants.HALF_PI && stop <= 3 * constants.HALF_PI) { + stop = Math.atan(w / h * Math.tan(stop)) + constants.PI; + } else { + stop = Math.atan(w / h * Math.tan(stop)) + constants.TWO_PI; + } + + // Exceed the interval if necessary in order to preserve the size and + // orientation of the arc. + if (start > stop) { + stop += constants.TWO_PI; + } + // p5 supports negative width and heights for ellipses + w = Math.abs(w); + h = Math.abs(h); + this._renderer.arc(x, y, w, h, start, stop, mode); + return this; +}; + +/** + * Draws an ellipse (oval) to the screen. An ellipse with equal width and + * height is a circle. By default, the first two parameters set the location, + * and the third and fourth parameters set the shape's width and height. If + * no height is specified, the value of width is used for both the width and + * height. If a negative height or width is specified, the absolute value is taken. + * The origin may be changed with the ellipseMode() function. + * + * @method ellipse + * @param {Number} x x-coordinate of the ellipse. + * @param {Number} y y-coordinate of the ellipse. + * @param {Number} w width of the ellipse. + * @param {Number} [h] height of the ellipse. + * @chainable + * @example + *
+ * + * ellipse(56, 46, 55, 55); + * + *
+ * + * @alt + *white ellipse with black outline in middle-right of canvas that is 55x55. + * + */ +p5.prototype.ellipse = function() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + // Duplicate 3rd argument if only 3 given. + if (args.length === 3) { + args.push(args[2]); + } + // p5 supports negative width and heights for rects + if (args[2] < 0){args[2] = Math.abs(args[2]);} + if (args[3] < 0){args[3] = Math.abs(args[3]);} + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + var vals = canvas.modeAdjust( + args[0], + args[1], + args[2], + args[3], + this._renderer._ellipseMode); + args[0] = vals.x; + args[1] = vals.y; + args[2] = vals.w; + args[3] = vals.h; + this._renderer.ellipse(args); + return this; +}; +/** + * Draws a line (a direct path between two points) to the screen. The version + * of line() with four parameters draws the line in 2D. To color a line, use + * the stroke() function. A line cannot be filled, therefore the fill() + * function will not affect the color of a line. 2D lines are drawn with a + * width of one pixel by default, but this can be changed with the + * strokeWeight() function. + * + * @method line + * @param {Number} x1 the x-coordinate of the first point + * @param {Number} y1 the y-coordinate of the first point + * @param {Number} x2 the x-coordinate of the second point + * @param {Number} y2 the y-coordinate of the second point + * @chainable + * @example + *
+ * + * line(30, 20, 85, 75); + * + *
+ * + *
+ * + * line(30, 20, 85, 20); + * stroke(126); + * line(85, 20, 85, 75); + * stroke(255); + * line(85, 75, 30, 75); + * + *
+ * + * @alt + *line 78 pixels long running from mid-top to bottom-right of canvas. + *3 lines of various stroke sizes. Form top, bottom and right sides of a square. + * + */ +////commented out original +// p5.prototype.line = function(x1, y1, x2, y2) { +// if (!this._renderer._doStroke) { +// return this; +// } +// if(this._renderer.isP3D){ +// } else { +// this._renderer.line(x1, y1, x2, y2); +// } +// }; +p5.prototype.line = function() { + if (!this._renderer._doStroke) { + return this; + } + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + //check whether we should draw a 3d line or 2d + if(this._renderer.isP3D){ + this._renderer.line( + args[0], + args[1], + args[2], + args[3], + args[4], + args[5]); + } else { + this._renderer.line( + args[0], + args[1], + args[2], + args[3]); + } + return this; +}; + +/** + * Draws a point, a coordinate in space at the dimension of one pixel. + * The first parameter is the horizontal value for the point, the second + * value is the vertical value for the point. The color of the point is + * determined by the current stroke. + * + * @method point + * @param {Number} x the x-coordinate + * @param {Number} y the y-coordinate + * @chainable + * @example + *
+ * + * point(30, 20); + * point(85, 20); + * point(85, 75); + * point(30, 75); + * + *
+ * + * @alt + *4 points centered in the middle-right of the canvas. + * + */ +p5.prototype.point = function() { + if (!this._renderer._doStroke) { + return this; + } + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + //check whether we should draw a 3d line or 2d + if(this._renderer.isP3D){ + this._renderer.point( + args[0], + args[1], + args[2] + ); + } else { + this._renderer.point( + args[0], + args[1] + ); + } + return this; +}; + + +/** + * Draw a quad. A quad is a quadrilateral, a four sided polygon. It is + * similar to a rectangle, but the angles between its edges are not + * constrained to ninety degrees. The first pair of parameters (x1,y1) + * sets the first vertex and the subsequent pairs should proceed + * clockwise or counter-clockwise around the defined shape. + * + * @method quad + * @param {Number} x1 the x-coordinate of the first point + * @param {Number} y1 the y-coordinate of the first point + * @param {Number} x2 the x-coordinate of the second point + * @param {Number} y2 the y-coordinate of the second point + * @param {Number} x3 the x-coordinate of the third point + * @param {Number} y3 the y-coordinate of the third point + * @param {Number} x4 the x-coordinate of the fourth point + * @param {Number} y4 the y-coordinate of the fourth point + * @chainable + * @example + *
+ * + * quad(38, 31, 86, 20, 69, 63, 30, 76); + * + *
+ * + * @alt + *irregular white quadrilateral shape with black outline mid-right of canvas. + * + */ +/** + * @method quad + * @param {Number} x1 + * @param {Number} y1 + * @param {Number} x2 + * @param {Number} y2 + * @param {Number} x3 + * @param {Number} y3 + * @param {Number} x4 + * @param {Number} y4 + * @chainable + */ +p5.prototype.quad = function() { + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if(this._renderer.isP3D){ + this._renderer.quad( + args[0], + args[1], + args[2], + args[3], + args[4], + args[5], + args[6], + args[7], + args[8], + args[9], + args[10], + args[11] + ); + } else { + this._renderer.quad( + args[0], + args[1], + args[2], + args[3], + args[4], + args[5], + args[6], + args[7] + ); + } + return this; +}; + +/** +* Draws a rectangle to the screen. A rectangle is a four-sided shape with +* every angle at ninety degrees. By default, the first two parameters set +* the location of the upper-left corner, the third sets the width, and the +* fourth sets the height. The way these parameters are interpreted, however, +* may be changed with the rectMode() function. +*

+* The fifth, sixth, seventh and eighth parameters, if specified, +* determine corner radius for the top-right, top-left, lower-right and +* lower-left corners, respectively. An omitted corner radius parameter is set +* to the value of the previously specified radius value in the parameter list. +* +* @method rect +* @param {Number} x x-coordinate of the rectangle. +* @param {Number} y y-coordinate of the rectangle. +* @param {Number} w width of the rectangle. +* @param {Number} h height of the rectangle. +* @param {Number} [tl] optional radius of top-left corner. +* @param {Number} [tr] optional radius of top-right corner. +* @param {Number} [br] optional radius of bottom-right corner. +* @param {Number} [bl] optional radius of bottom-left corner. +* @return {p5} the p5 object. +* @example +*
+* +* // Draw a rectangle at location (30, 20) with a width and height of 55. +* rect(30, 20, 55, 55); +* +*
+* +*
+* +* // Draw a rectangle with rounded corners, each having a radius of 20. +* rect(30, 20, 55, 55, 20); +* +*
+* +*
+* +* // Draw a rectangle with rounded corners having the following radii: +* // top-left = 20, top-right = 15, bottom-right = 10, bottom-left = 5. +* rect(30, 20, 55, 55, 20, 15, 10, 5); +* +*
+* +* @alt +* 55x55 white rect with black outline in mid-right of canvas. +* 55x55 white rect with black outline and rounded edges in mid-right of canvas. +* 55x55 white rect with black outline and rounded edges of different radii. +*/ +/** +* @method rect +* @param {Number} x +* @param {Number} y +* @param {Number} w +* @param {Number} h +* @param {Number} [detailX] +* @param {Number} [detailY] +* @chainable +*/ +p5.prototype.rect = function () { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + var vals = canvas.modeAdjust( + args[0], + args[1], + args[2], + args[3], + this._renderer._rectMode); + args[0] = vals.x; + args[1] = vals.y; + args[2] = vals.w; + args[3] = vals.h; + this._renderer.rect(args); + return this; +}; + +/** +* A triangle is a plane created by connecting three points. The first two +* arguments specify the first point, the middle two arguments specify the +* second point, and the last two arguments specify the third point. +* +* @method triangle +* @param {Number} x1 x-coordinate of the first point +* @param {Number} y1 y-coordinate of the first point +* @param {Number} x2 x-coordinate of the second point +* @param {Number} y2 y-coordinate of the second point +* @param {Number} x3 x-coordinate of the third point +* @param {Number} y3 y-coordinate of the third point +* @chainable +* @example +*
+* +* triangle(30, 75, 58, 20, 86, 75); +* +*
+* +*@alt +* white triangle with black outline in mid-right of canvas. +* +*/ +p5.prototype.triangle = function() { + + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + this._renderer.triangle(args); + return this; +}; + +module.exports = p5; + +},{"./canvas":52,"./constants":53,"./core":54,"./error_helpers":57}],51:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule Attributes + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); + +/** + * Modifies the location from which ellipses are drawn by changing the way + * in which parameters given to ellipse() are interpreted. + *

+ * The default mode is ellipseMode(CENTER), which interprets the first two + * parameters of ellipse() as the shape's center point, while the third and + * fourth parameters are its width and height. + *

+ * ellipseMode(RADIUS) also uses the first two parameters of ellipse() as + * the shape's center point, but uses the third and fourth parameters to + * specify half of the shapes's width and height. + *

+ * ellipseMode(CORNER) interprets the first two parameters of ellipse() as + * the upper-left corner of the shape, while the third and fourth parameters + * are its width and height. + *

+ * ellipseMode(CORNERS) interprets the first two parameters of ellipse() as + * the location of one corner of the ellipse's bounding box, and the third + * and fourth parameters as the location of the opposite corner. + *

+ * The parameter must be written in ALL CAPS because Javascript is a + * case-sensitive language. + * + * @method ellipseMode + * @param {Constant} mode either CENTER, RADIUS, CORNER, or CORNERS + * @chainable + * @example + *
+ * + * ellipseMode(RADIUS); // Set ellipseMode to RADIUS + * fill(255); // Set fill to white + * ellipse(50, 50, 30, 30); // Draw white ellipse using RADIUS mode + * + * ellipseMode(CENTER); // Set ellipseMode to CENTER + * fill(100); // Set fill to gray + * ellipse(50, 50, 30, 30); // Draw gray ellipse using CENTER mode + * + *
+ * + *
+ * + * ellipseMode(CORNER); // Set ellipseMode is CORNER + * fill(255); // Set fill to white + * ellipse(25, 25, 50, 50); // Draw white ellipse using CORNER mode + * + * ellipseMode(CORNERS); // Set ellipseMode to CORNERS + * fill(100); // Set fill to gray + * ellipse(25, 25, 50, 50); // Draw gray ellipse using CORNERS mode + * + *
+ * + * @alt + * 60x60 white ellipse and 30x30 grey ellipse with black outlines at center. + * 60x60 white ellipse @center and 30x30 grey ellipse top-right, black outlines. + * + */ +p5.prototype.ellipseMode = function(m) { + if (m === constants.CORNER || + m === constants.CORNERS || + m === constants.RADIUS || + m === constants.CENTER) { + this._renderer._ellipseMode = m; + } + return this; +}; + +/** + * Draws all geometry with jagged (aliased) edges. Note that smooth() is + * active by default, so it is necessary to call noSmooth() to disable + * smoothing of geometry, images, and fonts. + * + * @method noSmooth + * @chainable + * @example + *
+ * + * background(0); + * noStroke(); + * smooth(); + * ellipse(30, 48, 36, 36); + * noSmooth(); + * ellipse(70, 48, 36, 36); + * + *
+ * + * @alt + * 2 pixelated 36x36 white ellipses to left & right of center, black background + * + */ +p5.prototype.noSmooth = function() { + this._renderer.noSmooth(); + return this; +}; + +/** + * Modifies the location from which rectangles are drawn by changing the way + * in which parameters given to rect() are interpreted. + *

+ * The default mode is rectMode(CORNER), which interprets the first two + * parameters of rect() as the upper-left corner of the shape, while the + * third and fourth parameters are its width and height. + *

+ * rectMode(CORNERS) interprets the first two parameters of rect() as the + * location of one corner, and the third and fourth parameters as the + * location of the opposite corner. + *

+ * rectMode(CENTER) interprets the first two parameters of rect() as the + * shape's center point, while the third and fourth parameters are its + * width and height. + *

+ * rectMode(RADIUS) also uses the first two parameters of rect() as the + * shape's center point, but uses the third and fourth parameters to specify + * half of the shapes's width and height. + *

+ * The parameter must be written in ALL CAPS because Javascript is a + * case-sensitive language. + * + * @method rectMode + * @param {Constant} mode either CORNER, CORNERS, CENTER, or RADIUS + * @chainable + * @example + *
+ * + * rectMode(CORNER); // Default rectMode is CORNER + * fill(255); // Set fill to white + * rect(25, 25, 50, 50); // Draw white rect using CORNER mode + * + * rectMode(CORNERS); // Set rectMode to CORNERS + * fill(100); // Set fill to gray + * rect(25, 25, 50, 50); // Draw gray rect using CORNERS mode + * + *
+ * + *
+ * + * rectMode(RADIUS); // Set rectMode to RADIUS + * fill(255); // Set fill to white + * rect(50, 50, 30, 30); // Draw white rect using RADIUS mode + * + * rectMode(CENTER); // Set rectMode to CENTER + * fill(100); // Set fill to gray + * rect(50, 50, 30, 30); // Draw gray rect using CENTER mode + * + *
+ * + * @alt + * 50x50 white rect at center and 25x25 grey rect in the top left of the other. + * 50x50 white rect at center and 25x25 grey rect in the center of the other. + * + */ +p5.prototype.rectMode = function(m) { + if (m === constants.CORNER || + m === constants.CORNERS || + m === constants.RADIUS || + m === constants.CENTER) { + this._renderer._rectMode = m; + } + return this; +}; + +/** + * Draws all geometry with smooth (anti-aliased) edges. smooth() will also + * improve image quality of resized images. Note that smooth() is active by + * default; noSmooth() can be used to disable smoothing of geometry, + * images, and fonts. + * + * @method smooth + * @chainable + * @example + *
+ * + * background(0); + * noStroke(); + * smooth(); + * ellipse(30, 48, 36, 36); + * noSmooth(); + * ellipse(70, 48, 36, 36); + * + *
+ * + * @alt + * 2 pixelated 36x36 white ellipses one left one right of center. On black. + * + */ +p5.prototype.smooth = function() { + this._renderer.smooth(); + return this; +}; + +/** + * Sets the style for rendering line endings. These ends are either squared, + * extended, or rounded, each of which specified with the corresponding + * parameters: SQUARE, PROJECT, and ROUND. The default cap is ROUND. + * + * @method strokeCap + * @param {Constant} cap either SQUARE, PROJECT, or ROUND + * @chainable + * @example + *
+ * + * strokeWeight(12.0); + * strokeCap(ROUND); + * line(20, 30, 80, 30); + * strokeCap(SQUARE); + * line(20, 50, 80, 50); + * strokeCap(PROJECT); + * line(20, 70, 80, 70); + * + *
+ * + * @alt + * 3 lines. Top line: rounded ends, mid: squared, bottom:longer squared ends. + * + */ +p5.prototype.strokeCap = function(cap) { + if (cap === constants.ROUND || + cap === constants.SQUARE || + cap === constants.PROJECT) { + this._renderer.strokeCap(cap); + } + return this; +}; + +/** + * Sets the style of the joints which connect line segments. These joints + * are either mitered, beveled, or rounded and specified with the + * corresponding parameters MITER, BEVEL, and ROUND. The default joint is + * MITER. + * + * @method strokeJoin + * @param {Constant} join either MITER, BEVEL, ROUND + * @chainable + * @example + *
+ * + * noFill(); + * strokeWeight(10.0); + * strokeJoin(MITER); + * beginShape(); + * vertex(35, 20); + * vertex(65, 50); + * vertex(35, 80); + * endShape(); + * + *
+ * + *
+ * + * noFill(); + * strokeWeight(10.0); + * strokeJoin(BEVEL); + * beginShape(); + * vertex(35, 20); + * vertex(65, 50); + * vertex(35, 80); + * endShape(); + * + *
+ * + *
+ * + * noFill(); + * strokeWeight(10.0); + * strokeJoin(ROUND); + * beginShape(); + * vertex(35, 20); + * vertex(65, 50); + * vertex(35, 80); + * endShape(); + * + *
+ * + * @alt + * Right-facing arrowhead shape with pointed tip in center of canvas. + * Right-facing arrowhead shape with flat tip in center of canvas. + * Right-facing arrowhead shape with rounded tip in center of canvas. + * + */ +p5.prototype.strokeJoin = function(join) { + if (join === constants.ROUND || + join === constants.BEVEL || + join === constants.MITER) { + this._renderer.strokeJoin(join); + } + return this; +}; + +/** + * Sets the width of the stroke used for lines, points, and the border + * around shapes. All widths are set in units of pixels. + * + * @method strokeWeight + * @param {Number} weight the weight (in pixels) of the stroke + * @return {p5} the p5 object + * @example + *
+ * + * strokeWeight(1); // Default + * line(20, 20, 80, 20); + * strokeWeight(4); // Thicker + * line(20, 40, 80, 40); + * strokeWeight(10); // Beastly + * line(20, 70, 80, 70); + * + *
+ * + * @alt + * 3 horizontal black lines. Top line: thin, mid: medium, bottom:thick. + * + */ +p5.prototype.strokeWeight = function(w) { + this._renderer.strokeWeight(w); + return this; +}; + +module.exports = p5; + +},{"./constants":53,"./core":54}],52:[function(_dereq_,module,exports){ +/** + * @requires constants + */ + +var constants = _dereq_('./constants'); + +module.exports = { + + modeAdjust: function(a, b, c, d, mode) { + if (mode === constants.CORNER) { + return { x: a, y: b, w: c, h: d }; + } else if (mode === constants.CORNERS) { + return { x: a, y: b, w: c-a, h: d-b }; + } else if (mode === constants.RADIUS) { + return { x: a-c, y: b-d, w: 2*c, h: 2*d }; + } else if (mode === constants.CENTER) { + return { x: a-c*0.5, y: b-d*0.5, w: c, h: d }; + } + }, + + arcModeAdjust: function(a, b, c, d, mode) { + if (mode === constants.CORNER) { + return { x: a+c*0.5, y: b+d*0.5, w: c, h: d }; + } else if (mode === constants.CORNERS) { + return { x: a, y: b, w: c+a, h: d+b }; + } else if (mode === constants.RADIUS) { + return { x: a, y: b, w: 2*c, h: 2*d }; + } else if (mode === constants.CENTER) { + return { x: a, y: b, w: c, h: d }; + } + } + +}; + + +},{"./constants":53}],53:[function(_dereq_,module,exports){ +/** + * @module Constants + * @submodule Constants + * @for p5 + */ + +var PI = Math.PI; + +module.exports = { + + // GRAPHICS RENDERER + /** + * @property {String} P2D + * @final + */ + P2D: 'p2d', + /** + * @property {String} WEBGL + * @final + */ + WEBGL: 'webgl', + + // ENVIRONMENT + ARROW: 'default', + CROSS: 'crosshair', + HAND: 'pointer', + MOVE: 'move', + TEXT: 'text', + WAIT: 'wait', + + // TRIGONOMETRY + + /** + * HALF_PI is a mathematical constant with the value + * 1.57079632679489661923. It is half the ratio of the + * circumference of a circle to its diameter. It is useful in + * combination with the trigonometric functions sin() and cos(). + * + * @property {Number} HALF_PI + * @final + * + * @example + *
+ * arc(50, 50, 80, 80, 0, HALF_PI); + *
+ * + * @alt + * 80x80 white quarter-circle with curve toward bottom right of canvas. + * + */ + HALF_PI: PI / 2, + /** + * PI is a mathematical constant with the value + * 3.14159265358979323846. It is the ratio of the circumference + * of a circle to its diameter. It is useful in combination with + * the trigonometric functions sin() and cos(). + * + * @property {Number} PI + * @final + * + * @example + *
+ * arc(50, 50, 80, 80, 0, PI); + *
+ * + * @alt + * white half-circle with curve toward bottom of canvas. + * + */ + PI: PI, + /** + * QUARTER_PI is a mathematical constant with the value 0.7853982. + * It is one quarter the ratio of the circumference of a circle to + * its diameter. It is useful in combination with the trigonometric + * functions sin() and cos(). + * + * @property {Number} QUARTER_PI + * @final + * + * @example + *
+ * arc(50, 50, 80, 80, 0, QUARTER_PI); + *
+ * + * @alt + * white eighth-circle rotated about 40 degrees with curve bottom right canvas. + * + */ + QUARTER_PI: PI / 4, + /** + * TAU is an alias for TWO_PI, a mathematical constant with the + * value 6.28318530717958647693. It is twice the ratio of the + * circumference of a circle to its diameter. It is useful in + * combination with the trigonometric functions sin() and cos(). + * + * @property {Number} TAU + * @final + * + * @example + *
+ * arc(50, 50, 80, 80, 0, TAU); + *
+ * + * @alt + * 80x80 white ellipse shape in center of canvas. + * + */ + TAU: PI * 2, + /** + * TWO_PI is a mathematical constant with the value + * 6.28318530717958647693. It is twice the ratio of the + * circumference of a circle to its diameter. It is useful in + * combination with the trigonometric functions sin() and cos(). + * + * @property {Number} TWO_PI + * @final + * + * @example + *
+ * arc(50, 50, 80, 80, 0, TWO_PI); + *
+ * + * @alt + * 80x80 white ellipse shape in center of canvas. + * + */ + TWO_PI: PI * 2, + /** + * @property {String} DEGREES + * @final + */ + DEGREES: 'degrees', + /** + * @property {String} RADIANS + * @final + */ + RADIANS: 'radians', + DEG_TO_RAD: PI / 180.0, + RAD_TO_DEG: 180.0 / PI, + + // SHAPE + /** + * @property {String} CORNER + * @final + */ + CORNER: 'corner', + /** + * @property {String} CORNERS + * @final + */ + CORNERS: 'corners', + /** + * @property {String} RADIUS + * @final + */ + RADIUS: 'radius', + /** + * @property {String} RIGHT + * @final + */ + RIGHT: 'right', + /** + * @property {String} LEFT + * @final + */ + LEFT: 'left', + /** + * @property {String} CENTER + * @final + */ + CENTER: 'center', + /** + * @property {String} TOP + * @final + */ + TOP: 'top', + /** + * @property {String} BOTTOM + * @final + */ + BOTTOM: 'bottom', + /** + * @property {String} BASELINE + * @final + * @default alphabetic + */ + BASELINE: 'alphabetic', + /** + * @property {Number} POINTS + * @final + * @default 0x0000 + */ + POINTS: 0x0000, + /** + * @property {Number} LINES + * @final + * @default 0x0001 + */ + LINES: 0x0001, + /** + * @property {Number} LINE_STRIP + * @final + * @default 0x0003 + */ + LINE_STRIP: 0x0003, + /** + * @property {Number} LINE_LOOP + * @final + * @default 0x0002 + */ + LINE_LOOP: 0x0002, + /** + * @property {Number} TRIANGLES + * @final + * @default 0x0004 + */ + TRIANGLES: 0x0004, + /** + * @property {Number} TRIANGLE_FAN + * @final + * @default 0x0006 + */ + TRIANGLE_FAN: 0x0006, + /** + * @property {Number} TRIANGLE_STRIP + * @final + * @default 0x0005 + */ + TRIANGLE_STRIP: 0x0005, + /** + * @property {String} QUADS + * @final + */ + QUADS: 'quads', + /** + * @property {String} QUAD_STRIP + * @final + * @default quad_strip + */ + QUAD_STRIP: 'quad_strip', + /** + * @property {String} CLOSE + * @final + */ + CLOSE: 'close', + /** + * @property {String} OPEN + * @final + */ + OPEN: 'open', + /** + * @property {String} CHORD + * @final + */ + CHORD: 'chord', + /** + * @property {String} PIE + * @final + */ + PIE: 'pie', + /** + * @property {String} PROJECT + * @final + * @default square + */ + PROJECT: 'square', // PEND: careful this is counterintuitive + /** + * @property {String} SQUARE + * @final + * @default butt + */ + SQUARE: 'butt', + /** + * @property {String} ROUND + * @final + */ + ROUND: 'round', + /** + * @property {String} BEVEL + * @final + */ + BEVEL: 'bevel', + /** + * @property {String} MITER + * @final + */ + MITER: 'miter', + + // COLOR + /** + * @property {String} RGB + * @final + */ + RGB: 'rgb', + /** + * @property {String} HSB + * @final + */ + HSB: 'hsb', + /** + * @property {String} HSL + * @final + */ + HSL: 'hsl', + + // DOM EXTENSION + AUTO: 'auto', + + // INPUT + ALT: 18, + BACKSPACE: 8, + CONTROL: 17, + DELETE: 46, + DOWN_ARROW: 40, + ENTER: 13, + ESCAPE: 27, + LEFT_ARROW: 37, + OPTION: 18, + RETURN: 13, + RIGHT_ARROW: 39, + SHIFT: 16, + TAB: 9, + UP_ARROW: 38, + + // RENDERING + /** + * @property {String} BLEND + * @final + * @default source-over + */ + BLEND: 'source-over', + /** + * @property {String} ADD + * @final + * @default lighter + */ + ADD: 'lighter', + //ADD: 'add', // + //SUBTRACT: 'subtract', // + /** + * @property {String} DARKEST + * @final + */ + DARKEST: 'darken', + /** + * @property {String} LIGHTEST + * @final + * @default lighten + */ + LIGHTEST: 'lighten', + /** + * @property {String} DIFFERENCE + * @final + */ + DIFFERENCE: 'difference', + /** + * @property {String} EXCLUSION + * @final + */ + EXCLUSION: 'exclusion', + /** + * @property {String} MULTIPLY + * @final + */ + MULTIPLY: 'multiply', + /** + * @property {String} SCREEN + * @final + */ + SCREEN: 'screen', + /** + * @property {String} REPLACE + * @final + * @default copy + */ + REPLACE: 'copy', + /** + * @property {String} OVERLAY + * @final + */ + OVERLAY: 'overlay', + /** + * @property {String} HARD_LIGHT + * @final + */ + HARD_LIGHT: 'hard-light', + /** + * @property {String} SOFT_LIGHT + * @final + */ + SOFT_LIGHT: 'soft-light', + /** + * @property {String} DODGE + * @final + * @default color-dodge + */ + DODGE: 'color-dodge', + /** + * @property {String} BURN + * @final + * @default color-burn + */ + BURN: 'color-burn', + + // FILTERS + /** + * @property {String} THRESHOLD + * @final + */ + THRESHOLD: 'threshold', + /** + * @property {String} GRAY + * @final + */ + GRAY: 'gray', + /** + * @property {String} OPAQUE + * @final + */ + OPAQUE: 'opaque', + /** + * @property {String} INVERT + * @final + */ + INVERT: 'invert', + /** + * @property {String} POSTERIZE + * @final + */ + POSTERIZE: 'posterize', + /** + * @property {String} DILATE + * @final + */ + DILATE: 'dilate', + /** + * @property {String} ERODE + * @final + */ + ERODE: 'erode', + /** + * @property {String} BLUR + * @final + */ + BLUR: 'blur', + + // TYPOGRAPHY + /** + * @property {String} NORMAL + * @final + */ + NORMAL: 'normal', + /** + * @property {String} ITALIC + * @final + */ + ITALIC: 'italic', + /** + * @property {String} BOLD + * @final + */ + BOLD: 'bold', + + // TYPOGRAPHY-INTERNAL + _DEFAULT_TEXT_FILL: '#000000', + _DEFAULT_LEADMULT: 1.25, + _CTX_MIDDLE: 'middle', + + // VERTICES + LINEAR: 'linear', + QUADRATIC: 'quadratic', + BEZIER: 'bezier', + CURVE: 'curve', + + // DEVICE-ORIENTATION + /** + * @property {String} LANDSCAPE + * @final + */ + LANDSCAPE: 'landscape', + /** + * @property {String} PORTRAIT + * @final + */ + PORTRAIT: 'portrait', + + // DEFAULTS + _DEFAULT_STROKE: '#000000', + _DEFAULT_FILL: '#FFFFFF' + +}; + +},{}],54:[function(_dereq_,module,exports){ +/** + * @module Structure + * @submodule Structure + * @for p5 + * @requires constants + */ + +'use strict'; + +_dereq_('./shim'); + +// Core needs the PVariables object +var constants = _dereq_('./constants'); + +/** + * This is the p5 instance constructor. + * + * A p5 instance holds all the properties and methods related to + * a p5 sketch. It expects an incoming sketch closure and it can also + * take an optional node parameter for attaching the generated p5 canvas + * to a node. The sketch closure takes the newly created p5 instance as + * its sole argument and may optionally set preload(), setup(), and/or + * draw() properties on it for running a sketch. + * + * A p5 sketch can run in "global" or "instance" mode: + * "global" - all properties and methods are attached to the window + * "instance" - all properties and methods are bound to this p5 object + * + * @param {function} sketch a closure that can set optional preload(), + * setup(), and/or draw() properties on the + * given p5 instance + * @param {HTMLElement|boolean} [node] element to attach canvas to, if a + * boolean is passed in use it as sync + * @param {boolean} [sync] start synchronously (optional) + * @return {p5} a p5 instance + */ +var p5 = function(sketch, node, sync) { + + if (arguments.length === 2 && typeof node === 'boolean') { + sync = node; + node = undefined; + } + + ////////////////////////////////////////////// + // PUBLIC p5 PROPERTIES AND METHODS + ////////////////////////////////////////////// + + + /** + * Called directly before setup(), the preload() function is used to handle + * asynchronous loading of external files. If a preload function is + * defined, setup() will wait until any load calls within have finished. + * Nothing besides load calls should be inside preload (loadImage, + * loadJSON, loadFont, loadStrings, etc).

+ * By default the text "loading..." will be displayed. To make your own + * loading page, include an HTML element with id "p5_loading" in your + * page. More information here. + * + * @method preload + * @example + *
+ * var img; + * var c; + * function preload() { // preload() runs once + * img = loadImage('assets/laDefense.jpg'); + * } + * + * function setup() { // setup() waits until preload() is done + * img.loadPixels(); + * // get color of middle pixel + * c = img.get(img.width/2, img.height/2); + * } + * + * function draw() { + * background(c); + * image(img, 25, 25, 50, 50); + * } + *
+ * + * @alt + * nothing displayed + * + */ + + /** + * The setup() function is called once when the program starts. It's used to + * define initial environment properties such as screen size and background + * color and to load media such as images and fonts as the program starts. + * There can only be one setup() function for each program and it shouldn't + * be called again after its initial execution. + *

+ * Note: Variables declared within setup() are not accessible within other + * functions, including draw(). + * + * @method setup + * @example + *
+ * var a = 0; + * + * function setup() { + * background(0); + * noStroke(); + * fill(102); + * } + * + * function draw() { + * rect(a++%width, 10, 2, 80); + * } + *
+ * + * @alt + * nothing displayed + * + */ + + /** + * Called directly after setup(), the draw() function continuously executes + * the lines of code contained inside its block until the program is stopped + * or noLoop() is called. Note if noLoop() is called in setup(), draw() will + * still be executed once before stopping. draw() is called automatically and + * should never be called explicitly. + *

+ * It should always be controlled with noLoop(), redraw() and loop(). After + * noLoop() stops the code in draw() from executing, redraw() causes the + * code inside draw() to execute once, and loop() will cause the code + * inside draw() to resume executing continuously. + *

+ * The number of times draw() executes in each second may be controlled with + * the frameRate() function. + *

+ * There can only be one draw() function for each sketch, and draw() must + * exist if you want the code to run continuously, or to process events such + * as mousePressed(). Sometimes, you might have an empty call to draw() in + * your program, as shown in the above example. + *

+ * It is important to note that the drawing coordinate system will be reset + * at the beginning of each draw() call. If any transformations are performed + * within draw() (ex: scale, rotate, translate, their effects will be + * undone at the beginning of draw(), so transformations will not accumulate + * over time. On the other hand, styling applied (ex: fill, stroke, etc) will + * remain in effect. + * + * @method draw + * @example + *
+ * var yPos = 0; + * function setup() { // setup() runs once + * frameRate(30); + * } + * function draw() { // draw() loops forever, until stopped + * background(204); + * yPos = yPos - 1; + * if (yPos < 0) { + * yPos = height; + * } + * line(0, yPos, width, yPos); + * } + *
+ * + * @alt + * nothing displayed + * + */ + + + ////////////////////////////////////////////// + // PRIVATE p5 PROPERTIES AND METHODS + ////////////////////////////////////////////// + + this._setupDone = false; + // for handling hidpi + this._pixelDensity = Math.ceil(window.devicePixelRatio) || 1; + this._userNode = node; + this._curElement = null; + this._elements = []; + this._requestAnimId = 0; + this._preloadCount = 0; + this._isGlobal = false; + this._loop = true; + this._styles = []; + this._defaultCanvasSize = { + width: 100, + height: 100 + }; + this._events = { // keep track of user-events for unregistering later + 'mousemove': null, + 'mousedown': null, + 'mouseup': null, + 'dragend': null, + 'dragover': null, + 'click': null, + 'mouseover': null, + 'mouseout': null, + 'keydown': null, + 'keyup': null, + 'keypress': null, + 'touchstart': null, + 'touchmove': null, + 'touchend': null, + 'resize': null, + 'blur': null + }; + + this._events.wheel = null; + this._loadingScreenId = 'p5_loading'; + + if (window.DeviceOrientationEvent) { + this._events.deviceorientation = null; + } + if (window.DeviceMotionEvent && !window._isNodeWebkit) { + this._events.devicemotion = null; + } + + this._start = function () { + // Find node if id given + if (this._userNode) { + if (typeof this._userNode === 'string') { + this._userNode = document.getElementById(this._userNode); + } + } + + var userPreload = this.preload || window.preload; // look for "preload" + if (userPreload) { + + // Setup loading screen + // Set loading scfeen into dom if not present + // Otherwise displays and removes user provided loading screen + var loadingScreen = document.getElementById(this._loadingScreenId); + if(!loadingScreen){ + loadingScreen = document.createElement('div'); + loadingScreen.innerHTML = 'Loading...'; + loadingScreen.style.position = 'absolute'; + loadingScreen.id = this._loadingScreenId; + var node = this._userNode || document.body; + node.appendChild(loadingScreen); + } + // var methods = this._preloadMethods; + for (var method in this._preloadMethods){ + // default to p5 if no object defined + this._preloadMethods[method] = this._preloadMethods[method] || p5; + var obj = this._preloadMethods[method]; + //it's p5, check if it's global or instance + if (obj === p5.prototype || obj === p5){ + obj = this._isGlobal ? window : this; + } + this._registeredPreloadMethods[method] = obj[method]; + obj[method] = this._wrapPreload(obj, method); + } + + userPreload(); + this._runIfPreloadsAreDone(); + } else { + this._setup(); + this._runFrames(); + this._draw(); + } + }.bind(this); + + this._runIfPreloadsAreDone = function(){ + var context = this._isGlobal ? window : this; + if (context._preloadCount === 0) { + var loadingScreen = document.getElementById(context._loadingScreenId); + if (loadingScreen) { + loadingScreen.parentNode.removeChild(loadingScreen); + } + context._setup(); + context._runFrames(); + context._draw(); + } + }; + + this._decrementPreload = function(){ + var context = this._isGlobal ? window : this; + if(typeof context.preload === 'function'){ + context._setProperty('_preloadCount', context._preloadCount - 1); + context._runIfPreloadsAreDone(); + } + }; + + this._wrapPreload = function(obj, fnName){ + return function(){ + //increment counter + this._incrementPreload(); + //call original function + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + // args.push(this._decrementPreload.bind(this)); + return this._registeredPreloadMethods[fnName].apply(obj, args); + }.bind(this); + }; + + this._incrementPreload = function(){ + var context = this._isGlobal ? window : this; + context._setProperty('_preloadCount', context._preloadCount + 1); + }; + + this._setup = function() { + + // Always create a default canvas. + // Later on if the user calls createCanvas, this default one + // will be replaced + this.createCanvas( + this._defaultCanvasSize.width, + this._defaultCanvasSize.height, + 'p2d', + true + ); + + // return preload functions to their normal vals if switched by preload + var context = this._isGlobal ? window : this; + if (typeof context.preload === 'function') { + for (var f in this._preloadMethods) { + context[f] = this._preloadMethods[f][f]; + if (context[f] && this) { + context[f] = context[f].bind(this); + } + } + } + + // Short-circuit on this, in case someone used the library in "global" + // mode earlier + if (typeof context.setup === 'function') { + context.setup(); + } + + // unhide any hidden canvases that were created + var canvases = document.getElementsByTagName('canvas'); + for (var i = 0; i < canvases.length; i++) { + var k = canvases[i]; + if (k.dataset.hidden === 'true') { + k.style.visibility = ''; + delete(k.dataset.hidden); + } + } + this._setupDone = true; + + }.bind(this); + + this._draw = function () { + var now = window.performance.now(); + var time_since_last = now - this._lastFrameTime; + var target_time_between_frames = 1000 / this._targetFrameRate; + + // only draw if we really need to; don't overextend the browser. + // draw if we're within 5ms of when our next frame should paint + // (this will prevent us from giving up opportunities to draw + // again when it's really about time for us to do so). fixes an + // issue where the frameRate is too low if our refresh loop isn't + // in sync with the browser. note that we have to draw once even + // if looping is off, so we bypass the time delay if that + // is the case. + var epsilon = 5; + if (!this._loop || + time_since_last >= target_time_between_frames - epsilon) { + + //mandatory update values(matrixs and stack) + + this._setProperty('frameCount', this.frameCount + 1); + this.redraw(); + this._frameRate = 1000.0/(now - this._lastFrameTime); + this._lastFrameTime = now; + + // If the user is actually using mouse module, then update + // coordinates, otherwise skip. We can test this by simply + // checking if any of the mouse functions are available or not. + // NOTE : This reflects only in complete build or modular build. + if(typeof this._updateMouseCoords !== 'undefined') { + this._updateMouseCoords(); + } + } + + // get notified the next time the browser gives us + // an opportunity to draw. + if (this._loop) { + this._requestAnimId = window.requestAnimationFrame(this._draw); + } + }.bind(this); + + this._runFrames = function() { + if (this._updateInterval) { + clearInterval(this._updateInterval); + } + }.bind(this); + + this._setProperty = function(prop, value) { + this[prop] = value; + if (this._isGlobal) { + window[prop] = value; + } + }.bind(this); + + /** + * Removes the entire p5 sketch. This will remove the canvas and any + * elements created by p5.js. It will also stop the draw loop and unbind + * any properties or methods from the window global scope. It will + * leave a variable p5 in case you wanted to create a new p5 sketch. + * If you like, you can set p5 = null to erase it. + * @method remove + * @example + *
+ * function draw() { + * ellipse(50, 50, 10, 10); + * } + * + * function mousePressed() { + * remove(); // remove whole sketch on mouse press + * } + *
+ * + * @alt + * nothing displayed + * + */ + this.remove = function() { + if (this._curElement) { + + // stop draw + this._loop = false; + if (this._requestAnimId) { + window.cancelAnimationFrame(this._requestAnimId); + } + + // unregister events sketch-wide + for (var ev in this._events) { + window.removeEventListener(ev, this._events[ev]); + } + + // remove DOM elements created by p5, and listeners + for (var i=0; i
Bezier curves were developed by French + * automotive engineer Pierre Bezier, and are commonly used in computer + * graphics to define gently sloping curves. See also curve(). + * + * @method bezier + * @param {Number} x1 x-coordinate for the first anchor point + * @param {Number} y1 y-coordinate for the first anchor point + * @param {Number} x2 x-coordinate for the first control point + * @param {Number} y2 y-coordinate for the first control point + * @param {Number} x3 x-coordinate for the second control point + * @param {Number} y3 y-coordinate for the second control point + * @param {Number} x4 x-coordinate for the second anchor point + * @param {Number} y4 y-coordinate for the second anchor point + * @return {p5} the p5 object + * @example + *
+ * + * noFill(); + * stroke(255, 102, 0); + * line(85, 20, 10, 10); + * line(90, 90, 15, 80); + * stroke(0, 0, 0); + * bezier(85, 20, 10, 10, 90, 90, 15, 80); + * + *
+ * @alt + * stretched black s-shape in center with orange lines extending from end points. + * stretched black s-shape with 10 5x5 white ellipses along the shape. + * stretched black s-shape with 7 5x5 ellipses and orange lines along the shape. + * stretched black s-shape with 17 small orange lines extending from under shape. + * horseshoe shape with orange ends facing left and black curved center. + * horseshoe shape with orange ends facing left and black curved center. + * Line shaped like right-facing arrow,points move with mouse-x and warp shape. + * horizontal line that hooks downward on the right and 13 5x5 ellipses along it. + * right curving line mid-right of canvas with 7 short lines radiating from it. + */ +/** + * @method bezier + * @param {Number} z1 z-coordinate for the first anchor point + * @param {Number} z2 z-coordinate for the first control point + * @param {Number} z3 z-coordinate for the first anchor point + * @param {Number} z4 z-coordinate for the first control point + * @chainable + * @example + *
+ * + *background(0, 0, 0); + *noFill(); + *stroke(255); + *bezier(250,250,0, 100,100,0, 100,0,0, 0,100,0); + * + *
+*/ +p5.prototype.bezier = function() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if (!this._renderer._doStroke && !this._renderer._doFill) { + return this; + } + if (this._renderer.isP3D){ + args.push(bezierDetail);//adding value of bezier detail to the args array + this._renderer.bezier(args); + } else{ + this._renderer.bezier(args[0],args[1], + args[2],args[3], + args[4],args[5], + args[6],args[7]); + } + + return this; +}; + +/** + * Sets the resolution at which Beziers display. + * + * The default value is 20. + * + * @param {Number} detail resolution of the curves + * @chainable + * @example + *
+ * + * background(204); + * bezierDetail(50); + * bezier(85, 20, 10, 10, 90, 90, 15, 80); + * + *
+ * + * @alt + * stretched black s-shape with 7 5x5 ellipses and orange lines along the shape. + * + */ +p5.prototype.bezierDetail = function(d) { + bezierDetail = d; + return this; +}; + +/** + * Evaluates the Bezier at position t for points a, b, c, d. + * The parameters a and d are the first and last points + * on the curve, and b and c are the control points. + * The final parameter t varies between 0 and 1. + * This can be done once with the x coordinates and a second time + * with the y coordinates to get the location of a bezier curve at t. + * + * @method bezierPoint + * @param {Number} a coordinate of first point on the curve + * @param {Number} b coordinate of first control point + * @param {Number} c coordinate of second control point + * @param {Number} d coordinate of second point on the curve + * @param {Number} t value between 0 and 1 + * @return {Number} the value of the Bezier at position t + * @example + *
+ * + * noFill(); + * x1 = 85, x2 = 10, x3 = 90, x4 = 15; + * y1 = 20, y2 = 10, y3 = 90, y4 = 80; + * bezier(x1, y1, x2, y2, x3, y3, x4, y4); + * fill(255); + * steps = 10; + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * x = bezierPoint(x1, x2, x3, x4, t); + * y = bezierPoint(y1, y2, y3, y4, t); + * ellipse(x, y, 5, 5); + * } + * + *
+ * + * @alt + * stretched black s-shape with 17 small orange lines extending from under shape. + * + */ +p5.prototype.bezierPoint = function(a, b, c, d, t) { + var adjustedT = 1-t; + return Math.pow(adjustedT,3)*a + + 3*(Math.pow(adjustedT,2))*t*b + + 3*adjustedT*Math.pow(t,2)*c + + Math.pow(t,3)*d; +}; + +/** + * Evaluates the tangent to the Bezier at position t for points a, b, c, d. + * The parameters a and d are the first and last points + * on the curve, and b and c are the control points. + * The final parameter t varies between 0 and 1. + * + * @method bezierTangent + * @param {Number} a coordinate of first point on the curve + * @param {Number} b coordinate of first control point + * @param {Number} c coordinate of second control point + * @param {Number} d coordinate of second point on the curve + * @param {Number} t value between 0 and 1 + * @return {Number} the tangent at position t + * @example + *
+ * + * noFill(); + * bezier(85, 20, 10, 10, 90, 90, 15, 80); + * steps = 6; + * fill(255); + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * // Get the location of the point + * x = bezierPoint(85, 10, 90, 15, t); + * y = bezierPoint(20, 10, 90, 80, t); + * // Get the tangent points + * tx = bezierTangent(85, 10, 90, 15, t); + * ty = bezierTangent(20, 10, 90, 80, t); + * // Calculate an angle from the tangent points + * a = atan2(ty, tx); + * a += PI; + * stroke(255, 102, 0); + * line(x, y, cos(a)*30 + x, sin(a)*30 + y); + * // The following line of code makes a line + * // inverse of the above line + * //line(x, y, cos(a)*-30 + x, sin(a)*-30 + y); + * stroke(0); + * ellipse(x, y, 5, 5); + * } + * + *
+ * + *
+ * + * noFill(); + * bezier(85, 20, 10, 10, 90, 90, 15, 80); + * stroke(255, 102, 0); + * steps = 16; + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * x = bezierPoint(85, 10, 90, 15, t); + * y = bezierPoint(20, 10, 90, 80, t); + * tx = bezierTangent(85, 10, 90, 15, t); + * ty = bezierTangent(20, 10, 90, 80, t); + * a = atan2(ty, tx); + * a -= HALF_PI; + * line(x, y, cos(a)*8 + x, sin(a)*8 + y); + * } + * + *
+ * + * @alt + * s-shaped line with 17 short orange lines extending from underside of shape + * + */ +p5.prototype.bezierTangent = function(a, b, c, d, t) { + var adjustedT = 1-t; + return 3*d*Math.pow(t,2) - + 3*c*Math.pow(t,2) + + 6*c*adjustedT*t - + 6*b*adjustedT*t + + 3*b*Math.pow(adjustedT,2) - + 3*a*Math.pow(adjustedT,2); +}; + +/** + * Draws a curved line on the screen between two points, given as the + * middle four parameters. The first two parameters are a control point, as + * if the curve came from this point even though it's not drawn. The last + * two parameters similarly describe the other control point.

+ * Longer curves can be created by putting a series of curve() functions + * together or using curveVertex(). An additional function called + * curveTightness() provides control for the visual quality of the curve. + * The curve() function is an implementation of Catmull-Rom splines. + * + * @method curve + * @param {Number} x1 x-coordinate for the beginning control point + * @param {Number} y1 y-coordinate for the beginning control point + * @param {Number} x2 x-coordinate for the first point + * @param {Number} y2 y-coordinate for the first point + * @param {Number} x3 x-coordinate for the second point + * @param {Number} y3 y-coordinate for the second point + * @param {Number} x4 x-coordinate for the ending control point + * @param {Number} y4 y-coordinate for the ending control point + * @return {p5} the p5 object + * @example + *
+ * + * noFill(); + * stroke(255, 102, 0); + * curve(5, 26, 5, 26, 73, 24, 73, 61); + * stroke(0); + * curve(5, 26, 73, 24, 73, 61, 15, 65); + * stroke(255, 102, 0); + * curve(73, 24, 73, 61, 15, 65, 15, 65); + * + *
+ *
+ * + * // Define the curve points as JavaScript objects + * p1 = {x: 5, y: 26}, p2 = {x: 73, y: 24} + * p3 = {x: 73, y: 61}, p4 = {x: 15, y: 65} + * noFill(); + * stroke(255, 102, 0); + * curve(p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y) + * stroke(0); + * curve(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y) + * stroke(255, 102, 0); + * curve(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, p4.x, p4.y) + * + *
+ * + * @alt + * horseshoe shape with orange ends facing left and black curved center. + * horseshoe shape with orange ends facing left and black curved center. + * + */ +/** + * @method curve + * @param {Number} z1 z-coordinate for the beginning control point + * @param {Number} z2 z-coordinate for the first point + * @param {Number} z3 z-coordinate for the second point + * @param {Number} z4 z-coordinate for the ending control point + * @chainable + * @example + *
+ * + * noFill(); + * stroke(255, 102, 0); + * curve(5,26,0, 5,26,0, 73,24,0, 73,61,0); + * stroke(0); + * curve(5,26,0, 73,24,0, 73,61,0, 15,65,0); + * stroke(255, 102, 0); + * curve(73,24,0, 73,61,0, 15,65,0, 15,65,0); + * + *
+ * + * @alt + * curving black and orange lines. + */ +p5.prototype.curve = function() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if (!this._renderer._doStroke) { + return this; + } + if (this._renderer.isP3D){ + args.push(curveDetail); + this._renderer.curve(args); + } else{ + this._renderer.curve(args[0],args[1], + args[2],args[3], + args[4],args[5], + args[6],args[7]); + } + return this; +}; + +/** + * Sets the resolution at which curves display. + * + * The default value is 20. + * + * @param {Number} resolution of the curves + * @chainable + * @example + *
+ * + * background(204); + * curveDetail(20); + * curve(5, 26, 5, 26, 73, 24, 73, 61); + * + *
+ * + * @alt + * white arch shape in top-mid canvas. + * + */ +p5.prototype.curveDetail = function(d) { + curveDetail = d; + return this; +}; + +/** + * Modifies the quality of forms created with curve() and curveVertex(). + * The parameter tightness determines how the curve fits to the vertex + * points. The value 0.0 is the default value for tightness (this value + * defines the curves to be Catmull-Rom splines) and the value 1.0 connects + * all the points with straight lines. Values within the range -5.0 and 5.0 + * will deform the curves but will leave them recognizable and as values + * increase in magnitude, they will continue to deform. + * + * @method curveTightness + * @param {Number} amount of deformation from the original vertices + * @chainable + * @example + *
+ * + * // Move the mouse left and right to see the curve change + * + * function setup() { + * createCanvas(100, 100); + * noFill(); + * } + * + * function draw() { + * background(204); + * var t = map(mouseX, 0, width, -5, 5); + * curveTightness(t); + * beginShape(); + * curveVertex(10, 26); + * curveVertex(10, 26); + * curveVertex(83, 24); + * curveVertex(83, 61); + * curveVertex(25, 65); + * curveVertex(25, 65); + * endShape(); + * } + * + *
+ * + * @alt + * Line shaped like right-facing arrow,points move with mouse-x and warp shape. + */ +p5.prototype.curveTightness = function (t) { + this._renderer._curveTightness = t; +}; + +/** + * Evaluates the curve at position t for points a, b, c, d. + * The parameter t varies between 0 and 1, a and d are points + * on the curve, and b and c are the control points. + * This can be done once with the x coordinates and a second time + * with the y coordinates to get the location of a curve at t. + * + * @method curvePoint + * @param {Number} a coordinate of first point on the curve + * @param {Number} b coordinate of first control point + * @param {Number} c coordinate of second control point + * @param {Number} d coordinate of second point on the curve + * @param {Number} t value between 0 and 1 + * @return {Number} bezier value at position t + * @example + *
+ * + * noFill(); + * curve(5, 26, 5, 26, 73, 24, 73, 61); + * curve(5, 26, 73, 24, 73, 61, 15, 65); + * fill(255); + * ellipseMode(CENTER); + * steps = 6; + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * x = curvePoint(5, 5, 73, 73, t); + * y = curvePoint(26, 26, 24, 61, t); + * ellipse(x, y, 5, 5); + * x = curvePoint(5, 73, 73, 15, t); + * y = curvePoint(26, 24, 61, 65, t); + * ellipse(x, y, 5, 5); + * } + * + *
+ * + *line hooking down to right-bottom with 13 5x5 white ellipse points + */ +p5.prototype.curvePoint = function(a, b, c, d, t) { + var t3 = t*t*t, + t2 = t*t, + f1 = -0.5 * t3 + t2 - 0.5 * t, + f2 = 1.5 * t3 - 2.5 * t2 + 1.0, + f3 = -1.5 * t3 + 2.0 * t2 + 0.5 * t, + f4 = 0.5 * t3 - 0.5 * t2; + return a*f1 + b*f2 + c*f3 + d*f4; +}; + +/** + * Evaluates the tangent to the curve at position t for points a, b, c, d. + * The parameter t varies between 0 and 1, a and d are points on the curve, + * and b and c are the control points. + * + * @method curveTangent + * @param {Number} a coordinate of first point on the curve + * @param {Number} b coordinate of first control point + * @param {Number} c coordinate of second control point + * @param {Number} d coordinate of second point on the curve + * @param {Number} t value between 0 and 1 + * @return {Number} the tangent at position t + * @example + *
+ * + * noFill(); + * curve(5, 26, 73, 24, 73, 61, 15, 65); + * steps = 6; + * for (i = 0; i <= steps; i++) { + * t = i / steps; + * x = curvePoint(5, 73, 73, 15, t); + * y = curvePoint(26, 24, 61, 65, t); + * //ellipse(x, y, 5, 5); + * tx = curveTangent(5, 73, 73, 15, t); + * ty = curveTangent(26, 24, 61, 65, t); + * a = atan2(ty, tx); + * a -= PI/2.0; + * line(x, y, cos(a)*8 + x, sin(a)*8 + y); + * } + * + *
+ * + * @alt + *right curving line mid-right of canvas with 7 short lines radiating from it. + */ +p5.prototype.curveTangent = function(a, b,c, d, t) { + var t2 = t*t, + f1 = (-3*t2)/2 + 2*t - 0.5, + f2 = (9*t2)/2 - 5*t, + f3 = (-9*t2)/2 + 4*t + 0.5, + f4 = (3*t2)/2 - t; + return a*f1 + b*f2 + c*f3 + d*f4; +}; + +module.exports = p5; + +},{"./core":54,"./error_helpers":57}],56:[function(_dereq_,module,exports){ +/** + * @module Environment + * @submodule Environment + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var C = _dereq_('./constants'); + +var standardCursors = [C.ARROW, C.CROSS, C.HAND, C.MOVE, C.TEXT, C.WAIT]; + +p5.prototype._frameRate = 0; +p5.prototype._lastFrameTime = window.performance.now(); +p5.prototype._targetFrameRate = 60; + +var _windowPrint = window.print; + + +if (window.console && console.log) { + /** + * The print() function writes to the console area of your browser. + * This function is often helpful for looking at the data a program is + * producing. This function creates a new line of text for each call to + * the function. Individual elements can be + * separated with quotes ("") and joined with the addition operator (+). + *

+ * While print() is similar to console.log(), it does not directly map to + * it in order to simulate easier to understand behavior than + * console.log(). Due to this, it is slower. For fastest results, use + * console.log(). + * + * @method print + * @param {Any} contents any combination of Number, String, Object, Boolean, + * Array to print + * @example + *
+ * var x = 10; + * print("The value of x is " + x); + * // prints "The value of x is 10" + *
+ * @alt + * default grey canvas + */ + // Converts passed args into a string and then parses that string to + // simulate synchronous behavior. This is a hack and is gross. + // Since this will not work on all objects, particularly circular + // structures, simply console.log() on error. + p5.prototype.print = function(args) { + try { + if (arguments.length === 0) { + _windowPrint(); + } + else if (arguments.length > 1) { + console.log.apply(console, arguments); + } else { + var newArgs = JSON.parse(JSON.stringify(args)); + if (JSON.stringify(newArgs)==='{}'){ + console.log(args); + } else { + console.log(newArgs); + } + } + } catch(err) { + console.log(args); + } + }; +} else { + p5.prototype.print = function() {}; +} + + +/** + * The system variable frameCount contains the number of frames that have + * been displayed since the program started. Inside setup() the value is 0, + * after the first iteration of draw it is 1, etc. + * + * @property {Number} frameCount + * @readOnly + * @example + *
+ * function setup() { + * frameRate(30); + * textSize(20); + * textSize(30); + * textAlign(CENTER); + * } + * + * function draw() { + * background(200); + * text(frameCount, width/2, height/2); + * } + *
+ * + * @alt + * numbers rapidly counting upward with frame count set to 30. + * + */ +p5.prototype.frameCount = 0; + +/** + * Confirms if the window a p5.js program is in is "focused," meaning that + * the sketch will accept mouse or keyboard input. This variable is + * "true" if the window is focused and "false" if not. + * + * @property {Boolean} focused + * @readOnly + * @example + *
+ * // To demonstrate, put two windows side by side. + * // Click on the window that the p5 sketch isn't in! + * function draw() { + * background(200); + * noStroke(); + * fill(0, 200, 0); + * ellipse(25, 25, 50, 50); + * + * if (!focused) { // or "if (focused === false)" + * stroke(200,0,0); + * line(0, 0, 100, 100); + * line(100, 0, 0, 100); + * } + * } + *
+ * + * @alt + * green 50x50 ellipse at top left. Red X covers canvas when page focus changes + * + */ +p5.prototype.focused = (document.hasFocus()); + +/** + * Sets the cursor to a predefined symbol or an image, or makes it visible + * if already hidden. If you are trying to set an image as the cursor, the + * recommended size is 16x16 or 32x32 pixels. It is not possible to load an + * image as the cursor if you are exporting your program for the Web, and not + * all MODES work with all browsers. The values for parameters x and y must + * be less than the dimensions of the image. + * + * @method cursor + * @param {String|Constant} type either ARROW, CROSS, HAND, MOVE, TEXT, or + * WAIT, or path for image + * @param {Number} [x] the horizontal active spot of the cursor + * @param {Number} [y] the vertical active spot of the cursor + * @example + *
+ * // Move the mouse left and right across the image + * // to see the cursor change from a cross to a hand + * function draw() { + * line(width/2, 0, width/2, height); + * if (mouseX < 50) { + * cursor(CROSS); + * } else { + * cursor(HAND); + * } + * } + *
+ * + * @alt + * horizontal line divides canvas. cursor on left is a cross, right is hand. + * + */ +p5.prototype.cursor = function(type, x, y) { + var cursor = 'auto'; + var canvas = this._curElement.elt; + if (standardCursors.indexOf(type) > -1) { + // Standard css cursor + cursor = type; + } else if (typeof type === 'string') { + var coords = ''; + if (x && y && (typeof x === 'number' && typeof y === 'number')) { + // Note that x and y values must be unit-less positive integers < 32 + // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor + coords = x + ' ' + y; + } + if ((type.substring(0, 7) === 'http://') || + (type.substring(0, 8) === 'https://')) { + // Image (absolute url) + cursor = 'url(' + type + ') ' + coords + ', auto'; + } else if (/\.(cur|jpg|jpeg|gif|png|CUR|JPG|JPEG|GIF|PNG)$/.test(type)) { + // Image file (relative path) - Separated for performance reasons + cursor = 'url(' + type + ') ' + coords + ', auto'; + } else { + // Any valid string for the css cursor property + cursor = type; + } + } + canvas.style.cursor = cursor; +}; + +/** + * Specifies the number of frames to be displayed every second. For example, + * the function call frameRate(30) will attempt to refresh 30 times a second. + * If the processor is not fast enough to maintain the specified rate, the + * frame rate will not be achieved. Setting the frame rate within setup() is + * recommended. The default rate is 60 frames per second. This is the same as + * setFrameRate(val). + *

+ * Calling frameRate() with no arguments returns the current framerate. The + * draw function must run at least once before it will return a value. This + * is the same as getFrameRate(). + *

+ * Calling frameRate() with arguments that are not of the type numbers + * or are non positive also returns current framerate. + * + * @method frameRate + * @param {Number} fps number of frames to be displayed every second + * @chainable + */ +/** + * @method frameRate + * @return {Number} current frameRate + * @example + * + *
+ * var rectX = 0; + * var fr = 30; //starting FPS + * var clr; + * + * function setup() { + * background(200); + * frameRate(fr); // Attempt to refresh at starting FPS + * clr = color(255,0,0); + * } + * + * function draw() { + * background(200); + * rectX = rectX += 1; // Move Rectangle + * + * if (rectX >= width) { // If you go off screen. + * if (fr == 30) { + * clr = color(0,0,255); + * fr = 10; + * frameRate(fr); // make frameRate 10 FPS + * } else { + * clr = color(255,0,0); + * fr = 30; + * frameRate(fr); // make frameRate 30 FPS + * } + * rectX = 0; + * } + * fill(clr); + * rect(rectX, 40, 20,20); + * } + *
+ * + * @alt + * blue rect moves left to right, followed by red rect moving faster. Loops. + * + */ +p5.prototype.frameRate = function(fps) { + if (typeof fps !== 'number' || fps < 0) { + return this._frameRate; + } else { + this._setProperty('_targetFrameRate', fps); + this._runFrames(); + return this; + } +}; +/** + * Returns the current framerate. + * + * @return {Number} current frameRate + */ +p5.prototype.getFrameRate = function() { + return this.frameRate(); +}; + +/** + * Specifies the number of frames to be displayed every second. For example, + * the function call frameRate(30) will attempt to refresh 30 times a second. + * If the processor is not fast enough to maintain the specified rate, the + * frame rate will not be achieved. Setting the frame rate within setup() is + * recommended. The default rate is 60 frames per second. + * + * Calling frameRate() with no arguments returns the current framerate. + * + * @param {Number} [fps] number of frames to be displayed every second + */ +p5.prototype.setFrameRate = function(fps) { + return this.frameRate(fps); +}; + +/** + * Hides the cursor from view. + * + * @method noCursor + * @example + *
+ * function setup() { + * noCursor(); + * } + * + * function draw() { + * background(200); + * ellipse(mouseX, mouseY, 10, 10); + * } + *
+ * + * + * @alt + * cursor becomes 10x 10 white ellipse the moves with mouse x and y. + * + */ +p5.prototype.noCursor = function() { + this._curElement.elt.style.cursor = 'none'; +}; + + +/** + * System variable that stores the width of the entire screen display. This + * is used to run a full-screen program on any display size. + * + * @property {Number} displayWidth + * @readOnly + * @example + *
+ * createCanvas(displayWidth, displayHeight); + *
+ * + * @alt + * cursor becomes 10x 10 white ellipse the moves with mouse x and y. + * + */ +p5.prototype.displayWidth = screen.width; + +/** + * System variable that stores the height of the entire screen display. This + * is used to run a full-screen program on any display size. + * + * @property {Number} displayHeight + * @readOnly + * @example + *
+ * createCanvas(displayWidth, displayHeight); + *
+ * + * @alt + * no display. + * + */ +p5.prototype.displayHeight = screen.height; + +/** + * System variable that stores the width of the inner window, it maps to + * window.innerWidth. + * + * @property {Number} windowWidth + * @readOnly + * @example + *
+ * createCanvas(windowWidth, windowHeight); + *
+ * + * @alt + * no display. + * + */ +p5.prototype.windowWidth = getWindowWidth(); +/** + * System variable that stores the height of the inner window, it maps to + * window.innerHeight. + * + * @property {Number} windowHeight + * @readOnly + * @example + *
+ * createCanvas(windowWidth, windowHeight); + *
+*@alt + * no display. + * +*/ +p5.prototype.windowHeight = getWindowHeight(); + +/** + * The windowResized() function is called once every time the browser window + * is resized. This is a good place to resize the canvas or do any other + * adjustments to accommodate the new window size. + * + * @method windowResized + * @example + *
+ * function setup() { + * createCanvas(windowWidth, windowHeight); + * } + * + * function draw() { + * background(0, 100, 200); + * } + * + * function windowResized() { + * resizeCanvas(windowWidth, windowHeight); + * } + *
+ * @alt + * no display. + */ +p5.prototype._onresize = function(e){ + this._setProperty('windowWidth', getWindowWidth()); + this._setProperty('windowHeight', getWindowHeight()); + var context = this._isGlobal ? window : this; + var executeDefault; + if (typeof context.windowResized === 'function') { + executeDefault = context.windowResized(e); + if (executeDefault !== undefined && !executeDefault) { + e.preventDefault(); + } + } +}; + +function getWindowWidth() { + return window.innerWidth || + document.documentElement && document.documentElement.clientWidth || + document.body && document.body.clientWidth || + 0; +} + +function getWindowHeight() { + return window.innerHeight || + document.documentElement && document.documentElement.clientHeight || + document.body && document.body.clientHeight || + 0; +} + +/** + * System variable that stores the width of the drawing canvas. This value + * is set by the first parameter of the createCanvas() function. + * For example, the function call createCanvas(320, 240) sets the width + * variable to the value 320. The value of width defaults to 100 if + * createCanvas() is not used in a program. + * + * @property {Number} width + * @readOnly + */ +p5.prototype.width = 0; + +/** + * System variable that stores the height of the drawing canvas. This value + * is set by the second parameter of the createCanvas() function. For + * example, the function call createCanvas(320, 240) sets the height + * variable to the value 240. The value of height defaults to 100 if + * createCanvas() is not used in a program. + * + * @property {Number} height + * @readOnly + */ +p5.prototype.height = 0; + +/** + * If argument is given, sets the sketch to fullscreen or not based on the + * value of the argument. If no argument is given, returns the current + * fullscreen state. Note that due to browser restrictions this can only + * be called on user input, for example, on mouse press like the example + * below. + * + * @method fullscreen + * @param {Boolean} [val] whether the sketch should be in fullscreen mode + * or not + * @return {Boolean} current fullscreen state + * @example + *
+ * + * // Clicking in the box toggles fullscreen on and off. + * function setup() { + * background(200); + * } + * function mousePressed() { + * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) { + * var fs = fullscreen(); + * fullscreen(!fs); + * } + * } + * + *
+ * + * @alt + * no display. + * + */ +p5.prototype.fullscreen = function(val) { + // no arguments, return fullscreen or not + if (typeof val === 'undefined') { + return document.fullscreenElement || + document.webkitFullscreenElement || + document.mozFullScreenElement || + document.msFullscreenElement; + } else { // otherwise set to fullscreen or not + if (val) { + launchFullscreen(document.documentElement); + } else { + exitFullscreen(); + } + } +}; + +/** + * Sets the pixel scaling for high pixel density displays. By default + * pixel density is set to match display density, call pixelDensity(1) + * to turn this off. Calling pixelDensity() with no arguments returns + * the current pixel density of the sketch. + * + * + * @method pixelDensity + * @param {Number} [val] whether or how much the sketch should scale + * @returns {Number} current pixel density of the sketch + * @example + *
+ * + * function setup() { + * pixelDensity(1); + * createCanvas(100, 100); + * background(200); + * ellipse(width/2, height/2, 50, 50); + * } + * + *
+ *
+ * + * function setup() { + * pixelDensity(3.0); + * createCanvas(100, 100); + * background(200); + * ellipse(width/2, height/2, 50, 50); + * } + * + *
+ * + * @alt + * fuzzy 50x50 white ellipse with black outline in center of canvas. + * sharp 50x50 white ellipse with black outline in center of canvas. + */ +p5.prototype.pixelDensity = function(val) { + if (typeof val === 'number') { + this._pixelDensity = val; + } else { + return this._pixelDensity; + } + this.resizeCanvas(this.width, this.height, true); +}; + +/** + * Returns the pixel density of the current display the sketch is running on. + * + * @method displayDensity + * @returns {Number} current pixel density of the display + * @example + *
+ * + * function setup() { + * var density = displayDensity(); + * pixelDensity(density); + * createCanvas(100, 100); + * background(200); + * ellipse(width/2, height/2, 50, 50); + * } + * + *
+ * + * @alt + * 50x50 white ellipse with black outline in center of canvas. + */ +p5.prototype.displayDensity = function() { + return window.devicePixelRatio; +}; + +function launchFullscreen(element) { + var enabled = document.fullscreenEnabled || + document.webkitFullscreenEnabled || + document.mozFullScreenEnabled || + document.msFullscreenEnabled; + if (!enabled) { + throw new Error('Fullscreen not enabled in this browser.'); + } + if(element.requestFullscreen) { + element.requestFullscreen(); + } else if(element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + } else if(element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } else if(element.msRequestFullscreen) { + element.msRequestFullscreen(); + } +} + +function exitFullscreen() { + if(document.exitFullscreen) { + document.exitFullscreen(); + } else if(document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if(document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } +} + + +/** + * Gets the current URL. + * @method getURL + * @return {String} url + * @example + *
+ * + * var url; + * var x = 100; + * + * function setup() { + * fill(0); + * noStroke(); + * url = getURL(); + * } + * + * function draw() { + * background(200); + * text(url, x, height/2); + * x--; + * } + * + *
+ * + * @alt + * current url (http://p5js.org/reference/#/p5/getURL) moves right to left. + * + */ +p5.prototype.getURL = function() { + return location.href; +}; +/** + * Gets the current URL path as an array. + * @method getURLPath + * @return {String[]} path components + * @example + *
+ * function setup() { + * var urlPath = getURLPath(); + * for (var i=0; i<urlPath.length; i++) { + * text(urlPath[i], 10, i*20+20); + * } + * } + *
+ * + * @alt + *no display + * + */ +p5.prototype.getURLPath = function() { + return location.pathname.split('/').filter(function(v){return v!=='';}); +}; +/** + * Gets the current URL params as an Object. + * @method getURLParams + * @return {Object} URL params + * @example + *
+ * + * // Example: http://p5js.org?year=2014&month=May&day=15 + * + * function setup() { + * var params = getURLParams(); + * text(params.day, 10, 20); + * text(params.month, 10, 40); + * text(params.year, 10, 60); + * } + * + *
+ * @alt + * no display. + * + */ +p5.prototype.getURLParams = function() { + var re = /[?&]([^&=]+)(?:[&=])([^&=]+)/gim; + var m; + var v={}; + while ((m = re.exec(location.search)) != null) { + if (m.index === re.lastIndex) { + re.lastIndex++; + } + v[m[1]]=m[2]; + } + return v; +}; + +module.exports = p5; + +},{"./constants":53,"./core":54}],57:[function(_dereq_,module,exports){ +/** + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var doFriendlyWelcome = false; // TEMP until we get it all working LM + +// -- Borrowed from jQuery 1.11.3 -- +var class2type = {}; +var toString = class2type.toString; +var names = ['Boolean', 'Number', 'String', 'Function', + 'Array', 'Date', 'RegExp', 'Object', 'Error']; +for (var n=0; n p5.js says: '+message+'%c'+ + // '[https://github.com/processing/p5.js/wiki/Local-server]', + // 'background-color:' + color + ';color:#FFF;', + // 'background-color:transparent;color:' + color +';', + // 'background-color:' + color + ';color:#FFF;', + // 'background-color:transparent;color:' + color +';' + // ); + // } + // else{ + // console.log( + // '%c> p5.js says: '+message+'%c [http://p5js.org/reference/#p5/'+func+ + // ']', 'background-color:' + color + ';color:#FFF;', + // 'background-color:transparent;color:' + color +';' + // ); + // } +} + +var errorCases = { + '0': { + fileType: 'image', + method: 'loadImage', + message: ' hosting the image online,' + }, + '1': { + fileType: 'XML file', + method: 'loadXML' + }, + '2': { + fileType: 'table file', + method: 'loadTable' + }, + '3': { + fileType: 'text file', + method: 'loadStrings' + }, + '4': { + fileType: 'font', + method: 'loadFont', + message: ' hosting the font online,' + }, +}; +p5._friendlyFileLoadError = function (errorType, filePath) { + var errorInfo = errorCases[ errorType ]; + var message = 'It looks like there was a problem' + + ' loading your ' + errorInfo.fileType + '.' + + ' Try checking if the file path%c [' + filePath + '] %cis correct,' + + (errorInfo.message || '') + ' or running a local server.'; + report(message, errorInfo.method, FILE_LOAD); +}; + +function friendlyWelcome() { + // p5.js brand - magenta: #ED225D + var astrixBgColor = 'transparent'; + var astrixTxtColor = '#ED225D'; + var welcomeBgColor = '#ED225D'; + var welcomeTextColor = 'white'; + console.log( + '%c _ \n'+ + ' /\\| |/\\ \n'+ + ' \\ ` \' / \n'+ + ' / , . \\ \n'+ + ' \\/|_|\\/ '+ + '\n\n%c> p5.js says: Welcome! '+ + 'This is your friendly debugger. ' + + 'To turn me off switch to using “p5.min.js”.', + 'background-color:'+astrixBgColor+';color:' + astrixTxtColor +';', + 'background-color:'+welcomeBgColor+';color:' + welcomeTextColor +';' + ); +} + +/** + * Prints out all the colors in the color pallete with white text. + * For color blindness testing. + */ +/* function testColors() { + var str = 'A box of biscuits, a box of mixed biscuits and a biscuit mixer'; + report(str, 'print', '#ED225D'); // p5.js magenta + report(str, 'print', '#2D7BB6'); // p5.js blue + report(str, 'print', '#EE9900'); // p5.js orange + report(str, 'print', '#A67F59'); // p5.js light brown + report(str, 'print', '#704F21'); // p5.js gold + report(str, 'print', '#1CC581'); // auto cyan + report(str, 'print', '#FF6625'); // auto orange + report(str, 'print', '#79EB22'); // auto green + report(str, 'print', '#B40033'); // p5.js darkened magenta + report(str, 'print', '#084B7F'); // p5.js darkened blue + report(str, 'print', '#945F00'); // p5.js darkened orange + report(str, 'print', '#6B441D'); // p5.js darkened brown + report(str, 'print', '#2E1B00'); // p5.js darkened gold + report(str, 'print', '#008851'); // auto dark cyan + report(str, 'print', '#C83C00'); // auto dark orange + report(str, 'print', '#4DB200'); // auto dark green +} */ + +// This is a lazily-defined list of p5 symbols that may be +// misused by beginners at top-level code, outside of setup/draw. We'd like +// to detect these errors and help the user by suggesting they move them +// into setup/draw. +// +// For more details, see https://github.com/processing/p5.js/issues/1121. +var misusedAtTopLevelCode = null; +var FAQ_URL = 'https://github.com/processing/p5.js/wiki/' + + 'Frequently-Asked-Questions' + + '#why-cant-i-assign-variables-using-p5-functions-and-' + + 'variables-before-setup'; + +function defineMisusedAtTopLevelCode() { + var uniqueNamesFound = {}; + + var getSymbols = function(obj) { + return Object.getOwnPropertyNames(obj).filter(function(name) { + if (name[0] === '_') { + return false; + } + if (name in uniqueNamesFound) { + return false; + } + + uniqueNamesFound[name] = true; + + return true; + }).map(function(name) { + var type; + + if (typeof(obj[name]) === 'function') { + type = 'function'; + } else if (name === name.toUpperCase()) { + type = 'constant'; + } else { + type = 'variable'; + } + + return {name: name, type: type}; + }); + }; + + misusedAtTopLevelCode = [].concat( + getSymbols(p5.prototype), + // At present, p5 only adds its constants to p5.prototype during + // construction, which may not have happened at the time a + // ReferenceError is thrown, so we'll manually add them to our list. + getSymbols(_dereq_('./constants')) + ); + + // This will ultimately ensure that we report the most specific error + // possible to the user, e.g. advising them about HALF_PI instead of PI + // when their code misuses the former. + misusedAtTopLevelCode.sort(function(a, b) { + return b.name.length - a.name.length; + }); +} + +function helpForMisusedAtTopLevelCode(e, log) { + if (!log) { + log = console.log.bind(console); + } + + if (!misusedAtTopLevelCode) { + defineMisusedAtTopLevelCode(); + } + + // If we find that we're logging lots of false positives, we can + // uncomment the following code to avoid displaying anything if the + // user's code isn't likely to be using p5's global mode. (Note that + // setup/draw are more likely to be defined due to JS function hoisting.) + // + //if (!('setup' in window || 'draw' in window)) { + // return; + //} + + misusedAtTopLevelCode.some(function(symbol) { + // Note that while just checking for the occurrence of the + // symbol name in the error message could result in false positives, + // a more rigorous test is difficult because different browsers + // log different messages, and the format of those messages may + // change over time. + // + // For example, if the user uses 'PI' in their code, it may result + // in any one of the following messages: + // + // * 'PI' is undefined (Microsoft Edge) + // * ReferenceError: PI is undefined (Firefox) + // * Uncaught ReferenceError: PI is not defined (Chrome) + + if (e.message && e.message.match('\\W?'+symbol.name+'\\W') !== null) { + log('%cDid you just try to use p5.js\'s ' + symbol.name + + (symbol.type === 'function' ? '() ' : ' ') + symbol.type + + '? If so, you may want to ' + + 'move it into your sketch\'s setup() function.\n\n' + + 'For more details, see: ' + FAQ_URL, + 'color: #B40033' /* Dark magenta */); + return true; + } + }); +} + +// Exposing this primarily for unit testing. +p5.prototype._helpForMisusedAtTopLevelCode = helpForMisusedAtTopLevelCode; + +if (document.readyState !== 'complete') { + window.addEventListener('error', helpForMisusedAtTopLevelCode, false); + + // Our job is only to catch ReferenceErrors that are thrown when + // global (non-instance mode) p5 APIs are used at the top-level + // scope of a file, so we'll unbind our error listener now to make + // sure we don't log false positives later. + window.addEventListener('load', function() { + window.removeEventListener('error', helpForMisusedAtTopLevelCode, false); + }); +} + +module.exports = p5; + +},{"./constants":53,"./core":54}],58:[function(_dereq_,module,exports){ + +var p5 = _dereq_('../core/core'); + + +/** + * _globalInit + * + * TODO: ??? + * if sketch is on window + * assume "global" mode + * and instantiate p5 automatically + * otherwise do nothing + * + * @return {Undefined} + */ +var _globalInit = function() { + if (!window.PHANTOMJS && !window.mocha) { + // If there is a setup or draw function on the window + // then instantiate p5 in "global" mode + if(((window.setup && typeof window.setup === 'function') || + (window.draw && typeof window.draw === 'function')) && + !p5.instance) { + new p5(); + } + } +}; + +// TODO: ??? +if (document.readyState === 'complete') { + _globalInit(); +} else { + window.addEventListener('load', _globalInit , false); +} +},{"../core/core":54}],59:[function(_dereq_,module,exports){ +/** + * @module DOM + * @submodule DOM + * @for p5.Element + */ + +var p5 = _dereq_('./core'); + +/** + * Base class for all elements added to a sketch, including canvas, + * graphics buffers, and other HTML elements. Methods in blue are + * included in the core functionality, methods in brown are added + * with the p5.dom + * library. + * It is not called directly, but p5.Element + * objects are created by calling createCanvas, createGraphics, + * or in the p5.dom library, createDiv, createImg, createInput, etc. + * + * @class p5.Element + * @constructor + * @param {String} elt DOM node that is wrapped + * @param {p5} [pInst] pointer to p5 instance + */ +p5.Element = function(elt, pInst) { + /** + * Underlying HTML element. All normal HTML methods can be called on this. + * + * @property elt + * @readOnly + */ + this.elt = elt; + this._pInst = pInst; + this._events = {}; + this.width = this.elt.offsetWidth; + this.height = this.elt.offsetHeight; +}; + +/** + * + * Attaches the element to the parent specified. A way of setting + * the container for the element. Accepts either a string ID, DOM + * node, or p5.Element. If no arguments given, parent node is returned. + * For more ways to position the canvas, see the + * + * positioning the canvas wiki page. + * + * @method parent + * @param {String|p5.Element|Object} parent the ID, DOM node, or p5.Element + * of desired parent element + * @chainable + */ +/** + * @method parent + * @return {p5.Element} + * + * @example + *
+ * // in the html file: + * <div id="myContainer"></div> + * // in the js file: + * var cnv = createCanvas(100, 100); + * cnv.parent("myContainer"); + *
+ *
+ * var div0 = createDiv('this is the parent'); + * var div1 = createDiv('this is the child'); + * div1.parent(div0); // use p5.Element + *
+ *
+ * var div0 = createDiv('this is the parent'); + * div0.id('apples'); + * var div1 = createDiv('this is the child'); + * div1.parent('apples'); // use id + *
+ *
+ * var elt = document.getElementById('myParentDiv'); + * var div1 = createDiv('this is the child'); + * div1.parent(elt); // use element from page + *
+ * + * @alt + * no display. + * + */ +p5.Element.prototype.parent = function(p) { + if (arguments.length === 0){ + return this.elt.parentNode; + } else { + if (typeof p === 'string') { + if (p[0] === '#') { + p = p.substring(1); + } + p = document.getElementById(p); + } else if (p instanceof p5.Element) { + p = p.elt; + } + p.appendChild(this.elt); + return this; + } +}; + +/** + * + * Sets the ID of the element. If no ID argument is passed in, it instead + * returns the current ID of the element. + * + * @method id + * @param {String} id ID of the element + * @chainable + */ +/** + * @method id + * @return {String} the id of the element + * + * @example + *
+ * function setup() { + * var cnv = createCanvas(100, 100); + * // Assigns a CSS selector ID to + * // the canvas element. + * cnv.id("mycanvas"); + * } + *
+ * + * @alt + * no display. + * + */ +p5.Element.prototype.id = function(id) { + if (arguments.length === 0) { + return this.elt.id; + } else { + this.elt.id = id; + this.width = this.elt.offsetWidth; + this.height = this.elt.offsetHeight; + return this; + } +}; + +/** + * + * Adds given class to the element. If no class argument is passed in, it + * instead returns a string containing the current class(es) of the element. + * + * @method class + * @param {String} class class to add + * @chainable + */ +/** + * @method class + * @return {String} the class of the element + */ +p5.Element.prototype.class = function(c) { + if (arguments.length === 0) { + return this.elt.className; + } else { + this.elt.className = c; + return this; + } +}; + +/** + * The .mousePressed() function is called once after every time a + * mouse button is pressed over the element. This can be used to + * attach element specific event listeners. + * + * @method mousePressed + * @param {function} fxn function to be fired when mouse is + * pressed over the element. + * @chainable + * @example + *
+ * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mousePressed(changeGray); // attach listener for + * // canvas click only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires with any click anywhere + * function mousePressed() { + * d = d + 10; + * } + * + * // this function fires only when cnv is clicked + * function changeGray() { + * g = random(0, 255); + * } + *
+ * + * @alt + * no display. + * + */ +p5.Element.prototype.mousePressed = function (fxn) { + attachListener('mousedown', fxn, this); + attachListener('touchstart', fxn, this); + return this; +}; + +/** + * The .doubleClicked() function is called once after every time a + * mouse button is pressed twice over the element. This can be used to + * attach element and action specific event listeners. + * + * @method doubleClicked + * @param {Function} fxn function to be fired when mouse is + * pressed over the element. + * @return {p5.Element} + * @example + *
+ * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.doubleClicked(changeGray); // attach listener for + * // canvas click only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires with any double click anywhere + * function doubleClicked() { + * d = d + 10; + * } + * + * // this function fires only when cnv is clicked + * function changeGray() { + * g = random(0, 255); + * } + *
+ * + * @alt + * no display. + * + */ +p5.Element.prototype.doubleClicked = function (fxn) { + attachListener('doubleClicked', fxn, this); + return this; +}; + + +/** + * The .mouseWheel() function is called once after every time a + * mouse wheel is scrolled over the element. This can be used to + * attach element specific event listeners. + *

+ * The function accepts a callback function as argument which will be executed + * when the `wheel` event is triggered on the element, the callback function is + * passed one argument `event`. The `event.deltaY` property returns negative + * values if the mouse wheel is rotated up or away from the user and positive + * in the other direction. The `event.deltaX` does the same as `event.deltaY` + * except it reads the horizontal wheel scroll of the mouse wheel. + *

+ * On OS X with "natural" scrolling enabled, the `event.deltaY` values are + * reversed. + * + * @method mouseWheel + * @param {function} fxn function to be fired when mouse wheel is + * scrolled over the element. + * @chainable + * @example + *
+ * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseWheel(changeSize); // attach listener for + * // activity on canvas only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires with mousewheel movement + * // anywhere on screen + * function mouseWheel() { + * g = g + 10; + * } + * + * // this function fires with mousewheel movement + * // over canvas only + * function changeSize(event) { + * if (event.deltaY > 0) { + * d = d + 10; + * } else { + * d = d - 10; + * } + * } + *
+ * + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseWheel = function (fxn) { + attachListener('wheel', fxn, this); + return this; +}; + +/** + * The .mouseReleased() function is called once after every time a + * mouse button is released over the element. This can be used to + * attach element specific event listeners. + * + * @method mouseReleased + * @param {function} fxn function to be fired when mouse is + * released over the element. + * @chainable + * @example + *
+ * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseReleased(changeGray); // attach listener for + * // activity on canvas only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires after the mouse has been + * // released + * function mouseReleased() { + * d = d + 10; + * } + * + * // this function fires after the mouse has been + * // released while on canvas + * function changeGray() { + * g = random(0, 255); + * } + *
+ * + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseReleased = function (fxn) { + attachListener('mouseup', fxn, this); + attachListener('touchend', fxn, this); + return this; +}; + + +/** + * The .mouseClicked() function is called once after a mouse button is + * pressed and released over the element. This can be used to + * attach element specific event listeners. + * + * @method mouseClicked + * @param {function} fxn function to be fired when mouse is + * clicked over the element. + * @chainable + * @example + *
+ * + * var cnv; + * var d; + * var g; + * + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseClicked(changeGray); // attach listener for + * // activity on canvas only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires after the mouse has been + * // clicked anywhere + * function mouseClicked() { + * d = d + 10; + * } + * + * // this function fires after the mouse has been + * // clicked on canvas + * function changeGray() { + * g = random(0, 255); + * } + * + *
+ * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseClicked = function (fxn) { + attachListener('click', fxn, this); + return this; +}; + +/** + * The .mouseMoved() function is called once every time a + * mouse moves over the element. This can be used to attach an + * element specific event listener. + * + * @method mouseMoved + * @param {function} fxn function to be fired when mouse is + * moved over the element. + * @chainable + * @example + *
+ * var cnv; + * var d = 30; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseMoved(changeSize); // attach listener for + * // activity on canvas only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * fill(200); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires when mouse moves anywhere on + * // page + * function mouseMoved() { + * g = g + 5; + * if (g > 255) { + * g = 0; + * } + * } + * + * // this function fires when mouse moves over canvas + * function changeSize() { + * d = d + 2; + * if (d > 100) { + * d = 0; + * } + * } + *
+ * + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseMoved = function (fxn) { + attachListener('mousemove', fxn, this); + attachListener('touchmove', fxn, this); + return this; +}; + +/** + * The .mouseOver() function is called once after every time a + * mouse moves onto the element. This can be used to attach an + * element specific event listener. + * + * @method mouseOver + * @param {function} fxn function to be fired when mouse is + * moved over the element. + * @chainable + * @example + *
+ * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseOver(changeGray); + * d = 10; + * } + * + * function draw() { + * ellipse(width/2, height/2, d, d); + * } + * + * function changeGray() { + * d = d + 10; + * if (d > 100) { + * d = 0; + * } + * } + *
+ * + * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseOver = function (fxn) { + attachListener('mouseover', fxn, this); + return this; +}; + + +/** + * The .changed() function is called when the value of an + * element is changed. + * This can be used to attach an element specific event listener. + * + * @method changed + * @param {function} fxn function to be fired when the value of an + * element changes. + * @chainable + * @example + *
+ * var sel; + * + * function setup() { + * textAlign(CENTER); + * background(200); + * sel = createSelect(); + * sel.position(10, 10); + * sel.option('pear'); + * sel.option('kiwi'); + * sel.option('grape'); + * sel.changed(mySelectEvent); + * } + * + * function mySelectEvent() { + * var item = sel.value(); + * background(200); + * text("it's a "+item+"!", 50, 50); + * } + *
+ *
+ * var checkbox; + * var cnv; + * + * function setup() { + * checkbox = createCheckbox(" fill"); + * checkbox.changed(changeFill); + * cnv = createCanvas(100, 100); + * cnv.position(0, 30); + * noFill(); + * } + * + * function draw() { + * background(200); + * ellipse(50, 50, 50, 50); + * } + * + * function changeFill() { + * if (checkbox.checked()) { + * fill(0); + * } else { + * noFill(); + * } + * } + *
+ * + * @alt + * dropdown: pear, kiwi, grape. When selected text "its a" + selection shown. + * + */ +p5.Element.prototype.changed = function (fxn) { + attachListener('change', fxn, this); + return this; +}; + +/** + * The .input() function is called when any user input is + * detected with an element. The input event is often used + * to detect keystrokes in a input element, or changes on a + * slider element. This can be used to attach an element specific + * event listener. + * + * @method input + * @param {function} fxn function to be fired on user input. + * @chainable + * @example + *
+ * // Open your console to see the output + * function setup() { + * var inp = createInput(''); + * inp.input(myInputEvent); + * } + * + * function myInputEvent() { + * console.log('you are typing: ', this.value()); + * } + *
+ * + * @alt + * no display. + * + */ +p5.Element.prototype.input = function (fxn) { + attachListener('input', fxn, this); + return this; +}; + +/** + * The .mouseOut() function is called once after every time a + * mouse moves off the element. This can be used to attach an + * element specific event listener. + * + * @method mouseOut + * @param {function} fxn function to be fired when mouse is + * moved off the element. + * @chainable + * @example + *
+ * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.mouseOut(changeGray); + * d = 10; + * } + * + * function draw() { + * ellipse(width/2, height/2, d, d); + * } + * + * function changeGray() { + * d = d + 10; + * if (d > 100) { + * d = 0; + * } + * } + *
+ * + * @alt + * no display. + * + */ +p5.Element.prototype.mouseOut = function (fxn) { + attachListener('mouseout', fxn, this); + return this; +}; + +/** + * The .touchStarted() function is called once after every time a touch is + * registered. This can be used to attach element specific event listeners. + * + * @method touchStarted + * @param {function} fxn function to be fired when touch is + * started over the element. + * @chainable + * @example + *
+ * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.touchStarted(changeGray); // attach listener for + * // canvas click only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires with any touch anywhere + * function touchStarted() { + * d = d + 10; + * } + * + * // this function fires only when cnv is clicked + * function changeGray() { + * g = random(0, 255); + * } + *
+ * + * @alt + * no display. + * + */ +p5.Element.prototype.touchStarted = function (fxn) { + attachListener('touchstart', fxn, this); + attachListener('mousedown', fxn, this); + return this; +}; + +/** + * The .touchMoved() function is called once after every time a touch move is + * registered. This can be used to attach element specific event listeners. + * + * @method touchMoved + * @param {function} fxn function to be fired when touch is moved + * over the element. + * @chainable + * @example + *
+ * var cnv; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.touchMoved(changeGray); // attach listener for + * // canvas click only + * g = 100; + * } + * + * function draw() { + * background(g); + * } + * + * // this function fires only when cnv is clicked + * function changeGray() { + * g = random(0, 255); + * } + *
+ * + * @alt + * no display. + * + */ +p5.Element.prototype.touchMoved = function (fxn) { + attachListener('touchmove', fxn, this); + attachListener('mousemove', fxn, this); + return this; +}; + +/** + * The .touchEnded() function is called once after every time a touch is + * registered. This can be used to attach element specific event listeners. + * + * @method touchEnded + * @param {function} fxn function to be fired when touch is + * ended over the element. + * @chainable + * @example + *
+ * var cnv; + * var d; + * var g; + * function setup() { + * cnv = createCanvas(100, 100); + * cnv.touchEnded(changeGray); // attach listener for + * // canvas click only + * d = 10; + * g = 100; + * } + * + * function draw() { + * background(g); + * ellipse(width/2, height/2, d, d); + * } + * + * // this function fires with any touch anywhere + * function touchEnded() { + * d = d + 10; + * } + * + * // this function fires only when cnv is clicked + * function changeGray() { + * g = random(0, 255); + * } + *
+ * + * + * @alt + * no display. + * + */ +p5.Element.prototype.touchEnded = function (fxn) { + attachListener('touchend', fxn, this); + attachListener('mouseup', fxn, this); + return this; +}; + + + +/** + * The .dragOver() function is called once after every time a + * file is dragged over the element. This can be used to attach an + * element specific event listener. + * + * @method dragOver + * @param {function} fxn function to be fired when mouse is + * dragged over the element. + * @chainable + * @example + *
+ * // To test this sketch, simply drag a + * // file over the canvas + * function setup() { + * var c = createCanvas(100, 100); + * background(200); + * textAlign(CENTER); + * text('Drag file', width/2, height/2); + * c.dragOver(dragOverCallback); + * } + * + * // This function will be called whenever + * // a file is dragged over the canvas + * function dragOverCallback() { + * background(240); + * text('Dragged over', width/2, height/2); + * } + *
+ * @alt + * nothing displayed + */ +p5.Element.prototype.dragOver = function (fxn) { + attachListener('dragover', fxn, this); + return this; +}; + +/** + * The .dragLeave() function is called once after every time a + * dragged file leaves the element area. This can be used to attach an + * element specific event listener. + * + * @method dragLeave + * @param {function} fxn function to be fired when mouse is + * dragged over the element. + * @chainable + * @example + *
+ * // To test this sketch, simply drag a file + * // over and then out of the canvas area + * function setup() { + * var c = createCanvas(100, 100); + * background(200); + * textAlign(CENTER); + * text('Drag file', width/2, height/2); + * c.dragLeave(dragLeaveCallback); + * } + * + * // This function will be called whenever + * // a file is dragged out of the canvas + * function dragLeaveCallback() { + * background(240); + * text('Dragged off', width/2, height/2); + * } + *
+ * @alt + * nothing displayed + */ +p5.Element.prototype.dragLeave = function (fxn) { + attachListener('dragleave', fxn, this); + return this; +}; + +/** + * The .drop() function is called for each file dropped on the element. + * It requires a callback that is passed a p5.File object. You can + * optionally pass two callbacks, the first one (required) is triggered + * for each file dropped when the file is loaded. The second (optional) + * is triggered just once when a file (or files) are dropped. + * + * @method drop + * @param {function} callback callback triggered when files are dropped. + * @param {function} fxn callback to receive loaded file. + * @chainable + * @example + *
+ * function setup() { + * var c = createCanvas(100, 100); + * background(200); + * textAlign(CENTER); + * text('drop image', width/2, height/2); + * c.drop(gotFile); + * } + * + * function gotFile(file) { + * var img = createImg(file.data).hide(); + * // Draw the image onto the canvas + * image(img, 0, 0, width, height); + * } + *
+ * + * @alt + * Canvas turns into whatever image is dragged/dropped onto it. + * + */ +p5.Element.prototype.drop = function (callback, fxn) { + // Make a file loader callback and trigger user's callback + function makeLoader(theFile) { + // Making a p5.File object + var p5file = new p5.File(theFile); + return function(e) { + p5file.data = e.target.result; + callback(p5file); + }; + } + + // Is the file stuff supported? + if (window.File && window.FileReader && window.FileList && window.Blob) { + + // If you want to be able to drop you've got to turn off + // a lot of default behavior + attachListener('dragover',function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + },this); + + // If this is a drag area we need to turn off the default behavior + attachListener('dragleave',function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + },this); + + // If just one argument it's the callback for the files + if (arguments.length > 1) { + attachListener('drop', fxn, this); + } + + // Deal with the files + attachListener('drop', function(evt) { + + evt.stopPropagation(); + evt.preventDefault(); + + // A FileList + var files = evt.dataTransfer.files; + + // Load each one and trigger the callback + for (var i = 0; i < files.length; i++) { + var f = files[i]; + var reader = new FileReader(); + reader.onload = makeLoader(f); + + + // Text or data? + // This should likely be improved + if (f.type.indexOf('text') > -1) { + reader.readAsText(f); + } else { + reader.readAsDataURL(f); + } + } + }, this); + } else { + console.log('The File APIs are not fully supported in this browser.'); + } + + return this; +}; + + + + +function attachListener(ev, fxn, ctx) { + // LM removing, not sure why we had this? + // var _this = ctx; + // var f = function (e) { fxn(e, _this); }; + var f = fxn.bind(ctx); + ctx.elt.addEventListener(ev, f, false); + ctx._events[ev] = f; +} + +/** + * Helper fxn for sharing pixel methods + * + */ +p5.Element.prototype._setProperty = function (prop, value) { + this[prop] = value; +}; + + +module.exports = p5.Element; + +},{"./core":54}],60:[function(_dereq_,module,exports){ +/** + * @module Rendering + * @submodule Rendering + * @for p5 + */ + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); + +/** + * Thin wrapper around a renderer, to be used for creating a + * graphics buffer object. Use this class if you need + * to draw into an off-screen graphics buffer. The two parameters define the + * width and height in pixels. The fields and methods for this class are + * extensive, but mirror the normal drawing API for p5. + * + * @class p5.Graphics + * @constructor + * @extends p5.Element + * @param {Number} w width + * @param {Number} h height + * @param {Constant} renderer the renderer to use, either P2D or WEBGL + * @param {p5} [pInst] pointer to p5 instance + */ +p5.Graphics = function(w, h, renderer, pInst) { + + var r = renderer || constants.P2D; + + this.canvas = document.createElement('canvas'); + var node = this._userNode || document.body; + node.appendChild(this.canvas); + + p5.Element.call(this, this.canvas, pInst, false); + this._styles = []; + this.width = w; + this.height = h; + this._pixelDensity = pInst._pixelDensity; + + if (r === constants.WEBGL) { + this._renderer = new p5.RendererGL(this.canvas, this, false); + } else { + this._renderer = new p5.Renderer2D(this.canvas, this, false); + } + + this._renderer.resize(w, h); + this._renderer._applyDefaults(); + + pInst._elements.push(this); + + // bind methods and props of p5 to the new object + for (var p in p5.prototype) { + if (!this[p]) { + if (typeof p5.prototype[p] === 'function') { + this[p] = p5.prototype[p].bind(this); + } else { + this[p] = p5.prototype[p]; + } + } + } + + return this; +}; + +p5.Graphics.prototype = Object.create(p5.Element.prototype); + +p5.Graphics.prototype.remove = function() { + if (this.elt.parentNode) { + this.elt.parentNode.removeChild(this.elt); + } + for (var elt_ev in this._events) { + this.elt.removeEventListener(elt_ev, this._events[elt_ev]); + } +}; + +module.exports = p5.Graphics; + +},{"./constants":53,"./core":54}],61:[function(_dereq_,module,exports){ +/** + * @module Rendering + * @submodule Rendering + * @for p5 + */ + +var p5 = _dereq_('./core'); +var constants = _dereq_('../core/constants'); + +/** + * Main graphics and rendering context, as well as the base API + * implementation for p5.js "core". To be used as the superclass for + * Renderer2D and Renderer3D classes, respecitvely. + * + * @class p5.Renderer + * @constructor + * @extends p5.Element + * @param {String} elt DOM node that is wrapped + * @param {p5} [pInst] pointer to p5 instance + * @param {Boolean} [isMainCanvas] whether we're using it as main canvas + */ +p5.Renderer = function(elt, pInst, isMainCanvas) { + p5.Element.call(this, elt, pInst); + this.canvas = elt; + this._pInst = pInst; + if (isMainCanvas) { + this._isMainCanvas = true; + // for pixel method sharing with pimage + this._pInst._setProperty('_curElement', this); + this._pInst._setProperty('canvas', this.canvas); + this._pInst._setProperty('width', this.width); + this._pInst._setProperty('height', this.height); + } else { // hide if offscreen buffer by default + this.canvas.style.display = 'none'; + this._styles = []; // non-main elt styles stored in p5.Renderer + } + + + this._textSize = 12; + this._textLeading = 15; + this._textFont = 'sans-serif'; + this._textStyle = constants.NORMAL; + this._textAscent = null; + this._textDescent = null; + + + this._rectMode = constants.CORNER; + this._ellipseMode = constants.CENTER; + this._curveTightness = 0; + this._imageMode = constants.CORNER; + + this._tint = null; + this._doStroke = true; + this._doFill = true; + this._strokeSet = false; + this._fillSet = false; + this._colorMode = constants.RGB; + this._colorMaxes = { + rgb: [255, 255, 255, 255], + hsb: [360, 100, 100, 1], + hsl: [360, 100, 100, 1] + }; + +}; + +p5.Renderer.prototype = Object.create(p5.Element.prototype); + + + + +/** + * Resize our canvas element. + */ +p5.Renderer.prototype.resize = function(w, h) { + this.width = w; + this.height = h; + this.elt.width = w * this._pInst._pixelDensity; + this.elt.height = h * this._pInst._pixelDensity; + this.elt.style.width = w +'px'; + this.elt.style.height = h + 'px'; + if (this._isMainCanvas) { + this._pInst._setProperty('width', this.width); + this._pInst._setProperty('height', this.height); + } +}; + +p5.Renderer.prototype.textLeading = function(l) { + + if (arguments.length && arguments[0]) { + + this._setProperty('_textLeading', l); + return this; + } + + return this._textLeading; +}; + +p5.Renderer.prototype.textSize = function(s) { + + if (arguments.length && arguments[0]) { + + this._setProperty('_textSize', s); + this._setProperty('_textLeading', s * constants._DEFAULT_LEADMULT); + return this._applyTextProperties(); + } + + return this._textSize; +}; + +p5.Renderer.prototype.textStyle = function(s) { + + if (arguments.length && arguments[0]) { + + if (s === constants.NORMAL || + s === constants.ITALIC || + s === constants.BOLD) { + this._setProperty('_textStyle', s); + } + + return this._applyTextProperties(); + } + + return this._textStyle; +}; + +p5.Renderer.prototype.textAscent = function() { + if (this._textAscent === null) { + this._updateTextMetrics(); + } + return this._textAscent; +}; + +p5.Renderer.prototype.textDescent = function() { + + if (this._textDescent === null) { + this._updateTextMetrics(); + } + return this._textDescent; +}; + +p5.Renderer.prototype._applyDefaults = function(){ + return this; +}; + +/** + * Helper fxn to check font type (system or otf) + */ +p5.Renderer.prototype._isOpenType = function(f) { + + f = f || this._textFont; + return (typeof f === 'object' && f.font && f.font.supported); +}; + +p5.Renderer.prototype._updateTextMetrics = function() { + + if (this._isOpenType()) { + + this._setProperty('_textAscent', this._textFont._textAscent()); + this._setProperty('_textDescent', this._textFont._textDescent()); + return this; + } + + // Adapted from http://stackoverflow.com/a/25355178 + var text = document.createElement('span'); + text.style.fontFamily = this._textFont; + text.style.fontSize = this._textSize + 'px'; + text.innerHTML = 'ABCjgq|'; + + var block = document.createElement('div'); + block.style.display = 'inline-block'; + block.style.width = '1px'; + block.style.height = '0px'; + + var container = document.createElement('div'); + container.appendChild(text); + container.appendChild(block); + + container.style.height = '0px'; + container.style.overflow = 'hidden'; + document.body.appendChild(container); + + block.style.verticalAlign = 'baseline'; + var blockOffset = calculateOffset(block); + var textOffset = calculateOffset(text); + var ascent = blockOffset[1] - textOffset[1]; + + block.style.verticalAlign = 'bottom'; + blockOffset = calculateOffset(block); + textOffset = calculateOffset(text); + var height = blockOffset[1] - textOffset[1]; + var descent = height - ascent; + + document.body.removeChild(container); + + this._setProperty('_textAscent', ascent); + this._setProperty('_textDescent', descent); + + return this; +}; + +/** + * Helper fxn to measure ascent and descent. + * Adapted from http://stackoverflow.com/a/25355178 + */ +function calculateOffset(object) { + var currentLeft = 0, + currentTop = 0; + if (object.offsetParent) { + do { + currentLeft += object.offsetLeft; + currentTop += object.offsetTop; + } while (object = object.offsetParent); + } else { + currentLeft += object.offsetLeft; + currentTop += object.offsetTop; + } + return [currentLeft, currentTop]; +} + +module.exports = p5.Renderer; + +},{"../core/constants":53,"./core":54}],62:[function(_dereq_,module,exports){ + +var p5 = _dereq_('./core'); +var canvas = _dereq_('./canvas'); +var constants = _dereq_('./constants'); +var filters = _dereq_('../image/filters'); + +_dereq_('./p5.Renderer'); + +/** + * p5.Renderer2D + * The 2D graphics canvas renderer class. + * extends p5.Renderer + */ +var styleEmpty = 'rgba(0,0,0,0)'; +// var alphaThreshold = 0.00125; // minimum visible + +p5.Renderer2D = function(elt, pInst, isMainCanvas){ + p5.Renderer.call(this, elt, pInst, isMainCanvas); + this.drawingContext = this.canvas.getContext('2d'); + this._pInst._setProperty('drawingContext', this.drawingContext); + return this; +}; + +p5.Renderer2D.prototype = Object.create(p5.Renderer.prototype); + +p5.Renderer2D.prototype._applyDefaults = function() { + this._setFill(constants._DEFAULT_FILL); + this._setStroke(constants._DEFAULT_STROKE); + this.drawingContext.lineCap = constants.ROUND; + this.drawingContext.font = 'normal 12px sans-serif'; +}; + +p5.Renderer2D.prototype.resize = function(w,h) { + p5.Renderer.prototype.resize.call(this, w,h); + this.drawingContext.scale(this._pInst._pixelDensity, + this._pInst._pixelDensity); +}; + +////////////////////////////////////////////// +// COLOR | Setting +////////////////////////////////////////////// + +p5.Renderer2D.prototype.background = function() { + this.drawingContext.save(); + this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); + this.drawingContext.scale(this._pInst._pixelDensity, + this._pInst._pixelDensity); + + if (arguments[0] instanceof p5.Image) { + this._pInst.image(arguments[0], 0, 0, this.width, this.height); + } else { + var curFill = this._getFill(); + // create background rect + var color = this._pInst.color.apply(this, arguments); + var newFill = color.toString(); + this._setFill(newFill); + this.drawingContext.fillRect(0, 0, this.width, this.height); + // reset fill + this._setFill(curFill); + } + this.drawingContext.restore(); +}; + +p5.Renderer2D.prototype.clear = function() { + this.drawingContext.clearRect(0, 0, this.width, this.height); +}; + +p5.Renderer2D.prototype.fill = function() { + var color = this._pInst.color.apply(this, arguments); + this._setFill(color.toString()); +}; + +p5.Renderer2D.prototype.stroke = function() { + var color = this._pInst.color.apply(this, arguments); + this._setStroke(color.toString()); +}; + +////////////////////////////////////////////// +// IMAGE | Loading & Displaying +////////////////////////////////////////////// + +p5.Renderer2D.prototype.image = + function (img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) { + var cnv; + try { + if (this._tint) { + if (p5.MediaElement && img instanceof p5.MediaElement) { + img.loadPixels(); + } + if (img.canvas) { + cnv = this._getTintedImageCanvas(img); + } + } + if (!cnv) { + cnv = img.canvas || img.elt; + } + this.drawingContext.drawImage(cnv, sx, sy, sWidth, sHeight, dx, dy, + dWidth, dHeight); + } catch (e) { + if (e.name !== 'NS_ERROR_NOT_AVAILABLE') { + throw e; + } + } +}; + +p5.Renderer2D.prototype._getTintedImageCanvas = function (img) { + if (!img.canvas) { + return img; + } + var pixels = filters._toPixels(img.canvas); + var tmpCanvas = document.createElement('canvas'); + tmpCanvas.width = img.canvas.width; + tmpCanvas.height = img.canvas.height; + var tmpCtx = tmpCanvas.getContext('2d'); + var id = tmpCtx.createImageData(img.canvas.width, img.canvas.height); + var newPixels = id.data; + for (var i = 0; i < pixels.length; i += 4) { + var r = pixels[i]; + var g = pixels[i + 1]; + var b = pixels[i + 2]; + var a = pixels[i + 3]; + newPixels[i] = r * this._tint[0] / 255; + newPixels[i + 1] = g * this._tint[1] / 255; + newPixels[i + 2] = b * this._tint[2] / 255; + newPixels[i + 3] = a * this._tint[3] / 255; + } + tmpCtx.putImageData(id, 0, 0); + return tmpCanvas; +}; + + +////////////////////////////////////////////// +// IMAGE | Pixels +////////////////////////////////////////////// + +p5.Renderer2D.prototype.blendMode = function(mode) { + this.drawingContext.globalCompositeOperation = mode; +}; +p5.Renderer2D.prototype.blend = function() { + var currBlend = this.drawingContext.globalCompositeOperation; + var blendMode = arguments[arguments.length - 1]; + + var copyArgs = Array.prototype.slice.call( + arguments, + 0, + arguments.length - 1 + ); + + this.drawingContext.globalCompositeOperation = blendMode; + if (this._pInst) { + this._pInst.copy.apply(this._pInst, copyArgs); + } else { + this.copy.apply(this, copyArgs); + } + this.drawingContext.globalCompositeOperation = currBlend; +}; + +p5.Renderer2D.prototype.copy = function () { + var srcImage, sx, sy, sw, sh, dx, dy, dw, dh; + if (arguments.length === 9) { + srcImage = arguments[0]; + sx = arguments[1]; + sy = arguments[2]; + sw = arguments[3]; + sh = arguments[4]; + dx = arguments[5]; + dy = arguments[6]; + dw = arguments[7]; + dh = arguments[8]; + } else if (arguments.length === 8) { + srcImage = this._pInst; + sx = arguments[0]; + sy = arguments[1]; + sw = arguments[2]; + sh = arguments[3]; + dx = arguments[4]; + dy = arguments[5]; + dw = arguments[6]; + dh = arguments[7]; + } else { + throw new Error('Signature not supported'); + } + p5.Renderer2D._copyHelper(srcImage, sx, sy, sw, sh, dx, dy, dw, dh); +}; + +p5.Renderer2D._copyHelper = +function (srcImage, sx, sy, sw, sh, dx, dy, dw, dh) { + srcImage.loadPixels(); + var s = srcImage.canvas.width / srcImage.width; + this.drawingContext.drawImage(srcImage.canvas, + s * sx, s * sy, s * sw, s * sh, dx, dy, dw, dh); +}; + +p5.Renderer2D.prototype.get = function(x, y, w, h) { + if (x === undefined && y === undefined && + w === undefined && h === undefined){ + x = 0; + y = 0; + w = this.width; + h = this.height; + } else if (w === undefined && h === undefined) { + w = 1; + h = 1; + } + + // if the section does not overlap the canvas + if(x + w < 0 || y + h < 0 || x > this.width || y > this.height){ + return [0, 0, 0, 255]; + } + + var ctx = this._pInst || this; + ctx.loadPixels(); + + var pd = ctx._pixelDensity; + + // round down to get integer numbers + x = Math.floor(x); + y = Math.floor(y); + w = Math.floor(w); + h = Math.floor(h); + + var sx = x * pd; + var sy = y * pd; + if (w === 1 && h === 1){ + var imageData = this.drawingContext.getImageData(sx, sy, 1, 1).data; + //imageData = [0,0,0,0]; + return [ + imageData[0], + imageData[1], + imageData[2], + imageData[3] + ]; + } else { + //auto constrain the width and height to + //dimensions of the source image + var dw = Math.min(w, ctx.width); + var dh = Math.min(h, ctx.height); + var sw = dw * pd; + var sh = dh * pd; + + var region = new p5.Image(dw, dh); + region.canvas.getContext('2d').drawImage(this.canvas, sx, sy, sw, sh, + 0, 0, dw, dh); + + return region; + } +}; + +p5.Renderer2D.prototype.loadPixels = function () { + var pd = this._pixelDensity || this._pInst._pixelDensity; + var w = this.width * pd; + var h = this.height * pd; + var imageData = this.drawingContext.getImageData(0, 0, w, h); + // @todo this should actually set pixels per object, so diff buffers can + // have diff pixel arrays. + if (this._pInst) { + this._pInst._setProperty('imageData', imageData); + this._pInst._setProperty('pixels', imageData.data); + } else { // if called by p5.Image + this._setProperty('imageData', imageData); + this._setProperty('pixels', imageData.data); + } +}; + +p5.Renderer2D.prototype.set = function (x, y, imgOrCol) { + // round down to get integer numbers + x = Math.floor(x); + y = Math.floor(y); + if (imgOrCol instanceof p5.Image) { + this.drawingContext.save(); + this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); + this.drawingContext.scale(this._pInst._pixelDensity, + this._pInst._pixelDensity); + this.drawingContext.drawImage(imgOrCol.canvas, x, y); + this.loadPixels.call(this._pInst); + this.drawingContext.restore(); + } else { + var ctx = this._pInst || this; + var r = 0, g = 0, b = 0, a = 0; + var idx = 4*((y * ctx._pixelDensity) * + (this.width * ctx._pixelDensity) + (x * ctx._pixelDensity)); + if (!ctx.imageData) { + ctx.loadPixels.call(ctx); + } + if (typeof imgOrCol === 'number') { + if (idx < ctx.pixels.length) { + r = imgOrCol; + g = imgOrCol; + b = imgOrCol; + a = 255; + //this.updatePixels.call(this); + } + } + else if (imgOrCol instanceof Array) { + if (imgOrCol.length < 4) { + throw new Error('pixel array must be of the form [R, G, B, A]'); + } + if (idx < ctx.pixels.length) { + r = imgOrCol[0]; + g = imgOrCol[1]; + b = imgOrCol[2]; + a = imgOrCol[3]; + //this.updatePixels.call(this); + } + } else if (imgOrCol instanceof p5.Color) { + if (idx < ctx.pixels.length) { + r = imgOrCol.levels[0]; + g = imgOrCol.levels[1]; + b = imgOrCol.levels[2]; + a = imgOrCol.levels[3]; + //this.updatePixels.call(this); + } + } + // loop over pixelDensity * pixelDensity + for (var i = 0; i < ctx._pixelDensity; i++) { + for (var j = 0; j < ctx._pixelDensity; j++) { + // loop over + idx = 4*((y * ctx._pixelDensity + j) * this.width * + ctx._pixelDensity + (x * ctx._pixelDensity + i)); + ctx.pixels[idx] = r; + ctx.pixels[idx+1] = g; + ctx.pixels[idx+2] = b; + ctx.pixels[idx+3] = a; + } + } + } +}; + +p5.Renderer2D.prototype.updatePixels = function (x, y, w, h) { + var pd = this._pixelDensity || this._pInst._pixelDensity; + if (x === undefined && + y === undefined && + w === undefined && + h === undefined) { + x = 0; + y = 0; + w = this.width; + h = this.height; + } + w *= pd; + h *= pd; + + if (this._pInst) { + this.drawingContext.putImageData(this._pInst.imageData, x, y, 0, 0, w, h); + } else { + this.drawingContext.putImageData(this.imageData, x, y, 0, 0, w, h); + } +}; + +////////////////////////////////////////////// +// SHAPE | 2D Primitives +////////////////////////////////////////////// + +/** + * Generate a cubic Bezier representing an arc on the unit circle of total + * angle `size` radians, beginning `start` radians above the x-axis. Up to + * four of these curves are combined to make a full arc. + * + * See www.joecridge.me/bezier.pdf for an explanation of the method. + */ +p5.Renderer2D.prototype._acuteArcToBezier = + function _acuteArcToBezier(start, size) { + // Evauate constants. + var alpha = size / 2.0, + cos_alpha = Math.cos(alpha), + sin_alpha = Math.sin(alpha), + cot_alpha = 1.0 / Math.tan(alpha), + phi = start + alpha, // This is how far the arc needs to be rotated. + cos_phi = Math.cos(phi), + sin_phi = Math.sin(phi), + lambda = (4.0 - cos_alpha) / 3.0, + mu = sin_alpha + (cos_alpha - lambda) * cot_alpha; + + // Return rotated waypoints. + return { + ax: Math.cos(start), + ay: Math.sin(start), + bx: lambda * cos_phi + mu * sin_phi, + by: lambda * sin_phi - mu * cos_phi, + cx: lambda * cos_phi - mu * sin_phi, + cy: lambda * sin_phi + mu * cos_phi, + dx: Math.cos(start + size), + dy: Math.sin(start + size) + }; +}; + +p5.Renderer2D.prototype.arc = + function(x, y, w, h, start, stop, mode) { + var ctx = this.drawingContext; + var vals = canvas.arcModeAdjust(x, y, w, h, this._ellipseMode); + var rx = vals.w / 2.0; + var ry = vals.h / 2.0; + var epsilon = 0.00001; // Smallest visible angle on displays up to 4K. + var arcToDraw = 0; + var curves = []; + + // Create curves + while(stop - start > epsilon) { + arcToDraw = Math.min(stop - start, constants.HALF_PI); + curves.push(this._acuteArcToBezier(start, arcToDraw)); + start += arcToDraw; + } + + // Fill curves + if (this._doFill) { + ctx.beginPath(); + curves.forEach(function (curve, index) { + if (index === 0) { + ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry); + } + ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry, + vals.x + curve.cx * rx, vals.y + curve.cy * ry, + vals.x + curve.dx * rx, vals.y + curve.dy * ry); + }); + if (mode === constants.PIE || mode == null) { + ctx.lineTo(vals.x, vals.y); + } + ctx.closePath(); + ctx.fill(); + } + + // Stroke curves + if (this._doStroke) { + ctx.beginPath(); + curves.forEach(function (curve, index) { + if (index === 0) { + ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry); + } + ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry, + vals.x + curve.cx * rx, vals.y + curve.cy * ry, + vals.x + curve.dx * rx, vals.y + curve.dy * ry); + }); + if (mode === constants.PIE) { + ctx.lineTo(vals.x, vals.y); + ctx.closePath(); + } else if (mode === constants.CHORD) { + ctx.closePath(); + } + ctx.stroke(); + } + return this; +}; + +p5.Renderer2D.prototype.ellipse = function(args) { + var ctx = this.drawingContext; + var doFill = this._doFill, doStroke = this._doStroke; + var x = args[0], + y = args[1], + w = args[2], + h = args[3]; + if (doFill && !doStroke) { + if(this._getFill() === styleEmpty) { + return this; + } + } else if (!doFill && doStroke) { + if(this._getStroke() === styleEmpty) { + return this; + } + } + var kappa = 0.5522847498, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + ctx.beginPath(); + ctx.moveTo(x, ym); + ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + ctx.closePath(); + if (doFill) { + ctx.fill(); + } + if (doStroke) { + ctx.stroke(); + } +}; + +p5.Renderer2D.prototype.line = function(x1, y1, x2, y2) { + var ctx = this.drawingContext; + if (!this._doStroke) { + return this; + } else if(this._getStroke() === styleEmpty){ + return this; + } + // Translate the line by (0.5, 0.5) to draw it crisp + if (ctx.lineWidth % 2 === 1) { + ctx.translate(0.5, 0.5); + } + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + if (ctx.lineWidth % 2 === 1) { + ctx.translate(-0.5, -0.5); + } + return this; +}; + +p5.Renderer2D.prototype.point = function(x, y) { + var ctx = this.drawingContext; + if (!this._doStroke) { + return this; + } else if(this._getStroke() === styleEmpty){ + return this; + } + x = Math.round(x); + y = Math.round(y); + if (ctx.lineWidth > 1) { + ctx.beginPath(); + ctx.arc( + x, + y, + ctx.lineWidth / 2, + 0, + constants.TWO_PI, + false + ); + ctx.fill(); + } else { + ctx.fillRect(x, y, 1, 1); + } +}; + +p5.Renderer2D.prototype.quad = + function(x1, y1, x2, y2, x3, y3, x4, y4) { + var ctx = this.drawingContext; + var doFill = this._doFill, doStroke = this._doStroke; + if (doFill && !doStroke) { + if(this._getFill() === styleEmpty) { + return this; + } + } else if (!doFill && doStroke) { + if(this._getStroke() === styleEmpty) { + return this; + } + } + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.lineTo(x3, y3); + ctx.lineTo(x4, y4); + ctx.closePath(); + if (doFill) { + ctx.fill(); + } + if (doStroke) { + ctx.stroke(); + } + return this; +}; + +p5.Renderer2D.prototype.rect = function(args) { + var x = args[0], + y = args[1], + w = args[2], + h = args[3], + tl = args[4], + tr = args[5], + br = args[6], + bl = args[7]; + var ctx = this.drawingContext; + var doFill = this._doFill, doStroke = this._doStroke; + if (doFill && !doStroke) { + if(this._getFill() === styleEmpty) { + return this; + } + } else if (!doFill && doStroke) { + if(this._getStroke() === styleEmpty) { + return this; + } + } + // Translate the line by (0.5, 0.5) to draw a crisp rectangle border + if (this._doStroke && ctx.lineWidth % 2 === 1) { + ctx.translate(0.5, 0.5); + } + ctx.beginPath(); + + if (typeof tl === 'undefined') { + // No rounded corners + ctx.rect(x, y, w, h); + } else { + // At least one rounded corner + // Set defaults when not specified + if (typeof tr === 'undefined') { tr = tl; } + if (typeof br === 'undefined') { br = tr; } + if (typeof bl === 'undefined') { bl = br; } + + var hw = w / 2; + var hh = h / 2; + + // Clip radii + if (w < 2 * tl) { tl = hw; } + if (h < 2 * tl) { tl = hh; } + if (w < 2 * tr) { tr = hw; } + if (h < 2 * tr) { tr = hh; } + if (w < 2 * br) { br = hw; } + if (h < 2 * br) { br = hh; } + if (w < 2 * bl) { bl = hw; } + if (h < 2 * bl) { bl = hh; } + + // Draw shape + ctx.beginPath(); + ctx.moveTo(x + tl, y); + ctx.arcTo(x + w, y, x + w, y + h, tr); + ctx.arcTo(x + w, y + h, x, y + h, br); + ctx.arcTo(x, y + h, x, y, bl); + ctx.arcTo(x, y, x + w, y, tl); + ctx.closePath(); + } + if (this._doFill) { + ctx.fill(); + } + if (this._doStroke) { + ctx.stroke(); + } + if (this._doStroke && ctx.lineWidth % 2 === 1) { + ctx.translate(-0.5, -0.5); + } + return this; +}; + +p5.Renderer2D.prototype.triangle = function(args) { + var ctx = this.drawingContext; + var doFill = this._doFill, doStroke = this._doStroke; + var x1=args[0], y1=args[1]; + var x2=args[2], y2=args[3]; + var x3=args[4], y3=args[5]; + if (doFill && !doStroke) { + if(this._getFill() === styleEmpty) { + return this; + } + } else if (!doFill && doStroke) { + if(this._getStroke() === styleEmpty) { + return this; + } + } + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.lineTo(x3, y3); + ctx.closePath(); + if (doFill) { + ctx.fill(); + } + if (doStroke) { + ctx.stroke(); + } +}; + +p5.Renderer2D.prototype.endShape = +function (mode, vertices, isCurve, isBezier, + isQuadratic, isContour, shapeKind) { + if (vertices.length === 0) { + return this; + } + if (!this._doStroke && !this._doFill) { + return this; + } + var closeShape = mode === constants.CLOSE; + var v; + if (closeShape && !isContour) { + vertices.push(vertices[0]); + } + var i, j; + var numVerts = vertices.length; + if (isCurve && (shapeKind === constants.POLYGON || shapeKind === null)) { + if (numVerts > 3) { + var b = [], s = 1 - this._curveTightness; + this.drawingContext.beginPath(); + this.drawingContext.moveTo(vertices[1][0], vertices[1][1]); + for (i = 1; i + 2 < numVerts; i++) { + v = vertices[i]; + b[0] = [ + v[0], + v[1] + ]; + b[1] = [ + v[0] + (s * vertices[i + 1][0] - s * vertices[i - 1][0]) / 6, + v[1] + (s * vertices[i + 1][1] - s * vertices[i - 1][1]) / 6 + ]; + b[2] = [ + vertices[i + 1][0] + + (s * vertices[i][0]-s * vertices[i + 2][0]) / 6, + vertices[i + 1][1]+(s * vertices[i][1] - s*vertices[i + 2][1]) / 6 + ]; + b[3] = [ + vertices[i + 1][0], + vertices[i + 1][1] + ]; + this.drawingContext.bezierCurveTo(b[1][0],b[1][1], + b[2][0],b[2][1],b[3][0],b[3][1]); + } + if (closeShape) { + this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]); + } + this._doFillStrokeClose(); + } + } else if (isBezier&&(shapeKind===constants.POLYGON ||shapeKind === null)) { + this.drawingContext.beginPath(); + for (i = 0; i < numVerts; i++) { + if (vertices[i].isVert) { + if (vertices[i].moveTo) { + this.drawingContext.moveTo(vertices[i][0], vertices[i][1]); + } else { + this.drawingContext.lineTo(vertices[i][0], vertices[i][1]); + } + } else { + this.drawingContext.bezierCurveTo(vertices[i][0], vertices[i][1], + vertices[i][2], vertices[i][3], vertices[i][4], vertices[i][5]); + } + } + this._doFillStrokeClose(); + } else if (isQuadratic && + (shapeKind === constants.POLYGON || shapeKind === null)) { + this.drawingContext.beginPath(); + for (i = 0; i < numVerts; i++) { + if (vertices[i].isVert) { + if (vertices[i].moveTo) { + this.drawingContext.moveTo([0], vertices[i][1]); + } else { + this.drawingContext.lineTo(vertices[i][0], vertices[i][1]); + } + } else { + this.drawingContext.quadraticCurveTo(vertices[i][0], vertices[i][1], + vertices[i][2], vertices[i][3]); + } + } + this._doFillStrokeClose(); + } else { + if (shapeKind === constants.POINTS) { + for (i = 0; i < numVerts; i++) { + v = vertices[i]; + if (this._doStroke) { + this._pInst.stroke(v[6]); + } + this._pInst.point(v[0], v[1]); + } + } else if (shapeKind === constants.LINES) { + for (i = 0; i + 1 < numVerts; i += 2) { + v = vertices[i]; + if (this._doStroke) { + this._pInst.stroke(vertices[i + 1][6]); + } + this._pInst.line(v[0], v[1], vertices[i + 1][0], vertices[i + 1][1]); + } + } else if (shapeKind === constants.TRIANGLES) { + for (i = 0; i + 2 < numVerts; i += 3) { + v = vertices[i]; + this.drawingContext.beginPath(); + this.drawingContext.moveTo(v[0], v[1]); + this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]); + this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]); + this.drawingContext.lineTo(v[0], v[1]); + if (this._doFill) { + this._pInst.fill(vertices[i + 2][5]); + this.drawingContext.fill(); + } + if (this._doStroke) { + this._pInst.stroke(vertices[i + 2][6]); + this.drawingContext.stroke(); + } + this.drawingContext.closePath(); + } + } else if (shapeKind === constants.TRIANGLE_STRIP) { + for (i = 0; i + 1 < numVerts; i++) { + v = vertices[i]; + this.drawingContext.beginPath(); + this.drawingContext.moveTo(vertices[i + 1][0], vertices[i + 1][1]); + this.drawingContext.lineTo(v[0], v[1]); + if (this._doStroke) { + this._pInst.stroke(vertices[i + 1][6]); + } + if (this._doFill) { + this._pInst.fill(vertices[i + 1][5]); + } + if (i + 2 < numVerts) { + this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]); + if (this._doStroke) { + this._pInst.stroke(vertices[i + 2][6]); + } + if (this._doFill) { + this._pInst.fill(vertices[i + 2][5]); + } + } + this._doFillStrokeClose(); + } + } else if (shapeKind === constants.TRIANGLE_FAN) { + if (numVerts > 2) { + // For performance reasons, try to batch as many of the + // fill and stroke calls as possible. + this.drawingContext.beginPath(); + for (i = 2; i < numVerts; i++) { + v = vertices[i]; + this.drawingContext.moveTo(vertices[0][0], vertices[0][1]); + this.drawingContext.lineTo(vertices[i - 1][0], vertices[i - 1][1]); + this.drawingContext.lineTo(v[0], v[1]); + this.drawingContext.lineTo(vertices[0][0], vertices[0][1]); + // If the next colour is going to be different, stroke / fill now + if (i < numVerts - 1) { + if ( (this._doFill && v[5] !== vertices[i + 1][5]) || + (this._doStroke && v[6] !== vertices[i + 1][6])) { + if (this._doFill) { + this._pInst.fill(v[5]); + this.drawingContext.fill(); + this._pInst.fill(vertices[i + 1][5]); + } + if (this._doStroke) { + this._pInst.stroke(v[6]); + this.drawingContext.stroke(); + this._pInst.stroke(vertices[i + 1][6]); + } + this.drawingContext.closePath(); + this.drawingContext.beginPath(); // Begin the next one + } + } + } + this._doFillStrokeClose(); + } + } else if (shapeKind === constants.QUADS) { + for (i = 0; i + 3 < numVerts; i += 4) { + v = vertices[i]; + this.drawingContext.beginPath(); + this.drawingContext.moveTo(v[0], v[1]); + for (j = 1; j < 4; j++) { + this.drawingContext.lineTo(vertices[i + j][0], vertices[i + j][1]); + } + this.drawingContext.lineTo(v[0], v[1]); + if (this._doFill) { + this._pInst.fill(vertices[i + 3][5]); + } + if (this._doStroke) { + this._pInst.stroke(vertices[i + 3][6]); + } + this._doFillStrokeClose(); + } + } else if (shapeKind === constants.QUAD_STRIP) { + if (numVerts > 3) { + for (i = 0; i + 1 < numVerts; i += 2) { + v = vertices[i]; + this.drawingContext.beginPath(); + if (i + 3 < numVerts) { + this.drawingContext.moveTo(vertices[i + 2][0], vertices[i+2][1]); + this.drawingContext.lineTo(v[0], v[1]); + this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]); + this.drawingContext.lineTo(vertices[i + 3][0], vertices[i+3][1]); + if (this._doFill) { + this._pInst.fill(vertices[i + 3][5]); + } + if (this._doStroke) { + this._pInst.stroke(vertices[i + 3][6]); + } + } else { + this.drawingContext.moveTo(v[0], v[1]); + this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]); + } + this._doFillStrokeClose(); + } + } + } else { + this.drawingContext.beginPath(); + this.drawingContext.moveTo(vertices[0][0], vertices[0][1]); + for (i = 1; i < numVerts; i++) { + v = vertices[i]; + if (v.isVert) { + if (v.moveTo) { + this.drawingContext.moveTo(v[0], v[1]); + } else { + this.drawingContext.lineTo(v[0], v[1]); + } + } + } + this._doFillStrokeClose(); + } + } + isCurve = false; + isBezier = false; + isQuadratic = false; + isContour = false; + if (closeShape) { + vertices.pop(); + } + return this; +}; +////////////////////////////////////////////// +// SHAPE | Attributes +////////////////////////////////////////////// + +p5.Renderer2D.prototype.noSmooth = function() { + if ('imageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.imageSmoothingEnabled = false; + } + else if ('mozImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.mozImageSmoothingEnabled = false; + } + else if ('webkitImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.webkitImageSmoothingEnabled = false; + } + else if ('msImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.msImageSmoothingEnabled = false; + } + return this; +}; + +p5.Renderer2D.prototype.smooth = function() { + if ('imageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.imageSmoothingEnabled = true; + } + else if ('mozImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.mozImageSmoothingEnabled = true; + } + else if ('webkitImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.webkitImageSmoothingEnabled = true; + } + else if ('msImageSmoothingEnabled' in this.drawingContext) { + this.drawingContext.msImageSmoothingEnabled = true; + } + return this; +}; + +p5.Renderer2D.prototype.strokeCap = function(cap) { + if (cap === constants.ROUND || + cap === constants.SQUARE || + cap === constants.PROJECT) { + this.drawingContext.lineCap = cap; + } + return this; +}; + +p5.Renderer2D.prototype.strokeJoin = function(join) { + if (join === constants.ROUND || + join === constants.BEVEL || + join === constants.MITER) { + this.drawingContext.lineJoin = join; + } + return this; +}; + +p5.Renderer2D.prototype.strokeWeight = function(w) { + if (typeof w === 'undefined' || w === 0) { + // hack because lineWidth 0 doesn't work + this.drawingContext.lineWidth = 0.0001; + } else { + this.drawingContext.lineWidth = w; + } + return this; +}; + +p5.Renderer2D.prototype._getFill = function(){ + return this._cachedFillStyle; +}; + +p5.Renderer2D.prototype._setFill = function(fillStyle){ + if (fillStyle !== this._cachedFillStyle) { + this.drawingContext.fillStyle = fillStyle; + this._cachedFillStyle = fillStyle; + } +}; + +p5.Renderer2D.prototype._getStroke = function(){ + return this._cachedStrokeStyle; +}; + +p5.Renderer2D.prototype._setStroke = function(strokeStyle){ + if (strokeStyle !== this._cachedStrokeStyle) { + this.drawingContext.strokeStyle = strokeStyle; + this._cachedStrokeStyle = strokeStyle; + } +}; + + + +////////////////////////////////////////////// +// SHAPE | Curves +////////////////////////////////////////////// +p5.Renderer2D.prototype.bezier = function (x1, y1, x2, y2, x3, y3, x4, y4) { + this._pInst.beginShape(); + this._pInst.vertex(x1, y1); + this._pInst.bezierVertex(x2, y2, x3, y3, x4, y4); + this._pInst.endShape(); + return this; +}; + +p5.Renderer2D.prototype.curve = function (x1, y1, x2, y2, x3, y3, x4, y4) { + this._pInst.beginShape(); + this._pInst.curveVertex(x1, y1); + this._pInst.curveVertex(x2, y2); + this._pInst.curveVertex(x3, y3); + this._pInst.curveVertex(x4, y4); + this._pInst.endShape(); + return this; +}; + +////////////////////////////////////////////// +// SHAPE | Vertex +////////////////////////////////////////////// + +p5.Renderer2D.prototype._doFillStrokeClose = function () { + if (this._doFill) { + this.drawingContext.fill(); + } + if (this._doStroke) { + this.drawingContext.stroke(); + } + this.drawingContext.closePath(); +}; + +////////////////////////////////////////////// +// TRANSFORM +////////////////////////////////////////////// + +p5.Renderer2D.prototype.applyMatrix = +function(n00, n01, n02, n10, n11, n12) { + this.drawingContext.transform(n00, n01, n02, n10, n11, n12); +}; + +p5.Renderer2D.prototype.resetMatrix = function() { + this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); + this.drawingContext.scale(this._pInst._pixelDensity, + this._pInst._pixelDensity); + return this; +}; + +p5.Renderer2D.prototype.rotate = function(r) { + this.drawingContext.rotate(r); +}; + +p5.Renderer2D.prototype.scale = function(x,y) { + this.drawingContext.scale(x, y); + return this; +}; + +p5.Renderer2D.prototype.shearX = function(angle) { + if (this._pInst._angleMode === constants.DEGREES) { + // undoing here, because it gets redone in tan() + angle = this._pInst.degrees(angle); + } + this.drawingContext.transform(1, 0, this._pInst.tan(angle), 1, 0, 0); + return this; +}; + +p5.Renderer2D.prototype.shearY = function(angle) { + if (this._pInst._angleMode === constants.DEGREES) { + // undoing here, because it gets redone in tan() + angle = this._pInst.degrees(angle); + } + this.drawingContext.transform(1, this._pInst.tan(angle), 0, 1, 0, 0); + return this; +}; + +p5.Renderer2D.prototype.translate = function(x, y) { + this.drawingContext.translate(x, y); + return this; +}; + +////////////////////////////////////////////// +// TYPOGRAPHY +// +////////////////////////////////////////////// + +p5.Renderer2D.prototype.text = function (str, x, y, maxWidth, maxHeight) { + + var p = this._pInst, cars, n, ii, jj, line, testLine, + testWidth, words, totalHeight, baselineHacked, + finalMaxHeight = Number.MAX_VALUE; + + // baselineHacked: (HACK) + // A temporary fix to conform to Processing's implementation + // of BASELINE vertical alignment in a bounding box + + if (!(this._doFill || this._doStroke)) { + return; + } + + if (typeof str !== 'string') { + str = str.toString(); + } + + str = str.replace(/(\t)/g, ' '); + cars = str.split('\n'); + + if (typeof maxWidth !== 'undefined') { + + totalHeight = 0; + for (ii = 0; ii < cars.length; ii++) { + line = ''; + words = cars[ii].split(' '); + for (n = 0; n < words.length; n++) { + testLine = line + words[n] + ' '; + testWidth = this.textWidth(testLine); + if (testWidth > maxWidth) { + line = words[n] + ' '; + totalHeight += p.textLeading(); + } else { + line = testLine; + } + } + } + + if (this._rectMode === constants.CENTER) { + + x -= maxWidth / 2; + y -= maxHeight / 2; + } + + switch (this.drawingContext.textAlign) { + + case constants.CENTER: + x += maxWidth / 2; + break; + case constants.RIGHT: + x += maxWidth; + break; + } + + if (typeof maxHeight !== 'undefined') { + + switch (this.drawingContext.textBaseline) { + case constants.BOTTOM: + y += (maxHeight - totalHeight); + break; + case constants._CTX_MIDDLE: // CENTER? + y += (maxHeight - totalHeight) / 2; + break; + case constants.BASELINE: + baselineHacked = true; + this.drawingContext.textBaseline = constants.TOP; + break; + } + + // remember the max-allowed y-position for any line (fix to #928) + finalMaxHeight = (y + maxHeight) - p.textAscent(); + } + + for (ii = 0; ii < cars.length; ii++) { + + line = ''; + words = cars[ii].split(' '); + for (n = 0; n < words.length; n++) { + testLine = line + words[n] + ' '; + testWidth = this.textWidth(testLine); + if (testWidth > maxWidth && line.length > 0) { + this._renderText(p, line, x, y, finalMaxHeight); + line = words[n] + ' '; + y += p.textLeading(); + } else { + line = testLine; + } + } + + this._renderText(p, line, x, y, finalMaxHeight); + y += p.textLeading(); + } + } + else { + // Offset to account for vertically centering multiple lines of text - no + // need to adjust anything for vertical align top or baseline + var offset = 0, + vAlign = p.textAlign().vertical; + if (vAlign === constants.CENTER) { + offset = ((cars.length - 1) * p.textLeading()) / 2; + } else if (vAlign === constants.BOTTOM) { + offset = (cars.length - 1) * p.textLeading(); + } + + for (jj = 0; jj < cars.length; jj++) { + + this._renderText(p, cars[jj], x, y-offset, finalMaxHeight); + y += p.textLeading(); + } + } + + if (baselineHacked) { + this.drawingContext.textBaseline = constants.BASELINE; + } + + return p; +}; + +p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY) { + + if (y >= maxY) { + return; // don't render lines beyond our maxY position + } + + p.push(); // fix to #803 + + if (!this._isOpenType()) { // a system/browser font + + // no stroke unless specified by user + if (this._doStroke && this._strokeSet) { + + this.drawingContext.strokeText(line, x, y); + } + + if (this._doFill) { + + // if fill hasn't been set by user, use default text fill + if (! this._fillSet) { + this._setFill(constants._DEFAULT_TEXT_FILL); + } + + this.drawingContext.fillText(line, x, y); + } + } + else { // an opentype font, let it handle the rendering + + this._textFont._renderPath(line, x, y, { renderer: this }); + } + + p.pop(); + + return p; +}; + +p5.Renderer2D.prototype.textWidth = function(s) { + + if (this._isOpenType()) { + + return this._textFont._textWidth(s, this._textSize); + } + + return this.drawingContext.measureText(s).width; +}; + +p5.Renderer2D.prototype.textAlign = function(h, v) { + + if (arguments.length) { + + if (h === constants.LEFT || + h === constants.RIGHT || + h === constants.CENTER) { + + this.drawingContext.textAlign = h; + } + + if (v === constants.TOP || + v === constants.BOTTOM || + v === constants.CENTER || + v === constants.BASELINE) { + + if (v === constants.CENTER) { + this.drawingContext.textBaseline = constants._CTX_MIDDLE; + } else { + this.drawingContext.textBaseline = v; + } + } + + return this._pInst; + + } else { + + var valign = this.drawingContext.textBaseline; + + if (valign === constants._CTX_MIDDLE) { + + valign = constants.CENTER; + } + + return { + + horizontal: this.drawingContext.textAlign, + vertical: valign + }; + } +}; + +p5.Renderer2D.prototype._applyTextProperties = function() { + + var font, p = this._pInst; + + this._setProperty('_textAscent', null); + this._setProperty('_textDescent', null); + + font = this._textFont; + + if (this._isOpenType()) { + + font = this._textFont.font.familyName; + this._setProperty('_textStyle', this._textFont.font.styleName); + } + + this.drawingContext.font = (this._textStyle || 'normal') + + ' ' + (this._textSize || 12) + 'px ' + (font || 'sans-serif'); + + return p; +}; + + +////////////////////////////////////////////// +// STRUCTURE +////////////////////////////////////////////// + +p5.Renderer2D.prototype.push = function() { + this.drawingContext.save(); +}; + +p5.Renderer2D.prototype.pop = function() { + this.drawingContext.restore(); + // Re-cache the fill / stroke state + this._cachedFillStyle = this.drawingContext.fillStyle; + this._cachedStrokeStyle = this.drawingContext.strokeStyle; +}; + +module.exports = p5.Renderer2D; + +},{"../image/filters":73,"./canvas":52,"./constants":53,"./core":54,"./p5.Renderer":61}],63:[function(_dereq_,module,exports){ +/** + * @module Rendering + * @submodule Rendering + * @for p5 + */ + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); +_dereq_('./p5.Graphics'); +_dereq_('./p5.Renderer2D'); +_dereq_('../webgl/p5.RendererGL'); +var defaultId = 'defaultCanvas0'; // this gets set again in createCanvas + +/** + * Creates a canvas element in the document, and sets the dimensions of it + * in pixels. This method should be called only once at the start of setup. + * Calling createCanvas more than once in a sketch will result in very + * unpredicable behavior. If you want more than one drawing canvas + * you could use createGraphics (hidden by default but it can be shown). + *

+ * The system variables width and height are set by the parameters passed + * to this function. If createCanvas() is not used, the window will be + * given a default size of 100x100 pixels. + *

+ * For more ways to position the canvas, see the + * + * positioning the canvas wiki page. + * + * @method createCanvas + * @param {Number} w width of the canvas + * @param {Number} h height of the canvas + * @param {Constant} [renderer] either P2D or WEBGL + * @return {HTMLCanvasElement} canvas generated + * @example + *
+ * + * function setup() { + * createCanvas(100, 50); + * background(153); + * line(0, 0, width, height); + * } + * + *
+ * + * @alt + * Black line extending from top-left of canvas to bottom right. + * + */ + +p5.prototype.createCanvas = function(w, h, renderer) { + //optional: renderer, otherwise defaults to p2d + var r = renderer || constants.P2D; + var isDefault, c; + + //4th arg (isDefault) used when called onLoad, + //otherwise hidden to the public api + if(arguments[3]){ + isDefault = + (typeof arguments[3] === 'boolean') ? arguments[3] : false; + } + + if(r === constants.WEBGL){ + c = document.getElementById(defaultId); + if(c){ //if defaultCanvas already exists + c.parentNode.removeChild(c); //replace the existing defaultCanvas + } + c = document.createElement('canvas'); + c.id = defaultId; + } + else { + if (isDefault) { + c = document.createElement('canvas'); + var i = 0; + while (document.getElementById('defaultCanvas'+i)) { + i++; + } + defaultId = 'defaultCanvas'+i; + c.id = defaultId; + } else { // resize the default canvas if new one is created + c = this.canvas; + } + } + + // set to invisible if still in setup (to prevent flashing with manipulate) + if (!this._setupDone) { + c.dataset.hidden = true; // tag to show later + c.style.visibility='hidden'; + } + + if (this._userNode) { // user input node case + this._userNode.appendChild(c); + } else { + document.body.appendChild(c); + } + + + + // Init our graphics renderer + //webgl mode + if (r === constants.WEBGL) { + this._setProperty('_renderer', new p5.RendererGL(c, this, true)); + this._isdefaultGraphics = true; + } + //P2D mode + else { + if (!this._isdefaultGraphics) { + this._setProperty('_renderer', new p5.Renderer2D(c, this, true)); + this._isdefaultGraphics = true; + } + } + this._renderer.resize(w, h); + this._renderer._applyDefaults(); + if (isDefault) { // only push once + this._elements.push(this._renderer); + } + return this._renderer; +}; + +/** + * Resizes the canvas to given width and height. The canvas will be cleared + * and draw will be called immediately, allowing the sketch to re-render itself + * in the resized canvas. + * @method resizeCanvas + * @param {Number} w width of the canvas + * @param {Number} h height of the canvas + * @param {Boolean} noRedraw don't redraw the canvas immediately + * @example + *
+ * function setup() { + * createCanvas(windowWidth, windowHeight); + * } + * + * function draw() { + * background(0, 100, 200); + * } + * + * function windowResized() { + * resizeCanvas(windowWidth, windowHeight); + * } + *
+ * + * @alt + * No image displayed. + * + */ +p5.prototype.resizeCanvas = function (w, h, noRedraw) { + if (this._renderer) { + + // save canvas properties + var props = {}; + for (var key in this.drawingContext) { + var val = this.drawingContext[key]; + if (typeof val !== 'object' && typeof val !== 'function') { + props[key] = val; + } + } + this._renderer.resize(w, h); + // reset canvas properties + for (var savedKey in props) { + this.drawingContext[savedKey] = props[savedKey]; + } + if (!noRedraw) { + this.redraw(); + } + } +}; + + +/** + * Removes the default canvas for a p5 sketch that doesn't + * require a canvas + * @method noCanvas + * @example + *
+ * + * function setup() { + * noCanvas(); + * } + * + *
+ * + * @alt + * no image displayed + * + */ +p5.prototype.noCanvas = function() { + if (this.canvas) { + this.canvas.parentNode.removeChild(this.canvas); + } +}; + +/** + * Creates and returns a new p5.Renderer object. Use this class if you need + * to draw into an off-screen graphics buffer. The two parameters define the + * width and height in pixels. + * + * @method createGraphics + * @param {Number} w width of the offscreen graphics buffer + * @param {Number} h height of the offscreen graphics buffer + * @param {Constant} [renderer] either P2D or WEBGL + * undefined defaults to p2d + * @return {p5.Graphics} offscreen graphics buffer + * @example + *
+ * + * var pg; + * function setup() { + * createCanvas(100, 100); + * pg = createGraphics(100, 100); + * } + * function draw() { + * background(200); + * pg.background(100); + * pg.noStroke(); + * pg.ellipse(pg.width/2, pg.height/2, 50, 50); + * image(pg, 50, 50); + * image(pg, 0, 0, 50, 50); + * } + * + *
+ * + * @alt + * 4 grey squares alternating light and dark grey. White quarter circle mid-left. + * + */ +p5.prototype.createGraphics = function(w, h, renderer){ + return new p5.Graphics(w, h, renderer, this); +}; + +/** + * Blends the pixels in the display window according to the defined mode. + * There is a choice of the following modes to blend the source pixels (A) + * with the ones of pixels already in the display window (B): + *
    + *
  • BLEND - linear interpolation of colours: C = + * A*factor + B. This is the default blending mode.
  • + *
  • ADD - sum of A and B
  • + *
  • DARKEST - only the darkest colour succeeds: C = + * min(A*factor, B).
  • + *
  • LIGHTEST - only the lightest colour succeeds: C = + * max(A*factor, B).
  • + *
  • DIFFERENCE - subtract colors from underlying image.
  • + *
  • EXCLUSION - similar to DIFFERENCE, but less + * extreme.
  • + *
  • MULTIPLY - multiply the colors, result will always be + * darker.
  • + *
  • SCREEN - opposite multiply, uses inverse values of the + * colors.
  • + *
  • REPLACE - the pixels entirely replace the others and + * don't utilize alpha (transparency) values.
  • + *
  • OVERLAY - mix of MULTIPLY and SCREEN + * . Multiplies dark values, and screens light values.
  • + *
  • HARD_LIGHT - SCREEN when greater than 50% + * gray, MULTIPLY when lower.
  • + *
  • SOFT_LIGHT - mix of DARKEST and + * LIGHTEST. Works like OVERLAY, but not as harsh. + *
  • + *
  • DODGE - lightens light tones and increases contrast, + * ignores darks.
  • + *
  • BURN - darker areas are applied, increasing contrast, + * ignores lights.
  • + *
+ * + * @method blendMode + * @param {Constant} mode blend mode to set for canvas. + * either BLEND, DARKEST, LIGHTEST, DIFFERENCE, MULTIPLY, + * EXCLUSION, SCREEN, REPLACE, OVERLAY, HARD_LIGHT, + * SOFT_LIGHT, DODGE, BURN, ADD or NORMAL + * @example + *
+ * + * blendMode(LIGHTEST); + * strokeWeight(30); + * stroke(80, 150, 255); + * line(25, 25, 75, 75); + * stroke(255, 50, 50); + * line(75, 25, 25, 75); + * + *
+ *
+ * + * blendMode(MULTIPLY); + * strokeWeight(30); + * stroke(80, 150, 255); + * line(25, 25, 75, 75); + * stroke(255, 50, 50); + * line(75, 25, 25, 75); + * + *
+ * @alt + * translucent image thick red & blue diagonal rounded lines intersecting center + * Thick red & blue diagonal rounded lines intersecting center. dark at overlap + * + */ +p5.prototype.blendMode = function(mode) { + if (mode === constants.BLEND || mode === constants.DARKEST || + mode === constants.LIGHTEST || mode === constants.DIFFERENCE || + mode === constants.MULTIPLY || mode === constants.EXCLUSION || + mode === constants.SCREEN || mode === constants.REPLACE || + mode === constants.OVERLAY || mode === constants.HARD_LIGHT || + mode === constants.SOFT_LIGHT || mode === constants.DODGE || + mode === constants.BURN || mode === constants.ADD || + mode === constants.NORMAL) { + this._renderer.blendMode(mode); + } else { + throw new Error('Mode '+mode+' not recognized.'); + } +}; + +module.exports = p5; + +},{"../webgl/p5.RendererGL":105,"./constants":53,"./core":54,"./p5.Graphics":60,"./p5.Renderer2D":62}],64:[function(_dereq_,module,exports){ + +// requestAnim shim layer by Paul Irish +window.requestAnimationFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback, element){ + // should '60' here be framerate? + window.setTimeout(callback, 1000 / 60); + }; +})(); + +// use window.performance() to get max fast and accurate time in milliseconds +window.performance = window.performance || {}; +window.performance.now = (function(){ + var load_date = Date.now(); + return window.performance.now || + window.performance.mozNow || + window.performance.msNow || + window.performance.oNow || + window.performance.webkitNow || + function () { + return Date.now() - load_date; + }; +})(); + +/* +// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ +// http://my.opera.com/emoller/blog/2011/12/20/ +// requestanimationframe-for-smart-er-animating +// requestAnimationFrame polyfill by Erik Möller +// fixes from Paul Irish and Tino Zijdel +(function() { + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = + window[vendors[x]+'RequestAnimationFrame']; + window.cancelAnimationFrame = + window[vendors[x]+'CancelAnimationFrame'] || + window[vendors[x]+'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() + { callback(currTime + timeToCall); }, timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; + } +}()); +*/ + +/** + * shim for Uint8ClampedArray.slice + * (allows arrayCopy to work with pixels[]) + * with thanks to http://halfpapstudios.com/blog/tag/html5-canvas/ + * Enumerable set to false to protect for...in from + * Uint8ClampedArray.prototype pollution. + */ +(function () { + 'use strict'; + if (typeof Uint8ClampedArray !== 'undefined' && + !Uint8ClampedArray.prototype.slice) { + Object.defineProperty(Uint8ClampedArray.prototype, 'slice', { + value: Array.prototype.slice, + writable: true, configurable: true, enumerable: false + }); + } +}()); + +},{}],65:[function(_dereq_,module,exports){ +/** + * @module Structure + * @submodule Structure + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('./core'); + +p5.prototype.exit = function() { + throw 'exit() not implemented, see remove()'; +}; +/** + * Stops p5.js from continuously executing the code within draw(). + * If loop() is called, the code in draw() begins to run continuously again. + * If using noLoop() in setup(), it should be the last line inside the block. + *

+ * When noLoop() is used, it's not possible to manipulate or access the + * screen inside event handling functions such as mousePressed() or + * keyPressed(). Instead, use those functions to call redraw() or loop(), + * which will run draw(), which can update the screen properly. This means + * that when noLoop() has been called, no drawing can happen, and functions + * like saveFrame() or loadPixels() may not be used. + *

+ * Note that if the sketch is resized, redraw() will be called to update + * the sketch, even after noLoop() has been specified. Otherwise, the sketch + * would enter an odd state until loop() was called. + * + * @method noLoop + * @example + *
+ * function setup() { + * createCanvas(100, 100); + * background(200); + * noLoop(); + * } + + * function draw() { + * line(10, 10, 90, 90); + * } + *
+ * + *
+ * var x = 0; + * function setup() { + * createCanvas(100, 100); + * } + * + * function draw() { + * background(204); + * x = x + 0.1; + * if (x > width) { + * x = 0; + * } + * line(x, 0, x, height); + * } + * + * function mousePressed() { + * noLoop(); + * } + * + * function mouseReleased() { + * loop(); + * } + *
+ * + * @alt + * 113 pixel long line extending from top-left to bottom right of canvas. + * horizontal line moves slowly from left. Loops but stops on mouse press. + * + */ +p5.prototype.noLoop = function() { + this._loop = false; +}; +/** + * By default, p5.js loops through draw() continuously, executing the code + * within it. However, the draw() loop may be stopped by calling noLoop(). + * In that case, the draw() loop can be resumed with loop(). + * + * @method loop + * @example + *
+ * var x = 0; + * function setup() { + * createCanvas(100, 100); + * noLoop(); + * } + * + * function draw() { + * background(204); + * x = x + 0.1; + * if (x > width) { + * x = 0; + * } + * line(x, 0, x, height); + * } + * + * function mousePressed() { + * loop(); + * } + * + * function mouseReleased() { + * noLoop(); + * } + *
+ * + * @alt + * horizontal line moves slowly from left. Loops but stops on mouse press. + * + */ + +p5.prototype.loop = function() { + this._loop = true; + this._draw(); +}; + +/** + * The push() function saves the current drawing style settings and + * transformations, while pop() restores these settings. Note that these + * functions are always used together. They allow you to change the style + * and transformation settings and later return to what you had. When a new + * state is started with push(), it builds on the current style and transform + * information. The push() and pop() functions can be embedded to provide + * more control. (See the second example for a demonstration.) + *

+ * push() stores information related to the current transformation state + * and style settings controlled by the following functions: fill(), + * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(), + * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(), + * textFont(), textMode(), textSize(), textLeading(). + * + * @method push + * @example + *
+ * + * ellipse(0, 50, 33, 33); // Left circle + * + * push(); // Start a new drawing state + * strokeWeight(10); + * fill(204, 153, 0); + * translate(50, 0); + * ellipse(0, 50, 33, 33); // Middle circle + * pop(); // Restore original state + * + * ellipse(100, 50, 33, 33); // Right circle + * + *
+ *
+ * + * ellipse(0, 50, 33, 33); // Left circle + * + * push(); // Start a new drawing state + * strokeWeight(10); + * fill(204, 153, 0); + * ellipse(33, 50, 33, 33); // Left-middle circle + * + * push(); // Start another new drawing state + * stroke(0, 102, 153); + * ellipse(66, 50, 33, 33); // Right-middle circle + * pop(); // Restore previous state + * + * pop(); // Restore original state + * + * ellipse(100, 50, 33, 33); // Right circle + * + *
+ * + * @alt + * Gold ellipse + thick black outline @center 2 white ellipses on left and right. + * 2 Gold ellipses left black right blue stroke. 2 white ellipses on left+right. + * + */ +p5.prototype.push = function () { + this._renderer.push(); + this._styles.push({ + _doStroke: this._renderer._doStroke, + _strokeSet: this._renderer._strokeSet, + _doFill: this._renderer._doFill, + _fillSet: this._renderer._fillSet, + _tint: this._renderer._tint, + _imageMode: this._renderer._imageMode, + _rectMode: this._renderer._rectMode, + _ellipseMode: this._renderer._ellipseMode, + _colorMode: this._renderer._colorMode, + _textFont: this._renderer._textFont, + _textLeading: this._renderer._textLeading, + _textSize: this._renderer._textSize, + _textStyle: this._renderer._textStyle + }); +}; + +/** + * The push() function saves the current drawing style settings and + * transformations, while pop() restores these settings. Note that these + * functions are always used together. They allow you to change the style + * and transformation settings and later return to what you had. When a new + * state is started with push(), it builds on the current style and transform + * information. The push() and pop() functions can be embedded to provide + * more control. (See the second example for a demonstration.) + *

+ * push() stores information related to the current transformation state + * and style settings controlled by the following functions: fill(), + * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(), + * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(), + * textFont(), textMode(), textSize(), textLeading(). + * + * @method pop + * @example + *
+ * + * ellipse(0, 50, 33, 33); // Left circle + * + * push(); // Start a new drawing state + * translate(50, 0); + * strokeWeight(10); + * fill(204, 153, 0); + * ellipse(0, 50, 33, 33); // Middle circle + * pop(); // Restore original state + * + * ellipse(100, 50, 33, 33); // Right circle + * + *
+ *
+ * + * ellipse(0, 50, 33, 33); // Left circle + * + * push(); // Start a new drawing state + * strokeWeight(10); + * fill(204, 153, 0); + * ellipse(33, 50, 33, 33); // Left-middle circle + * + * push(); // Start another new drawing state + * stroke(0, 102, 153); + * ellipse(66, 50, 33, 33); // Right-middle circle + * pop(); // Restore previous state + * + * pop(); // Restore original state + * + * ellipse(100, 50, 33, 33); // Right circle + * + *
+ * + * @alt + * Gold ellipse + thick black outline @center 2 white ellipses on left and right. + * 2 Gold ellipses left black right blue stroke. 2 white ellipses on left+right. + * + */ +p5.prototype.pop = function () { + this._renderer.pop(); + var lastS = this._styles.pop(); + for(var prop in lastS){ + this._renderer[prop] = lastS[prop]; + } +}; + +p5.prototype.pushStyle = function() { + throw new Error('pushStyle() not used, see push()'); +}; + +p5.prototype.popStyle = function() { + throw new Error('popStyle() not used, see pop()'); +}; + +/** + * + * Executes the code within draw() one time. This functions allows the + * program to update the display window only when necessary, for example + * when an event registered by mousePressed() or keyPressed() occurs. + *

+ * In structuring a program, it only makes sense to call redraw() within + * events such as mousePressed(). This is because redraw() does not run + * draw() immediately (it only sets a flag that indicates an update is + * needed). + *

+ * The redraw() function does not work properly when called inside draw(). + * To enable/disable animations, use loop() and noLoop(). + *

+ * In addition you can set the number of redraws per method call. Just + * add an integer as single parameter for the number of redraws. + * + * @method redraw + * @param {Integer} [n] Redraw for n-times. The default value is 1. + * @example + *
+ * var x = 0; + * + * function setup() { + * createCanvas(100, 100); + * noLoop(); + * } + * + * function draw() { + * background(204); + * line(x, 0, x, height); + * } + * + * function mousePressed() { + * x += 1; + * redraw(); + * } + *
+ * + *
+ * var x = 0; + * + * function setup() { + * createCanvas(100, 100); + * noLoop(); + * } + * + * function draw() { + * background(204); + * x += 1; + * line(x, 0, x, height); + * } + * + * function mousePressed() { + * redraw(5); + * } + *
+ * + * @alt + * black line on far left of canvas + * black line on far left of canvas + * + */ +p5.prototype.redraw = function () { + this.resetMatrix(); + if(this._renderer.isP3D){ + this._renderer._update(); + } + + var numberOfRedraws = 1; + if (arguments.length === 1) { + try { + if (parseInt(arguments[0]) > 1) { + numberOfRedraws = parseInt(arguments[0]); + } + } catch (error) { + // Do nothing, because the default value didn't be changed. + } + } + var userSetup = this.setup || window.setup; + var userDraw = this.draw || window.draw; + if (typeof userDraw === 'function') { + if (typeof userSetup === 'undefined') { + this.scale(this._pixelDensity, this._pixelDensity); + } + var self = this; + var callMethod = function (f) { + f.call(self); + }; + for (var idxRedraw = 0; idxRedraw < numberOfRedraws; idxRedraw++) { + this._registeredMethods.pre.forEach(callMethod); + userDraw(); + this._registeredMethods.post.forEach(callMethod); + } + } +}; + +p5.prototype.size = function() { + var s = 'size() is not a valid p5 function, to set the size of the '; + s += 'drawing canvas, please use createCanvas() instead'; + throw s; +}; + + +module.exports = p5; + +},{"./core":54}],66:[function(_dereq_,module,exports){ +/** + * @module Transform + * @submodule Transform + * @for p5 + * @requires core + * @requires constants + */ + + +'use strict'; + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); + +/** + * Multiplies the current matrix by the one specified through the parameters. + * This is very slow because it will try to calculate the inverse of the + * transform, so avoid it whenever possible. + * + * @method applyMatrix + * @param {Number} n00 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n01 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n02 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n10 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n11 numbers which define the 3x2 matrix to be multiplied + * @param {Number} n12 numbers which define the 3x2 matrix to be multiplied + * @chainable + * @example + *
+ * + * // Example in the works. + * + *
+ * + * @alt + * no image diplayed + * + */ +p5.prototype.applyMatrix = function(n00, n01, n02, n10, n11, n12) { + this._renderer.applyMatrix(n00, n01, n02, n10, n11, n12); + return this; +}; + +p5.prototype.popMatrix = function() { + throw new Error('popMatrix() not used, see pop()'); +}; + +p5.prototype.printMatrix = function() { + throw new Error('printMatrix() not implemented'); +}; + +p5.prototype.pushMatrix = function() { + throw new Error('pushMatrix() not used, see push()'); +}; + +/** + * Replaces the current matrix with the identity matrix. + * + * @method resetMatrix + * @chainable + * @example + *
+ * + * // Example in the works. + * + *
+ * + * @alt + * no image diplayed + * + */ +p5.prototype.resetMatrix = function() { + this._renderer.resetMatrix(); + return this; +}; + +/** + * Rotates a shape the amount specified by the angle parameter. This + * function accounts for angleMode, so angles can be entered in either + * RADIANS or DEGREES. + *

+ * Objects are always rotated around their relative position to the + * origin and positive numbers rotate objects in a clockwise direction. + * Transformations apply to everything that happens after and subsequent + * calls to the function accumulates the effect. For example, calling + * rotate(HALF_PI) and then rotate(HALF_PI) is the same as rotate(PI). + * All tranformations are reset when draw() begins again. + *

+ * Technically, rotate() multiplies the current transformation matrix + * by a rotation matrix. This function can be further controlled by + * the push() and pop(). + * + * @method rotate + * @param {Number} angle the angle of rotation, specified in radians + * or degrees, depending on current angleMode + * @param {p5.Vector|Array} [axis] (in 3d) the axis to rotate around + * @chainable + * @example + *
+ * + * translate(width/2, height/2); + * rotate(PI/3.0); + * rect(-26, -26, 52, 52); + * + *
+ * + * @alt + * white 52x52 rect with black outline at center rotated counter 45 degrees + * + */ +p5.prototype.rotate = function(angle, axis) { + var args = new Array(arguments.length); + var r; + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if (this._angleMode === constants.DEGREES) { + r = this.radians(args[0]); + } else if (this._angleMode === constants.RADIANS){ + r = args[0]; + } + //in webgl mode + if(args.length > 1){ + this._renderer.rotate(r, args[1]); + } + else { + this._renderer.rotate(r); + } + return this; +}; + +/** + * Rotates around X axis. + * @method rotateX + * @param {Number} rad angles in radians + * @chainable + */ +p5.prototype.rotateX = function(rad) { + if (this._renderer.isP3D) { + this._renderer.rotateX(rad); + } else { + throw 'not supported in p2d. Please use webgl mode'; + } + return this; +}; + +/** + * Rotates around Y axis. + * @method rotateY + * @param {Number} rad angles in radians + * @chainable + */ +p5.prototype.rotateY = function(rad) { + if (this._renderer.isP3D) { + this._renderer.rotateY(rad); + } else { + throw 'not supported in p2d. Please use webgl mode'; + } + return this; +}; + +/** + * Rotates around Z axis. Webgl mode only. + * @method rotateZ + * @param {Number} rad angles in radians + * @chainable + */ +p5.prototype.rotateZ = function(rad) { + if (this._renderer.isP3D) { + this._renderer.rotateZ(rad); + } else { + throw 'not supported in p2d. Please use webgl mode'; + } + return this; +}; + +/** + * Increases or decreases the size of a shape by expanding and contracting + * vertices. Objects always scale from their relative origin to the + * coordinate system. Scale values are specified as decimal percentages. + * For example, the function call scale(2.0) increases the dimension of a + * shape by 200%. + *

+ * Transformations apply to everything that happens after and subsequent + * calls to the function multiply the effect. For example, calling scale(2.0) + * and then scale(1.5) is the same as scale(3.0). If scale() is called + * within draw(), the transformation is reset when the loop begins again. + *

+ * Using this function with the z parameter is only available in WEBGL mode. + * This function can be further controlled with push() and pop(). + * + * @method scale + * @param {Number|p5.Vector|Array} s + * percent to scale the object, or percentage to + * scale the object in the x-axis if multiple arguments + * are given + * @param {Number} [y] percent to scale the object in the y-axis + * @param {Number} [z] percent to scale the object in the z-axis (webgl only) + * @chainable + * @example + *
+ * + * translate(width/2, height/2); + * rotate(PI/3.0); + * rect(-26, -26, 52, 52); + * + *
+ * + *
+ * + * rect(30, 20, 50, 50); + * scale(0.5, 1.3); + * rect(30, 20, 50, 50); + * + *
+ * + * @alt + * white 52x52 rect with black outline at center rotated counter 45 degrees + * 2 white rects with black outline- 1 50x50 at center. other 25x65 bottom left + * + */ +p5.prototype.scale = function() { + var x,y,z; + var args = new Array(arguments.length); + for(var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + // Only check for Vector argument type if Vector is available + if (typeof p5.Vector !== 'undefined' && args[0] instanceof p5.Vector) { + x = args[0].x; + y = args[0].y; + z = args[0].z; + } + else if(args[0] instanceof Array){ + x = args[0][0]; + y = args[0][1]; + z = args[0][2] || 1; + } + else { + if(args.length === 1){ + x = y = z = args[0]; + } + else { + x = args[0]; + y = args[1]; + z = args[2] || 1; + } + } + + if(this._renderer.isP3D){ + this._renderer.scale.call(this._renderer, x,y,z); + } + else { + this._renderer.scale.call(this._renderer, x,y); + } + return this; +}; + +/** + * Shears a shape around the x-axis the amount specified by the angle + * parameter. Angles should be specified in the current angleMode. + * Objects are always sheared around their relative position to the origin + * and positive numbers shear objects in a clockwise direction. + *

+ * Transformations apply to everything that happens after and subsequent + * calls to the function accumulates the effect. For example, calling + * shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI). + * If shearX() is called within the draw(), the transformation is reset when + * the loop begins again. + *

+ * Technically, shearX() multiplies the current transformation matrix by a + * rotation matrix. This function can be further controlled by the + * push() and pop() functions. + * + * @method shearX + * @param {Number} angle angle of shear specified in radians or degrees, + * depending on current angleMode + * @chainable + * @example + *
+ * + * translate(width/4, height/4); + * shearX(PI/4.0); + * rect(0, 0, 30, 30); + * + *
+ * + * @alt + * white irregular quadrilateral with black outline at top middle. + * + */ +p5.prototype.shearX = function(angle) { + if (this._angleMode === constants.DEGREES) { + angle = this.radians(angle); + } + this._renderer.shearX(angle); + return this; +}; + +/** + * Shears a shape around the y-axis the amount specified by the angle + * parameter. Angles should be specified in the current angleMode. Objects + * are always sheared around their relative position to the origin and + * positive numbers shear objects in a clockwise direction. + *

+ * Transformations apply to everything that happens after and subsequent + * calls to the function accumulates the effect. For example, calling + * shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI). If + * shearY() is called within the draw(), the transformation is reset when + * the loop begins again. + *

+ * Technically, shearY() multiplies the current transformation matrix by a + * rotation matrix. This function can be further controlled by the + * push() and pop() functions. + * + * @method shearY + * @param {Number} angle angle of shear specified in radians or degrees, + * depending on current angleMode + * @chainable + * @example + *
+ * + * translate(width/4, height/4); + * shearY(PI/4.0); + * rect(0, 0, 30, 30); + * + *
+ * + * @alt + * white irregular quadrilateral with black outline at middle bottom. + * + */ +p5.prototype.shearY = function(angle) { + if (this._angleMode === constants.DEGREES) { + angle = this.radians(angle); + } + this._renderer.shearY(angle); + return this; +}; + +/** + * Specifies an amount to displace objects within the display window. + * The x parameter specifies left/right translation, the y parameter + * specifies up/down translation. + *

+ * Transformations are cumulative and apply to everything that happens after + * and subsequent calls to the function accumulates the effect. For example, + * calling translate(50, 0) and then translate(20, 0) is the same as + * translate(70, 0). If translate() is called within draw(), the + * transformation is reset when the loop begins again. This function can be + * further controlled by using push() and pop(). + * + * @method translate + * @param {Number} x left/right translation + * @param {Number} y up/down translation + * @param {Number} [z] forward/backward translation (webgl only) + * @chainable + * @example + *
+ * + * translate(30, 20); + * rect(0, 0, 55, 55); + * + *
+ * + *
+ * + * rect(0, 0, 55, 55); // Draw rect at original 0,0 + * translate(30, 20); + * rect(0, 0, 55, 55); // Draw rect at new 0,0 + * translate(14, 14); + * rect(0, 0, 55, 55); // Draw rect at new 0,0 + * + *
+ * + * @alt + * white 55x55 rect with black outline at center right. + * 3 white 55x55 rects with black outlines at top-l, center-r and bottom-r. + * + */ +p5.prototype.translate = function(x, y, z) { + if (this._renderer.isP3D) { + this._renderer.translate(x, y, z); + } else { + this._renderer.translate(x, y); + } + return this; +}; + +module.exports = p5; + +},{"./constants":53,"./core":54}],67:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule Vertex + * @for p5 + * @requires core + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('./core'); +var constants = _dereq_('./constants'); +var shapeKind = null; +var vertices = []; +var contourVertices = []; +var isBezier = false; +var isCurve = false; +var isQuadratic = false; +var isContour = false; +var isFirstContour = true; + +/** + * Use the beginContour() and endContour() functions to create negative + * shapes within shapes such as the center of the letter 'O'. beginContour() + * begins recording vertices for the shape and endContour() stops recording. + * The vertices that define a negative shape must "wind" in the opposite + * direction from the exterior shape. First draw vertices for the exterior + * clockwise order, then for internal shapes, draw vertices + * shape in counter-clockwise. + *

+ * These functions can only be used within a beginShape()/endShape() pair and + * transformations such as translate(), rotate(), and scale() do not work + * within a beginContour()/endContour() pair. It is also not possible to use + * other shapes, such as ellipse() or rect() within. + * + * @method beginContour + * @chainable + * @example + *
+ * + * translate(50, 50); + * stroke(255, 0, 0); + * beginShape(); + * // Exterior part of shape, clockwise winding + * vertex(-40, -40); + * vertex(40, -40); + * vertex(40, 40); + * vertex(-40, 40); + * // Interior part of shape, counter-clockwise winding + * beginContour(); + * vertex(-20, -20); + * vertex(-20, 20); + * vertex(20, 20); + * vertex(20, -20); + * endContour(); + * endShape(CLOSE); + * + *
+ * + * @alt + * white rect and smaller grey rect with red outlines in center of canvas. + * + */ +p5.prototype.beginContour = function() { + contourVertices = []; + isContour = true; + return this; +}; + +/** + * Using the beginShape() and endShape() functions allow creating more + * complex forms. beginShape() begins recording vertices for a shape and + * endShape() stops recording. The value of the kind parameter tells it which + * types of shapes to create from the provided vertices. With no mode + * specified, the shape can be any irregular polygon. + *

+ * The parameters available for beginShape() are POINTS, LINES, TRIANGLES, + * TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP. After calling the + * beginShape() function, a series of vertex() commands must follow. To stop + * drawing the shape, call endShape(). Each shape will be outlined with the + * current stroke color and filled with the fill color. + *

+ * Transformations such as translate(), rotate(), and scale() do not work + * within beginShape(). It is also not possible to use other shapes, such as + * ellipse() or rect() within beginShape(). + * + * @method beginShape + * @param {Constant} [kind] either POINTS, LINES, TRIANGLES, TRIANGLE_FAN + * TRIANGLE_STRIP, QUADS, or QUAD_STRIP + * @chainable + * @example + *
+ * + * beginShape(); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(CLOSE); + * + *
+ * + *
+ * + * // currently not working + * beginShape(POINTS); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(); + * + *
+ * + *
+ * + * beginShape(LINES); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(); + * + *
+ * + *
+ * + * noFill(); + * beginShape(); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(); + * + *
+ * + *
+ * + * noFill(); + * beginShape(); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(CLOSE); + * + *
+ * + *
+ * + * beginShape(TRIANGLES); + * vertex(30, 75); + * vertex(40, 20); + * vertex(50, 75); + * vertex(60, 20); + * vertex(70, 75); + * vertex(80, 20); + * endShape(); + * + *
+ * + *
+ * + * beginShape(TRIANGLE_STRIP); + * vertex(30, 75); + * vertex(40, 20); + * vertex(50, 75); + * vertex(60, 20); + * vertex(70, 75); + * vertex(80, 20); + * vertex(90, 75); + * endShape(); + * + *
+ * + *
+ * + * beginShape(TRIANGLE_FAN); + * vertex(57.5, 50); + * vertex(57.5, 15); + * vertex(92, 50); + * vertex(57.5, 85); + * vertex(22, 50); + * vertex(57.5, 15); + * endShape(); + * + *
+ * + *
+ * + * beginShape(QUADS); + * vertex(30, 20); + * vertex(30, 75); + * vertex(50, 75); + * vertex(50, 20); + * vertex(65, 20); + * vertex(65, 75); + * vertex(85, 75); + * vertex(85, 20); + * endShape(); + * + *
+ * + *
+ * + * beginShape(QUAD_STRIP); + * vertex(30, 20); + * vertex(30, 75); + * vertex(50, 20); + * vertex(50, 75); + * vertex(65, 20); + * vertex(65, 75); + * vertex(85, 20); + * vertex(85, 75); + * endShape(); + * + *
+ * + *
+ * + * beginShape(); + * vertex(20, 20); + * vertex(40, 20); + * vertex(40, 40); + * vertex(60, 40); + * vertex(60, 60); + * vertex(20, 60); + * endShape(CLOSE); + * + *
+ * @alt + * white square-shape with black outline in middle-right of canvas. + * 4 black points in a square shape in middle-right of canvas. + * 2 horizontal black lines. In the top-right and bottom-right of canvas. + * 3 line shape with horizontal on top, vertical in middle and horizontal bottom. + * square line shape in middle-right of canvas. + * 2 white triangle shapes mid-right canvas. left one pointing up and right down. + * 5 horizontal interlocking and alternating white triangles in mid-right canvas. + * 4 interlocking white triangles in 45 degree rotated square-shape. + * 2 white rectangle shapes in mid-right canvas. Both 20x55. + * 3 side-by-side white rectangles center rect is smaller in mid-right canvas. + * Thick white l-shape with black outline mid-top-left of canvas. + * + */ +p5.prototype.beginShape = function(kind) { + if (kind === constants.POINTS || + kind === constants.LINES || + kind === constants.TRIANGLES || + kind === constants.TRIANGLE_FAN || + kind === constants.TRIANGLE_STRIP || + kind === constants.QUADS || + kind === constants.QUAD_STRIP) { + shapeKind = kind; + } else { + shapeKind = null; + } + if(this._renderer.isP3D){ + this._renderer.beginShape(kind); + } else { + vertices = []; + contourVertices = []; + } + return this; +}; + +/** + * Specifies vertex coordinates for Bezier curves. Each call to + * bezierVertex() defines the position of two control points and + * one anchor point of a Bezier curve, adding a new segment to a + * line or shape. + *

+ * The first time bezierVertex() is used within a + * beginShape() call, it must be prefaced with a call to vertex() + * to set the first anchor point. This function must be used between + * beginShape() and endShape() and only when there is no MODE + * parameter specified to beginShape(). + * + * @method bezierVertex + * @param {Number} x2 x-coordinate for the first control point + * @param {Number} y2 y-coordinate for the first control point + * @param {Number} x3 x-coordinate for the second control point + * @param {Number} y3 y-coordinate for the second control point + * @param {Number} x4 x-coordinate for the anchor point + * @param {Number} y4 y-coordinate for the anchor point + * @chainable + * @example + *
+ * + * noFill(); + * beginShape(); + * vertex(30, 20); + * bezierVertex(80, 0, 80, 75, 30, 75); + * endShape(); + * + *
+ * + *
+ * + * beginShape(); + * vertex(30, 20); + * bezierVertex(80, 0, 80, 75, 30, 75); + * bezierVertex(50, 80, 60, 25, 30, 20); + * endShape(); + * + *
+ * + * @alt + * crescent-shaped line in middle of canvas. Points facing left. + * white crescent shape in middle of canvas. Points facing left. + * + */ +p5.prototype.bezierVertex = function(x2, y2, x3, y3, x4, y4) { + if (vertices.length === 0) { + throw 'vertex() must be used once before calling bezierVertex()'; + } else { + isBezier = true; + var vert = []; + for (var i = 0; i < arguments.length; i++) { + vert[i] = arguments[i]; + } + vert.isVert = false; + if (isContour) { + contourVertices.push(vert); + } else { + vertices.push(vert); + } + } + return this; +}; + +/** + * Specifies vertex coordinates for curves. This function may only + * be used between beginShape() and endShape() and only when there + * is no MODE parameter specified to beginShape(). + *

+ * The first and last points in a series of curveVertex() lines will be used to + * guide the beginning and end of a the curve. A minimum of four + * points is required to draw a tiny curve between the second and + * third points. Adding a fifth point with curveVertex() will draw + * the curve between the second, third, and fourth points. The + * curveVertex() function is an implementation of Catmull-Rom + * splines. + * + * @method curveVertex + * @param {Number} x x-coordinate of the vertex + * @param {Number} y y-coordinate of the vertex + * @chainable + * @example + *
+ * + * noFill(); + * beginShape(); + * curveVertex(84, 91); + * curveVertex(84, 91); + * curveVertex(68, 19); + * curveVertex(21, 17); + * curveVertex(32, 100); + * curveVertex(32, 100); + * endShape(); + * + *
+ * + * @alt + * Upside-down u-shape line, mid canvas. left point extends beyond canvas view. + * + */ +p5.prototype.curveVertex = function(x,y) { + isCurve = true; + this.vertex(x, y); + return this; +}; + +/** + * Use the beginContour() and endContour() functions to create negative + * shapes within shapes such as the center of the letter 'O'. beginContour() + * begins recording vertices for the shape and endContour() stops recording. + * The vertices that define a negative shape must "wind" in the opposite + * direction from the exterior shape. First draw vertices for the exterior + * clockwise order, then for internal shapes, draw vertices + * shape in counter-clockwise. + *

+ * These functions can only be used within a beginShape()/endShape() pair and + * transformations such as translate(), rotate(), and scale() do not work + * within a beginContour()/endContour() pair. It is also not possible to use + * other shapes, such as ellipse() or rect() within. + * + * @method endContour + * @chainable + * @example + *
+ * + * translate(50, 50); + * stroke(255, 0, 0); + * beginShape(); + * // Exterior part of shape, clockwise winding + * vertex(-40, -40); + * vertex(40, -40); + * vertex(40, 40); + * vertex(-40, 40); + * // Interior part of shape, counter-clockwise winding + * beginContour(); + * vertex(-20, -20); + * vertex(-20, 20); + * vertex(20, 20); + * vertex(20, -20); + * endContour(); + * endShape(CLOSE); + * + *
+ * + * @alt + * white rect and smaller grey rect with red outlines in center of canvas. + * + */ +p5.prototype.endContour = function() { + var vert = contourVertices[0].slice(); // copy all data + vert.isVert = contourVertices[0].isVert; + vert.moveTo = false; + contourVertices.push(vert); + + // prevent stray lines with multiple contours + if (isFirstContour) { + vertices.push(vertices[0]); + isFirstContour = false; + } + + for (var i = 0; i < contourVertices.length; i++) { + vertices.push(contourVertices[i]); + } + return this; +}; + +/** + * The endShape() function is the companion to beginShape() and may only be + * called after beginShape(). When endshape() is called, all of image data + * defined since the previous call to beginShape() is written into the image + * buffer. The constant CLOSE as the value for the MODE parameter to close + * the shape (to connect the beginning and the end). + * + * @method endShape + * @param {Constant} [mode] use CLOSE to close the shape + * @chainable + * @example + *
+ * + * noFill(); + * + * beginShape(); + * vertex(20, 20); + * vertex(45, 20); + * vertex(45, 80); + * endShape(CLOSE); + * + * beginShape(); + * vertex(50, 20); + * vertex(75, 20); + * vertex(75, 80); + * endShape(); + * + *
+ * + * @alt + * Triangle line shape with smallest interior angle on bottom and upside-down L. + * + */ +p5.prototype.endShape = function(mode) { + if(this._renderer.isP3D){ + this._renderer.endShape(mode, isCurve, isBezier, + isQuadratic, isContour, shapeKind); + }else{ + if (vertices.length === 0) { return this; } + if (!this._renderer._doStroke && !this._renderer._doFill) { return this; } + + var closeShape = mode === constants.CLOSE; + + // if the shape is closed, the first element is also the last element + if (closeShape && !isContour) { + vertices.push(vertices[0]); + } + + this._renderer.endShape(mode, vertices, isCurve, isBezier, + isQuadratic, isContour, shapeKind); + + // Reset some settings + isCurve = false; + isBezier = false; + isQuadratic = false; + isContour = false; + isFirstContour = true; + + // If the shape is closed, the first element was added as last element. + // We must remove it again to prevent the list of vertices from growing + // over successive calls to endShape(CLOSE) + if (closeShape) { + vertices.pop(); + } + } + return this; +}; + +/** + * Specifies vertex coordinates for quadratic Bezier curves. Each call to + * quadraticVertex() defines the position of one control points and one + * anchor point of a Bezier curve, adding a new segment to a line or shape. + * The first time quadraticVertex() is used within a beginShape() call, it + * must be prefaced with a call to vertex() to set the first anchor point. + * This function must be used between beginShape() and endShape() and only + * when there is no MODE parameter specified to beginShape(). + * + * @method quadraticVertex + * @param {Number} cx x-coordinate for the control point + * @param {Number} cy y-coordinate for the control point + * @param {Number} x3 x-coordinate for the anchor point + * @param {Number} y3 y-coordinate for the anchor point + * @chainable + * @example + *
+ * + * noFill(); + * strokeWeight(4); + * beginShape(); + * vertex(20, 20); + * quadraticVertex(80, 20, 50, 50); + * endShape(); + * + *
+ * + *
+ * + * noFill(); + * strokeWeight(4); + * beginShape(); + * vertex(20, 20); + * quadraticVertex(80, 20, 50, 50); + * quadraticVertex(20, 80, 80, 80); + * vertex(80, 60); + * endShape(); + * + *
+ * + * @alt + * arched-shaped black line with 4 pixel thick stroke weight. + * backwards s-shaped black line with 4 pixel thick stroke weight. + * + */ +p5.prototype.quadraticVertex = function(cx, cy, x3, y3) { + //if we're drawing a contour, put the points into an + // array for inside drawing + if(this._contourInited) { + var pt = {}; + pt.x = cx; + pt.y = cy; + pt.x3 = x3; + pt.y3 = y3; + pt.type = constants.QUADRATIC; + this._contourVertices.push(pt); + + return this; + } + if (vertices.length > 0) { + isQuadratic = true; + var vert = []; + for (var i = 0; i < arguments.length; i++) { + vert[i] = arguments[i]; + } + vert.isVert = false; + if (isContour) { + contourVertices.push(vert); + } else { + vertices.push(vert); + } + } else { + throw 'vertex() must be used once before calling quadraticVertex()'; + } + return this; +}; + +/** + * All shapes are constructed by connecting a series of vertices. vertex() + * is used to specify the vertex coordinates for points, lines, triangles, + * quads, and polygons. It is used exclusively within the beginShape() and + * endShape() functions. + * + * @method vertex + * @param {Number} x x-coordinate of the vertex + * @param {Number} y y-coordinate of the vertex + * @param {Number|Boolean} [z] z-coordinate of the vertex + * @chainable + * @example + *
+ * + * beginShape(POINTS); + * vertex(30, 20); + * vertex(85, 20); + * vertex(85, 75); + * vertex(30, 75); + * endShape(); + * + *
+ * + * @alt + * 4 black points in a square shape in middle-right of canvas. + * + */ +p5.prototype.vertex = function(x, y, moveTo) { + if(this._renderer.isP3D){ + this._renderer.vertex(x, y, moveTo); + }else{ + var vert = []; + vert.isVert = true; + vert[0] = x; + vert[1] = y; + vert[2] = 0; + vert[3] = 0; + vert[4] = 0; + vert[5] = this._renderer._getFill(); + vert[6] = this._renderer._getStroke(); + + if (moveTo) { + vert.moveTo = moveTo; + } + if (isContour) { + if (contourVertices.length === 0) { + vert.moveTo = true; + } + contourVertices.push(vert); + } else { + vertices.push(vert); + } + } + return this; +}; + +module.exports = p5; + +},{"./constants":53,"./core":54}],73:[function(_dereq_,module,exports){ +/*global ImageData:false */ + +/** + * This module defines the filters for use with image buffers. + * + * This module is basically a collection of functions stored in an object + * as opposed to modules. The functions are destructive, modifying + * the passed in canvas rather than creating a copy. + * + * Generally speaking users of this module will use the Filters.apply method + * on a canvas to create an effect. + * + * A number of functions are borrowed/adapted from + * http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ + * or the java processing implementation. + */ + +'use strict'; + +var Filters = {}; + + +/* + * Helper functions + */ + + +/** + * Returns the pixel buffer for a canvas + * + * @private + * + * @param {Canvas|ImageData} canvas the canvas to get pixels from + * @return {Uint8ClampedArray} a one-dimensional array containing + * the data in thc RGBA order, with integer + * values between 0 and 255 + */ +Filters._toPixels = function (canvas) { + if (canvas instanceof ImageData) { + return canvas.data; + } else { + return canvas.getContext('2d').getImageData( + 0, + 0, + canvas.width, + canvas.height + ).data; + } +}; + +/** + * Returns a 32 bit number containing ARGB data at ith pixel in the + * 1D array containing pixels data. + * + * @private + * + * @param {Uint8ClampedArray} data array returned by _toPixels() + * @param {Integer} i index of a 1D Image Array + * @return {Integer} 32 bit integer value representing + * ARGB value. + */ +Filters._getARGB = function (data, i) { + var offset = i * 4; + return (data[offset+3] << 24) & 0xff000000 | + (data[offset] << 16) & 0x00ff0000 | + (data[offset+1] << 8) & 0x0000ff00 | + data[offset+2] & 0x000000ff; +}; + +/** + * Modifies pixels RGBA values to values contained in the data object. + * + * @private + * + * @param {Uint8ClampedArray} pixels array returned by _toPixels() + * @param {Int32Array} data source 1D array where each value + * represents ARGB values + */ +Filters._setPixels = function (pixels, data) { + var offset = 0; + for( var i = 0, al = pixels.length; i < al; i++) { + offset = i*4; + pixels[offset + 0] = (data[i] & 0x00ff0000)>>>16; + pixels[offset + 1] = (data[i] & 0x0000ff00)>>>8; + pixels[offset + 2] = (data[i] & 0x000000ff); + pixels[offset + 3] = (data[i] & 0xff000000)>>>24; + } +}; + +/** + * Returns the ImageData object for a canvas + * https://developer.mozilla.org/en-US/docs/Web/API/ImageData + * + * @private + * + * @param {Canvas|ImageData} canvas canvas to get image data from + * @return {ImageData} Holder of pixel data (and width and + * height) for a canvas + */ +Filters._toImageData = function (canvas) { + if (canvas instanceof ImageData) { + return canvas; + } else { + return canvas.getContext('2d').getImageData( + 0, + 0, + canvas.width, + canvas.height + ); + } +}; + +/** + * Returns a blank ImageData object. + * + * @private + * + * @param {Integer} width + * @param {Integer} height + * @return {ImageData} + */ +Filters._createImageData = function (width, height) { + Filters._tmpCanvas = document.createElement('canvas'); + Filters._tmpCtx = Filters._tmpCanvas.getContext('2d'); + return this._tmpCtx.createImageData(width, height); +}; + + +/** + * Applys a filter function to a canvas. + * + * The difference between this and the actual filter functions defined below + * is that the filter functions generally modify the pixel buffer but do + * not actually put that data back to the canvas (where it would actually + * update what is visible). By contrast this method does make the changes + * actually visible in the canvas. + * + * The apply method is the method that callers of this module would generally + * use. It has been separated from the actual filters to support an advanced + * use case of creating a filter chain that executes without actually updating + * the canvas in between everystep. + * + * @param {HTMLCanvasElement} canvas [description] + * @param {function(ImageData,Object)} func [description] + * @param {Object} filterParam [description] + */ +Filters.apply = function (canvas, func, filterParam) { + var ctx = canvas.getContext('2d'); + var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + + //Filters can either return a new ImageData object, or just modify + //the one they received. + var newImageData = func(imageData, filterParam); + if (newImageData instanceof ImageData) { + ctx.putImageData(newImageData, 0, 0, 0, 0, canvas.width, canvas.height); + } else { + ctx.putImageData(imageData, 0, 0, 0, 0, canvas.width, canvas.height); + } +}; + + +/* + * Filters + */ + + +/** + * Converts the image to black and white pixels depending if they are above or + * below the threshold defined by the level parameter. The parameter must be + * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used. + * + * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ + * + * @param {Canvas} canvas + * @param {Float} level + */ +Filters.threshold = function (canvas, level) { + var pixels = Filters._toPixels(canvas); + + if (level === undefined) { + level = 0.5; + } + var thresh = Math.floor(level * 255); + + for (var i = 0; i < pixels.length; i += 4) { + var r = pixels[i]; + var g = pixels[i + 1]; + var b = pixels[i + 2]; + var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b); + var val; + if (gray >= thresh) { + val = 255; + } else { + val = 0; + } + pixels[i] = pixels[i + 1] = pixels[i + 2] = val; + } + +}; + + +/** + * Converts any colors in the image to grayscale equivalents. + * No parameter is used. + * + * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ + * + * @param {Canvas} canvas + */ +Filters.gray = function (canvas) { + var pixels = Filters._toPixels(canvas); + + for (var i = 0; i < pixels.length; i += 4) { + var r = pixels[i]; + var g = pixels[i + 1]; + var b = pixels[i + 2]; + + // CIE luminance for RGB + var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b); + pixels[i] = pixels[i + 1] = pixels[i + 2] = gray; + } +}; + +/** + * Sets the alpha channel to entirely opaque. No parameter is used. + * + * @param {Canvas} canvas + */ +Filters.opaque = function (canvas) { + var pixels = Filters._toPixels(canvas); + + for (var i = 0; i < pixels.length; i += 4) { + pixels[i + 3] = 255; + } + + return pixels; +}; + +/** + * Sets each pixel to its inverse value. No parameter is used. + * @param {Canvas} canvas + */ +Filters.invert = function (canvas) { + var pixels = Filters._toPixels(canvas); + + for (var i = 0; i < pixels.length; i += 4) { + pixels[i] = 255 - pixels[i]; + pixels[i + 1] = 255 - pixels[i + 1]; + pixels[i + 2] = 255 - pixels[i + 2]; + } + +}; + + +/** + * Limits each channel of the image to the number of colors specified as + * the parameter. The parameter can be set to values between 2 and 255, but + * results are most noticeable in the lower ranges. + * + * Adapted from java based processing implementation + * + * @param {Canvas} canvas + * @param {Integer} level + */ +Filters.posterize = function (canvas, level) { + var pixels = Filters._toPixels(canvas); + + if ((level < 2) || (level > 255)) { + throw new Error( + 'Level must be greater than 2 and less than 255 for posterize' + ); + } + + var levels1 = level - 1; + for (var i = 0; i < pixels.length; i+=4) { + var rlevel = pixels[i]; + var glevel = pixels[i + 1]; + var blevel = pixels[i + 2]; + + pixels[i] = (((rlevel * level) >> 8) * 255) / levels1; + pixels[i + 1] = (((glevel * level) >> 8) * 255) / levels1; + pixels[i + 2] = (((blevel * level) >> 8) * 255) / levels1; + } +}; + +/** + * reduces the bright areas in an image + * @param {Canvas} canvas + * + */ +Filters.dilate = function (canvas) { + var pixels = Filters._toPixels(canvas); + var currIdx = 0; + var maxIdx = pixels.length ? pixels.length/4 : 0; + var out = new Int32Array(maxIdx); + var currRowIdx, maxRowIdx, colOrig, colOut, currLum; + var idxRight, idxLeft, idxUp, idxDown, + colRight, colLeft, colUp, colDown, + lumRight, lumLeft, lumUp, lumDown; + + while(currIdx < maxIdx) { + currRowIdx = currIdx; + maxRowIdx = currIdx + canvas.width; + while (currIdx < maxRowIdx) { + colOrig = colOut = Filters._getARGB(pixels, currIdx); + idxLeft = currIdx - 1; + idxRight = currIdx + 1; + idxUp = currIdx - canvas.width; + idxDown = currIdx + canvas.width; + + if (idxLeft < currRowIdx) { + idxLeft = currIdx; + } + if (idxRight >= maxRowIdx) { + idxRight = currIdx; + } + if (idxUp < 0){ + idxUp = 0; + } + if (idxDown >= maxIdx) { + idxDown = currIdx; + } + colUp = Filters._getARGB(pixels, idxUp); + colLeft = Filters._getARGB(pixels, idxLeft); + colDown = Filters._getARGB(pixels, idxDown); + colRight = Filters._getARGB(pixels, idxRight); + + //compute luminance + currLum = 77*(colOrig>>16&0xff) + + 151*(colOrig>>8&0xff) + + 28*(colOrig&0xff); + lumLeft = 77*(colLeft>>16&0xff) + + 151*(colLeft>>8&0xff) + + 28*(colLeft&0xff); + lumRight = 77*(colRight>>16&0xff) + + 151*(colRight>>8&0xff) + + 28*(colRight&0xff); + lumUp = 77*(colUp>>16&0xff) + + 151*(colUp>>8&0xff) + + 28*(colUp&0xff); + lumDown = 77*(colDown>>16&0xff) + + 151*(colDown>>8&0xff) + + 28*(colDown&0xff); + + if (lumLeft > currLum) { + colOut = colLeft; + currLum = lumLeft; + } + if (lumRight > currLum) { + colOut = colRight; + currLum = lumRight; + } + if (lumUp > currLum) { + colOut = colUp; + currLum = lumUp; + } + if (lumDown > currLum) { + colOut = colDown; + currLum = lumDown; + } + out[currIdx++]=colOut; + } + } + Filters._setPixels(pixels, out); +}; + +/** + * increases the bright areas in an image + * @param {Canvas} canvas + * + */ +Filters.erode = function(canvas) { + var pixels = Filters._toPixels(canvas); + var currIdx = 0; + var maxIdx = pixels.length ? pixels.length/4 : 0; + var out = new Int32Array(maxIdx); + var currRowIdx, maxRowIdx, colOrig, colOut, currLum; + var idxRight, idxLeft, idxUp, idxDown, + colRight, colLeft, colUp, colDown, + lumRight, lumLeft, lumUp, lumDown; + + while(currIdx < maxIdx) { + currRowIdx = currIdx; + maxRowIdx = currIdx + canvas.width; + while (currIdx < maxRowIdx) { + colOrig = colOut = Filters._getARGB(pixels, currIdx); + idxLeft = currIdx - 1; + idxRight = currIdx + 1; + idxUp = currIdx - canvas.width; + idxDown = currIdx + canvas.width; + + if (idxLeft < currRowIdx) { + idxLeft = currIdx; + } + if (idxRight >= maxRowIdx) { + idxRight = currIdx; + } + if (idxUp < 0) { + idxUp = 0; + } + if (idxDown >= maxIdx) { + idxDown = currIdx; + } + colUp = Filters._getARGB(pixels, idxUp); + colLeft = Filters._getARGB(pixels, idxLeft); + colDown = Filters._getARGB(pixels, idxDown); + colRight = Filters._getARGB(pixels, idxRight); + + //compute luminance + currLum = 77*(colOrig>>16&0xff) + + 151*(colOrig>>8&0xff) + + 28*(colOrig&0xff); + lumLeft = 77*(colLeft>>16&0xff) + + 151*(colLeft>>8&0xff) + + 28*(colLeft&0xff); + lumRight = 77*(colRight>>16&0xff) + + 151*(colRight>>8&0xff) + + 28*(colRight&0xff); + lumUp = 77*(colUp>>16&0xff) + + 151*(colUp>>8&0xff) + + 28*(colUp&0xff); + lumDown = 77*(colDown>>16&0xff) + + 151*(colDown>>8&0xff) + + 28*(colDown&0xff); + + if (lumLeft < currLum) { + colOut = colLeft; + currLum = lumLeft; + } + if (lumRight < currLum) { + colOut = colRight; + currLum = lumRight; + } + if (lumUp < currLum) { + colOut = colUp; + currLum = lumUp; + } + if (lumDown < currLum) { + colOut = colDown; + currLum = lumDown; + } + + out[currIdx++]=colOut; + } + } + Filters._setPixels(pixels, out); +}; + +// BLUR + +// internal kernel stuff for the gaussian blur filter +var blurRadius; +var blurKernelSize; +var blurKernel; +var blurMult; + +/* + * Port of https://github.com/processing/processing/blob/ + * master/core/src/processing/core/PImage.java#L1250 + * + * Optimized code for building the blur kernel. + * further optimized blur code (approx. 15% for radius=20) + * bigger speed gains for larger radii (~30%) + * added support for various image types (ALPHA, RGB, ARGB) + * [toxi 050728] + */ +function buildBlurKernel(r) { + var radius = (r * 3.5)|0; + radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248); + + if (blurRadius !== radius) { + blurRadius = radius; + blurKernelSize = 1 + blurRadius<<1; + blurKernel = new Int32Array(blurKernelSize); + blurMult = new Array(blurKernelSize); + for(var l = 0; l < blurKernelSize; l++){ + blurMult[l] = new Int32Array(256); + } + + var bk,bki; + var bm,bmi; + + for (var i = 1, radiusi = radius - 1; i < radius; i++) { + blurKernel[radius+i] = blurKernel[radiusi] = bki = radiusi * radiusi; + bm = blurMult[radius+i]; + bmi = blurMult[radiusi--]; + for (var j = 0; j < 256; j++){ + bm[j] = bmi[j] = bki * j; + } + } + bk = blurKernel[radius] = radius * radius; + bm = blurMult[radius]; + + for (var k = 0; k < 256; k++){ + bm[k] = bk * k; + } + } + +} + +// Port of https://github.com/processing/processing/blob/ +// master/core/src/processing/core/PImage.java#L1433 +function blurARGB(canvas, radius) { + var pixels = Filters._toPixels(canvas); + var width = canvas.width; + var height = canvas.height; + var numPackedPixels = width * height; + var argb = new Int32Array(numPackedPixels); + for (var j = 0; j < numPackedPixels; j++) { + argb[j] = Filters._getARGB(pixels, j); + } + var sum, cr, cg, cb, ca; + var read, ri, ym, ymi, bk0; + var a2 = new Int32Array(numPackedPixels); + var r2 = new Int32Array(numPackedPixels); + var g2 = new Int32Array(numPackedPixels); + var b2 = new Int32Array(numPackedPixels); + var yi = 0; + buildBlurKernel(radius); + var x, y, i; + var bm; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + cb = cg = cr = ca = sum = 0; + read = x - blurRadius; + if (read < 0) { + bk0 = -read; + read = 0; + } else { + if (read >= width) { + break; + } + bk0 = 0; + } + for (i = bk0; i < blurKernelSize; i++) { + if (read >= width) { + break; + } + var c = argb[read + yi]; + bm = blurMult[i]; + ca += bm[(c & -16777216) >>> 24]; + cr += bm[(c & 16711680) >> 16]; + cg += bm[(c & 65280) >> 8]; + cb += bm[c & 255]; + sum += blurKernel[i]; + read++; + } + ri = yi + x; + a2[ri] = ca / sum; + r2[ri] = cr / sum; + g2[ri] = cg / sum; + b2[ri] = cb / sum; + } + yi += width; + } + yi = 0; + ym = -blurRadius; + ymi = ym * width; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + cb = cg = cr = ca = sum = 0; + if (ym < 0) { + bk0 = ri = -ym; + read = x; + } else { + if (ym >= height) { + break; + } + bk0 = 0; + ri = ym; + read = x + ymi; + } + for (i = bk0; i < blurKernelSize; i++) { + if (ri >= height) { + break; + } + bm = blurMult[i]; + ca += bm[a2[read]]; + cr += bm[r2[read]]; + cg += bm[g2[read]]; + cb += bm[b2[read]]; + sum += blurKernel[i]; + ri++; + read += width; + } + argb[x + yi] = (ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum); + } + yi += width; + ymi += width; + ym++; + } + Filters._setPixels(pixels, argb); +} + +Filters.blur = function(canvas, radius){ + blurARGB(canvas, radius); +}; + + +module.exports = Filters; + +},{}],74:[function(_dereq_,module,exports){ +/** + * @module Image + * @submodule Image + * @for p5 + * @requires core + */ + +/** + * This module defines the p5 methods for the p5.Image class + * for drawing images to the main display canvas. + */ +'use strict'; + + +var p5 = _dereq_('../core/core'); + +/* global frames:true */// This is not global, but JSHint is not aware that +// this module is implicitly enclosed with Browserify: this overrides the +// redefined-global error and permits using the name "frames" for the array +// of saved animation frames. +var frames = []; + + +/** + * Creates a new p5.Image (the datatype for storing images). This provides a + * fresh buffer of pixels to play with. Set the size of the buffer with the + * width and height parameters. + *

+ * .pixels gives access to an array containing the values for all the pixels + * in the display window. + * These values are numbers. This array is the size (including an appropriate + * factor for the pixelDensity) of the display window x4, + * representing the R, G, B, A values in order for each pixel, moving from + * left to right across each row, then down each column. See .pixels for + * more info. It may also be simpler to use set() or get(). + *

+ * Before accessing the pixels of an image, the data must loaded with the + * loadPixels() function. After the array data has been modified, the + * updatePixels() function must be run to update the changes. + * + * @method createImage + * @param {Integer} width width in pixels + * @param {Integer} height height in pixels + * @return {p5.Image} the p5.Image object + * @example + *
+ * + * img = createImage(66, 66); + * img.loadPixels(); + * for (i = 0; i < img.width; i++) { + * for (j = 0; j < img.height; j++) { + * img.set(i, j, color(0, 90, 102)); + * } + * } + * img.updatePixels(); + * image(img, 17, 17); + * + *
+ * + *
+ * + * img = createImage(66, 66); + * img.loadPixels(); + * for (i = 0; i < img.width; i++) { + * for (j = 0; j < img.height; j++) { + * img.set(i, j, color(0, 90, 102, i % img.width * 2)); + * } + * } + * img.updatePixels(); + * image(img, 17, 17); + * image(img, 34, 34); + * + *
+ * + *
+ * + * var pink = color(255, 102, 204); + * img = createImage(66, 66); + * img.loadPixels(); + * var d = pixelDensity(); + * var halfImage = 4 * (width * d) * (height/2 * d); + * for (var i = 0; i < halfImage; i+=4) { + * img.pixels[i] = red(pink); + * img.pixels[i+1] = green(pink); + * img.pixels[i+2] = blue(pink); + * img.pixels[i+3] = alpha(pink); + * } + * img.updatePixels(); + * image(img, 17, 17); + * + *
+ * + * @alt + * 66x66 dark turquoise rect in center of canvas. + * 2 gradated dark turquoise rects fade left. 1 center 1 bottom right of canvas + * no image displayed + * + */ +p5.prototype.createImage = function(width, height) { + return new p5.Image(width, height); +}; + +/** + * Save the current canvas as an image. In Safari, this will open the + * image in the window and the user must provide their own + * filename on save-as. Other browsers will either save the + * file immediately, or prompt the user with a dialogue window. + * + * @method saveCanvas + * @param {p5.Element|HTMLCanvasElement} selectedCanvas a variable + * representing a specific html5 canvas (optional) + * @param {String} [filename] + * @param {String} [extension] 'jpg' or 'png' +*/ +/** + * @method saveCanvas + * @param {String} [filename] + * @param {String} [extension] + * + * @example + *
+ * function setup() { + * var c = createCanvas(100, 100); + * background(255, 0, 0); + * saveCanvas(c, 'myCanvas', 'jpg'); + * } + *
+ *
+ * // note that this example has the same result as above + * // if no canvas is specified, defaults to main canvas + * function setup() { + * createCanvas(100, 100); + * background(255, 0, 0); + * saveCanvas('myCanvas', 'jpg'); + * } + *
+ *
+ * // all of the following are valid + * saveCanvas(c, 'myCanvas', 'jpg'); + * saveCanvas(c, 'myCanvas'); + * saveCanvas(c); + * saveCanvas('myCanvas', 'png'); + * saveCanvas('myCanvas'); + * saveCanvas(); + *
+ * + * @alt + * no image displayed + * no image displayed + * no image displayed + * + */ +p5.prototype.saveCanvas = function() { + + var cnv, filename, extension; + if (arguments.length === 3) { + cnv = arguments[0]; + filename = arguments[1]; + extension = arguments[2]; + } else if (arguments.length === 2) { + if (typeof arguments[0] === 'object') { + cnv = arguments[0]; + filename = arguments[1]; + } else { + filename = arguments[0]; + extension = arguments[1]; + } + } else if (arguments.length === 1) { + if (typeof arguments[0] === 'object') { + cnv = arguments[0]; + } else { + filename = arguments[0]; + } + } + + if (cnv instanceof p5.Element) { + cnv = cnv.elt; + } + if (!(cnv instanceof HTMLCanvasElement)) { + cnv = null; + } + + if (!extension) { + extension = p5.prototype._checkFileExtension(filename, extension)[1]; + if (extension === '') { + extension = 'png'; + } + } + + if (!cnv) { + if (this._curElement && this._curElement.elt) { + cnv = this._curElement.elt; + } + } + + if ( p5.prototype._isSafari() ) { + var aText = 'Hello, Safari user!\n'; + aText += 'Now capturing a screenshot...\n'; + aText += 'To save this image,\n'; + aText += 'go to File --> Save As.\n'; + alert(aText); + window.location.href = cnv.toDataURL(); + } else { + var mimeType; + if (typeof(extension) === 'undefined') { + extension = 'png'; + mimeType = 'image/png'; + } + else { + switch(extension){ + case 'png': + mimeType = 'image/png'; + break; + case 'jpeg': + mimeType = 'image/jpeg'; + break; + case 'jpg': + mimeType = 'image/jpeg'; + break; + default: + mimeType = 'image/png'; + break; + } + } + var downloadMime = 'image/octet-stream'; + var imageData = cnv.toDataURL(mimeType); + imageData = imageData.replace(mimeType, downloadMime); + + p5.prototype.downloadFile(imageData, filename, extension); + } +}; + +/** + * Capture a sequence of frames that can be used to create a movie. + * Accepts a callback. For example, you may wish to send the frames + * to a server where they can be stored or converted into a movie. + * If no callback is provided, the browser will pop up save dialogues in an + * attempt to download all of the images that have just been created. With the + * callback provided the image data isn't saved by default but instead passed + * as an argument to the callback function as an array of objects, with the + * size of array equal to the total number of frames. + * + * @method saveFrames + * @param {String} filename + * @param {String} extension 'jpg' or 'png' + * @param {Number} duration Duration in seconds to save the frames for. + * @param {Number} framerate Framerate to save the frames in. + * @param {function(Array)} [callback] A callback function that will be executed + to handle the image data. This function + should accept an array as argument. The + array will contain the specified number of + frames of objects. Each object has three + properties: imageData - an + image/octet-stream, filename and extension. + * @example + *
+ * function draw() { + * background(mouseX); + * } + * + * function mousePressed() { + * saveFrames("out", "png", 1, 25, function(data){ + * print(data); + * }); + * } + *
+ * + * @alt + * canvas background goes from light to dark with mouse x. + * + */ +p5.prototype.saveFrames = function(fName, ext, _duration, _fps, callback) { + var duration = _duration || 3; + duration = p5.prototype.constrain(duration, 0, 15); + duration = duration * 1000; + var fps = _fps || 15; + fps = p5.prototype.constrain(fps, 0, 22); + var count = 0; + + var makeFrame = p5.prototype._makeFrame; + var cnv = this._curElement.elt; + var frameFactory = setInterval(function(){ + makeFrame(fName + count, ext, cnv); + count++; + },1000/fps); + + setTimeout(function(){ + clearInterval(frameFactory); + if (callback) { + callback(frames); + } + else { + for (var i = 0; i < frames.length; i++) { + var f = frames[i]; + p5.prototype.downloadFile(f.imageData, f.filename, f.ext); + } + } + frames = []; // clear frames + }, duration + 0.01); +}; + +p5.prototype._makeFrame = function(filename, extension, _cnv) { + var cnv; + if (this) { + cnv = this._curElement.elt; + } else { + cnv = _cnv; + } + var mimeType; + if (!extension) { + extension = 'png'; + mimeType = 'image/png'; + } + else { + switch(extension.toLowerCase()){ + case 'png': + mimeType = 'image/png'; + break; + case 'jpeg': + mimeType = 'image/jpeg'; + break; + case 'jpg': + mimeType = 'image/jpeg'; + break; + default: + mimeType = 'image/png'; + break; + } + } + var downloadMime = 'image/octet-stream'; + var imageData = cnv.toDataURL(mimeType); + imageData = imageData.replace(mimeType, downloadMime); + + var thisFrame = {}; + thisFrame.imageData = imageData; + thisFrame.filename = filename; + thisFrame.ext = extension; + frames.push(thisFrame); +}; + +module.exports = p5; + +},{"../core/core":54}],75:[function(_dereq_,module,exports){ +/** + * @module Image + * @submodule Loading & Displaying + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var Filters = _dereq_('./filters'); +var canvas = _dereq_('../core/canvas'); +var constants = _dereq_('../core/constants'); + +_dereq_('../core/error_helpers'); + +/** + * Loads an image from a path and creates a p5.Image from it. + *

+ * The image may not be immediately available for rendering + * If you want to ensure that the image is ready before doing + * anything with it, place the loadImage() call in preload(). + * You may also supply a callback function to handle the image when it's ready. + *

+ * The path to the image should be relative to the HTML file + * that links in your sketch. Loading an image from a URL or other + * remote location may be blocked due to your browser's built-in + * security. + * + * @method loadImage + * @param {String} path Path of the image to be loaded + * @param {function(p5.Image)} [successCallback] Function to be called once + * the image is loaded. Will be passed the + * p5.Image. + * @param {function(Event)} [failureCallback] called with event error if + * the image fails to load. + * @return {p5.Image} the p5.Image object + * @example + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * } + * + *
+ *
+ * + * function setup() { + * // here we use a callback to display the image after loading + * loadImage("assets/laDefense.jpg", function(img) { + * image(img, 0, 0); + * }); + * } + * + *
+ * + * @alt + * image of the underside of a white umbrella and grided ceililng above + * image of the underside of a white umbrella and grided ceililng above + * + */ +p5.prototype.loadImage = function(path, successCallback, failureCallback) { + var img = new Image(); + var pImg = new p5.Image(1, 1, this); + + var self = this; + img.onload = function() { + pImg.width = pImg.canvas.width = img.width; + pImg.height = pImg.canvas.height = img.height; + + // Draw the image into the backing canvas of the p5.Image + pImg.drawingContext.drawImage(img, 0, 0); + + if (typeof successCallback === 'function') { + successCallback(pImg); + } + + self._decrementPreload(); + }; + img.onerror = function(e) { + p5._friendlyFileLoadError(0,img.src); + if (typeof failureCallback === 'function') { + failureCallback(e); + } + }; + + //set crossOrigin in case image is served which CORS headers + //this will let us draw to canvas without tainting it. + //see https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image + // When using data-uris the file will be loaded locally + // so we don't need to worry about crossOrigin with base64 file types + if(path.indexOf('data:image/') !== 0) { + img.crossOrigin = 'Anonymous'; + } + + //start loading the image + img.src = path; + + return pImg; +}; + +/** + * Validates clipping params. Per drawImage spec sWidth and sHight cannot be + * negative or greater than image intrinsic width and height + * @private + * @param {Number} sVal + * @param {Number} iVal + * @returns {Number} + * @private + */ +function _sAssign(sVal, iVal) { + if (sVal > 0 && sVal < iVal) { + return sVal; + } + else { + return iVal; + } +} + +/** + * Draw an image to the main canvas of the p5js sketch + * + * @method image + * @param {p5.Image} img the image to display + * @param {Number} x the x-coordinate at which to place the top-left + * corner of the source image + * @param {Number} y the y-coordinate at which to place the top-left + * corner of the source image + * @param {Number} [width] the width to draw the image + * @param {Number} [height] the height to draw the image + * @example + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * image(img, 0, 0, 100, 100); + * image(img, 0, 0, 100, 100, 0, 0, 100, 100); + * } + * + *
+ *
+ * + * function setup() { + * // here we use a callback to display the image after loading + * loadImage("assets/laDefense.jpg", function(img) { + * image(img, 0, 0); + * }); + * } + * + *
+ * + * @alt + * image of the underside of a white umbrella and grided ceiling above + * image of the underside of a white umbrella and grided ceiling above + * + */ +/** + * @method image + * @param {p5.Image} img + * @param {Number} x + * @param {Number} y + * @param {Number} width + * @param {Number} height + * @param {Number} sx the x-coordinate of the top left corner of the + * sub-rectangle of the source image to draw into + * the destination canvas + * @param {Number} sy the y-coordinate of the top left corner of the + * sub-rectangle of the source image to draw into + * the destination canvas + * @param {Number} [sWidth] the width of the sub-rectangle of the + * source image to draw into the destination + * canvas + * @param {Number} [sHeight] the height of the sub-rectangle of the + * source image to draw into the destination context + */ +p5.prototype.image = + function(img, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight) { + // set defaults per spec: https://goo.gl/3ykfOq + + var defW = img.width; + var defH = img.height; + + if (img.elt && img.elt.videoWidth && !img.canvas) { // video no canvas + defW = img.elt.videoWidth; + defH = img.elt.videoHeight; + } + + var _dx = dx; + var _dy = dy; + var _dw = dWidth || defW; + var _dh = dHeight || defH; + var _sx = sx || 0; + var _sy = sy || 0; + var _sw = sWidth || defW; + var _sh = sHeight || defH; + + _sw = _sAssign(_sw, defW); + _sh = _sAssign(_sh, defH); + + + // This part needs cleanup and unit tests + // see issues https://github.com/processing/p5.js/issues/1741 + // and https://github.com/processing/p5.js/issues/1673 + var pd = 1; + + if (img.elt && !img.canvas && img.elt.style.width) { + //if img is video and img.elt.size() has been used and + //no width passed to image() + if(img.elt.videoWidth && !dWidth){ + pd = img.elt.videoWidth; + } + //all other cases + else { + pd = img.elt.width; + } + pd /= parseInt(img.elt.style.width, 10); + } + + _sx *= pd; + _sy *= pd; + _sh *= pd; + _sw *= pd; + + var vals = canvas.modeAdjust(_dx, _dy, _dw, _dh, + this._renderer._imageMode); + + // tint the image if there is a tint + this._renderer.image(img, _sx, _sy, _sw, _sh, vals.x, vals.y, vals.w, + vals.h); +}; + + +/** + * Sets the fill value for displaying images. Images can be tinted to + * specified colors or made transparent by including an alpha value. + *

+ * To apply transparency to an image without affecting its color, use + * white as the tint color and specify an alpha value. For instance, + * tint(255, 128) will make an image 50% transparent (assuming the default + * alpha range of 0-255, which can be changed with colorMode()). + *

+ * The value for the gray parameter must be less than or equal to the current + * maximum value as specified by colorMode(). The default maximum value is + * 255. + * + * + * @method tint + * @param {Number} v1 red or hue value relative to + * the current color range + * @param {Number} v2 green or saturation value + * relative to the current color range + * @param {Number} v3 blue or brightness value + * relative to the current color range + * @param {Number} [alpha] + */ + +/** + * @method tint + * @param {String} value a color string + * @param {Number} [alpha] + */ + +/** + * @method tint + * @param {Number[]} values an array containing the red,green,blue & + * and alpha components of the color + */ + +/** + * @method tint + * @param {p5.Color} color the tint color + * @param {Number} [alpha] + * + * @example + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * tint(0, 153, 204); // Tint blue + * image(img, 50, 0); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * tint(0, 153, 204, 126); // Tint blue and set transparency + * image(img, 50, 0); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/laDefense.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * tint(255, 126); // Apply transparency without changing color + * image(img, 50, 0); + * } + * + *
+ * + * @alt + * 2 side by side images of umbrella and ceiling, one image with blue tint + * Images of umbrella and ceiling, one half of image with blue tint + * 2 side by side images of umbrella and ceiling, one image translucent + * + */ +p5.prototype.tint = function () { + var c = this.color.apply(this, arguments); + this._renderer._tint = c.levels; +}; + +/** + * Removes the current fill value for displaying images and reverts to + * displaying images with their original hues. + * + * @method noTint + * @example + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * tint(0, 153, 204); // Tint blue + * image(img, 0, 0); + * noTint(); // Disable tint + * image(img, 50, 0); + * } + * + *
+ * + * @alt + * 2 side by side images of bricks, left image with blue tint + * + */ +p5.prototype.noTint = function() { + this._renderer._tint = null; +}; + +/** + * Apply the current tint color to the input image, return the resulting + * canvas. + * + * @param {p5.Image} The image to be tinted + * @return {canvas} The resulting tinted canvas + * + */ +p5.prototype._getTintedImageCanvas = function(img) { + if (!img.canvas) { + return img; + } + var pixels = Filters._toPixels(img.canvas); + var tmpCanvas = document.createElement('canvas'); + tmpCanvas.width = img.canvas.width; + tmpCanvas.height = img.canvas.height; + var tmpCtx = tmpCanvas.getContext('2d'); + var id = tmpCtx.createImageData(img.canvas.width, img.canvas.height); + var newPixels = id.data; + + for(var i = 0; i < pixels.length; i += 4) { + var r = pixels[i]; + var g = pixels[i+1]; + var b = pixels[i+2]; + var a = pixels[i+3]; + + newPixels[i] = r*this._renderer._tint[0]/255; + newPixels[i+1] = g*this._renderer._tint[1]/255; + newPixels[i+2] = b*this._renderer._tint[2]/255; + newPixels[i+3] = a*this._renderer._tint[3]/255; + } + + tmpCtx.putImageData(id, 0, 0); + return tmpCanvas; +}; + +/** + * Set image mode. Modifies the location from which images are drawn by + * changing the way in which parameters given to image() are interpreted. + * The default mode is imageMode(CORNER), which interprets the second and + * third parameters of image() as the upper-left corner of the image. If + * two additional parameters are specified, they are used to set the image's + * width and height. + *

+ * imageMode(CORNERS) interprets the second and third parameters of image() + * as the location of one corner, and the fourth and fifth parameters as the + * opposite corner. + *

+ * imageMode(CENTER) interprets the second and third parameters of image() + * as the image's center point. If two additional parameters are specified, + * they are used to set the image's width and height. + * + * @method imageMode + * @param {Constant} mode either CORNER, CORNERS, or CENTER + * @example + * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * imageMode(CORNER); + * image(img, 10, 10, 50, 50); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * imageMode(CORNERS); + * image(img, 10, 10, 90, 40); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * imageMode(CENTER); + * image(img, 50, 50, 80, 80); + * } + * + *
+ * + * @alt + * small square image of bricks + * horizontal rectangle image of bricks + * large square image of bricks + * + */ +p5.prototype.imageMode = function(m) { + if (m === constants.CORNER || + m === constants.CORNERS || + m === constants.CENTER) { + this._renderer._imageMode = m; + } +}; + + +module.exports = p5; + +},{"../core/canvas":52,"../core/constants":53,"../core/core":54,"../core/error_helpers":57,"./filters":73}],76:[function(_dereq_,module,exports){ +/** + * @module Image + * @submodule Image + * @requires core + * @requires constants + * @requires filters + */ + +/** + * This module defines the p5.Image class and P5 methods for + * drawing images to the main display canvas. + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var Filters = _dereq_('./filters'); + +/* + * Class methods + */ + +/** + * Creates a new p5.Image. A p5.Image is a canvas backed representation of an + * image. + *

+ * p5 can display .gif, .jpg and .png images. Images may be displayed + * in 2D and 3D space. Before an image is used, it must be loaded with the + * loadImage() function. The p5.Image class contains fields for the width and + * height of the image, as well as an array called pixels[] that contains the + * values for every pixel in the image. + *

+ * The methods described below allow easy access to the image's pixels and + * alpha channel and simplify the process of compositing. + *

+ * Before using the pixels[] array, be sure to use the loadPixels() method on + * the image to make sure that the pixel data is properly loaded. + * + * @class p5.Image + * @constructor + * @param {Number} width + * @param {Number} height + */ +p5.Image = function(width, height){ + /** + * Image width. + * @property {Number} width + * @readOnly + * @example + *
+ * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * createCanvas(100, 100); + * image(img, 0, 0); + * for (var i=0; i < img.width; i++) { + * var c = img.get(i, img.height/2); + * stroke(c); + * line(i, height/2, i, height); + * } + * } + *
+ * + * @alt + * rocky mountains in top and horizontal lines in corresponding colors in bottom. + * + */ + this.width = width; + /** + * Image height. + * @property {Number} height + * @readOnly + * @example + *
+ * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * createCanvas(100, 100); + * image(img, 0, 0); + * for (var i=0; i < img.height; i++) { + * var c = img.get(img.width/2, i); + * stroke(c); + * line(0, i, width/2, i); + * } + * } + *
+ * + * @alt + * rocky mountains on right and vertical lines in corresponding colors on left. + * + */ + this.height = height; + this.canvas = document.createElement('canvas'); + this.canvas.width = this.width; + this.canvas.height = this.height; + this.drawingContext = this.canvas.getContext('2d'); + this._pixelDensity = 1; + //used for webgl texturing only + this.isTexture = false; + /** + * Array containing the values for all the pixels in the display window. + * These values are numbers. This array is the size (include an appropriate + * factor for pixelDensity) of the display window x4, + * representing the R, G, B, A values in order for each pixel, moving from + * left to right across each row, then down each column. Retina and other + * high denisty displays may have more pixels[] (by a factor of + * pixelDensity^2). + * For example, if the image is 100x100 pixels, there will be 40,000. With + * pixelDensity = 2, there will be 160,000. The first four values + * (indices 0-3) in the array will be the R, G, B, A values of the pixel at + * (0, 0). The second four values (indices 4-7) will contain the R, G, B, A + * values of the pixel at (1, 0). More generally, to set values for a pixel + * at (x, y): + * ```javascript + * var d = pixelDensity; + * for (var i = 0; i < d; i++) { + * for (var j = 0; j < d; j++) { + * // loop over + * idx = 4 * ((y * d + j) * width * d + (x * d + i)); + * pixels[idx] = r; + * pixels[idx+1] = g; + * pixels[idx+2] = b; + * pixels[idx+3] = a; + * } + * } + * ``` + *

+ * Before accessing this array, the data must loaded with the loadPixels() + * function. After the array data has been modified, the updatePixels() + * function must be run to update the changes. + * @property {Number[]} pixels + * @example + *
+ * + * img = createImage(66, 66); + * img.loadPixels(); + * for (i = 0; i < img.width; i++) { + * for (j = 0; j < img.height; j++) { + * img.set(i, j, color(0, 90, 102)); + * } + * } + * img.updatePixels(); + * image(img, 17, 17); + * + *
+ *
+ * + * var pink = color(255, 102, 204); + * img = createImage(66, 66); + * img.loadPixels(); + * for (var i = 0; i < 4*(width*height/2); i+=4) { + * img.pixels[i] = red(pink); + * img.pixels[i+1] = green(pink); + * img.pixels[i+2] = blue(pink); + * img.pixels[i+3] = alpha(pink); + * } + * img.updatePixels(); + * image(img, 17, 17); + * + *
+ * + * @alt + * 66x66 turquoise rect in center of canvas + * 66x66 pink rect in center of canvas + * + */ + this.pixels = []; +}; + +/** + * Helper fxn for sharing pixel methods + * + */ +p5.Image.prototype._setProperty = function (prop, value) { + this[prop] = value; +}; + +/** + * Loads the pixels data for this image into the [pixels] attribute. + * + * @method loadPixels + * @example + *
+ * var myImage; + * var halfImage; + * + * function preload() { + * myImage = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * myImage.loadPixels(); + * halfImage = 4 * width * height/2; + * for(var i = 0; i < halfImage; i++){ + * myImage.pixels[i+halfImage] = myImage.pixels[i]; + * } + * myImage.updatePixels(); + * } + * + * function draw() { + * image(myImage, 0, 0); + * } + *
+ * + * @alt + * 2 images of rocky mountains vertically stacked + * + */ +p5.Image.prototype.loadPixels = function(){ + p5.Renderer2D.prototype.loadPixels.call(this); +}; + +/** + * Updates the backing canvas for this image with the contents of + * the [pixels] array. + * + * @method updatePixels + * @param {Integer} x x-offset of the target update area for the + * underlying canvas + * @param {Integer} y y-offset of the target update area for the + * underlying canvas + * @param {Integer} w height of the target update area for the + * underlying canvas + * @param {Integer} h height of the target update area for the + * underlying canvas + */ +/** + * @method updatePixels + * @example + *
+ * var myImage; + * var halfImage; + * + * function preload() { + * myImage = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * myImage.loadPixels(); + * halfImage = 4 * width * height/2; + * for(var i = 0; i < halfImage; i++){ + * myImage.pixels[i+halfImage] = myImage.pixels[i]; + * } + * myImage.updatePixels(); + * } + * + * function draw() { + * image(myImage, 0, 0); + * } + *
+ * + * @alt + * 2 images of rocky mountains vertically stacked + * + */ +p5.Image.prototype.updatePixels = function(x, y, w, h){ + p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h); +}; + +/** + * Get a region of pixels from an image. + * + * If no params are passed, those whole image is returned, + * if x and y are the only params passed a single pixel is extracted + * if all params are passed a rectangle region is extracted and a p5.Image + * is returned. + * + * Returns undefined if the region is outside the bounds of the image + * + * @method get + * @param {Number} [x] x-coordinate of the pixel + * @param {Number} [y] y-coordinate of the pixel + * @param {Number} [w] width + * @param {Number} [h] height + * @return {Number[]|Color|p5.Image} color of pixel at x,y in array format + * [R, G, B, A] or p5.Image + * @example + *
+ * var myImage; + * var c; + * + * function preload() { + * myImage = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * background(myImage); + * noStroke(); + * c = myImage.get(60, 90); + * fill(c); + * rect(25, 25, 50, 50); + * } + * + * //get() returns color here + *
+ * + * @alt + * image of rocky mountains with 50x50 green rect in front + * + */ +p5.Image.prototype.get = function(x, y, w, h){ + return p5.Renderer2D.prototype.get.call(this, x, y, w, h); +}; + +/** + * Set the color of a single pixel or write an image into + * this p5.Image. + * + * Note that for a large number of pixels this will + * be slower than directly manipulating the pixels array + * and then calling updatePixels(). + * + * @method set + * @param {Number} x x-coordinate of the pixel + * @param {Number} y y-coordinate of the pixel + * @param {Number|Array|Object} a grayscale value | pixel array | + * a p5.Color | image to copy + * @example + *
+ * + * img = createImage(66, 66); + * img.loadPixels(); + * for (i = 0; i < img.width; i++) { + * for (j = 0; j < img.height; j++) { + * img.set(i, j, color(0, 90, 102, i % img.width * 2)); + * } + * } + * img.updatePixels(); + * image(img, 17, 17); + * image(img, 34, 34); + * + *
+ * + * @alt + * 2 gradated dark turquoise rects fade left. 1 center 1 bottom right of canvas + * + */ +p5.Image.prototype.set = function(x, y, imgOrCol){ + p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol); +}; + +/** + * Resize the image to a new width and height. To make the image scale + * proportionally, use 0 as the value for the wide or high parameter. + * For instance, to make the width of an image 150 pixels, and change + * the height using the same proportion, use resize(150, 0). + * + * @method resize + * @param {Number} width the resized image width + * @param {Number} height the resized image height + * @example + *
+ * var img; + * + * function setup() { + * img = loadImage("assets/rockies.jpg"); + * } + + * function draw() { + * image(img, 0, 0); + * } + * + * function mousePressed() { + * img.resize(50, 100); + * } + *
+ * + * @alt + * image of rocky mountains. zoomed in + * + */ +p5.Image.prototype.resize = function(width, height){ + + // Copy contents to a temporary canvas, resize the original + // and then copy back. + // + // There is a faster approach that involves just one copy and swapping the + // this.canvas reference. We could switch to that approach if (as i think + // is the case) there an expectation that the user would not hold a + // reference to the backing canvas of a p5.Image. But since we do not + // enforce that at the moment, I am leaving in the slower, but safer + // implementation. + + // auto-resize + if (width === 0 && height === 0) { + width = this.canvas.width; + height = this.canvas.height; + } else if (width === 0) { + width = this.canvas.width * height / this.canvas.height; + } else if (height === 0) { + height = this.canvas.height * width / this.canvas.width; + } + + width = Math.floor(width); + height = Math.floor(height); + + var tempCanvas = document.createElement('canvas'); + tempCanvas.width = width; + tempCanvas.height = height; + tempCanvas.getContext('2d').drawImage(this.canvas, + 0, 0, this.canvas.width, this.canvas.height, + 0, 0, tempCanvas.width, tempCanvas.height + ); + + + // Resize the original canvas, which will clear its contents + this.canvas.width = this.width = width; + this.canvas.height = this.height = height; + + //Copy the image back + + this.drawingContext.drawImage(tempCanvas, + 0, 0, width, height, + 0, 0, width, height + ); + + if(this.pixels.length > 0){ + this.loadPixels(); + } +}; + +/** + * Copies a region of pixels from one image to another. If no + * srcImage is specified this is used as the source. If the source + * and destination regions aren't the same size, it will + * automatically resize source pixels to fit the specified + * target region. + * + * @method copy + * @param {p5.Image|undefined} srcImage source image + * @param {Integer} sx X coordinate of the source's upper left corner + * @param {Integer} sy Y coordinate of the source's upper left corner + * @param {Integer} sw source image width + * @param {Integer} sh source image height + * @param {Integer} dx X coordinate of the destination's upper left corner + * @param {Integer} dy Y coordinate of the destination's upper left corner + * @param {Integer} dw destination image width + * @param {Integer} dh destination image height + * @example + *
+ * var photo; + * var bricks; + * var x; + * var y; + * + * function preload() { + * photo = loadImage("assets/rockies.jpg"); + * bricks = loadImage("assets/bricks.jpg"); + * } + * + * function setup() { + * x = bricks.width/2; + * y = bricks.height/2; + * photo.copy(bricks, 0, 0, x, y, 0, 0, x, y); + * image(photo, 0, 0); + * } + *
+ * + * @alt + * image of rocky mountains and smaller image on top of bricks at top left + * + */ +p5.Image.prototype.copy = function () { + p5.prototype.copy.apply(this, arguments); +}; + +/** + * Masks part of an image from displaying by loading another + * image and using it's alpha channel as an alpha channel for + * this image. + * + * @method mask + * @param {p5.Image} srcImage source image + * @example + *
+ * var photo, maskImage; + * function preload() { + * photo = loadImage("assets/rockies.jpg"); + * maskImage = loadImage("assets/mask2.png"); + * } + * + * function setup() { + * createCanvas(100, 100); + * photo.mask(maskImage); + * image(photo, 0, 0); + * } + *
+ * + * @alt + * image of rocky mountains with white at right + * + * + * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ + * + */ +// TODO: - Accept an array of alpha values. +// - Use other channels of an image. p5 uses the +// blue channel (which feels kind of arbitrary). Note: at the +// moment this method does not match native processings original +// functionality exactly. +p5.Image.prototype.mask = function(p5Image) { + if(p5Image === undefined){ + p5Image = this; + } + var currBlend = this.drawingContext.globalCompositeOperation; + + var scaleFactor = 1; + if (p5Image instanceof p5.Renderer) { + scaleFactor = p5Image._pInst._pixelDensity; + } + + var copyArgs = [ + p5Image, + 0, + 0, + scaleFactor*p5Image.width, + scaleFactor*p5Image.height, + 0, + 0, + this.width, + this.height + ]; + + this.drawingContext.globalCompositeOperation = 'destination-in'; + p5.Image.prototype.copy.apply(this, copyArgs); + this.drawingContext.globalCompositeOperation = currBlend; +}; + +/** + * Applies an image filter to a p5.Image + * + * @method filter + * @param {String} operation one of threshold, gray, invert, posterize, opaque + * erode, dilate and blur. See Filters.js for docs on + * each available filter + * @param {Number|undefined} value + * @example + *
+ * var photo1; + * var photo2; + * + * function preload() { + * photo1 = loadImage("assets/rockies.jpg"); + * photo2 = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * photo2.filter("gray"); + * image(photo1, 0, 0); + * image(photo2, width/2, 0); + * } + *
+ * + * @alt + * 2 images of rocky mountains left one in color, right in black and white + * + */ +p5.Image.prototype.filter = function(operation, value) { + Filters.apply(this.canvas, Filters[operation.toLowerCase()], value); +}; + +/** + * Copies a region of pixels from one image to another, using a specified + * blend mode to do the operation. + * + * @method blend + * @param {p5.Image|undefined} srcImage source image + * @param {Integer} sx X coordinate of the source's upper left corner + * @param {Integer} sy Y coordinate of the source's upper left corner + * @param {Integer} sw source image width + * @param {Integer} sh source image height + * @param {Integer} dx X coordinate of the destination's upper left corner + * @param {Integer} dy Y coordinate of the destination's upper left corner + * @param {Integer} dw destination image width + * @param {Integer} dh destination image height + * @param {Integer} blendMode the blend mode + * + * Available blend modes are: normal | multiply | screen | overlay | + * darken | lighten | color-dodge | color-burn | hard-light | + * soft-light | difference | exclusion | hue | saturation | + * color | luminosity + * + * + * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/ + * @example + *
+ * var mountains; + * var bricks; + * + * function preload() { + * mountains = loadImage("assets/rockies.jpg"); + * bricks = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, ADD); + * image(mountains, 0, 0); + * image(bricks, 0, 0); + * } + *
+ *
+ * var mountains; + * var bricks; + * + * function preload() { + * mountains = loadImage("assets/rockies.jpg"); + * bricks = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST); + * image(mountains, 0, 0); + * image(bricks, 0, 0); + * } + *
+ *
+ * var mountains; + * var bricks; + * + * function preload() { + * mountains = loadImage("assets/rockies.jpg"); + * bricks = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * mountains.blend(bricks, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST); + * image(mountains, 0, 0); + * image(bricks, 0, 0); + * } + *
+ * + * @alt + * image of rocky mountains. Brick images on left and right. Right overexposed + * image of rockies. Brickwall images on left and right. Right mortar transparent + * image of rockies. Brickwall images on left and right. Right translucent + * + */ +p5.Image.prototype.blend = function() { + p5.prototype.blend.apply(this, arguments); +}; + +/** + * Saves the image to a file and force the browser to download it. + * Accepts two strings for filename and file extension + * Supports png (default) and jpg. + * + * @method save + * @param {String} filename give your file a name + * @param {String} extension 'png' or 'jpg' + * @example + *
+ * var photo; + * + * function preload() { + * photo = loadImage("assets/rockies.jpg"); + * } + * + * function draw() { + * image(photo, 0, 0); + * } + * + * function keyTyped() { + * if (key == 's') { + * photo.save("photo", "png"); + * } + * } + *
+ * + * @alt + * image of rocky mountains. + * + */ +p5.Image.prototype.save = function(filename, extension) { + var mimeType; + if (!extension) { + extension = 'png'; + mimeType = 'image/png'; + } + else { + // en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support + switch(extension.toLowerCase()){ + case 'png': + mimeType = 'image/png'; + break; + case 'jpeg': + mimeType = 'image/jpeg'; + break; + case 'jpg': + mimeType = 'image/jpeg'; + break; + default: + mimeType = 'image/png'; + break; + } + } + var downloadMime = 'image/octet-stream'; + var imageData = this.canvas.toDataURL(mimeType); + imageData = imageData.replace(mimeType, downloadMime); + + //Make the browser download the file + p5.prototype.downloadFile(imageData, filename, extension); +}; + +module.exports = p5.Image; +},{"../core/core":54,"./filters":73}],77:[function(_dereq_,module,exports){ +/** + * @module Image + * @submodule Pixels + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var Filters = _dereq_('./filters'); +_dereq_('../color/p5.Color'); + +/** + * Uint8ClampedArray + * containing the values for all the pixels in the display window. + * These values are numbers. This array is the size (include an appropriate + * factor for pixelDensity) of the display window x4, + * representing the R, G, B, A values in order for each pixel, moving from + * left to right across each row, then down each column. Retina and other + * high density displays will have more pixels[] (by a factor of + * pixelDensity^2). + * For example, if the image is 100x100 pixels, there will be 40,000. On a + * retina display, there will be 160,000. + *

+ * The first four values (indices 0-3) in the array will be the R, G, B, A + * values of the pixel at (0, 0). The second four values (indices 4-7) will + * contain the R, G, B, A values of the pixel at (1, 0). More generally, to + * set values for a pixel at (x, y): + * ```javascript + * var d = pixelDensity; + * for (var i = 0; i < d; i++) { + * for (var j = 0; j < d; j++) { + * // loop over + * idx = 4 * ((y * d + j) * width * d + (x * d + i)); + * pixels[idx] = r; + * pixels[idx+1] = g; + * pixels[idx+2] = b; + * pixels[idx+3] = a; + * } + * } + * ``` + *

While the above method is complex, it is flexible enough to work with + * any pixelDensity. Note that set() will automatically take care of + * setting all the appropriate values in pixels[] for a given (x, y) at + * any pixelDensity, but the performance may not be as fast when lots of + * modifications are made to the pixel array. + *

+ * Before accessing this array, the data must loaded with the loadPixels() + * function. After the array data has been modified, the updatePixels() + * function must be run to update the changes. + *

+ * Note that this is not a standard javascript array. This means that + * standard javascript functions such as slice() or + * arrayCopy() do not + * work.

+ * + * @property {Number[]} pixels + * @example + *
+ * + * var pink = color(255, 102, 204); + * loadPixels(); + * var d = pixelDensity(); + * var halfImage = 4 * (width * d) * (height/2 * d); + * for (var i = 0; i < halfImage; i+=4) { + * pixels[i] = red(pink); + * pixels[i+1] = green(pink); + * pixels[i+2] = blue(pink); + * pixels[i+3] = alpha(pink); + * } + * updatePixels(); + * + *
+ * + * @alt + * top half of canvas pink, bottom grey + * + */ +p5.prototype.pixels = []; + +/** + * Copies a region of pixels from one image to another, using a specified + * blend mode to do the operation.

+ * Available blend modes are: BLEND | DARKEST | LIGHTEST | DIFFERENCE | + * MULTIPLY| EXCLUSION | SCREEN | REPLACE | OVERLAY | HARD_LIGHT | + * SOFT_LIGHT | DODGE | BURN | ADD | NORMAL + * + * + * @method blend + * @param {p5.Image|undefined} srcImage source image + * @param {Integer} sx X coordinate of the source's upper left corner + * @param {Integer} sy Y coordinate of the source's upper left corner + * @param {Integer} sw source image width + * @param {Integer} sh source image height + * @param {Integer} dx X coordinate of the destination's upper left corner + * @param {Integer} dy Y coordinate of the destination's upper left corner + * @param {Integer} dw destination image width + * @param {Integer} dh destination image height + * @param {Integer} blendMode the blend mode + * + * @example + *
+ * var img0; + * var img1; + * + * function preload() { + * img0 = loadImage("assets/rockies.jpg"); + * img1 = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * background(img0); + * image(img1, 0, 0); + * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST); + * } + *
+ *
+ * var img0; + * var img1; + * + * function preload() { + * img0 = loadImage("assets/rockies.jpg"); + * img1 = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * background(img0); + * image(img1, 0, 0); + * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST); + * } + *
+ *
+ * var img0; + * var img1; + * + * function preload() { + * img0 = loadImage("assets/rockies.jpg"); + * img1 = loadImage("assets/bricks_third.jpg"); + * } + * + * function setup() { + * background(img0); + * image(img1, 0, 0); + * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, ADD); + * } + *
+ * + * @alt + * image of rocky mountains. Brick images on left and right. Right overexposed + * image of rockies. Brickwall images on left and right. Right mortar transparent + * image of rockies. Brickwall images on left and right. Right translucent + * + * + */ +p5.prototype.blend = function() { + if (this._renderer) { + this._renderer.blend.apply(this._renderer, arguments); + } else { + p5.Renderer2D.prototype.blend.apply(this, arguments); + } +}; + +/** + * Copies a region of the canvas to another region of the canvas + * and copies a region of pixels from an image used as the srcImg parameter + * into the canvas srcImage is specified this is used as the source. If + * the source and destination regions aren't the same size, it will + * automatically resize source pixels to fit the specified + * target region. + * + * @method copy + * @param {p5.Image|undefined} srcImage source image + * @param {Integer} sx X coordinate of the source's upper left corner + * @param {Integer} sy Y coordinate of the source's upper left corner + * @param {Integer} sw source image width + * @param {Integer} sh source image height + * @param {Integer} dx X coordinate of the destination's upper left corner + * @param {Integer} dy Y coordinate of the destination's upper left corner + * @param {Integer} dw destination image width + * @param {Integer} dh destination image height + * + * @example + *
+ * var img; + * + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * background(img); + * copy(img, 7, 22, 10, 10, 35, 25, 50, 50); + * stroke(255); + * noFill(); + * // Rectangle shows area being copied + * rect(7, 22, 10, 10); + * } + *
+ * + * @alt + * image of rocky mountains. Brick images on left and right. Right overexposed + * image of rockies. Brickwall images on left and right. Right mortar transparent + * image of rockies. Brickwall images on left and right. Right translucent + * + */ +p5.prototype.copy = function () { + p5.Renderer2D._copyHelper.apply(this, arguments); +}; + +/** + * Applies a filter to the canvas. + *

+ * + * The presets options are: + *

+ * + * THRESHOLD + * Converts the image to black and white pixels depending if they are above or + * below the threshold defined by the level parameter. The parameter must be + * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used. + *

+ * + * GRAY + * Converts any colors in the image to grayscale equivalents. No parameter + * is used. + *

+ * + * OPAQUE + * Sets the alpha channel to entirely opaque. No parameter is used. + *

+ * + * INVERT + * Sets each pixel to its inverse value. No parameter is used. + *

+ * + * POSTERIZE + * Limits each channel of the image to the number of colors specified as the + * parameter. The parameter can be set to values between 2 and 255, but + * results are most noticeable in the lower ranges. + *

+ * + * BLUR + * Executes a Guassian blur with the level parameter specifying the extent + * of the blurring. If no parameter is used, the blur is equivalent to + * Guassian blur of radius 1. Larger values increase the blur. + *

+ * + * ERODE + * Reduces the light areas. No parameter is used. + *

+ * + * DILATE + * Increases the light areas. No parameter is used. + * + * @method filter + * @param {Constant} filterType either THRESHOLD, GRAY, OPAQUE, INVERT, + * POSTERIZE, BLUR, ERODE or DILATE + * @param {Number} filterParam an optional parameter unique + * to each filter, see above + * + * + * @example + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(THRESHOLD); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(GRAY); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(OPAQUE); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(INVERT); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(POSTERIZE,3); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(DILATE); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(BLUR,3); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/bricks.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * filter(ERODE); + * } + * + *
+ * + * @alt + * black and white image of a brick wall. + * greyscale image of a brickwall + * image of a brickwall + * jade colored image of a brickwall + * red and pink image of a brickwall + * image of a brickwall + * blurry image of a brickwall + * image of a brickwall + * image of a brickwall with less detail + * + */ +p5.prototype.filter = function(operation, value) { + if(this.canvas !== undefined) { + Filters.apply(this.canvas, Filters[operation.toLowerCase()], value); + } + else { + Filters.apply(this.elt, Filters[operation.toLowerCase()], value); + } +}; + +/** + * Returns an array of [R,G,B,A] values for any pixel or grabs a section of + * an image. If no parameters are specified, the entire image is returned. + * Use the x and y parameters to get the value of one pixel. Get a section of + * the display window by specifying additional w and h parameters. When + * getting an image, the x and y parameters define the coordinates for the + * upper-left corner of the image, regardless of the current imageMode(). + *

+ * If the pixel requested is outside of the image window, [0,0,0,255] is + * returned. To get the numbers scaled according to the current color ranges + * and taking into account colorMode, use getColor instead of get. + *

+ * Getting the color of a single pixel with get(x, y) is easy, but not as fast + * as grabbing the data directly from pixels[]. The equivalent statement to + * get(x, y) using pixels[] with pixel density d is + * + * var off = (y * width + x) * d * 4; + * [pixels[off], + * pixels[off+1], + * pixels[off+2], + * pixels[off+3]] + *

+ * See the reference for pixels[] for more information. + * + * @method get + * @param {Number} [x] x-coordinate of the pixel + * @param {Number} [y] y-coordinate of the pixel + * @param {Number} [w] width + * @param {Number} [h] height + * @return {Number[]|p5.Image} values of pixel at x,y in array format + * [R, G, B, A] or p5.Image + * @example + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * var c = get(); + * image(c, width/2, 0); + * } + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * function setup() { + * image(img, 0, 0); + * var c = get(50, 90); + * fill(c); + * noStroke(); + * rect(25, 25, 50, 50); + * } + * + *
+ * + * @alt + * 2 images of the rocky mountains, side-by-side + * Image of the rocky mountains with 50x50 green rect in center of canvas + * + */ +p5.prototype.get = function(x, y, w, h){ + return this._renderer.get(x, y, w, h); +}; + +/** + * Loads the pixel data for the display window into the pixels[] array. This + * function must always be called before reading from or writing to pixels[]. + * Note that only changes made with set() or direct manipulation of pixels[] + * will occur. + * + * @method loadPixels + * @example + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * image(img, 0, 0); + * var d = pixelDensity(); + * var halfImage = 4 * (img.width * d) * + (img.height/2 * d); + * loadPixels(); + * for (var i = 0; i < halfImage; i++) { + * pixels[i+halfImage] = pixels[i]; + * } + * updatePixels(); + * } + * + *
+ * + * @alt + * two images of the rocky mountains. one on top, one on bottom of canvas. + * + */ +p5.prototype.loadPixels = function() { + this._renderer.loadPixels(); +}; + +/** + *

Changes the color of any pixel, or writes an image directly to the + * display window.

+ *

The x and y parameters specify the pixel to change and the c parameter + * specifies the color value. This can be a p5.Color object, or [R, G, B, A] + * pixel array. It can also be a single grayscale value. + * When setting an image, the x and y parameters define the coordinates for + * the upper-left corner of the image, regardless of the current imageMode(). + *

+ *

+ * After using set(), you must call updatePixels() for your changes to appear. + * This should be called once all pixels have been set, and must be called before + * calling .get() or drawing the image. + *

+ *

Setting the color of a single pixel with set(x, y) is easy, but not as + * fast as putting the data directly into pixels[]. Setting the pixels[] + * values directly may be complicated when working with a retina display, + * but will perform better when lots of pixels need to be set directly on + * every loop.

+ *

See the reference for pixels[] for more information.

+ * + * @method set + * @param {Number} x x-coordinate of the pixel + * @param {Number} y y-coordinate of the pixel + * @param {Number|Array|Object} c insert a grayscale value | a pixel array | + * a p5.Color object | a p5.Image to copy + * @example + *
+ * + * var black = color(0); + * set(30, 20, black); + * set(85, 20, black); + * set(85, 75, black); + * set(30, 75, black); + * updatePixels(); + * + *
+ * + *
+ * + * for (var i = 30; i < width-15; i++) { + * for (var j = 20; j < height-25; j++) { + * var c = color(204-j, 153-i, 0); + * set(i, j, c); + * } + * } + * updatePixels(); + * + *
+ * + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * set(0, 0, img); + * updatePixels(); + * line(0, 0, width, height); + * line(0, height, width, 0); + * } + * + *
+ * + * @alt + * 4 black points in the shape of a square middle-right of canvas. + * square with orangey-brown gradient lightening at bottom right. + * image of the rocky mountains. with lines like an 'x' through the center. + */ +p5.prototype.set = function (x, y, imgOrCol) { + this._renderer.set(x, y, imgOrCol); +}; +/** + * Updates the display window with the data in the pixels[] array. + * Use in conjunction with loadPixels(). If you're only reading pixels from + * the array, there's no need to call updatePixels() — updating is only + * necessary to apply changes. updatePixels() should be called anytime the + * pixels array is manipulated or set() is called, and only changes made with + * set() or direct changes to pixels[] will occur. + * + * @method updatePixels + * @param {Number} [x] x-coordinate of the upper-left corner of region + * to update + * @param {Number} [y] y-coordinate of the upper-left corner of region + * to update + * @param {Number} [w] width of region to update + * @param {Number} [h] height of region to update + * @example + *
+ * + * var img; + * function preload() { + * img = loadImage("assets/rockies.jpg"); + * } + * + * function setup() { + * image(img, 0, 0); + * var halfImage = 4 * (img.width * pixelDensity()) * + * (img.height * pixelDensity()/2); + * loadPixels(); + * for (var i = 0; i < halfImage; i++) { + * pixels[i+halfImage] = pixels[i]; + * } + * updatePixels(); + * } + * + *
+ * @alt + * two images of the rocky mountains. one on top, one on bottom of canvas. + */ +p5.prototype.updatePixels = function (x, y, w, h) { + // graceful fail - if loadPixels() or set() has not been called, pixel + // array will be empty, ignore call to updatePixels() + if (this.pixels.length === 0) { + return; + } + this._renderer.updatePixels(x, y, w, h); +}; + +module.exports = p5; + +},{"../color/p5.Color":48,"../core/core":54,"./filters":73}],78:[function(_dereq_,module,exports){ +/** + * @module IO + * @submodule Input + * @for p5 + * @requires core + */ + +/* globals Request: false */ +/* globals Headers: false */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +_dereq_('../core/error_helpers'); + +/** + * Reads the contents of a file and creates a String array of its individual + * lines. If the name of the file is used as the parameter, as in the above + * example, the file must be located in the sketch directory/folder. + *

+ * Alternatively, the file maybe be loaded from anywhere on the local + * computer using an absolute path (something that starts with / on Unix and + * Linux, or a drive letter on Windows), or the filename parameter can be a + * URL for a file found on a network. + *

+ * This method is asynchronous, meaning it may not finish before the next + * line in your sketch is executed. + * + * @method loadStrings + * @param {String} filename name of the file or url to load + * @param {function} [callback] function to be executed after loadStrings() + * completes, Array is passed in as first + * argument + * @param {function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + * @return {String[]} Array of Strings + * @example + * + *

Calling loadStrings() inside preload() guarantees to complete the + * operation before setup() and draw() are called.

+ * + *
+ * var result; + * function preload() { + * result = loadStrings('assets/test.txt'); + * } + + * function setup() { + * background(200); + * var ind = floor(random(result.length)); + * text(result[ind], 10, 10, 80, 80); + * } + *
+ * + *

Outside of preload(), you may supply a callback function to handle the + * object:

+ * + *
+ * function setup() { + * loadStrings('assets/test.txt', pickString); + * } + * + * function pickString(result) { + * background(200); + * var ind = floor(random(result.length)); + * text(result[ind], 10, 10, 80, 80); + * } + *
+ * + * @alt + * randomly generated text from a file, for example "i smell like butter" + * randomly generated text from a file, for example "i have three feet" + * + */ +p5.prototype.loadStrings = function () { + var ret = []; + var callback, errorCallback; + + for(var i=1; ihttpDo(path, 'GET'). + * + * @method httpGet + * @param {String} path name of the file or url to load + * @param {String} [datatype] "json", "jsonp", "xml", or "text" + * @param {Object} [data] param data passed sent with request + * @param {function} [callback] function to be executed after + * httpGet() completes, data is passed in + * as first argument + * @param {function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + * @example + *
+* // Examples use USGS Earthquake API: +* // https://earthquake.usgs.gov/fdsnws/event/1/#methods +* var earthquakes; +* function preload() { +* // Get the most recent earthquake in the database +* var url = 'https://earthquake.usgs.gov/fdsnws/event/1/query?' + +* 'format=geojson&limit=1&orderby=time'; +* httpGet(url, "jsonp", false, function(response) { +* // when the HTTP request completes, populate the variable that holds the +* // earthquake data used in the visualization. +* earthquakes = response; +* }); +* } +* +* function draw() { +* if (!earthquakes) { +* // Wait until the earthquake data has loaded before drawing. +* return +* } +* background(200); +* // Get the magnitude and name of the earthquake out of the loaded JSON +* var earthquakeMag = earthquakes.features[0].properties.mag; +* var earthquakeName = earthquakes.features[0].properties.place; +* ellipse(width/2, height/2, earthquakeMag * 10, earthquakeMag * 10); +* textAlign(CENTER); +* text(earthquakeName, 0, height - 30, width, 30); +* noLoop(); +* } + *
+ */ +p5.prototype.httpGet = function () { + var args = Array.prototype.slice.call(arguments); + args.splice(1, 0, 'GET'); + p5.prototype.httpDo.apply(this, args); +}; + +/** + * Method for executing an HTTP POST request. If data type is not specified, + * p5 will try to guess based on the URL, defaulting to text. This is equivalent to + * calling httpDo(path, 'POST'). + * + * @method httpPost + * @param {String} path name of the file or url to load + * @param {String} [datatype] "json", "jsonp", "xml", or "text" + * @param {Object} [data] param data passed sent with request + * @param {function} [callback] function to be executed after + * httpGet() completes, data is passed in + * as first argument + * @param {function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + */ +p5.prototype.httpPost = function () { + var args = Array.prototype.slice.call(arguments); + args.splice(1, 0, 'POST'); + p5.prototype.httpDo.apply(this, args); +}; + +/** + * Method for executing an HTTP request. If data type is not specified, + * p5 will try to guess based on the URL, defaulting to text.

+ * For more advanced use, you may also pass in the path as the first argument + * and a object as the second argument, the signature follows the one specified + * in the Fetch API specification. + * + * @method httpDo + * @param {String} path name of the file or url to load + * @param {String} [method] either "GET", "POST", or "PUT", + * defaults to "GET" + * @param {String} [datatype] "json", "jsonp", "xml", or "text" + * @param {Object} [data] param data passed sent with request + * @param {function} [callback] function to be executed after + * httpGet() completes, data is passed in + * as first argument + * @param {function} [errorCallback] function to be executed if + * there is an error, response is passed + * in as first argument + */ + +/** + * @method httpDo + * @param {String} path + * @param {Object} options Request object options as documented in the + * "fetch" API + * reference + * @param {function} [callback] + * @param {function} [errorCallback] + */ +p5.prototype.httpDo = function () { + var type = ''; + var callback; + var errorCallback; + var request; + var jsonpOptions = {}; + var cbCount = 0; + var contentType = 'text/plain'; + // Trim the callbacks off the end to get an idea of how many arguments are passed + for (var i = arguments.length-1; i > 0; i--){ + if(typeof arguments[i] === 'function'){ + cbCount++; + }else{ + break; + } + } + // The number of arguments minus callbacks + var argsCount = arguments.length - cbCount; + if(argsCount === 2 && + typeof arguments[0] === 'string' && + typeof arguments[1] === 'object'){ + // Intended for more advanced use, pass in Request parameters directly + request = new Request(arguments[0], arguments[1]); + callback = arguments[2]; + errorCallback = arguments[3]; + + // do some sort of smart type checking + if (type === '') { + if (request.url.indexOf('json') !== -1) { + type = 'json'; + } else if (request.url.indexOf('xml') !== -1) { + type = 'xml'; + } else { + type = 'text'; + } + } + } else { + // Provided with arguments + var path = arguments[0]; + var method = 'GET'; + var data; + + for (var j = 1; j < arguments.length; j++) { + var a = arguments[j]; + if (typeof a === 'string') { + if (a === 'GET' || a === 'POST' || a === 'PUT' || a === 'DELETE') { + method = a; + } else if(a === 'json' || a === 'jsonp' || a === 'xml' || a === 'text') { + type = a; + } else { + data = a; + } + } else if (typeof a === 'number') { + data = a.toString(); + } else if (typeof a === 'object') { + if(a.hasOwnProperty('jsonpCallback')){ + for (var attr in a) { + jsonpOptions[attr] = a[attr]; + } + }else{ + data = JSON.stringify(a); + contentType = 'application/json'; + } + } else if (typeof a === 'function') { + if (!callback) { + callback = a; + } else { + errorCallback = a; + } + } + } + // do some sort of smart type checking + if (type === '') { + if (path.indexOf('json') !== -1) { + type = 'json'; + } else if (path.indexOf('xml') !== -1) { + type = 'xml'; + } else { + type = 'text'; + } + } + + request = new Request(path, { + method: method, + mode: 'cors', + body: data, + headers: new Headers({ + 'Content-Type': contentType + }) + }); + } + + if(type === 'jsonp'){ + fetchJsonp(arguments[0], jsonpOptions) + .then(function(res){ + if(res.ok){ + return res.json(); + } + throw res; + }).then(function(resp){ + callback(resp); + }).catch(function(err){ + if (errorCallback) { + errorCallback(err); + } else { + throw err; + } + }); + }else{ + fetch(request) + .then(function(res){ + if(res.ok){ + if(type === 'json'){ + return res.json(); + }else{ + return res.text(); + } + } + + throw res; + }) + .then(function(resp){ + if (type === 'xml'){ + var parser = new DOMParser(); + resp = parser.parseFromString(resp, 'text/xml'); + resp = parseXML(resp.documentElement); + } + callback(resp); + }).catch(function(err, msg){ + if (errorCallback) { + errorCallback(err); + } else { + throw err; + } + }); + } +}; + +// ======= +// HELPERS +// ======= + +/** + * Forces download. Accepts a url to filedata/blob, a filename, + * and an extension (optional). + * This is a private function because it does not do any formatting, + * but it is used by saveStrings, saveJSON, saveTable etc. + * + * @param {String} href i.e. an href generated by createObjectURL + * @param {[String]} filename + * @param {[String]} extension + */ +p5.prototype.downloadFile = function (href, fName, extension) { + var fx = _checkFileExtension(fName, extension); + var filename = fx[0]; + var ext = fx[1]; + + var a = document.createElement('a'); + a.href = href; + a.download = filename; + + // Firefox requires the link to be added to the DOM before click() + a.onclick = function(e) { + destroyClickedElement(e); + e.stopPropagation(); + }; + + a.style.display = 'none'; + document.body.appendChild(a); + + // Safari will open this file in the same page as a confusing Blob. + if (p5.prototype._isSafari()) { + var aText = 'Hello, Safari user! To download this file...\n'; + aText += '1. Go to File --> Save As.\n'; + aText += '2. Choose "Page Source" as the Format.\n'; + aText += '3. Name it with this extension: .\"' + ext + '\"'; + alert(aText); + } + a.click(); + href = null; +}; + +/** + * Returns a file extension, or another string + * if the provided parameter has no extension. + * + * @param {String} filename + * @return {String[]} [fileName, fileExtension] + * + * @private + */ +function _checkFileExtension(filename, extension) { + if (!extension || extension === true || extension === 'true') { + extension = ''; + } + if (!filename) { + filename = 'untitled'; + } + var ext = ''; + // make sure the file will have a name, see if filename needs extension + if (filename && filename.indexOf('.') > -1) { + ext = filename.split('.').pop(); + } + // append extension if it doesn't exist + if (extension) { + if (ext !== extension) { + ext = extension; + filename = filename + '.' + ext; + } + } + return [filename, ext]; +} +p5.prototype._checkFileExtension = _checkFileExtension; + +/** + * Returns true if the browser is Safari, false if not. + * Safari makes trouble for downloading files. + * + * @return {Boolean} [description] + * @private + */ +p5.prototype._isSafari = function () { + var x = Object.prototype.toString.call(window.HTMLElement); + return x.indexOf('Constructor') > 0; +}; + +/** + * Helper function, a callback for download that deletes + * an invisible anchor element from the DOM once the file + * has been automatically downloaded. + * + * @private + */ +function destroyClickedElement(event) { + document.body.removeChild(event.target); +} + +module.exports = p5; + +},{"../core/core":54,"../core/error_helpers":57}],82:[function(_dereq_,module,exports){ +/** + * @module Math + * @submodule Calculation + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Calculates the absolute value (magnitude) of a number. Maps to Math.abs(). + * The absolute value of a number is always positive. + * + * @method abs + * @param {Number} n number to compute + * @return {Number} absolute value of given number + * @example + *
+ * function setup() { + * var x = -3; + * var y = abs(x); + * + * print(x); // -3 + * print(y); // 3 + * } + *
+ * + * @alt + * no image displayed + * + */ +p5.prototype.abs = Math.abs; + +/** + * Calculates the closest int value that is greater than or equal to the + * value of the parameter. Maps to Math.ceil(). For example, ceil(9.03) + * returns the value 10. + * + * @method ceil + * @param {Number} n number to round up + * @return {Number} rounded up number + * @example + *
+ * function draw() { + * background(200); + * // map, mouseX between 0 and 5. + * var ax = map(mouseX, 0, 100, 0, 5); + * var ay = 66; + * + * //Get the ceiling of the mapped number. + * var bx = ceil(map(mouseX, 0, 100, 0,5)); + * var by = 33; + * + * // Multiply the mapped numbers by 20 to more easily + * // see the changes. + * stroke(0); + * fill(0); + * line(0, ay, ax * 20, ay); + * line(0, by, bx * 20, by); + * + * // Reformat the float returned by map and draw it. + * noStroke(); + * text(nfc(ax, 2,2), ax, ay - 5); + * text(nfc(bx,1,1), bx, by - 5); + * } + *
+ * + * @alt + * 2 horizontal lines & number sets. increase with mouse x. bottom to 2 decimals + * + */ +p5.prototype.ceil = Math.ceil; + +/** + * Constrains a value between a minimum and maximum value. + * + * @method constrain + * @param {Number} n number to constrain + * @param {Number} low minimum limit + * @param {Number} high maximum limit + * @return {Number} constrained number + * @example + *
+ * function draw() { + * background(200); + * + * var leftWall = 25; + * var rightWall = 75; + * + * // xm is just the mouseX, while + * // xc is the mouseX, but constrained + * // between the leftWall and rightWall! + * var xm = mouseX; + * var xc = constrain(mouseX, leftWall, rightWall); + * + * // Draw the walls. + * stroke(150); + * line(leftWall, 0, leftWall, height); + * line(rightWall, 0, rightWall, height); + * + * // Draw xm and xc as circles. + * noStroke(); + * fill(150); + * ellipse(xm, 33, 9,9); // Not Constrained + * fill(0); + * ellipse(xc, 66, 9,9); // Constrained + * } + *
+ * + * @alt + * 2 vertical lines. 2 ellipses move with mouse X 1 does not move passed lines + * + */ +p5.prototype.constrain = function(n, low, high) { + return Math.max(Math.min(n, high), low); +}; + +/** + * Calculates the distance between two points. + * + * @method dist + * @param {Number} x1 x-coordinate of the first point + * @param {Number} y1 y-coordinate of the first point + * @param {Number} x2 x-coordinate of the second point + * @param {Number} y2 y-coordinate of the second point + * @return {Number} distance between the two points + */ +/** + * @method dist + * @param {Number} x1 + * @param {Number} y1 + * @param {Number} z1 z-coordinate of the first point + * @param {Number} x2 + * @param {Number} y2 + * @param {Number} z2 z-coordinate of the second point + * @return {Number} distance between the two points + * @example + *
+ * // Move your mouse inside the canvas to see the + * // change in distance between two points! + * function draw() { + * background(200); + * fill(0); + * + * var x1 = 10; + * var y1 = 90; + * var x2 = mouseX; + * var y2 = mouseY; + * + * line(x1, y1, x2, y2); + * ellipse(x1, y1, 7, 7); + * ellipse(x2, y2, 7, 7); + * + * // d is the length of the line + * // the distance from point 1 to point 2. + * var d = int(dist(x1, y1, x2, y2)); + * + * // Let's write d along the line we are drawing! + * push(); + * translate( (x1+x2)/2, (y1+y2)/2 ); + * rotate( atan2(y2-y1,x2-x1) ); + * text(nfc(d,1,1), 0, -5); + * pop(); + * // Fancy! + * } + *
+ * + * @alt + * 2 ellipses joined by line. 1 ellipse moves with mouse X&Y. Distance displayed. + * + */ +p5.prototype.dist = function(x1, y1, z1, x2, y2, z2) { + if (arguments.length === 4) { + // In the case of 2d: z1 means x2 and x2 means y2 + return hypot(z1-x1, x2-y1); + } else if (arguments.length === 6) { + return hypot(x2-x1, y2-y1, z2-z1); + } +}; + +/** + * Returns Euler's number e (2.71828...) raised to the power of the n + * parameter. Maps to Math.exp(). + * + * @method exp + * @param {Number} n exponent to raise + * @return {Number} e^n + * @example + *
+ * function draw() { + * background(200); + * + * // Compute the exp() function with a value between 0 and 2 + * var xValue = map(mouseX, 0, width, 0, 2); + * var yValue = exp(xValue); + * + * var y = map(yValue, 0, 8, height, 0); + * + * var legend = "exp (" + nfc(xValue, 3) +")\n= " + nf(yValue, 1, 4); + * stroke(150); + * line(mouseX, y, mouseX, height); + * fill(0); + * text(legend, 5, 15); + * noStroke(); + * ellipse (mouseX,y, 7, 7); + * + * // Draw the exp(x) curve, + * // over the domain of x from 0 to 2 + * noFill(); + * stroke(0); + * beginShape(); + * for (var x = 0; x < width; x++) { + * xValue = map(x, 0, width, 0, 2); + * yValue = exp(xValue); + * y = map(yValue, 0, 8, height, 0); + * vertex(x, y); + * } + * + * endShape(); + * line(0, 0, 0, height); + * line(0, height-1, width, height-1); + * } + *
+ * + * @alt + * ellipse moves along a curve with mouse x. e^n displayed. + * + */ +p5.prototype.exp = Math.exp; + +/** + * Calculates the closest int value that is less than or equal to the + * value of the parameter. Maps to Math.floor(). + * + * @method floor + * @param {Number} n number to round down + * @return {Number} rounded down number + * @example + *
+ * function draw() { + * background(200); + * //map, mouseX between 0 and 5. + * var ax = map(mouseX, 0, 100, 0, 5); + * var ay = 66; + * + * //Get the floor of the mapped number. + * var bx = floor(map(mouseX, 0, 100, 0,5)); + * var by = 33; + * + * // Multiply the mapped numbers by 20 to more easily + * // see the changes. + * stroke(0); + * fill(0); + * line(0, ay, ax * 20, ay); + * line(0, by, bx * 20, by); + * + * // Reformat the float returned by map and draw it. + * noStroke(); + * text(nfc(ax, 2,2), ax, ay - 5); + * text(nfc(bx,1,1), bx, by - 5); + * } + *
+ * + * @alt + * 2 horizontal lines & number sets. increase with mouse x. bottom to 2 decimals + * + */ +p5.prototype.floor = Math.floor; + +/** + * Calculates a number between two numbers at a specific increment. The amt + * parameter is the amount to interpolate between the two values where 0.0 + * equal to the first point, 0.1 is very near the first point, 0.5 is + * half-way in between, etc. The lerp function is convenient for creating + * motion along a straight path and for drawing dotted lines. + * + * @method lerp + * @param {Number} start first value + * @param {Number} stop second value + * @param {Number} amt number between 0.0 and 1.0 + * @return {Number} lerped value + * @example + *
+ * function setup() { + * background(200); + * var a = 20; + * var b = 80; + * var c = lerp(a,b, .2); + * var d = lerp(a,b, .5); + * var e = lerp(a,b, .8); + * + * var y = 50 + * + * strokeWeight(5); + * stroke(0); // Draw the original points in black + * point(a, y); + * point(b, y); + * + * stroke(100); // Draw the lerp points in gray + * point(c, y); + * point(d, y); + * point(e, y); + * } + *
+ * + * @alt + * 5 points horizontally staggered mid-canvas. mid 3 are grey, outer black + * + */ +p5.prototype.lerp = function(start, stop, amt) { + return amt*(stop-start)+start; +}; + +/** + * Calculates the natural logarithm (the base-e logarithm) of a number. This + * function expects the n parameter to be a value greater than 0.0. Maps to + * Math.log(). + * + * @method log + * @param {Number} n number greater than 0 + * @return {Number} natural logarithm of n + * @example + *
+ * function draw() { + * background(200); + * var maxX = 2.8; + * var maxY = 1.5; + * + * // Compute the natural log of a value between 0 and maxX + * var xValue = map(mouseX, 0, width, 0, maxX); + * if (xValue > 0) { // Cannot take the log of a negative number. + * var yValue = log(xValue); + * var y = map(yValue, -maxY, maxY, height, 0); + * + * // Display the calculation occurring. + * var legend = "log(" + nf(xValue, 1, 2) + ")\n= " + nf(yValue, 1, 3); + * stroke(150); + * line(mouseX, y, mouseX, height); + * fill(0); + * text (legend, 5, 15); + * noStroke(); + * ellipse (mouseX, y, 7, 7); + * } + * + * // Draw the log(x) curve, + * // over the domain of x from 0 to maxX + * noFill(); + * stroke(0); + * beginShape(); + * for(var x=0; x < width; x++) { + * xValue = map(x, 0, width, 0, maxX); + * yValue = log(xValue); + * y = map(yValue, -maxY, maxY, height, 0); + * vertex(x, y); + * } + * endShape(); + * line(0,0,0,height); + * line(0,height/2,width, height/2); + * } + *
+ * + * @alt + * ellipse moves along a curve with mouse x. natural logarithm of n displayed. + * + */ +p5.prototype.log = Math.log; + +/** + * Calculates the magnitude (or length) of a vector. A vector is a direction + * in space commonly used in computer graphics and linear algebra. Because it + * has no "start" position, the magnitude of a vector can be thought of as + * the distance from the coordinate 0,0 to its x,y value. Therefore, mag() is + * a shortcut for writing dist(0, 0, x, y). + * + * @method mag + * @param {Number} a first value + * @param {Number} b second value + * @return {Number} magnitude of vector from (0,0) to (a,b) + * @example + *
+ * function setup() { + * var x1 = 20; + * var x2 = 80; + * var y1 = 30; + * var y2 = 70; + * + * line(0, 0, x1, y1); + * print(mag(x1, y1)); // Prints "36.05551275463989" + * line(0, 0, x2, y1); + * print(mag(x2, y1)); // Prints "85.44003745317531" + * line(0, 0, x1, y2); + * print(mag(x1, y2)); // Prints "72.80109889280519" + * line(0, 0, x2, y2); + * print(mag(x2, y2)); // Prints "106.3014581273465" + * } + *
+ * + * @alt + * 4 lines of different length radiate from top left of canvas. + * + */ +p5.prototype.mag = function(x, y) { + return hypot(x, y); +}; + +/** + * Re-maps a number from one range to another. + *

+ * In the first example above, the number 25 is converted from a value in the + * range of 0 to 100 into a value that ranges from the left edge of the + * window (0) to the right edge (width). + * + * @method map + * @param {Number} value the incoming value to be converted + * @param {Number} start1 lower bound of the value's current range + * @param {Number} stop1 upper bound of the value's current range + * @param {Number} start2 lower bound of the value's target range + * @param {Number} stop2 upper bound of the value's target range + * @return {Number} remapped number + * @example + *
+ * var value = 25; + * var m = map(value, 0, 100, 0, width); + * ellipse(m, 50, 10, 10); + *
+ * + *
+ * function setup() { + * noStroke(); + * } + * + * function draw() { + * background(204); + * var x1 = map(mouseX, 0, width, 25, 75); + * ellipse(x1, 25, 25, 25); + * var x2 = map(mouseX, 0, width, 0, 100); + * ellipse(x2, 75, 25, 25); + * } + *
+ * + * @alt + * 10 by 10 white ellipse with in mid left canvas + * 2 25 by 25 white ellipses move with mouse x. Bottom has more range from X + * + */ +p5.prototype.map = function(n, start1, stop1, start2, stop2) { + return ((n-start1)/(stop1-start1))*(stop2-start2)+start2; +}; + +/** + * Determines the largest value in a sequence of numbers, and then returns + * that value. max() accepts any number of Number parameters, or an Array + * of any length. + * + * @method max + * @param {Number|Array} n0 Numbers to compare + * @return {Number} maximum Number + * @example + *
+ * function setup() { + * // Change the elements in the array and run the sketch + * // to show how max() works! + * numArray = new Array(2,1,5,4,8,9); + * fill(0); + * noStroke(); + * text("Array Elements", 0, 10); + * // Draw all numbers in the array + * var spacing = 15; + * var elemsY = 25; + * for(var i = 0; i < numArray.length; i++) { + * text(numArray[i], i * spacing, elemsY); + * } + * maxX = 33; + * maxY = 80; + * // Draw the Maximum value in the array. + * textSize(32); + * text(max(numArray), maxX, maxY); + * } + *
+ * + * @alt + * Small text at top reads: Array Elements 2 1 5 4 8 9. Large text at center: 9 + * + */ +p5.prototype.max = function() { + if (arguments[0] instanceof Array) { + return Math.max.apply(null,arguments[0]); + } else { + return Math.max.apply(null,arguments); + } +}; + +/** + * Determines the smallest value in a sequence of numbers, and then returns + * that value. min() accepts any number of Number parameters, or an Array + * of any length. + * + * @method min + * @param {Number|Array} n0 Numbers to compare + * @return {Number} minimum Number + * @example + *
+ * function setup() { + * // Change the elements in the array and run the sketch + * // to show how min() works! + * numArray = new Array(2,1,5,4,8,9); + * fill(0); + * noStroke(); + * text("Array Elements", 0, 10); + * // Draw all numbers in the array + * var spacing = 15; + * var elemsY = 25; + * for(var i = 0; i < numArray.length; i++) { + * text(numArray[i], i * spacing, elemsY); + * } + * maxX = 33; + * maxY = 80; + * // Draw the Minimum value in the array. + * textSize(32); + * text(min(numArray), maxX, maxY); + * } + *
+ * + * @alt + * Small text at top reads: Array Elements 2 1 5 4 8 9. Large text at center: 1 + * + */ +p5.prototype.min = function() { + if (arguments[0] instanceof Array) { + return Math.min.apply(null,arguments[0]); + } else { + return Math.min.apply(null,arguments); + } +}; + +/** + * Normalizes a number from another range into a value between 0 and 1. + * Identical to map(value, low, high, 0, 1). + * Numbers outside of the range are not clamped to 0 and 1, because + * out-of-range values are often intentional and useful. (See the second + * example above.) + * + * @method norm + * @param {Number} value incoming value to be normalized + * @param {Number} start lower bound of the value's current range + * @param {Number} stop upper bound of the value's current range + * @return {Number} normalized number + * @example + *
+ * function draw() { + * background(200); + * currentNum = mouseX; + * lowerBound = 0; + * upperBound = width; //100; + * normalized = norm(currentNum, lowerBound, upperBound); + * lineY = 70 + * line(0, lineY, width, lineY); + * //Draw an ellipse mapped to the non-normalized value. + * noStroke(); + * fill(50) + * var s = 7; // ellipse size + * ellipse(currentNum, lineY, s, s); + * + * // Draw the guide + * guideY = lineY + 15; + * text("0", 0, guideY); + * textAlign(RIGHT); + * text("100", width, guideY); + * + * // Draw the normalized value + * textAlign(LEFT); + * fill(0); + * textSize(32); + * normalY = 40; + * normalX = 20; + * text(normalized, normalX, normalY); + * } + *
+ * + * @alt + * ellipse moves with mouse. 0 shown left & 100 right and updating values center + * + */ +p5.prototype.norm = function(n, start, stop) { + return this.map(n, start, stop, 0, 1); +}; + +/** + * Facilitates exponential expressions. The pow() function is an efficient + * way of multiplying numbers by themselves (or their reciprocals) in large + * quantities. For example, pow(3, 5) is equivalent to the expression + * 3*3*3*3*3 and pow(3, -5) is equivalent to 1 / 3*3*3*3*3. Maps to + * Math.pow(). + * + * @method pow + * @param {Number} n base of the exponential expression + * @param {Number} e power by which to raise the base + * @return {Number} n^e + * @example + *
+ * function setup() { + * //Exponentially increase the size of an ellipse. + * eSize = 3; // Original Size + * eLoc = 10; // Original Location + * + * ellipse(eLoc, eLoc, eSize, eSize); + * + * ellipse(eLoc*2, eLoc*2, pow(eSize, 2), pow(eSize, 2)); + * + * ellipse(eLoc*4, eLoc*4, pow(eSize, 3), pow(eSize, 3)); + * + * ellipse(eLoc*8, eLoc*8, pow(eSize, 4), pow(eSize, 4)); + * } + *
+ * + * @alt + * small to large ellipses radiating from top left of canvas + * + */ +p5.prototype.pow = Math.pow; + +/** + * Calculates the integer closest to the n parameter. For example, + * round(133.8) returns the value 134. Maps to Math.round(). + * + * @method round + * @param {Number} n number to round + * @return {Number} rounded number + * @example + *
+ * function draw() { + * background(200); + * //map, mouseX between 0 and 5. + * var ax = map(mouseX, 0, 100, 0, 5); + * var ay = 66; + * + * // Round the mapped number. + * var bx = round(map(mouseX, 0, 100, 0,5)); + * var by = 33; + * + * // Multiply the mapped numbers by 20 to more easily + * // see the changes. + * stroke(0); + * fill(0); + * line(0, ay, ax * 20, ay); + * line(0, by, bx * 20, by); + * + * // Reformat the float returned by map and draw it. + * noStroke(); + * text(nfc(ax, 2,2), ax, ay - 5); + * text(nfc(bx,1,1), bx, by - 5); + * } + *
+ * + * @alt + * horizontal center line squared values displayed on top and regular on bottom. + * + */ +p5.prototype.round = Math.round; + +/** + * Squares a number (multiplies a number by itself). The result is always a + * positive number, as multiplying two negative numbers always yields a + * positive result. For example, -1 * -1 = 1. + * + * @method sq + * @param {Number} n number to square + * @return {Number} squared number + * @example + *
+ * function draw() { + * background(200); + * eSize = 7; + * x1 = map(mouseX, 0, width, 0, 10); + * y1 = 80; + * x2 = sq(x1); + * y2 = 20; + * + * // Draw the non-squared. + * line(0, y1, width, y1); + * ellipse(x1, y1, eSize, eSize); + * + * // Draw the squared. + * line(0, y2, width, y2); + * ellipse(x2, y2, eSize, eSize); + * + * // Draw dividing line. + * stroke(100) + * line(0, height/2, width, height/2); + * + * // Draw text. + * var spacing = 15; + * noStroke(); + * fill(0); + * text("x = " + x1, 0, y1 + spacing); + * text("sq(x) = " + x2, 0, y2 + spacing); + * } + *
+ * + * @alt + * horizontal center line squared values displayed on top and regular on bottom. + * + */ +p5.prototype.sq = function(n) { return n*n; }; + +/** + * Calculates the square root of a number. The square root of a number is + * always positive, even though there may be a valid negative root. The + * square root s of number a is such that s*s = a. It is the opposite of + * squaring. Maps to Math.sqrt(). + * + * @method sqrt + * @param {Number} n non-negative number to square root + * @return {Number} square root of number + * @example + *
+ * function draw() { + * background(200); + * eSize = 7; + * x1 = mouseX; + * y1 = 80; + * x2 = sqrt(x1); + * y2 = 20; + * + * // Draw the non-squared. + * line(0, y1, width, y1); + * ellipse(x1, y1, eSize, eSize); + * + * // Draw the squared. + * line(0, y2, width, y2); + * ellipse(x2, y2, eSize, eSize); + * + * // Draw dividing line. + * stroke(100) + * line(0, height/2, width, height/2); + * + * // Draw text. + * noStroke(); + * fill(0); + * var spacing = 15; + * text("x = " + x1, 0, y1 + spacing); + * text("sqrt(x) = " + x2, 0, y2 + spacing); + * } + *
+ * + * @alt + * horizontal center line squareroot values displayed on top and regular on bottom. + * + */ +p5.prototype.sqrt = Math.sqrt; + +// Calculate the length of the hypotenuse of a right triangle +// This won't under- or overflow in intermediate steps +// https://en.wikipedia.org/wiki/Hypot +function hypot(x, y, z) { + // Use the native implementation if it's available + if (typeof Math.hypot === 'function') { + return Math.hypot.apply(null, arguments); + } + + // Otherwise use the V8 implementation + // https://github.com/v8/v8/blob/8cd3cf297287e581a49e487067f5cbd991b27123/src/js/math.js#L217 + var length = arguments.length; + var args = []; + var max = 0; + for (var i = 0; i < length; i++) { + var n = arguments[i]; + n = +n; + if (n === Infinity || n === -Infinity) { + return Infinity; + } + n = Math.abs(n); + if (n > max) { + max = n; + } + args[i] = n; + } + + if (max === 0) { + max = 1; + } + var sum = 0; + var compensation = 0; + for (var j = 0; j < length; j++) { + var m = args[j] / max; + var summand = m * m - compensation; + var preliminary = sum + summand; + compensation = (preliminary - sum) - summand; + sum = preliminary; + } + return Math.sqrt(sum) * max; +} + +module.exports = p5; + +},{"../core/core":54}],83:[function(_dereq_,module,exports){ +/** + * @module Math + * @submodule Math + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + + +/** + * Creates a new p5.Vector (the datatype for storing vectors). This provides a + * two or three dimensional vector, specifically a Euclidean (also known as + * geometric) vector. A vector is an entity that has both magnitude and + * direction. + * + * @method createVector + * @param {Number} [x] x component of the vector + * @param {Number} [y] y component of the vector + * @param {Number} [z] z component of the vector + * @return {p5.Vector} + */ +p5.prototype.createVector = function (x, y, z) { + if (this instanceof p5) { + return new p5.Vector(this, arguments); + } else { + return new p5.Vector(x, y, z); + } +}; + +module.exports = p5; + +},{"../core/core":54}],85:[function(_dereq_,module,exports){ +/** + * @module Math + * @submodule Math + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var polarGeometry = _dereq_('./polargeometry'); +var constants = _dereq_('../core/constants'); + +/** + * A class to describe a two or three dimensional vector, specifically + * a Euclidean (also known as geometric) vector. A vector is an entity + * that has both magnitude and direction. The datatype, however, stores + * the components of the vector (x, y for 2D, and x, y, z for 3D). The magnitude + * and direction can be accessed via the methods mag() and heading(). + *

+ * In many of the p5.js examples, you will see p5.Vector used to describe a + * position, velocity, or acceleration. For example, if you consider a rectangle + * moving across the screen, at any given instant it has a position (a vector + * that points from the origin to its location), a velocity (the rate at which + * the object's position changes per time unit, expressed as a vector), and + * acceleration (the rate at which the object's velocity changes per time + * unit, expressed as a vector). + *

+ * Since vectors represent groupings of values, we cannot simply use + * traditional addition/multiplication/etc. Instead, we'll need to do some + * "vector" math, which is made easy by the methods inside the p5.Vector class. + * + * @class p5.Vector + * @constructor + * @param {Number} [x] x component of the vector + * @param {Number} [y] y component of the vector + * @param {Number} [z] z component of the vector + * @example + *
+ * + * var v1 = createVector(40, 50); + * var v2 = createVector(40, 50); + * + * ellipse(v1.x, v1.y, 50, 50); + * ellipse(v2.x, v2.y, 50, 50); + * v1.add(v2); + * ellipse(v1.x, v1.y, 50, 50); + * + *
+ * + * @alt + * 2 white ellipses. One center-left the other bottom right and off canvas + * + */ +p5.Vector = function() { + var x,y,z; + // This is how it comes in with createVector() + if(arguments[0] instanceof p5) { + // save reference to p5 if passed in + this.p5 = arguments[0]; + x = arguments[1][0] || 0; + y = arguments[1][1] || 0; + z = arguments[1][2] || 0; + // This is what we'll get with new p5.Vector() + } else { + x = arguments[0] || 0; + y = arguments[1] || 0; + z = arguments[2] || 0; + } + /** + * The x component of the vector + * @property x {Number} + */ + this.x = x; + /** + * The y component of the vector + * @property y {Number} + */ + this.y = y; + /** + * The z component of the vector + * @property z {Number} + */ + this.z = z; +}; + +/** + * Returns a string representation of a vector v by calling String(v) + * or v.toString(). This method is useful for logging vectors in the + * console. + * @method toString + * @example + *
+ * function setup() { + * var v = createVector(20,30); + * print(String(v)); // prints "p5.Vector Object : [20, 30, 0]" + * } + *
+ * + */ +p5.Vector.prototype.toString = function p5VectorToString() { + return 'p5.Vector Object : ['+ this.x +', '+ this.y +', '+ this.z + ']'; +}; + +/** + * Sets the x, y, and z component of the vector using two or three separate + * variables, the data from a p5.Vector, or the values from a float array. + * @method set + * @param {Number|p5.Vector|Array} [x] the x component of the vector or a + * p5.Vector or an Array + * @param {Number} [y] the y component of the vector + * @param {Number} [z] the z component of the vector + * @chainable + * @example + *
+ * + * function setup() { + * var v = createVector(1, 2, 3); + * v.set(4,5,6); // Sets vector to [4, 5, 6] + * + * var v1 = createVector(0, 0, 0); + * var arr = [1, 2, 3]; + * v1.set(arr); // Sets vector to [1, 2, 3] + * } + * + *
+ */ +p5.Vector.prototype.set = function (x, y, z) { + if (x instanceof p5.Vector) { + this.x = x.x || 0; + this.y = x.y || 0; + this.z = x.z || 0; + return this; + } + if (x instanceof Array) { + this.x = x[0] || 0; + this.y = x[1] || 0; + this.z = x[2] || 0; + return this; + } + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + return this; +}; + +/** + * Gets a copy of the vector, returns a p5.Vector object. + * + * @method copy + * @return {p5.Vector} the copy of the p5.Vector object + * @example + *
+ * + * var v1 = createVector(1, 2, 3); + * var v2 = v1.copy(); + * print(v1.x == v2.x && v1.y == v2.y && v1.z == v2.z); + * // Prints "true" + * + *
+ */ +p5.Vector.prototype.copy = function () { + if (this.p5) { + return new p5.Vector(this.p5,[this.x, this.y, this.z]); + } else { + return new p5.Vector(this.x,this.y,this.z); + } +}; + +/** + * Adds x, y, and z components to a vector, adds one vector to another, or + * adds two independent vectors together. The version of the method that adds + * two vectors together is a static method and returns a p5.Vector, the others + * acts directly on the vector. See the examples for more context. + * + * @method add + * @param {Number|p5.Vector|Array} x the x component of the vector to be + * added or a p5.Vector or an Array + * @param {Number} [y] the y component of the vector to be + * added + * @param {Number} [z] the z component of the vector to be + * added + * @chainable + * @example + *
+ * + * var v = createVector(1, 2, 3); + * v.add(4,5,6); + * // v's components are set to [5, 7, 9] + * + *
+ *
+ * + * // Static method + * var v1 = createVector(1, 2, 3); + * var v2 = createVector(2, 3, 4); + * + * var v3 = p5.Vector.add(v1, v2); + * // v3 has components [3, 5, 7] + * + *
+ */ +p5.Vector.prototype.add = function (x, y, z) { + if (x instanceof p5.Vector) { + this.x += x.x || 0; + this.y += x.y || 0; + this.z += x.z || 0; + return this; + } + if (x instanceof Array) { + this.x += x[0] || 0; + this.y += x[1] || 0; + this.z += x[2] || 0; + return this; + } + this.x += x || 0; + this.y += y || 0; + this.z += z || 0; + return this; +}; + +/** + * Subtracts x, y, and z components from a vector, subtracts one vector from + * another, or subtracts two independent vectors. The version of the method + * that subtracts two vectors is a static method and returns a p5.Vector, the + * other acts directly on the vector. See the examples for more context. + * + * @method sub + * @param {Number|p5.Vector|Array} x the x component of the vector or a + * p5.Vector or an Array + * @param {Number} [y] the y component of the vector + * @param {Number} [z] the z component of the vector + * @chainable + * @example + *
+ * + * var v = createVector(4, 5, 6); + * v.sub(1, 1, 1); + * // v's components are set to [3, 4, 5] + * + *
+ * + *
+ * + * // Static method + * var v1 = createVector(2, 3, 4); + * var v2 = createVector(1, 2, 3); + * + * var v3 = p5.Vector.sub(v1, v2); + * // v3 has components [1, 1, 1] + * + *
+ */ +p5.Vector.prototype.sub = function (x, y, z) { + if (x instanceof p5.Vector) { + this.x -= x.x || 0; + this.y -= x.y || 0; + this.z -= x.z || 0; + return this; + } + if (x instanceof Array) { + this.x -= x[0] || 0; + this.y -= x[1] || 0; + this.z -= x[2] || 0; + return this; + } + this.x -= x || 0; + this.y -= y || 0; + this.z -= z || 0; + return this; +}; + +/** + * Multiply the vector by a scalar. The static version of this method + * creates a new p5.Vector while the non static version acts on the vector + * directly. See the examples for more context. + * + * @method mult + * @param {Number} n the number to multiply with the vector + * @chainable + * @example + *
+ * + * var v = createVector(1, 2, 3); + * v.mult(2); + * // v's components are set to [2, 4, 6] + * + *
+ * + *
+ * + * // Static method + * var v1 = createVector(1, 2, 3); + * var v2 = p5.Vector.mult(v1, 2); + * // v2 has components [2, 4, 6] + * + *
+ */ +p5.Vector.prototype.mult = function (n) { + this.x *= n || 0; + this.y *= n || 0; + this.z *= n || 0; + return this; +}; + +/** + * Divide the vector by a scalar. The static version of this method creates a + * new p5.Vector while the non static version acts on the vector directly. + * See the examples for more context. + * + * @method div + * @param {number} n the number to divide the vector by + * @chainable + * @example + *
+ * + * var v = createVector(6, 4, 2); + * v.div(2); //v's components are set to [3, 2, 1] + * + *
+ * + *
+ * + * // Static method + * var v1 = createVector(6, 4, 2); + * var v2 = p5.Vector.div(v, 2); + * // v2 has components [3, 2, 1] + * + *
+ */ +p5.Vector.prototype.div = function (n) { + this.x /= n; + this.y /= n; + this.z /= n; + return this; +}; + +/** + * Calculates the magnitude (length) of the vector and returns the result as + * a float (this is simply the equation sqrt(x*x + y*y + z*z).) + * + * @method mag + * @return {Number} magnitude of the vector + * @example + *
+ * + * var v = createVector(20.0, 30.0, 40.0); + * var m = v.mag(); + * print(m); // Prints "53.85164807134504" + * + *
+ */ +p5.Vector.prototype.mag = function () { + return Math.sqrt(this.magSq()); +}; + +/** + * Calculates the squared magnitude of the vector and returns the result + * as a float (this is simply the equation (x*x + y*y + z*z).) + * Faster if the real length is not required in the + * case of comparing vectors, etc. + * + * @method magSq + * @return {number} squared magnitude of the vector + * @example + *
+ * + * // Static method + * var v1 = createVector(6, 4, 2); + * print(v1.magSq()); // Prints "56" + * + *
+ */ +p5.Vector.prototype.magSq = function () { + var x = this.x, y = this.y, z = this.z; + return (x * x + y * y + z * z); +}; + +/** + * Calculates the dot product of two vectors. The version of the method + * that computes the dot product of two independent vectors is a static + * method. See the examples for more context. + * + * + * @method dot + * @param {Number|p5.Vector} x x component of the vector or a p5.Vector + * @param {Number} [y] y component of the vector + * @param {Number} [z] z component of the vector + * @return {Number} the dot product + * + * @example + *
+ * + * var v1 = createVector(1, 2, 3); + * var v2 = createVector(2, 3, 4); + * + * print(v1.dot(v2)); // Prints "20" + * + *
+ * + *
+ * + * //Static method + * var v1 = createVector(1, 2, 3); + * var v2 = createVector(3, 2, 1); + * print (p5.Vector.dot(v1, v2)); // Prints "10" + * + *
+ */ +p5.Vector.prototype.dot = function (x, y, z) { + if (x instanceof p5.Vector) { + return this.dot(x.x, x.y, x.z); + } + return this.x * (x || 0) + + this.y * (y || 0) + + this.z * (z || 0); +}; + +/** + * Calculates and returns a vector composed of the cross product between + * two vectors. Both the static and non static methods return a new p5.Vector. + * See the examples for more context. + * + * @method cross + * @param {p5.Vector} v p5.Vector to be crossed + * @return {p5.Vector} p5.Vector composed of cross product + * @example + *
+ * + * var v1 = createVector(1, 2, 3); + * var v2 = createVector(1, 2, 3); + * + * v1.cross(v2); // v's components are [0, 0, 0] + * + *
+ * + *
+ * + * // Static method + * var v1 = createVector(1, 0, 0); + * var v2 = createVector(0, 1, 0); + * + * var crossProduct = p5.Vector.cross(v1, v2); + * // crossProduct has components [0, 0, 1] + * + *
+ */ +p5.Vector.prototype.cross = function (v) { + var x = this.y * v.z - this.z * v.y; + var y = this.z * v.x - this.x * v.z; + var z = this.x * v.y - this.y * v.x; + if (this.p5) { + return new p5.Vector(this.p5,[x,y,z]); + } else { + return new p5.Vector(x,y,z); + } +}; + +/** + * Calculates the Euclidean distance between two points (considering a + * point as a vector object). + * + * @method dist + * @param {p5.Vector} v the x, y, and z coordinates of a p5.Vector + * @return {Number} the distance + * @example + *
+ * + * var v1 = createVector(1, 0, 0); + * var v2 = createVector(0, 1, 0); + * + * var distance = v1.dist(v2); // distance is 1.4142... + * + *
+ *
+ * + * // Static method + * var v1 = createVector(1, 0, 0); + * var v2 = createVector(0, 1, 0); + * + * var distance = p5.Vector.dist(v1,v2); + * // distance is 1.4142... + * + *
+ */ +p5.Vector.prototype.dist = function (v) { + var d = v.copy().sub(this); + return d.mag(); +}; + +/** + * Normalize the vector to length 1 (make it a unit vector). + * + * @method normalize + * @return {p5.Vector} normalized p5.Vector + * @example + *
+ * + * var v = createVector(10, 20, 2); + * // v has components [10.0, 20.0, 2.0] + * v.normalize(); + * // v's components are set to + * // [0.4454354, 0.8908708, 0.089087084] + * + *
+ * + */ +p5.Vector.prototype.normalize = function () { + return this.mag() === 0 ? this : this.div(this.mag()); +}; + +/** + * Limit the magnitude of this vector to the value used for the max + * parameter. + * + * @method limit + * @param {Number} max the maximum magnitude for the vector + * @chainable + * @example + *
+ * + * var v = createVector(10, 20, 2); + * // v has components [10.0, 20.0, 2.0] + * v.limit(5); + * // v's components are set to + * // [2.2271771, 4.4543543, 0.4454354] + * + *
+ */ +p5.Vector.prototype.limit = function (max) { + var mSq = this.magSq(); + if(mSq > max*max) { + this.div(Math.sqrt(mSq)); //normalize it + this.mult(max); + } + return this; +}; + +/** + * Set the magnitude of this vector to the value used for the len + * parameter. + * + * @method setMag + * @param {number} len the new length for this vector + * @chainable + * @example + *
+ * + * var v = createVector(10, 20, 2); + * // v has components [10.0, 20.0, 2.0] + * v.setMag(10); + * // v's components are set to [6.0, 8.0, 0.0] + * + *
+ */ +p5.Vector.prototype.setMag = function (n) { + return this.normalize().mult(n); +}; + +/** + * Calculate the angle of rotation for this vector (only 2D vectors) + * + * @method heading + * @return {Number} the angle of rotation + * @example + *
+ * function setup() { + * var v1 = createVector(30,50); + * print(v1.heading()); // 1.0303768265243125 + * + * var v1 = createVector(40,50); + * print(v1.heading()); // 0.8960553845713439 + * + * var v1 = createVector(30,70); + * print(v1.heading()); // 1.1659045405098132 + * } + *
+ */ +p5.Vector.prototype.heading = function () { + var h = Math.atan2(this.y, this.x); + if (this.p5) { + if (this.p5._angleMode === constants.RADIANS) { + return h; + } else { + return polarGeometry.radiansToDegrees(h); + } + } else { + return h; + } +}; + +/** + * Rotate the vector by an angle (only 2D vectors), magnitude remains the + * same + * + * @method rotate + * @param {number} angle the angle of rotation + * @chainable + * @example + *
+ * + * var v = createVector(10.0, 20.0); + * // v has components [10.0, 20.0, 0.0] + * v.rotate(HALF_PI); + * // v's components are set to [-20.0, 9.999999, 0.0] + * + *
+ */ +p5.Vector.prototype.rotate = function (a) { + var newHeading = this.heading() + a; + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + newHeading = polarGeometry.degreesToRadians(newHeading); + } + } + var mag = this.mag(); + this.x = Math.cos(newHeading) * mag; + this.y = Math.sin(newHeading) * mag; + return this; +}; + +/** + * Calculates and returns the angle (in radians) between two vectors. + * @method angleBetween + * @param {p5.Vector} the x, y, and z components of a p5.Vector + * @return {Number} the angle between (in radians) + * @example + *
+ * + * var v1 = createVector(1, 0, 0); + * var v2 = createVector(0, 1, 0); + * + * var angle = v1.angleBetween(v2); + * // angle is PI/2 + * + *
+ */ +p5.Vector.prototype.angleBetween = function (v) { + var angle = Math.acos(this.dot(v) / (this.mag() * v.mag())); + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + angle = polarGeometry.radiansToDegrees(angle); + } + } + return angle; +}; + +/** + * Linear interpolate the vector to another vector + * + * @method lerp + * @param {p5.Vector} x the x component + * @param {p5.Vector} y the y component + * @param {p5.Vector} z the z component + * @param {Number} amt the amount of interpolation; some value between 0.0 + * (old vector) and 1.0 (new vector). 0.1 is very near + * the new vector. 0.5 is halfway in between. + * @chainable + */ +/** + * @method lerp + * @param {p5.Vector} v the p5.Vector to lerp to + * @param {Number} amt + * @chainable + * + * @example + *
+ * + * var v = createVector(1, 1, 0); + * + * v.lerp(3, 3, 0, 0.5); // v now has components [2,2,0] + * + *
+ * + *
+ * + * var v1 = createVector(0, 0, 0); + * var v2 = createVector(100, 100, 0); + * + * var v3 = p5.Vector.lerp(v1, v2, 0.5); + * // v3 has components [50,50,0] + * + *
+ */ +p5.Vector.prototype.lerp = function (x, y, z, amt) { + if (x instanceof p5.Vector) { + return this.lerp(x.x, x.y, x.z, y); + } + this.x += (x - this.x) * amt || 0; + this.y += (y - this.y) * amt || 0; + this.z += (z - this.z) * amt || 0; + return this; +}; + +/** + * Return a representation of this vector as a float array. This is only + * for temporary use. If used in any other fashion, the contents should be + * copied by using the p5.Vector.copy() method to copy into your own + * array. + * + * @method array + * @return {Number[]} an Array with the 3 values + * @example + *
+ * function setup() { + * var v = createVector(20,30); + * print(v.array()); // Prints : Array [20, 30, 0] + * } + *
+ *
+ * + * var v = createVector(10.0, 20.0, 30.0); + * var f = v.array(); + * print(f[0]); // Prints "10.0" + * print(f[1]); // Prints "20.0" + * print(f[2]); // Prints "30.0" + * + *
+ */ +p5.Vector.prototype.array = function () { + return [this.x || 0, this.y || 0, this.z || 0]; +}; + +/** + * Equality check against a p5.Vector + * + * @method equals + * @param {Number|p5.Vector|Array} [x] the x component of the vector or a + * p5.Vector or an Array + * @param {Number} [y] the y component of the vector + * @param {Number} [z] the z component of the vector + * @return {Boolean} whether the vectors are equals + * @example + *
+ * v1 = createVector(5,10,20); + * v2 = createVector(5,10,20); + * v3 = createVector(13,10,19); + * + * print(v1.equals(v2.x,v2.y,v2.z)); // true + * print(v1.equals(v3.x,v3.y,v3.z)); // false + *
+ *
+ * + * var v1 = createVector(10.0, 20.0, 30.0); + * var v2 = createVector(10.0, 20.0, 30.0); + * var v3 = createVector(0.0, 0.0, 0.0); + * print (v1.equals(v2)) // true + * print (v1.equals(v3)) // false + * + *
+ */ +p5.Vector.prototype.equals = function (x, y, z) { + var a, b, c; + if (x instanceof p5.Vector) { + a = x.x || 0; + b = x.y || 0; + c = x.z || 0; + } else if (x instanceof Array) { + a = x[0] || 0; + b = x[1] || 0; + c = x[2] || 0; + } else { + a = x || 0; + b = y || 0; + c = z || 0; + } + return this.x === a && this.y === b && this.z === c; +}; + + +// Static Methods + + +/** + * Make a new 2D unit vector from an angle + * + * @method fromAngle + * @static + * @param {Number} angle the desired angle + * @return {p5.Vector} the new p5.Vector object + * @example + *
+ * + * function draw() { + * background (200); + * + * // Create a variable, proportional to the mouseX, + * // varying from 0-360, to represent an angle in degrees. + * angleMode(DEGREES); + * var myDegrees = map(mouseX, 0,width, 0,360); + * + * // Display that variable in an onscreen text. + * // (Note the nfc() function to truncate additional decimal places, + * // and the "\xB0" character for the degree symbol.) + * var readout = "angle = " + nfc(myDegrees,1,1) + "\xB0" + * noStroke(); + * fill (0); + * text (readout, 5, 15); + * + * // Create a p5.Vector using the fromAngle function, + * // and extract its x and y components. + * var v = p5.Vector.fromAngle(radians(myDegrees)); + * var vx = v.x; + * var vy = v.y; + * + * push(); + * translate (width/2, height/2); + * noFill(); + * stroke (150); + * line (0,0, 30,0); + * stroke (0); + * line (0,0, 30*vx, 30*vy); + * pop() + * } + * + *
+ */ +p5.Vector.fromAngle = function(angle) { + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + angle = polarGeometry.degreesToRadians(angle); + } + } + if (this.p5) { + return new p5.Vector(this.p5,[Math.cos(angle),Math.sin(angle),0]); + } else { + return new p5.Vector(Math.cos(angle),Math.sin(angle),0); + } +}; + +/** + * Make a new 2D unit vector from a random angle + * + * @method random2D + * @static + * @return {p5.Vector} the new p5.Vector object + * @example + *
+ * + * var v = p5.Vector.random2D(); + * // May make v's attributes something like: + * // [0.61554617, -0.51195765, 0.0] or + * // [-0.4695841, -0.14366731, 0.0] or + * // [0.6091097, -0.22805278, 0.0] + * + *
+ */ +p5.Vector.random2D = function () { + var angle; + // A lot of nonsense to determine if we know about a + // p5 sketch and whether we should make a random angle in degrees or radians + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + angle = this.p5.random(360); + } else { + angle = this.p5.random(constants.TWO_PI); + } + } else { + angle = Math.random()*Math.PI*2; + } + return this.fromAngle(angle); +}; + +/** + * Make a new random 3D unit vector. + * + * @method random3D + * @static + * @return {p5.Vector} the new p5.Vector object + * @example + *
+ * + * var v = p5.Vector.random3D(); + * // May make v's attributes something like: + * // [0.61554617, -0.51195765, 0.599168] or + * // [-0.4695841, -0.14366731, -0.8711202] or + * // [0.6091097, -0.22805278, -0.7595902] + * + *
+ */ +p5.Vector.random3D = function () { + var angle,vz; + // If we know about p5 + if (this.p5) { + angle = this.p5.random(0,constants.TWO_PI); + vz = this.p5.random(-1,1); + } else { + angle = Math.random()*Math.PI*2; + vz = Math.random()*2-1; + } + var vx = Math.sqrt(1-vz*vz)*Math.cos(angle); + var vy = Math.sqrt(1-vz*vz)*Math.sin(angle); + if (this.p5) { + return new p5.Vector(this.p5,[vx,vy,vz]); + } else { + return new p5.Vector(vx,vy,vz); + } +}; + +// Adds two vectors together and returns a new one. +/** + * @method add + * @static + * @param {p5.Vector} v1 a p5.Vector to add + * @param {p5.Vector} v2 a p5.Vector to add + * @param {p5.Vector} target the vector to receive the result + */ +/** + * @method add + * @static + * @param {p5.Vector} v1 + * @param {p5.Vector} v2 + * @return {p5.Vector} the resulting p5.Vector + * + */ + +p5.Vector.add = function (v1, v2, target) { + if (!target) { + target = v1.copy(); + } else { + target.set(v1); + } + target.add(v2); + return target; +}; + +/* + * Subtracts one p5.Vector from another and returns a new one. The second + * vector (v2) is subtracted from the first (v1), resulting in v1-v2. + */ +/** + * @method sub + * @static + * @param {p5.Vector} v1 a p5.Vector to subtract from + * @param {p5.Vector} v2 a p5.Vector to subtract + * @param {p5.Vector} target if undefined a new vector will be created + */ +/** + * @method sub + * @static + * @param {p5.Vector} v1 + * @param {p5.Vector} v2 + * @return {p5.Vector} the resulting p5.Vector + */ + +p5.Vector.sub = function (v1, v2, target) { + if (!target) { + target = v1.copy(); + } else { + target.set(v1); + } + target.sub(v2); + return target; +}; + + +/** + * Multiplies a vector by a scalar and returns a new vector. + */ +/** + * @method mult + * @static + * @param {p5.Vector} v the vector to multiply + * @param {Number} n + * @param {p5.Vector} target if undefined a new vector will be created + */ +/** + * @method mult + * @static + * @param {p5.Vector} v + * @param {Number} n + * @return {p5.Vector} the resulting new p5.Vector + */ +p5.Vector.mult = function (v, n, target) { + if (!target) { + target = v.copy(); + } else { + target.set(v); + } + target.mult(n); + return target; +}; + +/** + * Divides a vector by a scalar and returns a new vector. + */ +/** + * @method div + * @static + * @param {p5.Vector} v the vector to divide + * @param {Number} n + * @param {p5.Vector} target if undefined a new vector will be created + */ +/** + * @method div + * @static + * @param {p5.Vector} v + * @param {Number} n + * @return {p5.Vector} the resulting new p5.Vector + */ +p5.Vector.div = function (v, n, target) { + if (!target) { + target = v.copy(); + } else { + target.set(v); + } + target.div(n); + return target; +}; + + +/** + * Calculates the dot product of two vectors. + */ +/** + * @method dot + * @static + * @param {p5.Vector} v1 the first p5.Vector + * @param {p5.Vector} v2 the second p5.Vector + * @return {Number} the dot product + */ +p5.Vector.dot = function (v1, v2) { + return v1.dot(v2); +}; + +/** + * Calculates the cross product of two vectors. + */ +/** + * @method cross + * @static + * @param {p5.Vector} v1 the first p5.Vector + * @param {p5.Vector} v2 the second p5.Vector + * @return {Number} the cross product + */ +p5.Vector.cross = function (v1, v2) { + return v1.cross(v2); +}; + +/** + * Calculates the Euclidean distance between two points (considering a + * point as a vector object). + */ +/** + * @method dist + * @static + * @param {p5.Vector} v1 the first p5.Vector + * @param {p5.Vector} v2 the second p5.Vector + * @return {Number} the distance + */ +p5.Vector.dist = function (v1,v2) { + return v1.dist(v2); +}; + +/** + * Linear interpolate a vector to another vector and return the result as a + * new vector. + */ +/** + * @method lerp + * @static + * @param {p5.Vector} v1 + * @param {p5.Vector} v2 + * @param {Number} amt + * @param {p5.Vector} target if undefined a new vector will be created + */ +/** + * @method lerp + * @static + * @param {p5.Vector} v1 + * @param {p5.Vector} v2 + * @param {Number} amt + * @return {Number} the lerped value + */ +p5.Vector.lerp = function (v1, v2, amt, target) { + if (!target) { + target = v1.copy(); + } else { + target.set(v1); + } + target.lerp(v2, amt); + return target; +}; + +/** + * @method mag + * @param {p5.Vector} vecT the vector to return the magnitude of + * @return {Number} the magnitude of vecT + * @static + */ +p5.Vector.mag = function (vecT){ + var x = vecT.x, + y = vecT.y, + z = vecT.z; + var magSq = x * x + y * y + z * z; + return Math.sqrt(magSq); +}; + +module.exports = p5.Vector; + +},{"../core/constants":53,"../core/core":54,"./polargeometry":86}],86:[function(_dereq_,module,exports){ + +module.exports = { + + degreesToRadians: function(x) { + return 2 * Math.PI * x / 360; + }, + + radiansToDegrees: function(x) { + return 360 * x / (2 * Math.PI); + } + +}; + +},{}],88:[function(_dereq_,module,exports){ +/** + * @module Math + * @submodule Trigonometry + * @for p5 + * @requires core + * @requires polargeometry + * @requires constants + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var polarGeometry = _dereq_('./polargeometry'); +var constants = _dereq_('../core/constants'); + +p5.prototype._angleMode = constants.RADIANS; + +/** + * The inverse of cos(), returns the arc cosine of a value. This function + * expects the values in the range of -1 to 1 and values are returned in + * the range 0 to PI (3.1415927). + * + * @method acos + * @param {Number} value the value whose arc cosine is to be returned + * @return {Number} the arc cosine of the given value + * + * @example + *
+ * + * var a = PI; + * var c = cos(a); + * var ac = acos(c); + * // Prints: "3.1415927 : -1.0 : 3.1415927" + * print(a + " : " + c + " : " + ac); + * + *
+ * + *
+ * + * var a = PI + PI/4.0; + * var c = cos(a); + * var ac = acos(c); + * // Prints: "3.926991 : -0.70710665 : 2.3561943" + * print(a + " : " + c + " : " + ac); + * + *
+ */ +p5.prototype.acos = function(ratio) { + if (this._angleMode === constants.RADIANS) { + return Math.acos(ratio); + } else { + return polarGeometry.radiansToDegrees(Math.acos(ratio)); + } +}; + +/** + * The inverse of sin(), returns the arc sine of a value. This function + * expects the values in the range of -1 to 1 and values are returned + * in the range -PI/2 to PI/2. + * + * @method asin + * @param {Number} value the value whose arc sine is to be returned + * @return {Number} the arc sine of the given value + * + * @example + *
+ * + * var a = PI + PI/3; + * var s = sin(a); + * var as = asin(s); + * // Prints: "1.0471976 : 0.86602545 : 1.0471976" + * print(a + " : " + s + " : " + as); + * + *
+ * + *
+ * + * var a = PI + PI/3.0; + * var s = sin(a); + * var as = asin(s); + * // Prints: "4.1887903 : -0.86602545 : -1.0471976" + * print(a + " : " + s + " : " + as); + * + *
+ * + */ +p5.prototype.asin = function(ratio) { + if (this._angleMode === constants.RADIANS) { + return Math.asin(ratio); + } else { + return polarGeometry.radiansToDegrees(Math.asin(ratio)); + } +}; + +/** + * The inverse of tan(), returns the arc tangent of a value. This function + * expects the values in the range of -Infinity to Infinity (exclusive) and + * values are returned in the range -PI/2 to PI/2. + * + * @method atan + * @param {Number} value the value whose arc tangent is to be returned + * @return {Number} the arc tangent of the given value + * + * @example + *
+ * + * var a = PI + PI/3; + * var t = tan(a); + * var at = atan(t); + * // Prints: "1.0471976 : 1.7320509 : 1.0471976" + * print(a + " : " + t + " : " + at); + * + *
+ * + *
+ * + * var a = PI + PI/3.0; + * var t = tan(a); + * var at = atan(t); + * // Prints: "4.1887903 : 1.7320513 : 1.0471977" + * print(a + " : " + t + " : " + at); + * + *
+ * + */ +p5.prototype.atan = function(ratio) { + if (this._angleMode === constants.RADIANS) { + return Math.atan(ratio); + } else { + return polarGeometry.radiansToDegrees(Math.atan(ratio)); + } +}; + +/** + * Calculates the angle (in radians) from a specified point to the coordinate + * origin as measured from the positive x-axis. Values are returned as a + * float in the range from PI to -PI. The atan2() function is most often used + * for orienting geometry to the position of the cursor. + *

+ * Note: The y-coordinate of the point is the first parameter, and the + * x-coordinate is the second parameter, due the the structure of calculating + * the tangent. + * + * @method atan2 + * @param {Number} y y-coordinate of the point + * @param {Number} x x-coordinate of the point + * @return {Number} the arc tangent of the given point + * + * @example + *
+ * + * function draw() { + * background(204); + * translate(width/2, height/2); + * var a = atan2(mouseY-height/2, mouseX-width/2); + * rotate(a); + * rect(-30, -5, 60, 10); + * } + * + *
+ * + * @alt + * 60 by 10 rect at center of canvas rotates with mouse movements + * + */ +p5.prototype.atan2 = function (y, x) { + if (this._angleMode === constants.RADIANS) { + return Math.atan2(y, x); + } else { + return polarGeometry.radiansToDegrees(Math.atan2(y, x)); + } +}; + +/** + * Calculates the cosine of an angle. This function takes into account the + * current angleMode. Values are returned in the range -1 to 1. + * + * @method cos + * @param {Number} angle the angle + * @return {Number} the cosine of the angle + * + * @example + *
+ * + * var a = 0.0; + * var inc = TWO_PI/25.0; + * for (var i = 0; i < 25; i++) { + * line(i*4, 50, i*4, 50+cos(a)*40.0); + * a = a + inc; + * } + * + *
+ * + * @alt + * vertical black lines form wave patterns, extend-down on left and right side + * + */ +p5.prototype.cos = function(angle) { + if (this._angleMode === constants.RADIANS) { + return Math.cos(angle); + } else { + return Math.cos(this.radians(angle)); + } +}; + +/** + * Calculates the sine of an angle. This function takes into account the + * current angleMode. Values are returned in the range -1 to 1. + * + * @method sin + * @param {Number} angle the angle + * @return {Number} the sine of the angle + * + * @example + *
+ * + * var a = 0.0; + * var inc = TWO_PI/25.0; + * for (var i = 0; i < 25; i++) { + * line(i*4, 50, i*4, 50+sin(a)*40.0); + * a = a + inc; + * } + * + *
+ * + * @alt + * vertical black lines extend down and up from center to form wave pattern + * + */ +p5.prototype.sin = function(angle) { + if (this._angleMode === constants.RADIANS) { + return Math.sin(angle); + } else { + return Math.sin(this.radians(angle)); + } +}; + +/** + * Calculates the tangent of an angle. This function takes into account + * the current angleMode. Values are returned in the range -1 to 1. + * + * @method tan + * @param {Number} angle the angle + * @return {Number} the tangent of the angle + * + * @example + *
+ * + * var a = 0.0; + * var inc = TWO_PI/50.0; + * for (var i = 0; i < 100; i = i+2) { + * line(i, 50, i, 50+tan(a)*2.0); + * a = a + inc; + * } + * + * + * + * @alt + * vertical black lines end down and up from center to form spike pattern + * + */ +p5.prototype.tan = function(angle) { + if (this._angleMode === constants.RADIANS) { + return Math.tan(angle); + } else { + return Math.tan(this.radians(angle)); + } +}; + +/** + * Converts a radian measurement to its corresponding value in degrees. + * Radians and degrees are two ways of measuring the same thing. There are + * 360 degrees in a circle and 2*PI radians in a circle. For example, + * 90° = PI/2 = 1.5707964. + * + * @method degrees + * @param {Number} radians the radians value to convert to degrees + * @return {Number} the converted angle + * + * + * @example + *
+ * + * var rad = PI/4; + * var deg = degrees(rad); + * print(rad + " radians is " + deg + " degrees"); + * // Prints: 0.7853981633974483 radians is 45 degrees + * + *
+ * + */ +p5.prototype.degrees = function(angle) { + return polarGeometry.radiansToDegrees(angle); +}; + +/** + * Converts a degree measurement to its corresponding value in radians. + * Radians and degrees are two ways of measuring the same thing. There are + * 360 degrees in a circle and 2*PI radians in a circle. For example, + * 90° = PI/2 = 1.5707964. + * + * @method radians + * @param {Number} degrees the degree value to convert to radians + * @return {Number} the converted angle + * + * @example + *
+ * + * var deg = 45.0; + * var rad = radians(deg); + * print(deg + " degrees is " + rad + " radians"); + * // Prints: 45 degrees is 0.7853981633974483 radians + * + *
+ */ +p5.prototype.radians = function(angle) { + return polarGeometry.degreesToRadians(angle); +}; + +/** + * Sets the current mode of p5 to given mode. Default mode is RADIANS. + * + * @method angleMode + * @param {Constant} mode either RADIANS or DEGREES + * + * @example + *
+ * + * function draw(){ + * background(204); + * angleMode(DEGREES); // Change the mode to DEGREES + * var a = atan2(mouseY-height/2, mouseX-width/2); + * translate(width/2, height/2); + * push(); + * rotate(a); + * rect(-20, -5, 40, 10); // Larger rectangle is rotating in degrees + * pop(); + * angleMode(RADIANS); // Change the mode to RADIANS + * rotate(a); // var a stays the same + * rect(-40, -5, 20, 10); // Smaller rectangle is rotating in radians + * } + * + *
+ * + * @alt + * 40 by 10 rect in center rotates with mouse moves. 20 by 10 rect moves faster. + * + * + */ +p5.prototype.angleMode = function(mode) { + if (mode === constants.DEGREES || mode === constants.RADIANS) { + this._angleMode = mode; + } +}; + +module.exports = p5; + +},{"../core/constants":53,"../core/core":54,"./polargeometry":86}],96:[function(_dereq_,module,exports){ +/** + * @module Lights, Camera + * @submodule Camera + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Sets camera position + * @method camera + * @param {Number} x camera position value on x axis + * @param {Number} y camera position value on y axis + * @param {Number} z camera position value on z axis + * @return {p5} the p5 object + * @example + *
+ * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * //move the camera away from the plane by a sin wave + * camera(0, 0, sin(frameCount * 0.01) * 100); + * plane(120, 120); + * } + * + *
+ * + * @alt + * blue square shrinks in size grows to fill canvas. disappears then loops. + * + */ +p5.prototype.camera = function(x, y, z){ + //what it manipulates is the model view matrix + this._renderer.translate(-x, -y, -z); +}; + +/** + * Sets perspective camera + * @method perspective + * @param {Number} fovy camera frustum vertical field of view, + * from bottom to top of view, in degrees + * @param {Number} aspect camera frustum aspect ratio + * @param {Number} near frustum near plane length + * @param {Number} far frustum far plane length + * @return {p5} the p5 object + * @example + *
+ * + * //drag mouse to toggle the world! + * //you will see there's a vanish point + * function setup(){ + * createCanvas(100, 100, WEBGL); + * var fov = 60 / 180 * PI; + * var cameraZ = (height/2.0) / tan(fov/2.0); + * perspective(60 / 180 * PI, width/height, cameraZ * 0.1, cameraZ * 10); + * } + * function draw(){ + * background(200); + * orbitControl(); + * for(var i = -1; i < 2; i++){ + * for(var j = -2; j < 3; j++){ + * push(); + * translate(i*160, 0, j*160); + * box(40, 40, 40); + * pop(); + * } + * } + * } + * + *
+ * + * @alt + * colored 3d boxes toggleable with mouse position + * + */ +p5.prototype.perspective = function(fovy,aspect,near,far) { + fovy = fovy || (60 / 180 * this.PI); + aspect = aspect || (this.width/this.height); + near = near || ((this.height/2.0) / this.tan(fovy/2.0) * 0.1); + far = far || ((this.height/2.0) / this.tan(fovy/2.0) * 10); + this._renderer.uPMatrix = p5.Matrix.identity(); + this._renderer.uPMatrix.perspective(fovy,aspect,near,far); + this._renderer._curCamera = 'custom'; +}; + +/** + * Setup ortho camera + * @method ortho + * @param {Number} left camera frustum left plane + * @param {Number} right camera frustum right plane + * @param {Number} bottom camera frustum bottom plane + * @param {Number} top camera frustum top plane + * @param {Number} near camera frustum near plane + * @param {Number} far camera frustum far plane + * @return {p5} the p5 object + * @example + *
+ * + * //drag mouse to toggle the world! + * //there's no vanish point + * function setup(){ + * createCanvas(100, 100, WEBGL); + * ortho(-width/2, width/2, height/2, -height/2, 0, 500); + * } + * function draw(){ + * background(200); + * orbitControl(); + * for(var i = -1; i < 2; i++){ + * for(var j = -2; j < 3; j++){ + * push(); + * translate(i*160, 0, j*160); + * box(40, 40, 40); + * pop(); + * } + * } + * } + * + *
+ * + * @alt + * 3 3d boxes, reveal several more boxes on 3d plane when mouse used to toggle + * + */ +p5.prototype.ortho = function(left,right,bottom,top,near,far) { + left = left || (-this.width/2); + right = right || (this.width/2); + bottom = bottom || (-this.height/2); + top = top || (this.height/2); + near = near || 0; + far = far || Math.max(this.width, this.height); + this._renderer.uPMatrix = p5.Matrix.identity(); + this._renderer.uPMatrix.ortho(left,right,bottom,top,near,far); + this._renderer._curCamera = 'custom'; +}; + +module.exports = p5; + +},{"../core/core":54}],97:[function(_dereq_,module,exports){ +'use strict'; + +var p5 = _dereq_('../core/core'); + +//@TODO: implement full orbit controls including +//pan, zoom, quaternion rotation, etc. +p5.prototype.orbitControl = function(){ + if(this.mouseIsPressed){ + this.rotateY((this.mouseX - this.width / 2) / (this.width / 2)); + this.rotateX((this.mouseY - this.height / 2) / (this.width / 2)); + } + return this; +}; + +module.exports = p5; +},{"../core/core":54}],98:[function(_dereq_,module,exports){ +/** + * @module Lights, Camera + * @submodule Lights + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * Creates an ambient light with a color + * + * @method ambientLight + * @param {Number} v1 red or hue value relative to + * the current color range + * @param {Number} v2 green or saturation value + * relative to the current color range + * @param {Number} v3 blue or brightness value + * relative to the current color range + * @param {Number} [alpha] + * @chainable + */ + +/** + * @method ambientLight + * @param {String} value a color string + * @param {Number} [alpha] + * @chainable + */ + +/** + * @method ambientLight + * @param {Number[]} values an array containing the red,green,blue & + * and alpha components of the color + * @chainable + */ + +/** + * @method ambientLight + * @param {p5.Color} color the ambient light color + * @param {Number} [alpha] + * @chainable + * + * @example + *
+ * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * ambientLight(150); + * ambientMaterial(250); + * sphere(50); + * } + * + *
+ * + * @alt + * nothing displayed + * + */ +p5.prototype.ambientLight = function(v1, v2, v3, a){ + var gl = this._renderer.GL; + var shaderProgram = this._renderer._getShader( + 'lightVert', 'lightTextureFrag'); + + gl.useProgram(shaderProgram); + shaderProgram.uAmbientColor = gl.getUniformLocation( + shaderProgram, + 'uAmbientColor[' + this._renderer.ambientLightCount + ']'); + + var color = this._renderer._pInst.color.apply( + this._renderer._pInst, arguments); + var colors = color._array; + + gl.uniform3f( shaderProgram.uAmbientColor, + colors[0], colors[1], colors[2]); + + //in case there's no material color for the geometry + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1); + + this._renderer.ambientLightCount ++; + shaderProgram.uAmbientLightCount = + gl.getUniformLocation(shaderProgram, 'uAmbientLightCount'); + gl.uniform1i(shaderProgram.uAmbientLightCount, + this._renderer.ambientLightCount); + + return this; +}; + +/** + * Creates a directional light with a color and a direction + * @method directionalLight + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @param {Number|p5.Vector} x x axis direction or a p5.Vector + * @param {Number} [y] optional: y axis direction + * @param {Number} [z] optional: z axis direction + * @chainable + * @example + *
+ * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * //move your mouse to change light direction + * var dirX = (mouseX / width - 0.5) *2; + * var dirY = (mouseY / height - 0.5) *(-2); + * directionalLight(250, 250, 250, dirX, dirY, 0.25); + * ambientMaterial(250); + * sphere(50); + * } + * + *
+ * + * @alt + * light source on canvas changeable with mouse position + * + */ +p5.prototype.directionalLight = function(v1, v2, v3, a, x, y, z) { + var gl = this._renderer.GL; + var shaderProgram = this._renderer._getShader( + 'lightVert', 'lightTextureFrag'); + + gl.useProgram(shaderProgram); + shaderProgram.uDirectionalColor = gl.getUniformLocation( + shaderProgram, + 'uDirectionalColor[' + this._renderer.directionalLightCount + ']'); + + //@TODO: check parameters number + var color = this._renderer._pInst.color.apply( + this._renderer._pInst, [v1, v2, v3]); + var colors = color._array; + + gl.uniform3f( shaderProgram.uDirectionalColor, + colors[0], colors[1], colors[2]); + + var _x, _y, _z; + + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if(typeof args[args.length-1] === 'number'){ + _x = args[args.length-3]; + _y = args[args.length-2]; + _z = args[args.length-1]; + + }else{ + try{ + _x = args[args.length-1].x; + _y = args[args.length-1].y; + _z = args[args.length-1].z; + } + catch(error){ + throw error; + } + } + + shaderProgram.uLightingDirection = gl.getUniformLocation( + shaderProgram, + 'uLightingDirection[' + this._renderer.directionalLightCount + ']'); + gl.uniform3f( shaderProgram.uLightingDirection, _x, _y, _z); + + //in case there's no material color for the geometry + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1); + + this._renderer.directionalLightCount ++; + shaderProgram.uDirectionalLightCount = + gl.getUniformLocation(shaderProgram, 'uDirectionalLightCount'); + gl.uniform1i(shaderProgram.uDirectionalLightCount, + this._renderer.directionalLightCount); + + return this; +}; + +/** + * Creates a point light with a color and a light position + * @method pointLight + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @param {Number|p5.Vector} x x axis position or a p5.Vector + * @param {Number} [y] optional: y axis position + * @param {Number} [z] optional: z axis position + * @chainable + * @example + *
+ * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * //move your mouse to change light position + * var locY = (mouseY / height - 0.5) *(-2); + * var locX = (mouseX / width - 0.5) *2; + * //to set the light position, + * //think of the world's coordinate as: + * // -1,1 -------- 1,1 + * // | | + * // | | + * // | | + * // -1,-1---------1,-1 + * pointLight(250, 250, 250, locX, locY, 0); + * ambientMaterial(250); + * sphere(50); + * } + * + *
+ * + * @alt + * spot light on canvas changes position with mouse + * + */ +p5.prototype.pointLight = function(v1, v2, v3, a, x, y, z) { + var gl = this._renderer.GL; + var shaderProgram = this._renderer._getShader( + 'lightVert', 'lightTextureFrag'); + + gl.useProgram(shaderProgram); + shaderProgram.uPointLightColor = gl.getUniformLocation( + shaderProgram, + 'uPointLightColor[' + this._renderer.pointLightCount + ']'); + + //@TODO: check parameters number + var color = this._renderer._pInst.color.apply( + this._renderer._pInst, [v1, v2, v3]); + var colors = color._array; + + gl.uniform3f( shaderProgram.uPointLightColor, + colors[0], colors[1], colors[2]); + + var _x, _y, _z; + + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + if(typeof args[args.length-1] === 'number'){ + _x = args[args.length-3]; + _y = args[args.length-2]; + _z = args[args.length-1]; + + }else{ + try{ + _x = args[args.length-1].x; + _y = args[args.length-1].y; + _z = args[args.length-1].z; + } + catch(error){ + throw error; + } + } + + shaderProgram.uPointLightLocation = gl.getUniformLocation( + shaderProgram, + 'uPointLightLocation[' + this._renderer.pointLightCount + ']'); + gl.uniform3f( shaderProgram.uPointLightLocation, _x, _y, _z); + + //in case there's no material color for the geometry + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1); + + this._renderer.pointLightCount ++; + shaderProgram.uPointLightCount = + gl.getUniformLocation(shaderProgram, 'uPointLightCount'); + gl.uniform1i(shaderProgram.uPointLightCount, + this._renderer.pointLightCount); + + return this; +}; + +module.exports = p5; + +},{"../core/core":54}],99:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule 3D Models + * @for p5 + * @requires core + * @requires p5.Geometry + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +_dereq_('./p5.Geometry'); + +/** + * Load a 3d model from an OBJ file. + *

+ * One of the limitations of the OBJ format is that it doesn't have a built-in + * sense of scale. This means that models exported from different programs might + * be very different sizes. If your model isn't displaying, try calling + * loadModel() with the normalized parameter set to true. This will resize the + * model to a scale appropriate for p5. You can also make additional changes to + * the final size of your model with the scale() function. + * + * @method loadModel + * @param {String} path Path of the model to be loaded + * @param {Boolean} normalize If true, scale the model to a + * standardized size when loading + * @param {function(p5.Geometry)} [successCallback] Function to be called + * once the model is loaded. Will be passed + * the 3D model object. + * @param {function(Event)} [failureCallback] called with event error if + * the image fails to load. + * @return {p5.Geometry} the p5.Geometry object + */ +/** + * @method loadModel + * @param {String} path + * @param {function(p5.Geometry)} [successCallback] + * @param {function(Event)} [failureCallback] + * @return {p5.Geometry} the p5.Geometry object + * @example + *
+ * + * //draw a spinning teapot + * var teapot; + * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * + * teapot = loadModel('assets/teapot.obj'); + * } + * + * function draw(){ + * background(200); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * model(teapot); + * } + * + *
+ * + * @alt + * Vertically rotating 3-d teapot with red, green and blue gradient. + * + */ +p5.prototype.loadModel = function () { + var path = arguments[0]; + var normalize; + var successCallback; + var failureCallback; + if(typeof arguments[1] === 'boolean') { + normalize = arguments[1]; + successCallback = arguments[2]; + failureCallback = arguments[3]; + } else { + normalize = false; + successCallback = arguments[1]; + failureCallback = arguments[2]; + } + + var model = new p5.Geometry(); + model.gid = path + '|' + normalize; + this.loadStrings(path, function(strings) { + parseObj(model, strings); + + if (normalize) { + model.normalize(); + } + + if (typeof successCallback === 'function') { + successCallback(model); + } + }.bind(this), failureCallback); + + return model; +}; + +/** + * Parse OBJ lines into model. For reference, this is what a simple model of a + * square might look like: + * + * v -0.5 -0.5 0.5 + * v -0.5 -0.5 -0.5 + * v -0.5 0.5 -0.5 + * v -0.5 0.5 0.5 + * + * f 4 3 2 1 + */ +function parseObj( model, lines ) { + // OBJ allows a face to specify an index for a vertex (in the above example), + // but it also allows you to specify a custom combination of vertex, UV + // coordinate, and vertex normal. So, "3/4/3" would mean, "use vertex 3 with + // UV coordinate 4 and vertex normal 3". In WebGL, every vertex with different + // parameters must be a different vertex, so loadedVerts is used to + // temporarily store the parsed vertices, normals, etc., and indexedVerts is + // used to map a specific combination (keyed on, for example, the string + // "3/4/3"), to the actual index of the newly created vertex in the final + // object. + var loadedVerts = {'v' : [], + 'vt' : [], + 'vn' : []}; + var indexedVerts = {}; + + for (var line = 0; line < lines.length; ++line) { + // Each line is a separate object (vertex, face, vertex normal, etc) + // For each line, split it into tokens on whitespace. The first token + // describes the type. + var tokens = lines[line].trim().split(/\b\s+/); + + if (tokens.length > 0) { + if (tokens[0] === 'v' || tokens[0] === 'vn') { + // Check if this line describes a vertex or vertex normal. + // It will have three numeric parameters. + var vertex = new p5.Vector(parseFloat(tokens[1]), + parseFloat(tokens[2]), + parseFloat(tokens[3])); + loadedVerts[tokens[0]].push(vertex); + } else if (tokens[0] === 'vt') { + // Check if this line describes a texture coordinate. + // It will have two numeric parameters. + var texVertex = [parseFloat(tokens[1]), parseFloat(tokens[2])]; + loadedVerts[tokens[0]].push(texVertex); + } else if (tokens[0] === 'f') { + // Check if this line describes a face. + // OBJ faces can have more than three points. Triangulate points. + for (var tri = 3; tri < tokens.length; ++tri) { + var face = []; + + var vertexTokens = [1, tri - 1, tri]; + + for (var tokenInd = 0; tokenInd < vertexTokens.length; ++tokenInd) { + // Now, convert the given token into an index + var vertString = tokens[vertexTokens[tokenInd]]; + var vertIndex = 0; + + // TODO: Faces can technically use negative numbers to refer to the + // previous nth vertex. I haven't seen this used in practice, but + // it might be good to implement this in the future. + + if (indexedVerts[vertString] !== undefined) { + vertIndex = indexedVerts[vertString]; + } else { + var vertParts = vertString.split('/'); + for (var i = 0; i < vertParts.length; i++) { + vertParts[i] = parseInt(vertParts[i]) - 1; + } + + vertIndex = indexedVerts[vertString] = model.vertices.length; + model.vertices.push(loadedVerts.v[vertParts[0]].copy()); + if (loadedVerts.vt[vertParts[1]]) { + model.uvs.push(loadedVerts.vt[vertParts[1]].slice()); + } else { + model.uvs.push([0, 0]); + } + + if (loadedVerts.vn[vertParts[2]]) { + model.vertexNormals.push(loadedVerts.vn[vertParts[2]].copy()); + } + } + + face.push(vertIndex); + } + + model.faces.push(face); + } + } + } + } + + // If the model doesn't have normals, compute the normals + if(model.vertexNormals.length === 0) { + model.computeNormals(); + } + + return model; +} + +/** + * Render a 3d model to the screen. + * + * @method model + * @param {p5.Geometry} model Loaded 3d model to be rendered + * @example + *
+ * + * //draw a spinning teapot + * var teapot; + * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * + * teapot = loadModel('assets/teapot.obj'); + * } + * + * function draw(){ + * background(200); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * model(teapot); + * } + * + *
+ * + * @alt + * Vertically rotating 3-d teapot with red, green and blue gradient. + * + */ +p5.prototype.model = function ( model ) { + if (model.vertices.length > 0) { + if (!this._renderer.geometryInHash(model.gid)) { + this._renderer.createBuffers(model.gid, model); + } + + this._renderer.drawBuffers(model.gid); + } +}; + +module.exports = p5; + +},{"../core/core":54,"./p5.Geometry":101}],100:[function(_dereq_,module,exports){ +/** + * @module Lights, Camera + * @submodule Material + * @for p5 + * @requires core + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +//require('./p5.Texture'); + +/** + * Normal material for geometry. You can view all + * possible materials in this + * example. + * @method normalMaterial + * @chainable + * @example + *
+ * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * normalMaterial(); + * sphere(50); + * } + * + *
+ * + * @alt + * Red, green and blue gradient. + * + */ +p5.prototype.normalMaterial = function(){ + this._renderer._getShader('normalVert', 'normalFrag'); + return this; +}; + +/** + * Texture for geometry. You can view other possible materials in this + * example. + * @method texture + * @param {p5.Image | p5.MediaElement | p5.Graphics} tex 2-dimensional graphics + * to render as texture + * @chainable + * @example + *
+ * + * var img; + * function setup(){ + * createCanvas(100, 100, WEBGL); + * img = loadImage("assets/laDefense.jpg"); + * } + * + * function draw(){ + * background(0); + * rotateZ(frameCount * 0.01); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * //pass image as texture + * texture(img); + * box(200, 200, 200); + * } + * + *
+ * + *
+ * + * var pg; + * function setup(){ + * createCanvas(100, 100, WEBGL); + * pg = createGraphics(200, 200); + * pg.textSize(100); + * } + * + * function draw(){ + * background(0); + * pg.background(255); + * pg.text('hello!', 0, 100); + * //pass image as texture + * texture(pg); + * plane(200); + * } + * + *
+ * + *
+ * + * var vid; + * function preload(){ + * vid = createVideo("assets/fingers.mov"); + * vid.hide(); + * vid.loop(); + * } + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(0); + * //pass video frame as texture + * texture(vid); + * plane(200); + * } + * + *
+ * + * @alt + * Rotating view of many images umbrella and grid roof on a 3d plane + * black canvas + * black canvas + * + */ +p5.prototype.texture = function(){ + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + var gl = this._renderer.GL; + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + this._renderer.drawMode = 'texture'; + var shaderProgram = this._renderer._getShader('lightVert', + 'lightTextureFrag'); + gl.useProgram(shaderProgram); + var textureData; + //if argument is not already a texture + //create a new one + if(!args[0].isTexture){ + if (args[0] instanceof p5.Image) { + textureData = args[0].canvas; + } + //if param is a video + else if (typeof p5.MediaElement !== 'undefined' && + args[0] instanceof p5.MediaElement){ + if(!args[0].loadedmetadata) {return;} + textureData = args[0].elt; + } + //used with offscreen 2d graphics renderer + else if(args[0] instanceof p5.Graphics){ + textureData = args[0].elt; + } + var tex = gl.createTexture(); + args[0]._setProperty('tex', tex); + args[0]._setProperty('isTexture', true); + this._renderer._bind.call(this, tex, textureData); + } + else { + if(args[0] instanceof p5.Graphics || + (typeof p5.MediaElement !== 'undefined' && + args[0] instanceof p5.MediaElement)){ + textureData = args[0].elt; + } + else if(args[0] instanceof p5.Image){ + textureData = args[0].canvas; + } + this._renderer._bind.call(this, args[0].tex, textureData); + } + //this is where we'd activate multi textures + //eg. gl.activeTexture(gl.TEXTURE0 + (unit || 0)); + //but for now we just have a single texture. + //@TODO need to extend this functionality + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, args[0].tex); + gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), true); + gl.uniform1i(gl.getUniformLocation(shaderProgram, 'uSampler'), 0); + return this; +}; + +/** + * Texture Util functions + */ +p5.RendererGL.prototype._bind = function(tex, data){ + var gl = this._renderer.GL; + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texImage2D(gl.TEXTURE_2D, 0, + gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, data); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texParameteri(gl.TEXTURE_2D, + gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, + gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, + gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, + gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindTexture(gl.TEXTURE_2D, null); +}; + +/** + * Checks whether val is a pot + * more info on power of 2 here: + * https://www.opengl.org/wiki/NPOT_Texture + * @param {Number} value + * @return {Boolean} + */ +// function _isPowerOf2 (value){ +// return (value & (value - 1)) === 0; +// } + +/** + * returns the next highest power of 2 value + * @param {Number} value [description] + * @return {Number} [description] + */ +// function _nextHighestPOT (value){ +// --value; +// for (var i = 1; i < 32; i <<= 1) { +// value = value | value >> i; +// } +// return value + 1; + +/** + * Ambient material for geometry with a given color. You can view all + * possible materials in this + * example. + * @method ambientMaterial + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @chainable + * @example + *
+ * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * ambientLight(100); + * pointLight(250, 250, 250, 100, 100, 0); + * ambientMaterial(250); + * sphere(50); + * } + * + *
+ * + * @alt + * radiating light source from top right of canvas + * + */ +p5.prototype.ambientMaterial = function(v1, v2, v3, a) { + var gl = this._renderer.GL; + var shaderProgram = + this._renderer._getShader('lightVert', 'lightTextureFrag'); + + gl.useProgram(shaderProgram); + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + var colors = this._renderer._applyColorBlend.apply(this._renderer, arguments); + + gl.uniform4f(shaderProgram.uMaterialColor, + colors[0], colors[1], colors[2], colors[3]); + + shaderProgram.uSpecular = gl.getUniformLocation( + shaderProgram, 'uSpecular' ); + gl.uniform1i(shaderProgram.uSpecular, false); + + gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false); + + return this; +}; + +/** + * Specular material for geometry with a given color. You can view all + * possible materials in this + * example. + * @method specularMaterial + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @chainable + * @example + *
+ * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * function draw(){ + * background(0); + * ambientLight(100); + * pointLight(250, 250, 250, 100, 100, 0); + * specularMaterial(250); + * sphere(50); + * } + * + *
+ * + * @alt + * diffused radiating light source from top right of canvas + * + */ +p5.prototype.specularMaterial = function(v1, v2, v3, a) { + var gl = this._renderer.GL; + var shaderProgram = + this._renderer._getShader('lightVert', 'lightTextureFrag'); + gl.useProgram(shaderProgram); + gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false); + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + var colors = this._renderer._applyColorBlend.apply(this._renderer, arguments); + gl.uniform4f(shaderProgram.uMaterialColor, + colors[0], colors[1], colors[2], colors[3]); + shaderProgram.uSpecular = gl.getUniformLocation( + shaderProgram, 'uSpecular' ); + gl.uniform1i(shaderProgram.uSpecular, true); + + return this; +}; + +/** + * @private blends colors according to color components. + * If alpha value is less than 1, we need to enable blending + * on our gl context. Otherwise opaque objects need to a depthMask. + * @param {Number} v1 [description] + * @param {Number} v2 [description] + * @param {Number} v3 [description] + * @param {Number} a [description] + * @return {[Number]} Normalized numbers array + */ +p5.RendererGL.prototype._applyColorBlend = function(v1,v2,v3,a){ + var gl = this.GL; + var color = this._pInst.color.apply( + this._pInst, arguments); + var colors = color._array; + if(colors[colors.length-1] < 1.0){ + gl.depthMask(false); + gl.enable(gl.BLEND); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + } else { + gl.depthMask(true); + gl.disable(gl.BLEND); + } + return colors; +}; + +module.exports = p5; + +},{"../core/core":54}],101:[function(_dereq_,module,exports){ +//some of the functions are adjusted from Three.js(http://threejs.org) + +'use strict'; + +var p5 = _dereq_('../core/core'); + +/** + * p5 Geometry class + * @class p5.Geometry + * @constructor + * @param {function | Object} vertData callback function or Object + * containing routine(s) for vertex data generation + * @param {Number} [detailX] number of vertices on horizontal surface + * @param {Number} [detailY] number of vertices on horizontal surface + * @param {function} [callback] function to call upon object instantiation. + * + */ +p5.Geometry = function +(detailX, detailY, callback){ + //an array containing every vertex + //@type [p5.Vector] + this.vertices = []; + //an array containing 1 normal per vertex + //@type [p5.Vector] + //[p5.Vector, p5.Vector, p5.Vector,p5.Vector, p5.Vector, p5.Vector,...] + this.vertexNormals = []; + //an array containing each three vertex indices that form a face + //[[0, 1, 2], [2, 1, 3], ...] + this.faces = []; + //a 2D array containing uvs for every vertex + //[[0.0,0.0],[1.0,0.0], ...] + this.uvs = []; + this.detailX = (detailX !== undefined) ? detailX: 1; + this.detailY = (detailY !== undefined) ? detailY: 1; + if(callback instanceof Function){ + callback.call(this); + } + return this; // TODO: is this a constructor? +}; + +p5.Geometry.prototype.computeFaces = function(){ + var sliceCount = this.detailX + 1; + var a, b, c, d; + for (var i = 0; i < this.detailY; i++){ + for (var j = 0; j < this.detailX; j++){ + a = i * sliceCount + j;// + offset; + b = i * sliceCount + j + 1;// + offset; + c = (i + 1)* sliceCount + j + 1;// + offset; + d = (i + 1)* sliceCount + j;// + offset; + this.faces.push([a, b, d]); + this.faces.push([d, b, c]); + } + } + return this; +}; + +p5.Geometry.prototype._getFaceNormal = function(faceId,vertId){ + //This assumes that vA->vB->vC is a counter-clockwise ordering + var face = this.faces[faceId]; + var vA = this.vertices[face[vertId%3]]; + var vB = this.vertices[face[(vertId+1)%3]]; + var vC = this.vertices[face[(vertId+2)%3]]; + var n = p5.Vector.cross( + p5.Vector.sub(vB,vA), + p5.Vector.sub(vC,vA)); + var sinAlpha = p5.Vector.mag(n) / + (p5.Vector.mag(p5.Vector.sub(vB,vA))* + p5.Vector.mag(p5.Vector.sub(vC,vA))); + n = n.normalize(); + return n.mult(Math.asin(sinAlpha)); +}; +/** + * computes smooth normals per vertex as an average of each + * face. + * @chainable + */ +p5.Geometry.prototype.computeNormals = function (){ + for(var v=0; v < this.vertices.length; v++){ + var normal = new p5.Vector(); + for(var i=0; i < this.faces.length; i++){ + //if our face contains a given vertex + //calculate an average of the normals + //of the triangles adjacent to that vertex + if(this.faces[i][0] === v || + this.faces[i][1] === v || + this.faces[i][2] === v) + { + normal = normal.add(this._getFaceNormal(i, v)); + } + } + normal = normal.normalize(); + this.vertexNormals.push(normal); + } + return this; +}; + +/** + * Averages the vertex normals. Used in curved + * surfaces + * @chainable + */ +p5.Geometry.prototype.averageNormals = function() { + + for(var i = 0; i <= this.detailY; i++){ + var offset = this.detailX + 1; + var temp = p5.Vector + .add(this.vertexNormals[i*offset], + this.vertexNormals[i*offset + this.detailX]); + temp = p5.Vector.div(temp, 2); + this.vertexNormals[i*offset] = temp; + this.vertexNormals[i*offset + this.detailX] = temp; + } + return this; +}; + +/** + * Averages pole normals. Used in spherical primitives + * @chainable + */ +p5.Geometry.prototype.averagePoleNormals = function() { + + //average the north pole + var sum = new p5.Vector(0, 0, 0); + for(var i = 0; i < this.detailX; i++){ + sum.add(this.vertexNormals[i]); + } + sum = p5.Vector.div(sum, this.detailX); + + for(i = 0; i < this.detailX; i++){ + this.vertexNormals[i] = sum; + } + + //average the south pole + sum = new p5.Vector(0, 0, 0); + for(i = this.vertices.length - 1; + i > this.vertices.length - 1 - this.detailX; i--){ + sum.add(this.vertexNormals[i]); + } + sum = p5.Vector.div(sum, this.detailX); + + for(i = this.vertices.length - 1; + i > this.vertices.length - 1 - this.detailX; i--){ + this.vertexNormals[i] = sum; + } + return this; +}; + +/** + * Modifies all vertices to be centered within the range -100 to 100. + * @chainable + */ +p5.Geometry.prototype.normalize = function() { + if(this.vertices.length > 0) { + // Find the corners of our bounding box + var maxPosition = this.vertices[0].copy(); + var minPosition = this.vertices[0].copy(); + + for(var i = 0; i < this.vertices.length; i++) { + maxPosition.x = Math.max(maxPosition.x, this.vertices[i].x); + minPosition.x = Math.min(minPosition.x, this.vertices[i].x); + maxPosition.y = Math.max(maxPosition.y, this.vertices[i].y); + minPosition.y = Math.min(minPosition.y, this.vertices[i].y); + maxPosition.z = Math.max(maxPosition.z, this.vertices[i].z); + minPosition.z = Math.min(minPosition.z, this.vertices[i].z); + } + + var center = p5.Vector.lerp(maxPosition, minPosition, 0.5); + var dist = p5.Vector.sub(maxPosition, minPosition); + var longestDist = Math.max(Math.max(dist.x, dist.y), dist.z); + var scale = 200 / longestDist; + + for(i = 0; i < this.vertices.length; i++) { + this.vertices[i].sub(center); + this.vertices[i].mult(scale); + } + } + return this; +}; + +module.exports = p5.Geometry; + +},{"../core/core":54}],102:[function(_dereq_,module,exports){ +/** +* @requires constants +* @todo see methods below needing further implementation. +* future consideration: implement SIMD optimizations +* when browser compatibility becomes available +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/ +* Reference/Global_Objects/SIMD +*/ + +'use strict'; + +var p5 = _dereq_('../core/core'); +var polarGeometry = _dereq_('../math/polargeometry'); +var constants = _dereq_('../core/constants'); +var GLMAT_ARRAY_TYPE = ( + typeof Float32Array !== 'undefined') ? + Float32Array : Array; + +/** + * A class to describe a 4x4 matrix + * for model and view matrix manipulation in the p5js webgl renderer. + * class p5.Matrix + * @constructor + * @param {Array} [mat4] array literal of our 4x4 matrix + */ +p5.Matrix = function() { + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + // This is default behavior when object + // instantiated using createMatrix() + // @todo implement createMatrix() in core/math.js + if(args[0] instanceof p5) { + // save reference to p5 if passed in + this.p5 = args[0]; + if(args[1] === 'mat3'){ + this.mat3 = args[2] || new GLMAT_ARRAY_TYPE([ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + ]); + } + else { + this.mat4 = args[1] || new GLMAT_ARRAY_TYPE([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]); + } + // default behavior when object + // instantiated using new p5.Matrix() + } else { + if(args[0] === 'mat3'){ + this.mat3 = args[1] || new GLMAT_ARRAY_TYPE([ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + ]); + } + else { + this.mat4 = args[0] || new GLMAT_ARRAY_TYPE([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]); + } + } + return this; +}; + +/** + * Sets the x, y, and z component of the vector using two or three separate + * variables, the data from a p5.Matrix, or the values from a float array. + * + * @param {p5.Matrix|Float32Array|Array} [inMatrix] the input p5.Matrix or + * an Array of length 16 + * @chainable + */ +p5.Matrix.prototype.set = function (inMatrix) { + if (inMatrix instanceof p5.Matrix) { + this.mat4 = inMatrix.mat4; + return this; + } + else if (inMatrix instanceof GLMAT_ARRAY_TYPE) { + this.mat4 = inMatrix; + return this; + } + return this; +}; + +/** + * Gets a copy of the vector, returns a p5.Matrix object. + * + * @return {p5.Matrix} the copy of the p5.Matrix object + */ +p5.Matrix.prototype.get = function () { + return new p5.Matrix(this.mat4); +}; + +/** + * return a copy of a matrix + * @return {p5.Matrix} the result matrix + */ +p5.Matrix.prototype.copy = function(){ + var copied = new p5.Matrix(); + copied.mat4[0] = this.mat4[0]; + copied.mat4[1] = this.mat4[1]; + copied.mat4[2] = this.mat4[2]; + copied.mat4[3] = this.mat4[3]; + copied.mat4[4] = this.mat4[4]; + copied.mat4[5] = this.mat4[5]; + copied.mat4[6] = this.mat4[6]; + copied.mat4[7] = this.mat4[7]; + copied.mat4[8] = this.mat4[8]; + copied.mat4[9] = this.mat4[9]; + copied.mat4[10] = this.mat4[10]; + copied.mat4[11] = this.mat4[11]; + copied.mat4[12] = this.mat4[12]; + copied.mat4[13] = this.mat4[13]; + copied.mat4[14] = this.mat4[14]; + copied.mat4[15] = this.mat4[15]; + return copied; +}; + +/** + * return an identity matrix + * @return {p5.Matrix} the result matrix + */ +p5.Matrix.identity = function(){ + return new p5.Matrix(); +}; + +/** + * transpose according to a given matrix + * @param {p5.Matrix|Float32Array|Array} a the matrix to be based on to transpose + * @chainable + */ +p5.Matrix.prototype.transpose = function(a){ + var a01, a02, a03, a12, a13, a23; + if(a instanceof p5.Matrix){ + a01 = a.mat4[1]; + a02 = a.mat4[2]; + a03 = a.mat4[3]; + a12 = a.mat4[6]; + a13 = a.mat4[7]; + a23 = a.mat4[11]; + + this.mat4[0] = a.mat4[0]; + this.mat4[1] = a.mat4[4]; + this.mat4[2] = a.mat4[8]; + this.mat4[3] = a.mat4[12]; + this.mat4[4] = a01; + this.mat4[5] = a.mat4[5]; + this.mat4[6] = a.mat4[9]; + this.mat4[7] = a.mat4[13]; + this.mat4[8] = a02; + this.mat4[9] = a12; + this.mat4[10] = a.mat4[10]; + this.mat4[11] = a.mat4[14]; + this.mat4[12] = a03; + this.mat4[13] = a13; + this.mat4[14] = a23; + this.mat4[15] = a.mat4[15]; + + }else if(a instanceof GLMAT_ARRAY_TYPE){ + a01 = a[1]; + a02 = a[2]; + a03 = a[3]; + a12 = a[6]; + a13 = a[7]; + a23 = a[11]; + + this.mat4[0] = a[0]; + this.mat4[1] = a[4]; + this.mat4[2] = a[8]; + this.mat4[3] = a[12]; + this.mat4[4] = a01; + this.mat4[5] = a[5]; + this.mat4[6] = a[9]; + this.mat4[7] = a[13]; + this.mat4[8] = a02; + this.mat4[9] = a12; + this.mat4[10] = a[10]; + this.mat4[11] = a[14]; + this.mat4[12] = a03; + this.mat4[13] = a13; + this.mat4[14] = a23; + this.mat4[15] = a[15]; + } + return this; +}; + +/** + * invert matrix according to a give matrix + * @param {p5.Matrix|Float32Array|Array} a the matrix to be based on to invert + * @chainable + */ +p5.Matrix.prototype.invert = function(a){ + var a00, a01, a02, a03, a10, a11, a12, a13, + a20, a21, a22, a23, a30, a31, a32, a33; + if(a instanceof p5.Matrix){ + a00 = a.mat4[0]; + a01 = a.mat4[1]; + a02 = a.mat4[2]; + a03 = a.mat4[3]; + a10 = a.mat4[4]; + a11 = a.mat4[5]; + a12 = a.mat4[6]; + a13 = a.mat4[7]; + a20 = a.mat4[8]; + a21 = a.mat4[9]; + a22 = a.mat4[10]; + a23 = a.mat4[11]; + a30 = a.mat4[12]; + a31 = a.mat4[13]; + a32 = a.mat4[14]; + a33 = a.mat4[15]; + }else if(a instanceof GLMAT_ARRAY_TYPE){ + a00 = a[0]; + a01 = a[1]; + a02 = a[2]; + a03 = a[3]; + a10 = a[4]; + a11 = a[5]; + a12 = a[6]; + a13 = a[7]; + a20 = a[8]; + a21 = a[9]; + a22 = a[10]; + a23 = a[11]; + a30 = a[12]; + a31 = a[13]; + a32 = a[14]; + a33 = a[15]; + } + var b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - + b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + this.mat4[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + this.mat4[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + this.mat4[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + this.mat4[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + this.mat4[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + this.mat4[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + this.mat4[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + this.mat4[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + this.mat4[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + this.mat4[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + this.mat4[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + this.mat4[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + this.mat4[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + this.mat4[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + this.mat4[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + this.mat4[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return this; +}; + +/** + * Inverts a 3x3 matrix + * @chainable + */ +p5.Matrix.prototype.invert3x3 = function (){ + var a00 = this.mat3[0], + a01 = this.mat3[1], + a02 = this.mat3[2], + a10 = this.mat3[3], + a11 = this.mat3[4], + a12 = this.mat3[5], + a20 = this.mat3[6], + a21 = this.mat3[7], + a22 = this.mat3[8], + b01 = a22 * a11 - a12 * a21, + b11 = -a22 * a10 + a12 * a20, + b21 = a21 * a10 - a11 * a20, + + // Calculate the determinant + det = a00 * b01 + a01 * b11 + a02 * b21; + if (!det) { + return null; + } + det = 1.0 / det; + this.mat3[0] = b01 * det; + this.mat3[1] = (-a22 * a01 + a02 * a21) * det; + this.mat3[2] = (a12 * a01 - a02 * a11) * det; + this.mat3[3] = b11 * det; + this.mat3[4] = (a22 * a00 - a02 * a20) * det; + this.mat3[5] = (-a12 * a00 + a02 * a10) * det; + this.mat3[6] = b21 * det; + this.mat3[7] = (-a21 * a00 + a01 * a20) * det; + this.mat3[8] = (a11 * a00 - a01 * a10) * det; + return this; +}; + +/** + * transposes a 3x3 p5.Matrix by a mat3 + * @param {[Number]} mat3 1-dimensional array + * @chainable + */ +p5.Matrix.prototype.transpose3x3 = function (mat3){ + var a01 = mat3[1], a02 = mat3[2], a12 = mat3[5]; + this.mat3[1] = mat3[3]; + this.mat3[2] = mat3[6]; + this.mat3[3] = a01; + this.mat3[5] = mat3[7]; + this.mat3[6] = a02; + this.mat3[7] = a12; + return this; +}; + +/** + * converts a 4x4 matrix to its 3x3 inverse tranform + * commonly used in MVMatrix to NMatrix conversions. + * @param {p5.Matrix} mat4 the matrix to be based on to invert + * @chainable + * @todo finish implementation + */ +p5.Matrix.prototype.inverseTranspose = function (matrix){ + if(this.mat3 === undefined){ + console.error('sorry, this function only works with mat3'); + } + else { + //convert mat4 -> mat3 + this.mat3[0] = matrix.mat4[0]; + this.mat3[1] = matrix.mat4[1]; + this.mat3[2] = matrix.mat4[2]; + this.mat3[3] = matrix.mat4[4]; + this.mat3[4] = matrix.mat4[5]; + this.mat3[5] = matrix.mat4[6]; + this.mat3[6] = matrix.mat4[8]; + this.mat3[7] = matrix.mat4[9]; + this.mat3[8] = matrix.mat4[10]; + } + + this.invert3x3().transpose3x3(this.mat3); + return this; +}; + +/** + * inspired by Toji's mat4 determinant + * @return {Number} Determinant of our 4x4 matrix + */ +p5.Matrix.prototype.determinant = function(){ + var d00 = (this.mat4[0] * this.mat4[5]) - (this.mat4[1] * this.mat4[4]), + d01 = (this.mat4[0] * this.mat4[6]) - (this.mat4[2] * this.mat4[4]), + d02 = (this.mat4[0] * this.mat4[7]) - (this.mat4[3] * this.mat4[4]), + d03 = (this.mat4[1] * this.mat4[6]) - (this.mat4[2] * this.mat4[5]), + d04 = (this.mat4[1] * this.mat4[7]) - (this.mat4[3] * this.mat4[5]), + d05 = (this.mat4[2] * this.mat4[7]) - (this.mat4[3] * this.mat4[6]), + d06 = (this.mat4[8] * this.mat4[13]) - (this.mat4[9] * this.mat4[12]), + d07 = (this.mat4[8] * this.mat4[14]) - (this.mat4[10] * this.mat4[12]), + d08 = (this.mat4[8] * this.mat4[15]) - (this.mat4[11] * this.mat4[12]), + d09 = (this.mat4[9] * this.mat4[14]) - (this.mat4[10] * this.mat4[13]), + d10 = (this.mat4[9] * this.mat4[15]) - (this.mat4[11] * this.mat4[13]), + d11 = (this.mat4[10] * this.mat4[15]) - (this.mat4[11] * this.mat4[14]); + + // Calculate the determinant + return d00 * d11 - d01 * d10 + d02 * d09 + + d03 * d08 - d04 * d07 + d05 * d06; +}; + +/** + * multiply two mat4s + * @param {p5.Matrix|Float32Array|Array} multMatrix The matrix + * we want to multiply by + * @chainable + */ +p5.Matrix.prototype.mult = function(multMatrix){ + var _dest = new GLMAT_ARRAY_TYPE(16); + var _src = new GLMAT_ARRAY_TYPE(16); + + if(multMatrix instanceof p5.Matrix) { + _src = multMatrix.mat4; + } + else if(multMatrix instanceof GLMAT_ARRAY_TYPE){ + _src = multMatrix; + } + + // each row is used for the multiplier + var b0 = this.mat4[0], b1 = this.mat4[1], + b2 = this.mat4[2], b3 = this.mat4[3]; + _dest[0] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; + _dest[1] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; + _dest[2] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; + _dest[3] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; + + b0 = this.mat4[4]; + b1 = this.mat4[5]; + b2 = this.mat4[6]; + b3 = this.mat4[7]; + _dest[4] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; + _dest[5] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; + _dest[6] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; + _dest[7] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; + + b0 = this.mat4[8]; + b1 = this.mat4[9]; + b2 = this.mat4[10]; + b3 = this.mat4[11]; + _dest[8] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; + _dest[9] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; + _dest[10] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; + _dest[11] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; + + b0 = this.mat4[12]; + b1 = this.mat4[13]; + b2 = this.mat4[14]; + b3 = this.mat4[15]; + _dest[12] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12]; + _dest[13] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13]; + _dest[14] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14]; + _dest[15] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15]; + + this.mat4 = _dest; + + return this; +}; + +/** + * scales a p5.Matrix by scalars or a vector + * @param {p5.Vector|Float32Array|Array} s vector to scale by + * @chainable + */ +p5.Matrix.prototype.scale = function() { + var x,y,z; + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) { + args[i] = arguments[i]; + } + //if our 1st arg is a type p5.Vector + if (args[0] instanceof p5.Vector){ + x = args[0].x; + y = args[0].y; + z = args[0].z; + } + //otherwise if it's an array + else if (args[0] instanceof Array){ + x = args[0][0]; + y = args[0][1]; + z = args[0][2]; + } + var _dest = new GLMAT_ARRAY_TYPE(16); + _dest[0] = this.mat4[0] * x; + _dest[1] = this.mat4[1] * x; + _dest[2] = this.mat4[2] * x; + _dest[3] = this.mat4[3] * x; + _dest[4] = this.mat4[4] * y; + _dest[5] = this.mat4[5] * y; + _dest[6] = this.mat4[6] * y; + _dest[7] = this.mat4[7] * y; + _dest[8] = this.mat4[8] * z; + _dest[9] = this.mat4[9] * z; + _dest[10] = this.mat4[10] * z; + _dest[11] = this.mat4[11] * z; + _dest[12] = this.mat4[12]; + _dest[13] = this.mat4[13]; + _dest[14] = this.mat4[14]; + _dest[15] = this.mat4[15]; + + this.mat4 = _dest; + return this; +}; + +/** + * rotate our Matrix around an axis by the given angle. + * @param {Number} a The angle of rotation in radians + * @param {p5.Vector|Array} axis the axis(es) to rotate around + * @chainable + * inspired by Toji's gl-matrix lib, mat4 rotation + */ +p5.Matrix.prototype.rotate = function(a, axis){ + var x, y, z, _a, len; + + if (this.p5) { + if (this.p5._angleMode === constants.DEGREES) { + _a = polarGeometry.degreesToRadians(a); + } + } + else { + _a = a; + } + if (axis instanceof p5.Vector) { + x = axis.x; + y = axis.y; + z = axis.z; + } + else if (axis instanceof Array) { + x = axis[0]; + y = axis[1]; + z = axis[2]; + } + + len = Math.sqrt(x * x + y * y + z * z); + x *= (1/len); + y *= (1/len); + z *= (1/len); + + var a00 = this.mat4[0]; + var a01 = this.mat4[1]; + var a02 = this.mat4[2]; + var a03 = this.mat4[3]; + var a10 = this.mat4[4]; + var a11 = this.mat4[5]; + var a12 = this.mat4[6]; + var a13 = this.mat4[7]; + var a20 = this.mat4[8]; + var a21 = this.mat4[9]; + var a22 = this.mat4[10]; + var a23 = this.mat4[11]; + + //sin,cos, and tan of respective angle + var sA = Math.sin(_a); + var cA = Math.cos(_a); + var tA = 1 - cA; + // Construct the elements of the rotation matrix + var b00 = x * x * tA + cA; + var b01 = y * x * tA + z * sA; + var b02 = z * x * tA - y * sA; + var b10 = x * y * tA - z * sA; + var b11 = y * y * tA + cA; + var b12 = z * y * tA + x * sA; + var b20 = x * z * tA + y * sA; + var b21 = y * z * tA - x * sA; + var b22 = z * z * tA + cA; + + // rotation-specific matrix multiplication + this.mat4[0] = a00 * b00 + a10 * b01 + a20 * b02; + this.mat4[1] = a01 * b00 + a11 * b01 + a21 * b02; + this.mat4[2] = a02 * b00 + a12 * b01 + a22 * b02; + this.mat4[3] = a03 * b00 + a13 * b01 + a23 * b02; + this.mat4[4] = a00 * b10 + a10 * b11 + a20 * b12; + this.mat4[5] = a01 * b10 + a11 * b11 + a21 * b12; + this.mat4[6] = a02 * b10 + a12 * b11 + a22 * b12; + this.mat4[7] = a03 * b10 + a13 * b11 + a23 * b12; + this.mat4[8] = a00 * b20 + a10 * b21 + a20 * b22; + this.mat4[9] = a01 * b20 + a11 * b21 + a21 * b22; + this.mat4[10] = a02 * b20 + a12 * b21 + a22 * b22; + this.mat4[11] = a03 * b20 + a13 * b21 + a23 * b22; + + return this; +}; + +/** + * @todo finish implementing this method! + * translates + * @param {Number[]} v vector to translate by + * @chainable + */ +p5.Matrix.prototype.translate = function(v){ + var x = v[0], + y = v[1], + z = v[2] || 0; + this.mat4[12] = + this.mat4[0] * x +this.mat4[4] * y +this.mat4[8] * z +this.mat4[12]; + this.mat4[13] = + this.mat4[1] * x +this.mat4[5] * y +this.mat4[9] * z +this.mat4[13]; + this.mat4[14] = + this.mat4[2] * x +this.mat4[6] * y +this.mat4[10] * z +this.mat4[14]; + this.mat4[15] = + this.mat4[3] * x +this.mat4[7] * y +this.mat4[11] * z +this.mat4[15]; +}; + +p5.Matrix.prototype.rotateX = function(a){ + this.rotate(a, [1,0,0]); +}; +p5.Matrix.prototype.rotateY = function(a){ + this.rotate(a, [0,1,0]); +}; +p5.Matrix.prototype.rotateZ = function(a){ + this.rotate(a, [0,0,1]); +}; + +/** + * sets the perspective matrix + * @param {Number} fovy [description] + * @param {Number} aspect [description] + * @param {Number} near near clipping plane + * @param {Number} far far clipping plane + * @chainable + */ +p5.Matrix.prototype.perspective = function(fovy,aspect,near,far){ + + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + + this.mat4[0] = f / aspect; + this.mat4[1] = 0; + this.mat4[2] = 0; + this.mat4[3] = 0; + this.mat4[4] = 0; + this.mat4[5] = f; + this.mat4[6] = 0; + this.mat4[7] = 0; + this.mat4[8] = 0; + this.mat4[9] = 0; + this.mat4[10] = (far + near) * nf; + this.mat4[11] = -1; + this.mat4[12] = 0; + this.mat4[13] = 0; + this.mat4[14] = (2 * far * near) * nf; + this.mat4[15] = 0; + + return this; + +}; + +/** + * sets the ortho matrix + * @param {Number} left [description] + * @param {Number} right [description] + * @param {Number} bottom [description] + * @param {Number} top [description] + * @param {Number} near near clipping plane + * @param {Number} far far clipping plane + * @chainable + */ +p5.Matrix.prototype.ortho = function(left,right,bottom,top,near,far){ + + var lr = 1 / (left - right), + bt = 1 / (bottom - top), + nf = 1 / (near - far); + this.mat4[0] = -2 * lr; + this.mat4[1] = 0; + this.mat4[2] = 0; + this.mat4[3] = 0; + this.mat4[4] = 0; + this.mat4[5] = -2 * bt; + this.mat4[6] = 0; + this.mat4[7] = 0; + this.mat4[8] = 0; + this.mat4[9] = 0; + this.mat4[10] = 2 * nf; + this.mat4[11] = 0; + this.mat4[12] = (left + right) * lr; + this.mat4[13] = (top + bottom) * bt; + this.mat4[14] = (far + near) * nf; + this.mat4[15] = 1; + + return this; +}; + +/** + * PRIVATE + */ +// matrix methods adapted from: +// https://developer.mozilla.org/en-US/docs/Web/WebGL/ +// gluPerspective +// +// function _makePerspective(fovy, aspect, znear, zfar){ +// var ymax = znear * Math.tan(fovy * Math.PI / 360.0); +// var ymin = -ymax; +// var xmin = ymin * aspect; +// var xmax = ymax * aspect; +// return _makeFrustum(xmin, xmax, ymin, ymax, znear, zfar); +// } + +//// +//// glFrustum +//// +//function _makeFrustum(left, right, bottom, top, znear, zfar){ +// var X = 2*znear/(right-left); +// var Y = 2*znear/(top-bottom); +// var A = (right+left)/(right-left); +// var B = (top+bottom)/(top-bottom); +// var C = -(zfar+znear)/(zfar-znear); +// var D = -2*zfar*znear/(zfar-znear); +// var frustrumMatrix =[ +// X, 0, A, 0, +// 0, Y, B, 0, +// 0, 0, C, D, +// 0, 0, -1, 0 +//]; +//return frustrumMatrix; +// } + +// function _setMVPMatrices(){ +////an identity matrix +////@TODO use the p5.Matrix class to abstract away our MV matrices and +///other math +//var _mvMatrix = +//[ +// 1.0,0.0,0.0,0.0, +// 0.0,1.0,0.0,0.0, +// 0.0,0.0,1.0,0.0, +// 0.0,0.0,0.0,1.0 +//]; + +module.exports = p5.Matrix; + +},{"../core/constants":53,"../core/core":54,"../math/polargeometry":86}],103:[function(_dereq_,module,exports){ +/** + * Welcome to RendererGL Immediate Mode. + * Immediate mode is used for drawing custom shapes + * from a set of vertices. Immediate Mode is activated + * when you call beginShape() & de-activated when you call endShape(). + * Immediate mode is a style of programming borrowed + * from OpenGL's (now-deprecated) immediate mode. + * It differs from p5.js' default, Retained Mode, which caches + * geometries and buffers on the CPU to reduce the number of webgl + * draw calls. Retained mode is more efficient & performative, + * however, Immediate Mode is useful for sketching quick + * geometric ideas. + */ +'use strict'; + +var p5 = _dereq_('../core/core'); +var constants = _dereq_('../core/constants'); + +/** + * Begin shape drawing. This is a helpful way of generating + * custom shapes quickly. However in WEBGL mode, application + * performance will likely drop as a result of too many calls to + * beginShape() / endShape(). As a high performance alternative, + * please use p5.js geometry primitives. + * @param {Number} mode webgl primitives mode. beginShape supports the + * following modes: + * POINTS,LINES,LINE_STRIP,LINE_LOOP,TRIANGLES, + * TRIANGLE_STRIP,and TRIANGLE_FAN. + * @chainable + */ +p5.RendererGL.prototype.beginShape = function(mode){ + //default shape mode is line_strip + this.immediateMode.shapeMode = (mode !== undefined ) ? + mode : constants.LINE_STRIP; + //if we haven't yet initialized our + //immediateMode vertices & buffers, create them now! + if(this.immediateMode.vertexPositions === undefined){ + this.immediateMode.vertexPositions = []; + this.immediateMode.vertexColors = []; + this.immediateMode.vertexBuffer = this.GL.createBuffer(); + this.immediateMode.colorBuffer = this.GL.createBuffer(); + } else { + this.immediateMode.vertexPositions.length = 0; + this.immediateMode.vertexColors.length = 0; + } + this.isImmediateDrawing = true; + return this; +}; +/** + * adds a vertex to be drawn in a custom Shape. + * @param {Number} x x-coordinate of vertex + * @param {Number} y y-coordinate of vertex + * @param {Number} z z-coordinate of vertex + * @chainable + * @TODO implement handling of p5.Vector args + */ +p5.RendererGL.prototype.vertex = function(x, y, z){ + this.immediateMode.vertexPositions.push(x, y, z); + var vertexColor = this.curFillColor || [0.5, 0.5, 0.5, 1.0]; + this.immediateMode.vertexColors.push( + vertexColor[0], + vertexColor[1], + vertexColor[2], + vertexColor[3]); + return this; +}; + +/** + * End shape drawing and render vertices to screen. + * @chainable + */ +p5.RendererGL.prototype.endShape = +function(mode, isCurve, isBezier,isQuadratic, isContour, shapeKind){ + var gl = this.GL; + this._bindImmediateBuffers( + this.immediateMode.vertexPositions, + this.immediateMode.vertexColors); + if(mode){ + if(this.drawMode === 'fill' || this.drawMode ==='texture'){ + switch(this.immediateMode.shapeMode){ + case constants.LINE_STRIP: + this.immediateMode.shapeMode = constants.TRIANGLE_FAN; + break; + case constants.LINES: + this.immediateMode.shapeMode = constants.TRIANGLE_FAN; + break; + case constants.TRIANGLES: + this.immediateMode.shapeMode = constants.TRIANGLE_FAN; + break; + } + } else { + switch(this.immediateMode.shapeMode){ + case constants.LINE_STRIP: + this.immediateMode.shapeMode = constants.LINE_LOOP; + break; + case constants.LINES: + this.immediateMode.shapeMode = constants.LINE_LOOP; + break; + } + } + } + //QUADS & QUAD_STRIP are not supported primitives modes + //in webgl. + if(this.immediateMode.shapeMode === constants.QUADS || + this.immediateMode.shapeMode === constants.QUAD_STRIP){ + throw new Error('sorry, ' + this.immediateMode.shapeMode+ + ' not yet implemented in webgl mode.'); + } + else { + gl.enable(gl.BLEND); + gl.drawArrays(this.immediateMode.shapeMode, 0, + this.immediateMode.vertexPositions.length / 3); + } + //clear out our vertexPositions & colors arrays + //after rendering + this.immediateMode.vertexPositions.length = 0; + this.immediateMode.vertexColors.length = 0; + this.isImmediateDrawing = false; + return this; +}; +/** + * Bind immediateMode buffers to data, + * then draw gl arrays + * @param {Number[]} vertices Numbers array representing + * vertex positions + * @chainable + */ +p5.RendererGL.prototype._bindImmediateBuffers = function(vertices, colors){ + this._setDefaultCamera(); + var gl = this.GL; + var shaderKey = this._getCurShaderId(); + var shaderProgram = this.mHash[shaderKey]; + //vertex position Attribute + shaderProgram.vertexPositionAttribute = + gl.getAttribLocation(shaderProgram, 'aPosition'); + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + gl.bindBuffer(gl.ARRAY_BUFFER, this.immediateMode.vertexBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, + 3, gl.FLOAT, false, 0, 0); + + shaderProgram.vertexColorAttribute = + gl.getAttribLocation(shaderProgram, 'aVertexColor'); + gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); + gl.bindBuffer(gl.ARRAY_BUFFER, this.immediateMode.colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, + new Float32Array(colors),gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, + 4, gl.FLOAT, false, 0, 0); + //matrix + this._setMatrixUniforms(shaderKey); + //@todo implement in all shaders (not just immediateVert) + //set our default point size + // this._setUniform1f(shaderKey, + // 'uPointSize', + // this.pointSize); + return this; +}; + +////////////////////////////////////////////// +// COLOR +////////////////////////////////////////////// + +p5.RendererGL.prototype._getColorVertexShader = function(){ + var gl = this.GL; + var mId = 'immediateVert|vertexColorFrag'; + var shaderProgram; + + if(!this.materialInHash(mId)){ + shaderProgram = + this._initShaders('immediateVert', 'vertexColorFrag', true); + this.mHash[mId] = shaderProgram; + shaderProgram.vertexColorAttribute = + gl.getAttribLocation(shaderProgram, 'aVertexColor'); + gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); + }else{ + shaderProgram = this.mHash[mId]; + } + return shaderProgram; +}; + +module.exports = p5.RendererGL; +},{"../core/constants":53,"../core/core":54}],104:[function(_dereq_,module,exports){ +//Retained Mode. The default mode for rendering 3D primitives +//in WEBGL. +'use strict'; + +var p5 = _dereq_('../core/core'); +var hashCount = 0; +/** + * _initBufferDefaults + * @description initializes buffer defaults. runs each time a new geometry is + * registered + * @param {String} gId key of the geometry object + */ +p5.RendererGL.prototype._initBufferDefaults = function(gId) { + //@TODO remove this limit on hashes in gHash + hashCount ++; + if(hashCount > 1000){ + var key = Object.keys(this.gHash)[0]; + delete this.gHash[key]; + hashCount --; + } + + var gl = this.GL; + //create a new entry in our gHash + this.gHash[gId] = {}; + this.gHash[gId].vertexBuffer = gl.createBuffer(); + this.gHash[gId].normalBuffer = gl.createBuffer(); + this.gHash[gId].uvBuffer = gl.createBuffer(); + this.gHash[gId].indexBuffer = gl.createBuffer(); +}; +/** + * createBuffers description + * @param {String} gId key of the geometry object + * @param {p5.Geometry} obj contains geometry data + */ +p5.RendererGL.prototype.createBuffers = function(gId, obj) { + var gl = this.GL; + this._setDefaultCamera(); + //initialize the gl buffers for our geom groups + this._initBufferDefaults(gId); + //return the current shaderProgram from our material hash + var shaderProgram = this.mHash[this._getCurShaderId()]; + //@todo rename "numberOfItems" property to something more descriptive + //we mult the num geom faces by 3 + this.gHash[gId].numberOfItems = obj.faces.length * 3; + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array( vToNArray(obj.vertices) ), + gl.STATIC_DRAW); + //vertex position + shaderProgram.vertexPositionAttribute = + gl.getAttribLocation(shaderProgram, 'aPosition'); + gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); + + gl.vertexAttribPointer( + shaderProgram.vertexPositionAttribute, + 3, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array( vToNArray(obj.vertexNormals) ), + gl.STATIC_DRAW); + //vertex normal + shaderProgram.vertexNormalAttribute = + gl.getAttribLocation(shaderProgram, 'aNormal'); + gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute); + + gl.vertexAttribPointer( + shaderProgram.vertexNormalAttribute, + 3, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array( flatten(obj.uvs) ), + gl.STATIC_DRAW); + //texture coordinate Attribute + shaderProgram.textureCoordAttribute = + gl.getAttribLocation(shaderProgram, 'aTexCoord'); + gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); + gl.vertexAttribPointer( + shaderProgram.textureCoordAttribute, + 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer); + gl.bufferData( + gl.ELEMENT_ARRAY_BUFFER, + new Uint16Array( flatten(obj.faces) ), + gl.STATIC_DRAW); +}; + +/** + * Draws buffers given a geometry key ID + * @param {String} gId ID in our geom hash + * @chainable + */ +p5.RendererGL.prototype.drawBuffers = function(gId) { + this._setDefaultCamera(); + var gl = this.GL; + var shaderKey = this._getCurShaderId(); + var shaderProgram = this.mHash[shaderKey]; + //vertex position buffer + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer); + gl.vertexAttribPointer( + shaderProgram.vertexPositionAttribute, + 3, gl.FLOAT, false, 0, 0); + //normal buffer + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer); + gl.vertexAttribPointer( + shaderProgram.vertexNormalAttribute, + 3, gl.FLOAT, false, 0, 0); + // uv buffer + gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer); + gl.vertexAttribPointer( + shaderProgram.textureCoordAttribute, + 2, gl.FLOAT, false, 0, 0); + //vertex index buffer + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer); + this._setMatrixUniforms(shaderKey); + gl.drawElements( + gl.TRIANGLES, this.gHash[gId].numberOfItems, + gl.UNSIGNED_SHORT, 0); + return this; +}; +/////////////////////////////// +//// UTILITY FUNCTIONS +////////////////////////////// +/** + * turn a two dimensional array into one dimensional array + * @param {Array} arr 2-dimensional array + * @return {Array} 1-dimensional array + * [[1, 2, 3],[4, 5, 6]] -> [1, 2, 3, 4, 5, 6] + */ +function flatten(arr){ + if (arr.length>0){ + return arr.reduce(function(a, b){ + return a.concat(b); + }); + } else { + return []; + } +} + +/** + * turn a p5.Vector Array into a one dimensional number array + * @param {p5.Vector[]} arr an array of p5.Vector + * @return {Number[]} a one dimensional array of numbers + * [p5.Vector(1, 2, 3), p5.Vector(4, 5, 6)] -> + * [1, 2, 3, 4, 5, 6] + */ +function vToNArray(arr){ + return flatten(arr.map(function(item){ + return [item.x, item.y, item.z]; + })); +} +module.exports = p5.RendererGL; + +},{"../core/core":54}],105:[function(_dereq_,module,exports){ +'use strict'; + +var p5 = _dereq_('../core/core'); +var shader = _dereq_('./shader'); +_dereq_('../core/p5.Renderer'); +_dereq_('./p5.Matrix'); +var uMVMatrixStack = []; + +//@TODO should implement public method +//to override these attributes +var attributes = { + alpha: true, + depth: true, + stencil: true, + antialias: false, + premultipliedAlpha: false, + preserveDrawingBuffer: false +}; + +/** + * 3D graphics class + * @class p5.RendererGL + * @constructor + * @extends p5.Renderer + * @todo extend class to include public method for offscreen + * rendering (FBO). + * + */ +p5.RendererGL = function(elt, pInst, isMainCanvas) { + p5.Renderer.call(this, elt, pInst, isMainCanvas); + this._initContext(); + + this.isP3D = true; //lets us know we're in 3d mode + this.GL = this.drawingContext; + //lights + this.ambientLightCount = 0; + this.directionalLightCount = 0; + this.pointLightCount = 0; + //camera + this._curCamera = null; + + /** + * model view, projection, & normal + * matrices + */ + this.uMVMatrix = new p5.Matrix(); + this.uPMatrix = new p5.Matrix(); + this.uNMatrix = new p5.Matrix('mat3'); + //Geometry & Material hashes + this.gHash = {}; + this.mHash = {}; + //Imediate Mode + //default drawing is done in Retained Mode + this.isImmediateDrawing = false; + this.immediateMode = {}; + this.curFillColor = [0.5,0.5,0.5,1.0]; + this.curStrokeColor = [0.5,0.5,0.5,1.0]; + this.pointSize = 5.0;//default point/stroke + return this; +}; + +p5.RendererGL.prototype = Object.create(p5.Renderer.prototype); + +////////////////////////////////////////////// +// Setting +////////////////////////////////////////////// + +p5.RendererGL.prototype._initContext = function() { + try { + this.drawingContext = this.canvas.getContext('webgl', attributes) || + this.canvas.getContext('experimental-webgl', attributes); + if (this.drawingContext === null) { + throw new Error('Error creating webgl context'); + } else { + console.log('p5.RendererGL: enabled webgl context'); + var gl = this.drawingContext; + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + } + } catch (er) { + throw new Error(er); + } +}; +//detect if user didn't set the camera +//then call this function below +p5.RendererGL.prototype._setDefaultCamera = function(){ + if(this._curCamera === null){ + var _w = this.width; + var _h = this.height; + this.uPMatrix = p5.Matrix.identity(); + var cameraZ = (this.height / 2) / Math.tan(Math.PI * 30 / 180); + this.uPMatrix.perspective(60 / 180 * Math.PI, _w / _h, + cameraZ * 0.1, cameraZ * 10); + this._curCamera = 'default'; + } +}; + +p5.RendererGL.prototype._update = function() { + this.uMVMatrix = p5.Matrix.identity(); + this.translate(0, 0, -(this.height / 2) / Math.tan(Math.PI * 30 / 180)); + this.ambientLightCount = 0; + this.directionalLightCount = 0; + this.pointLightCount = 0; +}; + +/** + * [background description] + */ +p5.RendererGL.prototype.background = function() { + var gl = this.GL; + var _col = this._pInst.color.apply(this._pInst, arguments); + var _r = (_col.levels[0]) / 255; + var _g = (_col.levels[1]) / 255; + var _b = (_col.levels[2]) / 255; + var _a = (_col.levels[3]) / 255; + gl.clearColor(_r, _g, _b, _a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); +}; + +//@TODO implement this +// p5.RendererGL.prototype.clear = function() { +//@TODO +// }; + +////////////////////////////////////////////// +// SHADER +////////////////////////////////////////////// + +/** + * [_initShaders description] + * @param {string} vertId [description] + * @param {string} fragId [description] + * @return {Object} the shader program + */ +p5.RendererGL.prototype._initShaders = +function(vertId, fragId, isImmediateMode) { + var gl = this.GL; + //set up our default shaders by: + // 1. create the shader, + // 2. load the shader source, + // 3. compile the shader + var _vertShader = gl.createShader(gl.VERTEX_SHADER); + //load in our default vertex shader + gl.shaderSource(_vertShader, shader[vertId]); + gl.compileShader(_vertShader); + // if our vertex shader failed compilation? + if (!gl.getShaderParameter(_vertShader, gl.COMPILE_STATUS)) { + alert('Yikes! An error occurred compiling the shaders:' + + gl.getShaderInfoLog(_vertShader)); + return null; + } + + var _fragShader = gl.createShader(gl.FRAGMENT_SHADER); + //load in our material frag shader + gl.shaderSource(_fragShader, shader[fragId]); + gl.compileShader(_fragShader); + // if our frag shader failed compilation? + if (!gl.getShaderParameter(_fragShader, gl.COMPILE_STATUS)) { + alert('Darn! An error occurred compiling the shaders:' + + gl.getShaderInfoLog(_fragShader)); + return null; + } + + var shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, _vertShader); + gl.attachShader(shaderProgram, _fragShader); + gl.linkProgram(shaderProgram); + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + alert('Snap! Error linking shader program'); + } + //END SHADERS SETUP + + this._getLocation(shaderProgram, isImmediateMode); + + return shaderProgram; +}; + +p5.RendererGL.prototype._getLocation = +function(shaderProgram, isImmediateMode) { + var gl = this.GL; + gl.useProgram(shaderProgram); + + //projection Matrix uniform + shaderProgram.uPMatrixUniform = + gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'); + //model view Matrix uniform + shaderProgram.uMVMatrixUniform = + gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'); + + //@TODO: figure out a better way instead of if statement + if(isImmediateMode === undefined){ + //normal Matrix uniform + shaderProgram.uNMatrixUniform = + gl.getUniformLocation(shaderProgram, 'uNormalMatrix'); + + shaderProgram.samplerUniform = + gl.getUniformLocation(shaderProgram, 'uSampler'); + } +}; + +/** + * Sets a shader uniform given a shaderProgram and uniform string + * @param {String} shaderKey key to material Hash. + * @param {String} uniform location in shader. + * @param {Number} data data to bind uniform. Float data type. + * @chainable + * @todo currently this function sets uniform1f data. + * Should generalize function to accept any uniform + * data type. + */ +p5.RendererGL.prototype._setUniform1f = function(shaderKey,uniform,data) +{ + var gl = this.GL; + var shaderProgram = this.mHash[shaderKey]; + gl.useProgram(shaderProgram); + shaderProgram[uniform] = gl.getUniformLocation(shaderProgram, uniform); + gl.uniform1f(shaderProgram[uniform], data); + return this; +}; + +p5.RendererGL.prototype._setMatrixUniforms = function(shaderKey) { + var gl = this.GL; + var shaderProgram = this.mHash[shaderKey]; + + gl.useProgram(shaderProgram); + + gl.uniformMatrix4fv( + shaderProgram.uPMatrixUniform, + false, this.uPMatrix.mat4); + + gl.uniformMatrix4fv( + shaderProgram.uMVMatrixUniform, + false, this.uMVMatrix.mat4); + + this.uNMatrix.inverseTranspose(this.uMVMatrix); + + gl.uniformMatrix3fv( + shaderProgram.uNMatrixUniform, + false, this.uNMatrix.mat3); +}; +////////////////////////////////////////////// +// GET CURRENT | for shader and color +////////////////////////////////////////////// +p5.RendererGL.prototype._getShader = function(vertId, fragId, isImmediateMode) { + var mId = vertId + '|' + fragId; + //create it and put it into hashTable + if(!this.materialInHash(mId)){ + var shaderProgram = this._initShaders(vertId, fragId, isImmediateMode); + this.mHash[mId] = shaderProgram; + } + this.curShaderId = mId; + + return this.mHash[this.curShaderId]; +}; + +p5.RendererGL.prototype._getCurShaderId = function(){ + //if the shader ID is not yet defined + if(this.drawMode !== 'fill' && this.curShaderId === undefined){ + //default shader: normalMaterial() + var mId = 'normalVert|normalFrag'; + var shaderProgram = this._initShaders('normalVert', 'normalFrag'); + this.mHash[mId] = shaderProgram; + this.curShaderId = mId; + } else if(this.isImmediateDrawing && this.drawMode === 'fill'){ + // note that this._getShader will check if the shader already exists + // by looking up the shader id (composed of vertexShaderId|fragmentShaderId) + // in the material hash. If the material isn't found in the hash, it + // creates a new one using this._initShaders--however, we'd like + // use the cached version as often as possible, so we defer to this._getShader + // here instead of calling this._initShaders directly. + this._getShader('immediateVert', 'vertexColorFrag', true); + } + + return this.curShaderId; +}; + +////////////////////////////////////////////// +// COLOR +////////////////////////////////////////////// +/** + * Basic fill material for geometry with a given color + * @method fill + * @param {Number|Array|String|p5.Color} v1 gray value, + * red or hue value (depending on the current color mode), + * or color Array, or CSS color string + * @param {Number} [v2] optional: green or saturation value + * @param {Number} [v3] optional: blue or brightness value + * @param {Number} [a] optional: opacity + * @chainable + * @example + *
+ * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(0); + * fill(250, 0, 0); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * rotateZ(frameCount * 0.01); + * box(200, 200, 200); + * } + * + *
+ * + * @alt + * red canvas + * + */ +p5.RendererGL.prototype.fill = function(v1, v2, v3, a) { + var gl = this.GL; + var shaderProgram; + //see material.js for more info on color blending in webgl + var colors = this._applyColorBlend.apply(this, arguments); + this.curFillColor = colors; + this.drawMode = 'fill'; + if(this.isImmediateDrawing){ + shaderProgram = + this._getShader('immediateVert','vertexColorFrag'); + gl.useProgram(shaderProgram); + } else { + shaderProgram = + this._getShader('normalVert', 'basicFrag'); + gl.useProgram(shaderProgram); + //RetainedMode uses a webgl uniform to pass color vals + //in ImmediateMode, we want access to each vertex so therefore + //we cannot use a uniform. + shaderProgram.uMaterialColor = gl.getUniformLocation( + shaderProgram, 'uMaterialColor' ); + gl.uniform4f( shaderProgram.uMaterialColor, + colors[0], + colors[1], + colors[2], + colors[3]); + } + return this; +}; +p5.RendererGL.prototype.stroke = function(r, g, b, a) { + var color = this._pInst.color.apply(this._pInst, arguments); + var colorNormalized = color._array; + this.curStrokeColor = colorNormalized; + this.drawMode = 'stroke'; + return this; +}; + +//@TODO +p5.RendererGL.prototype._strokeCheck = function(){ + if(this.drawMode === 'stroke'){ + throw new Error( + 'stroke for shapes in 3D not yet implemented, use fill for now :(' + ); + } +}; + +/** + * [strokeWeight description] + * @param {Number} pointSize stroke point size + * @chainable + * @todo strokeWeight currently works on points only. + * implement on all wireframes and strokes. + */ +p5.RendererGL.prototype.strokeWeight = function(pointSize) { + this.pointSize = pointSize; + return this; +}; +////////////////////////////////////////////// +// HASH | for material and geometry +////////////////////////////////////////////// + +p5.RendererGL.prototype.geometryInHash = function(gId){ + return this.gHash[gId] !== undefined; +}; + +p5.RendererGL.prototype.materialInHash = function(mId){ + return this.mHash[mId] !== undefined; +}; + +/** + * [resize description] + * @param {Number} w [description] + * @param {Number} h [description] + */ +p5.RendererGL.prototype.resize = function(w,h) { + var gl = this.GL; + p5.Renderer.prototype.resize.call(this, w, h); + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + // If we're using the default camera, update the aspect ratio + if(this._curCamera === 'default') { + this._curCamera = null; + this._setDefaultCamera(); + } +}; + +/** + * clears color and depth buffers + * with r,g,b,a + * @param {Number} r normalized red val. + * @param {Number} g normalized green val. + * @param {Number} b normalized blue val. + * @param {Number} a normalized alpha val. + */ +p5.RendererGL.prototype.clear = function() { + var gl = this.GL; + gl.clearColor(arguments[0], + arguments[1], + arguments[2], + arguments[3]); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); +}; + +/** + * [translate description] + * @param {Number} x [description] + * @param {Number} y [description] + * @param {Number} z [description] + * @chainable + * @todo implement handle for components or vector as args + */ +p5.RendererGL.prototype.translate = function(x, y, z) { + this.uMVMatrix.translate([x,-y,z]); + return this; +}; + +/** + * Scales the Model View Matrix by a vector + * @param {Number | p5.Vector | Array} x [description] + * @param {Number} [y] y-axis scalar + * @param {Number} [z] z-axis scalar + * @chainable + */ +p5.RendererGL.prototype.scale = function(x,y,z) { + this.uMVMatrix.scale([x,y,z]); + return this; +}; + +p5.RendererGL.prototype.rotate = function(rad, axis){ + this.uMVMatrix.rotate(rad, axis); + return this; +}; + +p5.RendererGL.prototype.rotateX = function(rad) { + this.rotate(rad, [1,0,0]); + return this; +}; + +p5.RendererGL.prototype.rotateY = function(rad) { + this.rotate(rad, [0,1,0]); + return this; +}; + +p5.RendererGL.prototype.rotateZ = function(rad) { + this.rotate(rad, [0,0,1]); + return this; +}; + +/** + * pushes a copy of the model view matrix onto the + * MV Matrix stack. + */ +p5.RendererGL.prototype.push = function() { + uMVMatrixStack.push(this.uMVMatrix.copy()); +}; + +/** + * [pop description] + */ +p5.RendererGL.prototype.pop = function() { + if (uMVMatrixStack.length === 0) { + throw new Error('Invalid popMatrix!'); + } + this.uMVMatrix = uMVMatrixStack.pop(); +}; + +p5.RendererGL.prototype.resetMatrix = function() { + this.uMVMatrix = p5.Matrix.identity(); + this.translate(0, 0, -800); + return this; +}; + +// Text/Typography +// @TODO: +p5.RendererGL.prototype._applyTextProperties = function() { + //@TODO finish implementation + console.error('text commands not yet implemented in webgl'); +}; +module.exports = p5.RendererGL; + +},{"../core/core":54,"../core/p5.Renderer":61,"./p5.Matrix":102,"./shader":107}],106:[function(_dereq_,module,exports){ +/** + * @module Shape + * @submodule 3D Primitives + * @for p5 + * @requires core + * @requires p5.Geometry + */ + +'use strict'; + +var p5 = _dereq_('../core/core'); +_dereq_('./p5.Geometry'); + +/** + * Draw a line given two points + * @param {Number} x0 x-coordinate of first vertex + * @param {Number} y0 y-coordinate of first vertex + * @param {Number} z0 z-coordinate of first vertex + * @param {Number} x1 x-coordinate of second vertex + * @param {Number} y1 y-coordinate of second vertex + * @param {Number} z1 z-coordinate of second vertex + * @chainable + * @example + *
+ * + * //draw a line + * function setup(){ + * createCanvas(100, 100, WEBGL); + * } + * + * function draw(){ + * background(200); + * rotateX(frameCount * 0.01); + * rotateY(frameCount * 0.01); + * // Use fill instead of stroke to change the color of shape. + * fill(255, 0, 0); + * line(10, 10, 0, 60, 60, 20); + * } + * + *
+ */ +p5.RendererGL.prototype.line = function(x0,y0,z0,x1,y1,z1) { + if (typeof x0 !== 'undefined' || + typeof y0 !== 'undefined' || + typeof z0 !== 'undefined' || + typeof x1 !== 'undefined' || + typeof y1 !== 'undefined' || + typeof z1 !== 'undefined') + { + this.beginShape(); + this.vertex(x0, y0, z0); + this.vertex(x1, y1, z1); + this.endShape(); + } + return this; +}; + +module.exports = p5; + +},{"../core/core":54,"./p5.Geometry":101}],107:[function(_dereq_,module,exports){ + + +module.exports = { + immediateVert: + "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\nuniform float uPointSize;\n\nvarying vec4 vColor;\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n gl_PointSize = uPointSize;\n}\n", + vertexColorVert: + "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n}\n", + vertexColorFrag: + "precision mediump float;\nvarying vec4 vColor;\nvoid main(void) {\n gl_FragColor = vColor;\n}", + normalVert: + "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\n\nvarying vec3 vVertexNormal;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vVertexNormal = vec3( uNormalMatrix * aNormal );\n vVertTexCoord = aTexCoord;\n}\n", + normalFrag: + "precision mediump float;\nvarying vec3 vVertexNormal;\nvoid main(void) {\n gl_FragColor = vec4(vVertexNormal, 1.0);\n}", + basicFrag: + "precision mediump float;\nvarying vec3 vVertexNormal;\nuniform vec4 uMaterialColor;\nvoid main(void) {\n gl_FragColor = uMaterialColor;\n}", + lightVert: + "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat3 uNormalMatrix;\nuniform int uAmbientLightCount;\nuniform int uDirectionalLightCount;\nuniform int uPointLightCount;\n\nuniform vec3 uAmbientColor[8];\nuniform vec3 uLightingDirection[8];\nuniform vec3 uDirectionalColor[8];\nuniform vec3 uPointLightLocation[8];\nuniform vec3 uPointLightColor[8];\nuniform bool uSpecular;\n\nvarying vec3 vVertexNormal;\nvarying vec2 vVertTexCoord;\nvarying vec3 vLightWeighting;\n\nvec3 ambientLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 directionalLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor2 = vec3(0.0, 0.0, 0.0);\n\nvoid main(void){\n\n vec4 positionVec4 = vec4(aPosition, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n\n vec3 vertexNormal = vec3( uNormalMatrix * aNormal );\n vVertexNormal = vertexNormal;\n vVertTexCoord = aTexCoord;\n\n vec4 mvPosition = uModelViewMatrix * vec4(aPosition, 1.0);\n vec3 eyeDirection = normalize(-mvPosition.xyz);\n\n float shininess = 32.0;\n float specularFactor = 2.0;\n float diffuseFactor = 0.3;\n\n for(int i = 0; i < 8; i++){\n if(uAmbientLightCount == i) break;\n ambientLightFactor += uAmbientColor[i];\n }\n\n for(int j = 0; j < 8; j++){\n if(uDirectionalLightCount == j) break;\n vec3 dir = uLightingDirection[j];\n float directionalLightWeighting = max(dot(vertexNormal, dir), 0.0);\n directionalLightFactor += uDirectionalColor[j] * directionalLightWeighting;\n }\n\n for(int k = 0; k < 8; k++){\n if(uPointLightCount == k) break;\n vec3 loc = uPointLightLocation[k];\n vec3 lightDirection = normalize(loc - mvPosition.xyz);\n\n float directionalLightWeighting = max(dot(vertexNormal, lightDirection), 0.0);\n pointLightFactor += uPointLightColor[k] * directionalLightWeighting;\n\n //factor2 for specular\n vec3 reflectionDirection = reflect(-lightDirection, vertexNormal);\n float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess);\n\n pointLightFactor2 += uPointLightColor[k] * (specularFactor * specularLightWeighting\n + directionalLightWeighting * diffuseFactor);\n }\n\n if(!uSpecular){\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor;\n }else{\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor2;\n }\n\n}\n", + lightTextureFrag: + "precision mediump float;\n\nuniform vec4 uMaterialColor;\nuniform sampler2D uSampler;\nuniform bool isTexture;\n\nvarying vec3 vLightWeighting;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n if(!isTexture){\n gl_FragColor = vec4(vec3(uMaterialColor.rgb * vLightWeighting), uMaterialColor.a);\n }else{\n vec4 textureColor = texture2D(uSampler, vVertTexCoord);\n if(vLightWeighting == vec3(0., 0., 0.)){\n gl_FragColor = textureColor;\n }else{\n gl_FragColor = vec4(vec3(textureColor.rgb * vLightWeighting), textureColor.a);\n }\n }\n}" +}; +},{}]},{},[45])(45) +}); \ No newline at end of file diff --git a/html/scan.js b/html/scan.js index ed668f1..3ae08e8 100644 --- a/html/scan.js +++ b/html/scan.js @@ -9,7 +9,6 @@ function get_mode() { var json_url = 'mode.json'; // putting this on the HTML causes rendering issues! if (_('rtctab')) _('rtctab').style.display = 'none'; - if (_('httab')) _('httab').style.display = 'none'; xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { @@ -18,7 +17,6 @@ function get_mode() { if (data.mode==="STA") { _('stamode').style.display = 'block'; if (_('rtctab')) _('rtctab').removeAttribute('style'); - if (_('httab')) _('httab').removeAttribute('style'); _('ssid').textContent = data.ssid; } else { _('apmode').style.display = 'block'; @@ -501,7 +499,7 @@ const loadScript = (FILE_URL, async = true, type = "text/javascript") => { let ht_loaded = false; function start() { if (!ht_loaded) { - loadScript('https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.12/p5.min.js').then(()=>{ + loadScript('p5.js').then(()=>{ ht_loaded = true; websock = new WebSocket('ws://' + window.location.hostname + '/ws'); // websock.onopen = function(evt) { diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index fa5bd7f..270eb1f 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -143,6 +143,7 @@ static struct { #if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) {"/airplane.obj", "text/plain", (uint8_t *)PLANE_OBJ, sizeof(PLANE_OBJ)}, {"/texture.gif", "image/gif", (uint8_t *)TEXTURE_GIF, sizeof(TEXTURE_GIF)}, + {"/p5.js", "text/javascript", (uint8_t *)P5_JS, sizeof(P5_JS)}, #endif }; @@ -586,11 +587,6 @@ static void startServices() } server.on("/", WebUpdateHandleRoot); - server.on("/mui.css", WebUpdateSendContent); - server.on("/elrs.css", WebUpdateSendContent); - server.on("/mui.js", WebUpdateSendContent); - server.on("/scan.js", WebUpdateSendContent); - server.on("/logo.svg", WebUpdateSendContent); server.on("/mode.json", WebUpdateSendMode); server.on("/networks.json", WebUpdateSendNetworks); server.on("/sethome", WebUpdateSetHome); @@ -598,7 +594,7 @@ static void startServices() server.on("/connect", WebUpdateConnect); server.on("/access", WebUpdateAccessPoint); - server.on("/generate_204", WebUpdateHandleRoot); // handle Andriod phones doing shit to detect if there is 'real' internet and possibly dropping conn. + server.on("/generate_204", WebUpdateHandleRoot); // handle Android phones doing shit to detect if there is 'real' internet and possibly dropping conn. server.on("/gen_204", WebUpdateHandleRoot); server.on("/library/test/success.html", WebUpdateHandleRoot); server.on("/hotspot-detect.html", WebUpdateHandleRoot); @@ -611,15 +607,16 @@ static void startServices() server.on("/forceupdate", WebUploadForceUpdateHandler); server.on("/setrtc", WebUploadRTCUpdateHandler); - server.on("/log.js", WebUpdateSendContent); - server.on("/log.html", WebUpdateSendContent); server.addHandler(&logging); server.onNotFound(WebUpdateHandleNotFound); + for (int i=0 ; i Date: Sun, 18 Feb 2024 20:44:43 +1300 Subject: [PATCH 06/38] Add code to send PTR state to TX backpack --- lib/WIFI/devWIFI.cpp | 2 ++ src/module_base.cpp | 32 +++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index 270eb1f..d4dfbe7 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -147,6 +147,7 @@ static struct { #endif }; +#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { if (type == WS_EVT_CONNECT) { @@ -183,6 +184,7 @@ static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, Aw } } } +#endif static void WebUpdateSendContent(AsyncWebServerRequest *request) { diff --git a/src/module_base.cpp b/src/module_base.cpp index 89bf42e..dadccc9 100644 --- a/src/module_base.cpp +++ b/src/module_base.cpp @@ -6,6 +6,10 @@ #include "msptypes.h" #include "logging.h" +#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) +#include "devHeadTracker.h" +#endif + void RebootIntoWifi(); bool BindingExpired(uint32_t now); extern uint8_t backpackVersion[]; @@ -55,6 +59,32 @@ ModuleBase::SendBatteryTelemetry(uint8_t *rawCrsfPacket) void ModuleBase::Loop(uint32_t now) { +#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) + static uint32_t lastSend = 0; + if (headTrackingEnabled && now - lastSend > 20) + { + float fyaw, fpitch, froll; + getEuler(&fyaw, &fpitch, &froll); + + // convert from degrees to servo positions + int yaw = fyaw + 90 * 1000 / 180; + int pitch = fpitch + 90 * 1000 / 180; + int roll = froll + 90 * 1000 / 180; + + mspPacket_t packet; + packet.reset(); + packet.makeCommand(); + packet.function = MSP_ELRS_BACKPACK_SET_PTR; + packet.addByte(yaw & 0xFF); + packet.addByte(yaw >> 8); + packet.addByte(pitch & 0xFF); + packet.addByte(pitch >> 8); + packet.addByte(roll & 0xFF); + packet.addByte(roll >> 8); + sendMSPViaEspnow(&packet); + lastSend = now; + } +#endif } void @@ -120,7 +150,7 @@ MSPModuleBase::Loop(uint32_t now) } else if (packet->function == MSP_ELRS_BACKPACK_SET_PTR && headTrackingEnabled) { - sendMSPViaEspnow(packet); + sendMSPViaEspnow(packet); } msp.markPacketReceived(); } From c4f694a5bb45c1efb04e66df586a48dbca91070b Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sun, 18 Feb 2024 20:51:56 +1300 Subject: [PATCH 07/38] Remove calibrate function from compass library --- lib/QMC5883L/QMC5883LCompass.cpp | 49 -------------------------------- lib/QMC5883L/QMC5883LCompass.h | 1 - 2 files changed, 50 deletions(-) diff --git a/lib/QMC5883L/QMC5883LCompass.cpp b/lib/QMC5883L/QMC5883LCompass.cpp index 357dc5d..51fdb98 100644 --- a/lib/QMC5883L/QMC5883LCompass.cpp +++ b/lib/QMC5883L/QMC5883LCompass.cpp @@ -147,55 +147,6 @@ void QMC5883LCompass::setSmoothing(byte steps, bool adv){ _smoothAdvanced = (adv == true) ? true : false; } -void QMC5883LCompass::calibrate() { - clearCalibration(); - long calibrationData[3][2] = {{65000, -65000}, {65000, -65000}, {65000, -65000}}; - long x = calibrationData[0][0] = calibrationData[0][1] = getX(); - long y = calibrationData[1][0] = calibrationData[1][1] = getY(); - long z = calibrationData[2][0] = calibrationData[2][1] = getZ(); - - unsigned long startTime = millis(); - - while((millis() - startTime) < 10000) { - read(); - - x = getX(); - y = getY(); - z = getZ(); - - if(x < calibrationData[0][0]) { - calibrationData[0][0] = x; - } - if(x > calibrationData[0][1]) { - calibrationData[0][1] = x; - } - - if(y < calibrationData[1][0]) { - calibrationData[1][0] = y; - } - if(y > calibrationData[1][1]) { - calibrationData[1][1] = y; - } - - if(z < calibrationData[2][0]) { - calibrationData[2][0] = z; - } - if(z > calibrationData[2][1]) { - calibrationData[2][1] = z; - } - ESP.wdtFeed(); - } - - setCalibration( - calibrationData[0][0], - calibrationData[0][1], - calibrationData[1][0], - calibrationData[1][1], - calibrationData[2][0], - calibrationData[2][1] - ); -} - /** SET CALIBRATION Set calibration values for more accurate readings diff --git a/lib/QMC5883L/QMC5883LCompass.h b/lib/QMC5883L/QMC5883LCompass.h index 9345a1a..2ca4235 100644 --- a/lib/QMC5883L/QMC5883LCompass.h +++ b/lib/QMC5883L/QMC5883LCompass.h @@ -13,7 +13,6 @@ class QMC5883LCompass { void setMode(byte mode, byte odr, byte rng, byte osr); void setMagneticDeclination(int degrees, uint8_t minutes); void setSmoothing(byte steps, bool adv); - void calibrate(); void setCalibration(int x_min, int x_max, int y_min, int y_max, int z_min, int z_max); void setCalibrationOffsets(float x_offset, float y_offset, float z_offset); From 0c84c1e7f951e7daee751504328dc64173c15c96 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Mon, 19 Feb 2024 07:48:02 +1300 Subject: [PATCH 08/38] Fix the scaling of the PTR values --- lib/CrsfProtocol/crsf_protocol.h | 6 +++++- src/module_base.cpp | 16 +++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/CrsfProtocol/crsf_protocol.h b/lib/CrsfProtocol/crsf_protocol.h index 4e44160..a78b575 100644 --- a/lib/CrsfProtocol/crsf_protocol.h +++ b/lib/CrsfProtocol/crsf_protocol.h @@ -3,4 +3,8 @@ #define CRSF_CRC_POLY 0xd5 #define CRSF_FRAMETYPE_LINK_STATISTICS 0x14 -#define CRSF_FRAMETYPE_BATTERY_SENSOR 0x08 \ No newline at end of file +#define CRSF_FRAMETYPE_BATTERY_SENSOR 0x08 + +#define CRSF_CHANNEL_VALUE_1000 191 +#define CRSF_CHANNEL_VALUE_MID 992 +#define CRSF_CHANNEL_VALUE_2000 1792 diff --git a/src/module_base.cpp b/src/module_base.cpp index dadccc9..e14afa2 100644 --- a/src/module_base.cpp +++ b/src/module_base.cpp @@ -8,6 +8,7 @@ #if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) #include "devHeadTracker.h" +#include "crsf_protocol.h" #endif void RebootIntoWifi(); @@ -67,18 +68,19 @@ ModuleBase::Loop(uint32_t now) getEuler(&fyaw, &fpitch, &froll); // convert from degrees to servo positions - int yaw = fyaw + 90 * 1000 / 180; - int pitch = fpitch + 90 * 1000 / 180; - int roll = froll + 90 * 1000 / 180; + + int pan = map((fyaw + 90)*1000, 0, 180*1000, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); + int tilt = map((fpitch + 90)*1000, 0, 180*1000, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); + int roll = map((froll + 90)*1000, 0, 180*1000, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); mspPacket_t packet; packet.reset(); packet.makeCommand(); packet.function = MSP_ELRS_BACKPACK_SET_PTR; - packet.addByte(yaw & 0xFF); - packet.addByte(yaw >> 8); - packet.addByte(pitch & 0xFF); - packet.addByte(pitch >> 8); + packet.addByte(pan & 0xFF); + packet.addByte(pan >> 8); + packet.addByte(tilt & 0xFF); + packet.addByte(tilt >> 8); packet.addByte(roll & 0xFF); packet.addByte(roll >> 8); sendMSPViaEspnow(&packet); From 8f63d59624a26506a64943b870a832f63a33bc4b Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Mon, 19 Feb 2024 08:09:50 +1300 Subject: [PATCH 09/38] introduce a feature header reset centre on enable --- lib/HeadTracker/devHeadTracker.cpp | 2 +- lib/WIFI/devWIFI.cpp | 12 ++++++------ src/Vrx_main.cpp | 6 +++++- src/module_base.cpp | 4 ++-- targets/rapidfire.ini | 1 + 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index fcb4a11..5fc9732 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -1,7 +1,7 @@ #include #include "common.h" -#if defined(PIN_SCL) +#if defined(HAS_HEADTRACKING) #include #include "devHeadTracker.h" diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index d4dfbe7..d59a349 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -31,7 +31,7 @@ #include "config.h" #if defined(TARGET_VRX_BACKPACK) -#if defined(PIN_SCL) +#if defined(HAS_HEADTRACKING) #include "devHeadTracker.h" #endif extern VrxBackpackConfig config; @@ -70,7 +70,7 @@ static IPAddress netMsk(255, 255, 255, 0); static DNSServer dnsServer; static AsyncWebServer server(80); -#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) +#if defined(HAS_HEADTRACKING) static AsyncWebSocket ws("/ws"); #endif static bool servicesStarted = false; @@ -140,14 +140,14 @@ static struct { {"/logo.svg", "image/svg+xml", (uint8_t *)LOGO_SVG, sizeof(LOGO_SVG)}, {"/log.js", "text/javascript", (uint8_t *)LOG_JS, sizeof(LOG_JS)}, {"/log.html", "text/html", (uint8_t *)LOG_HTML, sizeof(LOG_HTML)}, -#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) +#if defined(HAS_HEADTRACKING) {"/airplane.obj", "text/plain", (uint8_t *)PLANE_OBJ, sizeof(PLANE_OBJ)}, {"/texture.gif", "image/gif", (uint8_t *)TEXTURE_GIF, sizeof(TEXTURE_GIF)}, {"/p5.js", "text/javascript", (uint8_t *)P5_JS, sizeof(P5_JS)}, #endif }; -#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) +#if defined(HAS_HEADTRACKING) static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { if (type == WS_EVT_CONNECT) { @@ -618,7 +618,7 @@ static void startServices() server.on(files[i].url, WebUpdateSendContent); } - #if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) + #if defined(HAS_HEADTRACKING) ws.onEvent(onWsEvent); server.addHandler(&ws); #endif @@ -724,7 +724,7 @@ static void HandleWebUpdate() rebootTime = millis() + 200; } -#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) +#if defined(HAS_HEADTRACKING) static long lastCall = 0; static HeadTrackerState last_state = STATE_ERROR; if (now - lastCall > 10) { diff --git a/src/Vrx_main.cpp b/src/Vrx_main.cpp index 21344be..32a0602 100644 --- a/src/Vrx_main.cpp +++ b/src/Vrx_main.cpp @@ -79,7 +79,7 @@ device_t *ui_devices[] = { &Button_device, #endif &WIFI_device, -#ifdef PIN_SCL +#ifdef HAS_HEADTRACKING &HeadTracker_device #endif }; @@ -231,7 +231,11 @@ void ProcessMSPPacket(mspPacket_t *packet) case MSP_ELRS_BACKPACK_SET_HEAD_TRACKING: DBGLN("Processing MSP_ELRS_BACKPACK_SET_HEAD_TRACKING..."); headTrackingEnabled = packet->readByte(); + #if defined(HAS_HEADTRACKING) + resetCenter(); + #else sendHeadTrackingChangesToVrx = true; + #endif break; case MSP_ELRS_BACKPACK_CRSF_TLM: DBGV("Processing MSP_ELRS_BACKPACK_CRSF_TLM type %x\n", packet->payload[1]); diff --git a/src/module_base.cpp b/src/module_base.cpp index e14afa2..bf39706 100644 --- a/src/module_base.cpp +++ b/src/module_base.cpp @@ -6,7 +6,7 @@ #include "msptypes.h" #include "logging.h" -#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) +#if defined(HAS_HEADTRACKING) #include "devHeadTracker.h" #include "crsf_protocol.h" #endif @@ -60,7 +60,7 @@ ModuleBase::SendBatteryTelemetry(uint8_t *rawCrsfPacket) void ModuleBase::Loop(uint32_t now) { -#if defined(TARGET_VRX_BACKPACK) && defined(PIN_SCL) +#if defined(HAS_HEADTRACKING) static uint32_t lastSend = 0; if (headTrackingEnabled && now - lastSend > 20) { diff --git a/targets/rapidfire.ini b/targets/rapidfire.ini index c1c7792..692409d 100644 --- a/targets/rapidfire.ini +++ b/targets/rapidfire.ini @@ -57,6 +57,7 @@ extends = env:Rapidfire_ESP12F_Backpack_via_UART extends = env:Rapidfire_ESP_RX_Backpack_via_UART build_flags = ${env:Rapidfire_ESP01F_Backpack_via_UART.build_flags} + -D HAS_HEADTRACKING -D PIN_SDA=2 -D PIN_SCL=4 From bbf9ac4b752e09b25570e2f4a183a558a810614a Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Mon, 19 Feb 2024 08:33:01 +1300 Subject: [PATCH 10/38] Normalise the directions so they make sense (at least to me!) --- html/scan.js | 4 ++-- html/vrx_index.html | 12 ++++++------ lib/HeadTracker/devHeadTracker.cpp | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/html/scan.js b/html/scan.js index 3ae08e8..6a53a4a 100644 --- a/html/scan.js +++ b/html/scan.js @@ -559,8 +559,8 @@ function setup() { function draw() { background(192); - rotateY(radians(Euler.heading+180)); - rotateX(radians(Euler.pitch)); + rotateY(radians(-Euler.heading+180)); + rotateX(radians(-Euler.pitch)); rotateZ(radians(Euler.roll)); push(); diff --git a/html/vrx_index.html b/html/vrx_index.html index 8207d8c..0af2d68 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -126,20 +126,20 @@

Head Tracking

diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index 5fc9732..33127be 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -291,8 +291,8 @@ void resetCenter() void getEuler(float *yaw, float *pitch, float *roll) { - *yaw = euler.angle.yaw; - *pitch = -euler.angle.pitch; + *yaw = -euler.angle.yaw; + *pitch = euler.angle.pitch; *roll = -euler.angle.roll; } From 9b95bec72ea818544c54cc9039c55a16f6f08467 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Mon, 19 Feb 2024 10:59:35 +1300 Subject: [PATCH 11/38] Fix error being printed in P5 --- html/p5.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/html/p5.js b/html/p5.js index 97aac34..f31f777 100644 --- a/html/p5.js +++ b/html/p5.js @@ -17772,12 +17772,14 @@ p5.RendererGL.prototype._bindImmediateBuffers = function(vertices, colors){ shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, 'aVertexColor'); - gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); - gl.bindBuffer(gl.ARRAY_BUFFER, this.immediateMode.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, - new Float32Array(colors),gl.DYNAMIC_DRAW); - gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, - 4, gl.FLOAT, false, 0, 0); + if (shaderProgram.vertexColorAttribute != -1) { + gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); + gl.bindBuffer(gl.ARRAY_BUFFER, this.immediateMode.colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, + new Float32Array(colors),gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, + 4, gl.FLOAT, false, 0, 0); + } //matrix this._setMatrixUniforms(shaderKey); //@todo implement in all shaders (not just immediateVert) From 7e30e3672f6afa19ad343378315ac131f891c34c Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Fri, 31 May 2024 08:36:27 +1200 Subject: [PATCH 12/38] Add bottle and serve_html.py for testing --- python/external/bottle.py | 4429 +++++++++++++++++ python/external/wheezy/__init__.py | 10 + python/external/wheezy/template/__init__.py | 20 + python/external/wheezy/template/builder.py | 120 + python/external/wheezy/template/comp.py | 9 + python/external/wheezy/template/compiler.py | 30 + python/external/wheezy/template/console.py | 126 + python/external/wheezy/template/engine.py | 221 + .../external/wheezy/template/ext/__init__.py | 0 python/external/wheezy/template/ext/code.py | 60 + python/external/wheezy/template/ext/core.py | 517 ++ .../wheezy/template/ext/determined.py | 163 + .../wheezy/template/ext/tests/__init__.py | 0 .../wheezy/template/ext/tests/test_code.py | 151 + .../wheezy/template/ext/tests/test_core.py | 883 ++++ .../template/ext/tests/test_determined.py | 43 + python/external/wheezy/template/lexer.py | 80 + python/external/wheezy/template/loader.py | 208 + python/external/wheezy/template/parser.py | 82 + .../external/wheezy/template/preprocessor.py | 107 + python/external/wheezy/template/py.typed | 0 .../wheezy/template/tests/__init__.py | 0 .../wheezy/template/tests/test_builder.py | 75 + .../wheezy/template/tests/test_console.py | 30 + .../wheezy/template/tests/test_engine.py | 72 + .../wheezy/template/tests/test_lexer.py | 53 + .../wheezy/template/tests/test_loader.py | 201 + .../wheezy/template/tests/test_parser.py | 71 + .../template/tests/test_preprocessor.py | 80 + .../wheezy/template/tests/test_utils.py | 51 + python/external/wheezy/template/typing.py | 85 + python/external/wheezy/template/utils.py | 43 + python/serve_html.py | 156 + 33 files changed, 8176 insertions(+) create mode 100644 python/external/bottle.py create mode 100644 python/external/wheezy/__init__.py create mode 100644 python/external/wheezy/template/__init__.py create mode 100644 python/external/wheezy/template/builder.py create mode 100644 python/external/wheezy/template/comp.py create mode 100644 python/external/wheezy/template/compiler.py create mode 100644 python/external/wheezy/template/console.py create mode 100644 python/external/wheezy/template/engine.py create mode 100644 python/external/wheezy/template/ext/__init__.py create mode 100644 python/external/wheezy/template/ext/code.py create mode 100644 python/external/wheezy/template/ext/core.py create mode 100644 python/external/wheezy/template/ext/determined.py create mode 100644 python/external/wheezy/template/ext/tests/__init__.py create mode 100644 python/external/wheezy/template/ext/tests/test_code.py create mode 100644 python/external/wheezy/template/ext/tests/test_core.py create mode 100644 python/external/wheezy/template/ext/tests/test_determined.py create mode 100644 python/external/wheezy/template/lexer.py create mode 100644 python/external/wheezy/template/loader.py create mode 100644 python/external/wheezy/template/parser.py create mode 100644 python/external/wheezy/template/preprocessor.py create mode 100644 python/external/wheezy/template/py.typed create mode 100644 python/external/wheezy/template/tests/__init__.py create mode 100644 python/external/wheezy/template/tests/test_builder.py create mode 100644 python/external/wheezy/template/tests/test_console.py create mode 100644 python/external/wheezy/template/tests/test_engine.py create mode 100644 python/external/wheezy/template/tests/test_lexer.py create mode 100644 python/external/wheezy/template/tests/test_loader.py create mode 100644 python/external/wheezy/template/tests/test_parser.py create mode 100644 python/external/wheezy/template/tests/test_preprocessor.py create mode 100644 python/external/wheezy/template/tests/test_utils.py create mode 100644 python/external/wheezy/template/typing.py create mode 100644 python/external/wheezy/template/utils.py create mode 100644 python/serve_html.py diff --git a/python/external/bottle.py b/python/external/bottle.py new file mode 100644 index 0000000..34c6e59 --- /dev/null +++ b/python/external/bottle.py @@ -0,0 +1,4429 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Bottle is a fast and simple micro-framework for small web applications. It +offers request dispatching (Routes) with URL parameter support, templates, +a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and +template engines - all in a single file and with no dependencies other than the +Python Standard Library. + +Homepage and documentation: http://bottlepy.org/ + +Copyright (c) 2009-2018, Marcel Hellkamp. +License: MIT (see LICENSE for details) +""" + +from __future__ import print_function +import sys + +__author__ = 'Marcel Hellkamp' +__version__ = '0.13-dev' +__license__ = 'MIT' + +############################################################################### +# Command-line interface ###################################################### +############################################################################### +# INFO: Some server adapters need to monkey-patch std-lib modules before they +# are imported. This is why some of the command-line handling is done here, but +# the actual call to _main() is at the end of the file. + + +def _cli_parse(args): # pragma: no coverage + from argparse import ArgumentParser + + parser = ArgumentParser(prog=args[0], usage="%(prog)s [options] package.module:app") + opt = parser.add_argument + opt("--version", action="store_true", help="show version number.") + opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.") + opt("-s", "--server", default='wsgiref', help="use SERVER as backend.") + opt("-p", "--plugin", action="append", help="install additional plugin/s.") + opt("-c", "--conf", action="append", metavar="FILE", + help="load config values from FILE.") + opt("-C", "--param", action="append", metavar="NAME=VALUE", + help="override config values.") + opt("--debug", action="store_true", help="start server in debug mode.") + opt("--reload", action="store_true", help="auto-reload on file changes.") + opt('app', help='WSGI app entry point.', nargs='?') + + cli_args = parser.parse_args(args[1:]) + + return cli_args, parser + + +def _cli_patch(cli_args): # pragma: no coverage + parsed_args, _ = _cli_parse(cli_args) + opts = parsed_args + if opts.server: + if opts.server.startswith('gevent'): + import gevent.monkey + gevent.monkey.patch_all() + elif opts.server.startswith('eventlet'): + import eventlet + eventlet.monkey_patch() + + +if __name__ == '__main__': + _cli_patch(sys.argv) + +############################################################################### +# Imports and Python 2/3 unification ########################################## +############################################################################### + +import base64, calendar, cgi, email.utils, functools, hmac, imp, itertools,\ + mimetypes, os, re, tempfile, threading, time, warnings, weakref, hashlib + +from types import FunctionType +from datetime import date as datedate, datetime, timedelta +from tempfile import TemporaryFile +from traceback import format_exc, print_exc +from unicodedata import normalize + +try: + from ujson import dumps as json_dumps, loads as json_lds +except ImportError: + from json import dumps as json_dumps, loads as json_lds + +# inspect.getargspec was removed in Python 3.6, use +# Signature-based version where we can (Python 3.3+) +try: + from inspect import signature + def getargspec(func): + params = signature(func).parameters + args, varargs, keywords, defaults = [], None, None, [] + for name, param in params.items(): + if param.kind == param.VAR_POSITIONAL: + varargs = name + elif param.kind == param.VAR_KEYWORD: + keywords = name + else: + args.append(name) + if param.default is not param.empty: + defaults.append(param.default) + return (args, varargs, keywords, tuple(defaults) or None) +except ImportError: + try: + from inspect import getfullargspec + def getargspec(func): + spec = getfullargspec(func) + kwargs = makelist(spec[0]) + makelist(spec.kwonlyargs) + return kwargs, spec[1], spec[2], spec[3] + except ImportError: + from inspect import getargspec + + +py = sys.version_info +py3k = py.major > 2 + +# Lots of stdlib and builtin differences. +if py3k: + import http.client as httplib + import _thread as thread + from urllib.parse import urljoin, SplitResult as UrlSplitResult + from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote + urlunquote = functools.partial(urlunquote, encoding='latin1') + from http.cookies import SimpleCookie, Morsel, CookieError + from collections.abc import MutableMapping as DictMixin + import pickle + from io import BytesIO + import configparser + + basestring = str + unicode = str + json_loads = lambda s: json_lds(touni(s)) + callable = lambda x: hasattr(x, '__call__') + imap = map + + def _raise(*a): + raise a[0](a[1]).with_traceback(a[2]) +else: # 2.x + import httplib + import thread + from urlparse import urljoin, SplitResult as UrlSplitResult + from urllib import urlencode, quote as urlquote, unquote as urlunquote + from Cookie import SimpleCookie, Morsel, CookieError + from itertools import imap + import cPickle as pickle + from StringIO import StringIO as BytesIO + import ConfigParser as configparser + from collections import MutableMapping as DictMixin + unicode = unicode + json_loads = json_lds + exec(compile('def _raise(*a): raise a[0], a[1], a[2]', '', 'exec')) + +# Some helpers for string/byte handling +def tob(s, enc='utf8'): + if isinstance(s, unicode): + return s.encode(enc) + return b'' if s is None else bytes(s) + + +def touni(s, enc='utf8', err='strict'): + if isinstance(s, bytes): + return s.decode(enc, err) + return unicode("" if s is None else s) + + +tonat = touni if py3k else tob + + +def _stderr(*args): + try: + print(*args, file=sys.stderr) + except (IOError, AttributeError): + pass # Some environments do not allow printing (mod_wsgi) + + +# A bug in functools causes it to break if the wrapper is an instance method +def update_wrapper(wrapper, wrapped, *a, **ka): + try: + functools.update_wrapper(wrapper, wrapped, *a, **ka) + except AttributeError: + pass + +# These helpers are used at module level and need to be defined first. +# And yes, I know PEP-8, but sometimes a lower-case classname makes more sense. + + +def depr(major, minor, cause, fix): + text = "Warning: Use of deprecated feature or API. (Deprecated in Bottle-%d.%d)\n"\ + "Cause: %s\n"\ + "Fix: %s\n" % (major, minor, cause, fix) + if DEBUG == 'strict': + raise DeprecationWarning(text) + warnings.warn(text, DeprecationWarning, stacklevel=3) + return DeprecationWarning(text) + + +def makelist(data): # This is just too handy + if isinstance(data, (tuple, list, set, dict)): + return list(data) + elif data: + return [data] + else: + return [] + + +class DictProperty(object): + """ Property that maps to a key in a local dict-like attribute. """ + + def __init__(self, attr, key=None, read_only=False): + self.attr, self.key, self.read_only = attr, key, read_only + + def __call__(self, func): + functools.update_wrapper(self, func, updated=[]) + self.getter, self.key = func, self.key or func.__name__ + return self + + def __get__(self, obj, cls): + if obj is None: return self + key, storage = self.key, getattr(obj, self.attr) + if key not in storage: storage[key] = self.getter(obj) + return storage[key] + + def __set__(self, obj, value): + if self.read_only: raise AttributeError("Read-Only property.") + getattr(obj, self.attr)[self.key] = value + + def __delete__(self, obj): + if self.read_only: raise AttributeError("Read-Only property.") + del getattr(obj, self.attr)[self.key] + + +class cached_property(object): + """ A property that is only computed once per instance and then replaces + itself with an ordinary attribute. Deleting the attribute resets the + property. """ + + def __init__(self, func): + update_wrapper(self, func) + self.func = func + + def __get__(self, obj, cls): + if obj is None: return self + value = obj.__dict__[self.func.__name__] = self.func(obj) + return value + + +class lazy_attribute(object): + """ A property that caches itself to the class object. """ + + def __init__(self, func): + functools.update_wrapper(self, func, updated=[]) + self.getter = func + + def __get__(self, obj, cls): + value = self.getter(cls) + setattr(cls, self.__name__, value) + return value + +############################################################################### +# Exceptions and Events ####################################################### +############################################################################### + + +class BottleException(Exception): + """ A base class for exceptions used by bottle. """ + pass + +############################################################################### +# Routing ###################################################################### +############################################################################### + + +class RouteError(BottleException): + """ This is a base class for all routing related exceptions """ + + +class RouteReset(BottleException): + """ If raised by a plugin or request handler, the route is reset and all + plugins are re-applied. """ + + +class RouterUnknownModeError(RouteError): + + pass + + +class RouteSyntaxError(RouteError): + """ The route parser found something not supported by this router. """ + + +class RouteBuildError(RouteError): + """ The route could not be built. """ + + +def _re_flatten(p): + """ Turn all capturing groups in a regular expression pattern into + non-capturing groups. """ + if '(' not in p: + return p + return re.sub(r'(\\*)(\(\?P<[^>]+>|\((?!\?))', lambda m: m.group(0) if + len(m.group(1)) % 2 else m.group(1) + '(?:', p) + + +class Router(object): + """ A Router is an ordered collection of route->target pairs. It is used to + efficiently match WSGI requests against a number of routes and return + the first target that satisfies the request. The target may be anything, + usually a string, ID or callable object. A route consists of a path-rule + and a HTTP method. + + The path-rule is either a static path (e.g. `/contact`) or a dynamic + path that contains wildcards (e.g. `/wiki/`). The wildcard syntax + and details on the matching order are described in docs:`routing`. + """ + + default_pattern = '[^/]+' + default_filter = 're' + + #: The current CPython regexp implementation does not allow more + #: than 99 matching groups per regular expression. + _MAX_GROUPS_PER_PATTERN = 99 + + def __init__(self, strict=False): + self.rules = [] # All rules in order + self._groups = {} # index of regexes to find them in dyna_routes + self.builder = {} # Data structure for the url builder + self.static = {} # Search structure for static routes + self.dyna_routes = {} + self.dyna_regexes = {} # Search structure for dynamic routes + #: If true, static routes are no longer checked first. + self.strict_order = strict + self.filters = { + 're': lambda conf: (_re_flatten(conf or self.default_pattern), + None, None), + 'int': lambda conf: (r'-?\d+', int, lambda x: str(int(x))), + 'float': lambda conf: (r'-?[\d.]+', float, lambda x: str(float(x))), + 'path': lambda conf: (r'.+?', None, None) + } + + def add_filter(self, name, func): + """ Add a filter. The provided function is called with the configuration + string as parameter and must return a (regexp, to_python, to_url) tuple. + The first element is a string, the last two are callables or None. """ + self.filters[name] = func + + rule_syntax = re.compile('(\\\\*)' + '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)' + '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)' + '(?::((?:\\\\.|[^\\\\>])+)?)?)?>))') + + def _itertokens(self, rule): + offset, prefix = 0, '' + for match in self.rule_syntax.finditer(rule): + prefix += rule[offset:match.start()] + g = match.groups() + if g[2] is not None: + depr(0, 13, "Use of old route syntax.", + "Use instead of :name in routes.") + if len(g[0]) % 2: # Escaped wildcard + prefix += match.group(0)[len(g[0]):] + offset = match.end() + continue + if prefix: + yield prefix, None, None + name, filtr, conf = g[4:7] if g[2] is None else g[1:4] + yield name, filtr or 'default', conf or None + offset, prefix = match.end(), '' + if offset <= len(rule) or prefix: + yield prefix + rule[offset:], None, None + + def add(self, rule, method, target, name=None): + """ Add a new rule or replace the target for an existing rule. """ + anons = 0 # Number of anonymous wildcards found + keys = [] # Names of keys + pattern = '' # Regular expression pattern with named groups + filters = [] # Lists of wildcard input filters + builder = [] # Data structure for the URL builder + is_static = True + + for key, mode, conf in self._itertokens(rule): + if mode: + is_static = False + if mode == 'default': mode = self.default_filter + mask, in_filter, out_filter = self.filters[mode](conf) + if not key: + pattern += '(?:%s)' % mask + key = 'anon%d' % anons + anons += 1 + else: + pattern += '(?P<%s>%s)' % (key, mask) + keys.append(key) + if in_filter: filters.append((key, in_filter)) + builder.append((key, out_filter or str)) + elif key: + pattern += re.escape(key) + builder.append((None, key)) + + self.builder[rule] = builder + if name: self.builder[name] = builder + + if is_static and not self.strict_order: + self.static.setdefault(method, {}) + self.static[method][self.build(rule)] = (target, None) + return + + try: + re_pattern = re.compile('^(%s)$' % pattern) + re_match = re_pattern.match + except re.error as e: + raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, e)) + + if filters: + + def getargs(path): + url_args = re_match(path).groupdict() + for name, wildcard_filter in filters: + try: + url_args[name] = wildcard_filter(url_args[name]) + except ValueError: + raise HTTPError(400, 'Path has wrong format.') + return url_args + elif re_pattern.groupindex: + + def getargs(path): + return re_match(path).groupdict() + else: + getargs = None + + flatpat = _re_flatten(pattern) + whole_rule = (rule, flatpat, target, getargs) + + if (flatpat, method) in self._groups: + if DEBUG: + msg = 'Route <%s %s> overwrites a previously defined route' + warnings.warn(msg % (method, rule), RuntimeWarning) + self.dyna_routes[method][ + self._groups[flatpat, method]] = whole_rule + else: + self.dyna_routes.setdefault(method, []).append(whole_rule) + self._groups[flatpat, method] = len(self.dyna_routes[method]) - 1 + + self._compile(method) + + def _compile(self, method): + all_rules = self.dyna_routes[method] + comborules = self.dyna_regexes[method] = [] + maxgroups = self._MAX_GROUPS_PER_PATTERN + for x in range(0, len(all_rules), maxgroups): + some = all_rules[x:x + maxgroups] + combined = (flatpat for (_, flatpat, _, _) in some) + combined = '|'.join('(^%s$)' % flatpat for flatpat in combined) + combined = re.compile(combined).match + rules = [(target, getargs) for (_, _, target, getargs) in some] + comborules.append((combined, rules)) + + def build(self, _name, *anons, **query): + """ Build an URL by filling the wildcards in a rule. """ + builder = self.builder.get(_name) + if not builder: + raise RouteBuildError("No route with that name.", _name) + try: + for i, value in enumerate(anons): + query['anon%d' % i] = value + url = ''.join([f(query.pop(n)) if n else f for (n, f) in builder]) + return url if not query else url + '?' + urlencode(query) + except KeyError as E: + raise RouteBuildError('Missing URL argument: %r' % E.args[0]) + + def match(self, environ): + """ Return a (target, url_args) tuple or raise HTTPError(400/404/405). """ + verb = environ['REQUEST_METHOD'].upper() + path = environ['PATH_INFO'] or '/' + + methods = ('PROXY', 'HEAD', 'GET', 'ANY') if verb == 'HEAD' else ('PROXY', verb, 'ANY') + + for method in methods: + if method in self.static and path in self.static[method]: + target, getargs = self.static[method][path] + return target, getargs(path) if getargs else {} + elif method in self.dyna_regexes: + for combined, rules in self.dyna_regexes[method]: + match = combined(path) + if match: + target, getargs = rules[match.lastindex - 1] + return target, getargs(path) if getargs else {} + + # No matching route found. Collect alternative methods for 405 response + allowed = set([]) + nocheck = set(methods) + for method in set(self.static) - nocheck: + if path in self.static[method]: + allowed.add(method) + for method in set(self.dyna_regexes) - allowed - nocheck: + for combined, rules in self.dyna_regexes[method]: + match = combined(path) + if match: + allowed.add(method) + if allowed: + allow_header = ",".join(sorted(allowed)) + raise HTTPError(405, "Method not allowed.", Allow=allow_header) + + # No matching route and no alternative method found. We give up + raise HTTPError(404, "Not found: " + repr(path)) + + +class Route(object): + """ This class wraps a route callback along with route specific metadata and + configuration and applies Plugins on demand. It is also responsible for + turning an URL path rule into a regular expression usable by the Router. + """ + + def __init__(self, app, rule, method, callback, + name=None, + plugins=None, + skiplist=None, **config): + #: The application this route is installed to. + self.app = app + #: The path-rule string (e.g. ``/wiki/``). + self.rule = rule + #: The HTTP method as a string (e.g. ``GET``). + self.method = method + #: The original callback with no plugins applied. Useful for introspection. + self.callback = callback + #: The name of the route (if specified) or ``None``. + self.name = name or None + #: A list of route-specific plugins (see :meth:`Bottle.route`). + self.plugins = plugins or [] + #: A list of plugins to not apply to this route (see :meth:`Bottle.route`). + self.skiplist = skiplist or [] + #: Additional keyword arguments passed to the :meth:`Bottle.route` + #: decorator are stored in this dictionary. Used for route-specific + #: plugin configuration and meta-data. + self.config = app.config._make_overlay() + self.config.load_dict(config) + + @cached_property + def call(self): + """ The route callback with all plugins applied. This property is + created on demand and then cached to speed up subsequent requests.""" + return self._make_callback() + + def reset(self): + """ Forget any cached values. The next time :attr:`call` is accessed, + all plugins are re-applied. """ + self.__dict__.pop('call', None) + + def prepare(self): + """ Do all on-demand work immediately (useful for debugging).""" + self.call + + def all_plugins(self): + """ Yield all Plugins affecting this route. """ + unique = set() + for p in reversed(self.app.plugins + self.plugins): + if True in self.skiplist: break + name = getattr(p, 'name', False) + if name and (name in self.skiplist or name in unique): continue + if p in self.skiplist or type(p) in self.skiplist: continue + if name: unique.add(name) + yield p + + def _make_callback(self): + callback = self.callback + for plugin in self.all_plugins(): + try: + if hasattr(plugin, 'apply'): + callback = plugin.apply(callback, self) + else: + callback = plugin(callback) + except RouteReset: # Try again with changed configuration. + return self._make_callback() + if callback is not self.callback: + update_wrapper(callback, self.callback) + return callback + + def get_undecorated_callback(self): + """ Return the callback. If the callback is a decorated function, try to + recover the original function. """ + func = self.callback + func = getattr(func, '__func__' if py3k else 'im_func', func) + closure_attr = '__closure__' if py3k else 'func_closure' + while hasattr(func, closure_attr) and getattr(func, closure_attr): + attributes = getattr(func, closure_attr) + func = attributes[0].cell_contents + + # in case of decorators with multiple arguments + if not isinstance(func, FunctionType): + # pick first FunctionType instance from multiple arguments + func = filter(lambda x: isinstance(x, FunctionType), + map(lambda x: x.cell_contents, attributes)) + func = list(func)[0] # py3 support + return func + + def get_callback_args(self): + """ Return a list of argument names the callback (most likely) accepts + as keyword arguments. If the callback is a decorated function, try + to recover the original function before inspection. """ + return getargspec(self.get_undecorated_callback())[0] + + def get_config(self, key, default=None): + """ Lookup a config field and return its value, first checking the + route.config, then route.app.config.""" + depr(0, 13, "Route.get_config() is deprecated.", + "The Route.config property already includes values from the" + " application config for missing keys. Access it directly.") + return self.config.get(key, default) + + def __repr__(self): + cb = self.get_undecorated_callback() + return '<%s %s -> %s:%s>' % (self.method, self.rule, cb.__module__, cb.__name__) + +############################################################################### +# Application Object ########################################################### +############################################################################### + + +class Bottle(object): + """ Each Bottle object represents a single, distinct web application and + consists of routes, callbacks, plugins, resources and configuration. + Instances are callable WSGI applications. + + :param catchall: If true (default), handle all exceptions. Turn off to + let debugging middleware handle exceptions. + """ + + @lazy_attribute + def _global_config(cls): + cfg = ConfigDict() + cfg.meta_set('catchall', 'validate', bool) + return cfg + + def __init__(self, **kwargs): + #: A :class:`ConfigDict` for app specific configuration. + self.config = self._global_config._make_overlay() + self.config._add_change_listener( + functools.partial(self.trigger_hook, 'config')) + + self.config.update({ + "catchall": True + }) + + if kwargs.get('catchall') is False: + depr(0, 13, "Bottle(catchall) keyword argument.", + "The 'catchall' setting is now part of the app " + "configuration. Fix: `app.config['catchall'] = False`") + self.config['catchall'] = False + if kwargs.get('autojson') is False: + depr(0, 13, "Bottle(autojson) keyword argument.", + "The 'autojson' setting is now part of the app " + "configuration. Fix: `app.config['json.enable'] = False`") + self.config['json.disable'] = True + + self._mounts = [] + + #: A :class:`ResourceManager` for application files + self.resources = ResourceManager() + + self.routes = [] # List of installed :class:`Route` instances. + self.router = Router() # Maps requests to :class:`Route` instances. + self.error_handler = {} + + # Core plugins + self.plugins = [] # List of installed plugins. + self.install(JSONPlugin()) + self.install(TemplatePlugin()) + + #: If true, most exceptions are caught and returned as :exc:`HTTPError` + catchall = DictProperty('config', 'catchall') + + __hook_names = 'before_request', 'after_request', 'app_reset', 'config' + __hook_reversed = {'after_request'} + + @cached_property + def _hooks(self): + return dict((name, []) for name in self.__hook_names) + + def add_hook(self, name, func): + """ Attach a callback to a hook. Three hooks are currently implemented: + + before_request + Executed once before each request. The request context is + available, but no routing has happened yet. + after_request + Executed once after each request regardless of its outcome. + app_reset + Called whenever :meth:`Bottle.reset` is called. + """ + if name in self.__hook_reversed: + self._hooks[name].insert(0, func) + else: + self._hooks[name].append(func) + + def remove_hook(self, name, func): + """ Remove a callback from a hook. """ + if name in self._hooks and func in self._hooks[name]: + self._hooks[name].remove(func) + return True + + def trigger_hook(self, __name, *args, **kwargs): + """ Trigger a hook and return a list of results. """ + return [hook(*args, **kwargs) for hook in self._hooks[__name][:]] + + def hook(self, name): + """ Return a decorator that attaches a callback to a hook. See + :meth:`add_hook` for details.""" + + def decorator(func): + self.add_hook(name, func) + return func + + return decorator + + def _mount_wsgi(self, prefix, app, **options): + segments = [p for p in prefix.split('/') if p] + if not segments: + raise ValueError('WSGI applications cannot be mounted to "/".') + path_depth = len(segments) + + def mountpoint_wrapper(): + try: + request.path_shift(path_depth) + rs = HTTPResponse([]) + + def start_response(status, headerlist, exc_info=None): + if exc_info: + _raise(*exc_info) + if py3k: + # Errors here mean that the mounted WSGI app did not + # follow PEP-3333 (which requires latin1) or used a + # pre-encoding other than utf8 :/ + status = status.encode('latin1').decode('utf8') + headerlist = [(k, v.encode('latin1').decode('utf8')) + for (k, v) in headerlist] + rs.status = status + for name, value in headerlist: + rs.add_header(name, value) + return rs.body.append + + body = app(request.environ, start_response) + rs.body = itertools.chain(rs.body, body) if rs.body else body + return rs + finally: + request.path_shift(-path_depth) + + options.setdefault('skip', True) + options.setdefault('method', 'PROXY') + options.setdefault('mountpoint', {'prefix': prefix, 'target': app}) + options['callback'] = mountpoint_wrapper + + self.route('/%s/<:re:.*>' % '/'.join(segments), **options) + if not prefix.endswith('/'): + self.route('/' + '/'.join(segments), **options) + + def _mount_app(self, prefix, app, **options): + if app in self._mounts or '_mount.app' in app.config: + depr(0, 13, "Application mounted multiple times. Falling back to WSGI mount.", + "Clone application before mounting to a different location.") + return self._mount_wsgi(prefix, app, **options) + + if options: + depr(0, 13, "Unsupported mount options. Falling back to WSGI mount.", + "Do not specify any route options when mounting bottle application.") + return self._mount_wsgi(prefix, app, **options) + + if not prefix.endswith("/"): + depr(0, 13, "Prefix must end in '/'. Falling back to WSGI mount.", + "Consider adding an explicit redirect from '/prefix' to '/prefix/' in the parent application.") + return self._mount_wsgi(prefix, app, **options) + + self._mounts.append(app) + app.config['_mount.prefix'] = prefix + app.config['_mount.app'] = self + for route in app.routes: + route.rule = prefix + route.rule.lstrip('/') + self.add_route(route) + + def mount(self, prefix, app, **options): + """ Mount an application (:class:`Bottle` or plain WSGI) to a specific + URL prefix. Example:: + + parent_app.mount('/prefix/', child_app) + + :param prefix: path prefix or `mount-point`. + :param app: an instance of :class:`Bottle` or a WSGI application. + + Plugins from the parent application are not applied to the routes + of the mounted child application. If you need plugins in the child + application, install them separately. + + While it is possible to use path wildcards within the prefix path + (:class:`Bottle` childs only), it is highly discouraged. + + The prefix path must end with a slash. If you want to access the + root of the child application via `/prefix` in addition to + `/prefix/`, consider adding a route with a 307 redirect to the + parent application. + """ + + if not prefix.startswith('/'): + raise ValueError("Prefix must start with '/'") + + if isinstance(app, Bottle): + return self._mount_app(prefix, app, **options) + else: + return self._mount_wsgi(prefix, app, **options) + + def merge(self, routes): + """ Merge the routes of another :class:`Bottle` application or a list of + :class:`Route` objects into this application. The routes keep their + 'owner', meaning that the :data:`Route.app` attribute is not + changed. """ + if isinstance(routes, Bottle): + routes = routes.routes + for route in routes: + self.add_route(route) + + def install(self, plugin): + """ Add a plugin to the list of plugins and prepare it for being + applied to all routes of this application. A plugin may be a simple + decorator or an object that implements the :class:`Plugin` API. + """ + if hasattr(plugin, 'setup'): plugin.setup(self) + if not callable(plugin) and not hasattr(plugin, 'apply'): + raise TypeError("Plugins must be callable or implement .apply()") + self.plugins.append(plugin) + self.reset() + return plugin + + def uninstall(self, plugin): + """ Uninstall plugins. Pass an instance to remove a specific plugin, a type + object to remove all plugins that match that type, a string to remove + all plugins with a matching ``name`` attribute or ``True`` to remove all + plugins. Return the list of removed plugins. """ + removed, remove = [], plugin + for i, plugin in list(enumerate(self.plugins))[::-1]: + if remove is True or remove is plugin or remove is type(plugin) \ + or getattr(plugin, 'name', True) == remove: + removed.append(plugin) + del self.plugins[i] + if hasattr(plugin, 'close'): plugin.close() + if removed: self.reset() + return removed + + def reset(self, route=None): + """ Reset all routes (force plugins to be re-applied) and clear all + caches. If an ID or route object is given, only that specific route + is affected. """ + if route is None: routes = self.routes + elif isinstance(route, Route): routes = [route] + else: routes = [self.routes[route]] + for route in routes: + route.reset() + if DEBUG: + for route in routes: + route.prepare() + self.trigger_hook('app_reset') + + def close(self): + """ Close the application and all installed plugins. """ + for plugin in self.plugins: + if hasattr(plugin, 'close'): plugin.close() + + def run(self, **kwargs): + """ Calls :func:`run` with the same parameters. """ + run(self, **kwargs) + + def match(self, environ): + """ Search for a matching route and return a (:class:`Route`, urlargs) + tuple. The second value is a dictionary with parameters extracted + from the URL. Raise :exc:`HTTPError` (404/405) on a non-match.""" + return self.router.match(environ) + + def get_url(self, routename, **kargs): + """ Return a string that matches a named route """ + scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/' + location = self.router.build(routename, **kargs).lstrip('/') + return urljoin(urljoin('/', scriptname), location) + + def add_route(self, route): + """ Add a route object, but do not change the :data:`Route.app` + attribute.""" + self.routes.append(route) + self.router.add(route.rule, route.method, route, name=route.name) + if DEBUG: route.prepare() + + def route(self, + path=None, + method='GET', + callback=None, + name=None, + apply=None, + skip=None, **config): + """ A decorator to bind a function to a request URL. Example:: + + @app.route('/hello/') + def hello(name): + return 'Hello %s' % name + + The ```` part is a wildcard. See :class:`Router` for syntax + details. + + :param path: Request path or a list of paths to listen to. If no + path is specified, it is automatically generated from the + signature of the function. + :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of + methods to listen to. (default: `GET`) + :param callback: An optional shortcut to avoid the decorator + syntax. ``route(..., callback=func)`` equals ``route(...)(func)`` + :param name: The name for this route. (default: None) + :param apply: A decorator or plugin or a list of plugins. These are + applied to the route callback in addition to installed plugins. + :param skip: A list of plugins, plugin classes or names. Matching + plugins are not installed to this route. ``True`` skips all. + + Any additional keyword arguments are stored as route-specific + configuration and passed to plugins (see :meth:`Plugin.apply`). + """ + if callable(path): path, callback = None, path + plugins = makelist(apply) + skiplist = makelist(skip) + + def decorator(callback): + if isinstance(callback, basestring): callback = load(callback) + for rule in makelist(path) or yieldroutes(callback): + for verb in makelist(method): + verb = verb.upper() + route = Route(self, rule, verb, callback, + name=name, + plugins=plugins, + skiplist=skiplist, **config) + self.add_route(route) + return callback + + return decorator(callback) if callback else decorator + + def get(self, path=None, method='GET', **options): + """ Equals :meth:`route`. """ + return self.route(path, method, **options) + + def post(self, path=None, method='POST', **options): + """ Equals :meth:`route` with a ``POST`` method parameter. """ + return self.route(path, method, **options) + + def put(self, path=None, method='PUT', **options): + """ Equals :meth:`route` with a ``PUT`` method parameter. """ + return self.route(path, method, **options) + + def delete(self, path=None, method='DELETE', **options): + """ Equals :meth:`route` with a ``DELETE`` method parameter. """ + return self.route(path, method, **options) + + def patch(self, path=None, method='PATCH', **options): + """ Equals :meth:`route` with a ``PATCH`` method parameter. """ + return self.route(path, method, **options) + + def error(self, code=500, callback=None): + """ Register an output handler for a HTTP error code. Can + be used as a decorator or called directly :: + + def error_handler_500(error): + return 'error_handler_500' + + app.error(code=500, callback=error_handler_500) + + @app.error(404) + def error_handler_404(error): + return 'error_handler_404' + + """ + + def decorator(callback): + if isinstance(callback, basestring): callback = load(callback) + self.error_handler[int(code)] = callback + return callback + + return decorator(callback) if callback else decorator + + def default_error_handler(self, res): + return tob(template(ERROR_PAGE_TEMPLATE, e=res, template_settings=dict(name='__ERROR_PAGE_TEMPLATE'))) + + def _handle(self, environ): + path = environ['bottle.raw_path'] = environ['PATH_INFO'] + if py3k: + environ['PATH_INFO'] = path.encode('latin1').decode('utf8', 'ignore') + + environ['bottle.app'] = self + request.bind(environ) + response.bind() + + try: + while True: # Remove in 0.14 together with RouteReset + out = None + try: + self.trigger_hook('before_request') + route, args = self.router.match(environ) + environ['route.handle'] = route + environ['bottle.route'] = route + environ['route.url_args'] = args + out = route.call(**args) + break + except HTTPResponse as E: + out = E + break + except RouteReset: + depr(0, 13, "RouteReset exception deprecated", + "Call route.call() after route.reset() and " + "return the result.") + route.reset() + continue + finally: + if isinstance(out, HTTPResponse): + out.apply(response) + try: + self.trigger_hook('after_request') + except HTTPResponse as E: + out = E + out.apply(response) + except (KeyboardInterrupt, SystemExit, MemoryError): + raise + except Exception as E: + if not self.catchall: raise + stacktrace = format_exc() + environ['wsgi.errors'].write(stacktrace) + environ['wsgi.errors'].flush() + environ['bottle.exc_info'] = sys.exc_info() + out = HTTPError(500, "Internal Server Error", E, stacktrace) + out.apply(response) + + return out + + def _cast(self, out, peek=None): + """ Try to convert the parameter into something WSGI compatible and set + correct HTTP headers when possible. + Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like, + iterable of strings and iterable of unicodes + """ + + # Empty output is done here + if not out: + if 'Content-Length' not in response: + response['Content-Length'] = 0 + return [] + # Join lists of byte or unicode strings. Mixed lists are NOT supported + if isinstance(out, (tuple, list))\ + and isinstance(out[0], (bytes, unicode)): + out = out[0][0:0].join(out) # b'abc'[0:0] -> b'' + # Encode unicode strings + if isinstance(out, unicode): + out = out.encode(response.charset) + # Byte Strings are just returned + if isinstance(out, bytes): + if 'Content-Length' not in response: + response['Content-Length'] = len(out) + return [out] + # HTTPError or HTTPException (recursive, because they may wrap anything) + # TODO: Handle these explicitly in handle() or make them iterable. + if isinstance(out, HTTPError): + out.apply(response) + out = self.error_handler.get(out.status_code, + self.default_error_handler)(out) + return self._cast(out) + if isinstance(out, HTTPResponse): + out.apply(response) + return self._cast(out.body) + + # File-like objects. + if hasattr(out, 'read'): + if 'wsgi.file_wrapper' in request.environ: + return request.environ['wsgi.file_wrapper'](out) + elif hasattr(out, 'close') or not hasattr(out, '__iter__'): + return WSGIFileWrapper(out) + + # Handle Iterables. We peek into them to detect their inner type. + try: + iout = iter(out) + first = next(iout) + while not first: + first = next(iout) + except StopIteration: + return self._cast('') + except HTTPResponse as E: + first = E + except (KeyboardInterrupt, SystemExit, MemoryError): + raise + except Exception as error: + if not self.catchall: raise + first = HTTPError(500, 'Unhandled exception', error, format_exc()) + + # These are the inner types allowed in iterator or generator objects. + if isinstance(first, HTTPResponse): + return self._cast(first) + elif isinstance(first, bytes): + new_iter = itertools.chain([first], iout) + elif isinstance(first, unicode): + encoder = lambda x: x.encode(response.charset) + new_iter = imap(encoder, itertools.chain([first], iout)) + else: + msg = 'Unsupported response type: %s' % type(first) + return self._cast(HTTPError(500, msg)) + if hasattr(out, 'close'): + new_iter = _closeiter(new_iter, out.close) + return new_iter + + def wsgi(self, environ, start_response): + """ The bottle WSGI-interface. """ + try: + out = self._cast(self._handle(environ)) + # rfc2616 section 4.3 + if response._status_code in (100, 101, 204, 304)\ + or environ['REQUEST_METHOD'] == 'HEAD': + if hasattr(out, 'close'): out.close() + out = [] + exc_info = environ.get('bottle.exc_info') + if exc_info is not None: + del environ['bottle.exc_info'] + start_response(response._wsgi_status_line(), response.headerlist, exc_info) + return out + except (KeyboardInterrupt, SystemExit, MemoryError): + raise + except Exception as E: + if not self.catchall: raise + err = '

Critical error while processing request: %s

' \ + % html_escape(environ.get('PATH_INFO', '/')) + if DEBUG: + err += '

Error:

\n
\n%s\n
\n' \ + '

Traceback:

\n
\n%s\n
\n' \ + % (html_escape(repr(E)), html_escape(format_exc())) + environ['wsgi.errors'].write(err) + environ['wsgi.errors'].flush() + headers = [('Content-Type', 'text/html; charset=UTF-8')] + start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info()) + return [tob(err)] + + def __call__(self, environ, start_response): + """ Each instance of :class:'Bottle' is a WSGI application. """ + return self.wsgi(environ, start_response) + + def __enter__(self): + """ Use this application as default for all module-level shortcuts. """ + default_app.push(self) + return self + + def __exit__(self, exc_type, exc_value, traceback): + default_app.pop() + + def __setattr__(self, name, value): + if name in self.__dict__: + raise AttributeError("Attribute %s already defined. Plugin conflict?" % name) + self.__dict__[name] = value + + +############################################################################### +# HTTP and WSGI Tools ########################################################## +############################################################################### + + +class BaseRequest(object): + """ A wrapper for WSGI environment dictionaries that adds a lot of + convenient access methods and properties. Most of them are read-only. + + Adding new attributes to a request actually adds them to the environ + dictionary (as 'bottle.request.ext.'). This is the recommended + way to store and access request-specific data. + """ + + __slots__ = ('environ', ) + + #: Maximum size of memory buffer for :attr:`body` in bytes. + MEMFILE_MAX = 102400 + + def __init__(self, environ=None): + """ Wrap a WSGI environ dictionary. """ + #: The wrapped WSGI environ dictionary. This is the only real attribute. + #: All other attributes actually are read-only properties. + self.environ = {} if environ is None else environ + self.environ['bottle.request'] = self + + @DictProperty('environ', 'bottle.app', read_only=True) + def app(self): + """ Bottle application handling this request. """ + raise RuntimeError('This request is not connected to an application.') + + @DictProperty('environ', 'bottle.route', read_only=True) + def route(self): + """ The bottle :class:`Route` object that matches this request. """ + raise RuntimeError('This request is not connected to a route.') + + @DictProperty('environ', 'route.url_args', read_only=True) + def url_args(self): + """ The arguments extracted from the URL. """ + raise RuntimeError('This request is not connected to a route.') + + @property + def path(self): + """ The value of ``PATH_INFO`` with exactly one prefixed slash (to fix + broken clients and avoid the "empty path" edge case). """ + return '/' + self.environ.get('PATH_INFO', '').lstrip('/') + + @property + def method(self): + """ The ``REQUEST_METHOD`` value as an uppercase string. """ + return self.environ.get('REQUEST_METHOD', 'GET').upper() + + @DictProperty('environ', 'bottle.request.headers', read_only=True) + def headers(self): + """ A :class:`WSGIHeaderDict` that provides case-insensitive access to + HTTP request headers. """ + return WSGIHeaderDict(self.environ) + + def get_header(self, name, default=None): + """ Return the value of a request header, or a given default value. """ + return self.headers.get(name, default) + + @DictProperty('environ', 'bottle.request.cookies', read_only=True) + def cookies(self): + """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT + decoded. Use :meth:`get_cookie` if you expect signed cookies. """ + cookies = SimpleCookie(self.environ.get('HTTP_COOKIE', '')).values() + return FormsDict((c.key, c.value) for c in cookies) + + def get_cookie(self, key, default=None, secret=None, digestmod=hashlib.sha256): + """ Return the content of a cookie. To read a `Signed Cookie`, the + `secret` must match the one used to create the cookie (see + :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing + cookie or wrong signature), return a default value. """ + value = self.cookies.get(key) + if secret: + # See BaseResponse.set_cookie for details on signed cookies. + if value and value.startswith('!') and '?' in value: + sig, msg = map(tob, value[1:].split('?', 1)) + hash = hmac.new(tob(secret), msg, digestmod=digestmod).digest() + if _lscmp(sig, base64.b64encode(hash)): + dst = pickle.loads(base64.b64decode(msg)) + if dst and dst[0] == key: + return dst[1] + return default + return value or default + + @DictProperty('environ', 'bottle.request.query', read_only=True) + def query(self): + """ The :attr:`query_string` parsed into a :class:`FormsDict`. These + values are sometimes called "URL arguments" or "GET parameters", but + not to be confused with "URL wildcards" as they are provided by the + :class:`Router`. """ + get = self.environ['bottle.get'] = FormsDict() + pairs = _parse_qsl(self.environ.get('QUERY_STRING', '')) + for key, value in pairs: + get[key] = value + return get + + @DictProperty('environ', 'bottle.request.forms', read_only=True) + def forms(self): + """ Form values parsed from an `url-encoded` or `multipart/form-data` + encoded POST or PUT request body. The result is returned as a + :class:`FormsDict`. All keys and values are strings. File uploads + are stored separately in :attr:`files`. """ + forms = FormsDict() + forms.recode_unicode = self.POST.recode_unicode + for name, item in self.POST.allitems(): + if not isinstance(item, FileUpload): + forms[name] = item + return forms + + @DictProperty('environ', 'bottle.request.params', read_only=True) + def params(self): + """ A :class:`FormsDict` with the combined values of :attr:`query` and + :attr:`forms`. File uploads are stored in :attr:`files`. """ + params = FormsDict() + for key, value in self.query.allitems(): + params[key] = value + for key, value in self.forms.allitems(): + params[key] = value + return params + + @DictProperty('environ', 'bottle.request.files', read_only=True) + def files(self): + """ File uploads parsed from `multipart/form-data` encoded POST or PUT + request body. The values are instances of :class:`FileUpload`. + + """ + files = FormsDict() + files.recode_unicode = self.POST.recode_unicode + for name, item in self.POST.allitems(): + if isinstance(item, FileUpload): + files[name] = item + return files + + @DictProperty('environ', 'bottle.request.json', read_only=True) + def json(self): + """ If the ``Content-Type`` header is ``application/json`` or + ``application/json-rpc``, this property holds the parsed content + of the request body. Only requests smaller than :attr:`MEMFILE_MAX` + are processed to avoid memory exhaustion. + Invalid JSON raises a 400 error response. + """ + ctype = self.environ.get('CONTENT_TYPE', '').lower().split(';')[0] + if ctype in ('application/json', 'application/json-rpc'): + b = self._get_body_string(self.MEMFILE_MAX) + if not b: + return None + try: + return json_loads(b) + except (ValueError, TypeError): + raise HTTPError(400, 'Invalid JSON') + return None + + def _iter_body(self, read, bufsize): + maxread = max(0, self.content_length) + while maxread: + part = read(min(maxread, bufsize)) + if not part: break + yield part + maxread -= len(part) + + @staticmethod + def _iter_chunked(read, bufsize): + err = HTTPError(400, 'Error while parsing chunked transfer body.') + rn, sem, bs = tob('\r\n'), tob(';'), tob('') + while True: + header = read(1) + while header[-2:] != rn: + c = read(1) + header += c + if not c: raise err + if len(header) > bufsize: raise err + size, _, _ = header.partition(sem) + try: + maxread = int(tonat(size.strip()), 16) + except ValueError: + raise err + if maxread == 0: break + buff = bs + while maxread > 0: + if not buff: + buff = read(min(maxread, bufsize)) + part, buff = buff[:maxread], buff[maxread:] + if not part: raise err + yield part + maxread -= len(part) + if read(2) != rn: + raise err + + @DictProperty('environ', 'bottle.request.body', read_only=True) + def _body(self): + try: + read_func = self.environ['wsgi.input'].read + except KeyError: + self.environ['wsgi.input'] = BytesIO() + return self.environ['wsgi.input'] + body_iter = self._iter_chunked if self.chunked else self._iter_body + body, body_size, is_temp_file = BytesIO(), 0, False + for part in body_iter(read_func, self.MEMFILE_MAX): + body.write(part) + body_size += len(part) + if not is_temp_file and body_size > self.MEMFILE_MAX: + body, tmp = TemporaryFile(mode='w+b'), body + body.write(tmp.getvalue()) + del tmp + is_temp_file = True + self.environ['wsgi.input'] = body + body.seek(0) + return body + + def _get_body_string(self, maxread): + """ Read body into a string. Raise HTTPError(413) on requests that are + too large. """ + if self.content_length > maxread: + raise HTTPError(413, 'Request entity too large') + data = self.body.read(maxread + 1) + if len(data) > maxread: + raise HTTPError(413, 'Request entity too large') + return data + + @property + def body(self): + """ The HTTP request body as a seek-able file-like object. Depending on + :attr:`MEMFILE_MAX`, this is either a temporary file or a + :class:`io.BytesIO` instance. Accessing this property for the first + time reads and replaces the ``wsgi.input`` environ variable. + Subsequent accesses just do a `seek(0)` on the file object. """ + self._body.seek(0) + return self._body + + @property + def chunked(self): + """ True if Chunked transfer encoding was. """ + return 'chunked' in self.environ.get( + 'HTTP_TRANSFER_ENCODING', '').lower() + + #: An alias for :attr:`query`. + GET = query + + @DictProperty('environ', 'bottle.request.post', read_only=True) + def POST(self): + """ The values of :attr:`forms` and :attr:`files` combined into a single + :class:`FormsDict`. Values are either strings (form values) or + instances of :class:`cgi.FieldStorage` (file uploads). + """ + post = FormsDict() + # We default to application/x-www-form-urlencoded for everything that + # is not multipart and take the fast path (also: 3.1 workaround) + if not self.content_type.startswith('multipart/'): + body = tonat(self._get_body_string(self.MEMFILE_MAX), 'latin1') + for key, value in _parse_qsl(body): + post[key] = value + return post + + safe_env = {'QUERY_STRING': ''} # Build a safe environment for cgi + for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'): + if key in self.environ: safe_env[key] = self.environ[key] + args = dict(fp=self.body, environ=safe_env, keep_blank_values=True) + + if py3k: + args['encoding'] = 'utf8' + post.recode_unicode = False + data = cgi.FieldStorage(**args) + self['_cgi.FieldStorage'] = data #http://bugs.python.org/issue18394 + data = data.list or [] + for item in data: + if item.filename is None: + post[item.name] = item.value + else: + post[item.name] = FileUpload(item.file, item.name, + item.filename, item.headers) + return post + + @property + def url(self): + """ The full request URI including hostname and scheme. If your app + lives behind a reverse proxy or load balancer and you get confusing + results, make sure that the ``X-Forwarded-Host`` header is set + correctly. """ + return self.urlparts.geturl() + + @DictProperty('environ', 'bottle.request.urlparts', read_only=True) + def urlparts(self): + """ The :attr:`url` string as an :class:`urlparse.SplitResult` tuple. + The tuple contains (scheme, host, path, query_string and fragment), + but the fragment is always empty because it is not visible to the + server. """ + env = self.environ + http = env.get('HTTP_X_FORWARDED_PROTO') \ + or env.get('wsgi.url_scheme', 'http') + host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST') + if not host: + # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients. + host = env.get('SERVER_NAME', '127.0.0.1') + port = env.get('SERVER_PORT') + if port and port != ('80' if http == 'http' else '443'): + host += ':' + port + path = urlquote(self.fullpath) + return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '') + + @property + def fullpath(self): + """ Request path including :attr:`script_name` (if present). """ + return urljoin(self.script_name, self.path.lstrip('/')) + + @property + def query_string(self): + """ The raw :attr:`query` part of the URL (everything in between ``?`` + and ``#``) as a string. """ + return self.environ.get('QUERY_STRING', '') + + @property + def script_name(self): + """ The initial portion of the URL's `path` that was removed by a higher + level (server or routing middleware) before the application was + called. This script path is returned with leading and tailing + slashes. """ + script_name = self.environ.get('SCRIPT_NAME', '').strip('/') + return '/' + script_name + '/' if script_name else '/' + + def path_shift(self, shift=1): + """ Shift path segments from :attr:`path` to :attr:`script_name` and + vice versa. + + :param shift: The number of path segments to shift. May be negative + to change the shift direction. (default: 1) + """ + script, path = path_shift(self.environ.get('SCRIPT_NAME', '/'), self.path, shift) + self['SCRIPT_NAME'], self['PATH_INFO'] = script, path + + @property + def content_length(self): + """ The request body length as an integer. The client is responsible to + set this header. Otherwise, the real length of the body is unknown + and -1 is returned. In this case, :attr:`body` will be empty. """ + return int(self.environ.get('CONTENT_LENGTH') or -1) + + @property + def content_type(self): + """ The Content-Type header as a lowercase-string (default: empty). """ + return self.environ.get('CONTENT_TYPE', '').lower() + + @property + def is_xhr(self): + """ True if the request was triggered by a XMLHttpRequest. This only + works with JavaScript libraries that support the `X-Requested-With` + header (most of the popular libraries do). """ + requested_with = self.environ.get('HTTP_X_REQUESTED_WITH', '') + return requested_with.lower() == 'xmlhttprequest' + + @property + def is_ajax(self): + """ Alias for :attr:`is_xhr`. "Ajax" is not the right term. """ + return self.is_xhr + + @property + def auth(self): + """ HTTP authentication data as a (user, password) tuple. This + implementation currently supports basic (not digest) authentication + only. If the authentication happened at a higher level (e.g. in the + front web-server or a middleware), the password field is None, but + the user field is looked up from the ``REMOTE_USER`` environ + variable. On any errors, None is returned. """ + basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION', '')) + if basic: return basic + ruser = self.environ.get('REMOTE_USER') + if ruser: return (ruser, None) + return None + + @property + def remote_route(self): + """ A list of all IPs that were involved in this request, starting with + the client IP and followed by zero or more proxies. This does only + work if all proxies support the ```X-Forwarded-For`` header. Note + that this information can be forged by malicious clients. """ + proxy = self.environ.get('HTTP_X_FORWARDED_FOR') + if proxy: return [ip.strip() for ip in proxy.split(',')] + remote = self.environ.get('REMOTE_ADDR') + return [remote] if remote else [] + + @property + def remote_addr(self): + """ The client IP as a string. Note that this information can be forged + by malicious clients. """ + route = self.remote_route + return route[0] if route else None + + def copy(self): + """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """ + return Request(self.environ.copy()) + + def get(self, value, default=None): + return self.environ.get(value, default) + + def __getitem__(self, key): + return self.environ[key] + + def __delitem__(self, key): + self[key] = "" + del (self.environ[key]) + + def __iter__(self): + return iter(self.environ) + + def __len__(self): + return len(self.environ) + + def keys(self): + return self.environ.keys() + + def __setitem__(self, key, value): + """ Change an environ value and clear all caches that depend on it. """ + + if self.environ.get('bottle.request.readonly'): + raise KeyError('The environ dictionary is read-only.') + + self.environ[key] = value + todelete = () + + if key == 'wsgi.input': + todelete = ('body', 'forms', 'files', 'params', 'post', 'json') + elif key == 'QUERY_STRING': + todelete = ('query', 'params') + elif key.startswith('HTTP_'): + todelete = ('headers', 'cookies') + + for key in todelete: + self.environ.pop('bottle.request.' + key, None) + + def __repr__(self): + return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url) + + def __getattr__(self, name): + """ Search in self.environ for additional user defined attributes. """ + try: + var = self.environ['bottle.request.ext.%s' % name] + return var.__get__(self) if hasattr(var, '__get__') else var + except KeyError: + raise AttributeError('Attribute %r not defined.' % name) + + def __setattr__(self, name, value): + if name == 'environ': return object.__setattr__(self, name, value) + key = 'bottle.request.ext.%s' % name + if hasattr(self, name): + raise AttributeError("Attribute already defined: %s" % name) + self.environ[key] = value + + def __delattr__(self, name): + try: + del self.environ['bottle.request.ext.%s' % name] + except KeyError: + raise AttributeError("Attribute not defined: %s" % name) + + +def _hkey(key): + if '\n' in key or '\r' in key or '\0' in key: + raise ValueError("Header names must not contain control characters: %r" % key) + return key.title().replace('_', '-') + + +def _hval(value): + value = tonat(value) + if '\n' in value or '\r' in value or '\0' in value: + raise ValueError("Header value must not contain control characters: %r" % value) + return value + + +class HeaderProperty(object): + def __init__(self, name, reader=None, writer=None, default=''): + self.name, self.default = name, default + self.reader, self.writer = reader, writer + self.__doc__ = 'Current value of the %r header.' % name.title() + + def __get__(self, obj, _): + if obj is None: return self + value = obj.get_header(self.name, self.default) + return self.reader(value) if self.reader else value + + def __set__(self, obj, value): + obj[self.name] = self.writer(value) if self.writer else value + + def __delete__(self, obj): + del obj[self.name] + + +class BaseResponse(object): + """ Storage class for a response body as well as headers and cookies. + + This class does support dict-like case-insensitive item-access to + headers, but is NOT a dict. Most notably, iterating over a response + yields parts of the body and not the headers. + + :param body: The response body as one of the supported types. + :param status: Either an HTTP status code (e.g. 200) or a status line + including the reason phrase (e.g. '200 OK'). + :param headers: A dictionary or a list of name-value pairs. + + Additional keyword arguments are added to the list of headers. + Underscores in the header name are replaced with dashes. + """ + + default_status = 200 + default_content_type = 'text/html; charset=UTF-8' + + # Header denylist for specific response codes + # (rfc2616 section 10.2.3 and 10.3.5) + bad_headers = { + 204: frozenset(('Content-Type', 'Content-Length')), + 304: frozenset(('Allow', 'Content-Encoding', 'Content-Language', + 'Content-Length', 'Content-Range', 'Content-Type', + 'Content-Md5', 'Last-Modified')) + } + + def __init__(self, body='', status=None, headers=None, **more_headers): + self._cookies = None + self._headers = {} + self.body = body + self.status = status or self.default_status + if headers: + if isinstance(headers, dict): + headers = headers.items() + for name, value in headers: + self.add_header(name, value) + if more_headers: + for name, value in more_headers.items(): + self.add_header(name, value) + + def copy(self, cls=None): + """ Returns a copy of self. """ + cls = cls or BaseResponse + assert issubclass(cls, BaseResponse) + copy = cls() + copy.status = self.status + copy._headers = dict((k, v[:]) for (k, v) in self._headers.items()) + if self._cookies: + cookies = copy._cookies = SimpleCookie() + for k,v in self._cookies.items(): + cookies[k] = v.value + cookies[k].update(v) # also copy cookie attributes + return copy + + def __iter__(self): + return iter(self.body) + + def close(self): + if hasattr(self.body, 'close'): + self.body.close() + + @property + def status_line(self): + """ The HTTP status line as a string (e.g. ``404 Not Found``).""" + return self._status_line + + @property + def status_code(self): + """ The HTTP status code as an integer (e.g. 404).""" + return self._status_code + + def _set_status(self, status): + if isinstance(status, int): + code, status = status, _HTTP_STATUS_LINES.get(status) + elif ' ' in status: + if '\n' in status or '\r' in status or '\0' in status: + raise ValueError('Status line must not include control chars.') + status = status.strip() + code = int(status.split()[0]) + else: + raise ValueError('String status line without a reason phrase.') + if not 100 <= code <= 999: + raise ValueError('Status code out of range.') + self._status_code = code + self._status_line = str(status or ('%d Unknown' % code)) + + def _get_status(self): + return self._status_line + + status = property( + _get_status, _set_status, None, + ''' A writeable property to change the HTTP response status. It accepts + either a numeric code (100-999) or a string with a custom reason + phrase (e.g. "404 Brain not found"). Both :data:`status_line` and + :data:`status_code` are updated accordingly. The return value is + always a status string. ''') + del _get_status, _set_status + + @property + def headers(self): + """ An instance of :class:`HeaderDict`, a case-insensitive dict-like + view on the response headers. """ + hdict = HeaderDict() + hdict.dict = self._headers + return hdict + + def __contains__(self, name): + return _hkey(name) in self._headers + + def __delitem__(self, name): + del self._headers[_hkey(name)] + + def __getitem__(self, name): + return self._headers[_hkey(name)][-1] + + def __setitem__(self, name, value): + self._headers[_hkey(name)] = [_hval(value)] + + def get_header(self, name, default=None): + """ Return the value of a previously defined header. If there is no + header with that name, return a default value. """ + return self._headers.get(_hkey(name), [default])[-1] + + def set_header(self, name, value): + """ Create a new response header, replacing any previously defined + headers with the same name. """ + self._headers[_hkey(name)] = [_hval(value)] + + def add_header(self, name, value): + """ Add an additional response header, not removing duplicates. """ + self._headers.setdefault(_hkey(name), []).append(_hval(value)) + + def iter_headers(self): + """ Yield (header, value) tuples, skipping headers that are not + allowed with the current response status code. """ + return self.headerlist + + def _wsgi_status_line(self): + """ WSGI conform status line (latin1-encodeable) """ + if py3k: + return self._status_line.encode('utf8').decode('latin1') + return self._status_line + + @property + def headerlist(self): + """ WSGI conform list of (header, value) tuples. """ + out = [] + headers = list(self._headers.items()) + if 'Content-Type' not in self._headers: + headers.append(('Content-Type', [self.default_content_type])) + if self._status_code in self.bad_headers: + bad_headers = self.bad_headers[self._status_code] + headers = [h for h in headers if h[0] not in bad_headers] + out += [(name, val) for (name, vals) in headers for val in vals] + if self._cookies: + for c in self._cookies.values(): + out.append(('Set-Cookie', _hval(c.OutputString()))) + if py3k: + out = [(k, v.encode('utf8').decode('latin1')) for (k, v) in out] + return out + + content_type = HeaderProperty('Content-Type') + content_length = HeaderProperty('Content-Length', reader=int, default=-1) + expires = HeaderProperty( + 'Expires', + reader=lambda x: datetime.utcfromtimestamp(parse_date(x)), + writer=lambda x: http_date(x)) + + @property + def charset(self, default='UTF-8'): + """ Return the charset specified in the content-type header (default: utf8). """ + if 'charset=' in self.content_type: + return self.content_type.split('charset=')[-1].split(';')[0].strip() + return default + + def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **options): + """ Create a new cookie or replace an old one. If the `secret` parameter is + set, create a `Signed Cookie` (described below). + + :param name: the name of the cookie. + :param value: the value of the cookie. + :param secret: a signature key required for signed cookies. + + Additionally, this method accepts all RFC 2109 attributes that are + supported by :class:`cookie.Morsel`, including: + + :param maxage: maximum age in seconds. (default: None) + :param expires: a datetime object or UNIX timestamp. (default: None) + :param domain: the domain that is allowed to read the cookie. + (default: current domain) + :param path: limits the cookie to a given path (default: current path) + :param secure: limit the cookie to HTTPS connections (default: off). + :param httponly: prevents client-side javascript to read this cookie + (default: off, requires Python 2.6 or newer). + :param samesite: Control or disable third-party use for this cookie. + Possible values: `lax`, `strict` or `none` (default). + + If neither `expires` nor `maxage` is set (default), the cookie will + expire at the end of the browser session (as soon as the browser + window is closed). + + Signed cookies may store any pickle-able object and are + cryptographically signed to prevent manipulation. Keep in mind that + cookies are limited to 4kb in most browsers. + + Warning: Pickle is a potentially dangerous format. If an attacker + gains access to the secret key, he could forge cookies that execute + code on server side if unpickled. Using pickle is discouraged and + support for it will be removed in later versions of bottle. + + Warning: Signed cookies are not encrypted (the client can still see + the content) and not copy-protected (the client can restore an old + cookie). The main intention is to make pickling and unpickling + save, not to store secret information at client side. + """ + if not self._cookies: + self._cookies = SimpleCookie() + + # Monkey-patch Cookie lib to support 'SameSite' parameter + # https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1 + if py < (3, 8, 0): + Morsel._reserved.setdefault('samesite', 'SameSite') + + if secret: + if not isinstance(value, basestring): + depr(0, 13, "Pickling of arbitrary objects into cookies is " + "deprecated.", "Only store strings in cookies. " + "JSON strings are fine, too.") + encoded = base64.b64encode(pickle.dumps([name, value], -1)) + sig = base64.b64encode(hmac.new(tob(secret), encoded, + digestmod=digestmod).digest()) + value = touni(tob('!') + sig + tob('?') + encoded) + elif not isinstance(value, basestring): + raise TypeError('Secret key required for non-string cookies.') + + # Cookie size plus options must not exceed 4kb. + if len(name) + len(value) > 3800: + raise ValueError('Content does not fit into a cookie.') + + self._cookies[name] = value + + for key, value in options.items(): + if key in ('max_age', 'maxage'): # 'maxage' variant added in 0.13 + key = 'max-age' + if isinstance(value, timedelta): + value = value.seconds + value.days * 24 * 3600 + if key == 'expires': + value = http_date(value) + if key in ('same_site', 'samesite'): # 'samesite' variant added in 0.13 + key, value = 'samesite', (value or "none").lower() + if value not in ('lax', 'strict', 'none'): + raise CookieError("Invalid value for SameSite") + if key in ('secure', 'httponly') and not value: + continue + self._cookies[name][key] = value + + def delete_cookie(self, key, **kwargs): + """ Delete a cookie. Be sure to use the same `domain` and `path` + settings as used to create the cookie. """ + kwargs['max_age'] = -1 + kwargs['expires'] = 0 + self.set_cookie(key, '', **kwargs) + + def __repr__(self): + out = '' + for name, value in self.headerlist: + out += '%s: %s\n' % (name.title(), value.strip()) + return out + + +def _local_property(): + ls = threading.local() + + def fget(_): + try: + return ls.var + except AttributeError: + raise RuntimeError("Request context not initialized.") + + def fset(_, value): + ls.var = value + + def fdel(_): + del ls.var + + return property(fget, fset, fdel, 'Thread-local property') + + +class LocalRequest(BaseRequest): + """ A thread-local subclass of :class:`BaseRequest` with a different + set of attributes for each thread. There is usually only one global + instance of this class (:data:`request`). If accessed during a + request/response cycle, this instance always refers to the *current* + request (even on a multithreaded server). """ + bind = BaseRequest.__init__ + environ = _local_property() + + +class LocalResponse(BaseResponse): + """ A thread-local subclass of :class:`BaseResponse` with a different + set of attributes for each thread. There is usually only one global + instance of this class (:data:`response`). Its attributes are used + to build the HTTP response at the end of the request/response cycle. + """ + bind = BaseResponse.__init__ + _status_line = _local_property() + _status_code = _local_property() + _cookies = _local_property() + _headers = _local_property() + body = _local_property() + + +Request = BaseRequest +Response = BaseResponse + + +class HTTPResponse(Response, BottleException): + def __init__(self, body='', status=None, headers=None, **more_headers): + super(HTTPResponse, self).__init__(body, status, headers, **more_headers) + + def apply(self, other): + other._status_code = self._status_code + other._status_line = self._status_line + other._headers = self._headers + other._cookies = self._cookies + other.body = self.body + + +class HTTPError(HTTPResponse): + default_status = 500 + + def __init__(self, + status=None, + body=None, + exception=None, + traceback=None, **more_headers): + self.exception = exception + self.traceback = traceback + super(HTTPError, self).__init__(body, status, **more_headers) + +############################################################################### +# Plugins ###################################################################### +############################################################################### + + +class PluginError(BottleException): + pass + + +class JSONPlugin(object): + name = 'json' + api = 2 + + def __init__(self, json_dumps=json_dumps): + self.json_dumps = json_dumps + + def setup(self, app): + app.config._define('json.enable', default=True, validate=bool, + help="Enable or disable automatic dict->json filter.") + app.config._define('json.ascii', default=False, validate=bool, + help="Use only 7-bit ASCII characters in output.") + app.config._define('json.indent', default=True, validate=bool, + help="Add whitespace to make json more readable.") + app.config._define('json.dump_func', default=None, + help="If defined, use this function to transform" + " dict into json. The other options no longer" + " apply.") + + def apply(self, callback, route): + dumps = self.json_dumps + if not self.json_dumps: return callback + + def wrapper(*a, **ka): + try: + rv = callback(*a, **ka) + except HTTPResponse as resp: + rv = resp + + if isinstance(rv, dict): + #Attempt to serialize, raises exception on failure + json_response = dumps(rv) + #Set content type only if serialization successful + response.content_type = 'application/json' + return json_response + elif isinstance(rv, HTTPResponse) and isinstance(rv.body, dict): + rv.body = dumps(rv.body) + rv.content_type = 'application/json' + return rv + + return wrapper + + +class TemplatePlugin(object): + """ This plugin applies the :func:`view` decorator to all routes with a + `template` config parameter. If the parameter is a tuple, the second + element must be a dict with additional options (e.g. `template_engine`) + or default variables for the template. """ + name = 'template' + api = 2 + + def setup(self, app): + app.tpl = self + + def apply(self, callback, route): + conf = route.config.get('template') + if isinstance(conf, (tuple, list)) and len(conf) == 2: + return view(conf[0], **conf[1])(callback) + elif isinstance(conf, str): + return view(conf)(callback) + else: + return callback + + +#: Not a plugin, but part of the plugin API. TODO: Find a better place. +class _ImportRedirect(object): + def __init__(self, name, impmask): + """ Create a virtual package that redirects imports (see PEP 302). """ + self.name = name + self.impmask = impmask + self.module = sys.modules.setdefault(name, imp.new_module(name)) + self.module.__dict__.update({ + '__file__': __file__, + '__path__': [], + '__all__': [], + '__loader__': self + }) + sys.meta_path.append(self) + + def find_module(self, fullname, path=None): + if '.' not in fullname: return + packname = fullname.rsplit('.', 1)[0] + if packname != self.name: return + return self + + def load_module(self, fullname): + if fullname in sys.modules: return sys.modules[fullname] + modname = fullname.rsplit('.', 1)[1] + realname = self.impmask % modname + __import__(realname) + module = sys.modules[fullname] = sys.modules[realname] + setattr(self.module, modname, module) + module.__loader__ = self + return module + +############################################################################### +# Common Utilities ############################################################# +############################################################################### + + +class MultiDict(DictMixin): + """ This dict stores multiple values per key, but behaves exactly like a + normal dict in that it returns only the newest value for any given key. + There are special methods available to access the full list of values. + """ + + def __init__(self, *a, **k): + self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items()) + + def __len__(self): + return len(self.dict) + + def __iter__(self): + return iter(self.dict) + + def __contains__(self, key): + return key in self.dict + + def __delitem__(self, key): + del self.dict[key] + + def __getitem__(self, key): + return self.dict[key][-1] + + def __setitem__(self, key, value): + self.append(key, value) + + def keys(self): + return self.dict.keys() + + if py3k: + + def values(self): + return (v[-1] for v in self.dict.values()) + + def items(self): + return ((k, v[-1]) for k, v in self.dict.items()) + + def allitems(self): + return ((k, v) for k, vl in self.dict.items() for v in vl) + + iterkeys = keys + itervalues = values + iteritems = items + iterallitems = allitems + + else: + + def values(self): + return [v[-1] for v in self.dict.values()] + + def items(self): + return [(k, v[-1]) for k, v in self.dict.items()] + + def iterkeys(self): + return self.dict.iterkeys() + + def itervalues(self): + return (v[-1] for v in self.dict.itervalues()) + + def iteritems(self): + return ((k, v[-1]) for k, v in self.dict.iteritems()) + + def iterallitems(self): + return ((k, v) for k, vl in self.dict.iteritems() for v in vl) + + def allitems(self): + return [(k, v) for k, vl in self.dict.iteritems() for v in vl] + + def get(self, key, default=None, index=-1, type=None): + """ Return the most recent value for a key. + + :param default: The default value to be returned if the key is not + present or the type conversion fails. + :param index: An index for the list of available values. + :param type: If defined, this callable is used to cast the value + into a specific type. Exception are suppressed and result in + the default value to be returned. + """ + try: + val = self.dict[key][index] + return type(val) if type else val + except Exception: + pass + return default + + def append(self, key, value): + """ Add a new value to the list of values for this key. """ + self.dict.setdefault(key, []).append(value) + + def replace(self, key, value): + """ Replace the list of values with a single value. """ + self.dict[key] = [value] + + def getall(self, key): + """ Return a (possibly empty) list of values for a key. """ + return self.dict.get(key) or [] + + #: Aliases for WTForms to mimic other multi-dict APIs (Django) + getone = get + getlist = getall + + +class FormsDict(MultiDict): + """ This :class:`MultiDict` subclass is used to store request form data. + Additionally to the normal dict-like item access methods (which return + unmodified data as native strings), this container also supports + attribute-like access to its values. Attributes are automatically de- + or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing + attributes default to an empty string. """ + + #: Encoding used for attribute values. + input_encoding = 'utf8' + #: If true (default), unicode strings are first encoded with `latin1` + #: and then decoded to match :attr:`input_encoding`. + recode_unicode = True + + def _fix(self, s, encoding=None): + if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI + return s.encode('latin1').decode(encoding or self.input_encoding) + elif isinstance(s, bytes): # Python 2 WSGI + return s.decode(encoding or self.input_encoding) + else: + return s + + def decode(self, encoding=None): + """ Returns a copy with all keys and values de- or recoded to match + :attr:`input_encoding`. Some libraries (e.g. WTForms) want a + unicode dictionary. """ + copy = FormsDict() + enc = copy.input_encoding = encoding or self.input_encoding + copy.recode_unicode = False + for key, value in self.allitems(): + copy.append(self._fix(key, enc), self._fix(value, enc)) + return copy + + def getunicode(self, name, default=None, encoding=None): + """ Return the value as a unicode string, or the default. """ + try: + return self._fix(self[name], encoding) + except (UnicodeError, KeyError): + return default + + def __getattr__(self, name, default=unicode()): + # Without this guard, pickle generates a cryptic TypeError: + if name.startswith('__') and name.endswith('__'): + return super(FormsDict, self).__getattr__(name) + return self.getunicode(name, default=default) + +class HeaderDict(MultiDict): + """ A case-insensitive version of :class:`MultiDict` that defaults to + replace the old value instead of appending it. """ + + def __init__(self, *a, **ka): + self.dict = {} + if a or ka: self.update(*a, **ka) + + def __contains__(self, key): + return _hkey(key) in self.dict + + def __delitem__(self, key): + del self.dict[_hkey(key)] + + def __getitem__(self, key): + return self.dict[_hkey(key)][-1] + + def __setitem__(self, key, value): + self.dict[_hkey(key)] = [_hval(value)] + + def append(self, key, value): + self.dict.setdefault(_hkey(key), []).append(_hval(value)) + + def replace(self, key, value): + self.dict[_hkey(key)] = [_hval(value)] + + def getall(self, key): + return self.dict.get(_hkey(key)) or [] + + def get(self, key, default=None, index=-1): + return MultiDict.get(self, _hkey(key), default, index) + + def filter(self, names): + for name in (_hkey(n) for n in names): + if name in self.dict: + del self.dict[name] + + +class WSGIHeaderDict(DictMixin): + """ This dict-like class wraps a WSGI environ dict and provides convenient + access to HTTP_* fields. Keys and values are native strings + (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI + environment contains non-native string values, these are de- or encoded + using a lossless 'latin1' character set. + + The API will remain stable even on changes to the relevant PEPs. + Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one + that uses non-native strings.) + """ + #: List of keys that do not have a ``HTTP_`` prefix. + cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH') + + def __init__(self, environ): + self.environ = environ + + def _ekey(self, key): + """ Translate header field name to CGI/WSGI environ key. """ + key = key.replace('-', '_').upper() + if key in self.cgikeys: + return key + return 'HTTP_' + key + + def raw(self, key, default=None): + """ Return the header value as is (may be bytes or unicode). """ + return self.environ.get(self._ekey(key), default) + + def __getitem__(self, key): + val = self.environ[self._ekey(key)] + if py3k: + if isinstance(val, unicode): + val = val.encode('latin1').decode('utf8') + else: + val = val.decode('utf8') + return val + + def __setitem__(self, key, value): + raise TypeError("%s is read-only." % self.__class__) + + def __delitem__(self, key): + raise TypeError("%s is read-only." % self.__class__) + + def __iter__(self): + for key in self.environ: + if key[:5] == 'HTTP_': + yield _hkey(key[5:]) + elif key in self.cgikeys: + yield _hkey(key) + + def keys(self): + return [x for x in self] + + def __len__(self): + return len(self.keys()) + + def __contains__(self, key): + return self._ekey(key) in self.environ + +_UNSET = object() + +class ConfigDict(dict): + """ A dict-like configuration storage with additional support for + namespaces, validators, meta-data, overlays and more. + + This dict-like class is heavily optimized for read access. All read-only + methods as well as item access should be as fast as the built-in dict. + """ + + __slots__ = ('_meta', '_change_listener', '_overlays', '_virtual_keys', '_source', '__weakref__') + + def __init__(self): + self._meta = {} + self._change_listener = [] + #: Weak references of overlays that need to be kept in sync. + self._overlays = [] + #: Config that is the source for this overlay. + self._source = None + #: Keys of values copied from the source (values we do not own) + self._virtual_keys = set() + + def load_module(self, path, squash=True): + """Load values from a Python module. + + Example modue ``config.py``:: + + DEBUG = True + SQLITE = { + "db": ":memory:" + } + + + >>> c = ConfigDict() + >>> c.load_module('config') + {DEBUG: True, 'SQLITE.DB': 'memory'} + >>> c.load_module("config", False) + {'DEBUG': True, 'SQLITE': {'DB': 'memory'}} + + :param squash: If true (default), dictionary values are assumed to + represent namespaces (see :meth:`load_dict`). + """ + config_obj = load(path) + obj = {key: getattr(config_obj, key) for key in dir(config_obj) + if key.isupper()} + + if squash: + self.load_dict(obj) + else: + self.update(obj) + return self + + def load_config(self, filename, **options): + """ Load values from an ``*.ini`` style config file. + + A configuration file consists of sections, each led by a + ``[section]`` header, followed by key/value entries separated by + either ``=`` or ``:``. Section names and keys are case-insensitive. + Leading and trailing whitespace is removed from keys and values. + Values can be omitted, in which case the key/value delimiter may + also be left out. Values can also span multiple lines, as long as + they are indented deeper than the first line of the value. Commands + are prefixed by ``#`` or ``;`` and may only appear on their own on + an otherwise empty line. + + Both section and key names may contain dots (``.``) as namespace + separators. The actual configuration parameter name is constructed + by joining section name and key name together and converting to + lower case. + + The special sections ``bottle`` and ``ROOT`` refer to the root + namespace and the ``DEFAULT`` section defines default values for all + other sections. + + With Python 3, extended string interpolation is enabled. + + :param filename: The path of a config file, or a list of paths. + :param options: All keyword parameters are passed to the underlying + :class:`python:configparser.ConfigParser` constructor call. + + """ + options.setdefault('allow_no_value', True) + if py3k: + options.setdefault('interpolation', + configparser.ExtendedInterpolation()) + conf = configparser.ConfigParser(**options) + conf.read(filename) + for section in conf.sections(): + for key in conf.options(section): + value = conf.get(section, key) + if section not in ('bottle', 'ROOT'): + key = section + '.' + key + self[key.lower()] = value + return self + + def load_dict(self, source, namespace=''): + """ Load values from a dictionary structure. Nesting can be used to + represent namespaces. + + >>> c = ConfigDict() + >>> c.load_dict({'some': {'namespace': {'key': 'value'} } }) + {'some.namespace.key': 'value'} + """ + for key, value in source.items(): + if isinstance(key, basestring): + nskey = (namespace + '.' + key).strip('.') + if isinstance(value, dict): + self.load_dict(value, namespace=nskey) + else: + self[nskey] = value + else: + raise TypeError('Key has type %r (not a string)' % type(key)) + return self + + def update(self, *a, **ka): + """ If the first parameter is a string, all keys are prefixed with this + namespace. Apart from that it works just as the usual dict.update(). + + >>> c = ConfigDict() + >>> c.update('some.namespace', key='value') + """ + prefix = '' + if a and isinstance(a[0], basestring): + prefix = a[0].strip('.') + '.' + a = a[1:] + for key, value in dict(*a, **ka).items(): + self[prefix + key] = value + + def setdefault(self, key, value): + if key not in self: + self[key] = value + return self[key] + + def __setitem__(self, key, value): + if not isinstance(key, basestring): + raise TypeError('Key has type %r (not a string)' % type(key)) + + self._virtual_keys.discard(key) + + value = self.meta_get(key, 'filter', lambda x: x)(value) + if key in self and self[key] is value: + return + + self._on_change(key, value) + dict.__setitem__(self, key, value) + + for overlay in self._iter_overlays(): + overlay._set_virtual(key, value) + + def __delitem__(self, key): + if key not in self: + raise KeyError(key) + if key in self._virtual_keys: + raise KeyError("Virtual keys cannot be deleted: %s" % key) + + if self._source and key in self._source: + # Not virtual, but present in source -> Restore virtual value + dict.__delitem__(self, key) + self._set_virtual(key, self._source[key]) + else: # not virtual, not present in source. This is OUR value + self._on_change(key, None) + dict.__delitem__(self, key) + for overlay in self._iter_overlays(): + overlay._delete_virtual(key) + + def _set_virtual(self, key, value): + """ Recursively set or update virtual keys. Do nothing if non-virtual + value is present. """ + if key in self and key not in self._virtual_keys: + return # Do nothing for non-virtual keys. + + self._virtual_keys.add(key) + if key in self and self[key] is not value: + self._on_change(key, value) + dict.__setitem__(self, key, value) + for overlay in self._iter_overlays(): + overlay._set_virtual(key, value) + + def _delete_virtual(self, key): + """ Recursively delete virtual entry. Do nothing if key is not virtual. + """ + if key not in self._virtual_keys: + return # Do nothing for non-virtual keys. + + if key in self: + self._on_change(key, None) + dict.__delitem__(self, key) + self._virtual_keys.discard(key) + for overlay in self._iter_overlays(): + overlay._delete_virtual(key) + + def _on_change(self, key, value): + for cb in self._change_listener: + if cb(self, key, value): + return True + + def _add_change_listener(self, func): + self._change_listener.append(func) + return func + + def meta_get(self, key, metafield, default=None): + """ Return the value of a meta field for a key. """ + return self._meta.get(key, {}).get(metafield, default) + + def meta_set(self, key, metafield, value): + """ Set the meta field for a key to a new value. """ + self._meta.setdefault(key, {})[metafield] = value + + def meta_list(self, key): + """ Return an iterable of meta field names defined for a key. """ + return self._meta.get(key, {}).keys() + + def _define(self, key, default=_UNSET, help=_UNSET, validate=_UNSET): + """ (Unstable) Shortcut for plugins to define own config parameters. """ + if default is not _UNSET: + self.setdefault(key, default) + if help is not _UNSET: + self.meta_set(key, 'help', help) + if validate is not _UNSET: + self.meta_set(key, 'validate', validate) + + def _iter_overlays(self): + for ref in self._overlays: + overlay = ref() + if overlay is not None: + yield overlay + + def _make_overlay(self): + """ (Unstable) Create a new overlay that acts like a chained map: Values + missing in the overlay are copied from the source map. Both maps + share the same meta entries. + + Entries that were copied from the source are called 'virtual'. You + can not delete virtual keys, but overwrite them, which turns them + into non-virtual entries. Setting keys on an overlay never affects + its source, but may affect any number of child overlays. + + Other than collections.ChainMap or most other implementations, this + approach does not resolve missing keys on demand, but instead + actively copies all values from the source to the overlay and keeps + track of virtual and non-virtual keys internally. This removes any + lookup-overhead. Read-access is as fast as a build-in dict for both + virtual and non-virtual keys. + + Changes are propagated recursively and depth-first. A failing + on-change handler in an overlay stops the propagation of virtual + values and may result in an partly updated tree. Take extra care + here and make sure that on-change handlers never fail. + + Used by Route.config + """ + # Cleanup dead references + self._overlays[:] = [ref for ref in self._overlays if ref() is not None] + + overlay = ConfigDict() + overlay._meta = self._meta + overlay._source = self + self._overlays.append(weakref.ref(overlay)) + for key in self: + overlay._set_virtual(key, self[key]) + return overlay + + + + +class AppStack(list): + """ A stack-like list. Calling it returns the head of the stack. """ + + def __call__(self): + """ Return the current default application. """ + return self.default + + def push(self, value=None): + """ Add a new :class:`Bottle` instance to the stack """ + if not isinstance(value, Bottle): + value = Bottle() + self.append(value) + return value + new_app = push + + @property + def default(self): + try: + return self[-1] + except IndexError: + return self.push() + + +class WSGIFileWrapper(object): + def __init__(self, fp, buffer_size=1024 * 64): + self.fp, self.buffer_size = fp, buffer_size + for attr in 'fileno', 'close', 'read', 'readlines', 'tell', 'seek': + if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr)) + + def __iter__(self): + buff, read = self.buffer_size, self.read + part = read(buff) + while part: + yield part + part = read(buff) + + +class _closeiter(object): + """ This only exists to be able to attach a .close method to iterators that + do not support attribute assignment (most of itertools). """ + + def __init__(self, iterator, close=None): + self.iterator = iterator + self.close_callbacks = makelist(close) + + def __iter__(self): + return iter(self.iterator) + + def close(self): + for func in self.close_callbacks: + func() + + +class ResourceManager(object): + """ This class manages a list of search paths and helps to find and open + application-bound resources (files). + + :param base: default value for :meth:`add_path` calls. + :param opener: callable used to open resources. + :param cachemode: controls which lookups are cached. One of 'all', + 'found' or 'none'. + """ + + def __init__(self, base='./', opener=open, cachemode='all'): + self.opener = opener + self.base = base + self.cachemode = cachemode + + #: A list of search paths. See :meth:`add_path` for details. + self.path = [] + #: A cache for resolved paths. ``res.cache.clear()`` clears the cache. + self.cache = {} + + def add_path(self, path, base=None, index=None, create=False): + """ Add a new path to the list of search paths. Return False if the + path does not exist. + + :param path: The new search path. Relative paths are turned into + an absolute and normalized form. If the path looks like a file + (not ending in `/`), the filename is stripped off. + :param base: Path used to absolutize relative search paths. + Defaults to :attr:`base` which defaults to ``os.getcwd()``. + :param index: Position within the list of search paths. Defaults + to last index (appends to the list). + + The `base` parameter makes it easy to reference files installed + along with a python module or package:: + + res.add_path('./resources/', __file__) + """ + base = os.path.abspath(os.path.dirname(base or self.base)) + path = os.path.abspath(os.path.join(base, os.path.dirname(path))) + path += os.sep + if path in self.path: + self.path.remove(path) + if create and not os.path.isdir(path): + os.makedirs(path) + if index is None: + self.path.append(path) + else: + self.path.insert(index, path) + self.cache.clear() + return os.path.exists(path) + + def __iter__(self): + """ Iterate over all existing files in all registered paths. """ + search = self.path[:] + while search: + path = search.pop() + if not os.path.isdir(path): continue + for name in os.listdir(path): + full = os.path.join(path, name) + if os.path.isdir(full): search.append(full) + else: yield full + + def lookup(self, name): + """ Search for a resource and return an absolute file path, or `None`. + + The :attr:`path` list is searched in order. The first match is + returned. Symlinks are followed. The result is cached to speed up + future lookups. """ + if name not in self.cache or DEBUG: + for path in self.path: + fpath = os.path.join(path, name) + if os.path.isfile(fpath): + if self.cachemode in ('all', 'found'): + self.cache[name] = fpath + return fpath + if self.cachemode == 'all': + self.cache[name] = None + return self.cache[name] + + def open(self, name, mode='r', *args, **kwargs): + """ Find a resource and return a file object, or raise IOError. """ + fname = self.lookup(name) + if not fname: raise IOError("Resource %r not found." % name) + return self.opener(fname, mode=mode, *args, **kwargs) + + +class FileUpload(object): + def __init__(self, fileobj, name, filename, headers=None): + """ Wrapper for file uploads. """ + #: Open file(-like) object (BytesIO buffer or temporary file) + self.file = fileobj + #: Name of the upload form field + self.name = name + #: Raw filename as sent by the client (may contain unsafe characters) + self.raw_filename = filename + #: A :class:`HeaderDict` with additional headers (e.g. content-type) + self.headers = HeaderDict(headers) if headers else HeaderDict() + + content_type = HeaderProperty('Content-Type') + content_length = HeaderProperty('Content-Length', reader=int, default=-1) + + def get_header(self, name, default=None): + """ Return the value of a header within the multipart part. """ + return self.headers.get(name, default) + + @cached_property + def filename(self): + """ Name of the file on the client file system, but normalized to ensure + file system compatibility. An empty filename is returned as 'empty'. + + Only ASCII letters, digits, dashes, underscores and dots are + allowed in the final filename. Accents are removed, if possible. + Whitespace is replaced by a single dash. Leading or tailing dots + or dashes are removed. The filename is limited to 255 characters. + """ + fname = self.raw_filename + if not isinstance(fname, unicode): + fname = fname.decode('utf8', 'ignore') + fname = normalize('NFKD', fname) + fname = fname.encode('ASCII', 'ignore').decode('ASCII') + fname = os.path.basename(fname.replace('\\', os.path.sep)) + fname = re.sub(r'[^a-zA-Z0-9-_.\s]', '', fname).strip() + fname = re.sub(r'[-\s]+', '-', fname).strip('.-') + return fname[:255] or 'empty' + + def _copy_file(self, fp, chunk_size=2 ** 16): + read, write, offset = self.file.read, fp.write, self.file.tell() + while 1: + buf = read(chunk_size) + if not buf: break + write(buf) + self.file.seek(offset) + + def save(self, destination, overwrite=False, chunk_size=2 ** 16): + """ Save file to disk or copy its content to an open file(-like) object. + If *destination* is a directory, :attr:`filename` is added to the + path. Existing files are not overwritten by default (IOError). + + :param destination: File path, directory or file(-like) object. + :param overwrite: If True, replace existing files. (default: False) + :param chunk_size: Bytes to read at a time. (default: 64kb) + """ + if isinstance(destination, basestring): # Except file-likes here + if os.path.isdir(destination): + destination = os.path.join(destination, self.filename) + if not overwrite and os.path.exists(destination): + raise IOError('File exists.') + with open(destination, 'wb') as fp: + self._copy_file(fp, chunk_size) + else: + self._copy_file(destination, chunk_size) + +############################################################################### +# Application Helper ########################################################### +############################################################################### + + +def abort(code=500, text='Unknown Error.'): + """ Aborts execution and causes a HTTP error. """ + raise HTTPError(code, text) + + +def redirect(url, code=None): + """ Aborts execution and causes a 303 or 302 redirect, depending on + the HTTP protocol version. """ + if not code: + code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302 + res = response.copy(cls=HTTPResponse) + res.status = code + res.body = "" + res.set_header('Location', urljoin(request.url, url)) + raise res + + +def _file_iter_range(fp, offset, bytes, maxread=1024 * 1024, close=False): + """ Yield chunks from a range in a file, optionally closing it at the end. + No chunk is bigger than maxread. """ + fp.seek(offset) + while bytes > 0: + part = fp.read(min(bytes, maxread)) + if not part: + break + bytes -= len(part) + yield part + if close: + fp.close() + + +def static_file(filename, root, + mimetype=True, + download=False, + charset='UTF-8', + etag=None, + headers=None): + """ Open a file in a safe way and return an instance of :exc:`HTTPResponse` + that can be sent back to the client. + + :param filename: Name or path of the file to send, relative to ``root``. + :param root: Root path for file lookups. Should be an absolute directory + path. + :param mimetype: Provide the content-type header (default: guess from + file extension) + :param download: If True, ask the browser to open a `Save as...` dialog + instead of opening the file with the associated program. You can + specify a custom filename as a string. If not specified, the + original filename is used (default: False). + :param charset: The charset for files with a ``text/*`` mime-type. + (default: UTF-8) + :param etag: Provide a pre-computed ETag header. If set to ``False``, + ETag handling is disabled. (default: auto-generate ETag header) + :param headers: Additional headers dict to add to the response. + + While checking user input is always a good idea, this function provides + additional protection against malicious ``filename`` parameters from + breaking out of the ``root`` directory and leaking sensitive information + to an attacker. + + Read-protected files or files outside of the ``root`` directory are + answered with ``403 Access Denied``. Missing files result in a + ``404 Not Found`` response. Conditional requests (``If-Modified-Since``, + ``If-None-Match``) are answered with ``304 Not Modified`` whenever + possible. ``HEAD`` and ``Range`` requests (used by download managers to + check or continue partial downloads) are also handled automatically. + + """ + + root = os.path.join(os.path.abspath(root), '') + filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) + headers = headers.copy() if headers else {} + + if not filename.startswith(root): + return HTTPError(403, "Access denied.") + if not os.path.exists(filename) or not os.path.isfile(filename): + return HTTPError(404, "File does not exist.") + if not os.access(filename, os.R_OK): + return HTTPError(403, "You do not have permission to access this file.") + + if mimetype is True: + if download and download is not True: + mimetype, encoding = mimetypes.guess_type(download) + else: + mimetype, encoding = mimetypes.guess_type(filename) + if encoding: + headers['Content-Encoding'] = encoding + + if mimetype: + if (mimetype[:5] == 'text/' or mimetype == 'application/javascript')\ + and charset and 'charset' not in mimetype: + mimetype += '; charset=%s' % charset + headers['Content-Type'] = mimetype + + if download: + download = os.path.basename(filename if download is True else download) + headers['Content-Disposition'] = 'attachment; filename="%s"' % download + + stats = os.stat(filename) + headers['Content-Length'] = clen = stats.st_size + headers['Last-Modified'] = email.utils.formatdate(stats.st_mtime, + usegmt=True) + headers['Date'] = email.utils.formatdate(time.time(), usegmt=True) + + getenv = request.environ.get + + if etag is None: + etag = '%d:%d:%d:%d:%s' % (stats.st_dev, stats.st_ino, stats.st_mtime, + clen, filename) + etag = hashlib.sha1(tob(etag)).hexdigest() + + if etag: + headers['ETag'] = etag + check = getenv('HTTP_IF_NONE_MATCH') + if check and check == etag: + return HTTPResponse(status=304, **headers) + + ims = getenv('HTTP_IF_MODIFIED_SINCE') + if ims: + ims = parse_date(ims.split(";")[0].strip()) + if ims is not None and ims >= int(stats.st_mtime): + return HTTPResponse(status=304, **headers) + + body = '' if request.method == 'HEAD' else open(filename, 'rb') + + headers["Accept-Ranges"] = "bytes" + range_header = getenv('HTTP_RANGE') + if range_header: + ranges = list(parse_range_header(range_header, clen)) + if not ranges: + return HTTPError(416, "Requested Range Not Satisfiable") + offset, end = ranges[0] + headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1, clen) + headers["Content-Length"] = str(end - offset) + if body: body = _file_iter_range(body, offset, end - offset, close=True) + return HTTPResponse(body, status=206, **headers) + return HTTPResponse(body, **headers) + +############################################################################### +# HTTP Utilities and MISC (TODO) ############################################### +############################################################################### + + +def debug(mode=True): + """ Change the debug level. + There is only one debug level supported at the moment.""" + global DEBUG + if mode: warnings.simplefilter('default') + DEBUG = bool(mode) + + +def http_date(value): + if isinstance(value, basestring): + return value + if isinstance(value, datetime): + # aware datetime.datetime is converted to UTC time + # naive datetime.datetime is treated as UTC time + value = value.utctimetuple() + elif isinstance(value, datedate): + # datetime.date is naive, and is treated as UTC time + value = value.timetuple() + if not isinstance(value, (int, float)): + # convert struct_time in UTC to UNIX timestamp + value = calendar.timegm(value) + return email.utils.formatdate(value, usegmt=True) + + +def parse_date(ims): + """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """ + try: + ts = email.utils.parsedate_tz(ims) + return calendar.timegm(ts[:8] + (0, )) - (ts[9] or 0) + except (TypeError, ValueError, IndexError, OverflowError): + return None + + +def parse_auth(header): + """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None""" + try: + method, data = header.split(None, 1) + if method.lower() == 'basic': + user, pwd = touni(base64.b64decode(tob(data))).split(':', 1) + return user, pwd + except (KeyError, ValueError): + return None + + +def parse_range_header(header, maxlen=0): + """ Yield (start, end) ranges parsed from a HTTP Range header. Skip + unsatisfiable ranges. The end index is non-inclusive.""" + if not header or header[:6] != 'bytes=': return + ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r] + for start, end in ranges: + try: + if not start: # bytes=-100 -> last 100 bytes + start, end = max(0, maxlen - int(end)), maxlen + elif not end: # bytes=100- -> all but the first 99 bytes + start, end = int(start), maxlen + else: # bytes=100-200 -> bytes 100-200 (inclusive) + start, end = int(start), min(int(end) + 1, maxlen) + if 0 <= start < end <= maxlen: + yield start, end + except ValueError: + pass + + +#: Header tokenizer used by _parse_http_header() +_hsplit = re.compile('(?:(?:"((?:[^"\\\\]|\\\\.)*)")|([^;,=]+))([;,=]?)').findall + +def _parse_http_header(h): + """ Parses a typical multi-valued and parametrised HTTP header (e.g. Accept headers) and returns a list of values + and parameters. For non-standard or broken input, this implementation may return partial results. + :param h: A header string (e.g. ``text/html,text/plain;q=0.9,*/*;q=0.8``) + :return: List of (value, params) tuples. The second element is a (possibly empty) dict. + """ + values = [] + if '"' not in h: # INFO: Fast path without regexp (~2x faster) + for value in h.split(','): + parts = value.split(';') + values.append((parts[0].strip(), {})) + for attr in parts[1:]: + name, value = attr.split('=', 1) + values[-1][1][name.strip()] = value.strip() + else: + lop, key, attrs = ',', None, {} + for quoted, plain, tok in _hsplit(h): + value = plain.strip() if plain else quoted.replace('\\"', '"') + if lop == ',': + attrs = {} + values.append((value, attrs)) + elif lop == ';': + if tok == '=': + key = value + else: + attrs[value] = '' + elif lop == '=' and key: + attrs[key] = value + key = None + lop = tok + return values + + +def _parse_qsl(qs): + r = [] + for pair in qs.split('&'): + if not pair: continue + nv = pair.split('=', 1) + if len(nv) != 2: nv.append('') + key = urlunquote(nv[0].replace('+', ' ')) + value = urlunquote(nv[1].replace('+', ' ')) + r.append((key, value)) + return r + + +def _lscmp(a, b): + """ Compares two strings in a cryptographically safe way: + Runtime is not affected by length of common prefix. """ + return not sum(0 if x == y else 1 + for x, y in zip(a, b)) and len(a) == len(b) + + +def cookie_encode(data, key, digestmod=None): + """ Encode and sign a pickle-able object. Return a (byte) string """ + depr(0, 13, "cookie_encode() will be removed soon.", + "Do not use this API directly.") + digestmod = digestmod or hashlib.sha256 + msg = base64.b64encode(pickle.dumps(data, -1)) + sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=digestmod).digest()) + return tob('!') + sig + tob('?') + msg + + +def cookie_decode(data, key, digestmod=None): + """ Verify and decode an encoded string. Return an object or None.""" + depr(0, 13, "cookie_decode() will be removed soon.", + "Do not use this API directly.") + data = tob(data) + if cookie_is_encoded(data): + sig, msg = data.split(tob('?'), 1) + digestmod = digestmod or hashlib.sha256 + hashed = hmac.new(tob(key), msg, digestmod=digestmod).digest() + if _lscmp(sig[1:], base64.b64encode(hashed)): + return pickle.loads(base64.b64decode(msg)) + return None + + +def cookie_is_encoded(data): + """ Return True if the argument looks like a encoded cookie.""" + depr(0, 13, "cookie_is_encoded() will be removed soon.", + "Do not use this API directly.") + return bool(data.startswith(tob('!')) and tob('?') in data) + + +def html_escape(string): + """ Escape HTML special characters ``&<>`` and quotes ``'"``. """ + return string.replace('&', '&').replace('<', '<').replace('>', '>')\ + .replace('"', '"').replace("'", ''') + + +def html_quote(string): + """ Escape and quote a string to be used as an HTTP attribute.""" + return '"%s"' % html_escape(string).replace('\n', ' ')\ + .replace('\r', ' ').replace('\t', ' ') + + +def yieldroutes(func): + """ Return a generator for routes that match the signature (name, args) + of the func parameter. This may yield more than one route if the function + takes optional keyword arguments. The output is best described by example:: + + a() -> '/a' + b(x, y) -> '/b//' + c(x, y=5) -> '/c/' and '/c//' + d(x=5, y=6) -> '/d' and '/d/' and '/d//' + """ + path = '/' + func.__name__.replace('__', '/').lstrip('/') + spec = getargspec(func) + argc = len(spec[0]) - len(spec[3] or []) + path += ('/<%s>' * argc) % tuple(spec[0][:argc]) + yield path + for arg in spec[0][argc:]: + path += '/<%s>' % arg + yield path + + +def path_shift(script_name, path_info, shift=1): + """ Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa. + + :return: The modified paths. + :param script_name: The SCRIPT_NAME path. + :param script_name: The PATH_INFO path. + :param shift: The number of path fragments to shift. May be negative to + change the shift direction. (default: 1) + """ + if shift == 0: return script_name, path_info + pathlist = path_info.strip('/').split('/') + scriptlist = script_name.strip('/').split('/') + if pathlist and pathlist[0] == '': pathlist = [] + if scriptlist and scriptlist[0] == '': scriptlist = [] + if 0 < shift <= len(pathlist): + moved = pathlist[:shift] + scriptlist = scriptlist + moved + pathlist = pathlist[shift:] + elif 0 > shift >= -len(scriptlist): + moved = scriptlist[shift:] + pathlist = moved + pathlist + scriptlist = scriptlist[:shift] + else: + empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO' + raise AssertionError("Cannot shift. Nothing left from %s" % empty) + new_script_name = '/' + '/'.join(scriptlist) + new_path_info = '/' + '/'.join(pathlist) + if path_info.endswith('/') and pathlist: new_path_info += '/' + return new_script_name, new_path_info + + +def auth_basic(check, realm="private", text="Access denied"): + """ Callback decorator to require HTTP auth (basic). + TODO: Add route(check_auth=...) parameter. """ + + def decorator(func): + + @functools.wraps(func) + def wrapper(*a, **ka): + user, password = request.auth or (None, None) + if user is None or not check(user, password): + err = HTTPError(401, text) + err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm) + return err + return func(*a, **ka) + + return wrapper + + return decorator + +# Shortcuts for common Bottle methods. +# They all refer to the current default application. + + +def make_default_app_wrapper(name): + """ Return a callable that relays calls to the current default app. """ + + @functools.wraps(getattr(Bottle, name)) + def wrapper(*a, **ka): + return getattr(app(), name)(*a, **ka) + + return wrapper + + +route = make_default_app_wrapper('route') +get = make_default_app_wrapper('get') +post = make_default_app_wrapper('post') +put = make_default_app_wrapper('put') +delete = make_default_app_wrapper('delete') +patch = make_default_app_wrapper('patch') +error = make_default_app_wrapper('error') +mount = make_default_app_wrapper('mount') +hook = make_default_app_wrapper('hook') +install = make_default_app_wrapper('install') +uninstall = make_default_app_wrapper('uninstall') +url = make_default_app_wrapper('get_url') + +############################################################################### +# Server Adapter ############################################################### +############################################################################### + +# Before you edit or add a server adapter, please read: +# - https://github.com/bottlepy/bottle/pull/647#issuecomment-60152870 +# - https://github.com/bottlepy/bottle/pull/865#issuecomment-242795341 + +class ServerAdapter(object): + quiet = False + + def __init__(self, host='127.0.0.1', port=8080, **options): + self.options = options + self.host = host + self.port = int(port) + + def run(self, handler): # pragma: no cover + pass + + def __repr__(self): + args = ', '.join('%s=%s' % (k, repr(v)) + for k, v in self.options.items()) + return "%s(%s)" % (self.__class__.__name__, args) + + +class CGIServer(ServerAdapter): + quiet = True + + def run(self, handler): # pragma: no cover + from wsgiref.handlers import CGIHandler + + def fixed_environ(environ, start_response): + environ.setdefault('PATH_INFO', '') + return handler(environ, start_response) + + CGIHandler().run(fixed_environ) + + +class FlupFCGIServer(ServerAdapter): + def run(self, handler): # pragma: no cover + import flup.server.fcgi + self.options.setdefault('bindAddress', (self.host, self.port)) + flup.server.fcgi.WSGIServer(handler, **self.options).run() + + +class WSGIRefServer(ServerAdapter): + def run(self, app): # pragma: no cover + from wsgiref.simple_server import make_server + from wsgiref.simple_server import WSGIRequestHandler, WSGIServer + import socket + + class FixedHandler(WSGIRequestHandler): + def address_string(self): # Prevent reverse DNS lookups please. + return self.client_address[0] + + def log_request(*args, **kw): + if not self.quiet: + return WSGIRequestHandler.log_request(*args, **kw) + + handler_cls = self.options.get('handler_class', FixedHandler) + server_cls = self.options.get('server_class', WSGIServer) + + if ':' in self.host: # Fix wsgiref for IPv6 addresses. + if getattr(server_cls, 'address_family') == socket.AF_INET: + + class server_cls(server_cls): + address_family = socket.AF_INET6 + + self.srv = make_server(self.host, self.port, app, server_cls, + handler_cls) + self.port = self.srv.server_port # update port actual port (0 means random) + try: + self.srv.serve_forever() + except KeyboardInterrupt: + self.srv.server_close() # Prevent ResourceWarning: unclosed socket + raise + + +class CherryPyServer(ServerAdapter): + def run(self, handler): # pragma: no cover + depr(0, 13, "The wsgi server part of cherrypy was split into a new " + "project called 'cheroot'.", "Use the 'cheroot' server " + "adapter instead of cherrypy.") + from cherrypy import wsgiserver # This will fail for CherryPy >= 9 + + self.options['bind_addr'] = (self.host, self.port) + self.options['wsgi_app'] = handler + + certfile = self.options.get('certfile') + if certfile: + del self.options['certfile'] + keyfile = self.options.get('keyfile') + if keyfile: + del self.options['keyfile'] + + server = wsgiserver.CherryPyWSGIServer(**self.options) + if certfile: + server.ssl_certificate = certfile + if keyfile: + server.ssl_private_key = keyfile + + try: + server.start() + finally: + server.stop() + + +class CherootServer(ServerAdapter): + def run(self, handler): # pragma: no cover + from cheroot import wsgi + from cheroot.ssl import builtin + self.options['bind_addr'] = (self.host, self.port) + self.options['wsgi_app'] = handler + certfile = self.options.pop('certfile', None) + keyfile = self.options.pop('keyfile', None) + chainfile = self.options.pop('chainfile', None) + server = wsgi.Server(**self.options) + if certfile and keyfile: + server.ssl_adapter = builtin.BuiltinSSLAdapter( + certfile, keyfile, chainfile) + try: + server.start() + finally: + server.stop() + + +class WaitressServer(ServerAdapter): + def run(self, handler): + from waitress import serve + serve(handler, host=self.host, port=self.port, _quiet=self.quiet, **self.options) + + +class PasteServer(ServerAdapter): + def run(self, handler): # pragma: no cover + from paste import httpserver + from paste.translogger import TransLogger + handler = TransLogger(handler, setup_console_handler=(not self.quiet)) + httpserver.serve(handler, + host=self.host, + port=str(self.port), **self.options) + + +class MeinheldServer(ServerAdapter): + def run(self, handler): + from meinheld import server + server.listen((self.host, self.port)) + server.run(handler) + + +class FapwsServer(ServerAdapter): + """ Extremely fast webserver using libev. See http://www.fapws.org/ """ + + def run(self, handler): # pragma: no cover + depr(0, 13, "fapws3 is not maintained and support will be dropped.") + import fapws._evwsgi as evwsgi + from fapws import base, config + port = self.port + if float(config.SERVER_IDENT[-2:]) > 0.4: + # fapws3 silently changed its API in 0.5 + port = str(port) + evwsgi.start(self.host, port) + # fapws3 never releases the GIL. Complain upstream. I tried. No luck. + if 'BOTTLE_CHILD' in os.environ and not self.quiet: + _stderr("WARNING: Auto-reloading does not work with Fapws3.") + _stderr(" (Fapws3 breaks python thread support)") + evwsgi.set_base_module(base) + + def app(environ, start_response): + environ['wsgi.multiprocess'] = False + return handler(environ, start_response) + + evwsgi.wsgi_cb(('', app)) + evwsgi.run() + + +class TornadoServer(ServerAdapter): + """ The super hyped asynchronous server by facebook. Untested. """ + + def run(self, handler): # pragma: no cover + import tornado.wsgi, tornado.httpserver, tornado.ioloop + container = tornado.wsgi.WSGIContainer(handler) + server = tornado.httpserver.HTTPServer(container) + server.listen(port=self.port, address=self.host) + tornado.ioloop.IOLoop.instance().start() + + +class AppEngineServer(ServerAdapter): + """ Adapter for Google App Engine. """ + quiet = True + + def run(self, handler): + depr(0, 13, "AppEngineServer no longer required", + "Configure your application directly in your app.yaml") + from google.appengine.ext.webapp import util + # A main() function in the handler script enables 'App Caching'. + # Lets makes sure it is there. This _really_ improves performance. + module = sys.modules.get('__main__') + if module and not hasattr(module, 'main'): + module.main = lambda: util.run_wsgi_app(handler) + util.run_wsgi_app(handler) + + +class TwistedServer(ServerAdapter): + """ Untested. """ + + def run(self, handler): + from twisted.web import server, wsgi + from twisted.python.threadpool import ThreadPool + from twisted.internet import reactor + thread_pool = ThreadPool() + thread_pool.start() + reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop) + factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler)) + reactor.listenTCP(self.port, factory, interface=self.host) + if not reactor.running: + reactor.run() + + +class DieselServer(ServerAdapter): + """ Untested. """ + + def run(self, handler): + depr(0, 13, "Diesel is not tested or supported and will be removed.") + from diesel.protocols.wsgi import WSGIApplication + app = WSGIApplication(handler, port=self.port) + app.run() + + +class GeventServer(ServerAdapter): + """ Untested. Options: + + * See gevent.wsgi.WSGIServer() documentation for more options. + """ + + def run(self, handler): + from gevent import pywsgi, local + if not isinstance(threading.local(), local.local): + msg = "Bottle requires gevent.monkey.patch_all() (before import)" + raise RuntimeError(msg) + if self.quiet: + self.options['log'] = None + address = (self.host, self.port) + server = pywsgi.WSGIServer(address, handler, **self.options) + if 'BOTTLE_CHILD' in os.environ: + import signal + signal.signal(signal.SIGINT, lambda s, f: server.stop()) + server.serve_forever() + + +class GunicornServer(ServerAdapter): + """ Untested. See http://gunicorn.org/configure.html for options. """ + + def run(self, handler): + from gunicorn.app.base import BaseApplication + + if self.host.startswith("unix:"): + config = {'bind': self.host} + else: + config = {'bind': "%s:%d" % (self.host, self.port)} + + config.update(self.options) + + class GunicornApplication(BaseApplication): + def load_config(self): + for key, value in config.items(): + self.cfg.set(key, value) + + def load(self): + return handler + + GunicornApplication().run() + + +class EventletServer(ServerAdapter): + """ Untested. Options: + + * `backlog` adjust the eventlet backlog parameter which is the maximum + number of queued connections. Should be at least 1; the maximum + value is system-dependent. + * `family`: (default is 2) socket family, optional. See socket + documentation for available families. + """ + + def run(self, handler): + from eventlet import wsgi, listen, patcher + if not patcher.is_monkey_patched(os): + msg = "Bottle requires eventlet.monkey_patch() (before import)" + raise RuntimeError(msg) + socket_args = {} + for arg in ('backlog', 'family'): + try: + socket_args[arg] = self.options.pop(arg) + except KeyError: + pass + address = (self.host, self.port) + try: + wsgi.server(listen(address, **socket_args), handler, + log_output=(not self.quiet)) + except TypeError: + # Fallback, if we have old version of eventlet + wsgi.server(listen(address), handler) + + +class BjoernServer(ServerAdapter): + """ Fast server written in C: https://github.com/jonashaag/bjoern """ + + def run(self, handler): + from bjoern import run + run(handler, self.host, self.port, reuse_port=True) + +class AsyncioServerAdapter(ServerAdapter): + """ Extend ServerAdapter for adding custom event loop """ + def get_event_loop(self): + pass + +class AiohttpServer(AsyncioServerAdapter): + """ Asynchronous HTTP client/server framework for asyncio + https://pypi.python.org/pypi/aiohttp/ + https://pypi.org/project/aiohttp-wsgi/ + """ + + def get_event_loop(self): + import asyncio + return asyncio.new_event_loop() + + def run(self, handler): + import asyncio + from aiohttp_wsgi.wsgi import serve + self.loop = self.get_event_loop() + asyncio.set_event_loop(self.loop) + + if 'BOTTLE_CHILD' in os.environ: + import signal + signal.signal(signal.SIGINT, lambda s, f: self.loop.stop()) + + serve(handler, host=self.host, port=self.port) + + +class AiohttpUVLoopServer(AiohttpServer): + """uvloop + https://github.com/MagicStack/uvloop + """ + def get_event_loop(self): + import uvloop + return uvloop.new_event_loop() + +class AutoServer(ServerAdapter): + """ Untested. """ + adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, + CherootServer, WSGIRefServer] + + def run(self, handler): + for sa in self.adapters: + try: + return sa(self.host, self.port, **self.options).run(handler) + except ImportError: + pass + + +server_names = { + 'cgi': CGIServer, + 'flup': FlupFCGIServer, + 'wsgiref': WSGIRefServer, + 'waitress': WaitressServer, + 'cherrypy': CherryPyServer, + 'cheroot': CherootServer, + 'paste': PasteServer, + 'fapws3': FapwsServer, + 'tornado': TornadoServer, + 'gae': AppEngineServer, + 'twisted': TwistedServer, + 'diesel': DieselServer, + 'meinheld': MeinheldServer, + 'gunicorn': GunicornServer, + 'eventlet': EventletServer, + 'gevent': GeventServer, + 'bjoern': BjoernServer, + 'aiohttp': AiohttpServer, + 'uvloop': AiohttpUVLoopServer, + 'auto': AutoServer, +} + +############################################################################### +# Application Control ########################################################## +############################################################################### + + +def load(target, **namespace): + """ Import a module or fetch an object from a module. + + * ``package.module`` returns `module` as a module object. + * ``pack.mod:name`` returns the module variable `name` from `pack.mod`. + * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result. + + The last form accepts not only function calls, but any type of + expression. Keyword arguments passed to this function are available as + local variables. Example: ``import_string('re:compile(x)', x='[a-z]')`` + """ + module, target = target.split(":", 1) if ':' in target else (target, None) + if module not in sys.modules: __import__(module) + if not target: return sys.modules[module] + if target.isalnum(): return getattr(sys.modules[module], target) + package_name = module.split('.')[0] + namespace[package_name] = sys.modules[package_name] + return eval('%s.%s' % (module, target), namespace) + + +def load_app(target): + """ Load a bottle application from a module and make sure that the import + does not affect the current default application, but returns a separate + application object. See :func:`load` for the target parameter. """ + global NORUN + NORUN, nr_old = True, NORUN + tmp = default_app.push() # Create a new "default application" + try: + rv = load(target) # Import the target module + return rv if callable(rv) else tmp + finally: + default_app.remove(tmp) # Remove the temporary added default application + NORUN = nr_old + + +_debug = debug + + +def run(app=None, + server='wsgiref', + host='127.0.0.1', + port=8080, + interval=1, + reloader=False, + quiet=False, + plugins=None, + debug=None, + config=None, **kargs): + """ Start a server instance. This method blocks until the server terminates. + + :param app: WSGI application or target string supported by + :func:`load_app`. (default: :func:`default_app`) + :param server: Server adapter to use. See :data:`server_names` keys + for valid names or pass a :class:`ServerAdapter` subclass. + (default: `wsgiref`) + :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on + all interfaces including the external one. (default: 127.0.0.1) + :param port: Server port to bind to. Values below 1024 require root + privileges. (default: 8080) + :param reloader: Start auto-reloading server? (default: False) + :param interval: Auto-reloader interval in seconds (default: 1) + :param quiet: Suppress output to stdout and stderr? (default: False) + :param options: Options passed to the server adapter. + """ + if NORUN: return + if reloader and not os.environ.get('BOTTLE_CHILD'): + import subprocess + fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock') + environ = os.environ.copy() + environ['BOTTLE_CHILD'] = 'true' + environ['BOTTLE_LOCKFILE'] = lockfile + args = [sys.executable] + sys.argv + # If a package was loaded with `python -m`, then `sys.argv` needs to be + # restored to the original value, or imports might break. See #1336 + if getattr(sys.modules.get('__main__'), '__package__', None): + args[1:1] = ["-m", sys.modules['__main__'].__package__] + + try: + os.close(fd) # We never write to this file + while os.path.exists(lockfile): + p = subprocess.Popen(args, env=environ) + while p.poll() is None: + os.utime(lockfile, None) # Tell child we are still alive + time.sleep(interval) + if p.returncode == 3: # Child wants to be restarted + continue + sys.exit(p.returncode) + except KeyboardInterrupt: + pass + finally: + if os.path.exists(lockfile): + os.unlink(lockfile) + return + + try: + if debug is not None: _debug(debug) + app = app or default_app() + if isinstance(app, basestring): + app = load_app(app) + if not callable(app): + raise ValueError("Application is not callable: %r" % app) + + for plugin in plugins or []: + if isinstance(plugin, basestring): + plugin = load(plugin) + app.install(plugin) + + if config: + app.config.update(config) + + if server in server_names: + server = server_names.get(server) + if isinstance(server, basestring): + server = load(server) + if isinstance(server, type): + server = server(host=host, port=port, **kargs) + if not isinstance(server, ServerAdapter): + raise ValueError("Unknown or unsupported server: %r" % server) + + server.quiet = server.quiet or quiet + if not server.quiet: + _stderr("Bottle v%s server starting up (using %s)..." % + (__version__, repr(server))) + if server.host.startswith("unix:"): + _stderr("Listening on %s" % server.host) + else: + _stderr("Listening on http://%s:%d/" % + (server.host, server.port)) + _stderr("Hit Ctrl-C to quit.\n") + + if reloader: + lockfile = os.environ.get('BOTTLE_LOCKFILE') + bgcheck = FileCheckerThread(lockfile, interval) + with bgcheck: + server.run(app) + if bgcheck.status == 'reload': + sys.exit(3) + else: + server.run(app) + except KeyboardInterrupt: + pass + except (SystemExit, MemoryError): + raise + except: + if not reloader: raise + if not getattr(server, 'quiet', quiet): + print_exc() + time.sleep(interval) + sys.exit(3) + + +class FileCheckerThread(threading.Thread): + """ Interrupt main-thread as soon as a changed module file is detected, + the lockfile gets deleted or gets too old. """ + + def __init__(self, lockfile, interval): + threading.Thread.__init__(self) + self.daemon = True + self.lockfile, self.interval = lockfile, interval + #: Is one of 'reload', 'error' or 'exit' + self.status = None + + def run(self): + exists = os.path.exists + mtime = lambda p: os.stat(p).st_mtime + files = dict() + + for module in list(sys.modules.values()): + path = getattr(module, '__file__', '') or '' + if path[-4:] in ('.pyo', '.pyc'): path = path[:-1] + if path and exists(path): files[path] = mtime(path) + + while not self.status: + if not exists(self.lockfile)\ + or mtime(self.lockfile) < time.time() - self.interval - 5: + self.status = 'error' + thread.interrupt_main() + for path, lmtime in list(files.items()): + if not exists(path) or mtime(path) > lmtime: + self.status = 'reload' + thread.interrupt_main() + break + time.sleep(self.interval) + + def __enter__(self): + self.start() + + def __exit__(self, exc_type, *_): + if not self.status: self.status = 'exit' # silent exit + self.join() + return exc_type is not None and issubclass(exc_type, KeyboardInterrupt) + +############################################################################### +# Template Adapters ############################################################ +############################################################################### + + +class TemplateError(BottleException): + pass + + +class BaseTemplate(object): + """ Base class and minimal API for template adapters """ + extensions = ['tpl', 'html', 'thtml', 'stpl'] + settings = {} #used in prepare() + defaults = {} #used in render() + + def __init__(self, + source=None, + name=None, + lookup=None, + encoding='utf8', **settings): + """ Create a new template. + If the source parameter (str or buffer) is missing, the name argument + is used to guess a template filename. Subclasses can assume that + self.source and/or self.filename are set. Both are strings. + The lookup, encoding and settings parameters are stored as instance + variables. + The lookup parameter stores a list containing directory paths. + The encoding parameter should be used to decode byte strings or files. + The settings parameter contains a dict for engine-specific settings. + """ + self.name = name + self.source = source.read() if hasattr(source, 'read') else source + self.filename = source.filename if hasattr(source, 'filename') else None + self.lookup = [os.path.abspath(x) for x in lookup] if lookup else [] + self.encoding = encoding + self.settings = self.settings.copy() # Copy from class variable + self.settings.update(settings) # Apply + if not self.source and self.name: + self.filename = self.search(self.name, self.lookup) + if not self.filename: + raise TemplateError('Template %s not found.' % repr(name)) + if not self.source and not self.filename: + raise TemplateError('No template specified.') + self.prepare(**self.settings) + + @classmethod + def search(cls, name, lookup=None): + """ Search name in all directories specified in lookup. + First without, then with common extensions. Return first hit. """ + if not lookup: + raise depr(0, 12, "Empty template lookup path.", "Configure a template lookup path.") + + if os.path.isabs(name): + raise depr(0, 12, "Use of absolute path for template name.", + "Refer to templates with names or paths relative to the lookup path.") + + for spath in lookup: + spath = os.path.abspath(spath) + os.sep + fname = os.path.abspath(os.path.join(spath, name)) + if not fname.startswith(spath): continue + if os.path.isfile(fname): return fname + for ext in cls.extensions: + if os.path.isfile('%s.%s' % (fname, ext)): + return '%s.%s' % (fname, ext) + + @classmethod + def global_config(cls, key, *args): + """ This reads or sets the global settings stored in class.settings. """ + if args: + cls.settings = cls.settings.copy() # Make settings local to class + cls.settings[key] = args[0] + else: + return cls.settings[key] + + def prepare(self, **options): + """ Run preparations (parsing, caching, ...). + It should be possible to call this again to refresh a template or to + update settings. + """ + raise NotImplementedError + + def render(self, *args, **kwargs): + """ Render the template with the specified local variables and return + a single byte or unicode string. If it is a byte string, the encoding + must match self.encoding. This method must be thread-safe! + Local variables may be provided in dictionaries (args) + or directly, as keywords (kwargs). + """ + raise NotImplementedError + + +class MakoTemplate(BaseTemplate): + def prepare(self, **options): + from mako.template import Template + from mako.lookup import TemplateLookup + options.update({'input_encoding': self.encoding}) + options.setdefault('format_exceptions', bool(DEBUG)) + lookup = TemplateLookup(directories=self.lookup, **options) + if self.source: + self.tpl = Template(self.source, lookup=lookup, **options) + else: + self.tpl = Template(uri=self.name, + filename=self.filename, + lookup=lookup, **options) + + def render(self, *args, **kwargs): + for dictarg in args: + kwargs.update(dictarg) + _defaults = self.defaults.copy() + _defaults.update(kwargs) + return self.tpl.render(**_defaults) + + +class CheetahTemplate(BaseTemplate): + def prepare(self, **options): + from Cheetah.Template import Template + self.context = threading.local() + self.context.vars = {} + options['searchList'] = [self.context.vars] + if self.source: + self.tpl = Template(source=self.source, **options) + else: + self.tpl = Template(file=self.filename, **options) + + def render(self, *args, **kwargs): + for dictarg in args: + kwargs.update(dictarg) + self.context.vars.update(self.defaults) + self.context.vars.update(kwargs) + out = str(self.tpl) + self.context.vars.clear() + return out + + +class Jinja2Template(BaseTemplate): + def prepare(self, filters=None, tests=None, globals={}, **kwargs): + from jinja2 import Environment, FunctionLoader + self.env = Environment(loader=FunctionLoader(self.loader), **kwargs) + if filters: self.env.filters.update(filters) + if tests: self.env.tests.update(tests) + if globals: self.env.globals.update(globals) + if self.source: + self.tpl = self.env.from_string(self.source) + else: + self.tpl = self.env.get_template(self.name) + + def render(self, *args, **kwargs): + for dictarg in args: + kwargs.update(dictarg) + _defaults = self.defaults.copy() + _defaults.update(kwargs) + return self.tpl.render(**_defaults) + + def loader(self, name): + if name == self.filename: + fname = name + else: + fname = self.search(name, self.lookup) + if not fname: return + with open(fname, "rb") as f: + return (f.read().decode(self.encoding), fname, lambda: False) + + +class SimpleTemplate(BaseTemplate): + def prepare(self, + escape_func=html_escape, + noescape=False, + syntax=None, **ka): + self.cache = {} + enc = self.encoding + self._str = lambda x: touni(x, enc) + self._escape = lambda x: escape_func(touni(x, enc)) + self.syntax = syntax + if noescape: + self._str, self._escape = self._escape, self._str + + @cached_property + def co(self): + return compile(self.code, self.filename or '', 'exec') + + @cached_property + def code(self): + source = self.source + if not source: + with open(self.filename, 'rb') as f: + source = f.read() + try: + source, encoding = touni(source), 'utf8' + except UnicodeError: + raise depr(0, 11, 'Unsupported template encodings.', 'Use utf-8 for templates.') + parser = StplParser(source, encoding=encoding, syntax=self.syntax) + code = parser.translate() + self.encoding = parser.encoding + return code + + def _rebase(self, _env, _name=None, **kwargs): + _env['_rebase'] = (_name, kwargs) + + def _include(self, _env, _name=None, **kwargs): + env = _env.copy() + env.update(kwargs) + if _name not in self.cache: + self.cache[_name] = self.__class__(name=_name, lookup=self.lookup, syntax=self.syntax) + return self.cache[_name].execute(env['_stdout'], env) + + def execute(self, _stdout, kwargs): + env = self.defaults.copy() + env.update(kwargs) + env.update({ + '_stdout': _stdout, + '_printlist': _stdout.extend, + 'include': functools.partial(self._include, env), + 'rebase': functools.partial(self._rebase, env), + '_rebase': None, + '_str': self._str, + '_escape': self._escape, + 'get': env.get, + 'setdefault': env.setdefault, + 'defined': env.__contains__ + }) + exec(self.co, env) + if env.get('_rebase'): + subtpl, rargs = env.pop('_rebase') + rargs['base'] = ''.join(_stdout) #copy stdout + del _stdout[:] # clear stdout + return self._include(env, subtpl, **rargs) + return env + + def render(self, *args, **kwargs): + """ Render the template using keyword arguments as local variables. """ + env = {} + stdout = [] + for dictarg in args: + env.update(dictarg) + env.update(kwargs) + self.execute(stdout, env) + return ''.join(stdout) + + +class StplSyntaxError(TemplateError): + pass + + +class StplParser(object): + """ Parser for stpl templates. """ + _re_cache = {} #: Cache for compiled re patterns + + # This huge pile of voodoo magic splits python code into 8 different tokens. + # We use the verbose (?x) regex mode to make this more manageable + + _re_tok = r'''( + [urbURB]* + (?: ''(?!') + |""(?!") + |'{6} + |"{6} + |'(?:[^\\']|\\.)+?' + |"(?:[^\\"]|\\.)+?" + |'{3}(?:[^\\]|\\.|\n)+?'{3} + |"{3}(?:[^\\]|\\.|\n)+?"{3} + ) + )''' + + _re_inl = _re_tok.replace(r'|\n', '') # We re-use this string pattern later + + _re_tok += r''' + # 2: Comments (until end of line, but not the newline itself) + |(\#.*) + + # 3: Open and close (4) grouping tokens + |([\[\{\(]) + |([\]\}\)]) + + # 5,6: Keywords that start or continue a python block (only start of line) + |^([\ \t]*(?:if|for|while|with|try|def|class)\b) + |^([\ \t]*(?:elif|else|except|finally)\b) + + # 7: Our special 'end' keyword (but only if it stands alone) + |((?:^|;)[\ \t]*end[\ \t]*(?=(?:%(block_close)s[\ \t]*)?\r?$|;|\#)) + + # 8: A customizable end-of-code-block template token (only end of line) + |(%(block_close)s[\ \t]*(?=\r?$)) + + # 9: And finally, a single newline. The 10th token is 'everything else' + |(\r?\n) + ''' + + # Match the start tokens of code areas in a template + _re_split = r'''(?m)^[ \t]*(\\?)((%(line_start)s)|(%(block_start)s))''' + # Match inline statements (may contain python strings) + _re_inl = r'''%%(inline_start)s((?:%s|[^'"\n])*?)%%(inline_end)s''' % _re_inl + + # add the flag in front of the regexp to avoid Deprecation warning (see Issue #949) + # verbose and dot-matches-newline mode + _re_tok = '(?mx)' + _re_tok + _re_inl = '(?mx)' + _re_inl + + + default_syntax = '<% %> % {{ }}' + + def __init__(self, source, syntax=None, encoding='utf8'): + self.source, self.encoding = touni(source, encoding), encoding + self.set_syntax(syntax or self.default_syntax) + self.code_buffer, self.text_buffer = [], [] + self.lineno, self.offset = 1, 0 + self.indent, self.indent_mod = 0, 0 + self.paren_depth = 0 + + def get_syntax(self): + """ Tokens as a space separated string (default: <% %> % {{ }}) """ + return self._syntax + + def set_syntax(self, syntax): + self._syntax = syntax + self._tokens = syntax.split() + if syntax not in self._re_cache: + names = 'block_start block_close line_start inline_start inline_end' + etokens = map(re.escape, self._tokens) + pattern_vars = dict(zip(names.split(), etokens)) + patterns = (self._re_split, self._re_tok, self._re_inl) + patterns = [re.compile(p % pattern_vars) for p in patterns] + self._re_cache[syntax] = patterns + self.re_split, self.re_tok, self.re_inl = self._re_cache[syntax] + + syntax = property(get_syntax, set_syntax) + + def translate(self): + if self.offset: raise RuntimeError('Parser is a one time instance.') + while True: + m = self.re_split.search(self.source, pos=self.offset) + if m: + text = self.source[self.offset:m.start()] + self.text_buffer.append(text) + self.offset = m.end() + if m.group(1): # Escape syntax + line, sep, _ = self.source[self.offset:].partition('\n') + self.text_buffer.append(self.source[m.start():m.start(1)] + + m.group(2) + line + sep) + self.offset += len(line + sep) + continue + self.flush_text() + self.offset += self.read_code(self.source[self.offset:], + multiline=bool(m.group(4))) + else: + break + self.text_buffer.append(self.source[self.offset:]) + self.flush_text() + return ''.join(self.code_buffer) + + def read_code(self, pysource, multiline): + code_line, comment = '', '' + offset = 0 + while True: + m = self.re_tok.search(pysource, pos=offset) + if not m: + code_line += pysource[offset:] + offset = len(pysource) + self.write_code(code_line.strip(), comment) + break + code_line += pysource[offset:m.start()] + offset = m.end() + _str, _com, _po, _pc, _blk1, _blk2, _end, _cend, _nl = m.groups() + if self.paren_depth > 0 and (_blk1 or _blk2): # a if b else c + code_line += _blk1 or _blk2 + continue + if _str: # Python string + code_line += _str + elif _com: # Python comment (up to EOL) + comment = _com + if multiline and _com.strip().endswith(self._tokens[1]): + multiline = False # Allow end-of-block in comments + elif _po: # open parenthesis + self.paren_depth += 1 + code_line += _po + elif _pc: # close parenthesis + if self.paren_depth > 0: + # we could check for matching parentheses here, but it's + # easier to leave that to python - just check counts + self.paren_depth -= 1 + code_line += _pc + elif _blk1: # Start-block keyword (if/for/while/def/try/...) + code_line = _blk1 + self.indent += 1 + self.indent_mod -= 1 + elif _blk2: # Continue-block keyword (else/elif/except/...) + code_line = _blk2 + self.indent_mod -= 1 + elif _cend: # The end-code-block template token (usually '%>') + if multiline: multiline = False + else: code_line += _cend + elif _end: + self.indent -= 1 + self.indent_mod += 1 + else: # \n + self.write_code(code_line.strip(), comment) + self.lineno += 1 + code_line, comment, self.indent_mod = '', '', 0 + if not multiline: + break + + return offset + + def flush_text(self): + text = ''.join(self.text_buffer) + del self.text_buffer[:] + if not text: return + parts, pos, nl = [], 0, '\\\n' + ' ' * self.indent + for m in self.re_inl.finditer(text): + prefix, pos = text[pos:m.start()], m.end() + if prefix: + parts.append(nl.join(map(repr, prefix.splitlines(True)))) + if prefix.endswith('\n'): parts[-1] += nl + parts.append(self.process_inline(m.group(1).strip())) + if pos < len(text): + prefix = text[pos:] + lines = prefix.splitlines(True) + if lines[-1].endswith('\\\\\n'): lines[-1] = lines[-1][:-3] + elif lines[-1].endswith('\\\\\r\n'): lines[-1] = lines[-1][:-4] + parts.append(nl.join(map(repr, lines))) + code = '_printlist((%s,))' % ', '.join(parts) + self.lineno += code.count('\n') + 1 + self.write_code(code) + + @staticmethod + def process_inline(chunk): + if chunk[0] == '!': return '_str(%s)' % chunk[1:] + return '_escape(%s)' % chunk + + def write_code(self, line, comment=''): + code = ' ' * (self.indent + self.indent_mod) + code += line.lstrip() + comment + '\n' + self.code_buffer.append(code) + + +def template(*args, **kwargs): + """ + Get a rendered template as a string iterator. + You can use a name, a filename or a template string as first parameter. + Template rendering arguments can be passed as dictionaries + or directly (as keyword arguments). + """ + tpl = args[0] if args else None + for dictarg in args[1:]: + kwargs.update(dictarg) + adapter = kwargs.pop('template_adapter', SimpleTemplate) + lookup = kwargs.pop('template_lookup', TEMPLATE_PATH) + tplid = (id(lookup), tpl) + if tplid not in TEMPLATES or DEBUG: + settings = kwargs.pop('template_settings', {}) + if isinstance(tpl, adapter): + TEMPLATES[tplid] = tpl + if settings: TEMPLATES[tplid].prepare(**settings) + elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl: + TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings) + else: + TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings) + if not TEMPLATES[tplid]: + abort(500, 'Template (%s) not found' % tpl) + return TEMPLATES[tplid].render(kwargs) + + +mako_template = functools.partial(template, template_adapter=MakoTemplate) +cheetah_template = functools.partial(template, + template_adapter=CheetahTemplate) +jinja2_template = functools.partial(template, template_adapter=Jinja2Template) + + +def view(tpl_name, **defaults): + """ Decorator: renders a template for a handler. + The handler can control its behavior like that: + + - return a dict of template vars to fill out the template + - return something other than a dict and the view decorator will not + process the template, but return the handler result as is. + This includes returning a HTTPResponse(dict) to get, + for instance, JSON with autojson or other castfilters. + """ + + def decorator(func): + + @functools.wraps(func) + def wrapper(*args, **kwargs): + result = func(*args, **kwargs) + if isinstance(result, (dict, DictMixin)): + tplvars = defaults.copy() + tplvars.update(result) + return template(tpl_name, **tplvars) + elif result is None: + return template(tpl_name, defaults) + return result + + return wrapper + + return decorator + + +mako_view = functools.partial(view, template_adapter=MakoTemplate) +cheetah_view = functools.partial(view, template_adapter=CheetahTemplate) +jinja2_view = functools.partial(view, template_adapter=Jinja2Template) + +############################################################################### +# Constants and Globals ######################################################## +############################################################################### + +TEMPLATE_PATH = ['./', './views/'] +TEMPLATES = {} +DEBUG = False +NORUN = False # If set, run() does nothing. Used by load_app() + +#: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found') +HTTP_CODES = httplib.responses.copy() +HTTP_CODES[418] = "I'm a teapot" # RFC 2324 +HTTP_CODES[428] = "Precondition Required" +HTTP_CODES[429] = "Too Many Requests" +HTTP_CODES[431] = "Request Header Fields Too Large" +HTTP_CODES[451] = "Unavailable For Legal Reasons" # RFC 7725 +HTTP_CODES[511] = "Network Authentication Required" +_HTTP_STATUS_LINES = dict((k, '%d %s' % (k, v)) + for (k, v) in HTTP_CODES.items()) + +#: The default template used for error pages. Override with @error() +ERROR_PAGE_TEMPLATE = """ +%%try: + %%from %s import DEBUG, request + + + + Error: {{e.status}} + + + +

Error: {{e.status}}

+

Sorry, the requested URL {{repr(request.url)}} + caused an error:

+
{{e.body}}
+ %%if DEBUG and e.exception: +

Exception:

+ %%try: + %%exc = repr(e.exception) + %%except: + %%exc = '' %% type(e.exception).__name__ + %%end +
{{exc}}
+ %%end + %%if DEBUG and e.traceback: +

Traceback:

+
{{e.traceback}}
+ %%end + + +%%except ImportError: + ImportError: Could not generate the error page. Please add bottle to + the import path. +%%end +""" % __name__ + +#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a +#: request callback, this instance always refers to the *current* request +#: (even on a multi-threaded server). +request = LocalRequest() + +#: A thread-safe instance of :class:`LocalResponse`. It is used to change the +#: HTTP response for the *current* request. +response = LocalResponse() + +#: A thread-safe namespace. Not used by Bottle. +local = threading.local() + +# Initialize app stack (create first empty Bottle app now deferred until needed) +# BC: 0.6.4 and needed for run() +apps = app = default_app = AppStack() + +#: A virtual package that redirects import statements. +#: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`. +ext = _ImportRedirect('bottle.ext' if __name__ == '__main__' else + __name__ + ".ext", 'bottle_%s').module + + +def _main(argv): # pragma: no coverage + args, parser = _cli_parse(argv) + + def _cli_error(cli_msg): + parser.print_help() + _stderr('\nError: %s\n' % cli_msg) + sys.exit(1) + + if args.version: + print('Bottle %s' % __version__) + sys.exit(0) + if not args.app: + _cli_error("No application entry point specified.") + + sys.path.insert(0, '.') + sys.modules.setdefault('bottle', sys.modules['__main__']) + + host, port = (args.bind or 'localhost'), 8080 + if ':' in host and host.rfind(']') < host.rfind(':'): + host, port = host.rsplit(':', 1) + host = host.strip('[]') + + config = ConfigDict() + + for cfile in args.conf or []: + try: + if cfile.endswith('.json'): + with open(cfile, 'rb') as fp: + config.load_dict(json_loads(fp.read())) + else: + config.load_config(cfile) + except configparser.Error as parse_error: + _cli_error(parse_error) + except IOError: + _cli_error("Unable to read config file %r" % cfile) + except (UnicodeError, TypeError, ValueError) as error: + _cli_error("Unable to parse config file %r: %s" % (cfile, error)) + + for cval in args.param or []: + if '=' in cval: + config.update((cval.split('=', 1),)) + else: + config[cval] = True + + run(args.app, + host=host, + port=int(port), + server=args.server, + reloader=args.reload, + plugins=args.plugin, + debug=args.debug, + config=config) + + +if __name__ == '__main__': # pragma: no coverage + _main(sys.argv) \ No newline at end of file diff --git a/python/external/wheezy/__init__.py b/python/external/wheezy/__init__.py new file mode 100644 index 0000000..e73e0d2 --- /dev/null +++ b/python/external/wheezy/__init__.py @@ -0,0 +1,10 @@ +import typing + +# See +# http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages +try: + __import__("pkg_resources").declare_namespace(__name__) +except ImportError: + from pkgutil import extend_path + + __path__: typing.Iterable[str] = extend_path(__path__, __name__) diff --git a/python/external/wheezy/template/__init__.py b/python/external/wheezy/template/__init__.py new file mode 100644 index 0000000..3fbeb7f --- /dev/null +++ b/python/external/wheezy/template/__init__.py @@ -0,0 +1,20 @@ +""" +""" + +from external.wheezy.template.engine import Engine +from external.wheezy.template.ext.code import CodeExtension +from external.wheezy.template.ext.core import CoreExtension +from external.wheezy.template.loader import DictLoader, FileLoader, PreprocessLoader +from external.wheezy.template.preprocessor import Preprocessor + +__all__ = ( + "Engine", + "CodeExtension", + "CoreExtension", + "DictLoader", + "FileLoader", + "PreprocessLoader", + "Preprocessor", +) + +__version__ = "0.1" diff --git a/python/external/wheezy/template/builder.py b/python/external/wheezy/template/builder.py new file mode 100644 index 0000000..a00beab --- /dev/null +++ b/python/external/wheezy/template/builder.py @@ -0,0 +1,120 @@ +import typing + +from external.wheezy.template.typing import Builder, BuilderRule, Token + + +def builder_scan( + extensions: typing.List[typing.Any], +) -> typing.Mapping[str, typing.Any]: + builder_rules: typing.Dict[str, typing.List[BuilderRule]] = {} + for extension in extensions: + if hasattr(extension, "builder_rules"): + rules = extension.builder_rules + for token, builder in rules: + builder_rules.setdefault(token, []).append(builder) + return {"builder_rules": builder_rules} + + +class BlockBuilder(Builder): + + __slots__ = ("rules", "indent", "lineno", "buf") + + def __init__( + self, + rules: typing.Dict[str, typing.List[BuilderRule]], + indent: str = "", + lineno: int = 0, + ) -> None: + self.rules = rules + self.indent = indent + self.lineno = lineno + self.buf: typing.List[str] = [] + + def start_block(self) -> None: + self.indent += " " + + def end_block(self) -> None: + if len(self.indent) < 4: + raise SyntaxError("Unexpected end of block.") + self.indent = self.indent[:-4] + + def add(self, lineno: int, code: str) -> None: + if lineno < self.lineno: + raise SyntaxError( + "Inconsistence at %s : %s" % (self.lineno, lineno) + ) + if lineno == self.lineno: + line = self.buf[-1] + if code: + if line: + if line[-1:] == ":": + self.buf[-1] = line + code + else: + self.buf[-1] = line + "; " + code + else: + self.buf[-1] = self.indent + code + else: + pad = lineno - self.lineno - 1 + if pad > 0: + self.buf.extend([""] * pad) + if code: + self.buf.append(self.indent + code) + else: + self.buf.append("") + self.lineno = lineno + code.count("\n") + + def build_block(self, nodes: typing.Iterable[Token]) -> None: + for lineno, token, value in nodes: + self.build_token(lineno, token, value) + + def build_token( + self, + lineno: int, + token: str, + value: typing.Union[str, typing.Iterable[Token]], + ) -> None: + if token in self.rules: + for rule in self.rules[token]: + if rule(self, lineno, token, value): + break + else: + raise SyntaxError( + 'No rule to build "%s" token at line %d.' % (token, lineno) + ) + + def to_string(self) -> str: + return "\n".join(self.buf) + + +class SourceBuilder(object): + + __slots__ = ("rules", "lineno") + + def __init__( + self, + builder_rules: typing.Dict[str, typing.List[BuilderRule]], + builder_offset: int = 2, + **ignore: typing.Any + ) -> None: + self.rules = builder_rules + self.lineno = 0 - builder_offset + + def build_source(self, nodes: typing.Iterable[Token]) -> str: + builder = BlockBuilder(self.rules) + builder.build_block(nodes) + return builder.to_string() + + def build_render(self, nodes: typing.Iterable[Token]) -> str: + builder = BlockBuilder(self.rules, lineno=self.lineno) + builder.add( + self.lineno + 1, "def render(ctx, local_defs, super_defs):" + ) + builder.start_block() + builder.build_token(self.lineno + 2, "render", nodes) + return builder.to_string() + + def build_module(self, nodes: typing.Iterable[Token]) -> str: + builder = BlockBuilder(self.rules, lineno=self.lineno) + builder.add(self.lineno + 1, "local_defs = {}; super_defs = {}") + builder.build_token(self.lineno + 2, "module", nodes) + return builder.to_string() diff --git a/python/external/wheezy/template/comp.py b/python/external/wheezy/template/comp.py new file mode 100644 index 0000000..8bbab93 --- /dev/null +++ b/python/external/wheezy/template/comp.py @@ -0,0 +1,9 @@ +import ast +import typing +from _thread import allocate_lock # noqa + + +def adjust_source_lineno(source: str, name: str, lineno: int) -> typing.Any: + node = compile(source, name, "exec", ast.PyCF_ONLY_AST) + ast.increment_lineno(node, lineno) + return node diff --git a/python/external/wheezy/template/compiler.py b/python/external/wheezy/template/compiler.py new file mode 100644 index 0000000..b38eb73 --- /dev/null +++ b/python/external/wheezy/template/compiler.py @@ -0,0 +1,30 @@ +import typing +from types import ModuleType + +from external.wheezy.template.comp import adjust_source_lineno + + +class Compiler(object): + def __init__( + self, global_vars: typing.Dict[str, typing.Any], source_lineno: int + ) -> None: + self.global_vars = global_vars + self.source_lineno = source_lineno + + def compile_module(self, source: str, name: str) -> ModuleType: + node = adjust_source_lineno(source, name, self.source_lineno) + compiled = compile(node, name, "exec") + module = ModuleType(name) + module.__name__ = name + module.__dict__.update(self.global_vars) + exec(compiled, module.__dict__) + return module + + def compile_source( + self, source: str, name: str + ) -> typing.Dict[str, typing.Any]: + node = adjust_source_lineno(source, name, self.source_lineno) + compiled = compile(node, name, "exec") + local_vars: typing.Dict[str, typing.Any] = {} + exec(compiled, self.global_vars, local_vars) + return local_vars diff --git a/python/external/wheezy/template/console.py b/python/external/wheezy/template/console.py new file mode 100644 index 0000000..7fafb6b --- /dev/null +++ b/python/external/wheezy/template/console.py @@ -0,0 +1,126 @@ +import getopt +import json +import os +import sys +import typing + +from external.wheezy.template.engine import Engine +from external.wheezy.template.ext.code import CodeExtension +from external.wheezy.template.ext.core import CoreExtension +from external.wheezy.template.loader import FileLoader + +try: + from wheezy.html.utils import escape_html as escape # type: ignore[import] +except ImportError: # pragma: nocover + from html import escape + + +class Options: + def __init__( + self, + token_start: str = "@", + line_join: str = "\\", + searchpath: typing.Optional[typing.List[str]] = None, + extensions: typing.Optional[typing.List[str]] = None, + ) -> None: + self.token_start = token_start + self.line_join = line_join + self.searchpath = searchpath or ["."] + self.extensions = extensions or [] + self.template = "" + self.context: typing.List[str] = [] + + +def main(argv: typing.Optional[typing.List[str]] = None) -> int: + args = parse_args(argv or sys.argv[1:]) + if not args: + return 2 + ts = args.token_start + extensions = [CoreExtension(ts, args.line_join), CodeExtension(ts)] + extensions.extend(args.extensions) + engine = Engine(FileLoader(args.searchpath), extensions) + engine.global_vars.update({"h": escape}) + t = engine.get_template(args.template) + sys.stdout.write(t.render(load_context(args.context))) + return 0 + + +def load_context(sources: typing.List[str]) -> typing.Mapping[str, typing.Any]: + c: typing.Dict[str, typing.Any] = {} + for s in sources: + if os.path.isfile(s): + d = json.load(open(s)) + else: + d = json.loads(s) + c.update(d) + return c + + +def parse_args( # noqa: C901 + args: typing.List[str], +) -> typing.Optional[Options]: + try: + opts, value = getopt.getopt(args, "s:t:j:wh") + except getopt.GetoptError: + e = sys.exc_info()[1] + usage() + print("error: %s" % e) + return None + d = Options(token_start="@", searchpath=["."], extensions=[]) + for o, a in opts: + if o == "-h": + return None + elif o == "-t": + d.token_start = a + elif o == "-j": + d.line_join = a + elif o == "-s": + d.searchpath = a.split(";") + elif o == "-w": # pragma: nocover + from wheezy.html.ext.template import ( # type: ignore[import] + WhitespaceExtension, + ) + + d.extensions.append(WhitespaceExtension()) + if not value: + usage() + return None + d.template = value[0] + d.context = value[1:] + return d + + +def usage() -> None: + from datetime import datetime + from os.path import basename + + from wheezy.template import __version__ + + print( + """\ +wheezy.template %s +Copyright (C) 2012-%d by Andriy Kornatskyy + +renders a template with the given context. + +usage: %s template [ context ... ] + +positional arguments: + + template a filename + context a filename or JSON string + +optional arguments: + + -s path search path for templates ( . ) + -t token token start ( @ ) + -j token line join ( \\ ) + -w whitespace clean up + -h show this help message +""" + % (__version__, datetime.now().year, basename(sys.argv[0])) + ) + + +if __name__ == "__main__": # pragma: nocover + sys.exit(main()) diff --git a/python/external/wheezy/template/engine.py b/python/external/wheezy/template/engine.py new file mode 100644 index 0000000..edc0c27 --- /dev/null +++ b/python/external/wheezy/template/engine.py @@ -0,0 +1,221 @@ +import typing +from types import ModuleType + +from external.wheezy.template.builder import SourceBuilder, builder_scan +from external.wheezy.template.comp import allocate_lock # type: ignore[attr-defined] +from external.wheezy.template.compiler import Compiler +from external.wheezy.template.lexer import Lexer, lexer_scan +from external.wheezy.template.parser import Parser, parser_scan +from external.wheezy.template.typing import ( + Loader, + RenderTemplate, + SupportsRender, + TemplateClass, + Token, +) + + +class Template(SupportsRender): + """Simple template class.""" + + __slots__ = ("name", "render_template") + + def __init__(self, name: str, render_template: RenderTemplate) -> None: + self.name = name + self.render_template = render_template + + def render(self, ctx: typing.Mapping[str, typing.Any]) -> str: + return self.render_template(ctx, {}, {}) + + +class Engine(object): + """The core component of template engine.""" + + def __init__( + self, + loader: Loader, + extensions: typing.List[typing.Any], + template_class: typing.Optional[TemplateClass] = None, + ) -> None: + self.lock = allocate_lock() + self.templates: typing.Dict[str, SupportsRender] = {} + self.renders: typing.Dict[str, RenderTemplate] = {} + self.modules: typing.Dict[str, ModuleType] = {} + self.global_vars = {"_r": self.render, "_i": self.import_name} + self.loader = loader + self.template_class = template_class or Template + self.compiler = Compiler(self.global_vars, 0) + self.lexer = Lexer(**lexer_scan(extensions)) + self.parser = Parser(**parser_scan(extensions)) + self.builder = SourceBuilder(**builder_scan(extensions)) + + def get_template(self, name: str) -> SupportsRender: + """Returns compiled template.""" + try: + return self.templates[name] + except KeyError: + self.compile_template(name) + return self.templates[name] + + def render( + self, + name: str, + ctx: typing.Mapping[str, typing.Any], + local_defs: typing.Mapping[str, typing.Any], + super_defs: typing.Mapping[str, typing.Any], + ) -> str: + """Renders template by name in given context.""" + try: + + return self.renders[name](ctx, local_defs, super_defs) + except KeyError: + self.compile_template(name) + return self.renders[name](ctx, local_defs, super_defs) + + def remove(self, name: str) -> None: + """Removes given ``name`` from internal cache.""" + self.lock.acquire(True) + try: + if name in self.renders: + del self.templates[name] + del self.renders[name] + if name in self.modules: + del self.modules[name] + finally: + self.lock.release() + + # region: internal details + + def import_name(self, name: str) -> ModuleType: + try: + return self.modules[name] + except KeyError: + self.compile_import(name) + return self.modules[name] + + def compile_template(self, name: str) -> None: + self.lock.acquire(True) + try: + if name not in self.renders: + template_source = self.loader.load(name) + if template_source is None: + raise IOError('Template "%s" not found.' % name) + tokens = self.lexer.tokenize(template_source) + nodes = self.parser.parse(tokens) + source = self.builder.build_render(nodes) + + # print_debug(name, tokens, nodes, source) + + try: + render_template = self.compiler.compile_source( + source, name + )["render"] + except SyntaxError as e: + raise complement_syntax_error(e, template_source, source) + + self.renders[name] = render_template + self.templates[name] = self.template_class( + name, render_template + ) + finally: + self.lock.release() + + def compile_import(self, name: str) -> None: + self.lock.acquire(True) + try: + if name not in self.modules: + template_source = self.loader.load(name) + if template_source is None: + raise IOError('Import "%s" not found.' % name) + tokens = self.lexer.tokenize(template_source) + nodes = self.parser.parse(tokens) + source = self.builder.build_module(nodes) + + # print_debug(name, tokens, nodes, source) + + try: + self.modules[name] = self.compiler.compile_module( + source, name + ) + except SyntaxError as e: + raise complement_syntax_error(e, template_source, source) + finally: + self.lock.release() + + +# region: internal details + + +def print_debug( + name: str, + tokens: typing.List[Token], + nodes: typing.List[typing.Any], + source: str, +) -> None: # pragma: nocover + print(name.center(80, "-")) + from pprint import pprint + + # pprint(tokens) + pprint(nodes) + from wheezy.template.utils import print_source + + print_source(source, -1) + + +def complement_syntax_error( + err: SyntaxError, template_source: str, source: str +) -> SyntaxError: + """Complements SyntaxError with template and source snippets, + like one below: + + .. code-block:: none + + File "shared/snippet/widget.html", line 4 + if : + + template snippet: + 02

+ 03 @msg!h + 04 @if : + 05 sd + 06 @end + + generated snippet: + 02 _b = []; w = _b.append; w('

\\n ') + 03 w(h(msg)); w('\\n') + 04 if : + 05 w(' sd\\n') + 06 + + if : + ^ + SyntaxError: invalid syntax + + """ + lineno = err.lineno or 0 + text = """\ +%s +template snippet: +%s + +generated snippet: +%s + + %s""" % ( + err.text, + source_chunk(template_source, lineno - 2, 1), + source_chunk(source, lineno, -1), + err.text and err.text.strip() or "", + ) + return err.__class__(err.msg, (err.filename, lineno - 2, err.offset, text)) + + +def source_chunk(source: str, lineno: int, offset: int, extra: int = 2) -> str: + lines = source.split("\n", lineno + extra) + s = max(0, lineno - extra - 1) + e = min(len(lines), lineno + extra) + r = [] + for i in range(s, e): + line = lines[i] + r.append(" %02d %s" % (i + offset, line)) + return "\n".join(r) diff --git a/python/external/wheezy/template/ext/__init__.py b/python/external/wheezy/template/ext/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/external/wheezy/template/ext/code.py b/python/external/wheezy/template/ext/code.py new file mode 100644 index 0000000..c4734ee --- /dev/null +++ b/python/external/wheezy/template/ext/code.py @@ -0,0 +1,60 @@ +import re +import typing + +from external.wheezy.template.typing import Builder, LexerRule, ParserRule, Token +from external.wheezy.template.utils import find_balanced + +# region: lexer extensions + + +def code_token(m: typing.Match[str]) -> Token: + source = m.string + start = m.end() + end = find_balanced(source, start) + if source[end::1] == "\n": + end += 1 + return end, "code", source[start:end] + + +# region: parser + + +def parse_code(value: str) -> typing.List[str]: + lines = value.rstrip("\n")[1:-1].split("\n") + lines[0] = lines[0].lstrip() + if len(lines) == 1: + return lines + line = lines[1] + n = len(line) - len(line.lstrip()) + return [s[:n].lstrip() + s[n:] for s in lines] + + +# region: block_builders + + +def build_code( + builder: Builder, lineno: int, token: str, lines: typing.List[str] +) -> bool: + for line in lines: + builder.add(lineno, line) + lineno += 1 + return True + + +# region: core extension + + +class CodeExtension: + """Includes support for embedded python code.""" + + def __init__(self, token_start: str = "@") -> None: + + self.lexer_rules: typing.Mapping[int, LexerRule] = { + 300: (re.compile(r"\s*%s(?=\()" % token_start), code_token), + } + + parser_rules: typing.Mapping[str, ParserRule] = {"code": parse_code} + + builder_rules: typing.List[typing.Tuple[str, typing.Any]] = [ + ("code", build_code) + ] diff --git a/python/external/wheezy/template/ext/core.py b/python/external/wheezy/template/ext/core.py new file mode 100644 index 0000000..62c0938 --- /dev/null +++ b/python/external/wheezy/template/ext/core.py @@ -0,0 +1,517 @@ +import re +import typing + +from external.wheezy.template.typing import Builder, LexerRule, ParserConfig, Token +from external.wheezy.template.utils import find_all_balanced + +# region: config + +end_tokens = ["end"] +continue_tokens = ["else:", "elif "] +compound_tokens = ["for ", "if ", "def ", "extends"] + continue_tokens +reserved_tokens = ["require", "#", "include", "import ", "from "] +all_tokens = end_tokens + compound_tokens + reserved_tokens +out_tokens = ["markup", "var", "include"] +extends_tokens = ["def ", "require", "import ", "from "] +known_var_filters = {"s": "str"} + + +WRITER_DECLARE = "_b = []; w = _b.append" +WRITER_RETURN = "return ''.join(_b)" + + +# region: lexer extensions + + +def stmt_token(m: typing.Match[str]) -> Token: + """Produces statement token.""" + return m.end(), str(m.group(2)), str(m.group(1)).replace("\\\n", "") + + +RE_VAR = re.compile(r"(\.\w+)+") +RE_VAR_FILTER = re.compile(r"(? Token: + """Produces variable token.""" + start = m.start(1) + pos = m.end() + source = m.string + while 1: + end = find_all_balanced(source, pos) + if pos == end: + break + mm = RE_VAR.match(source, end) + if not mm: + break + pos = mm.end() + value = source[start:end] + mm = RE_VAR_FILTER.match(source, end) + if mm: + end = mm.end() + value += "!" + mm.group() + return end, "var", value + + +def rvalue_token(m: typing.Match[str]) -> Token: + """Produces variable token as r-value expression.""" + return m.end(), "var", m.group(1).strip() + + +# region: parser config + + +def configure_parser(parser: ParserConfig) -> None: + parser.end_tokens.extend(end_tokens) + parser.continue_tokens.extend(continue_tokens) + parser.compound_tokens.extend(compound_tokens) + parser.out_tokens.extend(out_tokens) + + +# region: parser + + +def parse_require(value: str) -> typing.List[str]: + return [v.strip(" ") for v in value.rstrip()[8:-1].split(",")] + + +def parse_extends(value: str) -> str: + return value.rstrip()[8:-1] + + +def parse_include(value: str) -> str: + return value.rstrip()[8:-1] + + +def parse_import(value: str) -> typing.Tuple[str, str]: + name, var = value[7:].rsplit(" as ", 1) + return name, var + + +def parse_from(value: str) -> typing.Tuple[str, str, str]: + name, var = value[5:].rsplit(" import ", 1) + s = var.rsplit(" as ", 1) + if len(s) == 2: + var, alias = s + else: + alias = var + return name, var, alias + + +def parse_var( + value: str, +) -> typing.Tuple[str, typing.Optional[typing.List[str]]]: + if "!!" not in value: + return value, None + var, var_filter = value.rsplit("!!", 1) + return var, var_filter.strip().split("!") + + +# region: block_builders + + +def build_extends( + builder: Builder, lineno: int, token: str, nodes: typing.List[typing.Any] +) -> bool: + assert token == "render" + n = len(nodes) + if not n: + return False + lineno, token, value = nodes[-1] + if token != "extends": + return False + extends, nested = value + if n > 1: + nested = nodes[:-1] + nested + for lineno, token, value in nested: + if token in extends_tokens: + builder.build_token(lineno, token, value) + builder.add( + builder.lineno + 1, + "return _r(" + extends + ", ctx, local_defs, super_defs)", + ) + return True + + +def build_module( + builder: Builder, lineno: int, token: str, nodes: typing.List[typing.Any] +) -> bool: + assert token == "module" + for lineno, token, value in nodes: + if token == "def ": + builder.build_token(lineno, token, value) + return True + + +def build_import( + builder: Builder, lineno: int, token: str, value: typing.Tuple[str, str] +) -> bool: + assert token == "import " + name, var = value + builder.add(lineno, var + " = _i(" + name + ")") + return True + + +def build_from( + builder: Builder, + lineno: int, + token: str, + value: typing.Tuple[str, str, str], +) -> bool: + assert token == "from " + name, var, alias = value + builder.add( + lineno, alias + " = _i(" + name + ").local_defs['" + var + "']" + ) + return True + + +def build_render_single_markup( + builder: Builder, lineno: int, token: str, nodes: typing.List[typing.Any] +) -> bool: + assert lineno <= 0 + assert token == "render" + if len(nodes) > 1: + return False + if len(nodes) == 0: + builder.add(lineno, "return ''") + return True + ln, token, nodes = nodes[0] + if token != "out" or len(nodes) > 1: + return False + ln, token, value = nodes[0] + if token != "markup": + return False + if value: + builder.add(ln, "return " + value) + else: + builder.add(ln, "return ''") + return True + + +def build_render( + builder: Builder, lineno: int, token: str, nodes: typing.Iterable[Token] +) -> bool: + assert lineno <= 0 + assert token == "render" + builder.add(lineno, WRITER_DECLARE) + builder.build_block(nodes) + lineno = builder.lineno + builder.add(lineno + 1, WRITER_RETURN) + return True + + +def build_def_syntax_check( + builder: Builder, lineno: int, token: str, value: typing.Any +) -> bool: + assert token == "def " + stmt, nodes = value + lineno, token, value = nodes[0] + if token in compound_tokens: + builder.add(lineno, stmt) + builder.start_block() + token = token.rstrip() + error = """\ +The compound statement '%s' is not allowed here. \ +Add a line before it with @#ignore or leave it empty. + +%s + @#ignore + @%s ...""" % ( + token, + stmt, + token, + ) + builder.add(lineno, "raise SyntaxError(%s)" % repr(error)) + builder.end_block() + return True + elif len(nodes) > 1: + # token before @end + lineno, token, value = nodes[-2] + if token == "#": + del nodes[-2] + return False + + +def build_def_empty( + builder: Builder, lineno: int, token: str, value: typing.Any +) -> bool: + assert token == "def " + stmt, nodes = value + if len(nodes) > 1: + return False + def_name = stmt[4 : stmt.index("(", 5)] + builder.add(lineno, stmt) + builder.start_block() + builder.add(lineno, "return ''") + builder.end_block() + builder.add( + lineno + 1, + def_name.join( + ( + "super_defs['", + "'] = ", + "; ", + " = local_defs.setdefault('", + "', ", + ")", + ) + ), + ) + return True + + +def build_def_single_markup( + builder: Builder, lineno: int, token: str, value: typing.Any +) -> bool: + assert token == "def " + stmt, nodes = value + if len(nodes) > 2: + return False + ln, token, nodes = nodes[0] + if token != "out" or len(nodes) > 1: + return False + ln, token, value = nodes[0] + if token != "markup": + return False + def_name = stmt[4 : stmt.index("(", 5)] + builder.add(lineno, stmt) + builder.start_block() + builder.add(ln, "return " + value) + builder.end_block() + builder.add( + ln + 1, + def_name.join( + ( + "super_defs['", + "'] = ", + "; ", + " = local_defs.setdefault('", + "', ", + ")", + ) + ), + ) + return True + + +def build_def( + builder: Builder, lineno: int, token: str, value: typing.Any +) -> bool: + assert token == "def " + stmt, nodes = value + def_name = stmt[4 : stmt.index("(", 5)] + builder.add(lineno, stmt) + builder.start_block() + builder.add(lineno + 1, WRITER_DECLARE) + builder.build_block(nodes) + lineno = builder.lineno + builder.add(lineno, WRITER_RETURN) + builder.end_block() + builder.add( + lineno + 1, + def_name.join( + ( + "super_defs['", + "'] = ", + "; ", + " = local_defs.setdefault('", + "', ", + ")", + ) + ), + ) + return True + + +def build_out( + builder: Builder, lineno: int, token: str, nodes: typing.Iterable[Token] +) -> bool: + assert token == "out" + builder.build_block(nodes) + return True + + +def build_include( + builder: Builder, lineno: int, token: str, value: str +) -> bool: + assert token == "include" + builder.add(lineno, "w(_r(" + value + ", ctx, local_defs, super_defs))") + return True + + +def build_var( + builder: Builder, lineno: int, token: str, value: typing.Any +) -> bool: + assert token == "var" + var, var_filters = value + if var_filters: + for f in var_filters: + var = known_var_filters.get(f, f) + "(" + var + ")" + builder.add(lineno, "w(" + var + ")") + return True + + +def build_markup( + builder: Builder, lineno: int, token: str, value: str +) -> bool: + assert token == "markup" + if value: + builder.add(lineno, "w(" + value + ")") + return True + + +def build_compound( + builder: Builder, lineno: int, token: str, value: typing.Any +) -> bool: + assert token in compound_tokens + stmt, nodes = value + builder.add(lineno, stmt) + builder.start_block() + builder.build_block(nodes) + builder.end_block() + return True + + +def build_require( + builder: Builder, lineno: int, token: str, variables: typing.List[str] +) -> bool: + assert token == "require" + builder.add( + lineno, + "; ".join([name + " = ctx['" + name + "']" for name in variables]), + ) + return True + + +def build_comment( + builder: Builder, lineno: int, token: str, comment: str +) -> bool: + assert token == "#" + builder.add(lineno, comment) + return True + + +def build_end( + builder: Builder, lineno: int, token: str, value: typing.Any +) -> bool: + if builder.lineno != lineno: + builder.add(lineno - 1, "") + return True + + +# region: core extension + + +class CoreExtension(object): + """Includes basic statements, variables processing and markup.""" + + def __init__(self, token_start: str = "@", line_join: str = "\\"): + def markup_token(m: typing.Match[str]) -> Token: + """Produces markup token.""" + return ( + m.end(), + "markup", + m.group().replace(token_start + token_start, token_start), + ) + + self.lexer_rules: typing.Dict[int, LexerRule] = { + 100: ( + re.compile( + r"%s((%s).*?(? str: + """Cleans leading whitespace before token start for all control + tokens. Ignores escaped token start. + """ + return re_clean2.sub( + r"\n%s\2" % token_start, + re_clean1.sub( + r"%s\2" % token_start, source.replace("\r\n", "\n") + ), + ) + + self.preprocessors = [clean_source] + + # region: parser + + if line_join: + line_join = re.escape(line_join) + re_join1 = re.compile(r"(? typing.Optional[str]: + value = re_join2.sub("\\n", re_join1.sub("", value)) + if value: + return repr(value) + else: + return None + + else: + + def parse_markup(value: str) -> typing.Optional[str]: # noqa + if value: + return repr(value) + else: + return None + + self.parser_rules = { + "require": parse_require, + "extends": parse_extends, + "include": parse_include, + "import ": parse_import, + "from ": parse_from, + "var": parse_var, + "markup": parse_markup, + } + + parser_configs = [configure_parser] + + builder_rules = [ + ("render", build_extends), + ("render", build_render_single_markup), + ("render", build_render), + ("module", build_module), + ("import ", build_import), + ("from ", build_from), + ("require", build_require), + ("out", build_out), + ("markup", build_markup), + ("var", build_var), + ("include", build_include), + ("def ", build_def_syntax_check), + ("def ", build_def_empty), + ("def ", build_def_single_markup), + ("def ", build_def), + ("if ", build_compound), + ("elif ", build_compound), + ("else:", build_compound), + ("for ", build_compound), + ("#", build_comment), + ("end", build_end), + ] diff --git a/python/external/wheezy/template/ext/determined.py b/python/external/wheezy/template/ext/determined.py new file mode 100644 index 0000000..e0de781 --- /dev/null +++ b/python/external/wheezy/template/ext/determined.py @@ -0,0 +1,163 @@ +import re +import typing + +from external.wheezy.template.utils import find_balanced + +RE_ARGS = re.compile(r'\s*(?P(([\'"]).*?\3|.+?))\s*\,') +RE_KWARGS = re.compile( + r'\s*(?P\w+)\s*=\s*(?P([\'"].*?[\'"]|.+?))\s*\,' +) +RE_STR_VALUE = re.compile(r'^[\'"](?P.+)[\'"]$') +RE_INT_VALUE = re.compile(r"^(?P(\d+))$") + + +# region: core extension + + +class DeterminedExtension(object): + """Tranlates function calls between template engines. + + Strictly determined known calls are converted to preprocessor + calls, e.g.:: + + @_('Name:') + @path_for('default') + @path_for('static', path='/static/css/site.css') + + Those that are not strictly determined are ignored and processed + by runtime engine. + """ + + def __init__( + self, + known_calls: typing.List[str], + runtime_token_start: str = "@", + token_start: str = "#", + ) -> None: + self.token_start = token_start + self.pattern = re.compile( + r"%s(%s)(?=\()" % (runtime_token_start, "|".join(known_calls)) + ) + self.preprocessors = [self.preprocess] + + def preprocess(self, source: str) -> str: + result = [] + start = 0 + for m in self.pattern.finditer(source): + pstart = m.end() + pend = find_balanced(source, pstart) + if determined(source[pstart + 1 : pend - 1]): + name = m.group(1) + result.append(source[start : m.start()]) + result.append(self.token_start + "ctx['" + name + "']") + start = pstart + if start: + result.append(source[start:]) + return "".join(result) + else: + return source + + +def determined(expression: str) -> bool: + """Checks if expresion is strictly determined. + + >>> determined("'default'") + True + >>> determined('name') + False + >>> determined("'default', id=id") + False + >>> determined("'default', lang=100") + True + >>> determined('') + True + """ + args, kwargs = parse_params(expression) + for arg in args: + if not str_or_int(arg): + return False + for arg in kwargs.values(): + if not str_or_int(arg): + return False + return True + + +def parse_kwargs(text: str) -> typing.Mapping[str, str]: + """Parses key-value type of parameters. + + >>> parse_kwargs('id=item.id') + {'id': 'item.id'} + >>> sorted(parse_kwargs('lang="en", id=12').items()) + [('id', '12'), ('lang', '"en"')] + """ + kwargs = {} + for m in RE_KWARGS.finditer(text + ","): + groups = m.groupdict() + kwargs[groups["name"].rstrip("_")] = groups["expr"] + return kwargs + + +def parse_args(text: str) -> typing.List[str]: + """Parses argument type of parameters. + + >>> parse_args('') + [] + >>> parse_args('10, "x"') + ['10', '"x"'] + >>> parse_args("'x', 100") + ["'x'", '100'] + >>> parse_args('"default"') + ['"default"'] + """ + args = [] + for m in RE_ARGS.finditer(text + ","): + args.append(m.group("expr")) + return args + + +def parse_params( + text: str, +) -> typing.Tuple[typing.List[str], typing.Mapping[str, str]]: + """Parses function parameters. + + >>> parse_params('') + ([], {}) + >>> parse_params('id=item.id') + ([], {'id': 'item.id'}) + >>> parse_params('"default"') + (['"default"'], {}) + >>> parse_params('"default", lang="en"') + (['"default"'], {'lang': '"en"'}) + """ + if "=" in text: + args = text.split("=")[0] + if "," in args: + args = args.rsplit(",", 1)[0] + kwargs = text[len(args) :] + return parse_args(args), parse_kwargs(kwargs) + else: + return [], parse_kwargs(text) + else: + return parse_args(text), {} + + +def str_or_int(text: str) -> bool: + """Ensures ``text`` as string or int expression. + + >>> str_or_int('"default"') + True + >>> str_or_int("'Hello'") + True + >>> str_or_int('100') + True + >>> str_or_int('item.id') + False + """ + m = RE_STR_VALUE.match(text) + if m: + return True + else: + m = RE_INT_VALUE.match(text) + if m: + return True + return False diff --git a/python/external/wheezy/template/ext/tests/__init__.py b/python/external/wheezy/template/ext/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/external/wheezy/template/ext/tests/test_code.py b/python/external/wheezy/template/ext/tests/test_code.py new file mode 100644 index 0000000..de5d9fd --- /dev/null +++ b/python/external/wheezy/template/ext/tests/test_code.py @@ -0,0 +1,151 @@ +import typing +import unittest + +from wheezy.template.engine import Engine +from wheezy.template.ext.code import CodeExtension +from wheezy.template.ext.core import CoreExtension +from wheezy.template.loader import DictLoader +from wheezy.template.typing import Token + + +class LexerTestCase(unittest.TestCase): + """Test the ``CodeExtension`` lexers.""" + + def setUp(self) -> None: + self.engine = Engine( + loader=DictLoader({}), extensions=[CodeExtension()] + ) + + def tokenize(self, source: str) -> typing.List[Token]: + return self.engine.lexer.tokenize(source) + + def test_code_token(self) -> None: + """Test code token.""" + tokens = self.tokenize("\n @(i = 1)") + assert 1 == len(tokens) + assert (1, "code", "(i = 1)") == tokens[0] + tokens = self.tokenize("@(i = 1)\n") + assert (1, "code", "(i = 1)\n") == tokens[0] + + +class ParserTestCase(unittest.TestCase): + """Test the ``CodeExtension`` parsers.""" + + def setUp(self) -> None: + self.engine = Engine( + loader=DictLoader({}), extensions=[CodeExtension()] + ) + + def parse(self, source: str) -> typing.List[typing.Any]: + return list( + self.engine.parser.parse(self.engine.lexer.tokenize(source)) + ) + + def test_code_empty(self) -> None: + """Test parse_code.""" + nodes = self.parse("@()") + assert [(1, "code", [""])] == nodes + + def test_code(self) -> None: + """Test parse_code.""" + nodes = self.parse("@(i = 1)") + assert [(1, "code", ["i = 1"])] == nodes + + def test_code_multiline(self) -> None: + """Test parse_code multiline.""" + nodes = self.parse( + """\ +@( + i = 1 + j = 0 +) +""" + ) + assert [(1, "code", ["", "i = 1", "j = 0", ""])] == nodes + + +class BuilderTestCase(unittest.TestCase): + """Test the ``CodeExtension`` generators.""" + + def setUp(self) -> None: + self.engine = Engine( + loader=DictLoader({}), extensions=[CodeExtension()] + ) + + def build_source(self, source: str) -> str: + nodes = list( + self.engine.parser.parse(self.engine.lexer.tokenize(source)) + ) + return self.engine.builder.build_source(nodes) + + def test_code(self) -> None: + assert "i = 1" == self.build_source("@(i = 1)") + assert "i = 1 " == self.build_source("@( i = 1 )") + + def test_code_multiline(self) -> None: + assert """\ + +i = 1 +j = 0 +""" == self.build_source( + """\ +@( + i = 1 + j = 0 +) +""" + ) + + def test_code_with_indent(self) -> None: + assert """\ + +def x(): + return 'x' +""" == self.build_source( + """\ +@( + def x(): + return 'x' +)""" + ) + + +class TemplateTestCase(unittest.TestCase): + """Test the ``CodeExtension`` compiled templates.""" + + def setUp(self) -> None: + self.templates: typing.Dict[str, str] = {} + self.engine = Engine( + loader=DictLoader(templates=self.templates), + extensions=[CoreExtension(), CodeExtension()], + ) + + def render(self, source: str) -> str: + self.templates["test.html"] = source + template = self.engine.get_template("test.html") + return template.render({}) + + def test_code_single_line(self) -> None: + assert "1" == self.render("@(i = 1)@i!s") + + def test_code_continue_newline(self) -> None: + assert "1" == self.render("@(i = 1)\\\n@i!s") + + def test_code_separated_lines(self) -> None: + assert "\n1" == self.render("@(i = 1)\n@i!s") + + def test_code_with_markup1(self) -> None: + assert "x = 1" == self.render("x@(i = 1) = @i!s") + + def test_code_with_markup2(self) -> None: + assert "x = 1" == self.render("x @(i = 1)= @i!s") + + def test_code_multiline(self) -> None: + assert "x = 1" == self.render( + """\ +x \ +@( + i = 1 +)\ += @i!s""" + ) diff --git a/python/external/wheezy/template/ext/tests/test_core.py b/python/external/wheezy/template/ext/tests/test_core.py new file mode 100644 index 0000000..f24cc19 --- /dev/null +++ b/python/external/wheezy/template/ext/tests/test_core.py @@ -0,0 +1,883 @@ +import typing +import unittest + +from wheezy.template.engine import Engine +from wheezy.template.ext.core import CoreExtension, all_tokens +from wheezy.template.loader import DictLoader +from wheezy.template.typing import Token + +hello = "\u043f\u0440\u0438\u0432\u0456\u0442" + + +class CleanSourceTestCase(unittest.TestCase): + """Test the ``clean_source``.""" + + def setUp(self) -> None: + self.clean_source = CoreExtension().preprocessors[0] + + def test_new_line(self) -> None: + """Replace windows new line with linux new line.""" + assert "a\nb" == self.clean_source("a\r\nb") + + def test_clean_leading_whitespace(self) -> None: + """Remove leading whitespace before @, e.g. @if, @for, etc.""" + for token in all_tokens: + assert "@" + token == self.clean_source(" @" + token) + assert "\n@" + token == self.clean_source("\n @" + token) + assert "a\n@" + token == self.clean_source("a\n @" + token) + + def test_leave_leading_whitespace(self) -> None: + """Leave leading whitespace before @ tokens.""" + assert "a\n\n @b" == self.clean_source("a\n\n @b") + assert "a\n @b" == self.clean_source("a\n @b") + assert "a\n@b" == self.clean_source("a\n@b") + assert "a@b" == self.clean_source("a@b") + assert " @b" == self.clean_source(" @b") + + def test_ignore(self) -> None: + """Ignore double @.""" + assert "a\n @@b" == self.clean_source("a\n @@b") + assert " @@b" == self.clean_source(" @@b") + + +class LexerTestCase(unittest.TestCase): + """Test the ``CoreExtension`` lexers.""" + + def setUp(self) -> None: + self.engine = Engine( + loader=DictLoader({}), extensions=[CoreExtension()] + ) + + def tokenize(self, source: str) -> typing.List[Token]: + return self.engine.lexer.tokenize(source) + + def test_stmt_token(self) -> None: + """Test statement token.""" + tokens = self.tokenize("@require(title, users)\n") + assert (1, "require", "require(title, users)") == tokens[0] + + def test_comment_token(self) -> None: + """Test statement token.""" + tokens = self.tokenize("@#ignore\\\n@end\n") + assert (1, "#", "#ignore@end") == tokens[0] + + def test_line_join(self) -> None: + """Test line join.""" + tokens = self.tokenize("a \\\nb") + assert (1, "markup", "a \\\nb") == tokens[0] + tokens = self.tokenize("a \\\\\nb") + assert (1, "markup", "a \\\\\nb") == tokens[0] + + def test_var_token(self) -> None: + """Test variable token.""" + tokens = self.tokenize("@user.name ") + assert (1, "var", "user.name") == tokens[0] + tokens = self.tokenize("@user.pref[i].fmt() ") + assert (1, "var", "user.pref[i].fmt()") == tokens[0] + tokens = self.tokenize('@f("()")') + assert (1, "var", 'f("()")') == tokens[0] + tokens = self.tokenize('@f("a@a!x")!h') + assert (1, "var", 'f("a@a!x")!!h') == tokens[0] + + def test_var_token_unicode(self) -> None: + """Test variable token with unicode string as argument.""" + t = '_("' + hello + '")' + tokens = self.tokenize("@" + t) + assert (1, "var", t) == tokens[0] + + def test_var_token_filter(self) -> None: + """Test variable token filter.""" + tokens = self.tokenize("@user.age!s") + assert (1, "var", "user.age!!s") == tokens[0] + tokens = self.tokenize("@user.age!s!h") + assert (1, "var", "user.age!!s!h") == tokens[0] + # escape or ignore ! + tokens = self.tokenize("@user.age!s!") + assert (1, "var", "user.age!!s") == tokens[0] + assert (1, "markup", "!") == tokens[1] + tokens = self.tokenize("@user.age!!s") + assert (1, "var", "user.age") == tokens[0] + assert (1, "markup", "!!s") == tokens[1] + tokens = self.tokenize("@user! ") + assert (1, "var", "user") == tokens[0] + assert (1, "markup", "! ") == tokens[1] + + def test_rvalue_token(self) -> None: + """Test rvalue token.""" + tokens = self.tokenize("@{user.name}") + assert (1, "var", "user.name") == tokens[0] + tokens = self.tokenize("@{ user.name }") + assert (1, "var", "user.name") == tokens[0] + tokens = self.tokenize("@{ s(user.age) }") + assert (1, "var", "s(user.age)") == tokens[0] + + def test_rvalue_token_unicode(self) -> None: + """Test rvalue token with unicode string as argument.""" + t = '_("' + hello + '")' + tokens = self.tokenize("@{ " + t + " }") + assert (1, "var", t) == tokens[0] + + def test_rvalue_token_filter(self) -> None: + """Test rvalue token filter.""" + tokens = self.tokenize("@{ user.age!!s }") + assert (1, "var", "user.age!!s") == tokens[0] + tokens = self.tokenize("@{ user.age!!s!h }") + assert (1, "var", "user.age!!s!h") == tokens[0] + + def test_markup_token(self) -> None: + """Test markup token.""" + tokens = self.tokenize(" test ") + assert 1 == len(tokens) + assert (1, "markup", " test ") == tokens[0] + tokens = self.tokenize("x@n") + assert (1, "markup", "x") == tokens[0] + + def test_markup_token_escape(self) -> None: + """Test markup token with escape.""" + tokens = self.tokenize("support@@acme.org") + assert 1 == len(tokens) + assert (1, "markup", "support@acme.org") == tokens[0] + + +class ParserTestCase(unittest.TestCase): + """Test the ``CoreExtension`` parsers.""" + + def setUp(self) -> None: + self.engine = Engine( + loader=DictLoader({}), extensions=[CoreExtension()] + ) + + def parse(self, source: str) -> typing.List[typing.Any]: + return list( + self.engine.parser.parse(self.engine.lexer.tokenize(source)) + ) + + def test_require(self) -> None: + """Test parse_require.""" + nodes = self.parse("@require(title, users)\n") + assert [(1, "require", ["title", "users"])] == nodes + + def test_extends(self) -> None: + """Test parse_extends.""" + nodes = self.parse('@extends("shared/master.html")\n') + assert [(1, "extends", ('"shared/master.html"', []))] == nodes + + def test_include(self) -> None: + """Test parse_include.""" + nodes = self.parse('@include("shared/scripts.html")\n') + assert [(1, "out", [(1, "include", '"shared/scripts.html"')])] == nodes + + def test_markup(self) -> None: + """Test parse_markup.""" + nodes = self.parse( + """ + Welcome, @name! +""" + ) + assert [ + ( + 1, + "out", + [ + (1, "markup", "'\\n Welcome, '"), + (2, "var", ("name", None)), + (2, "markup", "'!\\n'"), + ], + ) + ] == nodes + nodes = self.parse("") + assert [] == nodes + + def test_line_join(self) -> None: + nodes = self.parse("a \\\nb") + assert [(1, "out", [(1, "markup", "'a b'")])] == nodes + nodes = self.parse("\\\n") + assert [(1, "out", [(1, "markup", None)])] == nodes + nodes = self.parse("a \\\\\nb") + assert [(1, "out", [(1, "markup", "'a \\\\\\nb'")])] == nodes + + def test_var(self) -> None: + """Test parse_markup.""" + nodes = self.parse("@name!h!") + assert [ + (1, "out", [(1, "var", ("name", ["h"])), (1, "markup", "'!'")]) + ] == nodes + assert nodes == self.parse("@{ name!!h }!") + nodes = self.parse("@name!s!h!") + assert [ + ( + 1, + "out", + [(1, "var", ("name", ["s", "h"])), (1, "markup", "'!'")], + ) + ] == nodes + assert nodes == self.parse("@{ name!!s!h }!") + nodes = self.parse("@user.pref[i].fmt() ") + assert [ + ( + 1, + "out", + [ + (1, "var", ("user.pref[i].fmt()", None)), + (1, "markup", "' '"), + ], + ) + ] == nodes + assert nodes == self.parse("@{ user.pref[i].fmt() } ") + nodes = self.parse('@f("()")') + assert [(1, "out", [(1, "var", ('f("()")', None))])] == nodes + assert nodes == self.parse('@{ f("()") }') + nodes = self.parse('@f("a@a!x")!h') + assert [(1, "out", [(1, "var", ('f("a@a!x")', ["h"]))])] == nodes + assert nodes == self.parse('@{ f("a@a!x")!!h }') + + +class ParserLineJoinTestCase(unittest.TestCase): + """Test the ``CoreExtension`` parsers.""" + + def setUp(self) -> None: + self.engine = Engine( + loader=DictLoader({}), extensions=[CoreExtension(line_join="")] + ) + + def parse(self, source: str) -> typing.List[typing.Any]: + return list( + self.engine.parser.parse(self.engine.lexer.tokenize(source)) + ) + + def test_markup(self) -> None: + """Test parse_markup.""" + nodes = self.parse("") + assert [] == nodes + assert not self.engine.parser.rules["markup"]("") + + def test_line_join(self) -> None: + nodes = self.parse("a \\\nb") + assert [(1, "out", [(1, "markup", "'a \\\\\\nb'")])] == nodes + nodes = self.parse("a \\\\\nb") + assert [(1, "out", [(1, "markup", "'a \\\\\\\\\\nb'")])] == nodes + + +class BuilderTestCase(unittest.TestCase): + """Test the ``CoreExtension`` builders.""" + + def setUp(self) -> None: + self.engine = Engine( + loader=DictLoader({}), extensions=[CoreExtension()] + ) + + def build_source(self, source: str) -> str: + nodes = list( + self.engine.parser.parse(self.engine.lexer.tokenize(source)) + ) + return self.engine.builder.build_source(nodes) + + def build_render(self, source: str) -> str: + nodes = list( + self.engine.parser.parse(self.engine.lexer.tokenize(source)) + ) + return self.engine.builder.build_render(nodes) + + def test_markup(self) -> None: + assert "w('Hello')" == self.build_source("Hello") + assert "" == self.build_source("") + + def test_line_join(self) -> None: + assert "" == self.build_source("\\\n") + assert "w('a b')" == self.build_source("a \\\nb") + assert "w('a \\\\\\nb')" == self.build_source("a \\\\\nb") + + def test_comment(self) -> None: + assert """\ +w('Hello') +# comment +w(' World')""" == self.build_source( + """\ +Hello\\ +@# comment + World""" + ) + + def test_require(self) -> None: + assert """\ +title = ctx['title']; username = ctx['username'] +w(username)""" == self.build_source( + """\ +@require(title, username) +@username""" + ) + + def test_out(self) -> None: + """Test build_out.""" + expected = "w('Welcome, '); w(username); w('!')" + assert expected == self.build_source("Welcome, @username!") + assert expected == self.build_source("Welcome, @{ username }!") + expected = """\ +w('\\n\\n ') + +w(username); w('\\n')""" + assert expected == self.build_source( + """ + + @username +""" + ) + assert expected == self.build_source( + """ + + @{ username } +""" + ) + + def test_include(self) -> None: + """Test build_include.""" + expected = 'w(_r("a", ctx, local_defs, super_defs))' + assert expected == self.build_source('@include("a")') + + def test_if(self) -> None: + """Test if elif else statements.""" + assert """\ +if n > 0: + w(' Positive\\n') +elif n == 0: + w(' Zero\\n') +else: + w(' Negative\\n')""" == self.build_source( + """\ +@if n > 0: + Positive +@elif n == 0: + Zero +@else: + Negative +@end +""" + ) + + def test_for(self) -> None: + assert """\ +for color in colors: + w(' '); w(color); w('\\n')""" == self.build_source( + """\ +@for color in colors: + @color +@end +""" + ) + + def test_def(self) -> None: + """Test def statement.""" + assert """\ +def link(url, text): + _b = []; w = _b.append; w(' '); w(text); w('\\n') + return ''.join(_b) +super_defs['link'] = link; link = local_defs.setdefault('link', link) +w(' Please '); w(link('/en/signin', 'sign in')); w('.\\n')\ +""" == self.build_source( + """\ + @def link(url, text): + @text + @#ignore + @end + Please @link('/en/signin', 'sign in'). +""" + ) + + def test_def_empty(self) -> None: + """Test def statement with empty function body.""" + assert """\ +def title():return '' +super_defs['title'] = title; title = local_defs.setdefault('title', title) +w(title()); w('.')""" == self.build_source( + """\ +@def title(): +\ +@end +@title().""" + ) + + def test_def_single_markup(self) -> None: + """Test def statement with a single return markup.""" + assert """\ +def title(): + return ' Hello\\n' +super_defs['title'] = title; title = local_defs.setdefault('title', title) +w(title()); w('.')""" == self.build_source( + """\ +@def title(): + Hello +@end +@title().""" + ) + + def test_def_single_var(self) -> None: + """Test def statement with a single return var.""" + expected = """\ +def title(x): + _b = []; w = _b.append; w(x); return ''.join(_b) +super_defs['title'] = title; title = local_defs.setdefault('title', title); \ +w(title()); w('.')""" + assert expected == self.build_source( + """\ +@def title(x): +@x\ +@end +@title().""" + ) + assert expected == self.build_source( + """\ +@def title(x): +@{ x }\ +@end +@title().""" + ) + + def test_render(self) -> None: + """Test build_render.""" + assert """\ +def render(ctx, local_defs, super_defs): + + return 'Hello'""" == self.build_render( + "Hello" + ) + + def test_render_empty(self) -> None: + """Test build_render with return of empty string.""" + assert """\ +def render(ctx, local_defs, super_defs): + return ''""" == self.build_render( + "" + ) + """ Test build_render with return of empty string. + """ + assert """\ +def render(ctx, local_defs, super_defs): + return ''""" == self.build_render( + "" + ) + + def test_render_var(self) -> None: + """Test build_render with return of var.""" + assert """\ +def render(ctx, local_defs, super_defs): + _b = []; w = _b.append + w(h(a)) + return ''.join(_b)""" == self.build_render( + "@a!h" + ) + + def test_extends(self) -> None: + """Test build_extends.""" + assert """\ +def render(ctx, local_defs, super_defs): + return _r("base.html", ctx, local_defs, super_defs)\ +""" == self.build_render( + """\ +@extends("base.html") +""" + ) + + def test_extends_with_require(self) -> None: + """Test build_extends with require token.""" + assert """\ +def render(ctx, local_defs, super_defs): + + + path_for = ctx['path_for'] + return _r("base.html", ctx, local_defs, super_defs)\ +""" == self.build_render( + """\ +@extends("base.html") +@require(path_for) +""" + ) + + def test_extends_with_import(self) -> None: + """Test build_extends with import token.""" + assert """\ +def render(ctx, local_defs, super_defs): + + + widget = _i('shared/snippet/widget.html') + return _r("base.html", ctx, local_defs, super_defs)\ +""" == self.build_render( + """\ +@extends("base.html") +@import 'shared/snippet/widget.html' as widget +""" + ) + + def test_extends_with_from(self) -> None: + """Test build_extends with from token.""" + assert """\ +def render(ctx, local_defs, super_defs): + + + menu_item = _i('shared/snippet/widget.html').local_defs['menu_item'] + return _r("base.html", ctx, local_defs, super_defs)\ +""" == self.build_render( + """\ +@extends("base.html") +@from 'shared/snippet/widget.html' import menu_item +""" + ) + + +class TemplateTestCase(unittest.TestCase): + """Test the ``CoreExtension`` compiled templates.""" + + def render( + self, + source: str, + ctx: typing.Optional[typing.Mapping[str, typing.Any]] = None, + ) -> str: + loader = DictLoader({"test.html": source}) + engine = Engine(loader=loader, extensions=[CoreExtension()]) + template = engine.get_template("test.html") + return template.render(ctx or {}) + + def test_markup(self) -> None: + assert "Hello" == self.render("Hello") + assert "" == self.render("") + assert "" == self.render("\\\n") + + def test_line_join(self) -> None: + assert "a b" == self.render("a \\\nb") + + def test_line_join_escape(self) -> None: + assert "a \\\nb \\\nc" == self.render("a \\\\\nb \\\\\nc") + + def test_comment(self) -> None: + assert "Hello World" == self.render( + """\ +Hello\\ +@# comment + World""" + ) + + def test_var(self) -> None: + ctx = {"username": "John"} + expected = "Welcome, John!" + assert expected == self.render( + """\ +@require(username) +Welcome, @username!""", + ctx, + ) + assert expected == self.render( + """\ +@require(username) +Welcome, @{ username }!""", + ctx, + ) + + def test_var_unicode(self) -> None: + ctx = {"_": lambda x: x} + assert hello == self.render('@require(_)\n@_("' + hello + '")', ctx) + + def test_rvalue_unicode(self) -> None: + ctx = {"_": lambda x: x} + assert hello == self.render('@require(_)\n@{_("' + hello + '")}', ctx) + + def test_var_filter(self) -> None: + ctx = {"age": 36} + expected = "age: 36" + assert expected == self.render( + """\ +@require(age) +age: @age!s""", + ctx, + ) + assert expected == self.render( + """\ +@require(age) +age: @{ age !! s }""", + ctx, + ) + + def test_rvalue(self) -> None: + ctx = {"accepted": True} + assert "YES" == self.render( + """\ +@require(accepted) +@{ accepted and 'YES' or 'NO' }""", + ctx, + ) + ctx2 = {"age": 36} + assert "OK" == self.render( + """\ +@require(age) +@{ (age > 20 and age < 120) and 'OK' or '' }""", + ctx2, + ) + + def test_rvalue_filter(self) -> None: + ctx = {"n": 36} + assert "1" == self.render( + """\ +@require(n) +@{ n > 0 and 1 or -1 !! s }""", + ctx, + ) + + def test_if(self) -> None: + src = """\ +@require(n) +@if n > 0: + Positive\\ +@elif n == 0: + Zero\\ +@else: + Negative\\ +@end +""" + assert " Positive" == self.render(src, {"n": 1}) + assert " Zero" == self.render(src, {"n": 0}) + assert " Negative" == self.render(src, {"n": -1}) + + def test_for(self) -> None: + ctx = {"colors": ["red", "yellow"]} + assert " red\n yellow\n" == self.render( + """\ +@require(colors) +@for color in colors: + @color +@end +""", + ctx, + ) + + def test_def(self) -> None: + assert "Welcome, John!" == self.render( + """\ +@def welcome(name): +Welcome, @name!\\ +@end +@welcome('John')""" + ) + + def test_def_empty(self) -> None: + assert "." == self.render( + """\ +@def title(): +@end +@title().""" + ) + + def test_def_syntax_error_compound(self) -> None: + self.assertRaises( + SyntaxError, + lambda: self.render( + """\ +@def welcome(name): +@if name: +Welcome, @name!\\ +@end +@end +@welcome('John')""" + ), + ) + + def test_def_no_syntax_error(self) -> None: + assert "Welcome, John!" == self.render( + """\ +@def welcome(name): +@#ignore +@if name: +Welcome, @name!\\ +@end +@end +@welcome('John')""" + ) + assert "\nWelcome, John!" == self.render( + """\ +@def welcome(name): + +@if name: +Welcome, @name!\\ +@end +@end +@welcome('John')""" + ) + + +class MultiTemplateTestCase(unittest.TestCase): + """Test the ``CoreExtension`` compiled templates.""" + + def setUp(self) -> None: + self.templates: typing.Dict[str, str] = {} + self.engine = Engine( + loader=DictLoader(templates=self.templates), + extensions=[CoreExtension()], + ) + + def render(self, name: str, ctx: typing.Mapping[str, typing.Any]) -> str: + template = self.engine.get_template(name) + return template.render(ctx) + + def test_extends(self) -> None: + self.templates.update( + { + "master.html": """\ +@def say_hi(name): + Hello, @name! +@end +@say_hi('John')""", + "tmpl.html": """\ +@extends('master.html') +@def say_hi(name): + Hi, @name! +@end +""", + } + ) + assert " Hi, John!\n" == self.render("tmpl.html", {}) + assert " Hello, John!\n" == self.render("master.html", {}) + + def test_extends_dynamic(self) -> None: + self.templates.update( + { + "master.html": """\ +@def say_hi(name): + Hello, @name! +@end +@say_hi('John')""", + "tmpl.html": """ +@require(master) +@extends(master) +@def say_hi(name): + Hi, @name! +@end +""", + } + ) + assert " Hi, John!\n" == self.render( + "tmpl.html", {"master": "master.html"} + ) + assert " Hello, John!\n" == self.render("master.html", {}) + + def test_super(self) -> None: + self.templates.update( + { + "master.html": """\ +@def say_hi(name): + Hello, @name!\ +@end +@say_hi('John')""", + "tmpl.html": """\ +@extends('master.html') +@def say_hi(name): + @super_defs['say_hi'](name)!!\ +@end +""", + } + ) + assert " Hello, John!!!" == self.render("tmpl.html", {}) + + def test_include(self) -> None: + self.templates.update( + { + "footer.html": """\ +@require(name) +Thanks, @name""", + "tmpl.html": """\ +Welcome to my site. +@include('footer.html') +""", + } + ) + ctx = {"name": "John"} + assert """\ +Welcome to my site. +Thanks, John""" == self.render( + "tmpl.html", ctx + ) + assert "Thanks, John" == self.render("footer.html", ctx) + + def test_import(self) -> None: + self.templates.update( + { + "helpers.html": """\ +@def say_hi(name): +Hi, @name\ +@end""", + "tmpl.html": """\ +@import 'helpers.html' as helpers +@helpers.say_hi('John')""", + } + ) + assert """\ +Hi, John""" == self.render( + "tmpl.html", {} + ) + + def test_import_dynamic(self) -> None: + self.templates.update( + { + "helpers.html": """\ +@def say_hi(name): +Hi, @name\ +@end""", + "tmpl.html": """\ +@require(helpers_impl) +@import helpers_impl as helpers +@helpers.say_hi('John')""", + } + ) + assert """\ +Hi, John""" == self.render( + "tmpl.html", {"helpers_impl": "helpers.html"} + ) + + def test_from_import(self) -> None: + self.templates.update( + { + "helpers.html": """\ +@def say_hi(name): +Hi, @name\ +@end""", + "tmpl.html": """\ +@from 'helpers.html' import say_hi +@say_hi('John')""", + } + ) + assert """\ +Hi, John""" == self.render( + "tmpl.html", {} + ) + + def test_from_import_dynamic(self) -> None: + self.templates.update( + { + "helpers.html": """\ +@def say_hi(name): +Hi, @name\ +@end""", + "tmpl.html": """\ +@require(helpers_impl) +@from helpers_impl import say_hi +@say_hi('John')""", + } + ) + assert """\ +Hi, John""" == self.render( + "tmpl.html", {"helpers_impl": "helpers.html"} + ) + + def test_from_import_as(self) -> None: + self.templates.update( + { + "share/helpers.html": """\ +@def say_hi(name): +Hi, @name\ +@end""", + "tmpl.html": """\ +@from 'share/helpers.html' import say_hi as hi +@hi('John')""", + } + ) + assert """\ +Hi, John""" == self.render( + "tmpl.html", {} + ) diff --git a/python/external/wheezy/template/ext/tests/test_determined.py b/python/external/wheezy/template/ext/tests/test_determined.py new file mode 100644 index 0000000..9e1ea14 --- /dev/null +++ b/python/external/wheezy/template/ext/tests/test_determined.py @@ -0,0 +1,43 @@ +""" Unit tests for ``wheezy.templates.ext.determined``. +""" + +import unittest + +from wheezy.template.ext.determined import DeterminedExtension + + +class DeterminedTestCase(unittest.TestCase): + """Test the ``DeterminedExtension``.""" + + def setUp(self) -> None: + self.preprocess = DeterminedExtension( + known_calls=["path_for", "_"] + ).preprocessors[0] + + def test_determined(self) -> None: + """Substitute determinded expressions for known calls to + preprocessor calls. + """ + assert """\ + #ctx['_']('Name:') + #ctx['path_for']('default') + #ctx['path_for']('static', path='/static/css/site.css') + """ == self.preprocess( + """\ + @_('Name:') + @path_for('default') + @path_for('static', path='/static/css/site.css') + """ + ) + + def test_undetermined(self) -> None: + """Calls that are not determined left unchanged.""" + assert """\ + @path_for('item', id=id) + @model.username.label(_('Username: ')) + """ == self.preprocess( + """\ + @path_for('item', id=id) + @model.username.label(_('Username: ')) + """ + ) diff --git a/python/external/wheezy/template/lexer.py b/python/external/wheezy/template/lexer.py new file mode 100644 index 0000000..5ae0455 --- /dev/null +++ b/python/external/wheezy/template/lexer.py @@ -0,0 +1,80 @@ +import typing + +from external.wheezy.template.typing import ( + LexerRule, + PostProcessorRule, + PreProcessorRule, + Token, +) + + +def lexer_scan( + extensions: typing.List[typing.Any], +) -> typing.Mapping[str, typing.Any]: + """Scans extensions for ``lexer_rules`` and ``preprocessors`` + attributes. + """ + lexer_rules: typing.Dict[int, LexerRule] = {} + preprocessors: typing.List[PreProcessorRule] = [] + postprocessors: typing.List[PostProcessorRule] = [] + for extension in extensions: + if hasattr(extension, "lexer_rules"): + lexer_rules.update(extension.lexer_rules) + if hasattr(extension, "preprocessors"): + preprocessors.extend(extension.preprocessors) + if hasattr(extension, "postprocessors"): + postprocessors.extend(extension.postprocessors) + return { + "lexer_rules": [lexer_rules[k] for k in sorted(lexer_rules.keys())], + "preprocessors": preprocessors, + "postprocessors": postprocessors, + } + + +class Lexer(object): + """Tokenizes input source per rules supplied.""" + + def __init__( + self, + lexer_rules: typing.List[LexerRule], + preprocessors: typing.Optional[typing.List[PreProcessorRule]] = None, + postprocessors: typing.Optional[typing.List[PostProcessorRule]] = None, + **ignore: typing.Any + ) -> None: + """Initializes with ``rules``. Rules must be a list of + two elements tuple: ``(regex, tokenizer)`` where + tokenizer if a callable of the following contract:: + + def tokenizer(match): + return end_index, token, value + """ + self.rules = lexer_rules + self.preprocessors = preprocessors or [] + self.postprocessors = postprocessors or [] + + def tokenize(self, source: str) -> typing.List[Token]: + """Translates ``source`` accoring to lexer rules into + an iteratable of tokens. + """ + for preprocessor in self.preprocessors: + source = preprocessor(source) + tokens: typing.List[Token] = [] + append = tokens.append + pos = 0 + lineno = 1 + end = len(source) + while pos < end: + for regex, tokenizer in self.rules: + m = regex.match(source, pos, end) + if m is not None: + npos, token, value = tokenizer(m) + assert npos > pos + append((lineno, token, value)) + lineno += source[pos:npos].count("\n") + pos = npos + break + else: + raise AssertionError("Lexer pattern mismatch.") + for postprocessor in self.postprocessors: + postprocessor(tokens) + return tokens diff --git a/python/external/wheezy/template/loader.py b/python/external/wheezy/template/loader.py new file mode 100644 index 0000000..c66a9a8 --- /dev/null +++ b/python/external/wheezy/template/loader.py @@ -0,0 +1,208 @@ +import os +import os.path +import stat +import time +import typing + +from external.wheezy.template.engine import Engine +from external.wheezy.template.typing import Loader, SupportsRender + + +class FileLoader(Loader): + """Loads templates from file system. + + ``directories`` - search path of directories to scan for template. + ``encoding`` - decode template content per encoding. + """ + + def __init__( + self, directories: typing.List[str], encoding: str = "UTF-8" + ) -> None: + searchpath: typing.List[str] = [] + for path in directories: + abspath = os.path.abspath(path) + assert os.path.exists(abspath) + assert os.path.isdir(abspath) + searchpath.append(abspath) + self.searchpath = searchpath + self.encoding = encoding + + def list_names(self) -> typing.Tuple[str, ...]: + """Return a list of names relative to directories. Ignores any files + and directories that start with dot. + """ + names = [] + for path in self.searchpath: + pathlen = len(path) + 1 + for dirpath, dirnames, filenames in os.walk(path): + for i in [ + i + for i, name in enumerate(dirnames) + if name.startswith(".") + ]: + del dirnames[i] + for filename in filenames: + if filename.startswith("."): + continue + name = os.path.join(dirpath, filename)[pathlen:] + name = name.replace("\\", "/") + names.append(name) + return tuple(sorted(names)) + + def get_fullname(self, name: str) -> typing.Optional[str]: + """Returns a full path by a template name.""" + for path in self.searchpath: + filename = os.path.join(path, name) + if not os.path.exists(filename): + continue + if not os.path.isfile(filename): + continue + return filename + else: + return None + + def load(self, name: str) -> typing.Optional[str]: + """Loads a template by name from file system.""" + filename = self.get_fullname(name) + if filename: + f = open(filename, "rb") + try: + return f.read().decode(self.encoding) + finally: + f.close() + return None + + +class DictLoader(Loader): + """Loads templates from python dictionary. + + ``templates`` - a dict where key corresponds to template name and + value to template content. + """ + + def __init__(self, templates: typing.Mapping[str, str]) -> None: + self.templates = templates + + def list_names(self) -> typing.Tuple[str, ...]: + """List all keys from internal dict.""" + return tuple(sorted(self.templates.keys())) + + def load(self, name: str) -> typing.Optional[str]: + """Returns template by name.""" + if name not in self.templates: + return None + return self.templates[name] + + +class ChainLoader(Loader): + """Loads templates from ``loaders`` until first succeed.""" + + def __init__(self, loaders: typing.List[Loader]) -> None: + self.loaders = loaders + + def list_names(self) -> typing.Tuple[str, ...]: + """Returns as list of names from all loaders.""" + names = set() + for loader in self.loaders: + names |= set(loader.list_names()) + return tuple(sorted(names)) + + def load(self, name: str) -> typing.Optional[str]: + """Returns template by name from the first loader that succeed.""" + for loader in self.loaders: + source = loader.load(name) + if source is not None: + return source + return None + + +class PreprocessLoader(Loader): + """Performs preprocessing of loaded template.""" + + def __init__( + self, + engine: Engine, + ctx: typing.Optional[typing.Mapping[str, typing.Any]] = None, + ) -> None: + self.engine = engine + self.ctx = ctx or {} + + def list_names(self) -> typing.Tuple[str, ...]: + return self.engine.loader.list_names() + + def load(self, name: str) -> str: + return self.engine.render(name, self.ctx, {}, {}) + + +def autoreload(engine: Engine, enabled: bool = True) -> Engine: + """Auto reload template if changes are detected in file. + + Limitation: master (inherited), imported and preprocessed templates. + + It is recommended to use application server that supports + file reload instead. + """ + if not enabled: + return engine + return AutoReloadProxy(engine) + + +# region: internal details + + +class AutoReloadProxy(Engine): + def __init__(self, engine: Engine): + from warnings import warn + + self.engine = engine + self.names: typing.Dict[str, int] = {} + warn( + "autoreload limitation: master (inherited), imported " + "and preprocessed templates. It is recommended to use " + "application server that supports file reload instead.", + stacklevel=3, + ) + + def get_template(self, name: str) -> SupportsRender: + if self.file_changed(name): + self.remove(name) + return self.engine.get_template(name) + + def render( + self, + name: str, + ctx: typing.Mapping[str, typing.Any], + local_defs: typing.Mapping[str, typing.Any], + super_defs: typing.Mapping[str, typing.Any], + ) -> str: + if self.file_changed(name): + self.remove(name) + return self.engine.render(name, ctx, local_defs, super_defs) + + def remove(self, name: str) -> None: + self.engine.remove(name) + + # region: internal details + + def __getattr__(self, name: str) -> typing.Any: + return getattr(self.engine, name) + + def file_changed(self, name: str) -> bool: + try: + last_known_stamp = self.names[name] + current_time = int(time.time()) + if current_time - last_known_stamp <= 2: + return False + except KeyError: + last_known_stamp = 0 + + loader = self.engine.loader + abspath = loader.get_fullname(name) # type: ignore[attr-defined] + if not abspath: + return False + + last_modified_stamp = os.stat(abspath)[stat.ST_MTIME] + if last_modified_stamp <= last_known_stamp: + return False + self.names[name] = last_modified_stamp + return True diff --git a/python/external/wheezy/template/parser.py b/python/external/wheezy/template/parser.py new file mode 100644 index 0000000..0ef4f26 --- /dev/null +++ b/python/external/wheezy/template/parser.py @@ -0,0 +1,82 @@ +import typing + +from external.wheezy.template.typing import ParserConfig, ParserRule, Token + + +def parser_scan( + extensions: typing.List[typing.Any], +) -> typing.Mapping[str, typing.Any]: + parser_rules = {} + parser_configs = [] + for extension in extensions: + if hasattr(extension, "parser_rules"): + parser_rules.update(extension.parser_rules) + if hasattr(extension, "parser_configs"): + parser_configs.extend(extension.parser_configs) + return { + "parser_rules": parser_rules, + "parser_configs": parser_configs, + } + + +class Parser(ParserConfig): + """ + ``continue_tokens`` are used to insert ``end`` node right + before them to simulate a block end. Such nodes have token + value ``None``. + + ``out_tokens`` are combined together into a single node. + """ + + def __init__( + self, + parser_rules: typing.Dict[str, ParserRule], + parser_configs: typing.Optional[ + typing.List[typing.Callable[[ParserConfig], None]] + ] = None, + **ignore: typing.Any + ) -> None: + self.end_tokens: typing.List[str] = [] + self.continue_tokens: typing.List[str] = [] + self.compound_tokens: typing.List[str] = [] + self.out_tokens: typing.List[str] = [] + self.rules = parser_rules + if parser_configs: + for config in parser_configs: + config(self) + + def end_continue( + self, tokens: typing.List[Token] + ) -> typing.Iterator[Token]: + """If token is in ``continue_tokens`` prepend it + with end token so it simulate a closed block. + """ + for t in tokens: + if t[1] in self.continue_tokens: + yield (t[0], "end", "") + yield t + + def parse_iter( + self, tokens: typing.Iterator[Token] + ) -> typing.Iterator[typing.Any]: + operands = [] + for lineno, token, value in tokens: + if token in self.rules: + value = self.rules[token](value) # type: ignore[assignment] + if token in self.out_tokens: + operands.append((lineno, token, value)) + else: + if operands: + yield operands[0][0], "out", operands + operands = [] + if token in self.compound_tokens: + yield lineno, token, (value, list(self.parse_iter(tokens))) + else: + yield lineno, token, value + if token in self.end_tokens: + break + if operands: + yield operands[0][0], "out", operands + + def parse(self, tokens: typing.List[Token]) -> typing.List[typing.Any]: + return list(self.parse_iter(self.end_continue(tokens))) diff --git a/python/external/wheezy/template/preprocessor.py b/python/external/wheezy/template/preprocessor.py new file mode 100644 index 0000000..3e633ee --- /dev/null +++ b/python/external/wheezy/template/preprocessor.py @@ -0,0 +1,107 @@ +import typing + +from external.wheezy.template.comp import allocate_lock # type: ignore[attr-defined] +from external.wheezy.template.engine import Engine +from external.wheezy.template.loader import ChainLoader, DictLoader +from external.wheezy.template.typing import Loader, SupportsRender + + +class Preprocessor(object): + """Preprocess templates with ``engine`` and vary runtime templates + by ``key_factory`` function using ``runtime_engine_factory``. + """ + + def __init__( + self, + runtime_engine_factory: typing.Callable[[Loader], Engine], + engine: Engine, + key_factory: typing.Callable[[typing.Mapping[str, typing.Any]], str], + ) -> None: + self.lock = allocate_lock() + self.runtime_engines: typing.Dict[str, Engine] = {} + self.runtime_engine_factory = runtime_engine_factory + self.engine = engine + self.loader = engine.loader + self.key_factory = key_factory + template_class = self.engine.template_class + self.engine.template_class = lambda name, _: template_class( + name, + lambda ctx, local_defs, super_defs: self.render( + name, ctx, local_defs, super_defs + ), + ) + + def get_template(self, name: str) -> SupportsRender: + return self.engine.get_template(name) + + def render( + self, + name: str, + ctx: typing.Mapping[str, typing.Any], + local_defs: typing.Mapping[str, typing.Any], + super_defs: typing.Mapping[str, typing.Any], + ) -> str: + try: + runtime_engine = self.runtime_engines[self.key_factory(ctx)] + except KeyError: + runtime_engine = self.ensure_runtime_engine(self.key_factory(ctx)) + try: + return runtime_engine.renders[name](ctx, local_defs, super_defs) + except KeyError: + self.preprocess_template(runtime_engine, name, ctx) + return runtime_engine.renders[name](ctx, local_defs, super_defs) + + def remove(self, name: str) -> None: + self.lock.acquire(True) + try: + self.engine.remove(name) + for runtime_engine in self.runtime_engines.values(): + runtime_engine.remove(name) + finally: + self.lock.release() + + # region: internal details + + def ensure_runtime_engine(self, key: str) -> Engine: + self.lock.acquire(True) + try: + engines = self.runtime_engines + if key in engines: # pragma: nocover + return engines[key] + engine = engines[key] = self.runtime_engine_factory( + ChainLoader([DictLoader({}), self.engine.loader]) + ) + + def render( + name: str, + ctx: typing.Mapping[str, typing.Any], + local_defs: typing.Mapping[str, typing.Any], + super_defs: typing.Mapping[str, typing.Any], + ) -> str: + try: + return engine.renders[name](ctx, local_defs, super_defs) + except KeyError: + self.preprocess_template(engine, name, ctx) + return engine.renders[name](ctx, local_defs, super_defs) + + engine.global_vars["_r"] = render + return engine + finally: + self.lock.release() + + def preprocess_template( + self, + runtime_engine: Engine, + name: str, + ctx: typing.Mapping[str, typing.Any], + ) -> None: + self.lock.acquire(True) + try: + if name not in runtime_engine.renders: + source = self.engine.render(name, ctx, {}, {}) + loader = runtime_engine.loader.loaders[0] # type: ignore + loader.templates[name] = source + runtime_engine.compile_template(name) + del loader.templates[name] + finally: + self.lock.release() diff --git a/python/external/wheezy/template/py.typed b/python/external/wheezy/template/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/python/external/wheezy/template/tests/__init__.py b/python/external/wheezy/template/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/external/wheezy/template/tests/test_builder.py b/python/external/wheezy/template/tests/test_builder.py new file mode 100644 index 0000000..5e78a3a --- /dev/null +++ b/python/external/wheezy/template/tests/test_builder.py @@ -0,0 +1,75 @@ +import unittest + +from wheezy.template.builder import BlockBuilder + + +class BlockBuilderTestCase(unittest.TestCase): + """Test the ``BlockBuilder`` blocks.""" + + def setUp(self) -> None: + self.builder = BlockBuilder({}) + + def test_start_end_block(self) -> None: + """Test start_block and end_block.""" + self.builder.start_block() + assert self.builder.indent == " " + self.builder.end_block() + assert self.builder.indent == "" + + def test_unexpected_end_block(self) -> None: + """Test raises error.""" + self.assertRaises(SyntaxError, lambda: self.builder.end_block()) + + def test_inconsistence(self) -> None: + """Test add a line with wrong lineno.""" + self.assertRaises(SyntaxError, lambda: self.builder.add(-1, "")) + + def test_unknown_token(self) -> None: + """Test raises error if token is unknown.""" + self.assertRaises( + SyntaxError, lambda: self.builder.build_token(1, "x", "") + ) + + +class BlockBuilderAddSameLineTestCase(unittest.TestCase): + """Test the ``BlockBuilder.add`` to the same line.""" + + def setUp(self) -> None: + self.builder = BlockBuilder({}, indent=" ", lineno=1) + + def test_code_empty(self) -> None: + self.builder.buf = [""] + self.builder.add(1, "") + assert "" == self.builder.to_string() + + def test_line_empty(self) -> None: + self.builder.buf = [""] + self.builder.add(1, "pass") + assert " pass" == self.builder.to_string() + + def test_line_ends_colon(self) -> None: + self.builder.buf = ["def title():"] + self.builder.add(1, 'return ""') + assert 'def title():return ""' == self.builder.to_string() + + def test_continue_same_line(self) -> None: + self.builder.buf = ["pass"] + self.builder.add(1, "pass") + assert "pass; pass" == self.builder.to_string() + + +class BlockBuilderAddNextLineTestCase(unittest.TestCase): + """Test the ``BlockBuilder.add`` to add a new line.""" + + def setUp(self) -> None: + self.builder = BlockBuilder({}, indent=" ") + + def test_code_empty(self) -> None: + self.builder.add(1, "pass") + self.builder.add(2, "") + assert " pass\n" == self.builder.to_string() + + def test_pad(self) -> None: + self.builder.add(1, "pass") + self.builder.add(3, "pass") + assert " pass\n\n pass" == self.builder.to_string() diff --git a/python/external/wheezy/template/tests/test_console.py b/python/external/wheezy/template/tests/test_console.py new file mode 100644 index 0000000..a4eefbe --- /dev/null +++ b/python/external/wheezy/template/tests/test_console.py @@ -0,0 +1,30 @@ +""" Unit tests for ``wheezy.templates.console``. +""" + +import unittest + +from wheezy.template.console import main + + +class ConsoleTestCase(unittest.TestCase): + """Test the console ``main`` function.""" + + def test_usage(self) -> None: + assert 2 == main(["-h"]) + assert 2 == main(["-t @"]) + assert 2 == main(["-j \\"]) + assert 2 == main(["-x"]) + + def test_context_file(self) -> None: + assert 0 == main( + ["demos/helloworld/hello.txt", "demos/helloworld/hello.json"] + ) + + def test_context_string(self) -> None: + assert 0 == main(["demos/helloworld/hello.txt", '{"name": "World"}']) + + def test_master(self) -> None: + assert 0 == main(["-s", "demos/master", "index.html"]) + + def test_line_join(self) -> None: + assert 0 == main(["-j", "\\", "-s", "demos/master", "index.html"]) diff --git a/python/external/wheezy/template/tests/test_engine.py b/python/external/wheezy/template/tests/test_engine.py new file mode 100644 index 0000000..fd119bc --- /dev/null +++ b/python/external/wheezy/template/tests/test_engine.py @@ -0,0 +1,72 @@ +import typing +import unittest + +from wheezy.template.engine import Engine +from wheezy.template.ext.core import CoreExtension +from wheezy.template.loader import DictLoader + + +class EngineTestCase(unittest.TestCase): + """Test the ``Engine``.""" + + def setUp(self) -> None: + self.templates: typing.Dict[str, str] = {"a": ""} + self.engine = Engine( + loader=DictLoader(templates=self.templates), + extensions=[CoreExtension()], + ) + + def test_template_not_found(self) -> None: + """Raises IOError.""" + self.assertRaises(IOError, lambda: self.engine.get_template("x")) + + def test_import_not_found(self) -> None: + """Raises IOError.""" + self.assertRaises(IOError, lambda: self.engine.import_name("x")) + + def test_remove_unknown_name(self) -> None: + """Invalidate name that is not known to engine.""" + self.engine.remove("x") + + def test_remove_name(self) -> None: + """Invalidate name that is known to engine.""" + # self.templates["a"] = "" + self.engine.compile_import("a") + self.engine.compile_template("a") + self.engine.remove("a") + + +class EngineSyntaxErrorTestCase(unittest.TestCase): + """Test the ``Engine``.""" + + def setUp(self) -> None: + self.templates: typing.Dict[str, str] = {} + self.engine = Engine( + loader=DictLoader(templates=self.templates), + extensions=[CoreExtension()], + ) + + def test_compile_template_error(self) -> None: + """Raises SyntaxError.""" + self.templates[ + "x" + ] = """ + @if : + @end + """ + self.assertRaises( + SyntaxError, lambda: self.engine.compile_template("x") + ) + + def test_compile_import_error(self) -> None: + """Raises SyntaxError.""" + self.templates[ + "m" + ] = """ + @def x(): + @# ignore + @if : + @end + @end + """ + self.assertRaises(SyntaxError, lambda: self.engine.compile_import("m")) diff --git a/python/external/wheezy/template/tests/test_lexer.py b/python/external/wheezy/template/tests/test_lexer.py new file mode 100644 index 0000000..3882201 --- /dev/null +++ b/python/external/wheezy/template/tests/test_lexer.py @@ -0,0 +1,53 @@ +import re +import typing +import unittest + +from wheezy.template.lexer import Lexer, lexer_scan +from wheezy.template.typing import Token + + +class LexerTestCase(unittest.TestCase): + """Test the ``Lexer``.""" + + def test_tokenize(self) -> None: + """Test with simple rules""" + + def word_token(m: typing.Match[str]) -> Token: + return m.end(), "w", m.group() + + def blank_token(m: typing.Match[str]) -> Token: + return m.end(), "b", m.group() + + def to_upper(s: str) -> str: + return s.upper() + + def cleanup(tokens: typing.List[Token]) -> None: + for i in range(len(tokens)): + t = tokens[i] + if t[i] == "b": + tokens[i] = (t[0], "b", " ") + + class Extension(object): + lexer_rules = { + 100: (re.compile(r"\w+"), word_token), + 200: (re.compile(r"\s+"), blank_token), + } + preprocessors = [to_upper] + postprocessors = [cleanup] + + lexer = Lexer(**lexer_scan([Extension])) + assert [ + (1, "w", "HELLO"), + (1, "b", " "), + (2, "w", "WORLD"), + ] == lexer.tokenize("hello\n world") + + def test_trivial(self) -> None: + """Empty rules and source""" + lexer = Lexer([]) + assert [] == lexer.tokenize("") + + def test_raises_error(self) -> None: + """If there is no match it raises AssertionError.""" + lexer = Lexer([]) + self.assertRaises(AssertionError, lambda: lexer.tokenize("test")) diff --git a/python/external/wheezy/template/tests/test_loader.py b/python/external/wheezy/template/tests/test_loader.py new file mode 100644 index 0000000..10547a5 --- /dev/null +++ b/python/external/wheezy/template/tests/test_loader.py @@ -0,0 +1,201 @@ +import os.path +import stat +import unittest +import warnings +from time import time +from unittest.mock import Mock, patch + +from wheezy.template.engine import Engine +from wheezy.template.ext.core import CoreExtension +from wheezy.template.loader import ( + AutoReloadProxy, + ChainLoader, + DictLoader, + FileLoader, + PreprocessLoader, + autoreload, +) + + +class FileLoaderTestCase(unittest.TestCase): + """Test the ``FileLoader``.""" + + def setUp(self) -> None: + self.tmpldir = os.path.dirname(__file__) + self.loader = FileLoader(directories=[self.tmpldir]) + + @patch("os.walk") + def test_get_template(self, mock_walk: Mock) -> None: + mock_walk.return_value = [ + ( + self.tmpldir + "/", + [".ignore", "shared"], + [".ignore", "tmpl1.html"], + ), + ( + self.tmpldir + "/shared", + [".ignore", "snippet"], + ["master.html", ".ignore"], + ), + ( + self.tmpldir + "/shared/snippet", + [".ignore"], + [".ignore", "script.html"], + ), + ] + assert ( + "shared/master.html", + "shared/snippet/script.html", + "tmpl1.html", + ) == self.loader.list_names() + + def test_load_existing(self) -> None: + """Tests load.""" + assert "" == self.loader.load("__init__.py") + + def test_load_not_found(self) -> None: + """Tests load if the name is not found.""" + assert not self.loader.load("tmpl-x.html") + + def test_load_not_a_file(self) -> None: + """Tests load if the name is not a file.""" + assert not self.loader.load("..") + + +class DictLoaderTestCase(unittest.TestCase): + """Test the ``DictLoader``.""" + + def setUp(self) -> None: + self.loader = DictLoader( + templates={"tmpl1.html": "x", "shared/master.html": "x"} + ) + + def test_list_names(self) -> None: + """Tests list_names.""" + assert ("shared/master.html", "tmpl1.html") == self.loader.list_names() + + def test_load_existing(self) -> None: + """Tests load.""" + assert "x" == self.loader.load("tmpl1.html") + + def test_load_not_found(self) -> None: + """Tests load if the name is not found.""" + assert self.loader.load("tmpl-x.html") is None + + +class ChainLoaderTestCase(unittest.TestCase): + """Test the ``ChainLoader``.""" + + def setUp(self) -> None: + + self.loader = ChainLoader( + loaders=[ + DictLoader( + templates={ + "tmpl1.html": "x1", + } + ), + DictLoader(templates={"shared/master.html": "x2"}), + ] + ) + + def test_list_names(self) -> None: + """Tests list_names.""" + assert ("shared/master.html", "tmpl1.html") == self.loader.list_names() + + def test_load_existing(self) -> None: + """Tests load.""" + assert "x1" == self.loader.load("tmpl1.html") + assert "x2" == self.loader.load("shared/master.html") + + def test_load_missing(self) -> None: + """Tests load not found.""" + assert not self.loader.load("missing") + + +class PreprocessLoaderTestCase(unittest.TestCase): + """Test the ``PreprocessLoader``.""" + + def setUp(self) -> None: + templates = {"tmpl1.html": "x1", "shared/master.html": "x2"} + engine = Engine( + loader=DictLoader(templates=templates), + extensions=[CoreExtension()], + ) + self.loader = PreprocessLoader(engine, {"x": 1}) + + def test_list_names(self) -> None: + """Tests list_names.""" + assert ("shared/master.html", "tmpl1.html") == self.loader.list_names() + + def test_load_existing(self) -> None: + """Tests load existing.""" + assert "x1" == self.loader.load("tmpl1.html") + + +class AutoReloadProxyTestCase(unittest.TestCase): + """Test the ``PreprocessLoader``.""" + + def setUp(self) -> None: + self.mock_engine = Mock() + self.mock_engine.loader.get_fullname.return_value = "/t.html" + self.mock_template = Mock() + self.mock_engine.get_template.return_value = self.mock_template + self.mock_engine.render.return_value = "x" + warnings.simplefilter("ignore") + self.proxy = AutoReloadProxy(self.mock_engine) + warnings.simplefilter("default") + + def test_disabled(self) -> None: + """Tests autoreload disabled.""" + assert autoreload(self.mock_engine, enabled=False) is self.mock_engine + + def test_enabled(self) -> None: + """Tests autoreload enabled.""" + assert ( + autoreload(self.mock_engine, enabled=True) is not self.mock_engine + ) + + @patch("os.stat") + def test_get_template(self, mock_stat: Mock) -> None: + """Tests get_template.""" + mock_stat.return_value.__getitem__.return_value = 777 + assert self.mock_template == self.proxy.get_template("t.html") + self.mock_engine.loader.get_fullname.assert_called_once_with("t.html") + mock_stat.assert_called_once_with("/t.html") + mock_stat.return_value.__getitem__.assert_called_once_with( + stat.ST_MTIME + ) + assert 777 == self.proxy.names["t.html"] + + @patch("os.stat") + def test_render(self, mock_stat: Mock) -> None: + """Tests render.""" + mock_stat.return_value.__getitem__.return_value = 777 + ctx = {"1": 1} + local_defs = {"2": 2} + super_defs = {"3": 3} + assert "x" == self.proxy.render("t.html", ctx, local_defs, super_defs) + + def test_getattr(self) -> None: + """Tests __getattr__.""" + self.mock_engine.x = 100 + assert 100 == self.proxy.x + + def test_file_not_found(self) -> None: + """Tests file not found.""" + self.mock_engine.loader.get_fullname.return_value = None + assert not self.proxy.file_changed("t.html") + self.mock_engine.loader.get_fullname.assert_called_once_with("t.html") + + def test_file_not_changed_first_access(self) -> None: + """Tests not changed on first access.""" + self.proxy.names["t.html"] = int(time()) + assert not self.proxy.file_changed("t.html") + + @patch("os.stat") + def test_file_not_changed_known(self, mock_stat: Mock) -> None: + """Test not changed for the file that was previously accessed.""" + self.proxy.names["t.html"] = 777 + mock_stat.return_value.__getitem__.return_value = 777 + assert not self.proxy.file_changed("t.html") diff --git a/python/external/wheezy/template/tests/test_parser.py b/python/external/wheezy/template/tests/test_parser.py new file mode 100644 index 0000000..7bce86b --- /dev/null +++ b/python/external/wheezy/template/tests/test_parser.py @@ -0,0 +1,71 @@ +import unittest + +from wheezy.template.parser import Parser + + +class ParserTestCase(unittest.TestCase): + """Test the ``Parser``.""" + + def setUp(self) -> None: + self.parser = Parser({}) + self.tokens = [ + (1, "a", "11"), + (2, "b", "12"), + (3, "c", "13"), + (4, "b", "14"), + (5, "c", "15"), + ] + + def test_end_continue(self) -> None: + """Ensure end nodes are inserted before continue tokens.""" + self.parser.continue_tokens = ["b"] + nodes = list(self.parser.end_continue(self.tokens)) + assert 7 == len(nodes) + assert (2, "end", "") == nodes[1] + assert (2, "b", "12") == nodes[2] + assert (4, "end", "") == nodes[4] + assert (4, "b", "14") == nodes[5] + + def test_parse_unchanged(self) -> None: + """If there is no parser tokens defined the result is unchanged + input. + """ + nodes = list(self.parser.parse(self.tokens)) + assert self.tokens == nodes + + def test_parse_with_rules(self) -> None: + """Ensure the rules applied.""" + self.parser.rules["a"] = lambda value: value + "100" + self.parser.rules["b"] = lambda value: value + "10" + nodes = list(self.parser.parse(self.tokens)) + assert (1, "a", "11100") == nodes[0] + assert (2, "b", "1210") == nodes[1] + assert (4, "b", "1410") == nodes[3] + + def test_out_tokens(self) -> None: + """Tokens from ``out_tokens`` are combined together into a single + node. + """ + self.parser.out_tokens = ["a", "b"] + nodes = list(self.parser.parse(self.tokens)) + assert 4 == len(nodes) + assert (1, "out", [(1, "a", "11"), (2, "b", "12")]) == nodes[0] + assert (4, "out", [(4, "b", "14")]) == nodes[2] + + self.parser.out_tokens = ["b", "c"] + nodes = list(self.parser.parse(self.tokens)) + assert 2 == len(nodes) + assert ( + 2, + "out", + [(2, "b", "12"), (3, "c", "13"), (4, "b", "14"), (5, "c", "15")], + ) == nodes[1] + + def test_compound(self) -> None: + """""" + self.parser.compound_tokens = ["b"] + self.parser.end_tokens = ["c"] + nodes = list(self.parser.parse(self.tokens)) + assert 3 == len(nodes) + assert (2, "b", ("12", [(3, "c", "13")])) == nodes[1] + assert (4, "b", ("14", [(5, "c", "15")])) == nodes[2] diff --git a/python/external/wheezy/template/tests/test_preprocessor.py b/python/external/wheezy/template/tests/test_preprocessor.py new file mode 100644 index 0000000..3c8773d --- /dev/null +++ b/python/external/wheezy/template/tests/test_preprocessor.py @@ -0,0 +1,80 @@ +import typing +import unittest + +from wheezy.template.engine import Engine +from wheezy.template.ext.core import CoreExtension +from wheezy.template.loader import DictLoader +from wheezy.template.preprocessor import Preprocessor +from wheezy.template.typing import Loader + + +class PreprocessorTestCase(unittest.TestCase): + """Test the ``Preprocessor``.""" + + def setUp(self) -> None: + def runtime_engine_factory(loader: Loader) -> Engine: + engine = Engine( + loader=loader, + extensions=[ + CoreExtension(), + ], + ) + return engine + + self.templates: typing.Dict[str, str] = {} + engine = Engine( + loader=DictLoader(templates=self.templates), + extensions=[ + CoreExtension("#", line_join=""), + ], + ) + self.engine = Preprocessor( + runtime_engine_factory, engine, key_factory=lambda ctx: "" + ) + + def render(self, name: str, ctx: typing.Mapping[str, typing.Any]) -> str: + template = self.engine.get_template(name) + return template.render(ctx) + + def test_render(self) -> None: + self.templates[ + "test.html" + ] = """\ +#require(_) +@require(username) +#_('Welcome,') @username!""" + + assert "Welcome, John!" == self.render( + "test.html", ctx={"_": lambda x: x, "username": "John"} + ) + + def test_extends(self) -> None: + self.templates.update( + { + "master.html": """\ +#require(_) +@def say_hi(name): + #_('Hello,') @name! +@end +@say_hi('John')""", + "tmpl.html": """\ +#require(_) +@extends('master.html') +@def say_hi(name): + #_('Hi,') @name! +@end +""", + } + ) + + assert " Hi, John!\n" == self.render( + "tmpl.html", + ctx={ + "_": lambda x: x, + }, + ) + + def test_remove(self) -> None: + self.templates["test.html"] = "Hello" + assert "Hello" == self.render("test.html", {}) + self.engine.remove("x") diff --git a/python/external/wheezy/template/tests/test_utils.py b/python/external/wheezy/template/tests/test_utils.py new file mode 100644 index 0000000..428518b --- /dev/null +++ b/python/external/wheezy/template/tests/test_utils.py @@ -0,0 +1,51 @@ +import unittest + +from wheezy.template.utils import find_all_balanced, find_balanced + + +class FindAllBalancedTestCase(unittest.TestCase): + """Test the ``find_all_balanced``.""" + + def test_start_out(self) -> None: + """The start index is out of range.""" + assert 10 == find_all_balanced("test", 10) + + def test_start_separator(self) -> None: + """If text doesn't start with ``([`` return.""" + assert 0 == find_all_balanced("test([", 0) + assert 3 == find_all_balanced("test([", 3) + + def test_not_balanced(self) -> None: + """Separators are not balanced.""" + assert 4 == find_all_balanced("test(a, b", 4) + assert 4 == find_all_balanced("test[a, b()", 4) + + def test_balanced(self) -> None: + """Separators are balanced.""" + assert 10 == find_all_balanced("test(a, b)", 4) + assert 13 == find_all_balanced("test(a, b)[0]", 4) + assert 12 == find_all_balanced("test(a, b())", 4) + assert 17 == find_all_balanced("test(a, b())[0]()", 4) + + +class FindBalancedTestCase(unittest.TestCase): + """Test the ``find_balanced``.""" + + def test_start_out(self) -> None: + """The start index is out of range.""" + assert 10 == find_balanced("test", 10) + + def test_start_separator(self) -> None: + """If text doesn't start with ``start_sep`` return.""" + assert 0 == find_balanced("test(", 0) + assert 3 == find_balanced("test(", 3) + + def test_not_balanced(self) -> None: + """Separators are not balanced.""" + assert 4 == find_balanced("test(a, b", 4) + assert 4 == find_balanced("test(a, b()", 4) + + def test_balanced(self) -> None: + """Separators are balanced.""" + assert 10 == find_balanced("test(a, b)", 4) + assert 12 == find_balanced("test(a, b())", 4) diff --git a/python/external/wheezy/template/typing.py b/python/external/wheezy/template/typing.py new file mode 100644 index 0000000..138b22d --- /dev/null +++ b/python/external/wheezy/template/typing.py @@ -0,0 +1,85 @@ +import typing +from abc import abstractmethod + +Token = typing.Tuple[int, str, str] + + +class Builder: + lineno: int + + @abstractmethod + def start_block(self) -> None: + ... # pragma: nocover + + @abstractmethod + def end_block(self) -> None: + ... # pragma: nocover + + @abstractmethod + def add(self, lineno: int, code: str) -> None: + ... # pragma: nocover + + @abstractmethod + def build_block(self, nodes: typing.Iterable[Token]) -> None: + ... # pragma: nocover + + @abstractmethod + def build_token( + self, + lineno: int, + token: str, + value: typing.Union[str, typing.Iterable[Token]], + ) -> None: + ... # pragma: nocover + + +Tokenizer = typing.Callable[[typing.Match], Token] +LexerRule = typing.Tuple[typing.Pattern, Tokenizer] +PreProcessorRule = typing.Callable[[str], str] +PostProcessorRule = typing.Callable[[typing.List[Token]], str] +BuilderRule = typing.Callable[ + [ + Builder, + int, + str, + typing.Union[str, typing.List[str], typing.Iterable[Token]], + ], + bool, +] +ParserRule = typing.Callable[[str], typing.Union[str, typing.List[str]]] + + +class ParserConfig: + end_tokens: typing.List[str] + continue_tokens: typing.List[str] + compound_tokens: typing.List[str] + out_tokens: typing.List[str] + + +RenderTemplate = typing.Callable[ + [ + typing.Mapping[str, typing.Any], + typing.Mapping[str, typing.Any], + typing.Mapping[str, typing.Any], + ], + str, +] + + +class SupportsRender: + @abstractmethod + def render(self, ctx: typing.Mapping[str, typing.Any]) -> str: + ... # pragma: nocover + + +TemplateClass = typing.Callable[[str, RenderTemplate], SupportsRender] + + +class Loader: + @abstractmethod + def list_names(self) -> typing.Tuple[str, ...]: + ... # pragma: nocover + + @abstractmethod + def load(self, name: str) -> typing.Optional[str]: + ... # pragma: nocover diff --git a/python/external/wheezy/template/utils.py b/python/external/wheezy/template/utils.py new file mode 100644 index 0000000..a13defc --- /dev/null +++ b/python/external/wheezy/template/utils.py @@ -0,0 +1,43 @@ +def find_all_balanced(text: str, start: int = 0) -> int: + """Finds balanced ``([`` with ``])`` assuming + that ``start`` is pointing to ``(`` or ``[`` in ``text``. + """ + if start >= len(text) or text[start] not in "([": + return start + while 1: + pos = find_balanced(text, start) + pos = find_balanced(text, pos, "[", "]") + if pos != start: + start = pos + else: + return pos + + +def find_balanced( + text: str, start: int = 0, start_sep: str = "(", end_sep: str = ")" +) -> int: + """Finds balanced ``start_sep`` with ``end_sep`` assuming + that ``start`` is pointing to ``start_sep`` in ``text``. + """ + if start >= len(text) or start_sep != text[start]: + return start + balanced = 1 + pos = start + 1 + while pos < len(text): + token = text[pos] + pos += 1 + if token == end_sep: + if balanced == 1: + return pos + balanced -= 1 + elif token == start_sep: + balanced += 1 + return start + + +def print_source(source: str, lineno: int = 1) -> None: # pragma: nocover + lines = [] + for line in source.split("\n"): + lines.append("%02d " % lineno + line) + lineno += line.count("\n") + 1 + print("\n".join(lines)) diff --git a/python/serve_html.py b/python/serve_html.py new file mode 100644 index 0000000..83cc87f --- /dev/null +++ b/python/serve_html.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +A simple http server for testing/debugging the web-UI + +open http://localhost:8080/ +add the following query params for TX and/or 900Mhz testing + isTX + hasSubGHz +""" + +from external.bottle import route, run, response, request, static_file +from external.wheezy.template.engine import Engine +from external.wheezy.template.ext.core import CoreExtension +from external.wheezy.template.loader import FileLoader + +net_counter = 0 +isTX = False +hasSubGHz = False + +config = { + "config": { + "mode":"STA", + "ssid":"network-ssid", + "product_name": "VRX Testing", + # "aat": { + # "servosmoo": 5, + # "servomode": 0, + # "azim_center": 0, + # "azim_min": 500, + # "azim_max": 2500, + # "elev_min": 1000, + # "elev_max": 2000, + # }, + # "vbat": { + # "offset": 292, + # "scale": -2 + # } + } + } + +def apply_template(mainfile): + global isTX, hasSubGHz + engine = Engine( + loader=FileLoader(["html"]), + extensions=[CoreExtension("@@")] + ) + template = engine.get_template(mainfile) + data = template.render({ + 'VERSION': 'testing (xxxxxx)', + 'PLATFORM': 'Unified_ESP8285', + 'isTX': isTX, + 'hasSubGHz': hasSubGHz + }) + return data + +@route('/') +def index(): + global net_counter, isTX, hasSubGHz + net_counter = 0 + isTX = 'isTX' in request.query + hasSubGHz = 'hasSubGHz' in request.query + response.content_type = 'text/html; charset=latin9' + return apply_template('vrx_index.html') + +@route('/logo.svg') +def logo(): + response.content_type = 'image/svg+xml; charset=latin9' + return apply_template('logo.svg') + +@route('/elrs.css') +def elrs(): + response.content_type = 'text/css; charset=latin9' + return apply_template('elrs.css') + +@route('/scan.js') +def scan(): + response.content_type = 'text/javascript; charset=latin9' + return apply_template('scan.js') + +@route('/mui.css') +def mui_css(): + response.content_type = 'text/css; charset=latin9' + return apply_template('mui.css') + +@route('/mui.js') +def mui(): + response.content_type = 'text/javascript; charset=latin9' + return apply_template('mui.js') + +@route('/p5.js') +def p5(): + response.content_type = 'text/javascript; charset=latin9' + return static_file('p5.js', root='html') + +@route('/airplane.obj') +def airplane_obj(): + response.content_type = 'text/plain; charset=latin9' + return static_file('airplane.obj', root='html') + +@route('/texture.gif') +def texture_gif(): + response.content_type = 'image/gif; charset=latin9' + return static_file('texture.gif', root='html') + +@route('/config') +def options(): + response.content_type = 'application/json; charset=latin9' + return config + +@route('/config', method='POST') +def update_config(): + if 'button-actions' in request.json: + config['config']['button-actions'] = request.json['button-actions'] + if 'pwm' in request.json: + i=0 + for x in request.json['pwm']: + print(x) + config['config']['pwm'][i]['config'] = x + i = i + 1 + if 'protocol' in request.json: + config['config']['serial-protocol'] = request.json['protocol'] + if 'modelid' in request.json: + config['config']['modelid'] = request.json['modelid'] + if 'forcetlm' in request.json: + config['config']['force-tlm'] = request.json['forcetlm'] + return "Config Updated" + +@route('/options.json', method='POST') +def update_options(): + config['options'] = request.json + return "Options Updated" + +@route('/import', method='POST') +def import_config(): + json = request.json + print(json) + return "Config Updated" + +@route('/sethome', method='POST') +def options(): + response.content_type = 'application/json; charset=latin9' + return "Connecting to network '" + request.forms.get('network') + "', connect to http://elrs_tx.local from a browser on that network" + +@route('/networks.json') +def mode(): + global net_counter + net_counter = net_counter + 1 + if (net_counter > 3): + return '["Test Network 1", "Test Network 2", "Test Network 3", "Test Network 4", "Test Network 5"]' + response.status = 204 + return '[]' + +if __name__ == '__main__': + run(host='localhost', port=8080) From 0097ccfb62cf7ab4df7c8e4425be21f60119aada Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sun, 21 Jul 2024 13:12:56 +1200 Subject: [PATCH 13/38] Nobody like you, Windoze! --- python/build_html.py | 2 +- src/Vrx_main.cpp | 4 ++-- src/{fusion.cpp => tbs_fusion.cpp} | 4 ++-- src/{fusion.h => tbs_fusion.h} | 0 4 files changed, 5 insertions(+), 5 deletions(-) rename src/{fusion.cpp => tbs_fusion.cpp} (99%) rename src/{fusion.h => tbs_fusion.h} (100%) diff --git a/python/build_html.py b/python/build_html.py index 3da3c11..c25257f 100644 --- a/python/build_html.py +++ b/python/build_html.py @@ -27,7 +27,7 @@ def compress(data): return buf.getvalue() def build_html(mainfile, var, out, env): - with open('html/%s' % mainfile, 'r') as file: + with open('html/%s' % mainfile, 'r', encoding='ISO-8859-1') as file: data = file.read() if mainfile.endswith('.html'): data = html_minifier.html_minify(data).replace('@VERSION@', get_version(env)).replace('@PLATFORM@', re.sub("_via_.*", "", env['PIOENV'])) diff --git a/src/Vrx_main.cpp b/src/Vrx_main.cpp index f18a17a..351e146 100644 --- a/src/Vrx_main.cpp +++ b/src/Vrx_main.cpp @@ -32,7 +32,7 @@ #elif defined(STEADYVIEW_BACKPACK) #include "steadyview.h" #elif defined(FUSION_BACKPACK) - #include "fusion.h" + #include "tbs_fusion.h" #elif defined(HDZERO_BACKPACK) #include "hdzero.h" #elif defined(SKYZONE_MSP_BACKPACK) @@ -493,7 +493,7 @@ void loop() turnOffLED(); ESP.restart(); } - #endif + #endif if (connectionState == wifiUpdate) { diff --git a/src/fusion.cpp b/src/tbs_fusion.cpp similarity index 99% rename from src/fusion.cpp rename to src/tbs_fusion.cpp index 9a719f6..51c6847 100644 --- a/src/fusion.cpp +++ b/src/tbs_fusion.cpp @@ -1,4 +1,4 @@ -#include "fusion.h" +#include "tbs_fusion.h" #include "logging.h" #include "crc.h" #include "crsf_protocol.h" @@ -15,7 +15,7 @@ Fusion::Init() void Fusion::SendIndexCmd(uint8_t index) -{ +{ uint16_t f = frequencyTable[index]; uint8_t buf[12]; uint8_t pos = 0; diff --git a/src/fusion.h b/src/tbs_fusion.h similarity index 100% rename from src/fusion.h rename to src/tbs_fusion.h From 312e4f8c0bfc0a1e6fd4c6dba642f0362c60d68e Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Mon, 26 Aug 2024 17:08:03 +1200 Subject: [PATCH 14/38] Add support for MPU6050 --- .idea/.gitignore | 8 + .idea/Backpack.iml | 8 + .idea/misc.xml | 20 +++ .idea/modules.xml | 8 + .idea/vcs.xml | 6 + lib/HeadTracker/IMU.cpp | 121 +++++++++++++++ lib/HeadTracker/IMU.h | 20 +++ lib/HeadTracker/devHeadTracker.cpp | 238 +++++++++++++---------------- lib/QMC5883L/QMC5883LCompass.cpp | 14 +- lib/QMC5883L/QMC5883LCompass.h | 1 + lib/WIFI/devWIFI.cpp | 7 +- targets/common.ini | 1 + targets/debug.ini | 36 ++++- targets/rapidfire.ini | 1 + 14 files changed, 350 insertions(+), 139 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/Backpack.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 lib/HeadTracker/IMU.cpp create mode 100644 lib/HeadTracker/IMU.h diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/Backpack.iml b/.idea/Backpack.iml new file mode 100644 index 0000000..91a038c --- /dev/null +++ b/.idea/Backpack.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..db716ae --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,20 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..de0664a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp new file mode 100644 index 0000000..58741f3 --- /dev/null +++ b/lib/HeadTracker/IMU.cpp @@ -0,0 +1,121 @@ +#include "IMU.h" + +#include "logging.h" + +#include "ICM42670P.h" +#include "MPU6050.h" + +static enum { + IMU_ICM42670P, + IMU_MPU6050 +} deviceType; + +static void *device; +static float aRes; +static float gRes; + +static volatile bool irq_received; + +static IRAM_ATTR void irq_handler() { + irq_received = true; +} + +bool IMU::initialize() { + auto *imu = new ICM42670P(Wire, false); + int ret = imu->begin(); + if (ret == 0) { + if ((ret = imu->enableDataInterrupt(PIN_INT, irq_handler))) { + DBGLN("Interrupt enable failed: %d"); + return false; + } + + sampleRate = 100; + + // Accel ODR = 100 Hz and Full Scale Range = 16G + imu->startAccel(100, 16); + aRes = 16.0/32768; + // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps + imu->startGyro(100, 2000); + gRes = 2000.0/32768; + + device = imu; + deviceType = IMU_ICM42670P; + return true; + } + delete imu; + + auto *mpu6050 = new MPU6050(MPU6050_DEFAULT_ADDRESS, &Wire); + mpu6050->initialize(); + if (mpu6050->testConnection()) { + DBGLN("Found MPU6050"); + pinMode(PIN_INT, INPUT_PULLUP); + attachInterrupt(PIN_INT, irq_handler, RISING); + mpu6050->setSleepEnabled(false); + mpu6050->setFullScaleGyroRange(MPU6050_GYRO_FS_2000); + mpu6050->setFullScaleAccelRange(MPU6050_ACCEL_FS_16); + mpu6050->setDLPFMode(MPU6050_DLPF_BW_188); + mpu6050->setRate(9); // 1000/(1+79) = 100Hz + mpu6050->setInterruptMode(false); + mpu6050->setInterruptDrive(false); + mpu6050->setInterruptLatch(false); + mpu6050->setInterruptLatchClear(false); + mpu6050->setFSyncInterruptLevel(false); + mpu6050->setFSyncInterruptEnabled(false); + mpu6050->setI2CBypassEnabled(true); + mpu6050->setClockOutputEnabled(false); + mpu6050->setIntDataReadyEnabled(true); + mpu6050->setFIFOEnabled(true); + mpu6050->resetFIFO(); + mpu6050->setXGyroFIFOEnabled(true); + mpu6050->setYGyroFIFOEnabled(true); + mpu6050->setZGyroFIFOEnabled(true); + mpu6050->setAccelFIFOEnabled(true); + + sampleRate = 100; + + aRes = 16.0/32768; + gRes = 2000.0/32768; + + device = mpu6050; + deviceType = IMU_MPU6050; + return true; + } + delete mpu6050; + + DBGLN("IMU initialization failed: No IMU"); + return false; +} + +bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { + bool hasData = irq_received; + if (!hasData) return false; + irq_received = false; + + switch (deviceType) { + case IMU_ICM42670P: { + inv_imu_sensor_event_t imu_event; + ((ICM42670P *)device)->getDataFromRegisters(&imu_event); + accel.axis.x = imu_event.accel[0] * aRes; + accel.axis.y = imu_event.accel[1] * aRes; + accel.axis.z = imu_event.accel[2] * aRes; + gyro.axis.x = imu_event.gyro[0] * gRes; + gyro.axis.y = imu_event.gyro[1] * gRes; + gyro.axis.z = imu_event.gyro[2] * gRes; + break; + } + + case IMU_MPU6050: { + uint8_t values[12]; + ((MPU6050 *)device)->GetCurrentFIFOPacket(values, 12); + ((MPU6050 *)device)->getIntDataReadyStatus(); // Clears the interrupt status + accel.axis.x = (int16_t)((values[0] << 8) | values[1]) * aRes; + accel.axis.y = (int16_t)((values[2] << 8) | values[3]) * aRes; + accel.axis.z = (int16_t)((values[4] << 8) | values[5]) * aRes; + gyro.axis.x = (int16_t)((values[6] << 8) | values[7]) * gRes; + gyro.axis.y = (int16_t)((values[8] << 8) | values[9]) * gRes; + gyro.axis.z = (int16_t)((values[10] << 8) | values[11]) * gRes; + break; + } + } + return true; +} diff --git a/lib/HeadTracker/IMU.h b/lib/HeadTracker/IMU.h new file mode 100644 index 0000000..c436ca9 --- /dev/null +++ b/lib/HeadTracker/IMU.h @@ -0,0 +1,20 @@ +// +// Created by Paul Kendall on 25/08/2024. +// + +#ifndef BACKPACK_IMU_H +#define BACKPACK_IMU_H + +#include "Fusion.h" + +class IMU { +public: + bool initialize(); + bool readIMUData(FusionVector &accel, FusionVector &gyro); + int getSampleRate() { return sampleRate; } + +private: + int sampleRate = 0; +}; + +#endif //BACKPACK_IMU_H diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index 33127be..5a2c6f9 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -1,28 +1,21 @@ #include -#include "common.h" #if defined(HAS_HEADTRACKING) #include - -#include "devHeadTracker.h" - #include "config.h" #include "logging.h" -#include "ICM42670P.h" -#include "QMC5883LCompass.h" +#include "devHeadTracker.h" -#include "Fusion.h" +#include "QMC5883LCompass.h" +#include "IMU.h" static HeadTrackerState ht_state = STATE_ERROR; -static ICM42670P IMU(Wire,0); +static IMU imu; static QMC5883LCompass compass; +static bool hasCompass = false; static FusionAhrs ahrs; -static float aRes; -static float gRes; -static volatile uint8_t irq_received = 0; - static float orientation[3] = {0.0, 0.0, 0.0}; static FusionEuler euler; static float rollHome = 0, pitchHome = 0, yawHome = 0; @@ -30,34 +23,22 @@ static int calibrationData[3][2]; static uint32_t cal_started; -static IRAM_ATTR void irq_handler(void) { - irq_received = 1; -} - static void initialize() { - Wire.begin(PIN_SDA, PIN_SCL); + Wire.begin(PIN_SDA, PIN_SCL, 400000); // Compass init first compass.init(); - compass.setMode(0x01,0x08,0x10,0X00); // continuous, 100Hz, 8G, 512 over sample + hasCompass = compass.readChipId() == 0xFF; + hasCompass = false; + if (hasCompass) compass.setMode(0x01,0x08,0x10,0X00); // continuous, 100Hz, 8G, 512 over sample - // Initializing the ICM42607C - int ret = IMU.begin(); - if (ret != 0) { - DBGLN("ICM42607C initialization failed: %d", ret); - return; - } - if ((ret = IMU.enableDataInterrupt(9, irq_handler))) { - DBGLN("Interrupt enable failed: %d"); + // Initializing the IMU + if (!imu.initialize()) + { + ht_state = STATE_ERROR; return; } - // Accel ODR = 100 Hz and Full Scale Range = 16G - IMU.startAccel(100, 16); - aRes = 16.0/32768; - // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps - IMU.startGyro(100, 2000); - gRes = 2000.0/32768; FusionAhrsInitialise(&ahrs); // Set AHRS algorithm settings @@ -67,9 +48,10 @@ static void initialize() .gyroscopeRange = 2000.0f, /* replace this with actual gyroscope range in degrees/s */ .accelerationRejection = 10.0f, .magneticRejection = 10.0f, - .recoveryTriggerPeriod = 5 * 100, /* 5 seconds */ + .recoveryTriggerPeriod = 5U * imu.getSampleRate(), /* 5 seconds */ }; FusionAhrsSetSettings(&ahrs, &settings); + DBGLN("starting head tracker"); ht_state = STATE_RUNNING; } @@ -79,8 +61,11 @@ static int start() { return DURATION_NEVER; } - int (*cal)[3][2] = config.GetCompassCalibration(); - compass.setCalibration((*cal)[0][0],(*cal)[0][1],(*cal)[1][0],(*cal)[1][1],(*cal)[2][0],(*cal)[2][1]); + if (hasCompass) + { + int (*cal)[3][2] = config.GetCompassCalibration(); + compass.setCalibration((*cal)[0][0],(*cal)[0][1],(*cal)[1][0],(*cal)[1][1],(*cal)[2][0],(*cal)[2][1]); + } //TODO: load gyro/accel calibration from settings memcpy(orientation, *config.GetBoardOrientation(), sizeof(orientation)); return DURATION_IMMEDIATELY; @@ -127,120 +112,105 @@ static void rotate(float pn[3], const float rot[3]) { static int timeout() { - if(!irq_received) - { - return DURATION_IMMEDIATELY; - } - irq_received = 0; - - inv_imu_sensor_event_t imu_event; - IMU.getDataFromRegisters(&imu_event); - - switch(ht_state) - { - case STATE_RUNNING: - { - FusionVector a; - a.axis.x = imu_event.accel[0] * aRes; - a.axis.y = imu_event.accel[1] * aRes; - a.axis.z = imu_event.accel[2] * aRes; - rotate(a.array, orientation); - - FusionVector g; - g.axis.x = imu_event.gyro[0] * gRes; - g.axis.y = imu_event.gyro[1] * gRes; - g.axis.z = imu_event.gyro[2] * gRes; - rotate(g.array, orientation); - - compass.read(); - - FusionVector m; - m.axis.x = compass.getX(); - m.axis.y = compass.getY(); - m.axis.z = compass.getZ(); - rotate(m.array, orientation); - - // Calculate delta time (in seconds) to account for gyroscope sample clock error - const clock_t timestamp = micros(); - static clock_t previousTimestamp; - const float deltaTime = (float) (timestamp - previousTimestamp) / (float) 1000000; - previousTimestamp = timestamp; - - FusionAhrsUpdate(&ahrs, g, a, m, deltaTime); - - euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs)); - euler.angle.roll -= rollHome; - euler.angle.pitch -= pitchHome; - euler.angle.yaw -= yawHome; - } - break; + FusionVector a; + FusionVector g; + + if (imu.readIMUData(a, g)) { + switch (ht_state) { + case STATE_RUNNING: { + rotate(a.array, orientation); + rotate(g.array, orientation); + + FusionVector m FUSION_VECTOR_ZERO; + if (hasCompass) + { + compass.read(); + + m.axis.x = compass.getX(); + m.axis.y = compass.getY(); + m.axis.z = compass.getZ(); + rotate(m.array, orientation); + } - case STATE_COMPASS_CALIBRATING: - { - if ((millis() - cal_started) < 10000) - { - compass.read(); + // Calculate delta time (in seconds) to account for gyroscope sample clock error + const clock_t timestamp = micros(); + static clock_t previousTimestamp; + const float deltaTime = (float) (timestamp - previousTimestamp) / (float) 1000000; + previousTimestamp = timestamp; - int x = compass.getX(); - int y = compass.getY(); - int z = compass.getZ(); + FusionAhrsUpdate(&ahrs, g, a, m, deltaTime); - if(x < calibrationData[0][0]) { - calibrationData[0][0] = x; - } - if(x > calibrationData[0][1]) { - calibrationData[0][1] = x; - } - - if(y < calibrationData[1][0]) { - calibrationData[1][0] = y; - } - if(y > calibrationData[1][1]) { - calibrationData[1][1] = y; - } + euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs)); + euler.angle.roll -= rollHome; + euler.angle.pitch -= pitchHome; + euler.angle.yaw -= yawHome; +// DBGLN("%d %d %d", (int)euler.angle.roll, (int)euler.angle.pitch, (int)euler.angle.yaw); + break; + } - if(z < calibrationData[2][0]) { - calibrationData[2][0] = z; - } - if(z > calibrationData[2][1]) { - calibrationData[2][1] = z; + case STATE_COMPASS_CALIBRATING: { + if ((millis() - cal_started) < 10000) { + compass.read(); + + int x = compass.getX(); + int y = compass.getY(); + int z = compass.getZ(); + + if (x < calibrationData[0][0]) { + calibrationData[0][0] = x; + } + if (x > calibrationData[0][1]) { + calibrationData[0][1] = x; + } + + if (y < calibrationData[1][0]) { + calibrationData[1][0] = y; + } + if (y > calibrationData[1][1]) { + calibrationData[1][1] = y; + } + + if (z < calibrationData[2][0]) { + calibrationData[2][0] = z; + } + if (z > calibrationData[2][1]) { + calibrationData[2][1] = z; + } + } else { + compass.setCalibration( + calibrationData[0][0], + calibrationData[0][1], + calibrationData[1][0], + calibrationData[1][1], + calibrationData[2][0], + calibrationData[2][1] + ); + config.SetCompassCalibration(calibrationData); + ht_state = STATE_RUNNING; } + break; } - else - { - compass.setCalibration( - calibrationData[0][0], - calibrationData[0][1], - calibrationData[1][0], - calibrationData[1][1], - calibrationData[2][0], - calibrationData[2][1] - ); - config.SetCompassCalibration(calibrationData); - ht_state = STATE_RUNNING; - } - } - break; - case STATE_IMU_CALIBRATING: - { + case STATE_IMU_CALIBRATING: { + break; + } } - break; } - return DURATION_IMMEDIATELY; } void startCompassCalibration() { - compass.clearCalibration(); - calibrationData[0][0] = calibrationData[0][1] = compass.getX(); - calibrationData[1][0] = calibrationData[1][1] = compass.getY(); - calibrationData[2][0] = calibrationData[2][1] = compass.getZ(); - - cal_started = millis(); - ht_state = STATE_COMPASS_CALIBRATING; + if (hasCompass) { + compass.clearCalibration(); + calibrationData[0][0] = calibrationData[0][1] = compass.getX(); + calibrationData[1][0] = calibrationData[1][1] = compass.getY(); + calibrationData[2][0] = calibrationData[2][1] = compass.getZ(); + + cal_started = millis(); + ht_state = STATE_COMPASS_CALIBRATING; + } } void startIMUCalibration() diff --git a/lib/QMC5883L/QMC5883LCompass.cpp b/lib/QMC5883L/QMC5883LCompass.cpp index 51fdb98..faa69d3 100644 --- a/lib/QMC5883L/QMC5883LCompass.cpp +++ b/lib/QMC5883L/QMC5883LCompass.cpp @@ -67,7 +67,6 @@ QMC5883LCompass::QMC5883LCompass() { @since v0.1; **/ void QMC5883LCompass::init(){ - Wire.begin(); _writeReg(0x0B,0x01); setMode(0x01,0x0C,0x10,0X00); } @@ -417,4 +416,15 @@ void QMC5883LCompass::getDirection(char* myArray, int azimuth){ myArray[0] = _bearings[d][0]; myArray[1] = _bearings[d][1]; myArray[2] = _bearings[d][2]; -} \ No newline at end of file +} + +int QMC5883LCompass::readChipId(){ + Wire.beginTransmission(_ADDR); + Wire.write(0x0D); + int err = Wire.endTransmission(); + if (!err) { + Wire.requestFrom(_ADDR, (byte)1); + return Wire.read(); + } + return -1; +} diff --git a/lib/QMC5883L/QMC5883LCompass.h b/lib/QMC5883L/QMC5883LCompass.h index 2ca4235..cf6d2d3 100644 --- a/lib/QMC5883L/QMC5883LCompass.h +++ b/lib/QMC5883L/QMC5883LCompass.h @@ -28,6 +28,7 @@ class QMC5883LCompass { int getAzimuth(); byte getBearing(int azimuth); void getDirection(char *myArray, int azimuth); + int readChipId(); private: void _writeReg(byte reg, byte val); diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index 555a4d8..46c3f69 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -868,7 +868,7 @@ static void HandleWebUpdate() #if defined(HAS_HEADTRACKING) static long lastCall = 0; static HeadTrackerState last_state = STATE_ERROR; - if (now - lastCall > 10) { + if (now - lastCall > 50) { auto current_state = getHeadTrackerState(); switch(current_state) { @@ -885,7 +885,10 @@ static void HandleWebUpdate() float yaw, pitch, roll; getEuler(&yaw, &pitch, &roll); snprintf_P(payload, sizeof(payload), IMU_JSON, yaw, pitch, roll); - ws.textAll(payload, strlen(payload)); + if (ws.availableForWriteAll()) + { + ws.textAll(payload, strlen(payload)); + } } break; diff --git a/targets/common.ini b/targets/common.ini index 6c4ff81..6b46174 100644 --- a/targets/common.ini +++ b/targets/common.ini @@ -10,6 +10,7 @@ lib_deps = ottowinter/ESPAsyncWebServer-esphome @ 3.2.2 esphome/AsyncTCP-esphome @ 2.1.3 # use specific version - an update to this library breaks the build bblanchon/ArduinoJson @ 7.1.0 + jrowberg/I2Cdevlib-MPU6050 @ 1.0.0 monitor_filters = esp8266_exception_decoder diff --git a/targets/debug.ini b/targets/debug.ini index 8cc146e..7e660f8 100644 --- a/targets/debug.ini +++ b/targets/debug.ini @@ -79,4 +79,38 @@ build_flags = lib_ignore = STM32UPDATE [env:DEBUG_TIMER_ESP32_Backpack_via_WIFI] -extends = env:DEBUG_TIMER_ESP32_Backpack_via_UART \ No newline at end of file +extends = env:DEBUG_TIMER_ESP32_Backpack_via_UART + +# ******************************** +# Head Tracker VRX backpack targets +# ******************************** + +[env:DEBUG_C3_HT_Backpack_via_UART] +extends = env_common_esp32c3, skyzone_msp_vrx_backpack_common +build_flags = + ${env_common_esp32c3.build_flags} + ${skyzone_msp_vrx_backpack_common.build_flags} + -D HAS_HEADTRACKING + -D PIN_SDA=2 + -D PIN_SCL=3 + -D PIN_INT=4 +monitor_filters = esp32_exception_decoder +monitor_speed = 115200 + +[env:DEBUG_C3_HT_Backpack_via_WIFI] +extends = env:DEBUG_C3_HT_Backpack_via_UART + +[env:DEBUG_32_HT_Backpack_via_UART] +extends = env_common_esp32, skyzone_msp_vrx_backpack_common +build_flags = + ${env_common_esp32.build_flags} + ${skyzone_msp_vrx_backpack_common.build_flags} + -D HAS_HEADTRACKING + -D PIN_SDA=22 + -D PIN_SCL=19 + -D PIN_INT=15 +monitor_filters = esp32_exception_decoder +monitor_speed = 115200 + +[env:DEBUG_32_HT_Backpack_via_WIFI] +extends = env:DEBUG_32_HT_Backpack_via_UART diff --git a/targets/rapidfire.ini b/targets/rapidfire.ini index 692409d..0772c86 100644 --- a/targets/rapidfire.ini +++ b/targets/rapidfire.ini @@ -60,6 +60,7 @@ build_flags = -D HAS_HEADTRACKING -D PIN_SDA=2 -D PIN_SCL=4 + -D PIN_INT=9 [env:Rapidfire_RM_HT_Backpack_via_WIFI] extends = env:Rapidfire_RM_HT_Backpack_via_UART From 77cb311c776d5c0400820dd4113b738404aacb83 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Mon, 2 Sep 2024 14:53:20 +1200 Subject: [PATCH 15/38] Add gyro calibration --- html/scan.js | 20 ++-- html/vrx_index.html | 3 - lib/HeadTracker/IMU.cpp | 94 ++++++++++++++++++ lib/HeadTracker/IMU.h | 13 ++- lib/HeadTracker/devHeadTracker.cpp | 154 +++++++++++++++-------------- lib/config/config.cpp | 7 ++ lib/config/config.h | 7 +- src/Vrx_main.cpp | 2 + 8 files changed, 206 insertions(+), 94 deletions(-) diff --git a/html/scan.js b/html/scan.js index 78fde34..a97fc53 100644 --- a/html/scan.js +++ b/html/scan.js @@ -589,11 +589,11 @@ function start() { // e.style.backgroundColor = 'red'; // }; websock.onerror = function(evt) { console.log(evt); }; - websock.onmessage = function(evt) { + websock.onmessage = async function(evt) { d = JSON.parse(evt.data); if (d['done']) { calibrationOff(); - cuteAlert({ + await cuteAlert({ type: 'info', title: "Calibration", message: "Calibration successful", @@ -668,8 +668,8 @@ if (_('x-angle')) _('x-angle').addEventListener('input', setOrientation); if (_('y-angle')) _('y-angle').addEventListener('input', setOrientation); if (_('z-angle')) _('z-angle').addEventListener('input', setOrientation); -function calibrateCompass() { - cuteAlert({ +async function calibrateCompass() { + await cuteAlert({ type: 'info', title: "Calibrate Compass", message: "Rotate the board in all directions for 10 seconds until the succeeded popup appears", @@ -681,8 +681,8 @@ function calibrateCompass() { }); } -function calibrateIMU() { - cuteAlert({ +async function calibrateIMU() { + await cuteAlert({ type: 'info', title: "Calibrate IMU", message: "Place the board flat on the table and wait until the succeeded popup appears", @@ -713,15 +713,15 @@ function saveOrientation() { function calibrationOn() { _('main').classList.add('loading'); - _('calibrating').style.display='block'; + let calibrating = document.createElement('div') + calibrating.innerHTML = '
Calibrating
' mui.overlay('on', { 'keyboard': false, 'static': true - }, _('calibrating')); + }, calibrating); } -function calibrationOff() -{ +function calibrationOff() { _('main').classList.remove('loading'); mui.overlay('off'); } diff --git a/html/vrx_index.html b/html/vrx_index.html index 87a20ce..4de2c0c 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -10,9 +10,6 @@ -
diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp index 58741f3..e797be7 100644 --- a/lib/HeadTracker/IMU.cpp +++ b/lib/HeadTracker/IMU.cpp @@ -1,3 +1,4 @@ +#if defined(HAS_HEADTRACKING) #include "IMU.h" #include "logging.h" @@ -14,6 +15,14 @@ static void *device; static float aRes; static float gRes; +// Calibration data +static float imuCalibration[3]; +static double kP, kI; +static int Loops = 20; +static int16_t eSample; +static float ITerm[3] = {0 ,0, 0}; +static int c, L; + static volatile bool irq_received; static IRAM_ATTR void irq_handler() { @@ -117,5 +126,90 @@ bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { break; } } + gyro.axis.x += imuCalibration[0]; + gyro.axis.y += imuCalibration[1]; + gyro.axis.z += imuCalibration[2]; return true; } + +void IMU::BeginCalibration() +{ + kP = 0.5; + kI = 150; + float x = (100 - map(Loops, 1, 5, 20, 0)) * .01; + kP *= x; + kI *= x; + + c = 0; + L = 0; + eSample = 0; + ITerm[0] = 0; + ITerm[1] = 0; + ITerm[2] = 0; +} + +bool IMU::UpdateCalibration(FusionVector &g) +{ + float eSum = 0; + float Data, Error, PTerm; + + ITerm[0] = imuCalibration[0]; + ITerm[1] = imuCalibration[1]; + ITerm[2] = imuCalibration[2]; + + Error = -g.axis.x; + eSum += abs(g.axis.x); + PTerm = kP * Error; + ITerm[0] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 + Data = PTerm + ITerm[0]; //Compute PID Output + imuCalibration[0] = Data; + + Error = -g.axis.y; + eSum += abs(g.axis.y); + PTerm = kP * Error; + ITerm[1] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 + Data = PTerm + ITerm[1]; //Compute PID Output + imuCalibration[1] = Data; + + Error = -g.axis.z; + eSum += abs(g.axis.z); + PTerm = kP * Error; + ITerm[2] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 + Data = PTerm + ITerm[2]; //Compute PID Output + imuCalibration[2] = Data; + + c++; + if((c == 99) && eSum > 1000 * gRes) { // Error is still to great to continue + c = 0; + DBGLN("Calibration retry %d", (int)eSum); + } + if(eSum < 5 * gRes) eSample++; // Successfully found offsets prepare to advance + if(c == 100 || ((eSum < 100 * gRes) && (c > 10) && (eSample >= 10))) { // Advance to next Loop + imuCalibration[0] = ITerm[0]; + imuCalibration[1] = ITerm[1]; + imuCalibration[2] = ITerm[2]; + kP *= .75; + kI *= .75; + c = 0; + eSample = 0; + L++; + DBGLN("Finished loop iteration %d", L); + Serial.printf("%6.2f %6.2f %6.2f\r\n", imuCalibration[0], imuCalibration[1], imuCalibration[2]); + } + return L == Loops; +} + +void IMU::SetCalibration(float (*calibration)[3]) +{ + memcpy(imuCalibration, calibration, sizeof(imuCalibration)); + for (float & i : imuCalibration) { + if (i > 1000 || i < -1000 || isnan(i)) { + i = 0; + } + } +} + +const float *IMU::GetCalibration() { + return imuCalibration; +} +#endif \ No newline at end of file diff --git a/lib/HeadTracker/IMU.h b/lib/HeadTracker/IMU.h index c436ca9..bee3b30 100644 --- a/lib/HeadTracker/IMU.h +++ b/lib/HeadTracker/IMU.h @@ -1,7 +1,3 @@ -// -// Created by Paul Kendall on 25/08/2024. -// - #ifndef BACKPACK_IMU_H #define BACKPACK_IMU_H @@ -11,7 +7,14 @@ class IMU { public: bool initialize(); bool readIMUData(FusionVector &accel, FusionVector &gyro); - int getSampleRate() { return sampleRate; } + int getSampleRate() const { return sampleRate; } + + void BeginCalibration(); + bool UpdateCalibration(FusionVector &g); + + void SetCalibration(float (*calibration)[3]); + + const float *GetCalibration(); private: int sampleRate = 0; diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index 5a2c6f9..fb9d4de 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -30,7 +30,6 @@ static void initialize() // Compass init first compass.init(); hasCompass = compass.readChipId() == 0xFF; - hasCompass = false; if (hasCompass) compass.setMode(0x01,0x08,0x10,0X00); // continuous, 100Hz, 8G, 512 over sample // Initializing the IMU @@ -66,7 +65,7 @@ static int start() int (*cal)[3][2] = config.GetCompassCalibration(); compass.setCalibration((*cal)[0][0],(*cal)[0][1],(*cal)[1][0],(*cal)[1][1],(*cal)[2][0],(*cal)[2][1]); } - //TODO: load gyro/accel calibration from settings + imu.SetCalibration(config.GetIMUCalibration()); memcpy(orientation, *config.GetBoardOrientation(), sizeof(orientation)); return DURATION_IMMEDIATELY; } @@ -115,86 +114,91 @@ static int timeout() FusionVector a; FusionVector g; - if (imu.readIMUData(a, g)) { - switch (ht_state) { - case STATE_RUNNING: { - rotate(a.array, orientation); - rotate(g.array, orientation); - - FusionVector m FUSION_VECTOR_ZERO; - if (hasCompass) - { - compass.read(); - - m.axis.x = compass.getX(); - m.axis.y = compass.getY(); - m.axis.z = compass.getZ(); - rotate(m.array, orientation); - } + if (!imu.readIMUData(a, g)) + return DURATION_IMMEDIATELY; - // Calculate delta time (in seconds) to account for gyroscope sample clock error - const clock_t timestamp = micros(); - static clock_t previousTimestamp; - const float deltaTime = (float) (timestamp - previousTimestamp) / (float) 1000000; - previousTimestamp = timestamp; + switch (ht_state) { + case STATE_RUNNING: { + rotate(a.array, orientation); + rotate(g.array, orientation); - FusionAhrsUpdate(&ahrs, g, a, m, deltaTime); + FusionVector m FUSION_VECTOR_ZERO; + if (hasCompass) + { + compass.read(); - euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs)); - euler.angle.roll -= rollHome; - euler.angle.pitch -= pitchHome; - euler.angle.yaw -= yawHome; -// DBGLN("%d %d %d", (int)euler.angle.roll, (int)euler.angle.pitch, (int)euler.angle.yaw); - break; + m.axis.x = compass.getX(); + m.axis.y = compass.getY(); + m.axis.z = compass.getZ(); + rotate(m.array, orientation); } - case STATE_COMPASS_CALIBRATING: { - if ((millis() - cal_started) < 10000) { - compass.read(); - - int x = compass.getX(); - int y = compass.getY(); - int z = compass.getZ(); - - if (x < calibrationData[0][0]) { - calibrationData[0][0] = x; - } - if (x > calibrationData[0][1]) { - calibrationData[0][1] = x; - } - - if (y < calibrationData[1][0]) { - calibrationData[1][0] = y; - } - if (y > calibrationData[1][1]) { - calibrationData[1][1] = y; - } - - if (z < calibrationData[2][0]) { - calibrationData[2][0] = z; - } - if (z > calibrationData[2][1]) { - calibrationData[2][1] = z; - } - } else { - compass.setCalibration( - calibrationData[0][0], - calibrationData[0][1], - calibrationData[1][0], - calibrationData[1][1], - calibrationData[2][0], - calibrationData[2][1] - ); - config.SetCompassCalibration(calibrationData); - ht_state = STATE_RUNNING; + // Calculate delta time (in seconds) to account for gyroscope sample clock error + const clock_t timestamp = micros(); + static clock_t previousTimestamp; + const float deltaTime = (float) (timestamp - previousTimestamp) / (float) 1000000; + previousTimestamp = timestamp; + + FusionAhrsUpdate(&ahrs, g, a, m, deltaTime); + + euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs)); + euler.angle.roll -= rollHome; + euler.angle.pitch -= pitchHome; + euler.angle.yaw -= yawHome; + break; + } + + case STATE_COMPASS_CALIBRATING: { + if ((millis() - cal_started) < 10000) { + compass.read(); + + int x = compass.getX(); + int y = compass.getY(); + int z = compass.getZ(); + + if (x < calibrationData[0][0]) { + calibrationData[0][0] = x; + } + if (x > calibrationData[0][1]) { + calibrationData[0][1] = x; } - break; - } - case STATE_IMU_CALIBRATING: { + if (y < calibrationData[1][0]) { + calibrationData[1][0] = y; + } + if (y > calibrationData[1][1]) { + calibrationData[1][1] = y; + } + + if (z < calibrationData[2][0]) { + calibrationData[2][0] = z; + } + if (z > calibrationData[2][1]) { + calibrationData[2][1] = z; + } + } else { + compass.setCalibration( + calibrationData[0][0], + calibrationData[0][1], + calibrationData[1][0], + calibrationData[1][1], + calibrationData[2][0], + calibrationData[2][1] + ); + config.SetCompassCalibration(calibrationData); + config.Commit(); + ht_state = STATE_RUNNING; + } + break; + } - break; + case STATE_IMU_CALIBRATING: { + if (imu.UpdateCalibration(g)) { + config.SetIMUCalibration(imu.GetCalibration()); + config.Commit(); + ht_state = STATE_RUNNING; } + break; } } return DURATION_IMMEDIATELY; @@ -215,7 +219,9 @@ void startCompassCalibration() void startIMUCalibration() { - + imu.BeginCalibration(); + cal_started = millis(); + ht_state = STATE_IMU_CALIBRATING; } HeadTrackerState getHeadTrackerState() diff --git a/lib/config/config.cpp b/lib/config/config.cpp index fd432d2..3d4d7dd 100644 --- a/lib/config/config.cpp +++ b/lib/config/config.cpp @@ -220,6 +220,13 @@ VrxBackpackConfig::SetCompassCalibration(const int calibrationData[3][2]) m_modified = true; } +void +VrxBackpackConfig::SetIMUCalibration(const float calibration[3]) +{ + memcpy(m_config.imuCalibration, calibration, sizeof(m_config.imuCalibration)); + m_modified = true; +} + void VrxBackpackConfig::SetBoardOrientation(const float orientation[3]) { diff --git a/lib/config/config.h b/lib/config/config.h index f29bde9..45da539 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -83,6 +83,7 @@ typedef struct { uint8_t address[6]; #if defined(HAS_HEADTRACKING) int compassCalibration[3][2]; + float imuCalibration[3]; float boardOrientation[3]; #endif #if defined(AAT_BACKPACK) @@ -131,10 +132,12 @@ class VrxBackpackConfig void SetGroupAddress(const uint8_t address[6]); #if defined(HAS_HEADTRACKING) - int (*GetCompassCalibration())[3][2] { return &m_config.compassCalibration; }; - float (*GetBoardOrientation())[3] { return &m_config.boardOrientation; }; + int (*GetCompassCalibration())[3][2] { return &m_config.compassCalibration; } + float (*GetIMUCalibration())[3] { return &m_config.imuCalibration; } + float (*GetBoardOrientation())[3] { return &m_config.boardOrientation; } void SetCompassCalibration(const int calibration[3][2]); + void SetIMUCalibration(const float calibration[3]); void SetBoardOrientation(const float orientation[3]); #endif diff --git a/src/Vrx_main.cpp b/src/Vrx_main.cpp index c9c7172..4f50046 100644 --- a/src/Vrx_main.cpp +++ b/src/Vrx_main.cpp @@ -441,6 +441,8 @@ void setup() Serial.begin(VRX_UART_BAUD); #endif + delay(2000); + options_init(); eeprom.Begin(); From 3ee8654dbd345ff62f59446844d94f125b411512 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Tue, 3 Sep 2024 20:23:55 +1200 Subject: [PATCH 16/38] Fixes for ESP32 and ESP32C3 targets --- targets/debug.ini | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/targets/debug.ini b/targets/debug.ini index 7e660f8..7390d75 100644 --- a/targets/debug.ini +++ b/targets/debug.ini @@ -86,31 +86,37 @@ extends = env:DEBUG_TIMER_ESP32_Backpack_via_UART # ******************************** [env:DEBUG_C3_HT_Backpack_via_UART] -extends = env_common_esp32c3, skyzone_msp_vrx_backpack_common +extends = env_common_esp32c3, rapidfire_vrx_backpack_common build_flags = ${env_common_esp32c3.build_flags} - ${skyzone_msp_vrx_backpack_common.build_flags} + ${rapidfire_vrx_backpack_common.build_flags} -D HAS_HEADTRACKING -D PIN_SDA=2 -D PIN_SCL=3 -D PIN_INT=4 + -D PIN_MOSI=5 + -D PIN_CLK=6 + -D PIN_CS=7 monitor_filters = esp32_exception_decoder -monitor_speed = 115200 +monitor_speed = 460800 [env:DEBUG_C3_HT_Backpack_via_WIFI] extends = env:DEBUG_C3_HT_Backpack_via_UART [env:DEBUG_32_HT_Backpack_via_UART] -extends = env_common_esp32, skyzone_msp_vrx_backpack_common +extends = env_common_esp32, rapidfire_vrx_backpack_common build_flags = ${env_common_esp32.build_flags} - ${skyzone_msp_vrx_backpack_common.build_flags} + ${rapidfire_vrx_backpack_common.build_flags} -D HAS_HEADTRACKING -D PIN_SDA=22 -D PIN_SCL=19 -D PIN_INT=15 + -D PIN_MOSI=12 + -D PIN_CLK=13 + -D PIN_CS=14 monitor_filters = esp32_exception_decoder -monitor_speed = 115200 +monitor_speed = 460800 [env:DEBUG_32_HT_Backpack_via_WIFI] extends = env:DEBUG_32_HT_Backpack_via_UART From 9c6fafa6f5b2192ce89cf76177282e213190a05f Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Wed, 4 Sep 2024 17:01:46 +1200 Subject: [PATCH 17/38] Fixes for 8266 based head tracker --- lib/HeadTracker/IMU.cpp | 50 ++++++++++++++++-------------- lib/HeadTracker/devHeadTracker.cpp | 16 +++++++++- src/Vrx_main.cpp | 2 -- targets/debug.ini | 4 +++ 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp index e797be7..3892a1b 100644 --- a/lib/HeadTracker/IMU.cpp +++ b/lib/HeadTracker/IMU.cpp @@ -30,29 +30,8 @@ static IRAM_ATTR void irq_handler() { } bool IMU::initialize() { - auto *imu = new ICM42670P(Wire, false); - int ret = imu->begin(); - if (ret == 0) { - if ((ret = imu->enableDataInterrupt(PIN_INT, irq_handler))) { - DBGLN("Interrupt enable failed: %d"); - return false; - } - - sampleRate = 100; - - // Accel ODR = 100 Hz and Full Scale Range = 16G - imu->startAccel(100, 16); - aRes = 16.0/32768; - // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps - imu->startGyro(100, 2000); - gRes = 2000.0/32768; - - device = imu; - deviceType = IMU_ICM42670P; - return true; - } - delete imu; - + Wire.setClock(400000); + DBGLN("Try MPU6050"); auto *mpu6050 = new MPU6050(MPU6050_DEFAULT_ADDRESS, &Wire); mpu6050->initialize(); if (mpu6050->testConnection()) { @@ -91,6 +70,31 @@ bool IMU::initialize() { } delete mpu6050; + DBGLN("Try ICM42670P"); + auto *imu = new ICM42670P(Wire, false); + int ret = imu->begin(); + if (ret == 0) { + if ((ret = imu->enableDataInterrupt(PIN_INT, irq_handler))) { + DBGLN("Interrupt enable failed: %d"); + return false; + } + + sampleRate = 100; + + // Accel ODR = 100 Hz and Full Scale Range = 16G + imu->startAccel(100, 16); + aRes = 16.0/32768; + // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps + imu->startGyro(100, 2000); + gRes = 2000.0/32768; + + device = imu; + deviceType = IMU_ICM42670P; + DBGLN("Found ICM42670P"); + return true; + } + delete imu; + DBGLN("IMU initialization failed: No IMU"); return false; } diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index fb9d4de..d29a24a 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -25,7 +25,8 @@ static uint32_t cal_started; static void initialize() { - Wire.begin(PIN_SDA, PIN_SCL, 400000); + Wire.setClock(400000); + Wire.begin(PIN_SDA, PIN_SCL); // Compass init first compass.init(); @@ -67,6 +68,16 @@ static int start() } imu.SetCalibration(config.GetIMUCalibration()); memcpy(orientation, *config.GetBoardOrientation(), sizeof(orientation)); + +#ifdef DEBUG_LOG + Serial.printf("Load %7.2f %7.2f %7.2f\r\n", orientation[0], orientation[1], orientation[2]); +#endif + if (isnan(orientation[0]) || isnan(orientation[2]) || isnan(orientation[2])) { + orientation[0] = 0; + orientation[1] = 0; + orientation[2] = 0; + } + FusionAhrsReset(&ahrs); return DURATION_IMMEDIATELY; } @@ -242,6 +253,9 @@ void resetBoardOrientation() void saveBoardOrientation() { +#ifdef DEBUG_LOG + Serial.printf("Save %7.2f %7.2f %7.2f\r\n", orientation[0], orientation[1], orientation[2]); +#endif config.SetBoardOrientation(orientation); config.Commit(); } diff --git a/src/Vrx_main.cpp b/src/Vrx_main.cpp index 4f50046..c9c7172 100644 --- a/src/Vrx_main.cpp +++ b/src/Vrx_main.cpp @@ -441,8 +441,6 @@ void setup() Serial.begin(VRX_UART_BAUD); #endif - delay(2000); - options_init(); eeprom.Begin(); diff --git a/targets/debug.ini b/targets/debug.ini index 7390d75..f9b0af8 100644 --- a/targets/debug.ini +++ b/targets/debug.ini @@ -9,10 +9,14 @@ build_flags = ${env_common_esp8285.build_flags} ${steadyview_vrx_backpack_common.build_flags} -D DEBUG_LOG + -D HAS_HEADTRACKING -D PIN_LED=16 -D PIN_MOSI=12 ;Some pin (not a UART) -D PIN_CLK=0 ;Boot pad -D PIN_CS=15 ;Some other pin (not a UART) + -D PIN_SDA=2 + -D PIN_SCL=14 + -D PIN_INT=13 reset_method=nodemcu [env:DEBUG_ESP_RX_Backpack_via_WIFI] From 4799256ea8b234665025b514c47630a2b7de650c Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sat, 7 Sep 2024 07:49:11 +1200 Subject: [PATCH 18/38] Minor fixes --- lib/HeadTracker/IMU.cpp | 6 +++--- lib/HeadTracker/IMU.h | 10 +++++----- lib/HeadTracker/devHeadTracker.cpp | 2 +- targets/debug.ini | 1 + 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp index 3892a1b..6161991 100644 --- a/lib/HeadTracker/IMU.cpp +++ b/lib/HeadTracker/IMU.cpp @@ -16,9 +16,9 @@ static float aRes; static float gRes; // Calibration data +constexpr int Loops = 20; static float imuCalibration[3]; static double kP, kI; -static int Loops = 20; static int16_t eSample; static float ITerm[3] = {0 ,0, 0}; static int c, L; @@ -33,12 +33,12 @@ bool IMU::initialize() { Wire.setClock(400000); DBGLN("Try MPU6050"); auto *mpu6050 = new MPU6050(MPU6050_DEFAULT_ADDRESS, &Wire); - mpu6050->initialize(); if (mpu6050->testConnection()) { DBGLN("Found MPU6050"); pinMode(PIN_INT, INPUT_PULLUP); attachInterrupt(PIN_INT, irq_handler, RISING); mpu6050->setSleepEnabled(false); + mpu6050->setClockSource(1); mpu6050->setFullScaleGyroRange(MPU6050_GYRO_FS_2000); mpu6050->setFullScaleAccelRange(MPU6050_ACCEL_FS_16); mpu6050->setDLPFMode(MPU6050_DLPF_BW_188); @@ -52,8 +52,8 @@ bool IMU::initialize() { mpu6050->setI2CBypassEnabled(true); mpu6050->setClockOutputEnabled(false); mpu6050->setIntDataReadyEnabled(true); - mpu6050->setFIFOEnabled(true); mpu6050->resetFIFO(); + mpu6050->setFIFOEnabled(true); mpu6050->setXGyroFIFOEnabled(true); mpu6050->setYGyroFIFOEnabled(true); mpu6050->setZGyroFIFOEnabled(true); diff --git a/lib/HeadTracker/IMU.h b/lib/HeadTracker/IMU.h index bee3b30..b7882d3 100644 --- a/lib/HeadTracker/IMU.h +++ b/lib/HeadTracker/IMU.h @@ -6,15 +6,15 @@ class IMU { public: bool initialize(); - bool readIMUData(FusionVector &accel, FusionVector &gyro); + static bool readIMUData(FusionVector &accel, FusionVector &gyro); int getSampleRate() const { return sampleRate; } - void BeginCalibration(); - bool UpdateCalibration(FusionVector &g); + static void BeginCalibration(); + static bool UpdateCalibration(FusionVector &g); - void SetCalibration(float (*calibration)[3]); + static void SetCalibration(float (*calibration)[3]); - const float *GetCalibration(); + static const float *GetCalibration(); private: int sampleRate = 0; diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index d29a24a..f0cf8e9 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -289,7 +289,7 @@ void getEuler(float *yaw, float *pitch, float *roll) device_t HeadTracker_device = { .initialize = initialize, .start = start, - .event = NULL, + .event = nullptr, .timeout = timeout }; diff --git a/targets/debug.ini b/targets/debug.ini index f9b0af8..49c2d93 100644 --- a/targets/debug.ini +++ b/targets/debug.ini @@ -95,6 +95,7 @@ build_flags = ${env_common_esp32c3.build_flags} ${rapidfire_vrx_backpack_common.build_flags} -D HAS_HEADTRACKING + -D PIN_BUTTON=9 -D PIN_SDA=2 -D PIN_SCL=3 -D PIN_INT=4 From e9d570abb5703e3675fabdba7d96032d496e0845 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sun, 8 Sep 2024 08:03:44 +1200 Subject: [PATCH 19/38] Slight optimisation --- lib/HeadTracker/IMU.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp index 6161991..e8d763b 100644 --- a/lib/HeadTracker/IMU.cpp +++ b/lib/HeadTracker/IMU.cpp @@ -46,7 +46,7 @@ bool IMU::initialize() { mpu6050->setInterruptMode(false); mpu6050->setInterruptDrive(false); mpu6050->setInterruptLatch(false); - mpu6050->setInterruptLatchClear(false); + mpu6050->setInterruptLatchClear(true); mpu6050->setFSyncInterruptLevel(false); mpu6050->setFSyncInterruptEnabled(false); mpu6050->setI2CBypassEnabled(true); @@ -120,7 +120,6 @@ bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { case IMU_MPU6050: { uint8_t values[12]; ((MPU6050 *)device)->GetCurrentFIFOPacket(values, 12); - ((MPU6050 *)device)->getIntDataReadyStatus(); // Clears the interrupt status accel.axis.x = (int16_t)((values[0] << 8) | values[1]) * aRes; accel.axis.y = (int16_t)((values[2] << 8) | values[3]) * aRes; accel.axis.z = (int16_t)((values[4] << 8) | values[5]) * aRes; From 8878ca7db95a0f37e09826a8b1833ee5045e9998 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sun, 8 Sep 2024 10:18:43 +1200 Subject: [PATCH 20/38] Slightly nicer layout --- html/vrx_index.html | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/html/vrx_index.html b/html/vrx_index.html index 4de2c0c..22d8c72 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -109,35 +109,34 @@

RTC Update via NTP

-
-

Head Tracking

+
- +
-
- - -
-
-
From d82a4c0e2be84e5a31caedd562a04734cd313e09 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sun, 8 Sep 2024 12:05:59 +1200 Subject: [PATCH 21/38] Add head tracker targets for Orqa, Rapidfire & RX5808 --- targets/orqa.ini | 12 ++++++++++++ targets/rapidfire.ini | 8 ++++---- targets/rx5808.ini | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/targets/orqa.ini b/targets/orqa.ini index e538fab..be7407c 100644 --- a/targets/orqa.ini +++ b/targets/orqa.ini @@ -28,3 +28,15 @@ extends = env:Orqa_ESP01F_Backpack_via_UART [env:Orqa_HappyModel_EP82_VRX_Backpack_via_WIFI] extends = env:Orqa_HappyModel_EP82_VRX_Backpack_via_UART + +[env:Orqa_HT_Backpack_via_UART] +extends = env:Orqa_ESP01F_Backpack_via_UART +build_flags = + ${env:Orqa_ESP01F_Backpack_via_UART.build_flags} + -D HAS_HEADTRACKING + -D PIN_SDA=2 + -D PIN_SCL=4 + -D PIN_INT=9 + +[env:Orqa_HT_Backpack_via_WIFI] +extends = env:Orqa_HT_Backpack_via_UART diff --git a/targets/rapidfire.ini b/targets/rapidfire.ini index 0772c86..affafdc 100644 --- a/targets/rapidfire.ini +++ b/targets/rapidfire.ini @@ -53,8 +53,8 @@ build_flags = [env:Rapidfire_ESP12F_Backpack_via_WIFI] extends = env:Rapidfire_ESP12F_Backpack_via_UART -[env:Rapidfire_RM_HT_Backpack_via_UART] -extends = env:Rapidfire_ESP_RX_Backpack_via_UART +[env:Rapidfire_HT_Backpack_via_UART] +extends = env:Rapidfire_ESP01F_Backpack_via_UART build_flags = ${env:Rapidfire_ESP01F_Backpack_via_UART.build_flags} -D HAS_HEADTRACKING @@ -62,5 +62,5 @@ build_flags = -D PIN_SCL=4 -D PIN_INT=9 -[env:Rapidfire_RM_HT_Backpack_via_WIFI] -extends = env:Rapidfire_RM_HT_Backpack_via_UART +[env:Rapidfire_HT_Backpack_via_WIFI] +extends = env:Rapidfire_HT_Backpack_via_UART diff --git a/targets/rx5808.ini b/targets/rx5808.ini index 70f9d62..b04d318 100644 --- a/targets/rx5808.ini +++ b/targets/rx5808.ini @@ -60,3 +60,24 @@ build_flags = [env:RX5808_Diversity_ESP01F_Backpack_via_WIFI] extends = env:RX5808_Diversity_ESP01F_Backpack_via_UART + +[env:RX5808_HT_Backpack_via_UART] +extends = env:RX5808_ESP01F_Backpack_via_UART +build_flags = + ${env:RX5808_ESP01F_Backpack_via_UART.build_flags} + -D HAS_HEADTRACKING + -D PIN_SDA=2 + -D PIN_SCL=4 + -D PIN_INT=9 + +[env:RX5808_HT_Backpack_via_WIFI] +extends = env:RX5808_HT_Backpack_via_UART + +[env:RX5808_Diversity_HT_Backpack_via_UART] +extends = env:RX5808_HT_Backpack_via_UART +build_flags = + ${env:RX5808_HT_Backpack_via_UART.build_flags} + -D PIN_CS_2=12 + +[env:RX5808_Diversity_HT_Backpack_via_WIFI] +extends = env:RX5808_Diversity_HT_Backpack_via_UART From af41b52d1e4561b6c8b9931cd93b7391e9c306cc Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sun, 8 Sep 2024 14:51:29 +1200 Subject: [PATCH 22/38] Fix orientation to use right-hand rule --- html/scan.js | 12 +++++++++--- lib/HeadTracker/devHeadTracker.cpp | 4 ++-- lib/WIFI/devWIFI.cpp | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/html/scan.js b/html/scan.js index a97fc53..7dfcd86 100644 --- a/html/scan.js +++ b/html/scan.js @@ -636,8 +636,8 @@ function draw() { background(192); rotateY(radians(-Euler.heading+180)); - rotateX(radians(-Euler.pitch)); - rotateZ(radians(Euler.roll)); + rotateZ(radians(Euler.pitch)); + rotateX(radians(Euler.roll)); push(); stroke('#CCC'); @@ -662,7 +662,13 @@ function draw() { if (_('set-center')) _('set-center').addEventListener('click', () => {websock.send('sc');}); if (_('cal-compass')) _('cal-compass').addEventListener('click', calibrateCompass); if (_('cal-gyro')) _('cal-gyro').addEventListener('click', calibrateIMU); -if (_('reset-board')) _('reset-board').addEventListener('click', () => {websock.send('ro');}); +if (_('reset-board')) _('reset-board').addEventListener('click', (e) => { + _('x-angle').value = 0; + _('y-angle').value = 0; + _('z-angle').value = 0; + setOrientation(e); + websock.send('ro'); +}); if (_('save-orientation')) _('save-orientation').addEventListener('click', saveOrientation); if (_('x-angle')) _('x-angle').addEventListener('input', setOrientation); if (_('y-angle')) _('y-angle').addEventListener('input', setOrientation); diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index f0cf8e9..4683f52 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -262,8 +262,8 @@ void saveBoardOrientation() void setBoardOrientation(int xAngle, int yAngle, int zAngle) { - orientation[0] = yAngle * DEG_TO_RAD; - orientation[1] = xAngle * DEG_TO_RAD; + orientation[0] = xAngle * DEG_TO_RAD; + orientation[1] = yAngle * DEG_TO_RAD; orientation[2] = zAngle * DEG_TO_RAD; ahrs.initialising = true; ahrs.rampedGain = 10.0f; diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index 46c3f69..04b5fac 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -177,7 +177,7 @@ static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, Aw // Send JSON over websocket char payload[80]; float (*o)[3] = config.GetBoardOrientation(); - snprintf_P(payload, sizeof(payload), IMU_JSON, (*o)[2], (*o)[1], (*o)[0]); + snprintf_P(payload, sizeof(payload), IMU_JSON, (*o)[2] * RAD_TO_DEG, (*o)[0] * RAD_TO_DEG, (*o)[1] * RAD_TO_DEG); ws.text(client->id(), payload, strlen(payload)); } if (type == WS_EVT_DATA) { From e1a26455747e1084b11301add49dfaa6f11737d4 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sun, 8 Sep 2024 15:11:33 +1200 Subject: [PATCH 23/38] Instructions for adjusting the board orientation --- html/vrx_index.html | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/html/vrx_index.html b/html/vrx_index.html index 22d8c72..c20c5d0 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -118,12 +118,13 @@

RTC Update via NTP

- To set the board orientation, place the device that the head tracker is mounted in/on in its natural position - and use the controls below to obtain a flat and level ground plane then press "Save Board Orientation". -
-
- - + To set the board orientation, you need to think about the transformations required to take the board from flat and level and + pointing away from you using the right-hand rule to its mounted orientation. +
    +
  • First, calculate the angle of rotation about the pitch axis (pitch back is positive)
  • +
  • Second, calculate the angle of rotation about the roll axis (roll right is positive)
  • +
  • Lastly, calculate the angle of rotation about the yaw axis (yaw left is positive)
  • +
@@ -133,6 +134,10 @@

RTC Update via NTP

+
+ + +
From 26c19be62dc8f6607d957c5b19022d98bd45deea Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sun, 8 Sep 2024 17:19:53 +1200 Subject: [PATCH 24/38] MPU6050 accuracy by not using FIFO --- lib/HeadTracker/IMU.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp index e8d763b..ae80039 100644 --- a/lib/HeadTracker/IMU.cpp +++ b/lib/HeadTracker/IMU.cpp @@ -52,13 +52,6 @@ bool IMU::initialize() { mpu6050->setI2CBypassEnabled(true); mpu6050->setClockOutputEnabled(false); mpu6050->setIntDataReadyEnabled(true); - mpu6050->resetFIFO(); - mpu6050->setFIFOEnabled(true); - mpu6050->setXGyroFIFOEnabled(true); - mpu6050->setYGyroFIFOEnabled(true); - mpu6050->setZGyroFIFOEnabled(true); - mpu6050->setAccelFIFOEnabled(true); - sampleRate = 100; aRes = 16.0/32768; @@ -118,14 +111,14 @@ bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { } case IMU_MPU6050: { - uint8_t values[12]; - ((MPU6050 *)device)->GetCurrentFIFOPacket(values, 12); - accel.axis.x = (int16_t)((values[0] << 8) | values[1]) * aRes; - accel.axis.y = (int16_t)((values[2] << 8) | values[3]) * aRes; - accel.axis.z = (int16_t)((values[4] << 8) | values[5]) * aRes; - gyro.axis.x = (int16_t)((values[6] << 8) | values[7]) * gRes; - gyro.axis.y = (int16_t)((values[8] << 8) | values[9]) * gRes; - gyro.axis.z = (int16_t)((values[10] << 8) | values[11]) * gRes; + int16_t values[6]; + ((MPU6050 *)device)->getMotion6(&values[0], &values[1], &values[2], &values[3], &values[4], &values[5]); + accel.axis.x = values[0] * aRes; + accel.axis.y = values[1] * aRes; + accel.axis.z = values[2] * aRes; + gyro.axis.x = values[3] * gRes; + gyro.axis.y = values[4] * gRes; + gyro.axis.z = values[5] * gRes; break; } } From 05a0e0905b27e1d182580214da406bb473046695 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sat, 7 Sep 2024 15:56:34 +1200 Subject: [PATCH 25/38] Simplified MPU6050 code --- lib/HeadTracker/IMU.cpp | 31 +++--------------- lib/HeadTracker/MPU6050.cpp | 63 +++++++++++++++++++++++++++++++++++++ lib/HeadTracker/MPU6050.h | 7 +++++ targets/common.ini | 1 - 4 files changed, 75 insertions(+), 27 deletions(-) create mode 100644 lib/HeadTracker/MPU6050.cpp create mode 100644 lib/HeadTracker/MPU6050.h diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp index ae80039..ecd8aed 100644 --- a/lib/HeadTracker/IMU.cpp +++ b/lib/HeadTracker/IMU.cpp @@ -32,26 +32,11 @@ static IRAM_ATTR void irq_handler() { bool IMU::initialize() { Wire.setClock(400000); DBGLN("Try MPU6050"); - auto *mpu6050 = new MPU6050(MPU6050_DEFAULT_ADDRESS, &Wire); - if (mpu6050->testConnection()) { + auto *mpu6050 = new MPU6050(); + if (mpu6050->initialize()) { DBGLN("Found MPU6050"); pinMode(PIN_INT, INPUT_PULLUP); attachInterrupt(PIN_INT, irq_handler, RISING); - mpu6050->setSleepEnabled(false); - mpu6050->setClockSource(1); - mpu6050->setFullScaleGyroRange(MPU6050_GYRO_FS_2000); - mpu6050->setFullScaleAccelRange(MPU6050_ACCEL_FS_16); - mpu6050->setDLPFMode(MPU6050_DLPF_BW_188); - mpu6050->setRate(9); // 1000/(1+79) = 100Hz - mpu6050->setInterruptMode(false); - mpu6050->setInterruptDrive(false); - mpu6050->setInterruptLatch(false); - mpu6050->setInterruptLatchClear(true); - mpu6050->setFSyncInterruptLevel(false); - mpu6050->setFSyncInterruptEnabled(false); - mpu6050->setI2CBypassEnabled(true); - mpu6050->setClockOutputEnabled(false); - mpu6050->setIntDataReadyEnabled(true); sampleRate = 100; aRes = 16.0/32768; @@ -95,7 +80,6 @@ bool IMU::initialize() { bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { bool hasData = irq_received; if (!hasData) return false; - irq_received = false; switch (deviceType) { case IMU_ICM42670P: { @@ -111,20 +95,15 @@ bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { } case IMU_MPU6050: { - int16_t values[6]; - ((MPU6050 *)device)->getMotion6(&values[0], &values[1], &values[2], &values[3], &values[4], &values[5]); - accel.axis.x = values[0] * aRes; - accel.axis.y = values[1] * aRes; - accel.axis.z = values[2] * aRes; - gyro.axis.x = values[3] * gRes; - gyro.axis.y = values[4] * gRes; - gyro.axis.z = values[5] * gRes; + if (!((MPU6050 *)device)->getDataFromRegisters(accel, gyro)) + return false; break; } } gyro.axis.x += imuCalibration[0]; gyro.axis.y += imuCalibration[1]; gyro.axis.z += imuCalibration[2]; + irq_received = false; return true; } diff --git a/lib/HeadTracker/MPU6050.cpp b/lib/HeadTracker/MPU6050.cpp new file mode 100644 index 0000000..b01f74a --- /dev/null +++ b/lib/HeadTracker/MPU6050.cpp @@ -0,0 +1,63 @@ +#include "Wire.h" +#include "MPU6050.h" + +#define ADDR 0x68 +#define WHO_AM_I 0x75 +#define INT_STATUS 0x3A +#define ACCEL_X_H 0x3B + +#define ARES (16.0 / 32768) +#define GRES (2000.0 / 32768) + +bool MPU6050::initialize() { + static uint8_t INIT_DATA[] = { + 0x6B, 0x01, + 0x1B, 0x18, + 0x1C, 0x18, + 0x1A, 0x01, + 0x19, 0x09, + 0x37, 0x02, + 0x38, 0x01, + 0x23, 0x78 + }; + Wire.setTimeOut(1000); + Wire.setTimeOut(1000); + Wire.beginTransmission(ADDR); + Wire.write(WHO_AM_I); + Wire.endTransmission(); + Wire.requestFrom(ADDR, 1); + if (((Wire.read() >> 1) & 0x3F) != 0x34) { + return false; + } + for (int i=0 ; i Date: Mon, 9 Sep 2024 13:44:47 +1200 Subject: [PATCH 26/38] Add support for S3 and QMI8658C IMU --- lib/HeadTracker/IMU.cpp | 31 +++++++- lib/HeadTracker/MPU6050.cpp | 3 +- lib/HeadTracker/QMI8658C.cpp | 136 +++++++++++++++++++++++++++++++++++ lib/HeadTracker/QMI8658C.h | 15 ++++ targets/debug.ini | 21 ++++++ 5 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 lib/HeadTracker/QMI8658C.cpp create mode 100644 lib/HeadTracker/QMI8658C.h diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp index ecd8aed..d9b361d 100644 --- a/lib/HeadTracker/IMU.cpp +++ b/lib/HeadTracker/IMU.cpp @@ -5,10 +5,12 @@ #include "ICM42670P.h" #include "MPU6050.h" +#include "QMI8658C.h" static enum { IMU_ICM42670P, - IMU_MPU6050 + IMU_MPU6050, + IMU_QMI8658C } deviceType; static void *device; @@ -48,6 +50,23 @@ bool IMU::initialize() { } delete mpu6050; + DBGLN("Try QMI8658C"); + auto *qmi8658c = new QMI8658C(); + if (qmi8658c->initialize()) { + DBGLN("Found QMI8658C"); + pinMode(PIN_INT, INPUT_PULLUP); + attachInterrupt(PIN_INT, irq_handler, RISING); + sampleRate = 100; + + aRes = 16.0/32768; + gRes = 2000.0/32768; + + device = qmi8658c; + deviceType = IMU_QMI8658C; + return true; + } + delete qmi8658c; + DBGLN("Try ICM42670P"); auto *imu = new ICM42670P(Wire, false); int ret = imu->begin(); @@ -84,7 +103,7 @@ bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { switch (deviceType) { case IMU_ICM42670P: { inv_imu_sensor_event_t imu_event; - ((ICM42670P *)device)->getDataFromRegisters(&imu_event); + ((ICM42670P *) device)->getDataFromRegisters(&imu_event); accel.axis.x = imu_event.accel[0] * aRes; accel.axis.y = imu_event.accel[1] * aRes; accel.axis.z = imu_event.accel[2] * aRes; @@ -95,7 +114,13 @@ bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { } case IMU_MPU6050: { - if (!((MPU6050 *)device)->getDataFromRegisters(accel, gyro)) + if (!((MPU6050 *) device)->getDataFromRegisters(accel, gyro)) + return false; + break; + } + + case IMU_QMI8658C: { + if (!((QMI8658C *) device)->getDataFromRegisters(accel, gyro)) return false; break; } diff --git a/lib/HeadTracker/MPU6050.cpp b/lib/HeadTracker/MPU6050.cpp index b01f74a..f3be7a9 100644 --- a/lib/HeadTracker/MPU6050.cpp +++ b/lib/HeadTracker/MPU6050.cpp @@ -20,8 +20,7 @@ bool MPU6050::initialize() { 0x38, 0x01, 0x23, 0x78 }; - Wire.setTimeOut(1000); - Wire.setTimeOut(1000); + Wire.setTimeout(1000); Wire.beginTransmission(ADDR); Wire.write(WHO_AM_I); Wire.endTransmission(); diff --git a/lib/HeadTracker/QMI8658C.cpp b/lib/HeadTracker/QMI8658C.cpp new file mode 100644 index 0000000..1ac9591 --- /dev/null +++ b/lib/HeadTracker/QMI8658C.cpp @@ -0,0 +1,136 @@ +#include "Arduino.h" +#include "logging.h" +#include "Wire.h" +#include "QMI8658C.h" + +#define ADDR 0x6B +#define WHO_AM_I 0x00 +#define REVISION_ID 0x01 +#define INT_STATUS 0x3A +#define ACCEL_X_L 0x35 + +#define ARES (16.0 / 32768) +#define GRES (1024.0 / 32768) + +void QMI8658C::writeRegister(uint8_t reg, uint8_t val) { + Wire.beginTransmission(ADDR); + Wire.write(reg); + Wire.write(val); + Wire.endTransmission(); +} + +uint8_t QMI8658C::readRegister(uint8_t reg) { + Wire.beginTransmission(ADDR); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom(ADDR, 1); + return Wire.read(); +} + +uint8_t QMI8658C::readBuffer(uint8_t reg, uint8_t *buffer, int length) { + uint32_t t1 = millis(); + Wire.beginTransmission(ADDR); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom(ADDR, length); + int count = 0; + while (Wire.available() && (millis() - t1 < 1000) && count < length) { + buffer[count++] = Wire.read(); + } + return count; +} + + +int QMI8658C::writeCommand(uint8_t cmd) +{ + int val; + uint32_t startMillis; + + writeRegister(0x0A, cmd); + startMillis = millis(); + do { + val = readRegister(0x2D); + if (millis() - startMillis > 1000) { + DBGLN("Wait for ctrl9 command done time out : %x val: %x", cmd, val); + return -1; + } + } while (!(val & 0x80)); + + writeRegister(0x0A, 0x00); + + startMillis = millis(); + do { + val = readRegister(0x2D); + if (millis() - startMillis > 1000) { + DBGLN("Clear ctrl9 command done flag time out : %x val: %x", cmd, val); + return -1; + } + } while ((val & 0x80)); + + return 0; +} + +bool QMI8658C::initialize() { + Wire.setTimeout(1000); + Wire.setClock(400000); + + writeRegister(0x60, 0xB0); // Reset + while(!(readRegister(0x4D) & 0x80)); + + writeRegister(0x02, 0b01100000); // enable auto-increment, BE + + if (readRegister(WHO_AM_I) != 0x05) { + DBGLN("WHO_AM_I failed"); + return false; + } + + if (readRegister(REVISION_ID) != 0x7C) { + DBGLN("REVISION_ID failed"); + return false; + } + + writeRegister(0x03, 0b00110011); // 16G, 896.8 ODR + writeRegister(0x06, 0b00000111); // both LPF enabled @ 13.37% ODR + writeRegister(0x03, 0b10110011); // 16G, 896.8 ODR + self test + writeRegister(0x04, 0b01100011); // 1024dps, 896.8 ODR + writeRegister(0x06, 0b01110111); // both LPF enabled @ 13.37% ODR + writeRegister(0x04, 0b11100011); // 1024dps, 896.8 ODR + self test + writeRegister(0x08, 0b10000011); // SyncSample, G+A enabled + + // disable AHB clock gating + writeRegister(0x0B, 0x01); + writeCommand(0x12); // times out! + + writeRegister(0x02, 0b01110000); // enable auto-increment, BE, INT2 enable + + if ((readRegister(0x08) & 3) != 3) { + DBGLN("Check mode failed"); + return false; + } + return true; +} + +bool QMI8658C::getDataFromRegisters(FusionVector &accel, FusionVector &gyro) { + uint8_t values[12]; + + // Wait for data ready + uint32_t t1 = millis(); + do { + readBuffer(0x2D, values, 3); + } while ((values[0] & 3) != 3 && (millis() - t1 < 1000)); + if ((values[0] & 3) != 3) { + return false; + } + + // read teh accel and gyro data + readBuffer(ACCEL_X_L, values, 12); + + // map into Gs and dps + accel.axis.x = (int16_t)((values[1] << 8) | values[0]) * ARES; + accel.axis.y = (int16_t)((values[3] << 8) | values[2]) * ARES; + accel.axis.z = (int16_t)((values[5] << 8) | values[4]) * ARES; + gyro.axis.x = (int16_t)((values[7] << 8) | values[6]) * GRES; + gyro.axis.y = (int16_t)((values[9] << 8) | values[8]) * GRES; + gyro.axis.z = (int16_t)((values[11] << 8) | values[10]) * GRES; + return true; +} diff --git a/lib/HeadTracker/QMI8658C.h b/lib/HeadTracker/QMI8658C.h new file mode 100644 index 0000000..ef909a8 --- /dev/null +++ b/lib/HeadTracker/QMI8658C.h @@ -0,0 +1,15 @@ +#include "FusionMath.h" + +class QMI8658C { +public: + bool initialize(); + bool getDataFromRegisters(FusionVector &accel, FusionVector &gyro); + +private: + static void writeRegister(uint8_t reg, uint8_t val); + static uint8_t readRegister(uint8_t reg); + + static int writeCommand(uint8_t cmd); + + static uint8_t readBuffer(uint8_t reg, uint8_t *buffer, int length); +}; \ No newline at end of file diff --git a/targets/debug.ini b/targets/debug.ini index 49c2d93..3f5e94d 100644 --- a/targets/debug.ini +++ b/targets/debug.ini @@ -108,6 +108,27 @@ monitor_speed = 460800 [env:DEBUG_C3_HT_Backpack_via_WIFI] extends = env:DEBUG_C3_HT_Backpack_via_UART +[env:DEBUG_S3_HT_Backpack_via_UART] +extends = env_common_esp32s3, rapidfire_vrx_backpack_common +build_flags = + ${env_common_esp32s3.build_flags} + ${rapidfire_vrx_backpack_common.build_flags} + -D HAS_HEADTRACKING + -D PIN_BUTTON=0 + -D PIN_SDA=11 + -D PIN_SCL=12 + -D PIN_INT=13 + -D PIN_MOSI=5 + -D PIN_CLK=6 + -D PIN_CS=7 + -D ARDUINO_USB_CDC_ON_BOOT +monitor_filters = esp32_exception_decoder +monitor_speed = 460800 +board_upload.flash_size=4MB + +[env:DEBUG_S3_HT_Backpack_via_WIFI] +extends = env:DEBUG_S3_HT_Backpack_via_UART + [env:DEBUG_32_HT_Backpack_via_UART] extends = env_common_esp32, rapidfire_vrx_backpack_common build_flags = From 16f784bf2cd30af5235b0085de270bc7fd1ff7a0 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Tue, 10 Sep 2024 14:45:46 +1200 Subject: [PATCH 27/38] No dependence on 3rd party library for IMU --- lib/HeadTracker/ICMSeries.cpp | 114 + lib/HeadTracker/ICMSeries.h | 19 + lib/HeadTracker/IMU.cpp | 36 +- lib/ICM42670P/.piopm | 1 - lib/ICM42670P/LICENSE | 18 - lib/ICM42670P/README.md | 242 -- .../FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino | 59 - .../examples/Polling_I2C/Polling_I2C.ino | 52 - .../examples/Polling_SPI/Polling_SPI.ino | 52 - lib/ICM42670P/keywords.txt | 5 - lib/ICM42670P/library.properties | 10 - lib/ICM42670P/src/ICM42670P.cpp | 306 -- lib/ICM42670P/src/ICM42670P.h | 65 - lib/ICM42670P/src/Invn/InvError.h | 64 - lib/ICM42670P/src/imu/inv_imu_defs.h | 991 ----- lib/ICM42670P/src/imu/inv_imu_driver.c | 1578 -------- lib/ICM42670P/src/imu/inv_imu_driver.h | 457 --- lib/ICM42670P/src/imu/inv_imu_extfunc.h | 56 - lib/ICM42670P/src/imu/inv_imu_regmap.h | 3391 ----------------- lib/ICM42670P/src/imu/inv_imu_transport.c | 273 -- lib/ICM42670P/src/imu/inv_imu_transport.h | 121 - lib/ICM42670P/src/imu/inv_imu_version.h | 37 - lib/ICM42670P/src/inv_time.c | 36 - 23 files changed, 145 insertions(+), 7838 deletions(-) create mode 100644 lib/HeadTracker/ICMSeries.cpp create mode 100644 lib/HeadTracker/ICMSeries.h delete mode 100644 lib/ICM42670P/.piopm delete mode 100644 lib/ICM42670P/LICENSE delete mode 100644 lib/ICM42670P/README.md delete mode 100644 lib/ICM42670P/examples/FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino delete mode 100644 lib/ICM42670P/examples/Polling_I2C/Polling_I2C.ino delete mode 100644 lib/ICM42670P/examples/Polling_SPI/Polling_SPI.ino delete mode 100644 lib/ICM42670P/keywords.txt delete mode 100644 lib/ICM42670P/library.properties delete mode 100644 lib/ICM42670P/src/ICM42670P.cpp delete mode 100644 lib/ICM42670P/src/ICM42670P.h delete mode 100644 lib/ICM42670P/src/Invn/InvError.h delete mode 100644 lib/ICM42670P/src/imu/inv_imu_defs.h delete mode 100644 lib/ICM42670P/src/imu/inv_imu_driver.c delete mode 100644 lib/ICM42670P/src/imu/inv_imu_driver.h delete mode 100644 lib/ICM42670P/src/imu/inv_imu_extfunc.h delete mode 100644 lib/ICM42670P/src/imu/inv_imu_regmap.h delete mode 100644 lib/ICM42670P/src/imu/inv_imu_transport.c delete mode 100644 lib/ICM42670P/src/imu/inv_imu_transport.h delete mode 100644 lib/ICM42670P/src/imu/inv_imu_version.h delete mode 100644 lib/ICM42670P/src/inv_time.c diff --git a/lib/HeadTracker/ICMSeries.cpp b/lib/HeadTracker/ICMSeries.cpp new file mode 100644 index 0000000..9446ff8 --- /dev/null +++ b/lib/HeadTracker/ICMSeries.cpp @@ -0,0 +1,114 @@ +#include "Arduino.h" +#include "logging.h" +#include "Wire.h" +#include "ICMSeries.h" + +#define ADDR 0x68 +#define WHO_AM_I 0x75 +#define REVISION_ID 0x01 +#define ACCEL_X1 0x0B +#define INT_STATUS_DRDY 0x39 + +#define ARES (16.0 / 32768) +#define GRES (2000.0 / 32768) + +void ICMSeries::writeRegister(uint8_t reg, uint8_t val) { + Wire.beginTransmission(ADDR); + Wire.write(reg); + Wire.write(val); + Wire.endTransmission(); +} + +void ICMSeries::writeMem1Register(uint8_t reg, uint8_t val) { + uint32_t t1 = millis(); + writeRegister(0x1F, 0x10); + while (!(readRegister(0x00) & 0x08) && (millis() - t1 < 1000)); + writeRegister(0x7A, reg); + writeRegister(0x7B, val); + writeRegister(0x1F, 0x00); +} + +uint8_t ICMSeries::readRegister(uint8_t reg) { + Wire.beginTransmission(ADDR); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom(ADDR, 1); + return Wire.read(); +} + +uint8_t ICMSeries::readBuffer(uint8_t reg, uint8_t *buffer, int length) { + uint32_t t1 = millis(); + Wire.beginTransmission(ADDR); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom(ADDR, length); + int count = 0; + while (Wire.available() && (millis() - t1 < 1000) && count < length) { + buffer[count++] = Wire.read(); + } + return count; +} + +bool ICMSeries::initialize() { + Wire.setTimeout(1000); + Wire.setClock(1000000); + + int whoami = readRegister(WHO_AM_I); + DBGLN("WHO_AM_I %x", whoami); + if (whoami != 0x60 && whoami != 0x61 && whoami != 0x67 && whoami != 0x69 && whoami != 0x64) { + return false; + } + + /* Reset device */ + /* Ensure BLK_SEL_R and BLK_SEL_W are set to 0 */ + writeRegister(0x7C, 0); + writeRegister(0x79, 0); + writeRegister(0x02, 0x10); + delay(1); + writeRegister(0x01, 0x04); + writeRegister(0x36, 0x41); + + /* Clear the reset done interrupt */ + readRegister(0x3A); + + writeMem1Register(0x03, 0x00); + + writeRegister(0x06, 0x03); + writeRegister(0x35, 0x50); + writeMem1Register(0x00, 0x09); + writeRegister(0x29, 0x01); + + writeMem1Register(0x02, 0x01); + writeMem1Register(0x01, 0x24); + + writeRegister(0x2B, 0x1C); + writeRegister(0x2C, 0x0F); + writeMem1Register(0x2F, 0xF8); + + writeRegister(0x21, 9); // 16G, 100Hz + writeRegister(0x20, 9); // 2000dps, 100Hz + writeRegister(0x1F, 0x0F); // Low noise mode for Accel/Gyro + + return true; +} + +bool ICMSeries::getDataFromRegisters(FusionVector &accel, FusionVector &gyro) { + uint8_t values[12]; + + // return if NOT ready + if (!(readRegister(INT_STATUS_DRDY) & 1)) { + return false; + } + + // read teh accel and gyro data + readBuffer(ACCEL_X1, values, 12); + + // map into Gs and dps + accel.axis.x = (int16_t)((values[0] << 8) | values[1]) * ARES; + accel.axis.y = (int16_t)((values[2] << 8) | values[3]) * ARES; + accel.axis.z = (int16_t)((values[4] << 8) | values[5]) * ARES; + gyro.axis.x = (int16_t)((values[6] << 8) | values[7]) * GRES; + gyro.axis.y = (int16_t)((values[8] << 8) | values[9]) * GRES; + gyro.axis.z = (int16_t)((values[10] << 8) | values[11]) * GRES; + return true; +} diff --git a/lib/HeadTracker/ICMSeries.h b/lib/HeadTracker/ICMSeries.h new file mode 100644 index 0000000..4dab4b1 --- /dev/null +++ b/lib/HeadTracker/ICMSeries.h @@ -0,0 +1,19 @@ +#ifndef BACKPACK_ICM_H +#define BACKPACK_ICM_H + +#include "FusionMath.h" + +class ICMSeries { +public: + bool initialize(); + bool getDataFromRegisters(FusionVector &accel, FusionVector &gyro); + +private: + static void writeRegister(uint8_t reg, uint8_t val); + static uint8_t readRegister(uint8_t reg); + static uint8_t readBuffer(uint8_t reg, uint8_t *buffer, int length); + + static void writeMem1Register(uint8_t reg, uint8_t val); +}; + +#endif //BACKPACK_ICM_H diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp index d9b361d..c41023c 100644 --- a/lib/HeadTracker/IMU.cpp +++ b/lib/HeadTracker/IMU.cpp @@ -1,9 +1,11 @@ #if defined(HAS_HEADTRACKING) -#include "IMU.h" - +#include "Arduino.h" +#include "Wire.h" #include "logging.h" -#include "ICM42670P.h" +#include "IMU.h" + +#include "ICMSeries.h" #include "MPU6050.h" #include "QMI8658C.h" @@ -68,26 +70,18 @@ bool IMU::initialize() { delete qmi8658c; DBGLN("Try ICM42670P"); - auto *imu = new ICM42670P(Wire, false); - int ret = imu->begin(); - if (ret == 0) { - if ((ret = imu->enableDataInterrupt(PIN_INT, irq_handler))) { - DBGLN("Interrupt enable failed: %d"); - return false; - } - + auto *imu = new ICMSeries(); + if (imu->initialize()) { + DBGLN("Found ICM42670P"); + pinMode(PIN_INT, INPUT_PULLUP); + attachInterrupt(PIN_INT, irq_handler, RISING); sampleRate = 100; - // Accel ODR = 100 Hz and Full Scale Range = 16G - imu->startAccel(100, 16); aRes = 16.0/32768; - // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps - imu->startGyro(100, 2000); gRes = 2000.0/32768; device = imu; deviceType = IMU_ICM42670P; - DBGLN("Found ICM42670P"); return true; } delete imu; @@ -102,14 +96,8 @@ bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { switch (deviceType) { case IMU_ICM42670P: { - inv_imu_sensor_event_t imu_event; - ((ICM42670P *) device)->getDataFromRegisters(&imu_event); - accel.axis.x = imu_event.accel[0] * aRes; - accel.axis.y = imu_event.accel[1] * aRes; - accel.axis.z = imu_event.accel[2] * aRes; - gyro.axis.x = imu_event.gyro[0] * gRes; - gyro.axis.y = imu_event.gyro[1] * gRes; - gyro.axis.z = imu_event.gyro[2] * gRes; + if (!((ICMSeries *) device)->getDataFromRegisters(accel, gyro)) + return false; break; } diff --git a/lib/ICM42670P/.piopm b/lib/ICM42670P/.piopm deleted file mode 100644 index b1418e7..0000000 --- a/lib/ICM42670P/.piopm +++ /dev/null @@ -1 +0,0 @@ -{"type": "library", "name": "ICM42670P", "version": "1.0.0", "spec": {"owner": "invensenseinc", "id": 14400, "name": "ICM42670P", "requirements": null, "uri": null}} \ No newline at end of file diff --git a/lib/ICM42670P/LICENSE b/lib/ICM42670P/LICENSE deleted file mode 100644 index 809d9ae..0000000 --- a/lib/ICM42670P/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2022 InvenSense, Inc All rights reserved. - -This software, related documentation and any modifications thereto (collectively "Software") is subject -to InvenSense, Inc and its licencors' intellectual property rights under U.S. and international copyright -and other intellectual property rights laws. - -InvenSense, Inc and its licencors retain all intellectual property and proprietary rights in and to the Software -and any use, reproduction, disclosure or distribution of the Software without an express license agreement -from InvenSense, Inc is strictly prohibited. - -EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS -PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. -EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL -InvenSense, Inc BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY -DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -NEGLIGENCE OR OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -OF THE SOFTWARE. diff --git a/lib/ICM42670P/README.md b/lib/ICM42670P/README.md deleted file mode 100644 index 0ac731a..0000000 --- a/lib/ICM42670P/README.md +++ /dev/null @@ -1,242 +0,0 @@ -![TDKInvensense](doc/pictures/TDKInvensense.jpg) - -# ICM42670P Arduino library -This arduino library for the [TDK/Invensense ICM42670P High Performance 6-Axis MotionTracking(TM) IMU](https://invensense.tdk.com/products/motion-tracking/6-axis/icm-42670-p). -The ICM-42670-P is a high performance 6-axis MEMS MotionTracking device that combines a 3-axis gyroscope and a 3-axis accelerometer. It has a configurable host interface that supports I3CSM, I2C, and SPI serial communication, features up to 2.25 Kbytes FIFO and 2 programmable interrupts with ultra-low-power wake-on-motion support to minimize system power consumption. -This library supports both I2C and SPI commmunication with the ICM42670P. - -# Software setup -Use Arduino Library manager to find and install the ICM42670P library. - -# Hardware setup -There is currenlty no Arduino shield for the ICM42670P. -The wiring must be done manually between the Arduino motherboard and the ICM42670P daughter board. -The below wiring description is given for an Arduino Zero board, it depends on the interface to be used: -* I2C - -|Arduino Zero|ICM42670P daughter board| -| --- | --- | -| 3V3 | CN4.9 & CN5.10 | -| GND | CN5.9 | -| SDA | CN5.4 | -| SCL | CN5.6 | - -* SPI - -|Arduino Zero|ICM42670P daughter board| -| --- | --- | -| 3V3 | CN4.9 & CN5.10 | -| GND | CN5.9 | -| MISO=SPI.1 | CN4.7 | -| MOSI=SPI.4 | CN5.4 | -| SCK=SPI.3 | CN5.6 | -| CS=DIG.8 | CN5.8 | - -Note: SPI Chip Select can be mapped on any free digital IO, updating the sketches accordingly - -* Interrupt - -|Arduino Zero|ICM42670P daughter board| -| --- | --- | -| DIG.2 | CN4.1 | - -Note: Interrupt pin can be mapped on any free interruptable IO, updating the sketches accordingly - -## Orientation of axes - -The diagram below shows the orientation of the axes of sensitivity and the polarity of rotation. Note the pin 1 identifier (•) in the -figure. - -![Orientation of Axes](doc/pictures/OrientationOfAxes.jpg) - -# Library API - -## Create ICM42670P instance - -**ICM42670P(TwoWire &i2c,bool lsb)** -Create an instance of the ICM42670P that will be accessed using the specified I2C. The LSB of the I2C address can be set to 0 or 1. - -```C++ -ICM42670P IMU(Wire,0); -``` - -**ICM42670P(SPIClass &spi,uint8_t cs_id)** -Create an instance of the ICM42670P that will be accessed using the specified SPI. The IO number to be used as chip select must be specified. - -```C++ -ICM42670P IMU(SPI,10); -``` - -**/!\ This library does NOT support multiple instances of ICM42670P.** - - -## Initialize the ICM42670P -Call the begin method to execute the ICM42670P initialization routine. - -**int begin()** - -Initializes all the required parameters in order to communicate and use the ICM42670P. - -```C++ -IMU.begin(); -``` - -## Log sensor data - -**int startAccel(uint16_t odr, uint16_t fsr)** - -This method starts logging with the accelerometer, using the specified full scale range and output data rate. -Supported ODR are: 12, 25, 50, 100, 200, 400, 800, 1600 Hz (any other value defaults to 100 Hz). -Supported full scale ranges are: 2, 4, 8, 16 G (any other value defaults to 16 G). - -```C++ -IMU.startAccel(100,16); -``` - -**int startGyro(uint16_t odr, uint16_t fsr)** - -This method starts logging with the gyroscope, using the specified full scale range and output data rate. -Supported ODR are: 12, 25, 50, 100, 200, 400, 800, 1600 Hz (any other value defaults to 100 Hz). -Supported full scale ranges are: 250, 500, 1000, 2000 dps (any other value defaults to 2000 dps). - -```C++ -IMU.startGyro(100,2000); -``` - -**int getDataFromRegisters(inv_imu_sensor_event_t* evt)** - -This method reads the ICM42670P sensor data from registers and fill the provided event structure with sensor raw data. -Raw data can be translated to International System using the configured FSR for each sensor. Temperature in Degrees Centigrade = (TEMP_DATA / 128) + 25 - -```C++ -inv_imu_sensor_event_t event; -IMU.getDataFromRegisters(&event); -Serial.print("Accel X = "); -Serial.println(imu_event.accel[0]); -Serial.print("Accel Y = "); -Serial.print(imu_event.accel[1]); -Serial.print("Accel Z = "); -Serial.print(imu_event.accel[2]); -Serial.print("Gyro X = "); -Serial.print(imu_event.gyro[0]); -Serial.print("Gyro Y = "); -Serial.print(imu_event.gyro[1]); -Serial.print("Gyro Z = "); -Serial.print(imu_event.gyro[2]); -Serial.print("Temperature = "); -Serial.println(imu_event.temperature); -``` - -**int enableFifoInterrupt(uint8_t intpin, ICM42670P_irq_handler handler, uint8_t fifo_watermark)** - -This method initializes the fifo and the interrupt of the ICM42670P. The interrupt is triggered each time there is enough samples in the fifo (as specified by fifo_watermark), and the provided handler is called. -Any interuptable pin of the Arduino can be used for intpin. - -```C++ -uint8_t irq_received = 0; - -void irq_handler(void) -{ - irq_received = 1; -} -... - -// Enable interrupt on pin 2 -IMU.enableFifoInterrupt(2,irq_handler); -``` - -**int getDataFromFifo(ICM42670P_sensor_event_cb event_cb)** - -This method reads the ICM42670P sensor data samples stored in the FIFO and call the provided event handler with the sample event as parameter. -Raw data can be translated to International System using the configured FSR for each sensor. Temperature in Degrees Centigrade = (TEMP_DATA / 128) + 25 - -```C++ -void event_cb(inv_imu_sensor_event_t *evt) -{ - Serial.print(evt->accel[0]); - Serial.print(","); - Serial.print(evt->accel[1]); - Serial.print(","); - Serial.print(evt->accel[2]); - Serial.print(","); - Serial.print(evt->gyro[0]); - Serial.print(","); - Serial.print(evt->gyro[1]); - Serial.print(","); - Serial.print(evt->gyro[2]); - Serial.print(","); - Serial.println(evt->temperature); -} - -void loop() { - // Wait for interrupt to read data from fifo - if(irq_received) - { - irq_received = 0; - IMU.getDataFromFifo(event_cb); - } -} -``` - -**bool isAccelDataValid(inv_imu_sensor_event_t *evt)** - -This method checks if the accelerometer data in the FIFO sample is valid. - -```C++ -void event_cb(inv_imu_sensor_event_t *evt) -{ - if(IMU.isAccelDataValid(evt)) { - ... - } -} - -``` - -**bool isGyroDataValid(inv_imu_sensor_event_t *evt)** - -This method checks if the gyroscope data in the FIFO sample is valid. - -```C++ -void event_cb(inv_imu_sensor_event_t *evt) -{ - if(IMU.isGyroDataValid(evt)) { - ... - } -} - -``` - -**inv_imu_sensor_event_t** - -This structure is used by the ICM42670P driver to return raw sensor data. Available data is: -|Field name|description| -| --- | --- | -| sensor_mask | Mask describing available data | -| timestamp_fsync | Timestamp in us | -| accel[3] | 3-axis accel raw data | -| gyro[3] | 3-axis gyro raw data | -| temperature | Temperature raw data (on 1 or 2 bytes) | -| accel_high_res[3] | 3- axis accel lsb in high resolution | -| gyro_high_res[3] | 3- axis gyro LSB in high resolution | - - -# Available Sketches - -**Polling_I2C** - -This sketch initializes the ICM42670P with the I2C interface, and starts logging raw sensor data from IMU registers. Sensor data can be monitored on Serial monitor or Serial plotter - -**Polling_SPI** - -This sketch initializes the ICM42670P with the SPI interface, and starts logging raw sensor data from IMU registers. Sensor data can be monitored on Serial monitor or Serial plotter - -**FIFO_Interrupt** - -This sketch initializes the ICM42670P with the SPI interface and interrupt PIN2, and starts logging raw sensor data from IMU FIFO. Sensor data can be monitored on Serial monitor or Serial plotter - -**IMU data monitoring** - -When the ICM42670P IMU is logging, the Accelerometer, Gyroscope and Temperature raw data can be monitored with the Arduino Serial Plotter (Tools->Serial Plotter). - -![Serial Plotter](doc/pictures/SerialPlotter.jpg) - diff --git a/lib/ICM42670P/examples/FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino b/lib/ICM42670P/examples/FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino deleted file mode 100644 index 0dac9c9..0000000 --- a/lib/ICM42670P/examples/FIFO_Interrupt_SPI/FIFO_Interrupt_SPI.ino +++ /dev/null @@ -1,59 +0,0 @@ -#include "ICM42670P.h" - -// Instantiate an ICM42670P with SPI interface and CS on pin 8 -ICM42670P IMU(SPI,8); - -uint8_t irq_received = 0; - -void irq_handler(void) { - irq_received = 1; -} - -void event_cb(inv_imu_sensor_event_t *evt) { - // Format data for Serial Plotter - if(IMU.isAccelDataValid(evt)&&IMU.isGyroDataValid(evt)) { - Serial.print(evt->accel[0]); - Serial.print(","); - Serial.print(evt->accel[1]); - Serial.print(","); - Serial.print(evt->accel[2]); - Serial.print(","); - Serial.print(evt->gyro[0]); - Serial.print(","); - Serial.print(evt->gyro[1]); - Serial.print(","); - Serial.print(evt->gyro[2]); - Serial.print(","); - Serial.println(evt->temperature); - } -} - -void setup() { - int ret; - Serial.begin(115200); - while(!Serial) {} - - // Initializing the ICM42670P - ret = IMU.begin(); - if (ret != 0) { - Serial.print("ICM42670P initialization failed: "); - Serial.println(ret); - while(1); - } - // Enable interrupt on pin 2, Fifo watermark=10 - IMU.enableFifoInterrupt(2,irq_handler,10); - // Accel ODR = 100 Hz and Full Scale Range = 16G - IMU.startAccel(100,16); - // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps - IMU.startGyro(100,2000); - // Plotter axis header - Serial.println("AccelX,AccelY,AccelZ,GyroX,GyroY,GyroZ,Temperature"); -} - -void loop() { - // Wait for interrupt to read data from fifo - if(irq_received) { - irq_received = 0; - IMU.getDataFromFifo(event_cb); - } -} diff --git a/lib/ICM42670P/examples/Polling_I2C/Polling_I2C.ino b/lib/ICM42670P/examples/Polling_I2C/Polling_I2C.ino deleted file mode 100644 index 31d25d3..0000000 --- a/lib/ICM42670P/examples/Polling_I2C/Polling_I2C.ino +++ /dev/null @@ -1,52 +0,0 @@ -#include "ICM42670P.h" - -// Instantiate an ICM42670P with LSB address set to 0 -ICM42670P IMU(Wire,0); - -void setup() { - int ret; - Serial.begin(115200); - while(!Serial) {} - - // Initializing the ICM42670P - ret = IMU.begin(); - if (ret != 0) { - Serial.print("ICM42670P initialization failed: "); - Serial.println(ret); - while(1); - } - // Accel ODR = 100 Hz and Full Scale Range = 16G - IMU.startAccel(100,16); - // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps - IMU.startGyro(100,2000); - // Wait IMU to start - delay(100); - // Plotter axis header - Serial.println("AccelX,AccelY,AccelZ,GyroX,GyroY,GyroZ,Temperature"); -} - -void loop() { - - inv_imu_sensor_event_t imu_event; - - // Get last event - IMU.getDataFromRegisters(&imu_event); - - // Format data for Serial Plotter - Serial.print(imu_event.accel[0]); - Serial.print(","); - Serial.print(imu_event.accel[1]); - Serial.print(","); - Serial.print(imu_event.accel[2]); - Serial.print(","); - Serial.print(imu_event.gyro[0]); - Serial.print(","); - Serial.print(imu_event.gyro[1]); - Serial.print(","); - Serial.print(imu_event.gyro[2]); - Serial.print(","); - Serial.println(imu_event.temperature); - - // Run @ ODR 100Hz - delay(10); -} diff --git a/lib/ICM42670P/examples/Polling_SPI/Polling_SPI.ino b/lib/ICM42670P/examples/Polling_SPI/Polling_SPI.ino deleted file mode 100644 index 479ea23..0000000 --- a/lib/ICM42670P/examples/Polling_SPI/Polling_SPI.ino +++ /dev/null @@ -1,52 +0,0 @@ -#include "ICM42670P.h" - -// Instantiate an ICM42670P with SPI interface and CS on pin 8 -ICM42670P IMU(SPI,8); - -void setup() { - int ret; - Serial.begin(115200); - while(!Serial) {} - - // Initializing the ICM42670P - ret = IMU.begin(); - if (ret != 0) { - Serial.print("ICM42670P initialization failed: "); - Serial.println(ret); - while(1); - } - // Accel ODR = 100 Hz and Full Scale Range = 16G - IMU.startAccel(100,16); - // Gyro ODR = 100 Hz and Full Scale Range = 2000 dps - IMU.startGyro(100,2000); - // Wait IMU to start - delay(100); - // Plotter axis header - Serial.println("AccelX,AccelY,AccelZ,GyroX,GyroY,GyroZ,Temperature"); -} - -void loop() { - - inv_imu_sensor_event_t imu_event; - - // Get last event - IMU.getDataFromRegisters(&imu_event); - - // Format data for Serial Plotter - Serial.print(imu_event.accel[0]); - Serial.print(","); - Serial.print(imu_event.accel[1]); - Serial.print(","); - Serial.print(imu_event.accel[2]); - Serial.print(","); - Serial.print(imu_event.gyro[0]); - Serial.print(","); - Serial.print(imu_event.gyro[1]); - Serial.print(","); - Serial.print(imu_event.gyro[2]); - Serial.print(","); - Serial.println(imu_event.temperature); - - // Run @ ODR 100Hz - delay(10); -} diff --git a/lib/ICM42670P/keywords.txt b/lib/ICM42670P/keywords.txt deleted file mode 100644 index ba431e1..0000000 --- a/lib/ICM42670P/keywords.txt +++ /dev/null @@ -1,5 +0,0 @@ -ICM42670P KEYWORD1 -begin KEYWORD2 -startAccel KEYWORD2 -startGyro KEYWORD2 -getDataFromRegisters KEYWORD2 diff --git a/lib/ICM42670P/library.properties b/lib/ICM42670P/library.properties deleted file mode 100644 index c50bf07..0000000 --- a/lib/ICM42670P/library.properties +++ /dev/null @@ -1,10 +0,0 @@ -name=ICM42670P -version=1.0.0 -author=TDK/Invensense -maintainer=TDK/Invensense -sentence=The ICM42670P arduino library manages the communication with an ICM42670P Invensence high performance 6-axis MEMS MotionTracking device. -paragraph=This library allows to easily configure and log accelerometer, gyroscope and temperature data from an ICM42670P device, using the SPI or the I2C interface. -category=Sensors -url=https://github.com/InvenSenseInc/arduino.ICM42670P -architectures=* -includes=ICM42670P.h diff --git a/lib/ICM42670P/src/ICM42670P.cpp b/lib/ICM42670P/src/ICM42670P.cpp deleted file mode 100644 index 51b997f..0000000 --- a/lib/ICM42670P/src/ICM42670P.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * - * ------------------------------------------------------------------------------------------------------------ - * Copyright (c) 2022 InvenSense, Inc All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense, Inc and its licencors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense, Inc and its licencors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense, Inc is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * InvenSense, Inc BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * - * ------------------------------------------------------------------------------------------------------------ - */ - -#include "Arduino.h" -#include "ICM42670P.h" - -static int i2c_write(struct inv_imu_serif * serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen); -static int i2c_read(struct inv_imu_serif * serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen); -static int spi_write(struct inv_imu_serif * serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen); -static int spi_read(struct inv_imu_serif * serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen); -static void event_cb(inv_imu_sensor_event_t *event); - -// i2c and SPI interfaces are used from C driver callbacks, without any knowledge of the object -// As they are declared as static, they will be overriden each time a new ICM42670P object is created -// i2c -uint8_t i2c_address = 0; -static TwoWire *i2c = NULL; -#define ICM42670P_I2C_SPEED 1000000 -#define ICM42670P_I2C_ADDRESS 0x68 -// spi -static SPIClass *spi = NULL; -static uint8_t chip_select_id = 0; -bool _useSPI = false; -#define SPI_READ 0x80 -#define SPI_CLOCK 16000000 - -// This is used by the event callback (not object aware), declared static -static inv_imu_sensor_event_t* event; - -// This is used by the getDataFromFifo callback (not object aware), declared static -static struct inv_imu_device *icm_driver_ptr = NULL; - -// ICM42670P constructor for I2c interface -ICM42670P::ICM42670P(TwoWire &i2c_ref,bool lsb) { - i2c = &i2c_ref; - i2c_address = ICM42670P_I2C_ADDRESS | (lsb ? 0x1 : 0); -} - -// ICM42670P constructor for spi interface -ICM42670P::ICM42670P(SPIClass &spi_ref,uint8_t cs_id) { - spi = &spi_ref; - chip_select_id = cs_id; -} - -/* starts communication with the ICM42670P */ -int ICM42670P::begin() { - struct inv_imu_serif icm_serif; - int rc = 0; - uint8_t who_am_i; - - if (i2c != NULL) { - i2c->begin(); - i2c->setClock(ICM42670P_I2C_SPEED); - icm_serif.serif_type = UI_I2C; - icm_serif.read_reg = i2c_read; - icm_serif.write_reg = i2c_write; - } else { - spi->begin(); - pinMode(chip_select_id,OUTPUT); - digitalWrite(chip_select_id,HIGH); - icm_serif.serif_type = UI_SPI4; - icm_serif.read_reg = spi_read; - icm_serif.write_reg = spi_write; - } - /* Initialize serial interface between MCU and Icm43xxx */ - icm_serif.context = 0; /* no need */ - icm_serif.max_read = 2048; /* maximum number of bytes allowed per serial read */ - icm_serif.max_write = 2048; /* maximum number of bytes allowed per serial write */ - rc = inv_imu_init(&icm_driver, &icm_serif, NULL); - if (rc != INV_ERROR_SUCCESS) { - return rc; - } - icm_driver.sensor_event_cb = event_cb; - - /* Check WHOAMI */ - rc = inv_imu_get_who_am_i(&icm_driver, &who_am_i); - if(rc != 0) { - return -2; - } - if ((who_am_i != ICM42607P_WHOAMI) && (who_am_i != ICM42607C_WHOAMI) && (who_am_i != ICM42670P_WHOAMI) && (who_am_i != ICM42670S_WHOAMI) && (who_am_i != ICM42670T_WHOAMI)) { - return -3; - } - - // successful init, return 0 - return 0; -} - -int ICM42670P::startAccel(uint16_t odr, uint16_t fsr) { - int rc = 0; - rc |= inv_imu_set_accel_fsr(&icm_driver, accel_fsr_g_to_param(fsr)); - rc |= inv_imu_set_accel_frequency(&icm_driver, accel_freq_to_param(odr)); - rc |= inv_imu_enable_accel_low_noise_mode(&icm_driver); - return rc; -} - -int ICM42670P::startGyro(uint16_t odr, uint16_t fsr) { - int rc = 0; - rc |= inv_imu_set_gyro_fsr(&icm_driver, gyro_fsr_dps_to_param(fsr)); - rc |= inv_imu_set_gyro_frequency(&icm_driver, gyro_freq_to_param(odr)); - rc |= inv_imu_enable_gyro_low_noise_mode(&icm_driver); - return rc; -} - -int ICM42670P::getDataFromRegisters(inv_imu_sensor_event_t* evt) { - if(evt != NULL) { - // Set event buffer to be used by the callback - event = evt; - return inv_imu_get_data_from_registers(&icm_driver); - } else { - return -1; - } -} - -int ICM42670P::enableFifoInterrupt(uint8_t intpin, ICM42670P_irq_handler handler, uint8_t fifo_watermark) { - int rc = 0; - uint8_t data; - - if(handler == NULL) { - return -1; - } - pinMode(intpin, INPUT); - attachInterrupt(intpin, handler, RISING); - rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_ENABLED); - rc |= inv_imu_write_reg(&icm_driver, FIFO_CONFIG2, 1, &fifo_watermark); - // Set fifo_wm_int_w generating condition : fifo_wm_int_w generated when counter == threshold - rc |= inv_imu_read_reg(&icm_driver, FIFO_CONFIG5_MREG1, 1, &data); - data &= (uint8_t)~FIFO_CONFIG5_WM_GT_TH_EN; - rc |= inv_imu_write_reg(&icm_driver, FIFO_CONFIG5_MREG1, 1, &data); - // Disable APEX to use 2.25kB of fifo for raw data - data = SENSOR_CONFIG3_APEX_DISABLE_MASK; - rc |= inv_imu_write_reg(&icm_driver, SENSOR_CONFIG3_MREG1, 1, &data); - return rc; -} - -int ICM42670P::enableDataInterrupt(uint8_t intpin, ICM42670P_irq_handler handler) { - int rc = 0; - uint8_t data; - - if(handler == NULL) { - return -1; - } - pinMode(intpin, INPUT); - attachInterrupt(intpin, handler, RISING); - rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_DISABLED); - return rc; -} - -int ICM42670P::getDataFromFifo(ICM42670P_sensor_event_cb event_cb) { - if(event_cb == NULL) { - return -1; - } - icm_driver.sensor_event_cb = event_cb; - return inv_imu_get_data_from_fifo(&icm_driver); -} - -bool ICM42670P::isAccelDataValid(inv_imu_sensor_event_t *evt) { - return (evt->sensor_mask & (1<sensor_mask & (1<beginTransmission(i2c_address); - i2c->write(reg); - for(uint8_t i = 0; i < wlen; i++) { - i2c->write(wbuffer[i]); - } - i2c->endTransmission(); - return 0; -} - -static int i2c_read(struct inv_imu_serif * serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen) { - uint16_t rx_bytes = 0; - - i2c->beginTransmission(i2c_address); - i2c->write(reg); - i2c->endTransmission(false); - rx_bytes = i2c->requestFrom(i2c_address, rlen); - if (rlen == rx_bytes) { - for(uint8_t i = 0; i < rx_bytes; i++) { - rbuffer[i] = i2c->read(); - } - return 0; - } else { - return -1; - } -} - -static int spi_write(struct inv_imu_serif * serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen) { - spi->beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE1)); - digitalWrite(chip_select_id,LOW); - spi->transfer(reg); - for(uint8_t i = 0; i < wlen; i++) { - spi->transfer(wbuffer[i]); - } - digitalWrite(chip_select_id,HIGH); - spi->endTransaction(); - return 0; -} - -static int spi_read(struct inv_imu_serif * serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen) { - spi->beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE1)); - digitalWrite(chip_select_id,LOW); - spi->transfer(reg | SPI_READ); - spi->transfer(rbuffer,rlen); - digitalWrite(chip_select_id,HIGH); - spi->endTransaction(); - return 0; -} - -ACCEL_CONFIG0_FS_SEL_t ICM42670P::accel_fsr_g_to_param(uint16_t accel_fsr_g) { - ACCEL_CONFIG0_FS_SEL_t ret = ACCEL_CONFIG0_FS_SEL_16g; - - switch(accel_fsr_g) { - case 2: ret = ACCEL_CONFIG0_FS_SEL_2g; break; - case 4: ret = ACCEL_CONFIG0_FS_SEL_4g; break; - case 8: ret = ACCEL_CONFIG0_FS_SEL_8g; break; - case 16: ret = ACCEL_CONFIG0_FS_SEL_16g; break; - default: - /* Unknown accel FSR. Set to default 16G */ - break; - } - return ret; -} - -GYRO_CONFIG0_FS_SEL_t ICM42670P::gyro_fsr_dps_to_param(uint16_t gyro_fsr_dps) { - GYRO_CONFIG0_FS_SEL_t ret = GYRO_CONFIG0_FS_SEL_2000dps; - - switch(gyro_fsr_dps) { - case 250: ret = GYRO_CONFIG0_FS_SEL_250dps; break; - case 500: ret = GYRO_CONFIG0_FS_SEL_500dps; break; - case 1000: ret = GYRO_CONFIG0_FS_SEL_1000dps; break; - case 2000: ret = GYRO_CONFIG0_FS_SEL_2000dps; break; - default: - /* Unknown gyro FSR. Set to default 2000dps" */ - break; - } - return ret; -} - -ACCEL_CONFIG0_ODR_t ICM42670P::accel_freq_to_param(uint16_t accel_freq_hz) { - ACCEL_CONFIG0_ODR_t ret = ACCEL_CONFIG0_ODR_100_HZ; - - switch(accel_freq_hz) { - case 12: ret = ACCEL_CONFIG0_ODR_12_5_HZ; break; - case 25: ret = ACCEL_CONFIG0_ODR_25_HZ; break; - case 50: ret = ACCEL_CONFIG0_ODR_50_HZ; break; - case 100: ret = ACCEL_CONFIG0_ODR_100_HZ; break; - case 200: ret = ACCEL_CONFIG0_ODR_200_HZ; break; - case 400: ret = ACCEL_CONFIG0_ODR_400_HZ; break; - case 800: ret = ACCEL_CONFIG0_ODR_800_HZ; break; - case 1600: ret = ACCEL_CONFIG0_ODR_1600_HZ; break; - default: - /* Unknown accel frequency. Set to default 100Hz */ - break; - } - return ret; -} - -GYRO_CONFIG0_ODR_t ICM42670P::gyro_freq_to_param(uint16_t gyro_freq_hz) { - GYRO_CONFIG0_ODR_t ret = GYRO_CONFIG0_ODR_100_HZ; - - switch(gyro_freq_hz) { - case 12: ret = GYRO_CONFIG0_ODR_12_5_HZ; break; - case 25: ret = GYRO_CONFIG0_ODR_25_HZ; break; - case 50: ret = GYRO_CONFIG0_ODR_50_HZ; break; - case 100: ret = GYRO_CONFIG0_ODR_100_HZ; break; - case 200: ret = GYRO_CONFIG0_ODR_200_HZ; break; - case 400: ret = GYRO_CONFIG0_ODR_400_HZ; break; - case 800: ret = GYRO_CONFIG0_ODR_800_HZ; break; - case 1600: ret = GYRO_CONFIG0_ODR_1600_HZ; break; - default: - /* Unknown gyro ODR. Set to default 100Hz */ - break; - } - return ret; -} - - -static void event_cb(inv_imu_sensor_event_t *evt) { - memcpy(event,evt,sizeof(inv_imu_sensor_event_t)); -} diff --git a/lib/ICM42670P/src/ICM42670P.h b/lib/ICM42670P/src/ICM42670P.h deleted file mode 100644 index 90cca38..0000000 --- a/lib/ICM42670P/src/ICM42670P.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * ------------------------------------------------------------------------------------------------------------ - * Copyright (c) 2022 InvenSense, Inc All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense, Inc and its licencors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense, Inc and its licencors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense, Inc is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * InvenSense, Inc BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * - * ------------------------------------------------------------------------------------------------------------ - */ - -#ifndef ICM42670P_H -#define ICM42670P_H - -#include "Arduino.h" -#include "SPI.h" -#include "Wire.h" - -extern "C" { -#include "imu/inv_imu_driver.h" -#undef ICM42670P -} - -// This defines the handler called when retrieving a sample from the FIFO -typedef void (*ICM42670P_sensor_event_cb)(inv_imu_sensor_event_t *event); -// This defines the handler called when receiving an irq -typedef void (*ICM42670P_irq_handler)(void); - -class ICM42670P { - public: - ICM42670P(TwoWire &i2c,bool address_lsb); - ICM42670P(SPIClass &spi,uint8_t chip_select_id); - int begin(); - int startAccel(uint16_t odr, uint16_t fsr); - int startGyro(uint16_t odr, uint16_t fsr); - int getDataFromRegisters(inv_imu_sensor_event_t* evt); - int enableFifoInterrupt(uint8_t intpin, ICM42670P_irq_handler handler, uint8_t fifo_watermark); - int enableDataInterrupt(uint8_t intpin, ICM42670P_irq_handler handler); - int getDataFromFifo(ICM42670P_sensor_event_cb event_cb); - bool isAccelDataValid(inv_imu_sensor_event_t *evt); - bool isGyroDataValid(inv_imu_sensor_event_t *evt); - - protected: - struct inv_imu_device icm_driver; - ACCEL_CONFIG0_ODR_t accel_freq_to_param(uint16_t accel_freq_hz); - GYRO_CONFIG0_ODR_t gyro_freq_to_param(uint16_t gyro_freq_hz); - ACCEL_CONFIG0_FS_SEL_t accel_fsr_g_to_param(uint16_t accel_fsr_g); - GYRO_CONFIG0_FS_SEL_t gyro_fsr_dps_to_param(uint16_t gyro_fsr_dps); -}; - -#endif // ICM42670P_H diff --git a/lib/ICM42670P/src/Invn/InvError.h b/lib/ICM42670P/src/Invn/InvError.h deleted file mode 100644 index 9fe49a2..0000000 --- a/lib/ICM42670P/src/Invn/InvError.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * ________________________________________________________________________________________________________ - * Copyright (c) 2015-2015 InvenSense Inc. All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively “Software”) is subject - * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * ________________________________________________________________________________________________________ - */ - -/** @defgroup InvError Error code - * @brief Common error code - * - * @ingroup EmbUtils - * @{ - */ - -#ifndef _INV_ERROR_H_ -#define _INV_ERROR_H_ - -/** @brief Common error code definition - */ -enum inv_error -{ - INV_ERROR_SUCCESS = 0, /**< no error */ - INV_ERROR = -1, /**< unspecified error */ - INV_ERROR_NIMPL = -2, /**< function not implemented for given - arguments */ - INV_ERROR_TRANSPORT = -3, /**< error occured at transport level */ - INV_ERROR_TIMEOUT = -4, /**< action did not complete in the expected - time window */ - INV_ERROR_SIZE = -5, /**< size/length of given arguments is not - suitable to complete requested action */ - INV_ERROR_OS = -6, /**< error related to OS */ - INV_ERROR_IO = -7, /**< error related to IO operation */ - INV_ERROR_MEM = -9, /**< not enough memory to complete requested - action */ - INV_ERROR_HW = -10, /**< error at HW level */ - INV_ERROR_BAD_ARG = -11, /**< provided arguments are not good to - perform requestion action */ - INV_ERROR_UNEXPECTED = -12, /**< something unexpected happened */ - INV_ERROR_FILE = -13, /**< cannot access file or unexpected format */ - INV_ERROR_PATH = -14, /**< invalid file path */ - INV_ERROR_IMAGE_TYPE = -15, /**< error when image type is not managed */ - INV_ERROR_WATCHDOG = -16, /**< error when device doesn't respond - to ping */ -}; - -#endif /* _INV_ERROR_H_ */ - -/** @} */ diff --git a/lib/ICM42670P/src/imu/inv_imu_defs.h b/lib/ICM42670P/src/imu/inv_imu_defs.h deleted file mode 100644 index 7ad503a..0000000 --- a/lib/ICM42670P/src/imu/inv_imu_defs.h +++ /dev/null @@ -1,991 +0,0 @@ -/* - * ________________________________________________________________________________________________________ - * Copyright (c) 2017 InvenSense Inc. All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * ________________________________________________________________________________________________________ - */ - -#ifndef _INV_IMU_DEFS_H_ -#define _INV_IMU_DEFS_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/** @file inv_imu_defs.h - * File exposing the device register map - */ - -#include -/* List whoami values for all device variants*/ -#define T1000_WHOAMI 0x30 -#define ICM42607P_WHOAMI 0x60 -#define ICM42607C_WHOAMI 0x61 -#define ICM42670P_WHOAMI 0x67 -#define ICM42670T_WHOAMI 0x64 -#define ICM42670S_WHOAMI 0x69 -#define ICM42680_WHOAMI 0x80 - - -#define ICM42670P - -/* Define whoami value for the targeted product and make sure the target is valid */ -#if defined(T1000) -#define ICM_WHOAMI T1000_WHOAMI -#elif defined(ICM42607P) -#define ICM_WHOAMI ICM42607P_WHOAMI -#elif defined(ICM42670P) -#define ICM_WHOAMI ICM42670P_WHOAMI -#elif defined(ICM42670T) -#define ICM_WHOAMI ICM42670T_WHOAMI -#elif defined(ICM42670S) -#define ICM_WHOAMI ICM42670S_WHOAMI -#elif defined(ICM42680) -#define ICM_WHOAMI ICM42680_WHOAMI -#else -#error "Please define which IMU variant is targeted." -#endif - -#include "imu/inv_imu_regmap.h" - -/* ---------------------------------------------------------------------------- - * Device features - * - * Next macros define some of the device features such as FIFO, sensor data - * size or whoami value. - * ---------------------------------------------------------------------------- */ - -#define ACCEL_DATA_SIZE 6 -#define GYRO_DATA_SIZE 6 -#define TEMP_DATA_SIZE 2 - -#define FIFO_HEADER_SIZE 1 -#define FIFO_ACCEL_DATA_SIZE ACCEL_DATA_SIZE -#define FIFO_GYRO_DATA_SIZE GYRO_DATA_SIZE -#define FIFO_TEMP_DATA_SIZE 1 -#define FIFO_TS_FSYNC_SIZE 2 -#define FIFO_TEMP_HIGH_RES_SIZE 1 -#define FIFO_ACCEL_GYRO_HIGH_RES_SIZE 3 - -#define FIFO_16BYTES_PACKET_SIZE \ - (FIFO_HEADER_SIZE + FIFO_ACCEL_DATA_SIZE + FIFO_GYRO_DATA_SIZE + FIFO_TEMP_DATA_SIZE + \ - FIFO_TS_FSYNC_SIZE) -#define FIFO_20BYTES_PACKET_SIZE \ - (FIFO_HEADER_SIZE + FIFO_ACCEL_DATA_SIZE + FIFO_GYRO_DATA_SIZE + FIFO_TEMP_DATA_SIZE + \ - FIFO_TS_FSYNC_SIZE + FIFO_TEMP_HIGH_RES_SIZE + FIFO_ACCEL_GYRO_HIGH_RES_SIZE) - -#define FIFO_HEADER_ODR_ACCEL 0x01 -#define FIFO_HEADER_ODR_GYRO 0x02 -#define FIFO_HEADER_FSYNC 0x04 -#define FIFO_HEADER_TMST 0x08 -#define FIFO_HEADER_HEADER_20 0x10 -#define FIFO_HEADER_GYRO 0x20 -#define FIFO_HEADER_ACC 0x40 -#define FIFO_HEADER_MSG 0x80 - -#define INVALID_VALUE_FIFO ((int16_t)0x8000) -#define INVALID_VALUE_FIFO_1B ((int8_t)0x80) -#define OUT_OF_BOUND_TEMPERATURE_NEG_FIFO_1B ((int8_t)0x81) -#define OUT_OF_BOUND_TEMPERATURE_POS_FIFO_1B ((int8_t)0x7F) - -/** Describe the content of the FIFO header */ -typedef union { - unsigned char Byte; - struct { - unsigned char gyro_odr_different : 1; - unsigned char accel_odr_different : 1; - unsigned char fsync_bit : 1; - unsigned char timestamp_bit : 1; - unsigned char twentybits_bit : 1; - unsigned char gyro_bit : 1; - unsigned char accel_bit : 1; - unsigned char msg_bit : 1; - } bits; -} fifo_header_t; - -/* ---------------------------------------------------------------------------- - * Device registers description - * - * Next section defines some of the registers bitfield and declare corresponding - * accessors. - * Note that descriptors and accessors are not provided for all the registers - * but only for the most useful ones. - * For all details on registers and bitfields functionalities please refer to - * the device datasheet. - * ---------------------------------------------------------------------------- */ - -/* --------------------------------------------------------------------------- - * register bank 0 - * ---------------------------------------------------------------------------- */ - -/* - * DEVICE_CONFIG - * Register Name : DEVICE_CONFIG - */ - -/* SPI_MODE */ -typedef enum { - DEVICE_CONFIG_SPI_MODE_1_2 = (0x1 << DEVICE_CONFIG_SPI_MODE_POS), - DEVICE_CONFIG_SPI_MODE_0_3 = (0x0 << DEVICE_CONFIG_SPI_MODE_POS), -} DEVICE_CONFIG_SPI_MODE_t; - -/* SPI_AP_4WIRE */ -typedef enum { - DEVICE_CONFIG_SPI_4WIRE = (0x1 << DEVICE_CONFIG_SPI_AP_4WIRE_POS), - DEVICE_CONFIG_SPI_3WIRE = (0x0 << DEVICE_CONFIG_SPI_AP_4WIRE_POS), -} DEVICE_CONFIG_SPI_AP_4WIRE_t; - -/* - * SIGNAL_PATH_RESET - * Register Name: SIGNAL_PATH_RESET - */ - -/* SOFT_RESET_DEVICE_CONFIG */ -typedef enum { - SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_EN = - (0x01 << SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_POS), - SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_DIS = - (0x00 << SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_POS), -} SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_t; - -/* FIFO_FLUSH */ -typedef enum { - SIGNAL_PATH_RESET_FIFO_FLUSH_EN = (0x01 << SIGNAL_PATH_RESET_FIFO_FLUSH_POS), - SIGNAL_PATH_RESET_FIFO_FLUSH_DIS = (0x00 << SIGNAL_PATH_RESET_FIFO_FLUSH_POS), -} SIGNAL_PATH_RESET_FIFO_FLUSH_t; - -/* - * INT_CONFIG - * Register Name: INT_CONFIG - */ - -/* INT2_MODE */ -typedef enum { - INT_CONFIG_INT2_MODE_LATCHED = (0x01 << INT_CONFIG_INT2_MODE_POS), - INT_CONFIG_INT2_MODE_PULSED = (0x00 << INT_CONFIG_INT2_MODE_POS), -} INT_CONFIG_INT2_MODE_t; - -/* INT2_DRIVE_CIRCUIT */ -typedef enum { - INT_CONFIG_INT2_DRIVE_CIRCUIT_PP = (0x01 << INT_CONFIG_INT2_DRIVE_CIRCUIT_POS), - INT_CONFIG_INT2_DRIVE_CIRCUIT_OD = (0x00 << INT_CONFIG_INT2_DRIVE_CIRCUIT_POS), -} INT_CONFIG_INT2_DRIVE_CIRCUIT_t; - -/* INT2_POLARITY */ -typedef enum { - INT_CONFIG_INT2_POLARITY_HIGH = (0x01 << INT_CONFIG_INT2_POLARITY_POS), - INT_CONFIG_INT2_POLARITY_LOW = (0x00 << INT_CONFIG_INT2_POLARITY_POS), -} INT_CONFIG_INT2_POLARITY_t; - -/* INT1_MODE */ -typedef enum { - INT_CONFIG_INT1_MODE_LATCHED = (0x01 << INT_CONFIG_INT1_MODE_POS), - INT_CONFIG_INT1_MODE_PULSED = (0x00 << INT_CONFIG_INT1_MODE_POS), -} INT_CONFIG_INT1_MODE_t; - -/* INT1_DRIVE_CIRCUIT */ -typedef enum { - INT_CONFIG_INT1_DRIVE_CIRCUIT_PP = (0x01 << INT_CONFIG_INT1_DRIVE_CIRCUIT_POS), - INT_CONFIG_INT1_DRIVE_CIRCUIT_OD = (0x00 << INT_CONFIG_INT1_DRIVE_CIRCUIT_POS), -} INT_CONFIG_INT1_DRIVE_CIRCUIT_t; - -/* INT1_POLARITY */ -typedef enum { - INT_CONFIG_INT1_POLARITY_HIGH = 0x01, - INT_CONFIG_INT1_POLARITY_LOW = 0x00, -} INT_CONFIG_INT1_POLARITY_t; - -/* - * PWR_MGMT0 - * Register Name: PWR_MGMT0 - */ - -/* ACCEL_LP_CLK_SEL */ -typedef enum { - PWR_MGMT0_ACCEL_LP_CLK_WUOSC = (0x00 << PWR_MGMT0_ACCEL_LP_CLK_SEL_POS), - PWR_MGMT0_ACCEL_LP_CLK_RCOSC = (0x01 << PWR_MGMT0_ACCEL_LP_CLK_SEL_POS), -} PWR_MGMT0_ACCEL_LP_CLK_t; - -/* IDLE */ -typedef enum { - PWR_MGMT0_IDLE_DIS = (0x01 << PWR_MGMT0_IDLE_POS), - PWR_MGMT0_IDLE_EN = (0x00 << PWR_MGMT0_IDLE_POS), -} PWR_MGMT0_IDLE_t; - -/* GYRO_MODE */ -typedef enum { - PWR_MGMT0_GYRO_MODE_LN = (0x03 << PWR_MGMT0_GYRO_MODE_POS), - PWR_MGMT0_GYRO_MODE_LP = (0x02 << PWR_MGMT0_GYRO_MODE_POS), - PWR_MGMT0_GYRO_MODE_STANDBY = (0x01 << PWR_MGMT0_GYRO_MODE_POS), - PWR_MGMT0_GYRO_MODE_OFF = (0x00 << PWR_MGMT0_GYRO_MODE_POS), -} PWR_MGMT0_GYRO_MODE_t; - -/* ACCEL_MODE */ -typedef enum { - PWR_MGMT0_ACCEL_MODE_LN = 0x03, - PWR_MGMT0_ACCEL_MODE_LP = 0x02, - PWR_MGMT0_ACCEL_MODE_OFF = 0x00, -} PWR_MGMT0_ACCEL_MODE_t; - -/* - * GYRO_CONFIG0 - * Register Name: GYRO_CONFIG0 - */ - -/* GYRO_FS_SEL*/ -typedef enum { - GYRO_CONFIG0_FS_SEL_250dps = (3 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS), - GYRO_CONFIG0_FS_SEL_500dps = (2 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS), - GYRO_CONFIG0_FS_SEL_1000dps = (1 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS), - GYRO_CONFIG0_FS_SEL_2000dps = (0 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS), -} GYRO_CONFIG0_FS_SEL_t; - -/* GYRO_ODR */ -typedef enum { - GYRO_CONFIG0_ODR_1_5625_HZ = 0xF, - GYRO_CONFIG0_ODR_3_125_HZ = 0xE, - GYRO_CONFIG0_ODR_6_25_HZ = 0xD, - GYRO_CONFIG0_ODR_12_5_HZ = 0xC, - GYRO_CONFIG0_ODR_25_HZ = 0xB, - GYRO_CONFIG0_ODR_50_HZ = 0xA, - GYRO_CONFIG0_ODR_100_HZ = 0x9, - GYRO_CONFIG0_ODR_200_HZ = 0x8, - GYRO_CONFIG0_ODR_400_HZ = 0x7, - GYRO_CONFIG0_ODR_800_HZ = 0x6, - GYRO_CONFIG0_ODR_1600_HZ = 0x5, -} GYRO_CONFIG0_ODR_t; - -/* - * ACCEL_CONFIG0 - * Register Name: ACCEL_CONFIG0 - */ - -/* ACCEL_FS_SEL */ -typedef enum { - ACCEL_CONFIG0_FS_SEL_2g = (0x3 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS), - ACCEL_CONFIG0_FS_SEL_4g = (0x2 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS), - ACCEL_CONFIG0_FS_SEL_8g = (0x1 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS), - ACCEL_CONFIG0_FS_SEL_16g = (0x0 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS), -} ACCEL_CONFIG0_FS_SEL_t; - -/* ACCEL_ODR */ -typedef enum { - ACCEL_CONFIG0_ODR_1_5625_HZ = 0xF, - ACCEL_CONFIG0_ODR_3_125_HZ = 0xE, - ACCEL_CONFIG0_ODR_6_25_HZ = 0xD, - ACCEL_CONFIG0_ODR_12_5_HZ = 0xC, - ACCEL_CONFIG0_ODR_25_HZ = 0xB, - ACCEL_CONFIG0_ODR_50_HZ = 0xA, - ACCEL_CONFIG0_ODR_100_HZ = 0x9, - ACCEL_CONFIG0_ODR_200_HZ = 0x8, - ACCEL_CONFIG0_ODR_400_HZ = 0x7, - ACCEL_CONFIG0_ODR_800_HZ = 0x6, - ACCEL_CONFIG0_ODR_1600_HZ = 0x5, -} ACCEL_CONFIG0_ODR_t; - -/* - * GYRO_CONFIG1 - * Register Name: GYRO_CONFIG1 - */ - -/* GYRO_UI_FILT_BW_IND */ -typedef enum { - GYRO_CONFIG1_GYRO_FILT_BW_16 = (0x07 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), - GYRO_CONFIG1_GYRO_FILT_BW_25 = (0x06 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), - GYRO_CONFIG1_GYRO_FILT_BW_34 = (0x05 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), - GYRO_CONFIG1_GYRO_FILT_BW_53 = (0x04 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), - GYRO_CONFIG1_GYRO_FILT_BW_73 = (0x03 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), - GYRO_CONFIG1_GYRO_FILT_BW_121 = (0x02 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), - GYRO_CONFIG1_GYRO_FILT_BW_180 = (0x01 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), - GYRO_CONFIG1_GYRO_FILT_BW_NO_FILTER = (0x00 << GYRO_CONFIG1_GYRO_UI_FILT_BW_POS), -} GYRO_CONFIG1_GYRO_FILT_BW_t; - -/* - * ACCEL_CONFIG1 - * Register Name: ACCEL_CONFIG1 - */ - -/* ACCEL_UI_AVG_IND */ -typedef enum { - ACCEL_CONFIG1_ACCEL_FILT_AVG_64 = (0x5 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), - ACCEL_CONFIG1_ACCEL_FILT_AVG_32 = (0x4 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), - ACCEL_CONFIG1_ACCEL_FILT_AVG_16 = (0x3 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), - ACCEL_CONFIG1_ACCEL_FILT_AVG_8 = (0x2 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), - ACCEL_CONFIG1_ACCEL_FILT_AVG_4 = (0x1 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), - ACCEL_CONFIG1_ACCEL_FILT_AVG_2 = (0x0 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS), -} ACCEL_CONFIG1_ACCEL_FILT_AVG_t; - -/* ACCEL_UI_FILT_BW_IND */ -typedef enum { - ACCEL_CONFIG1_ACCEL_FILT_BW_16 = (0x7 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), - ACCEL_CONFIG1_ACCEL_FILT_BW_25 = (0x6 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), - ACCEL_CONFIG1_ACCEL_FILT_BW_34 = (0x5 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), - ACCEL_CONFIG1_ACCEL_FILT_BW_53 = (0x4 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), - ACCEL_CONFIG1_ACCEL_FILT_BW_73 = (0x3 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), - ACCEL_CONFIG1_ACCEL_FILT_BW_121 = (0x2 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), - ACCEL_CONFIG1_ACCEL_FILT_BW_180 = (0x1 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), - ACCEL_CONFIG1_ACCEL_FILT_BW_NO_FILTER = (0x0 << ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS), -} ACCEL_CONFIG1_ACCEL_FILT_BW_t; - -/* - * APEX_CONFIG0 - * Register Name: APEX_CONFIG0 - */ - -/* DMP_POWER_SAVE_EN */ -typedef enum { - APEX_CONFIG0_DMP_POWER_SAVE_EN = (0x1 << APEX_CONFIG0_DMP_POWER_SAVE_EN_POS), - APEX_CONFIG0_DMP_POWER_SAVE_DIS = (0x0 << APEX_CONFIG0_DMP_POWER_SAVE_EN_POS), -} APEX_CONFIG0_DMP_POWER_SAVE_t; - -/* DMP_INIT_EN */ -typedef enum { - APEX_CONFIG0_DMP_INIT_EN = (0x01 << APEX_CONFIG0_DMP_INIT_EN_POS), - APEX_CONFIG0_DMP_INIT_DIS = (0x00 << APEX_CONFIG0_DMP_INIT_EN_POS), -} APEX_CONFIG0_DMP_INIT_t; - -/* DMP_MEM_RESET */ -typedef enum { - APEX_CONFIG0_DMP_MEM_RESET_APEX_ST_EN = (0x01 << APEX_CONFIG0_DMP_MEM_RESET_EN_POS), - APEX_CONFIG0_DMP_MEM_RESET_DIS = (0x00 << APEX_CONFIG0_DMP_MEM_RESET_EN_POS), -} APEX_CONFIG0_DMP_MEM_RESET_t; - -/* - * APEX_CONFIG1 - * Register Name: APEX_CONFIG1 - */ - -/* SMD_ENABLE */ -typedef enum { - APEX_CONFIG1_SMD_ENABLE_DIS = (0x00 << APEX_CONFIG1_SMD_ENABLE_POS), - APEX_CONFIG1_SMD_ENABLE_EN = (0x01 << APEX_CONFIG1_SMD_ENABLE_POS), -} APEX_CONFIG1_SMD_ENABLE_t; - -/* FF_ENABLE */ -typedef enum { - APEX_CONFIG1_FF_ENABLE_DIS = (0x00 << APEX_CONFIG1_FF_ENABLE_POS), - APEX_CONFIG1_FF_ENABLE_EN = (0x01 << APEX_CONFIG1_FF_ENABLE_POS), -} APEX_CONFIG1_FF_ENABLE_t; - -/* TILT_ENABLE */ -typedef enum { - APEX_CONFIG1_TILT_ENABLE_DIS = (0x0 << APEX_CONFIG1_TILT_ENABLE_POS), - APEX_CONFIG1_TILT_ENABLE_EN = (0x1 << APEX_CONFIG1_TILT_ENABLE_POS), -} APEX_CONFIG1_TILT_ENABLE_t; - -/* PED_ENABLE */ -typedef enum { - APEX_CONFIG1_PED_ENABLE_DIS = (0x0 << APEX_CONFIG1_PED_ENABLE_POS), - APEX_CONFIG1_PED_ENABLE_EN = (0x1 << APEX_CONFIG1_PED_ENABLE_POS), -} APEX_CONFIG1_PED_ENABLE_t; - -/* DMP_ODR */ -typedef enum { - APEX_CONFIG1_DMP_ODR_25Hz = (0x0 << APEX_CONFIG1_DMP_ODR_POS), - APEX_CONFIG1_DMP_ODR_50Hz = (0x2 << APEX_CONFIG1_DMP_ODR_POS), - APEX_CONFIG1_DMP_ODR_100Hz = (0x3 << APEX_CONFIG1_DMP_ODR_POS), - APEX_CONFIG1_DMP_ODR_400Hz = (0x1 << APEX_CONFIG1_DMP_ODR_POS), -} APEX_CONFIG1_DMP_ODR_t; - -/* - * WOM_CONFIG - * Register Name: WOM_CONFIG - */ - -/* WOM_INT_DUR */ -typedef enum { - WOM_CONFIG_WOM_INT_DUR_1_SMPL = (0x00 << WOM_CONFIG_WOM_INT_DUR_POS), - WOM_CONFIG_WOM_INT_DUR_2_SMPL = (0x01 << WOM_CONFIG_WOM_INT_DUR_POS), - WOM_CONFIG_WOM_INT_DUR_3_SMPL = (0x02 << WOM_CONFIG_WOM_INT_DUR_POS), - WOM_CONFIG_WOM_INT_DUR_4_SMPL = (0x03 << WOM_CONFIG_WOM_INT_DUR_POS), -} WOM_CONFIG_WOM_INT_DUR_t; - -/* WOM_INT_MODE */ -typedef enum { - WOM_CONFIG_WOM_INT_MODE_ANDED = (0x01 << WOM_CONFIG_WOM_INT_MODE_POS), - WOM_CONFIG_WOM_INT_MODE_ORED = (0x00 << WOM_CONFIG_WOM_INT_MODE_POS), -} WOM_CONFIG_WOM_INT_MODE_t; - -/* WOM_MODE */ -typedef enum { - WOM_CONFIG_WOM_MODE_CMP_PREV = (0x01 << WOM_CONFIG_WOM_MODE_POS), - WOM_CONFIG_WOM_MODE_CMP_INIT = (0x00 << WOM_CONFIG_WOM_MODE_POS), -} WOM_CONFIG_WOM_MODE_t; - -/* WOM_ENABLE */ -typedef enum { - WOM_CONFIG_WOM_EN_ENABLE = (0x01 << WOM_CONFIG_WOM_EN_POS), - WOM_CONFIG_WOM_EN_DISABLE = (0x00 << WOM_CONFIG_WOM_EN_POS), -} WOM_CONFIG_WOM_EN_t; - -/* - * FIFO_CONFIG1 - * Register Name: FIFO_CONFIG - */ - -/* FIFO_MODE */ -typedef enum { - FIFO_CONFIG1_FIFO_MODE_SNAPSHOT = (0x01 << FIFO_CONFIG1_FIFO_MODE_POS), - FIFO_CONFIG1_FIFO_MODE_STREAM = (0x00 << FIFO_CONFIG1_FIFO_MODE_POS) -} FIFO_CONFIG1_FIFO_MODE_t; - -/* FIFO_BYPASS */ -typedef enum { - FIFO_CONFIG1_FIFO_BYPASS_ON = (0x01 << FIFO_CONFIG1_FIFO_BYPASS_POS), - FIFO_CONFIG1_FIFO_BYPASS_OFF = (0x00 << FIFO_CONFIG1_FIFO_BYPASS_POS), -} FIFO_CONFIG1_FIFO_BYPASS_t; - -/* - * APEX_DATA0 and APEX_DATA1 - * Register Name: APEX_DATA0 and APEX_DATA1 - */ - -/* Pedometer output */ -typedef struct APEX_DATA_STEP_ACTIVITY { - uint16_t step_cnt; /* Number of steps taken */ - uint8_t step_cadence; /* Walk/run cadence in number of samples. Format is u6.2.*/ - uint8_t activity_class; /* Detected activity unknown (0), walk (1) or run (2) */ -} APEX_DATA_STEP_ACTIVITY_t; - -/* - * APEX_DATA3 - * Register Name: APEX_DATA3 - */ - -/* DMP_IDLE */ -typedef enum { - APEX_DATA3_DMP_IDLE_ON = (0x01 << APEX_DATA3_DMP_IDLE_POS), - APEX_DATA3_DMP_IDLE_OFF = (0x00 << APEX_DATA3_DMP_IDLE_POS), -} APEX_DATA3_DMP_IDLE_OFF_t; - -/* ACTIVITY_CLASS */ -typedef enum { - APEX_DATA3_ACTIVITY_CLASS_OTHER = 0x0, - APEX_DATA3_ACTIVITY_CLASS_WALK = 0x1, - APEX_DATA3_ACTIVITY_CLASS_RUN = 0x2, -} APEX_DATA3_ACTIVITY_CLASS_t; - -/* - * INTF_CONFIG0 - * Register Name: INTF_CONFIG0 - */ - -/* FIFO_COUNT_REC */ -typedef enum { - INTF_CONFIG0_FIFO_COUNT_REC_RECORD = (0x01 << INTF_CONFIG0_FIFO_COUNT_FORMAT_POS), - INTF_CONFIG0_FIFO_COUNT_REC_BYTE = (0x00 << INTF_CONFIG0_FIFO_COUNT_FORMAT_POS), -} INTF_CONFIG0_FIFO_COUNT_REC_t; - -/* FIFO_COUNT_ENDIAN */ -typedef enum { - INTF_CONFIG0_FIFO_COUNT_BIG_ENDIAN = (0x01 << INTF_CONFIG0_FIFO_COUNT_ENDIAN_POS), - INTF_CONFIG0_FIFO_COUNT_LITTLE_ENDIAN = (0x00 << INTF_CONFIG0_FIFO_COUNT_ENDIAN_POS), -} INTF_CONFIG0_FIFO_COUNT_ENDIAN_t; - -/* DATA_ENDIAN */ -typedef enum { - INTF_CONFIG0_DATA_BIG_ENDIAN = (0x01 << INTF_CONFIG0_SENSOR_DATA_ENDIAN_POS), - INTF_CONFIG0_DATA_LITTLE_ENDIAN = (0x00 << INTF_CONFIG0_SENSOR_DATA_ENDIAN_POS), -} INTF_CONFIG0_DATA_ENDIAN_t; - -/* --------------------------------------------------------------------------- - * register bank MREG1 - * ---------------------------------------------------------------------------- */ - -/* - * TMST_CONFIG1_MREG1 - * Register Name: TMST_CONFIG1 - */ - -/* TMST_RES */ -typedef enum { - TMST_CONFIG1_RESOL_16us = (0x01 << TMST_CONFIG1_TMST_RES_POS), - TMST_CONFIG1_RESOL_1us = (0x00 << TMST_CONFIG1_TMST_RES_POS), -} TMST_CONFIG1_RESOL_t; - -/* TMST_FSYNC */ -typedef enum { - TMST_CONFIG1_TMST_FSYNC_EN = (0x01 << TMST_CONFIG1_TMST_FSYNC_EN_POS), - TMST_CONFIG1_TMST_FSYNC_DIS = (0x00 << TMST_CONFIG1_TMST_FSYNC_EN_POS), -} TMST_CONFIG1_TMST_FSYNC_EN_t; - -/* TMST_EN */ -typedef enum { - TMST_CONFIG1_TMST_EN = 0x01, - TMST_CONFIG1_TMST_DIS = 0x00, -} TMST_CONFIG1_TMST_EN_t; - -/* - * FIFO_CONFIG5_MREG1 - * Register Name: FIFO_CONFIG5 - */ -/* FIFO_WM_GT_TH */ -typedef enum { - FIFO_CONFIG5_WM_GT_TH_EN = (0x1 << FIFO_CONFIG5_FIFO_WM_GT_TH_POS), - FIFO_CONFIG5_WM_GT_TH_DIS = (0x0 << FIFO_CONFIG5_FIFO_WM_GT_TH_POS), -} FIFO_CONFIG5_WM_GT_t; - -/* FIFO_HIRES_EN */ -typedef enum { - FIFO_CONFIG5_HIRES_EN = (0x1 << FIFO_CONFIG5_FIFO_HIRES_EN_POS), - FIFO_CONFIG5_HIRES_DIS = (0x0 << FIFO_CONFIG5_FIFO_HIRES_EN_POS), -} FIFO_CONFIG5_HIRES_t; - -/* FIFO_TMST_FSYNC_EN */ -typedef enum { - FIFO_CONFIG5_TMST_FSYNC_EN = (0x1 << FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_POS), - FIFO_CONFIG5_TMST_FSYNC_DIS = (0x0 << FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_POS), -} FIFO_CONFIG5_TMST_FSYNC_t; - -/* FIFO_GYRO_EN */ -typedef enum { - FIFO_CONFIG5_GYRO_EN = (0x1 << FIFO_CONFIG5_FIFO_GYRO_EN_POS), - FIFO_CONFIG5_GYRO_DIS = (0x0 << FIFO_CONFIG5_FIFO_GYRO_EN_POS), -} FIFO_CONFIG5_GYRO_t; - -/* FIFO_ACCEL_EN*/ -typedef enum { - FIFO_CONFIG5_ACCEL_EN = 0x01, - FIFO_CONFIG5_ACCEL_DIS = 0x00, -} FIFO_CONFIG5_ACCEL_t; - -/* - * FSYNC_CONFIG_MREG1 - * Register Name: FSYNC_CONFIG - */ - -/* FSYNC_UI_SEL */ -typedef enum { - FSYNC_CONFIG_UI_SEL_NO = (0x0 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), - FSYNC_CONFIG_UI_SEL_TEMP = (0x1 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), - FSYNC_CONFIG_UI_SEL_GYRO_X = (0x2 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), - FSYNC_CONFIG_UI_SEL_GYRO_Y = (0x3 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), - FSYNC_CONFIG_UI_SEL_GYRO_Z = (0x4 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), - FSYNC_CONFIG_UI_SEL_ACCEL_X = (0x5 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), - FSYNC_CONFIG_UI_SEL_ACCEL_Y = (0x6 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), - FSYNC_CONFIG_UI_SEL_ACCEL_Z = (0x7 << FSYNC_CONFIG_FSYNC_UI_SEL_POS), -} FSYNC_CONFIG_UI_SEL_t; - -/* - * ST_CONFIG_MREG1 - * Register Name: ST_CONFIG - */ -typedef enum { - ST_CONFIG_16_SAMPLES = (0 << ST_CONFIG_ST_NUMBER_SAMPLE_POS), - ST_CONFIG_200_SAMPLES = (1 << ST_CONFIG_ST_NUMBER_SAMPLE_POS), -} ST_CONFIG_NUM_SAMPLES_t; - -typedef enum { - ST_CONFIG_ACCEL_ST_LIM_50 = (7 << ST_CONFIG_ACCEL_ST_LIM_POS), -} ST_CONFIG_ACCEL_ST_LIM_t; - -typedef enum { - ST_CONFIG_GYRO_ST_LIM_50 = (7 << ST_CONFIG_GYRO_ST_LIM_POS), -} ST_CONFIG_GYRO_ST_LIM_t; - -/* - * SELFTEST_MREG1 - * Register Name: SELFTEST - */ - -/* GYRO_ST_EN and ACCEL_ST_EN */ -typedef enum { - SELFTEST_DIS = 0, - SELFTEST_ACCEL_EN = SELFTEST_ACCEL_ST_EN_MASK, - SELFTEST_GYRO_EN = SELFTEST_GYRO_ST_EN_MASK, - SELFTEST_EN = (SELFTEST_ACCEL_ST_EN_MASK | SELFTEST_GYRO_ST_EN_MASK) -} SELFTEST_ACCEL_GYRO_ST_EN_t; - -/* - * OTP_CONFIG_MREG1 - * Register Name: OTP_CONFIG - */ - -/* OTP_CONFIG */ -typedef enum { - OTP_CONFIG_OTP_COPY_TRIM = (1 << OTP_CONFIG_OTP_COPY_MODE_POS), - OTP_CONFIG_OTP_COPY_ST_DATA = (3 << OTP_CONFIG_OTP_COPY_MODE_POS), -} OTP_CONFIG_COPY_MODE_t; - -/* - * APEX_CONFIG2_MREG1 - * Register Name: APEX_CONFIG2 -*/ - -/* LOW_ENERGY_AMP_TH_SEL */ -typedef enum { - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_30_MG = (0 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_35_MG = (1 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_40_MG = (2 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_45_MG = (3 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_50_MG = (4 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_55_MG = (5 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_60_MG = (6 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_65_MG = (7 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_70_MG = (8 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_75_MG = (9 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_80_MG = (10 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_85_MG = (11 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_90_MG = (12 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_95_MG = (13 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_100_MG = (14 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), - APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_105_MG = (15 << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS), -} APEX_CONFIG2_LOW_ENERGY_AMP_TH_t; - -/* DMP_POWER_SAVE_TIME_SEL */ -typedef enum { - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_0_S = 0x0, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_4_S = 0x1, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_8_S = 0x2, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_12_S = 0x3, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_16_S = 0x4, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_20_S = 0x5, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_24_S = 0x6, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_28_S = 0x7, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_32_S = 0x8, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_36_S = 0x9, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_40_S = 0xA, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_44_S = 0xB, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_48_S = 0xC, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_52_S = 0xD, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_56_S = 0xE, - APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_60_S = 0xF, -} APEX_CONFIG2_DMP_POWER_SAVE_TIME_t; - -/* - * APEX_CONFIG3_MREG1 - * Register Name: APEX_CONFIG3 -*/ - -/* PEDO_AMP_TH_SEL */ -typedef enum { - APEX_CONFIG3_PEDO_AMP_TH_30_MG = (0 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_34_MG = (1 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_38_MG = (2 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_42_MG = (3 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_46_MG = (4 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_50_MG = (5 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_54_MG = (6 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_58_MG = (7 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_62_MG = (8 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_66_MG = (9 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_70_MG = (10 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_74_MG = (11 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_78_MG = (12 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_82_MG = (13 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_86_MG = (14 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), - APEX_CONFIG3_PEDO_AMP_TH_90_MG = (15 << APEX_CONFIG3_PED_AMP_TH_SEL_POS), -} APEX_CONFIG3_PEDO_AMP_TH_t; - -/* - * APEX_CONFIG4_MREG1 - * Register Name: APEX_CONFIG4 -*/ - -/* PEDO_SB_TIMER_TH_SEL */ -typedef enum { - APEX_CONFIG4_PEDO_SB_TIMER_TH_50_SAMPLES = (0 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), - APEX_CONFIG4_PEDO_SB_TIMER_TH_75_SAMPLES = (1 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), - APEX_CONFIG4_PEDO_SB_TIMER_TH_100_SAMPLES = (2 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), - APEX_CONFIG4_PEDO_SB_TIMER_TH_125_SAMPLES = (3 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), - APEX_CONFIG4_PEDO_SB_TIMER_TH_150_SAMPLES = (4 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), - APEX_CONFIG4_PEDO_SB_TIMER_TH_175_SAMPLES = (5 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), - APEX_CONFIG4_PEDO_SB_TIMER_TH_200_SAMPLES = (6 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), - APEX_CONFIG4_PEDO_SB_TIMER_TH_225_SAMPLES = (7 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS), -} APEX_CONFIG4_PEDO_SB_TIMER_TH_t; - -/* PEDO_HI_ENRGY_TH_SEL */ -typedef enum { - APEX_CONFIG4_PEDO_HI_ENRGY_TH_88_MG = (0 << APEX_CONFIG4_PED_HI_EN_TH_SEL_POS), - APEX_CONFIG4_PEDO_HI_ENRGY_TH_104_MG = (1 << APEX_CONFIG4_PED_HI_EN_TH_SEL_POS), - APEX_CONFIG4_PEDO_HI_ENRGY_TH_133_MG = (2 << APEX_CONFIG4_PED_HI_EN_TH_SEL_POS), - APEX_CONFIG4_PEDO_HI_ENRGY_TH_155_MG = (3 << APEX_CONFIG4_PED_HI_EN_TH_SEL_POS), -} APEX_CONFIG4_PEDO_HI_ENRGY_TH_t; - -/* - * APEX_CONFIG5_MREG1 - * Register Name: APEX_CONFIG5 -*/ - -/* TILT_WAIT_TIME_SEL */ -typedef enum { - APEX_CONFIG5_TILT_WAIT_TIME_0_S = (0 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS), - APEX_CONFIG5_TILT_WAIT_TIME_2_S = (1 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS), - APEX_CONFIG5_TILT_WAIT_TIME_4_S = (2 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS), - APEX_CONFIG5_TILT_WAIT_TIME_6_S = (3 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS), -} APEX_CONFIG5_TILT_WAIT_TIME_t; - -/* LOWG_PEAK_TH_HYST_SEL */ -typedef enum { - APEX_CONFIG5_LOWG_PEAK_TH_HYST_31_MG = (0 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_LOWG_PEAK_TH_HYST_63_MG = (1 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_LOWG_PEAK_TH_HYST_94_MG = (2 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_LOWG_PEAK_TH_HYST_125_MG = (3 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_LOWG_PEAK_TH_HYST_156_MG = (4 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_LOWG_PEAK_TH_HYST_188_MG = (5 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_LOWG_PEAK_TH_HYST_219_MG = (6 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_LOWG_PEAK_TH_HYST_250_MG = (7 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS), -} APEX_CONFIG5_LOWG_PEAK_TH_HYST_t; - -/* HIGHG_PEAK_TH_HYST_SEL */ -typedef enum { - APEX_CONFIG5_HIGHG_PEAK_TH_HYST_31_MG = (0 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_HIGHG_PEAK_TH_HYST_63_MG = (1 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_HIGHG_PEAK_TH_HYST_94_MG = (2 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_HIGHG_PEAK_TH_HYST_125_MG = (3 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_HIGHG_PEAK_TH_HYST_156_MG = (4 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_HIGHG_PEAK_TH_HYST_188_MG = (5 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_HIGHG_PEAK_TH_HYST_219_MG = (6 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), - APEX_CONFIG5_HIGHG_PEAK_TH_HYST_250_MG = (7 << APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS), -} APEX_CONFIG5_HIGHG_PEAK_TH_HYST_t; - -/* - * APEX_CONFIG9_MREG1 - * Register Name: APEX_CONFIG9 -*/ - -/* FF_DEBOUNCE_DURATION_SEL */ -typedef enum { - APEX_CONFIG9_FF_DEBOUNCE_DURATION_0_MS = (0 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_1250_MS = (1 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_1375_MS = (2 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_1500_MS = (3 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_1625_MS = (4 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_1750_MS = (5 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_1875_MS = (6 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_2000_MS = (7 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_2125_MS = (8 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_2250_MS = (9 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_2375_MS = (10 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_2500_MS = (11 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_2625_MS = (12 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_2750_MS = (13 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_2875_MS = (14 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), - APEX_CONFIG9_FF_DEBOUNCE_DURATION_3000_MS = (15 << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS), -} APEX_CONFIG9_FF_DEBOUNCE_DURATION_t; - -/* SMD_SENSITIVITY_SEL */ -typedef enum { - APEX_CONFIG9_SMD_SENSITIVITY_0 = (0 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), - APEX_CONFIG9_SMD_SENSITIVITY_1 = (1 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), - APEX_CONFIG9_SMD_SENSITIVITY_2 = (2 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), - APEX_CONFIG9_SMD_SENSITIVITY_3 = (3 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), - APEX_CONFIG9_SMD_SENSITIVITY_4 = (4 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS), -} APEX_CONFIG9_SMD_SENSITIVITY_t; - -/* SMD_SENSITIVITY_MODE */ -typedef enum { - APEX_CONFIG9_SENSITIVITY_MODE_NORMAL = (0 << APEX_CONFIG9_SENSITIVITY_MODE_POS), - APEX_CONFIG9_SENSITIVITY_MODE_SLOW_WALK = (1 << APEX_CONFIG9_SENSITIVITY_MODE_POS), -} APEX_CONFIG9_SENSITIVITY_MODE_t; - -/* - * APEX_CONFIG10_MREG1 - * Register Name: APEX_CONFIG10 -*/ - -/* LOWG_PEAK_TH_SEL */ -typedef enum { - APEX_CONFIG10_LOWG_PEAK_TH_31_MG = (0x00 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_63_MG = (0x01 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_94_MG = (0x02 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_125_MG = (0x03 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_156_MG = (0x04 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_188_MG = (0x05 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_219_MG = (0x06 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_250_MG = (0x07 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_281_MG = (0x08 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_313_MG = (0x09 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_344_MG = (0x0A << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_375_MG = (0x0B << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_406_MG = (0x0C << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_438_MG = (0x0D << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_469_MG = (0x0E << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_500_MG = (0x0F << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_531_MG = (0x10 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_563_MG = (0x11 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_594_MG = (0x12 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_625_MG = (0x13 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_656_MG = (0x14 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_688_MG = (0x15 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_719_MG = (0x16 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_750_MG = (0x17 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_781_MG = (0x18 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_813_MG = (0x19 << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_844_MG = (0x1A << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_875_MG = (0x1B << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_906_MG = (0x1C << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_938_MG = (0x1D << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_969_MG = (0x1E << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), - APEX_CONFIG10_LOWG_PEAK_TH_1000_MG = (0x1F << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS), -} APEX_CONFIG10_LOWG_PEAK_TH_t; - -/* LOWG_TIME_TH_SEL */ -typedef enum { - APEX_CONFIG10_LOWG_TIME_TH_1_SAMPLE = (0x00 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), - APEX_CONFIG10_LOWG_TIME_TH_2_SAMPLES = (0x01 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), - APEX_CONFIG10_LOWG_TIME_TH_3_SAMPLES = (0x02 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), - APEX_CONFIG10_LOWG_TIME_TH_4_SAMPLES = (0x03 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), - APEX_CONFIG10_LOWG_TIME_TH_5_SAMPLES = (0x04 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), - APEX_CONFIG10_LOWG_TIME_TH_6_SAMPLES = (0x05 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), - APEX_CONFIG10_LOWG_TIME_TH_7_SAMPLES = (0x06 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), - APEX_CONFIG10_LOWG_TIME_TH_8_SAMPLES = (0x07 << APEX_CONFIG10_LOWG_TIME_TH_SEL_POS), -} APEX_CONFIG10_LOWG_TIME_TH_SAMPLES_t; - -/* - * APEX_CONFIG11_MREG1 - * Register Name: APEX_CONFIG11 -*/ - -/* HIGHG_PEAK_TH_SEL */ -typedef enum { - APEX_CONFIG11_HIGHG_PEAK_TH_250_MG = (0x00 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_500_MG = (0x01 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_750_MG = (0x02 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_1000MG = (0x03 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_1250_MG = (0x04 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_1500_MG = (0x05 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_1750_MG = (0x06 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_2000_MG = (0x07 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_2250_MG = (0x08 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_2500_MG = (0x09 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_2750_MG = (0x0A << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_3000_MG = (0x0B << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_3250_MG = (0x0C << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_3500_MG = (0x0D << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_3750_MG = (0x0E << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_4000_MG = (0x0F << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_4250_MG = (0x10 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_4500_MG = (0x11 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_4750_MG = (0x12 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_5000_MG = (0x13 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_5250_MG = (0x14 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_5500_MG = (0x15 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_5750_MG = (0x16 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_6000_MG = (0x17 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_6250_MG = (0x18 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_6500_MG = (0x19 << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_6750_MG = (0x1A << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_7000_MG = (0x1B << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_7250_MG = (0x1C << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_7500_MG = (0x1D << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_7750_MG = (0x1E << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), - APEX_CONFIG11_HIGHG_PEAK_TH_8000_MG = (0x1F << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS), -} APEX_CONFIG11_HIGHG_PEAK_TH_t; - -/* HIGHG_TIME_TH_SEL */ -typedef enum { - APEX_CONFIG11_HIGHG_TIME_TH_1_SAMPLE = (0x00 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), - APEX_CONFIG11_HIGHG_TIME_TH_2_SAMPLES = (0x01 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), - APEX_CONFIG11_HIGHG_TIME_TH_3_SAMPLES = (0x02 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), - APEX_CONFIG11_HIGHG_TIME_TH_4_SAMPLES = (0x03 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), - APEX_CONFIG11_HIGHG_TIME_TH_5_SAMPLES = (0x04 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), - APEX_CONFIG11_HIGHG_TIME_TH_6_SAMPLES = (0x05 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), - APEX_CONFIG11_HIGHG_TIME_TH_7_SAMPLES = (0x06 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), - APEX_CONFIG11_HIGHG_TIME_TH_8_SAMPLES = (0x07 << APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS), -} APEX_CONFIG11_HIGHG_TIME_TH_SAMPLES_t; - -/* - * FDR_CONFIG_MREG1 - * Register Name: FDR_CONFIG - */ - -/* FDR_SEL */ -typedef enum { - FDR_CONFIG_FDR_SEL_DIS = (0x0 << FDR_CONFIG_FDR_SEL_POS), - FDR_CONFIG_FDR_SEL_FACTOR_2 = (0x8 << FDR_CONFIG_FDR_SEL_POS), - FDR_CONFIG_FDR_SEL_FACTOR_4 = (0x9 << FDR_CONFIG_FDR_SEL_POS), - FDR_CONFIG_FDR_SEL_FACTOR_8 = (0xA << FDR_CONFIG_FDR_SEL_POS), - FDR_CONFIG_FDR_SEL_FACTOR_16 = (0xB << FDR_CONFIG_FDR_SEL_POS), - FDR_CONFIG_FDR_SEL_FACTOR_32 = (0xC << FDR_CONFIG_FDR_SEL_POS), - FDR_CONFIG_FDR_SEL_FACTOR_64 = (0xD << FDR_CONFIG_FDR_SEL_POS), - FDR_CONFIG_FDR_SEL_FACTOR_128 = (0xE << FDR_CONFIG_FDR_SEL_POS), - FDR_CONFIG_FDR_SEL_FACTOR_256 = (0xF << FDR_CONFIG_FDR_SEL_POS), -} FDR_CONFIG_FDR_SEL_t; - -/* - * APEX_CONFIG12_MREG1 - * Register Name: APEX_CONFIG12 -*/ -/* FF_MAX_DURATION_SEL */ -typedef enum { - APEX_CONFIG12_FF_MAX_DURATION_102_CM = (0 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_120_CM = (1 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_139_CM = (2 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_159_CM = (3 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_181_CM = (4 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_204_CM = (5 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_228_CM = (6 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_254_CM = (7 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_281_CM = (8 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_310_CM = (9 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_339_CM = (10 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_371_CM = (11 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_403_CM = (12 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_438_CM = (13 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_473_CM = (14 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), - APEX_CONFIG12_FF_MAX_DURATION_510_CM = (15 << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS), -} APEX_CONFIG12_FF_MAX_DURATION_t; - -/* FF_MIN_DURATION_SEL */ -typedef enum { - APEX_CONFIG12_FF_MIN_DURATION_10_CM = (0 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_12_CM = (1 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_13_CM = (2 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_16_CM = (3 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_18_CM = (4 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_20_CM = (5 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_23_CM = (6 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_25_CM = (7 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_28_CM = (8 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_31_CM = (9 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_34_CM = (10 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_38_CM = (11 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_41_CM = (12 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_45_CM = (13 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_48_CM = (14 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), - APEX_CONFIG12_FF_MIN_DURATION_52_CM = (15 << APEX_CONFIG12_FF_MIN_DURATION_SEL_POS), -} APEX_CONFIG12_FF_MIN_DURATION_t; - -/* --------------------------------------------------------------------------- - * register bank MREG2 - * ---------------------------------------------------------------------------- */ - -/* - * OTP_CTRL7_MREG2 - * Register Name: OTP_CTRL7 -*/ - -/* OTP_CTRL7 */ -typedef enum { - OTP_CTRL7_OTP_RELOAD_EN = (1 << OTP_CTRL7_OTP_RELOAD_POS), - OTP_CTRL7_OTP_RELOAD_DIS = (0 << OTP_CTRL7_OTP_RELOAD_POS), -} OTP_CTRL7_OTP_RELOAD_t; - -/* OTP_PWR_DOWN */ -typedef enum { - OTP_CTRL7_PWR_DOWN_EN = (1 << OTP_CTRL7_OTP_PWR_DOWN_POS), - OTP_CTRL7_PWR_DOWN_DIS = (0 << OTP_CTRL7_OTP_PWR_DOWN_POS), -} OTP_CTRL7_PWR_DOWN_t; - -#ifdef __cplusplus -} -#endif - -#endif /* #ifndef _INV_IMU_DEFS_H_ */ diff --git a/lib/ICM42670P/src/imu/inv_imu_driver.c b/lib/ICM42670P/src/imu/inv_imu_driver.c deleted file mode 100644 index 6f64e47..0000000 --- a/lib/ICM42670P/src/imu/inv_imu_driver.c +++ /dev/null @@ -1,1578 +0,0 @@ -/* - * ________________________________________________________________________________________________________ - * Copyright (c) 2017 InvenSense Inc. All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * ________________________________________________________________________________________________________ - */ - -#include "imu/inv_imu_defs.h" -#include "imu/inv_imu_extfunc.h" -#include "imu/inv_imu_driver.h" -#include "imu/inv_imu_version.h" - -static int select_rcosc(struct inv_imu_device *s); -static int select_wuosc(struct inv_imu_device *s); -static int configure_serial_interface(struct inv_imu_device *s); -static int init_hardware_from_ui(struct inv_imu_device *s); -static int resume_dmp(struct inv_imu_device *s); - -int inv_imu_init(struct inv_imu_device *s, struct inv_imu_serif *serif, - void (*sensor_event_cb)(inv_imu_sensor_event_t *event)) -{ - int status = 0; - - memset(s, 0, sizeof(*s)); - - s->transport.serif = *serif; - - /* Supply ramp time max is 3 ms */ - inv_imu_sleep_us(3000); - - /* Configure serial interface */ - status |= configure_serial_interface(s); - if (status) - return status; - - /* Register sensor event callback */ - s->sensor_event_cb = sensor_event_cb; - - /* Make sure `need_mclk_cnt` is cleared */ - s->transport.need_mclk_cnt = 0; - - /* Reset device */ - status |= inv_imu_device_reset(s); - - /* Init transport layer */ - status |= inv_imu_init_transport(s); - - /* Read and set endianness for further processing */ - status |= inv_imu_get_endianness(s); - - /* Initialize hardware */ - status |= init_hardware_from_ui(s); - - /* Set default value for sensor start/stop time */ - s->gyro_start_time_us = UINT32_MAX; - s->accel_start_time_us = UINT32_MAX; - s->gyro_power_off_tmst = UINT32_MAX; - - return status; -} - -int inv_imu_device_reset(struct inv_imu_device *s) -{ - int status = INV_ERROR_SUCCESS; - uint8_t device_config_backup; - uint8_t intf_config1_backup; - uint8_t data; - - /* Ensure BLK_SEL_R and BLK_SEL_W are set to 0 */ - data = 0; - status |= inv_imu_write_reg(s, BLK_SEL_R, 1, &data); - status |= inv_imu_write_reg(s, BLK_SEL_W, 1, &data); - - /* Backup registers to configure serial interface */ - status |= inv_imu_read_reg(s, DEVICE_CONFIG, 1, &device_config_backup); - status |= inv_imu_read_reg(s, INTF_CONFIG1, 1, &intf_config1_backup); - - /* Trigger soft reset */ - data = (uint8_t)SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_EN; - status |= inv_imu_write_reg(s, SIGNAL_PATH_RESET, 1, &data); - - /* Wait 1ms for soft reset to be effective */ - inv_imu_sleep_us(1000); - - /* Restore registers to configure serial interface */ - status |= inv_imu_write_reg(s, DEVICE_CONFIG, 1, &device_config_backup); - status |= inv_imu_write_reg(s, INTF_CONFIG1, 1, &intf_config1_backup); - - /* Clear the reset done interrupt */ - status |= inv_imu_read_reg(s, INT_STATUS, 1, &data); - if (data != INT_STATUS_RESET_DONE_INT_MASK) - status |= INV_ERROR_UNEXPECTED; - - return status; -} - -int inv_imu_get_who_am_i(struct inv_imu_device *s, uint8_t *who_am_i) -{ - return inv_imu_read_reg(s, WHO_AM_I, 1, who_am_i); -} - -static int select_rcosc(struct inv_imu_device *s) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); - data &= ~PWR_MGMT0_ACCEL_LP_CLK_SEL_MASK; - data |= PWR_MGMT0_ACCEL_LP_CLK_RCOSC; - status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data); - - return status; -} - -static int select_wuosc(struct inv_imu_device *s) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); - data &= ~PWR_MGMT0_ACCEL_LP_CLK_SEL_MASK; - data |= PWR_MGMT0_ACCEL_LP_CLK_WUOSC; - status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data); - - return status; -} - -int inv_imu_enable_accel_low_power_mode(struct inv_imu_device *s) -{ - int status = 0; - PWR_MGMT0_ACCEL_MODE_t accel_mode; - PWR_MGMT0_GYRO_MODE_t gyro_mode; - ACCEL_CONFIG0_ODR_t acc_odr_bitfield; - uint32_t accel_odr_us = 0; - uint8_t pwr_mgmt0_reg; - uint8_t accel_config0_reg; - uint8_t value; - - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - accel_mode = (PWR_MGMT0_ACCEL_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_ACCEL_MODE_MASK); - gyro_mode = (PWR_MGMT0_GYRO_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_GYRO_MODE_MASK); - - /* Check if the accelerometer is the only one enabled */ - if ((accel_mode != PWR_MGMT0_ACCEL_MODE_LP) && - ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) || (gyro_mode == PWR_MGMT0_GYRO_MODE_STANDBY))) { - /* Get accelerometer's ODR for next required wait */ - status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); - acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); - accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); - /* Select the RC OSC as clock source for the accelerometer */ - status |= select_rcosc(s); - } - - /* Enable/Switch the accelerometer in/to low power mode */ - /* Read a new time because select_rcosc() modified it */ - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - pwr_mgmt0_reg &= ~PWR_MGMT0_ACCEL_MODE_MASK; - pwr_mgmt0_reg |= PWR_MGMT0_ACCEL_MODE_LP; - status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - inv_imu_sleep_us(200); - - if ((accel_mode != PWR_MGMT0_ACCEL_MODE_LP) && - ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) || (gyro_mode == PWR_MGMT0_GYRO_MODE_STANDBY))) { - /* Wait one accelerometer ODR before switching to the WU OSC */ - if (accel_odr_us > - 200) /* if ODR is smaller than 200 us, we already waited for one ODR above. */ - inv_imu_sleep_us(accel_odr_us - 200); - status |= select_wuosc(s); - } - - if (accel_mode == PWR_MGMT0_ACCEL_MODE_OFF) { - /* First data are wrong after accel enable using IIR filter - There is no signal that says accel start-up has completed and data are stable using FIR filter - So keep track of the time at start-up to discard the invalid data, about 20ms after enable - */ - if (s->fifo_is_used) - s->accel_start_time_us = inv_imu_get_time_us(); - } - - /* Enable the automatic RCOSC power on so that FIFO is entirely powered on */ - status |= inv_imu_read_reg(s, FIFO_CONFIG6_MREG1, 1, &value); - value &= ~FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK; - status |= inv_imu_write_reg(s, FIFO_CONFIG6_MREG1, 1, &value); - - return status; -} - -int inv_imu_enable_accel_low_noise_mode(struct inv_imu_device *s) -{ - int status = 0; - PWR_MGMT0_ACCEL_MODE_t accel_mode; - PWR_MGMT0_GYRO_MODE_t gyro_mode; - ACCEL_CONFIG0_ODR_t acc_odr_bitfield; - uint32_t accel_odr_us; - uint8_t pwr_mgmt0_reg; - uint8_t accel_config0_reg; - - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - accel_mode = (PWR_MGMT0_ACCEL_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_ACCEL_MODE_MASK); - gyro_mode = (PWR_MGMT0_GYRO_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_GYRO_MODE_MASK); - /* Check if the accelerometer is the only one enabled */ - if ((accel_mode == PWR_MGMT0_ACCEL_MODE_LP) && - ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) || (gyro_mode == PWR_MGMT0_GYRO_MODE_STANDBY))) { - /* Get accelerometer's ODR for next required wait */ - status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); - acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); - accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); - /* Select the RC OSC as clock source for the accelerometer */ - status |= select_rcosc(s); - /* Wait one accel ODR before switching to low noise mode */ - inv_imu_sleep_us(accel_odr_us); - } - - /* Enable/Switch the accelerometer in/to low noise mode */ - /* Read a new time because select_rcosc() modified it */ - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - pwr_mgmt0_reg &= ~PWR_MGMT0_ACCEL_MODE_MASK; - pwr_mgmt0_reg |= PWR_MGMT0_ACCEL_MODE_LN; - status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - inv_imu_sleep_us(200); - - if (accel_mode == PWR_MGMT0_ACCEL_MODE_OFF) { - /* First data are wrong after accel enable using IIR filter - There is no signal that says accel start-up has completed and data are stable using FIR filter - So keep track of the time at start-up to discard the invalid data, about 20ms after enable - */ - if (s->fifo_is_used) - s->accel_start_time_us = inv_imu_get_time_us(); - } - - return status; -} - -int inv_imu_disable_accel(struct inv_imu_device *s) -{ - int status = 0; - int stop_fifo_usage = 0; - uint32_t accel_odr_us; - PWR_MGMT0_GYRO_MODE_t gyro_mode; - ACCEL_CONFIG0_ODR_t acc_odr_bitfield; - uint8_t pwr_mgmt0_reg; - uint8_t accel_config0_reg, fifo_cfg_6_reg; - - /* Get accelerometer's ODR for next required wait */ - status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); - acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); - accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); - - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - gyro_mode = (PWR_MGMT0_GYRO_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_GYRO_MODE_MASK); - if ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) && s->fifo_is_used) { - /* - * Accel is off and gyro is about to be turned off. - * Flush FIFO so that there is no old data at next enable time - */ - stop_fifo_usage = 1; - } - - /* Check if accel is the last sensor enabled and bit rcosc dis is not set */ - status |= inv_imu_read_reg(s, FIFO_CONFIG6_MREG1, 1, &fifo_cfg_6_reg); - if ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) && - ((fifo_cfg_6_reg & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) == 0)) { - /* - * Disable the automatic RCOSC power on to avoid extra power consumption - * in sleep mode (all sensors and clocks off) - */ - fifo_cfg_6_reg |= ((1 & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) - << FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_POS); - status |= inv_imu_write_reg(s, FIFO_CONFIG6_MREG1, 1, &fifo_cfg_6_reg); - inv_imu_sleep_us(accel_odr_us); - } - - pwr_mgmt0_reg &= ~PWR_MGMT0_ACCEL_MODE_MASK; - pwr_mgmt0_reg |= PWR_MGMT0_ACCEL_MODE_OFF; - status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - - if (stop_fifo_usage && s->fifo_is_used) { - /* Reset FIFO explicitly so there is no data left in FIFO once all sensors are off */ - status |= inv_imu_reset_fifo(s); - } - - return status; -} - -int inv_imu_enable_gyro_low_noise_mode(struct inv_imu_device *s) -{ - int status = 0; - PWR_MGMT0_ACCEL_MODE_t accel_mode; - PWR_MGMT0_GYRO_MODE_t gyro_mode; - ACCEL_CONFIG0_ODR_t acc_odr_bitfield; - uint32_t accel_odr_us; - uint8_t pwr_mgmt0_reg; - uint8_t accel_config0_reg; - uint64_t current_time; - - /* - * Powering the gyroscope on immediately after powering it off can result in device failure. - * The gyroscope proof mass can continue vibrating after it has been powered off, - * and powering it back on immediately can result in unpredictable proof mass movement. - * After powering the gyroscope off, a period of > 20 ms should be allowed - * to elapse before it is powered back on. - */ - if (s->gyro_power_off_tmst != UINT32_MAX) { - current_time = inv_imu_get_time_us(); - /* Handle rollover */ - if (current_time <= s->gyro_power_off_tmst) - current_time += UINT32_MAX; - /* If 20 ms are not elapsed since power-off error is returned */ - if ((current_time - s->gyro_power_off_tmst) <= GYR_POWER_OFF_DUR_US) - return INV_ERROR_HW; - } - - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - accel_mode = (PWR_MGMT0_ACCEL_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_ACCEL_MODE_MASK); - gyro_mode = (PWR_MGMT0_GYRO_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_GYRO_MODE_MASK); - /* Check if the accelerometer is the only one enabled */ - if ((accel_mode == PWR_MGMT0_ACCEL_MODE_LP) && - ((gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) || (gyro_mode == PWR_MGMT0_GYRO_MODE_STANDBY))) { - /* Get accelerometer's ODR for next required wait */ - status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); - acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); - accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); - /* Select the RC OSC as clock source for the accelerometer */ - status |= select_rcosc(s); - /* Wait one accel ODR before enabling the gyroscope */ - inv_imu_sleep_us(accel_odr_us); - } - - /* Enable/Switch the gyroscope in/to low noise mode */ - /* Read a new time because select_rcosc() modified it */ - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - pwr_mgmt0_reg &= ~PWR_MGMT0_GYRO_MODE_MASK; - pwr_mgmt0_reg |= (uint8_t)PWR_MGMT0_GYRO_MODE_LN; - status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - inv_imu_sleep_us(200); - - if (gyro_mode == PWR_MGMT0_GYRO_MODE_OFF) { - /* First data are wrong after gyro enable using IIR filter - There is no signal that says Gyro start-up has completed and data are stable using FIR filter - and the Gyro max start-up time is 40ms - So keep track of the time at start-up to discard the invalid data, about 60ms after enable - */ - if (s->fifo_is_used) - s->gyro_start_time_us = inv_imu_get_time_us(); - } - - return status; -} - -int inv_imu_disable_gyro(struct inv_imu_device *s) -{ - int status = 0; - int stop_fifo_usage = 0; - ACCEL_CONFIG0_ODR_t acc_odr_bitfield; - uint32_t accel_odr_us; - PWR_MGMT0_ACCEL_MODE_t accel_mode; - uint8_t pwr_mgmt0_reg; - uint8_t accel_config0_reg, fifo_cfg_6_reg; - - /* Get accelerometer's ODR for next required wait */ - status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); - acc_odr_bitfield = (ACCEL_CONFIG0_ODR_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_ODR_MASK); - accel_odr_us = inv_imu_convert_odr_bitfield_to_us(acc_odr_bitfield); - - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - accel_mode = (PWR_MGMT0_ACCEL_MODE_t)(pwr_mgmt0_reg & PWR_MGMT0_ACCEL_MODE_MASK); - - if ((accel_mode == PWR_MGMT0_ACCEL_MODE_OFF) && s->fifo_is_used) { - /* - * Accel is off and gyro is about to be turned off. - * Flush FIFO so that there is no old data at next enable time - */ - stop_fifo_usage = 1; - } - - /* Check if the accelerometer is enabled in low power mode */ - if (accel_mode == PWR_MGMT0_ACCEL_MODE_LP) { - /* Select the RC OSC as clock source for the accelerometer */ - status |= select_rcosc(s); - } - - /* Check if gyro is the last sensor enabled and bit rcosc dis is not set */ - status |= inv_imu_read_reg(s, FIFO_CONFIG6_MREG1, 1, &fifo_cfg_6_reg); - if ((accel_mode == PWR_MGMT0_ACCEL_MODE_OFF) && - ((fifo_cfg_6_reg & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) == 0)) { - GYRO_CONFIG0_ODR_t gyro_odr_bitfield; - uint32_t gyro_odr_us; - uint8_t gyro_config0_reg; - - /* Read gyro odr to apply it to the sleep */ - status |= inv_imu_read_reg(s, GYRO_CONFIG0, 1, &gyro_config0_reg); - gyro_odr_bitfield = (GYRO_CONFIG0_ODR_t)(gyro_config0_reg & GYRO_CONFIG0_GYRO_ODR_MASK); - gyro_odr_us = inv_imu_convert_odr_bitfield_to_us(gyro_odr_bitfield); - - /* - * Disable the automatic RCOSC power on to avoid extra power consumption - * in sleep mode (all sensors and clocks off) - */ - fifo_cfg_6_reg |= ((1 & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) - << FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_POS); - status |= inv_imu_write_reg(s, FIFO_CONFIG6_MREG1, 1, &fifo_cfg_6_reg); - inv_imu_sleep_us(gyro_odr_us); - } - - /* Read a new time because select_rcosc() modified it */ - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - pwr_mgmt0_reg &= ~PWR_MGMT0_GYRO_MODE_MASK; - pwr_mgmt0_reg |= PWR_MGMT0_GYRO_MODE_OFF; - status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &pwr_mgmt0_reg); - /* keep track of gyro power-off time */ - s->gyro_power_off_tmst = inv_imu_get_time_us(); - - if (accel_mode == PWR_MGMT0_ACCEL_MODE_LP) { - /* Wait based on accelerometer ODR */ - inv_imu_sleep_us(2 * accel_odr_us); - /* Select the WU OSC as clock source for the accelerometer */ - status |= select_wuosc(s); - } - - if (stop_fifo_usage && s->fifo_is_used) { - /* Reset FIFO explicitly so there is no data left in FIFO once all sensors are off */ - status |= inv_imu_reset_fifo(s); - } - - return status; -} - -int inv_imu_enable_fsync(struct inv_imu_device *s) -{ - int status = 0; - uint8_t value; - - status |= inv_imu_switch_on_mclk(s); - - //Enable Fsync - status |= inv_imu_read_reg(s, FSYNC_CONFIG_MREG1, 1, &value); - value &= ~FSYNC_CONFIG_FSYNC_UI_SEL_MASK; - value |= (uint8_t)FSYNC_CONFIG_UI_SEL_TEMP; - status |= inv_imu_write_reg(s, FSYNC_CONFIG_MREG1, 1, &value); - - status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &value); - value &= ~TMST_CONFIG1_TMST_FSYNC_EN_MASK; - value |= TMST_CONFIG1_TMST_FSYNC_EN; - status |= inv_imu_write_reg(s, TMST_CONFIG1_MREG1, 1, &value); - - status |= inv_imu_switch_off_mclk(s); - - return status; -} - -int inv_imu_disable_fsync(struct inv_imu_device *s) -{ - int status = 0; - uint8_t value; - - status |= inv_imu_switch_on_mclk(s); - - // Disable Fsync - status |= inv_imu_read_reg(s, FSYNC_CONFIG_MREG1, 1, &value); - value &= ~FSYNC_CONFIG_FSYNC_UI_SEL_MASK; - value |= (uint8_t)FSYNC_CONFIG_UI_SEL_NO; - status |= inv_imu_write_reg(s, FSYNC_CONFIG_MREG1, 1, &value); - - status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &value); - value &= ~TMST_CONFIG1_TMST_FSYNC_EN_MASK; - value |= TMST_CONFIG1_TMST_FSYNC_DIS; - status |= inv_imu_write_reg(s, TMST_CONFIG1_MREG1, 1, &value); - - status |= inv_imu_switch_off_mclk(s); - - return status; -} - -int inv_imu_configure_wom(struct inv_imu_device *s, const uint8_t wom_x_th, const uint8_t wom_y_th, - const uint8_t wom_z_th, WOM_CONFIG_WOM_INT_MODE_t wom_int, - WOM_CONFIG_WOM_INT_DUR_t wom_dur) -{ - int status = 0; - uint8_t data[3]; - uint8_t value; - - data[0] = wom_x_th; // Set X threshold - data[1] = wom_y_th; // Set Y threshold - data[2] = wom_z_th; // Set Z threshold - status |= inv_imu_write_reg(s, ACCEL_WOM_X_THR_MREG1, sizeof(data), &data[0]); - - // Compare current sample with the previous sample and WOM from the 3 axis are ORed or ANDed to produce WOM signal. - status |= inv_imu_read_reg(s, WOM_CONFIG, 1, &value); - value &= ~WOM_CONFIG_WOM_INT_MODE_MASK; - value |= (uint8_t)WOM_CONFIG_WOM_MODE_CMP_PREV | (uint8_t)wom_int; - - // Configure the number of overthreshold event to wait before producing the WOM signal. - value &= ~WOM_CONFIG_WOM_INT_DUR_MASK; - value |= (uint8_t)wom_dur; - status |= inv_imu_write_reg(s, WOM_CONFIG, 1, &value); - - return status; -} - -int inv_imu_enable_wom(struct inv_imu_device *s) -{ - int status = 0; - uint8_t value; - inv_imu_interrupt_parameter_t config_int = { (inv_imu_interrupt_value)0 }; - - /* Disable fifo threshold int1 */ - status |= inv_imu_get_config_int1(s, &config_int); - config_int.INV_FIFO_THS = INV_IMU_DISABLE; - status |= inv_imu_set_config_int1(s, &config_int); - - /* Enable WOM */ - status |= inv_imu_read_reg(s, WOM_CONFIG, 1, &value); - value &= ~WOM_CONFIG_WOM_EN_MASK; - value |= (uint8_t)WOM_CONFIG_WOM_EN_ENABLE; - status |= inv_imu_write_reg(s, WOM_CONFIG, 1, &value); - - return status; -} - -int inv_imu_disable_wom(struct inv_imu_device *s) -{ - int status = 0; - uint8_t value; - inv_imu_interrupt_parameter_t config_int = { (inv_imu_interrupt_value)0 }; - - /* Disable WOM */ - status |= inv_imu_read_reg(s, WOM_CONFIG, 1, &value); - value &= ~WOM_CONFIG_WOM_EN_MASK; - value |= WOM_CONFIG_WOM_EN_DISABLE; - status |= inv_imu_write_reg(s, WOM_CONFIG, 1, &value); - - /* Enable fifo threshold int1 */ - status |= inv_imu_get_config_int1(s, &config_int); - config_int.INV_FIFO_THS = INV_IMU_ENABLE; - status |= inv_imu_set_config_int1(s, &config_int); - - return status; -} - -int inv_imu_get_config_int1(struct inv_imu_device *s, inv_imu_interrupt_parameter_t *it) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, INT_SOURCE0, 1, &data); - it->INV_UI_FSYNC = (inv_imu_interrupt_value)((data & INT_SOURCE0_FSYNC_INT1_EN_MASK) >> - INT_SOURCE0_FSYNC_INT1_EN_POS); - it->INV_UI_DRDY = (inv_imu_interrupt_value)((data & INT_SOURCE0_DRDY_INT1_EN_MASK) >> - INT_SOURCE0_DRDY_INT1_EN_POS); - it->INV_FIFO_THS = (inv_imu_interrupt_value)((data & INT_SOURCE0_FIFO_THS_INT1_EN_MASK) >> - INT_SOURCE0_FIFO_THS_INT1_EN_POS); - it->INV_FIFO_FULL = (inv_imu_interrupt_value)((data & INT_SOURCE0_FIFO_FULL_INT1_EN_MASK) >> - INT_SOURCE0_FIFO_FULL_INT1_EN_POS); - - status |= inv_imu_read_reg(s, INT_SOURCE1, 1, &data); - it->INV_SMD = (inv_imu_interrupt_value)((data & INT_SOURCE1_SMD_INT1_EN_MASK) >> - INT_SOURCE1_SMD_INT1_EN_POS); - it->INV_WOM_X = (inv_imu_interrupt_value)((data & INT_SOURCE1_WOM_X_INT1_EN_MASK) >> - INT_SOURCE1_WOM_X_INT1_EN_POS); - it->INV_WOM_Y = (inv_imu_interrupt_value)((data & INT_SOURCE1_WOM_Y_INT1_EN_MASK) >> - INT_SOURCE1_WOM_Y_INT1_EN_POS); - it->INV_WOM_Z = (inv_imu_interrupt_value)((data & INT_SOURCE1_WOM_Z_INT1_EN_MASK) >> - INT_SOURCE1_WOM_Z_INT1_EN_POS); - - status |= inv_imu_read_reg(s, INT_SOURCE6_MREG1, 1, &data); - it->INV_FF = (inv_imu_interrupt_value)((data & INT_SOURCE6_FF_INT1_EN_MASK) >> - INT_SOURCE6_FF_INT1_EN_POS); - it->INV_LOWG = (inv_imu_interrupt_value)((data & INT_SOURCE6_LOWG_INT1_EN_MASK) >> - INT_SOURCE6_LOWG_INT1_EN_POS); - it->INV_STEP_DET = (inv_imu_interrupt_value)((data & INT_SOURCE6_STEP_DET_INT1_EN_MASK) >> - INT_SOURCE6_STEP_DET_INT1_EN_POS); - it->INV_STEP_CNT_OVFL = (inv_imu_interrupt_value)( - (data & INT_SOURCE6_STEP_CNT_OFL_INT1_EN_MASK) >> INT_SOURCE6_STEP_CNT_OFL_INT1_EN_POS); - it->INV_TILT_DET = (inv_imu_interrupt_value)((data & INT_SOURCE6_TILT_DET_INT1_EN_MASK) >> - INT_SOURCE6_TILT_DET_INT1_EN_POS); - - return status; -} - -int inv_imu_get_config_int2(struct inv_imu_device *s, inv_imu_interrupt_parameter_t *it) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, INT_SOURCE3, 1, &data); - it->INV_UI_FSYNC = (inv_imu_interrupt_value)((data & INT_SOURCE3_FSYNC_INT2_EN_MASK) >> - INT_SOURCE3_FSYNC_INT2_EN_POS); - it->INV_UI_DRDY = (inv_imu_interrupt_value)((data & INT_SOURCE3_DRDY_INT2_EN_MASK) >> - INT_SOURCE3_DRDY_INT2_EN_POS); - it->INV_FIFO_THS = (inv_imu_interrupt_value)((data & INT_SOURCE3_FIFO_THS_INT2_EN_MASK) >> - INT_SOURCE3_FIFO_THS_INT2_EN_POS); - it->INV_FIFO_FULL = (inv_imu_interrupt_value)((data & INT_SOURCE3_FIFO_FULL_INT2_EN_MASK) >> - INT_SOURCE3_FIFO_FULL_INT2_EN_POS); - - status |= inv_imu_read_reg(s, INT_SOURCE4, 1, &data); - it->INV_SMD = (inv_imu_interrupt_value)((data & INT_SOURCE4_SMD_INT2_EN_MASK) >> - INT_SOURCE4_SMD_INT2_EN_POS); - it->INV_WOM_X = (inv_imu_interrupt_value)((data & INT_SOURCE4_WOM_X_INT2_EN_MASK) >> - INT_SOURCE4_WOM_X_INT2_EN_POS); - it->INV_WOM_Y = (inv_imu_interrupt_value)((data & INT_SOURCE4_WOM_Y_INT2_EN_MASK) >> - INT_SOURCE4_WOM_Y_INT2_EN_POS); - it->INV_WOM_Z = (inv_imu_interrupt_value)((data & INT_SOURCE4_WOM_Z_INT2_EN_MASK) >> - INT_SOURCE4_WOM_Z_INT2_EN_POS); - - status |= inv_imu_read_reg(s, INT_SOURCE7_MREG1, 1, &data); - it->INV_FF = (inv_imu_interrupt_value)((data & INT_SOURCE7_FF_INT2_EN_MASK) >> - INT_SOURCE7_FF_INT2_EN_POS); - it->INV_LOWG = (inv_imu_interrupt_value)((data & INT_SOURCE7_LOWG_INT2_EN_MASK) >> - INT_SOURCE7_LOWG_INT2_EN_POS); - it->INV_STEP_DET = (inv_imu_interrupt_value)((data & INT_SOURCE7_STEP_DET_INT2_EN_MASK) >> - INT_SOURCE7_STEP_DET_INT2_EN_POS); - it->INV_STEP_CNT_OVFL = (inv_imu_interrupt_value)( - (data & INT_SOURCE7_STEP_CNT_OFL_INT2_EN_MASK) >> INT_SOURCE7_STEP_CNT_OFL_INT2_EN_POS); - it->INV_TILT_DET = (inv_imu_interrupt_value)((data & INT_SOURCE7_TILT_DET_INT2_EN_MASK) >> - INT_SOURCE7_TILT_DET_INT2_EN_POS); - - return status; -} - -int inv_imu_set_config_int1(struct inv_imu_device *s, inv_imu_interrupt_parameter_t *it) -{ - int status = 0; - uint8_t data[2]; - - status |= inv_imu_read_reg(s, INT_SOURCE0, 2, &data[0]); - - data[0] &= ~(INT_SOURCE0_FSYNC_INT1_EN_MASK | INT_SOURCE0_DRDY_INT1_EN_MASK | - INT_SOURCE0_FIFO_THS_INT1_EN_MASK | INT_SOURCE0_FIFO_FULL_INT1_EN_MASK); - data[0] |= ((it->INV_UI_FSYNC != 0) << INT_SOURCE0_FSYNC_INT1_EN_POS); - data[0] |= ((it->INV_UI_DRDY != 0) << INT_SOURCE0_DRDY_INT1_EN_POS); - data[0] |= ((it->INV_FIFO_THS != 0) << INT_SOURCE0_FIFO_THS_INT1_EN_POS); - data[0] |= ((it->INV_FIFO_FULL != 0) << INT_SOURCE0_FIFO_FULL_INT1_EN_POS); - - data[1] &= ~(INT_SOURCE1_SMD_INT1_EN_MASK | INT_SOURCE1_WOM_X_INT1_EN_MASK | - INT_SOURCE1_WOM_Y_INT1_EN_MASK | INT_SOURCE1_WOM_Z_INT1_EN_MASK); - data[1] |= ((it->INV_SMD != 0) << INT_SOURCE1_SMD_INT1_EN_POS); - data[1] |= ((it->INV_WOM_X != 0) << INT_SOURCE1_WOM_X_INT1_EN_POS); - data[1] |= ((it->INV_WOM_Y != 0) << INT_SOURCE1_WOM_Y_INT1_EN_POS); - data[1] |= ((it->INV_WOM_Z != 0) << INT_SOURCE1_WOM_Z_INT1_EN_POS); - - status |= inv_imu_write_reg(s, INT_SOURCE0, 2, &data[0]); - - status |= inv_imu_read_reg(s, INT_SOURCE6_MREG1, 1, &data[0]); - - data[0] &= ~(INT_SOURCE6_FF_INT1_EN_MASK | INT_SOURCE6_LOWG_INT1_EN_MASK | - INT_SOURCE6_STEP_DET_INT1_EN_MASK | INT_SOURCE6_STEP_CNT_OFL_INT1_EN_MASK | - INT_SOURCE6_TILT_DET_INT1_EN_MASK); - data[0] |= ((it->INV_FF != 0) << INT_SOURCE6_FF_INT1_EN_POS); - data[0] |= ((it->INV_LOWG != 0) << INT_SOURCE6_LOWG_INT1_EN_POS); - data[0] |= ((it->INV_STEP_DET != 0) << INT_SOURCE6_STEP_DET_INT1_EN_POS); - data[0] |= ((it->INV_STEP_CNT_OVFL != 0) << INT_SOURCE6_STEP_CNT_OFL_INT1_EN_POS); - data[0] |= ((it->INV_TILT_DET != 0) << INT_SOURCE6_TILT_DET_INT1_EN_POS); - status |= inv_imu_write_reg(s, INT_SOURCE6_MREG1, 1, &data[0]); - - return status; -} - -int inv_imu_set_config_int2(struct inv_imu_device *s, inv_imu_interrupt_parameter_t *it) -{ - int status = 0; - uint8_t data[2]; - - status |= inv_imu_read_reg(s, INT_SOURCE3, 2, &data[0]); - - data[0] &= ~(INT_SOURCE3_FSYNC_INT2_EN_MASK | INT_SOURCE3_DRDY_INT2_EN_MASK | - INT_SOURCE3_FIFO_THS_INT2_EN_MASK | INT_SOURCE3_FIFO_FULL_INT2_EN_MASK); - data[0] |= ((it->INV_UI_FSYNC != 0) << INT_SOURCE3_FSYNC_INT2_EN_POS); - data[0] |= ((it->INV_UI_DRDY != 0) << INT_SOURCE3_DRDY_INT2_EN_POS); - data[0] |= ((it->INV_FIFO_THS != 0) << INT_SOURCE3_FIFO_THS_INT2_EN_POS); - data[0] |= ((it->INV_FIFO_FULL != 0) << INT_SOURCE3_FIFO_FULL_INT2_EN_POS); - - data[1] &= ~(INT_SOURCE4_SMD_INT2_EN_MASK | INT_SOURCE4_WOM_X_INT2_EN_MASK | - INT_SOURCE4_WOM_Y_INT2_EN_MASK | INT_SOURCE4_WOM_Z_INT2_EN_MASK); - data[1] |= ((it->INV_SMD != 0) << INT_SOURCE4_SMD_INT2_EN_POS); - data[1] |= ((it->INV_WOM_X != 0) << INT_SOURCE4_WOM_X_INT2_EN_POS); - data[1] |= ((it->INV_WOM_Y != 0) << INT_SOURCE4_WOM_Y_INT2_EN_POS); - data[1] |= ((it->INV_WOM_Z != 0) << INT_SOURCE4_WOM_Z_INT2_EN_POS); - - status |= inv_imu_write_reg(s, INT_SOURCE3, 2, &data[0]); - - status |= inv_imu_read_reg(s, INT_SOURCE7_MREG1, 1, &data[0]); - - data[0] &= ~(INT_SOURCE7_FF_INT2_EN_MASK | INT_SOURCE7_LOWG_INT2_EN_MASK | - INT_SOURCE7_STEP_DET_INT2_EN_MASK | INT_SOURCE7_STEP_CNT_OFL_INT2_EN_MASK | - INT_SOURCE7_TILT_DET_INT2_EN_MASK); - data[0] |= ((it->INV_FF != 0) << INT_SOURCE7_FF_INT2_EN_POS); - data[0] |= ((it->INV_LOWG != 0) << INT_SOURCE7_LOWG_INT2_EN_POS); - data[0] |= ((it->INV_STEP_DET != 0) << INT_SOURCE7_STEP_DET_INT2_EN_POS); - data[0] |= ((it->INV_STEP_CNT_OVFL != 0) << INT_SOURCE7_STEP_CNT_OFL_INT2_EN_POS); - data[0] |= ((it->INV_TILT_DET != 0) << INT_SOURCE7_TILT_DET_INT2_EN_POS); - - status |= inv_imu_write_reg(s, INT_SOURCE7_MREG1, 1, &data[0]); - - return status; -} - -int inv_imu_get_data_from_registers(struct inv_imu_device *s) -{ - int status = 0; - uint8_t int_status; - uint8_t temperature[2]; - uint8_t accel[ACCEL_DATA_SIZE]; - uint8_t gyro[GYRO_DATA_SIZE]; - inv_imu_sensor_event_t event; - - /* Ensure data ready status bit is set */ - if ((status |= inv_imu_read_reg(s, INT_STATUS_DRDY, 1, &int_status))) - return status; - - if (int_status & INT_STATUS_DRDY_DATA_RDY_INT_MASK) { - status |= inv_imu_read_reg(s, TEMP_DATA1, 2, &temperature[0]); - - if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { - event.temperature = (((int16_t)temperature[0]) << 8) | temperature[1]; - } else { - event.temperature = (((int16_t)temperature[1]) << 8) | temperature[0]; - } - - status |= inv_imu_read_reg(s, ACCEL_DATA_X1, ACCEL_DATA_SIZE, &accel[0]); - - if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { - event.accel[0] = (accel[0] << 8) | accel[1]; - event.accel[1] = (accel[2] << 8) | accel[3]; - event.accel[2] = (accel[4] << 8) | accel[5]; - } else { - event.accel[0] = (accel[1] << 8) | accel[0]; - event.accel[1] = (accel[3] << 8) | accel[2]; - event.accel[2] = (accel[5] << 8) | accel[4]; - } - - status |= inv_imu_read_reg(s, GYRO_DATA_X1, GYRO_DATA_SIZE, &gyro[0]); - - if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { - event.gyro[0] = (gyro[0] << 8) | gyro[1]; - event.gyro[1] = (gyro[2] << 8) | gyro[3]; - event.gyro[2] = (gyro[4] << 8) | gyro[5]; - } else { - event.gyro[0] = (gyro[1] << 8) | gyro[0]; - event.gyro[1] = (gyro[3] << 8) | gyro[2]; - event.gyro[2] = (gyro[5] << 8) | gyro[4]; - } - - /* call sensor event callback */ - if (s->sensor_event_cb) - s->sensor_event_cb(&event); - } - /*else: Data Ready was not reached*/ - - return status; -} - -int inv_imu_get_data_from_fifo(struct inv_imu_device *s) -{ - int status = 0; - uint8_t int_status; - uint16_t packet_count_i, packet_count, total_packet_count = 0; - uint16_t packet_size = FIFO_HEADER_SIZE + FIFO_ACCEL_DATA_SIZE + FIFO_GYRO_DATA_SIZE + - FIFO_TEMP_DATA_SIZE + FIFO_TS_FSYNC_SIZE; - fifo_header_t *header; - - /* Ensure data ready status bit is set */ - if ((status |= inv_imu_read_reg(s, INT_STATUS, 1, &int_status))) - return status; - - if ((int_status & INT_STATUS_FIFO_THS_INT_MASK) || - (int_status & INT_STATUS_FIFO_FULL_INT_MASK)) { - uint8_t data[2]; - - /* - * Make sure RCOSC is enabled to guarrantee FIFO read. - * For power optimization, this call can be ommited under specific conditions: - * - If using WM interrupt and you can guarrantee entire FIFO will be read at once. - * - If gyro is enabled or accel is in LN or LP+RCOSC mode. - * - In accel LP+WUOSC mode, if you wait 100 us after reading FIFO_COUNT and - * you can guarrantee that the FIFO will be read within 1 ms. - * Please refer to the AN-000324 for more information. - */ - status |= inv_imu_switch_on_mclk(s); - - /* FIFO record mode configured at driver init, so we read packet number, not byte count */ - if ((status |= inv_imu_read_reg(s, FIFO_COUNTH, 2, &data[0])) != INV_ERROR_SUCCESS) { - status |= inv_imu_switch_off_mclk(s); - return status; - } - - total_packet_count = (uint16_t)(data[0] | (data[1] << 8)); - packet_count = total_packet_count; - while (packet_count > 0) { - uint16_t invalid_frame_cnt = 0; - /* Read FIFO only when data is expected in FIFO */ - /* fifo_idx type variable must be large enough to parse the FIFO_MIRRORING_SIZE */ - uint16_t fifo_idx = 0; - - if (s->fifo_highres_enabled) - packet_size = FIFO_20BYTES_PACKET_SIZE; - - if ((status |= - inv_imu_read_reg(s, FIFO_DATA, packet_size * packet_count, s->fifo_data))) { - /* - * Sensor data is in FIFO according to FIFO_COUNT but failed to read FIFO, - * reset FIFO and try next chance - */ - status |= inv_imu_reset_fifo(s); - status |= inv_imu_switch_off_mclk(s); - return status; - } - - for (packet_count_i = 0; packet_count_i < packet_count; packet_count_i++) { - inv_imu_sensor_event_t event; - event.sensor_mask = 0; - header = (fifo_header_t *)&s->fifo_data[fifo_idx]; - fifo_idx += FIFO_HEADER_SIZE; - - /* Decode invalid frame, this typically happens if packet_count is greater - than 2 in case of WOM event since FIFO_THS IRQ is disabled if WOM is enabled, - and we wake up only upon a WOM event so we can have more than 1 ACC packet in FIFO - and we do not wait the oscillator wake-up time so we will receive 1 invalid packet, - which we will read again upon next FIFO read operation thanks to while() loop*/ - if (header->Byte == 0x80) { - uint8_t is_invalid_frame = 1; - /* Check N-FIFO_HEADER_SIZE remaining bytes are all 0 to be invalid frame */ - for (uint8_t i = 0; i < (packet_size - FIFO_HEADER_SIZE); i++) { - if (s->fifo_data[fifo_idx + i]) { - is_invalid_frame = 0; - break; - } - } - /* In case of invalid frame read FIFO will be retried for this packet */ - invalid_frame_cnt += is_invalid_frame; - fifo_idx += packet_size - FIFO_HEADER_SIZE; - } else { - /* Decode packet */ - if (header->bits.msg_bit) { - /* MSG BIT set in FIFO header, Resetting FIFO */ - status |= inv_imu_reset_fifo(s); - status |= inv_imu_switch_off_mclk(s); - return INV_ERROR; - } - - if (header->bits.accel_bit) { - if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { - event.accel[0] = - (s->fifo_data[0 + fifo_idx] << 8) | s->fifo_data[1 + fifo_idx]; - event.accel[1] = - (s->fifo_data[2 + fifo_idx] << 8) | s->fifo_data[3 + fifo_idx]; - event.accel[2] = - (s->fifo_data[4 + fifo_idx] << 8) | s->fifo_data[5 + fifo_idx]; - } else { - event.accel[0] = - (s->fifo_data[1 + fifo_idx] << 8) | s->fifo_data[0 + fifo_idx]; - event.accel[1] = - (s->fifo_data[3 + fifo_idx] << 8) | s->fifo_data[2 + fifo_idx]; - event.accel[2] = - (s->fifo_data[5 + fifo_idx] << 8) | s->fifo_data[4 + fifo_idx]; - } - fifo_idx += FIFO_ACCEL_DATA_SIZE; - } - - if (header->bits.gyro_bit) { - if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { - event.gyro[0] = - (s->fifo_data[0 + fifo_idx] << 8) | s->fifo_data[1 + fifo_idx]; - event.gyro[1] = - (s->fifo_data[2 + fifo_idx] << 8) | s->fifo_data[3 + fifo_idx]; - event.gyro[2] = - (s->fifo_data[4 + fifo_idx] << 8) | s->fifo_data[5 + fifo_idx]; - } else { - event.gyro[0] = - (s->fifo_data[1 + fifo_idx] << 8) | s->fifo_data[0 + fifo_idx]; - event.gyro[1] = - (s->fifo_data[3 + fifo_idx] << 8) | s->fifo_data[2 + fifo_idx]; - event.gyro[2] = - (s->fifo_data[5 + fifo_idx] << 8) | s->fifo_data[4 + fifo_idx]; - } - fifo_idx += FIFO_GYRO_DATA_SIZE; - } - - if ((header->bits.accel_bit) || (header->bits.gyro_bit)) { - /* - * The coarse temperature (8 or 16B FIFO packet format) - * range is ± 64 degrees with 0.5°C resolution. - * but the fine temperature range (2 bytes) (20B FIFO packet format) is - * ± 256 degrees with (1/128)°C resolution - */ - if (header->bits.twentybits_bit) { - if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) { - event.temperature = (((int16_t)s->fifo_data[0 + fifo_idx]) << 8) | - s->fifo_data[1 + fifo_idx]; - } else { - event.temperature = (((int16_t)s->fifo_data[1 + fifo_idx]) << 8) | - s->fifo_data[0 + fifo_idx]; - } - fifo_idx += FIFO_TEMP_DATA_SIZE + FIFO_TEMP_HIGH_RES_SIZE; - - /* new temperature data */ - if (event.temperature != INVALID_VALUE_FIFO) - event.sensor_mask |= (1 << INV_SENSOR_TEMPERATURE); - } else { - /* cast to int8_t since FIFO is in 16 bits mode (temperature on 8 bits) */ - event.temperature = (int8_t)s->fifo_data[0 + fifo_idx]; - fifo_idx += FIFO_TEMP_DATA_SIZE; - - /* new temperature data */ - if (event.temperature != INVALID_VALUE_FIFO_1B) - event.sensor_mask |= (1 << INV_SENSOR_TEMPERATURE); - } - } - - if ((header->bits.timestamp_bit) || (header->bits.fsync_bit)) { - if (s->endianness_data == INTF_CONFIG0_DATA_BIG_ENDIAN) - event.timestamp_fsync = - (s->fifo_data[0 + fifo_idx] << 8) | s->fifo_data[1 + fifo_idx]; - else - event.timestamp_fsync = - (s->fifo_data[1 + fifo_idx] << 8) | s->fifo_data[0 + fifo_idx]; - fifo_idx += FIFO_TS_FSYNC_SIZE; - /* new fsync event */ - if (header->bits.fsync_bit) - event.sensor_mask |= (1 << INV_SENSOR_FSYNC_EVENT); - } - - if (header->bits.accel_bit) { - if ((event.accel[0] != INVALID_VALUE_FIFO) && - (event.accel[1] != INVALID_VALUE_FIFO) && - (event.accel[2] != INVALID_VALUE_FIFO)) { - if (s->accel_start_time_us == UINT32_MAX) { - event.sensor_mask |= (1 << INV_SENSOR_ACCEL); - } else { - if (!header->bits.fsync_bit) { - /* Discard first data after startup to let output to settle */ - if ((inv_imu_get_time_us() - s->accel_start_time_us) >= - ACC_STARTUP_TIME_US) { - s->accel_start_time_us = UINT32_MAX; - event.sensor_mask |= (1 << INV_SENSOR_ACCEL); - } - } - } - - if ((event.sensor_mask & (1 << INV_SENSOR_ACCEL)) && - (header->bits.twentybits_bit)) { - event.accel_high_res[0] = (s->fifo_data[0 + fifo_idx] >> 4) & 0xF; - event.accel_high_res[1] = (s->fifo_data[1 + fifo_idx] >> 4) & 0xF; - event.accel_high_res[2] = (s->fifo_data[2 + fifo_idx] >> 4) & 0xF; - } - } - } - - if (header->bits.gyro_bit) { - if ((event.gyro[0] != INVALID_VALUE_FIFO) && - (event.gyro[1] != INVALID_VALUE_FIFO) && - (event.gyro[2] != INVALID_VALUE_FIFO)) { - if (s->gyro_start_time_us == UINT32_MAX) { - event.sensor_mask |= (1 << INV_SENSOR_GYRO); - } else { - if (!header->bits.fsync_bit) { - /* Discard first data after startup to let output to settle */ - if ((inv_imu_get_time_us() - s->gyro_start_time_us) >= - GYR_STARTUP_TIME_US) { - s->gyro_start_time_us = UINT32_MAX; - event.sensor_mask |= (1 << INV_SENSOR_GYRO); - } - } - } - - if ((event.sensor_mask & (1 << INV_SENSOR_GYRO)) && - (header->bits.twentybits_bit)) { - event.gyro_high_res[0] = (s->fifo_data[0 + fifo_idx]) & 0xF; - event.gyro_high_res[1] = (s->fifo_data[1 + fifo_idx]) & 0xF; - event.gyro_high_res[2] = (s->fifo_data[2 + fifo_idx]) & 0xF; - } - } - } - - if (header->bits.twentybits_bit) - fifo_idx += FIFO_ACCEL_GYRO_HIGH_RES_SIZE; - - /* call sensor event callback */ - if (s->sensor_event_cb) - s->sensor_event_cb(&event); - - } /* end of else invalid frame */ - } /* end of FIFO read for loop */ - packet_count = invalid_frame_cnt; - } /*end of while: packet_count > 0*/ - - status |= inv_imu_switch_off_mclk(s); - - } /*else: FIFO threshold was not reached and FIFO was not full*/ - - return total_packet_count; -} - -uint32_t inv_imu_convert_odr_bitfield_to_us(uint32_t odr_bitfield) -{ - /* - odr bitfield - frequency : odr ms - 0 - N/A - 1 - N/A - 2 - N/A - 3 - N/A - 4 - N/A - 5 - 1.6k : 0.625ms - (default) 6 - 800 : 1.25ms - 7 - 400 : 2.5 ms - 8 - 200 : 5 ms - 9 - 100 : 10 ms - 10 - 50 : 20 ms - 11 - 25 : 40 ms - 12 - 12.5 : 80 ms - 13 - 6.25 : 160 ms - 14 - 3.125 : 320 ms - 15 - 1.5625 : 640 ms - */ - - switch (odr_bitfield) { - case ACCEL_CONFIG0_ODR_1600_HZ: - return 625; - case ACCEL_CONFIG0_ODR_800_HZ: - return 1250; - case ACCEL_CONFIG0_ODR_400_HZ: - return 2500; - case ACCEL_CONFIG0_ODR_200_HZ: - return 5000; - case ACCEL_CONFIG0_ODR_100_HZ: - return 10000; - case ACCEL_CONFIG0_ODR_50_HZ: - return 20000; - case ACCEL_CONFIG0_ODR_25_HZ: - return 40000; - case ACCEL_CONFIG0_ODR_12_5_HZ: - return 80000; - case ACCEL_CONFIG0_ODR_6_25_HZ: - return 160000; - case ACCEL_CONFIG0_ODR_3_125_HZ: - return 320000; - case ACCEL_CONFIG0_ODR_1_5625_HZ: - default: - return 640000; - } -} - -int inv_imu_set_accel_frequency(struct inv_imu_device *s, const ACCEL_CONFIG0_ODR_t frequency) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &data); - data &= ~ACCEL_CONFIG0_ACCEL_ODR_MASK; - data |= frequency; - status |= inv_imu_write_reg(s, ACCEL_CONFIG0, 1, &data); - - return status; -} - -int inv_imu_set_gyro_frequency(struct inv_imu_device *s, const GYRO_CONFIG0_ODR_t frequency) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, GYRO_CONFIG0, 1, &data); - data &= ~GYRO_CONFIG0_GYRO_ODR_MASK; - data |= frequency; - status |= inv_imu_write_reg(s, GYRO_CONFIG0, 1, &data); - - return status; -} - -int inv_imu_set_accel_fsr(struct inv_imu_device *s, ACCEL_CONFIG0_FS_SEL_t accel_fsr_g) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &data); - data &= ~ACCEL_CONFIG0_ACCEL_UI_FS_SEL_MASK; - data |= accel_fsr_g; - status |= inv_imu_write_reg(s, ACCEL_CONFIG0, 1, &data); - - return status; -} - -int inv_imu_set_gyro_fsr(struct inv_imu_device *s, GYRO_CONFIG0_FS_SEL_t gyro_fsr_dps) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, GYRO_CONFIG0, 1, &data); - data &= ~GYRO_CONFIG0_GYRO_UI_FS_SEL_MASK; - data |= gyro_fsr_dps; - status |= inv_imu_write_reg(s, GYRO_CONFIG0, 1, &data); - - return status; -} - -int inv_imu_get_accel_fsr(struct inv_imu_device *s, ACCEL_CONFIG0_FS_SEL_t *accel_fsr_g) -{ - int status = 0; - uint8_t accel_config0_reg; - - if ((s->fifo_highres_enabled) && (s->fifo_is_used == INV_IMU_FIFO_ENABLED)) - *accel_fsr_g = ACCEL_CONFIG0_FS_SEL_MAX; - else { - status |= inv_imu_read_reg(s, ACCEL_CONFIG0, 1, &accel_config0_reg); - *accel_fsr_g = - (ACCEL_CONFIG0_FS_SEL_t)(accel_config0_reg & ACCEL_CONFIG0_ACCEL_UI_FS_SEL_MASK); - } - - return status; -} - -int inv_imu_get_gyro_fsr(struct inv_imu_device *s, GYRO_CONFIG0_FS_SEL_t *gyro_fsr_dps) -{ - int status = 0; - uint8_t gyro_config0_reg; - - if ((s->fifo_highres_enabled) && (s->fifo_is_used == INV_IMU_FIFO_ENABLED)) - *gyro_fsr_dps = GYRO_CONFIG0_FS_SEL_MAX; - else { - status |= inv_imu_read_reg(s, GYRO_CONFIG0, 1, &gyro_config0_reg); - *gyro_fsr_dps = - (GYRO_CONFIG0_FS_SEL_t)(gyro_config0_reg & GYRO_CONFIG0_GYRO_UI_FS_SEL_MASK); - } - - return status; -} - -int inv_imu_set_accel_lp_avg(struct inv_imu_device *s, ACCEL_CONFIG1_ACCEL_FILT_AVG_t acc_avg) -{ - uint8_t value; - int status = 0; - - status |= inv_imu_read_reg(s, ACCEL_CONFIG1, 1, &value); - if (status) - return status; - - value &= ~ACCEL_CONFIG1_ACCEL_UI_AVG_MASK; - value |= acc_avg; - - status |= inv_imu_write_reg(s, ACCEL_CONFIG1, 1, &value); - - return status; -} - -int inv_imu_set_accel_ln_bw(struct inv_imu_device *s, ACCEL_CONFIG1_ACCEL_FILT_BW_t acc_bw) -{ - uint8_t value; - int status = 0; - - status |= inv_imu_read_reg(s, ACCEL_CONFIG1, 1, &value); - if (status) - return status; - - value &= ~ACCEL_CONFIG1_ACCEL_UI_FILT_BW_MASK; - value |= acc_bw; - - status |= inv_imu_write_reg(s, ACCEL_CONFIG1, 1, &value); - - return status; -} - -int inv_imu_set_gyro_ln_bw(struct inv_imu_device *s, GYRO_CONFIG1_GYRO_FILT_BW_t gyr_bw) -{ - uint8_t value; - int status = 0; - - status |= inv_imu_read_reg(s, GYRO_CONFIG1, 1, &value); - if (status) - return status; - - value &= ~GYRO_CONFIG1_GYRO_UI_FILT_BW_MASK; - value |= gyr_bw; - - status |= inv_imu_write_reg(s, GYRO_CONFIG1, 1, &value); - - return status; -} - -int inv_imu_set_timestamp_resolution(struct inv_imu_device * s, - const TMST_CONFIG1_RESOL_t timestamp_resol) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &data); - data &= ~TMST_CONFIG1_TMST_RES_MASK; - data |= timestamp_resol; - status |= inv_imu_write_reg(s, TMST_CONFIG1_MREG1, 1, &data); - - return status; -} - -int inv_imu_reset_fifo(struct inv_imu_device *s) -{ - int status = 0; - uint8_t fifo_flush_status = (uint8_t)SIGNAL_PATH_RESET_FIFO_FLUSH_EN; - - status |= inv_imu_switch_on_mclk(s); - - status |= inv_imu_write_reg(s, SIGNAL_PATH_RESET, 1, &fifo_flush_status); - inv_imu_sleep_us(10); - - /* Wait for FIFO flush (idle bit will go high at appropriate time and unlock flush) */ - while ((status == 0) && ((fifo_flush_status & SIGNAL_PATH_RESET_FIFO_FLUSH_MASK) == - (uint8_t)SIGNAL_PATH_RESET_FIFO_FLUSH_EN)) { - status |= inv_imu_read_reg(s, SIGNAL_PATH_RESET, 1, &fifo_flush_status); - } - - status |= inv_imu_switch_off_mclk(s); - - return status; -} - -int inv_imu_enable_high_resolution_fifo(struct inv_imu_device *s) -{ - uint8_t value; - int status = 0; - - /* set FIFO packets to 20bit format (i.e. high res is enabled) */ - s->fifo_highres_enabled = 1; - - status |= inv_imu_read_reg(s, FIFO_CONFIG5_MREG1, 1, &value); - value &= ~FIFO_CONFIG5_FIFO_HIRES_EN_MASK; - value |= FIFO_CONFIG5_HIRES_EN; - status |= inv_imu_write_reg(s, FIFO_CONFIG5_MREG1, 1, &value); - - return status; -} - -int inv_imu_disable_high_resolution_fifo(struct inv_imu_device *s) -{ - uint8_t value; - int status = 0; - - /* set FIFO packets to 16bit format (i.e. high res is disabled) */ - s->fifo_highres_enabled = 0; - - status |= inv_imu_read_reg(s, FIFO_CONFIG5_MREG1, 1, &value); - value &= ~FIFO_CONFIG5_FIFO_HIRES_EN_MASK; - value |= FIFO_CONFIG5_HIRES_DIS; - status |= inv_imu_write_reg(s, FIFO_CONFIG5_MREG1, 1, &value); - - return status; -} - -int inv_imu_configure_fifo(struct inv_imu_device *s, INV_IMU_FIFO_CONFIG_t fifo_config) -{ - int status = 0; - uint8_t data; - inv_imu_interrupt_parameter_t config_int = { (inv_imu_interrupt_value)0 }; - - s->fifo_is_used = fifo_config; - - inv_imu_switch_on_mclk(s); - - switch (fifo_config) { - case INV_IMU_FIFO_ENABLED: - /* Configure: - - FIFO record mode i.e FIFO count unit is packet - - FIFO snapshot mode i.e drop the data when the FIFO overflows - - Timestamp is logged in FIFO - - Little Endian fifo_count - */ - - status |= inv_imu_read_reg(s, INTF_CONFIG0, 1, &data); - data &= ~(INTF_CONFIG0_FIFO_COUNT_FORMAT_MASK | INTF_CONFIG0_FIFO_COUNT_ENDIAN_MASK); - data |= (uint8_t)INTF_CONFIG0_FIFO_COUNT_REC_RECORD | - (uint8_t)INTF_CONFIG0_FIFO_COUNT_LITTLE_ENDIAN; - status |= inv_imu_write_reg(s, INTF_CONFIG0, 1, &data); - - status |= inv_imu_read_reg(s, FIFO_CONFIG1, 1, &data); - data &= ~(FIFO_CONFIG1_FIFO_MODE_MASK | FIFO_CONFIG1_FIFO_BYPASS_MASK); - data |= (uint8_t)FIFO_CONFIG1_FIFO_MODE_SNAPSHOT | (uint8_t)FIFO_CONFIG1_FIFO_BYPASS_OFF; - status |= inv_imu_write_reg(s, FIFO_CONFIG1, 1, &data); - - status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &data); - data &= ~TMST_CONFIG1_TMST_EN_MASK; - data |= TMST_CONFIG1_TMST_EN; - status |= inv_imu_write_reg(s, TMST_CONFIG1_MREG1, 1, &data); - - /* restart and reset FIFO configuration */ - status |= inv_imu_read_reg(s, FIFO_CONFIG5_MREG1, 1, &data); - data &= ~(FIFO_CONFIG5_FIFO_GYRO_EN_MASK | FIFO_CONFIG5_FIFO_ACCEL_EN_MASK | - FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_MASK | FIFO_CONFIG5_FIFO_HIRES_EN_MASK); - data |= ((uint8_t)FIFO_CONFIG5_GYRO_EN | (uint8_t)FIFO_CONFIG5_ACCEL_EN | - (uint8_t)FIFO_CONFIG5_TMST_FSYNC_EN | (uint8_t)FIFO_CONFIG5_WM_GT_TH_EN); - status |= inv_imu_write_reg(s, FIFO_CONFIG5_MREG1, 1, &data); - - // Configure FIFO WM so that INT is triggered for each packet - data = 0x1; - status |= inv_imu_write_reg(s, FIFO_CONFIG2, 1, &data); - - /* Disable Data Ready Interrupt */ - status |= inv_imu_get_config_int1(s, &config_int); - config_int.INV_UI_DRDY = INV_IMU_DISABLE; - status |= inv_imu_set_config_int1(s, &config_int); - break; - - case INV_IMU_FIFO_DISABLED: - /* make sure FIFO is disabled */ - status |= inv_imu_read_reg(s, FIFO_CONFIG1, 1, &data); - data &= ~FIFO_CONFIG1_FIFO_BYPASS_MASK; - data |= (uint8_t)FIFO_CONFIG1_FIFO_BYPASS_ON; - status |= inv_imu_write_reg(s, FIFO_CONFIG1, 1, &data); - - /* restart and reset FIFO configuration */ - status |= inv_imu_read_reg(s, FIFO_CONFIG5_MREG1, 1, &data); - data &= ~(FIFO_CONFIG5_FIFO_GYRO_EN_MASK | FIFO_CONFIG5_FIFO_ACCEL_EN_MASK | - FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_MASK); - data |= ((uint8_t)FIFO_CONFIG5_GYRO_DIS | (uint8_t)FIFO_CONFIG5_ACCEL_DIS | - (uint8_t)FIFO_CONFIG5_TMST_FSYNC_EN); - status |= inv_imu_write_reg(s, FIFO_CONFIG5_MREG1, 1, &data); - - /* Enable Data Ready Interrupt */ - status |= inv_imu_get_config_int1(s, &config_int); - config_int.INV_UI_DRDY = INV_IMU_ENABLE; - status |= inv_imu_set_config_int1(s, &config_int); - break; - - default: - status = -1; - } - - status |= inv_imu_switch_off_mclk(s); - - return status; -} - -uint32_t inv_imu_get_timestamp_resolution_us(struct inv_imu_device *s) -{ - int status = 0; - uint8_t tmst_config1_reg; - TMST_CONFIG1_RESOL_t tmst_resol; - - status |= inv_imu_read_reg(s, TMST_CONFIG1_MREG1, 1, &tmst_config1_reg); - if (status < 0) - return INV_ERROR; - - tmst_resol = (TMST_CONFIG1_RESOL_t)(tmst_config1_reg & TMST_CONFIG1_TMST_RES_MASK); - - if (tmst_resol == TMST_CONFIG1_RESOL_16us) - return 16; - else if (tmst_resol == TMST_CONFIG1_RESOL_1us) - return 1; - - // Should not happen, return 0 - return 0; -} - -const char *inv_imu_get_version(void) -{ - return INV_IMU_VERSION_STRING; -} - -/* - * Static functions definition - */ -static int configure_serial_interface(struct inv_imu_device *s) -{ - uint8_t value; - int status = 0; - - /* Ensure BLK_SEL_R and BLK_SEL_W are set to 0 */ - value = 0; - status |= inv_imu_write_reg(s, BLK_SEL_R, 1, &value); - status |= inv_imu_write_reg(s, BLK_SEL_W, 1, &value); - - if (s->transport.serif.serif_type == UI_I2C) { - /* Enable I2C 50ns spike filtering */ - status |= inv_imu_read_reg(s, INTF_CONFIG1, 1, &value); - value &= ~(INTF_CONFIG1_I3C_SDR_EN_MASK | INTF_CONFIG1_I3C_DDR_EN_MASK); - status |= inv_imu_write_reg(s, INTF_CONFIG1, 1, &value); - } else { - /* Configure SPI */ - if (s->transport.serif.serif_type == UI_SPI4) - value = (uint8_t)DEVICE_CONFIG_SPI_4WIRE | (uint8_t)DEVICE_CONFIG_SPI_MODE_0_3; - else if (s->transport.serif.serif_type == UI_SPI3) - value = (uint8_t)DEVICE_CONFIG_SPI_3WIRE | (uint8_t)DEVICE_CONFIG_SPI_MODE_0_3; - else - return INV_ERROR_BAD_ARG; /* Not supported */ - status |= inv_imu_write_reg(s, DEVICE_CONFIG, 1, &value); - - /* Device operation in shared spi bus configuration (AN-000352) */ - status |= inv_imu_read_reg(s, INTF_CONFIG0, 1, &value); - value |= 0x3; - status |= inv_imu_write_reg(s, INTF_CONFIG0, 1, &value); - } - - return status; -} - -static int init_hardware_from_ui(struct inv_imu_device *s) -{ - int status = 0; - uint8_t value; - inv_imu_interrupt_parameter_t config_int = {(inv_imu_interrupt_value)0}; - - /* Deactivate FSYNC by default */ - status |= inv_imu_disable_fsync(s); - - /* Set default timestamp resolution 16us (Mobile use cases) */ - status |= inv_imu_set_timestamp_resolution(s, TMST_CONFIG1_RESOL_16us); - - /* Enable push pull on INT1 to avoid moving in Test Mode after a soft reset */ - status |= inv_imu_read_reg(s, INT_CONFIG, 1, &value); - value &= ~INT_CONFIG_INT1_DRIVE_CIRCUIT_MASK; - value |= (uint8_t)INT_CONFIG_INT1_DRIVE_CIRCUIT_PP; - status |= inv_imu_write_reg(s, INT_CONFIG, 1, &value); - - /* Configure the INT1 interrupt pulse as active high */ - status |= inv_imu_read_reg(s, INT_CONFIG, 1, &value); - value &= ~INT_CONFIG_INT1_POLARITY_MASK; - value |= (uint8_t)INT_CONFIG_INT1_POLARITY_HIGH; - status |= inv_imu_write_reg(s, INT_CONFIG, 1, &value); - - /* Set interrupt config */ - config_int.INV_UI_FSYNC = INV_IMU_DISABLE; - config_int.INV_UI_DRDY = INV_IMU_DISABLE; - config_int.INV_FIFO_THS = INV_IMU_ENABLE; - config_int.INV_FIFO_FULL = INV_IMU_DISABLE; - config_int.INV_SMD = INV_IMU_ENABLE; - config_int.INV_WOM_X = INV_IMU_ENABLE; - config_int.INV_WOM_Y = INV_IMU_ENABLE; - config_int.INV_WOM_Z = INV_IMU_ENABLE; - config_int.INV_FF = INV_IMU_ENABLE; - config_int.INV_LOWG = INV_IMU_ENABLE; - config_int.INV_STEP_DET = INV_IMU_ENABLE; - config_int.INV_STEP_CNT_OVFL = INV_IMU_ENABLE; - config_int.INV_TILT_DET = INV_IMU_ENABLE; - status |= inv_imu_set_config_int1(s, &config_int); - - /* Enable FIFO: use 16-bit format by default (i.e. high res is disabled) */ - status |= inv_imu_configure_fifo(s, INV_IMU_FIFO_ENABLED); - - /* - * Disable the automatic RCOSC power on to avoid - * extra power consumption in sleep mode (all sensors and clocks off) - */ - status |= inv_imu_read_reg(s, FIFO_CONFIG6_MREG1, 1, &value); - value |= ((1 & FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK) - << FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_POS); - status |= inv_imu_write_reg(s, FIFO_CONFIG6_MREG1, 1, &value); - - return status; -} - -int inv_imu_configure_fifo_data_rate(struct inv_imu_device *s, FDR_CONFIG_FDR_SEL_t dec_factor) -{ - int status = 0; - uint8_t data; - - status |= inv_imu_read_reg(s, FDR_CONFIG_MREG1, 1, &data); - data &= (uint8_t)~FDR_CONFIG_FDR_SEL_MASK; - data |= (uint8_t)dec_factor; - status |= inv_imu_write_reg(s, FDR_CONFIG_MREG1, 1, &data); - - return status; -} - -static int resume_dmp(struct inv_imu_device *s) -{ - int status = 0; - uint8_t value; - uint64_t start; - - status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &value); - value &= ~APEX_CONFIG0_DMP_INIT_EN_MASK; - value |= (uint8_t)APEX_CONFIG0_DMP_INIT_EN; - status |= inv_imu_write_reg(s, APEX_CONFIG0, 1, &value); - - /* wait to make sure dmp_init_en = 0 */ - start = inv_imu_get_time_us(); - do { - inv_imu_read_reg(s, APEX_CONFIG0, 1, &value); - inv_imu_sleep_us(100); - - if ((value & APEX_CONFIG0_DMP_INIT_EN_MASK) == 0) - break; - - } while (inv_imu_get_time_us() - start < 50000); - - return status; -} - -int inv_imu_start_dmp(struct inv_imu_device *s) -{ - int status = 0; - - // On first enabling of DMP, reset internal state - if (!s->dmp_is_on) { - // Reset SRAM to 0's - status |= inv_imu_reset_dmp(s, APEX_CONFIG0_DMP_MEM_RESET_APEX_ST_EN); - if (status) - return status; - s->dmp_is_on = 1; - } - - // Initialize DMP - status |= resume_dmp(s); - - return status; -} - -int inv_imu_reset_dmp(struct inv_imu_device *s, const APEX_CONFIG0_DMP_MEM_RESET_t sram_reset) -{ - const int ref_timeout = 5000; /*50 ms*/ - int status = 0; - int timeout = ref_timeout; - uint8_t data_dmp_reset; - uint8_t value = 0; - - status |= inv_imu_switch_on_mclk(s); - - // Reset DMP internal memories - status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &value); - value &= ~APEX_CONFIG0_DMP_MEM_RESET_EN_MASK; - value |= (sram_reset & APEX_CONFIG0_DMP_MEM_RESET_EN_MASK); - status |= inv_imu_write_reg(s, APEX_CONFIG0, 1, &value); - - inv_imu_sleep_us(1000); - - // Make sure reset procedure has finished by reading back mem_reset_en bit - do { - inv_imu_sleep_us(10); - status |= inv_imu_read_reg(s, APEX_CONFIG0, 1, &data_dmp_reset); - } while ( - ((data_dmp_reset & APEX_CONFIG0_DMP_MEM_RESET_EN_MASK) != APEX_CONFIG0_DMP_MEM_RESET_DIS) && - timeout-- && !status); - - status |= inv_imu_switch_off_mclk(s); - - if (timeout <= 0) - return INV_ERROR_TIMEOUT; - - return status; -} - -int inv_imu_set_endianness(struct inv_imu_device *s, INTF_CONFIG0_DATA_ENDIAN_t endianness) -{ - int status = 0; - uint8_t value; - - status |= inv_imu_read_reg(s, INTF_CONFIG0, 1, &value); - value &= ~INTF_CONFIG0_SENSOR_DATA_ENDIAN_MASK; - value |= (uint8_t)endianness; - status |= inv_imu_write_reg(s, INTF_CONFIG0, 1, &value); - - if (!status) - s->endianness_data = (uint8_t)endianness; - - return status; -} - -int inv_imu_get_endianness(struct inv_imu_device *s) -{ - int status = 0; - uint8_t value; - - status |= inv_imu_read_reg(s, INTF_CONFIG0, 1, &value); - if (!status) - s->endianness_data = value & INTF_CONFIG0_SENSOR_DATA_ENDIAN_MASK; - - return status; -} diff --git a/lib/ICM42670P/src/imu/inv_imu_driver.h b/lib/ICM42670P/src/imu/inv_imu_driver.h deleted file mode 100644 index 1522c72..0000000 --- a/lib/ICM42670P/src/imu/inv_imu_driver.h +++ /dev/null @@ -1,457 +0,0 @@ -/* - * ________________________________________________________________________________________________________ - * Copyright (c) 2017 InvenSense Inc. All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * ________________________________________________________________________________________________________ - */ - -/** @defgroup Driver Driver - * @brief High-level functions to drive the device - * @{ - */ - -/** @file inv_imu_driver.h */ - -#ifndef _INV_IMU_DRIVER_H_ -#define _INV_IMU_DRIVER_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "imu/inv_imu_defs.h" -#include "imu/inv_imu_transport.h" - -#include "Invn/InvError.h" - -#include -#include - -/** Max FSR values for accel */ -#define ACCEL_CONFIG0_FS_SEL_MAX ACCEL_CONFIG0_FS_SEL_16g - -/** Max FSR values for gyro */ -#define GYRO_CONFIG0_FS_SEL_MAX GYRO_CONFIG0_FS_SEL_2000dps - -/** Max user offset value for accel (mg) */ -#define ACCEL_OFFUSER_MAX_MG 1000 - -/** Max user offset value for gyro (dps) */ -#define GYRO_OFFUSER_MAX_DPS 64 - -/** Max buffer size mirrored from FIFO at polling time */ -#define FIFO_MIRRORING_SIZE 16 * 258 // packet size * max_count = 4kB - -/** Accel start-up time */ -#define ACC_STARTUP_TIME_US 10000 - -/** Gyro start-up time */ -#define GYR_STARTUP_TIME_US 70000 - -/** Gyro power-off to power-on duration */ -#define GYR_POWER_OFF_DUR_US 20000 - -/** Sensor identifier for UI control function */ -enum inv_imu_sensor { - INV_SENSOR_ACCEL, /**< Accelerometer */ - INV_SENSOR_GYRO, /**< Gyroscope */ - INV_SENSOR_FSYNC_EVENT, /**< FSYNC */ - INV_SENSOR_TEMPERATURE, /**< Chip temperature */ - INV_SENSOR_DMP_PEDOMETER_EVENT, /**< Pedometer: step detected */ - INV_SENSOR_DMP_PEDOMETER_COUNT, /**< Pedometer: step counter */ - INV_SENSOR_DMP_TILT, /**< Tilt */ - INV_SENSOR_DMP_FF, /**< FreeFall */ - INV_SENSOR_DMP_LOWG, /**< Low G */ - INV_SENSOR_DMP_SMD, /**< Significant Motion Detection */ - INV_SENSOR_MAX -}; - -/** Configure Fifo usage */ -typedef enum { - INV_IMU_FIFO_DISABLED = 0, /**< Fifo is disabled and data source is sensors registers */ - INV_IMU_FIFO_ENABLED = 1, /**< Fifo is used as data source */ -} INV_IMU_FIFO_CONFIG_t; - -/** Sensor event structure definition */ -typedef struct { - int sensor_mask; - uint16_t timestamp_fsync; - int16_t accel[3]; - int16_t gyro[3]; - int16_t temperature; - int8_t accel_high_res[3]; - int8_t gyro_high_res[3]; -} inv_imu_sensor_event_t; - -/** IMU driver states definition */ -struct inv_imu_device { - /** Transport layer. - * @warning Must be the first one of struct inv_imu_device - */ - struct inv_imu_transport transport; - - /** callback executed by: - * * inv_imu_get_data_from_fifo (if FIFO is used). - * * inv_imu_get_data_from_registers (if FIFO isn't used). - * May be NULL if above API are not used by application - */ - void (*sensor_event_cb)(inv_imu_sensor_event_t *event); - - - uint8_t fifo_data[FIFO_MIRRORING_SIZE]; /**< FIFO mirroring memory area */ - uint8_t dmp_is_on; /**< DMP started status */ - uint8_t endianness_data; /**< Data endianness configuration */ - uint8_t fifo_highres_enabled; /**< Highres mode configuration */ - INV_IMU_FIFO_CONFIG_t fifo_is_used; /**< FIFO configuration */ - uint64_t gyro_start_time_us; /**< Gyro start time used to discard first samples */ - uint64_t accel_start_time_us; /**< Accel start time used to discard first samples */ - uint64_t gyro_power_off_tmst; /**< Gyro power off time */ -}; - -/** Interrupt enum state for INT1, INT2, and IBI */ -typedef enum { - INV_IMU_DISABLE = 0, - INV_IMU_ENABLE -} inv_imu_interrupt_value; - -/** Interrupt definition */ -typedef struct { - inv_imu_interrupt_value INV_UI_FSYNC; - inv_imu_interrupt_value INV_UI_DRDY; - inv_imu_interrupt_value INV_FIFO_THS; - inv_imu_interrupt_value INV_FIFO_FULL; - inv_imu_interrupt_value INV_SMD; - inv_imu_interrupt_value INV_WOM_X; - inv_imu_interrupt_value INV_WOM_Y; - inv_imu_interrupt_value INV_WOM_Z; - inv_imu_interrupt_value INV_FF; - inv_imu_interrupt_value INV_LOWG; - inv_imu_interrupt_value INV_STEP_DET; - inv_imu_interrupt_value INV_STEP_CNT_OVFL; - inv_imu_interrupt_value INV_TILT_DET; -} inv_imu_interrupt_parameter_t; - -/** @brief Initializes device. - * @param[in] s Pointer to device. - * @param[in] serif Pointer on serial interface structure. - * @param[in] sensor_event_cb Callback executed when a new sensor event is available. * - * @return 0 on success, negative value on error. - */ -int inv_imu_init(struct inv_imu_device *s, struct inv_imu_serif *serif, - void (*sensor_event_cb)(inv_imu_sensor_event_t *event)); - -/** @brief Reset device by reloading OTPs. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_device_reset(struct inv_imu_device *s); - -/** @brief return WHOAMI value. - * @param[in] s Pointer to device. - * @param[out] who_am_i WHOAMI for device. - * @return 0 on success, negative value on error. - */ -int inv_imu_get_who_am_i(struct inv_imu_device *s, uint8_t *who_am_i); - -/** @brief Enable/put accel in low power mode. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_enable_accel_low_power_mode(struct inv_imu_device *s); - -/** @brief Enable/put accel in low noise mode. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_enable_accel_low_noise_mode(struct inv_imu_device *s); - -/** @brief Disable all 3 axes of accel. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_disable_accel(struct inv_imu_device *s); - -/** @brief Enable/put gyro in low noise mode. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_enable_gyro_low_noise_mode(struct inv_imu_device *s); - -/** @brief Disable all 3 axes of gyro. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_disable_gyro(struct inv_imu_device *s); - -/** @brief Enable fsync tagging functionality. - * * Enables fsync. - * * Enables timestamp to registers. Once fsync is enabled fsync counter is pushed to - * fifo instead of timestamp. So timestamp is made available in registers. Note that - * this increase power consumption. - * * Enables fsync related interrupt. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_enable_fsync(struct inv_imu_device *s); - -/** @brief Disable fsync tagging functionality. - * * Disables fsync. - * * Disables timestamp to registers. Once fsync is disabled timestamp is pushed to fifo - * instead of fsync counter. So in order to decrease power consumption, timestamp is no - * more available in registers. - * * Disables fsync related interrupt. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_disable_fsync(struct inv_imu_device *s); - -/** @brief Configure which interrupt source can trigger INT1. - * @param[in] s Pointer to device. - * @param[in] interrupt_to_configure Structure with the corresponding state to manage INT1. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_config_int1(struct inv_imu_device * s, - inv_imu_interrupt_parameter_t *interrupt_to_configure); - -/** @brief Retrieve interrupts configuration. - * @param[in] s Pointer to device. - * @param[in] interrupt_to_configure Structure with the corresponding state to manage INT1. - * @return 0 on success, negative value on error. - */ -int inv_imu_get_config_int1(struct inv_imu_device * s, - inv_imu_interrupt_parameter_t *interrupt_to_configure); - -/** @brief Configure which interrupt source can trigger INT2. - * @param[in] s Pointer to device. - * @param[in] interrupt_to_configure Structure with the corresponding state to INT2. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_config_int2(struct inv_imu_device * s, - inv_imu_interrupt_parameter_t *interrupt_to_configure); - -/** @brief Retrieve interrupts configuration. - * @param[in] s Pointer to device. - * @param[in] interrupt_to_configure Structure with the corresponding state to manage INT2. - * @return 0 on success, negative value on error. - */ -int inv_imu_get_config_int2(struct inv_imu_device * s, - inv_imu_interrupt_parameter_t *interrupt_to_configure); - -/** @brief Read all registers containing data (temperature, accelerometer and gyroscope). - * Then it calls sensor_event_cb function passed at init for each packet. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_get_data_from_registers(struct inv_imu_device *s); - -/** @brief Read all available packets from the FIFO. - * For each packet function builds a sensor event containing packet data - * and validity information. Then it calls sensor_event_cb funtion passed - * at init for each packet. - * @param[in] s Pointer to device. - * @return Number of valid packets read on success, negative value on error. - */ -int inv_imu_get_data_from_fifo(struct inv_imu_device *s); - -/** @brief Converts ACCEL_CONFIG0_ODR_t or GYRO_CONFIG0_ODR_t enums to period expressed in us. - * @param[in] odr_bitfield An ACCEL_CONFIG0_ODR_t or GYRO_CONFIG0_ODR_t enum. - * @return The corresponding period expressed in us. - */ -uint32_t inv_imu_convert_odr_bitfield_to_us(uint32_t odr_bitfield); - -/** @brief Configure accel Output Data Rate. - * @param[in] s Pointer to device. - * @param[in] frequency The requested frequency. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_accel_frequency(struct inv_imu_device *s, const ACCEL_CONFIG0_ODR_t frequency); - -/** @brief Configure gyro Output Data Rate. - * @param[in] s Pointer to device. - * @param[in] frequency The requested frequency. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_gyro_frequency(struct inv_imu_device *s, const GYRO_CONFIG0_ODR_t frequency); - -/** @brief Set accel full scale range. - * @param[in] s Pointer to device. - * @param[in] accel_fsr_g Requested full scale range. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_accel_fsr(struct inv_imu_device *s, ACCEL_CONFIG0_FS_SEL_t accel_fsr_g); - -/** @brief Access accel full scale range. - * @param[in] s Pointer to device. - * @param[out] accel_fsr_g Current full scale range. - * @return 0 on success, negative value on error. - */ -int inv_imu_get_accel_fsr(struct inv_imu_device *s, ACCEL_CONFIG0_FS_SEL_t *accel_fsr_g); - -/** @brief Set gyro full scale range. - * @param[in] s Pointer to device. - * @param[in] gyro_fsr_dps Requested full scale range. -* @return 0 on success, negative value on error. - */ -int inv_imu_set_gyro_fsr(struct inv_imu_device *s, GYRO_CONFIG0_FS_SEL_t gyro_fsr_dps); - -/** @brief Access gyro full scale range. - * @param[in] s Pointer to device. - * @param[out] gyro_fsr_dps Current full scale range. - * @return 0 on success, negative value on error. - */ -int inv_imu_get_gyro_fsr(struct inv_imu_device *s, GYRO_CONFIG0_FS_SEL_t *gyro_fsr_dps); - -/** @brief Set accel Low-Power averaging value. - * @param[in] s Pointer to device. - * @param[in] acc_avg Requested averaging value. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_accel_lp_avg(struct inv_imu_device *s, ACCEL_CONFIG1_ACCEL_FILT_AVG_t acc_avg); - -/** @brief Set accel Low-Noise bandwidth value. - * @param[in] s Pointer to device. - * @param[in] acc_bw Requested averaging value. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_accel_ln_bw(struct inv_imu_device *s, ACCEL_CONFIG1_ACCEL_FILT_BW_t acc_bw); - -/** @brief Set gyro Low-Noise bandwidth value. - * @param[in] s Pointer to device. - * @param[in] gyr_bw Requested averaging value. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_gyro_ln_bw(struct inv_imu_device *s, GYRO_CONFIG1_GYRO_FILT_BW_t gyr_bw); - -/** @brief Set timestamp resolution. - * @param[in] s Pointer to device. - * @param[in] timestamp_resol Requested timestamp resolution. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_timestamp_resolution(struct inv_imu_device * s, - const TMST_CONFIG1_RESOL_t timestamp_resol); - -/** @brief Reset IMU fifo. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_reset_fifo(struct inv_imu_device *s); - -/** @brief Enable 20 bits raw acc and raw gyr data in fifo. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_enable_high_resolution_fifo(struct inv_imu_device *s); - -/** @brief Disable 20 bits raw acc and raw gyr data in fifo. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_disable_high_resolution_fifo(struct inv_imu_device *s); - -/** @brief Configure Fifo. - * @param[in] s Pointer to device. - * @param[in] fifo_config Fifo configuration method. - * Enabled: data are pushed to FIFO and FIFO THS interrupt is set. - * Disabled: data are not pused to FIFO and DRDY interrupt is set. - * @return 0 on success, negative value on error. - */ -int inv_imu_configure_fifo(struct inv_imu_device *s, INV_IMU_FIFO_CONFIG_t fifo_config); - -/** @brief Get timestamp resolution - * @param[in] s Pointer to device. - * @return The timestamp resolution in us or 0 in case of error - */ -uint32_t inv_imu_get_timestamp_resolution_us(struct inv_imu_device *s); - -/** @brief Enable Wake On Motion. - * @param[in] s Pointer to device. - * @param[in] wom_x_th Threshold value for the Wake on Motion Interrupt for X-axis accel. - * @param[in] wom_y_th Threshold value for the Wake on Motion Interrupt for Y-axis accel. - * @param[in] wom_z_th Threshold value for the Wake on Motion Interrupt for Z-axis accel. - * @param[in] wom_int Select which mode between AND/OR is used to generate interrupt. - * @param[in] wom_dur Select the number of overthreshold event to wait - * before generating interrupt. - * @return 0 on success, negative value on error. - */ -int inv_imu_configure_wom(struct inv_imu_device *s, const uint8_t wom_x_th, const uint8_t wom_y_th, - const uint8_t wom_z_th, WOM_CONFIG_WOM_INT_MODE_t wom_int, - WOM_CONFIG_WOM_INT_DUR_t wom_dur); - -/** @brief Enable Wake On Motion. - * WoM requests to have the accelerometer enabled to work. - * As a consequence Fifo water-mark interrupt is disabled to only trigger WoM interrupts. - * To have good performance, it's recommended to set accel ODR to 20ms - * and in Low Power Mode. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_enable_wom(struct inv_imu_device *s); - -/** @brief Disable Wake On Motion. - * Fifo water-mark interrupt is re-enabled when WoM is disabled. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_disable_wom(struct inv_imu_device *s); - -/** @brief Start DMP for APEX algorithms and selftest. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_start_dmp(struct inv_imu_device *s); - -/** @brief Reset DMP for APEX algorithms and selftest. - * @param[in] s Pointer to device. - * @param[in] sram_reset Reset mode for the SRAM. - * @return 0 on success, negative value on error. - */ -int inv_imu_reset_dmp(struct inv_imu_device *s, const APEX_CONFIG0_DMP_MEM_RESET_t sram_reset); - -/** @brief Set the UI endianness and set the inv_device endianness field. - * @param[in] s Pointer to device. - * @param[in] endianness Endianness to be set. - * @return 0 on success, negative value on error. - */ -int inv_imu_set_endianness(struct inv_imu_device *s, INTF_CONFIG0_DATA_ENDIAN_t endianness); - -/** @brief Read the UI endianness and set the inv_device endianness field. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_get_endianness(struct inv_imu_device *s); - -/** @brief Configure Fifo decimation. - * @param[in] s Pointer to device. - * @param[in] dec_factor Requested decimation factor value from 2 to 256. - * @return 0 on success, negative value on error. - */ -int inv_imu_configure_fifo_data_rate(struct inv_imu_device *s, FDR_CONFIG_FDR_SEL_t dec_factor); - -/** @brief Return driver version x.y.z-suffix as a char array - * @return driver version a char array "x.y.z-suffix" - */ -const char *inv_imu_get_version(void); - -#ifdef __cplusplus -} -#endif - -#endif /* _INV_IMU_DRIVER_H_ */ - -/** @} */ diff --git a/lib/ICM42670P/src/imu/inv_imu_extfunc.h b/lib/ICM42670P/src/imu/inv_imu_extfunc.h deleted file mode 100644 index f10e608..0000000 --- a/lib/ICM42670P/src/imu/inv_imu_extfunc.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * ________________________________________________________________________________________________________ - * Copyright (c) 2017 InvenSense Inc. All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * ________________________________________________________________________________________________________ - */ - -/** @defgroup ExtFunc ExtFunc - * @brief Extern functions (to be implemented in application) required by the driver. - * @{ - */ - -/** @file inv_imu_extfunc.h */ - -#ifndef _INV_IMU_EXTFUNC_H_ -#define _INV_IMU_EXTFUNC_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** @brief Sleep function. - * @param[in] us Sleep duration in microseconds (us). - */ -extern void inv_imu_sleep_us(uint32_t us); - -/** @brief Get time function. Value is expected to be on 64bit with a 1 us resolution. - * @return The current time in us. - */ -extern uint64_t inv_imu_get_time_us(void); - -#ifdef __cplusplus -} -#endif - -#endif /* _INV_IMU_EXTFUNC_H_ */ - -/** @} */ diff --git a/lib/ICM42670P/src/imu/inv_imu_regmap.h b/lib/ICM42670P/src/imu/inv_imu_regmap.h deleted file mode 100644 index 954fd6b..0000000 --- a/lib/ICM42670P/src/imu/inv_imu_regmap.h +++ /dev/null @@ -1,3391 +0,0 @@ -/* - *________________________________________________________________________________________________________ - * Copyright (c) 2017 InvenSense Inc. All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense and its licensors intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - OF THE SOFTWARE. - * ________________________________________________________________________________________________________ - */ -#ifndef _INV_IMU_REGMAP_H_ -#define _INV_IMU_REGMAP_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/** @file inv_imu_regmap.h - * File exposing the device register map - */ - -#include - -/* forward declaration */ -struct inv_imu_device; - - - -/* ---------------------------------------------------------------------------- - * Device Register map - * - * Next macros defines address for all registers as listed by device - * datasheet. - * Macros name is with REGISTER_NAME being the name of - * the corresponding register in datasheet. - * Note that macro name is _Bx with x being the bank - * number for registers that are in bank 1 and further (suffix is ommitted for - * bank 0 registers) - * ---------------------------------------------------------------------------- */ - -/* BANK0 */ -#define MCLK_RDY 0x10000 -#define DEVICE_CONFIG 0x10001 -#define SIGNAL_PATH_RESET 0x10002 -#define DRIVE_CONFIG1 0x10003 -#define DRIVE_CONFIG2 0x10004 -#define DRIVE_CONFIG3 0x10005 -#define INT_CONFIG 0x10006 -#define TEMP_DATA1 0x10009 -#define TEMP_DATA0 0x1000a -#define ACCEL_DATA_X1 0x1000b -#define ACCEL_DATA_X0 0x1000c -#define ACCEL_DATA_Y1 0x1000d -#define ACCEL_DATA_Y0 0x1000e -#define ACCEL_DATA_Z1 0x1000f -#define ACCEL_DATA_Z0 0x10010 -#define GYRO_DATA_X1 0x10011 -#define GYRO_DATA_X0 0x10012 -#define GYRO_DATA_Y1 0x10013 -#define GYRO_DATA_Y0 0x10014 -#define GYRO_DATA_Z1 0x10015 -#define GYRO_DATA_Z0 0x10016 -#define TMST_FSYNCH 0x10017 -#define TMST_FSYNCL 0x10018 -#define APEX_DATA4 0x1001d -#define APEX_DATA5 0x1001e -#define PWR_MGMT0 0x1001f -#define GYRO_CONFIG0 0x10020 -#define ACCEL_CONFIG0 0x10021 -#define TEMP_CONFIG0 0x10022 -#define GYRO_CONFIG1 0x10023 -#define ACCEL_CONFIG1 0x10024 -#define APEX_CONFIG0 0x10025 -#define APEX_CONFIG1 0x10026 -#define WOM_CONFIG 0x10027 -#define FIFO_CONFIG1 0x10028 -#define FIFO_CONFIG2 0x10029 -#define FIFO_CONFIG3 0x1002a -#define INT_SOURCE0 0x1002b -#define INT_SOURCE1 0x1002c -#define INT_SOURCE3 0x1002d -#define INT_SOURCE4 0x1002e -#define FIFO_LOST_PKT0 0x1002f -#define FIFO_LOST_PKT1 0x10030 -#define APEX_DATA0 0x10031 -#define APEX_DATA1 0x10032 -#define APEX_DATA2 0x10033 -#define APEX_DATA3 0x10034 -#define INTF_CONFIG0 0x10035 -#define INTF_CONFIG1 0x10036 -#define INT_STATUS_DRDY 0x10039 -#define INT_STATUS 0x1003a -#define INT_STATUS2 0x1003b -#define INT_STATUS3 0x1003c -#define FIFO_COUNTH 0x1003d -#define FIFO_COUNTL 0x1003e -#define FIFO_DATA 0x1003f -#define WHO_AM_I 0x10075 -#define BLK_SEL_W 0x10079 -#define MADDR_W 0x1007a -#define M_W 0x1007b -#define BLK_SEL_R 0x1007c -#define MADDR_R 0x1007d -#define M_R 0x1007e - -/* MREG1 */ -#define TMST_CONFIG1_MREG1 0x00 -#define FIFO_CONFIG5_MREG1 0x01 -#define FIFO_CONFIG6_MREG1 0x02 -#define FSYNC_CONFIG_MREG1 0x03 -#define INT_CONFIG0_MREG1 0x04 -#define INT_CONFIG1_MREG1 0x05 -#define SENSOR_CONFIG3_MREG1 0x06 -#define ST_CONFIG_MREG1 0x13 -#define SELFTEST_MREG1 0x14 -#define INTF_CONFIG6_MREG1 0x23 -#define INTF_CONFIG10_MREG1 0x25 -#define INTF_CONFIG7_MREG1 0x28 -#define OTP_CONFIG_MREG1 0x2b -#define INT_SOURCE6_MREG1 0x2f -#define INT_SOURCE7_MREG1 0x30 -#define INT_SOURCE8_MREG1 0x31 -#define INT_SOURCE9_MREG1 0x32 -#define INT_SOURCE10_MREG1 0x33 -#define APEX_CONFIG2_MREG1 0x44 -#define APEX_CONFIG3_MREG1 0x45 -#define APEX_CONFIG4_MREG1 0x46 -#define APEX_CONFIG5_MREG1 0x47 -#define APEX_CONFIG9_MREG1 0x48 -#define APEX_CONFIG10_MREG1 0x49 -#define APEX_CONFIG11_MREG1 0x4a -#define ACCEL_WOM_X_THR_MREG1 0x4b -#define ACCEL_WOM_Y_THR_MREG1 0x4c -#define ACCEL_WOM_Z_THR_MREG1 0x4d -#define OFFSET_USER0_MREG1 0x4e -#define OFFSET_USER1_MREG1 0x4f -#define OFFSET_USER2_MREG1 0x50 -#define OFFSET_USER3_MREG1 0x51 -#define OFFSET_USER4_MREG1 0x52 -#define OFFSET_USER5_MREG1 0x53 -#define OFFSET_USER6_MREG1 0x54 -#define OFFSET_USER7_MREG1 0x55 -#define OFFSET_USER8_MREG1 0x56 -#define ST_STATUS1_MREG1 0x63 -#define ST_STATUS2_MREG1 0x64 -#define FDR_CONFIG_MREG1 0x66 -#define APEX_CONFIG12_MREG1 0x67 - -/* MREG3 */ -#define XA_ST_DATA_MREG3 0x5000 -#define YA_ST_DATA_MREG3 0x5001 -#define ZA_ST_DATA_MREG3 0x5002 -#define XG_ST_DATA_MREG3 0x5003 -#define YG_ST_DATA_MREG3 0x5004 -#define ZG_ST_DATA_MREG3 0x5005 - -/* MREG2 */ -#define OTP_CTRL7_MREG2 0x2806 - - -/* --------------------------------------------------------------------------- - * register BANK0 - * ---------------------------------------------------------------------------*/ - -/* - * MCLK_RDY - * Register Name : MCLK_RDY - */ - -/* - * mclk_rdy - * 0: Indicates internal clock is currently not running - * 1: Indicates internal clock is currently running - */ -#define MCLK_RDY_MCLK_RDY_POS 0x03 -#define MCLK_RDY_MCLK_RDY_MASK (0x01 << MCLK_RDY_MCLK_RDY_POS) - -/* - * DEVICE_CONFIG - * Register Name : DEVICE_CONFIG - */ - -/* - * spi_ap_4wire - * 0: AP interface uses 3-wire SPI mode - * 1: AP interface uses 4-wire SPI mode - */ -#define DEVICE_CONFIG_SPI_AP_4WIRE_POS 0x02 -#define DEVICE_CONFIG_SPI_AP_4WIRE_MASK (0x01 << DEVICE_CONFIG_SPI_AP_4WIRE_POS) - -/* - * spi_mode - * SPI mode selection - * - * 0: Mode 0 and Mode 3 - * 1: Mode 1 and Mode 2 - * - * If device is operating in non-SPI mode, user is not allowed to change the power-on default setting of this register. Change of this register setting will not take effect till AP_CS = 1. - */ -#define DEVICE_CONFIG_SPI_MODE_POS 0x00 -#define DEVICE_CONFIG_SPI_MODE_MASK 0x01 - - - -/* - * SIGNAL_PATH_RESET - * Register Name : SIGNAL_PATH_RESET - */ - -/* - * soft_reset_device_config - * Software Reset (auto clear bit) - * - * 0: Software reset not enabled - * 1: Software reset enabled - */ -#define SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_POS 0x04 -#define SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_MASK (0x01 << SIGNAL_PATH_RESET_SOFT_RESET_DEVICE_CONFIG_POS) - -/* - * fifo_flush - * When set to 1, FIFO will get flushed. - * FIFO flush requires the following programming sequence: - * • Write FIFO_FLUSH =1 - * • Wait for 1.5 µs - * • Read FIFO_FLUSH, it should now be 0 - * Host can only program this register bit to 1. - */ -#define SIGNAL_PATH_RESET_FIFO_FLUSH_POS 0x02 -#define SIGNAL_PATH_RESET_FIFO_FLUSH_MASK (0x01 << SIGNAL_PATH_RESET_FIFO_FLUSH_POS) - - - -/* - * DRIVE_CONFIG1 - * Register Name : DRIVE_CONFIG1 - */ - -/* - * i3c_ddr_slew_rate - * Controls slew rate for output pin 14 when device is in I3CSM DDR protocol. - * While in I3CSM operation, the device automatically switches to use I3C_DDR_SLEW_RATE after receiving ENTHDR0 ccc command from the host. The device automatically switches back to I3C_SDR_SLEW_RATE after the host issues HDR_EXIT pattern. - * - * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns - * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns - * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns - * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns - * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns - * 101: MAX: 2 ns - * 110: Reserved - * 111: Reserved - * - * This register field should not be programmed in I3C/DDR mode. - */ -#define DRIVE_CONFIG1_I3C_DDR_SLEW_RATE_POS 0x03 -#define DRIVE_CONFIG1_I3C_DDR_SLEW_RATE_MASK (0x07 << DRIVE_CONFIG1_I3C_DDR_SLEW_RATE_POS) - -/* - * i3c_sdr_slew_rate - * Controls slew rate for output pin 14 in I3CSM SDR protocol. - * After device reset, I2C_SLEW_RATE is used by default. If I3CSM feature is enabled, the device automatically switches to use I3C_SDR_SLEW_RATE after receiving 0x7E+W message (an I3CSM broadcast message). - * - * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns - * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns - * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns - * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns - * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns - * 101: MAX: 2 ns - * 110: Reserved - * 111: Reserved - * - * This register field should not be programmed in I3C/DDR mode - */ -#define DRIVE_CONFIG1_I3C_SDR_SLEW_RATE_POS 0x00 -#define DRIVE_CONFIG1_I3C_SDR_SLEW_RATE_MASK 0x07 - - - -/* - * DRIVE_CONFIG2 - * Register Name : DRIVE_CONFIG2 - */ - -/* - * i2c_slew_rate - * Controls slew rate for output pin 14 in I2C mode. - * After device reset, the I2C_SLEW_RATE is used by default. If the 1st write operation from host is an SPI transaction, the device automatically switches to SPI_SLEW_RATE. If I3CSM feature is enabled, the device automatically switches to I3C_SDR_SLEW_RATE after receiving 0x7E+W message (an I3C broadcast message). - * - * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns - * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns - * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns - * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns - * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns - * 101: MAX: 2 ns - * 110: Reserved - * 111: Reserved - * - * This register field should not be programmed in I3C/DDR mode - */ -#define DRIVE_CONFIG2_I2C_SLEW_RATE_POS 0x03 -#define DRIVE_CONFIG2_I2C_SLEW_RATE_MASK (0x07 << DRIVE_CONFIG2_I2C_SLEW_RATE_POS) - -/* - * all_slew_rate - * Configure drive strength for all output pins in all modes (SPI3, SPI4, I2C, I3CSM) excluding pin 14. - * - * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns - * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns - * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns - * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns - * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns - * 101: MAX: 2 ns - * 110: Reserved - * 111: Reserved - * - * This register field should not be programmed in I3C/DDR mode - */ -#define DRIVE_CONFIG2_ALL_SLEW_RATE_POS 0x00 -#define DRIVE_CONFIG2_ALL_SLEW_RATE_MASK 0x07 - - - -/* - * DRIVE_CONFIG3 - * Register Name : DRIVE_CONFIG3 - */ - -/* - * spi_slew_rate - * Controls slew rate for output pin 14 in SPI 3-wire mode. In SPI 4-wire mode this register controls the slew rate of pin 1 as it is used as an output in SPI 4-wire mode only. After chip reset, the I2C_SLEW_RATE is used by default for pin 14 pin. If the 1st write operation from the host is an SPI3/4 transaction, the device automatically switches to SPI_SLEW_RATE. - * - * 000: MIN: 20 ns; TYP: 40 ns; MAX: 60 ns - * 001: MIN: 12 ns; TYP: 24 ns; MAX: 36 ns - * 010: MIN: 6 ns; TYP: 12 ns; MAX: 19 ns - * 011: MIN: 4 ns; TYP: 8 ns; MAX: 14 ns - * 100: MIN: 2 ns; TYP: 4 ns; MAX: 8 ns - * 101: MAX: 2 ns - * 110: Reserved - * 111: Reserved - * - * This register field should not be programmed in I3C/DDR mode - */ -#define DRIVE_CONFIG3_SPI_SLEW_RATE_POS 0x00 -#define DRIVE_CONFIG3_SPI_SLEW_RATE_MASK 0x07 - - - -/* - * INT_CONFIG - * Register Name : INT_CONFIG - */ - -/* - * int2_mode - * Interrupt mode and drive circuit shall be configurable by register. - * Interrupt Mode - * 1: Latched Mode - * 0: Pulsed Mode - */ -#define INT_CONFIG_INT2_MODE_POS 0x05 -#define INT_CONFIG_INT2_MODE_MASK (0x01 << INT_CONFIG_INT2_MODE_POS) - -/* - * int2_drive_circuit - * Interrupt mode and drive circuit shall be configurable by register. - * Drive Circuit - * 1: Push-Pull - * 0: Open drain - */ -#define INT_CONFIG_INT2_DRIVE_CIRCUIT_POS 0x04 -#define INT_CONFIG_INT2_DRIVE_CIRCUIT_MASK (0x01 << INT_CONFIG_INT2_DRIVE_CIRCUIT_POS) - -/* - * int2_polarity - * Interrupt mode and drive circuit shall be configurable by register. - * Interrupt Polarity - * 1: Active High - * 0: Active Low - */ -#define INT_CONFIG_INT2_POLARITY_POS 0x03 -#define INT_CONFIG_INT2_POLARITY_MASK (0x01 << INT_CONFIG_INT2_POLARITY_POS) - -/* - * int1_mode - * Interrupt mode and drive circuit shall be configurable by register. - * Interrupt Mode - * 1: Latched Mode - * 0: Pulsed Mode - */ -#define INT_CONFIG_INT1_MODE_POS 0x02 -#define INT_CONFIG_INT1_MODE_MASK (0x01 << INT_CONFIG_INT1_MODE_POS) - -/* - * int1_drive_circuit - * Interrupt mode and drive circuit shall be configurable by register. - * Drive Circuit - * 1: Push-Pull - * 0: Open drain - */ -#define INT_CONFIG_INT1_DRIVE_CIRCUIT_POS 0x01 -#define INT_CONFIG_INT1_DRIVE_CIRCUIT_MASK (0x01 << INT_CONFIG_INT1_DRIVE_CIRCUIT_POS) - -/* - * int1_polarity - * Interrupt mode and drive circuit shall be configurable by register. - * Interrupt Polarity - * 1: Active High - * 0: Active Low - */ -#define INT_CONFIG_INT1_POLARITY_POS 0x00 -#define INT_CONFIG_INT1_POLARITY_MASK 0x01 - - - -/* - * TEMP_DATA1 - * Register Name : TEMP_DATA1 - */ - -/* - * temp_data - * Temperature data - */ -#define TEMP_DATA1_TEMP_DATA_POS 0x00 -#define TEMP_DATA1_TEMP_DATA_MASK 0xff - - - -/* - * TEMP_DATA0 - * Register Name : TEMP_DATA0 - */ - -/* - * temp_data - * Temperature data - */ -#define TEMP_DATA0_TEMP_DATA_POS 0x00 -#define TEMP_DATA0_TEMP_DATA_MASK 0xff - - - -/* - * ACCEL_DATA_X1 - * Register Name : ACCEL_DATA_X1 - */ - -/* - * accel_data_x - * Accel X axis data - */ -#define ACCEL_DATA_X1_ACCEL_DATA_X_POS 0x00 -#define ACCEL_DATA_X1_ACCEL_DATA_X_MASK 0xff - - - -/* - * ACCEL_DATA_X0 - * Register Name : ACCEL_DATA_X0 - */ - -/* - * accel_data_x - * Accel X axis data - */ -#define ACCEL_DATA_X0_ACCEL_DATA_X_POS 0x00 -#define ACCEL_DATA_X0_ACCEL_DATA_X_MASK 0xff - - - -/* - * ACCEL_DATA_Y1 - * Register Name : ACCEL_DATA_Y1 - */ - -/* - * accel_data_y - * Accel Y axis data - */ -#define ACCEL_DATA_Y1_ACCEL_DATA_Y_POS 0x00 -#define ACCEL_DATA_Y1_ACCEL_DATA_Y_MASK 0xff - - - -/* - * ACCEL_DATA_Y0 - * Register Name : ACCEL_DATA_Y0 - */ - -/* - * accel_data_y - * Accel Y axis data - */ -#define ACCEL_DATA_Y0_ACCEL_DATA_Y_POS 0x00 -#define ACCEL_DATA_Y0_ACCEL_DATA_Y_MASK 0xff - - - -/* - * ACCEL_DATA_Z1 - * Register Name : ACCEL_DATA_Z1 - */ - -/* - * accel_data_z - * Accel Z axis data - */ -#define ACCEL_DATA_Z1_ACCEL_DATA_Z_POS 0x00 -#define ACCEL_DATA_Z1_ACCEL_DATA_Z_MASK 0xff - - - -/* - * ACCEL_DATA_Z0 - * Register Name : ACCEL_DATA_Z0 - */ - -/* - * accel_data_z - * Accel Z axis data - */ -#define ACCEL_DATA_Z0_ACCEL_DATA_Z_POS 0x00 -#define ACCEL_DATA_Z0_ACCEL_DATA_Z_MASK 0xff - - - -/* - * GYRO_DATA_X1 - * Register Name : GYRO_DATA_X1 - */ - -/* - * gyro_data_x - * Gyro X axis data - */ -#define GYRO_DATA_X1_GYRO_DATA_X_POS 0x00 -#define GYRO_DATA_X1_GYRO_DATA_X_MASK 0xff - - - -/* - * GYRO_DATA_X0 - * Register Name : GYRO_DATA_X0 - */ - -/* - * gyro_data_x - * Gyro X axis data - */ -#define GYRO_DATA_X0_GYRO_DATA_X_POS 0x00 -#define GYRO_DATA_X0_GYRO_DATA_X_MASK 0xff - - - -/* - * GYRO_DATA_Y1 - * Register Name : GYRO_DATA_Y1 - */ - -/* - * gyro_data_y - * Gyro Y axis data - */ -#define GYRO_DATA_Y1_GYRO_DATA_Y_POS 0x00 -#define GYRO_DATA_Y1_GYRO_DATA_Y_MASK 0xff - - - -/* - * GYRO_DATA_Y0 - * Register Name : GYRO_DATA_Y0 - */ - -/* - * gyro_data_y - * Gyro Y axis data - */ -#define GYRO_DATA_Y0_GYRO_DATA_Y_POS 0x00 -#define GYRO_DATA_Y0_GYRO_DATA_Y_MASK 0xff - - - -/* - * GYRO_DATA_Z1 - * Register Name : GYRO_DATA_Z1 - */ - -/* - * gyro_data_z - * Gyro Z axis data - */ -#define GYRO_DATA_Z1_GYRO_DATA_Z_POS 0x00 -#define GYRO_DATA_Z1_GYRO_DATA_Z_MASK 0xff - - - -/* - * GYRO_DATA_Z0 - * Register Name : GYRO_DATA_Z0 - */ - -/* - * gyro_data_z - * Gyro Z axis data - */ -#define GYRO_DATA_Z0_GYRO_DATA_Z_POS 0x00 -#define GYRO_DATA_Z0_GYRO_DATA_Z_MASK 0xff - - - -/* - * TMST_FSYNCH - * Register Name : TMST_FSYNCH - */ - -/* - * tmst_fsync_data - * Stores the time delta from the rising edge of FSYNC to the latest ODR until the UI Interface reads the FSYNC tag in the status register - */ -#define TMST_FSYNCH_TMST_FSYNC_DATA_POS 0x00 -#define TMST_FSYNCH_TMST_FSYNC_DATA_MASK 0xff - - - -/* - * TMST_FSYNCL - * Register Name : TMST_FSYNCL - */ - -/* - * tmst_fsync_data - * Stores the time delta from the rising edge of FSYNC to the latest ODR until the UI Interface reads the FSYNC tag in the status register - */ -#define TMST_FSYNCL_TMST_FSYNC_DATA_POS 0x00 -#define TMST_FSYNCL_TMST_FSYNC_DATA_MASK 0xff - - - -/* - * APEX_DATA4 - * Register Name : APEX_DATA4 - */ - -/* - * ff_dur - * Free Fall duration. The duration is given in number of samples and it can be converted to freefall distance by applying the following formula: - * ff_distance = 0.5*9.81*(ff_duration*dmp_odr_s)^2) - * Note: dmp_odr_s in the duration of DMP_ODR expressed in seconds. - */ -#define APEX_DATA4_FF_DUR_POS 0x00 -#define APEX_DATA4_FF_DUR_MASK 0xff - - - -/* - * APEX_DATA5 - * Register Name : APEX_DATA5 - */ - -/* - * ff_dur - * Free Fall duration. The duration is given in number of samples and it can be converted to freefall distance by applying the following formula: - * ff_distance = 0.5*9.81*(ff_duration*dmp_odr_s)^2) - * Note: dmp_odr_s in the duration of DMP_ODR expressed in seconds. - */ -#define APEX_DATA5_FF_DUR_POS 0x00 -#define APEX_DATA5_FF_DUR_MASK 0xff - - - -/* - * PWR_MGMT0 - * Register Name : PWR_MGMT0 - */ - -/* - * accel_lp_clk_sel - * 0: Accelerometer LP mode uses Wake Up oscillator clock. This is the lowest power consumption mode and it is the recommended setting. - * 1: Accelerometer LP mode uses RC oscillator clock. - * - * This field can be changed on-the-fly even if accel sensor is on. - */ -#define PWR_MGMT0_ACCEL_LP_CLK_SEL_POS 0x07 -#define PWR_MGMT0_ACCEL_LP_CLK_SEL_MASK (0x01 << PWR_MGMT0_ACCEL_LP_CLK_SEL_POS) - -/* - * idle - * If this bit is set to 1, the RC oscillator is powered on even if Accel and Gyro are powered off. - * Nominally this bit is set to 0, so when Accel and Gyro are powered off, - * the chip will go to OFF state , since the RC oscillator will also be powered off. - * - * This field can be changed on-the-fly even if a sensor is already on - */ -#define PWR_MGMT0_IDLE_POS 0x04 -#define PWR_MGMT0_IDLE_MASK (0x01 << PWR_MGMT0_IDLE_POS) - -/* - * gyro_mode - * 00: Turns gyroscope off - * 01: Places gyroscope in Standby Mode - * 10: Reserved - * 11: Places gyroscope in Low Noise (LN) Mode - * - * Gyroscope needs to be kept ON for a minimum of 45ms. When transitioning from OFF to any of the other modes, do not issue any register writes for 200 µs. - * - * This field can be changed on-the-fly even if gyro sensor is on - */ -#define PWR_MGMT0_GYRO_MODE_POS 0x02 -#define PWR_MGMT0_GYRO_MODE_MASK (0x03 << PWR_MGMT0_GYRO_MODE_POS) - -/* - * accel_mode - * 00: Turns accelerometer off - * 01: Turns accelerometer off - * 10: Places accelerometer in Low Power (LP) Mode - * 11: Places accelerometer in Low Noise (LN) Mode - * - * When selecting LP Mode please refer to ACCEL_LP_CLK_SEL setting, bit[7] of this register. - * - * Before entering LP mode and during LP Mode the following combinations of ODR and averaging are not permitted: - * 1) ODR=1600 Hz or ODR=800 Hz: any averaging. - * 2) ODR=400 Hz: averaging=16x, 32x or 64x. - * 3) ODR=200 Hz: averaging=64x. - * - * When transitioning from OFF to any of the other modes, do not issue any register writes for 200 µs. - * - * This field can be changed on-the-fly even if accel sensor is on - */ -#define PWR_MGMT0_ACCEL_MODE_POS 0x00 -#define PWR_MGMT0_ACCEL_MODE_MASK 0x03 - - - -/* - * GYRO_CONFIG0 - * Register Name : GYRO_CONFIG0 - */ - -/* - * gyro_ui_fs_sel - * Full scale select for gyroscope UI interface output - * - * 00: ±2000 dps - * 01: ±1000 dps - * 10: ±500 dps - * 11: ±250 dps - * - * This field can be changed on-the-fly even if gyro sensor is on - */ -#define GYRO_CONFIG0_GYRO_UI_FS_SEL_POS 0x05 -#define GYRO_CONFIG0_GYRO_UI_FS_SEL_MASK (0x03 << GYRO_CONFIG0_GYRO_UI_FS_SEL_POS) - -/* - * gyro_odr - * Gyroscope ODR selection for UI interface output - * - * 0000: Reserved - * 0001: Reserved - * 0010: Reserved - * 0011: Reserved - * 0100: Reserved - * 0101: 1.6k Hz - * 0110: 800 Hz - * 0111: 400 Hz - * 1000: 200 Hz - * 1001: 100 Hz - * 1010: 50 Hz - * 1011: 25 Hz - * 1100: 12.5 Hz - * 1101: Reserved - * 1110: Reserved - * 1111: Reserved - * - * This field can be changed on-the-fly even if gyro sensor is on - */ -#define GYRO_CONFIG0_GYRO_ODR_POS 0x00 -#define GYRO_CONFIG0_GYRO_ODR_MASK 0x0f - - - -/* - * ACCEL_CONFIG0 - * Register Name : ACCEL_CONFIG0 - */ - -/* - * accel_ui_fs_sel - * Full scale select for accelerometer UI interface output - * - * 00: ±16g - * 01: ±8g - * 10: ±4g - * 11: ±2g - * - * This field can be changed on-the-fly even if accel sensor is on - */ -#define ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS 0x05 -#define ACCEL_CONFIG0_ACCEL_UI_FS_SEL_MASK (0x03 << ACCEL_CONFIG0_ACCEL_UI_FS_SEL_POS) - -/* - * accel_odr - * Accelerometer ODR selection for UI interface output - * - * 0000: Reserved - * 0001: Reserved - * 0010: Reserved - * 0011: Reserved - * 0100: Reserved - * 0101: 1.6 kHz (LN mode) - * 0110: 800 Hz (LN mode) - * 0111: 400 Hz (LP or LN mode) - * 1000: 200 Hz (LP or LN mode) - * 1001: 100 Hz (LP or LN mode) - * 1010: 50 Hz (LP or LN mode) - * 1011: 25 Hz (LP or LN mode) - * 1100: 12.5 Hz (LP or LN mode) - * 1101: 6.25 Hz (LP mode) - * 1110: 3.125 Hz (LP mode) - * 1111: 1.5625 Hz (LP mode) - * - * This field can be changed on-the-fly when accel sensor is on - */ -#define ACCEL_CONFIG0_ACCEL_ODR_POS 0x00 -#define ACCEL_CONFIG0_ACCEL_ODR_MASK 0x0f - - - -/* - * TEMP_CONFIG0 - * Register Name : TEMP_CONFIG0 - */ - -/* - * temp_filt_bw - * Sets the bandwidth of the temperature signal DLPF - * - * 000: DLPF bypassed - * 001: DLPF BW = 180 Hz - * 010: DLPF BW = 72 Hz - * 011: DLPF BW = 34 Hz - * 100: DLPF BW = 16 Hz - * 101: DLPF BW = 8 Hz - * 110: DLPF BW = 4 Hz - * 111: DLPF BW = 4 Hz - * - * This field can be changed on-the-fly even if sensor is on - */ -#define TEMP_CONFIG0_TEMP_FILT_BW_POS 0x04 -#define TEMP_CONFIG0_TEMP_FILT_BW_MASK (0x07 << TEMP_CONFIG0_TEMP_FILT_BW_POS) - - - -/* - * GYRO_CONFIG1 - * Register Name : GYRO_CONFIG1 - */ - -/* - * gyro_ui_filt_bw - * Selects GYRO UI low pass filter bandwidth - * - * 000: Low pass filter bypassed - * 001: 180 Hz - * 010: 121 Hz - * 011: 73 Hz - * 100: 53 Hz - * 101: 34 Hz - * 110: 25 Hz - * 111: 16 Hz - * - * This field can be changed on-the-fly even if gyro sensor is on - */ -#define GYRO_CONFIG1_GYRO_UI_FILT_BW_POS 0x00 -#define GYRO_CONFIG1_GYRO_UI_FILT_BW_MASK 0x07 - - - -/* - * ACCEL_CONFIG1 - * Register Name : ACCEL_CONFIG1 - */ - -/* - * accel_ui_avg - * Selects averaging filter setting to create accelerometer output in accelerometer low power mode (LPM) - * - * 000: 2x average - * 001: 4x average - * 010: 8x average - * 011: 16x average - * 100: 32x average - * 101: 64x average - * 110: 64x average - * 111: 64x average - * - * This field cannot be changed when the accel sensor is in LPM - */ -#define ACCEL_CONFIG1_ACCEL_UI_AVG_POS 0x04 -#define ACCEL_CONFIG1_ACCEL_UI_AVG_MASK (0x07 << ACCEL_CONFIG1_ACCEL_UI_AVG_POS) - -/* - * accel_ui_filt_bw - * Selects ACCEL UI low pass filter bandwidth - * - * 000: Low pass filter bypassed - * 001: 180 Hz - * 010: 121 Hz - * 011: 73 Hz - * 100: 53 Hz - * 101: 34 Hz - * 110: 25 Hz - * 111: 16 Hz - * - * This field can be changed on-the-fly even if accel sensor is on - */ -#define ACCEL_CONFIG1_ACCEL_UI_FILT_BW_POS 0x00 -#define ACCEL_CONFIG1_ACCEL_UI_FILT_BW_MASK 0x07 - - - -/* - * APEX_CONFIG0 - * Register Name : APEX_CONFIG0 - */ - -/* - * dmp_power_save_en - * When this bit is set to 1, power saving is enabled for DMP algorithms - */ -#define APEX_CONFIG0_DMP_POWER_SAVE_EN_POS 0x03 -#define APEX_CONFIG0_DMP_POWER_SAVE_EN_MASK (0x01 << APEX_CONFIG0_DMP_POWER_SAVE_EN_POS) - -/* - * dmp_init_en - * When this bit is set to 1, DMP runs DMP SW initialization procedure. Bit is reset by hardware when the procedure is finished. All other APEX features are ignored as long as DMP_INIT_EN is set. - * - * This field can be changed on-the-fly even if accel sensor is on. - */ -#define APEX_CONFIG0_DMP_INIT_EN_POS 0x02 -#define APEX_CONFIG0_DMP_INIT_EN_MASK (0x01 << APEX_CONFIG0_DMP_INIT_EN_POS) - -/* - * dmp_mem_reset_en - * When this bit is set to 1, it clears DMP SRAM for APEX operation or Self-test operation. - */ -#define APEX_CONFIG0_DMP_MEM_RESET_EN_POS 0x00 -#define APEX_CONFIG0_DMP_MEM_RESET_EN_MASK 0x03 - - - -/* - * APEX_CONFIG1 - * Register Name : APEX_CONFIG1 - */ - -/* - * smd_enable - * 0: Significant Motion Detection not enabled - * 1: Significant Motion Detection enabled - * - * This field can be changed on-the-fly even if accel sensor is on - */ -#define APEX_CONFIG1_SMD_ENABLE_POS 0x06 -#define APEX_CONFIG1_SMD_ENABLE_MASK (0x01 << APEX_CONFIG1_SMD_ENABLE_POS) - -/* - * ff_enable - * 0: Freefall Detection not enabled - * 1: Freefall Detection enabled - * - * This field can be changed on-the-fly even if accel sensor is on - */ -#define APEX_CONFIG1_FF_ENABLE_POS 0x05 -#define APEX_CONFIG1_FF_ENABLE_MASK (0x01 << APEX_CONFIG1_FF_ENABLE_POS) - -/* - * tilt_enable - * 0: Tilt Detection not enabled - * 1: Tilt Detection enabled - * - * This field can be changed on-the-fly even if accel sensor is on - */ -#define APEX_CONFIG1_TILT_ENABLE_POS 0x04 -#define APEX_CONFIG1_TILT_ENABLE_MASK (0x01 << APEX_CONFIG1_TILT_ENABLE_POS) - -/* - * ped_enable - * 0: Pedometer not enabled - * 1: Pedometer enabled - * - * This field can be changed on-the-fly even if accel sensor is on - */ -#define APEX_CONFIG1_PED_ENABLE_POS 0x03 -#define APEX_CONFIG1_PED_ENABLE_MASK (0x01 << APEX_CONFIG1_PED_ENABLE_POS) - -/* - * dmp_odr - * 00: 25 Hz - * 01: 400 Hz - * 10: 50 Hz - * 11: 100 Hz - * - * The ACCEL_ODR field must be configured to an ODR equal or greater to the DMP_ODR field, for correct device operation. - * - * This field can be changed on-the-fly even if accel sensor is on - */ -#define APEX_CONFIG1_DMP_ODR_POS 0x00 -#define APEX_CONFIG1_DMP_ODR_MASK 0x03 - - - -/* - * WOM_CONFIG - * Register Name : WOM_CONFIG - */ - -/* - * wom_int_dur - * Selects Wake on Motion interrupt assertion from among the following options - * - * 00: WoM interrupt asserted at first overthreshold event - * 01: WoM interrupt asserted at second overthreshold event - * 10: WoM interrupt asserted at third overthreshold event - * 11: WoM interrupt asserted at fourth overthreshold event - * - * This field can be changed on-the-fly even if accel sensor is on, but it cannot be changed if WOM_EN is already enabled - */ -#define WOM_CONFIG_WOM_INT_DUR_POS 0x03 -#define WOM_CONFIG_WOM_INT_DUR_MASK (0x03 << WOM_CONFIG_WOM_INT_DUR_POS) - -/* - * wom_int_mode - * 0: Set WoM interrupt on the OR of all enabled accelerometer thresholds - * 1: Set WoM interrupt on the AND of all enabled accelerometer thresholds - * - * This field can be changed on-the-fly even if accel sensor is on, but it cannot be changed if WOM_EN is already enabled - */ -#define WOM_CONFIG_WOM_INT_MODE_POS 0x02 -#define WOM_CONFIG_WOM_INT_MODE_MASK (0x01 << WOM_CONFIG_WOM_INT_MODE_POS) - -/* - * wom_mode - * 0 - Initial sample is stored. Future samples are compared to initial sample - * 1 - Compare current sample to previous sample - * - * This field can be changed on-the-fly even if accel sensor is already on, but it cannot be changed if wom_en is already enabled. - */ -#define WOM_CONFIG_WOM_MODE_POS 0x01 -#define WOM_CONFIG_WOM_MODE_MASK (0x01 << WOM_CONFIG_WOM_MODE_POS) - -/* - * wom_en - * 1: enable wake-on-motion detection. - * 0: disable wake-on-motion detection. - * - * This field can be changed on-the-fly even if accel sensor is already on. - */ -#define WOM_CONFIG_WOM_EN_POS 0x00 -#define WOM_CONFIG_WOM_EN_MASK 0x01 - - - -/* - * FIFO_CONFIG1 - * Register Name : FIFO_CONFIG1 - */ - -/* - * fifo_mode - * FIFO mode control - * - * 0: Stream-to-FIFO Mode - * 1: STOP-on-FULL Mode - */ -#define FIFO_CONFIG1_FIFO_MODE_POS 0x01 -#define FIFO_CONFIG1_FIFO_MODE_MASK (0x01 << FIFO_CONFIG1_FIFO_MODE_POS) - -/* - * fifo_bypass - * FIFO bypass control - * 0: FIFO is not bypassed - * 1: FIFO is bypassed - */ -#define FIFO_CONFIG1_FIFO_BYPASS_POS 0x00 -#define FIFO_CONFIG1_FIFO_BYPASS_MASK 0x01 - - - -/* - * FIFO_CONFIG2 - * Register Name : FIFO_CONFIG2 - */ - -/* - * fifo_wm - * FIFO watermark. Generate interrupt when the FIFO reaches or exceeds FIFO_WM size in bytes or records according to FIFO_COUNT_FORMAT setting. FIFO_WM_EN must be zero before writing this register. Interrupt only fires once. This register should be set to non-zero value, before choosing this interrupt source. - * - * This field should be changed when FIFO is empty to avoid spurious interrupts. - */ -#define FIFO_CONFIG2_FIFO_WM_POS 0x00 -#define FIFO_CONFIG2_FIFO_WM_MASK 0xff - - - -/* - * FIFO_CONFIG3 - * Register Name : FIFO_CONFIG3 - */ - -/* - * fifo_wm - * FIFO watermark. Generate interrupt when the FIFO reaches or exceeds FIFO_WM size in bytes or records according to FIFO_COUNT_FORMAT setting. FIFO_WM_EN must be zero before writing this register. Interrupt only fires once. This register should be set to non-zero value, before choosing this interrupt source. - * - * This field should be changed when FIFO is empty to avoid spurious interrupts. - */ -#define FIFO_CONFIG3_FIFO_WM_POS 0x00 -#define FIFO_CONFIG3_FIFO_WM_MASK 0x0f - - - -/* - * INT_SOURCE0 - * Register Name : INT_SOURCE0 - */ - -/* - * st_int1_en - * 0: Self-Test Done interrupt not routed to INT1 - * 1: Self-Test Done interrupt routed to INT1 - */ -#define INT_SOURCE0_ST_INT1_EN_POS 0x07 -#define INT_SOURCE0_ST_INT1_EN_MASK (0x01 << INT_SOURCE0_ST_INT1_EN_POS) - -/* - * fsync_int1_en - * 0: FSYNC interrupt not routed to INT1 - * 1: FSYNC interrupt routed to INT1 - */ -#define INT_SOURCE0_FSYNC_INT1_EN_POS 0x06 -#define INT_SOURCE0_FSYNC_INT1_EN_MASK (0x01 << INT_SOURCE0_FSYNC_INT1_EN_POS) - -/* - * pll_rdy_int1_en - * 0: PLL ready interrupt not routed to INT1 - * 1: PLL ready interrupt routed to INT1 - */ -#define INT_SOURCE0_PLL_RDY_INT1_EN_POS 0x05 -#define INT_SOURCE0_PLL_RDY_INT1_EN_MASK (0x01 << INT_SOURCE0_PLL_RDY_INT1_EN_POS) - -/* - * reset_done_int1_en - * 0: Reset done interrupt not routed to INT1 - * 1: Reset done interrupt routed to INT1 - */ -#define INT_SOURCE0_RESET_DONE_INT1_EN_POS 0x04 -#define INT_SOURCE0_RESET_DONE_INT1_EN_MASK (0x01 << INT_SOURCE0_RESET_DONE_INT1_EN_POS) - -/* - * drdy_int1_en - * 0: Data Ready interrupt not routed to INT1 - * 1: Data Ready interrupt routed to INT1 - */ -#define INT_SOURCE0_DRDY_INT1_EN_POS 0x03 -#define INT_SOURCE0_DRDY_INT1_EN_MASK (0x01 << INT_SOURCE0_DRDY_INT1_EN_POS) - -/* - * fifo_ths_int1_en - * 0: FIFO threshold interrupt not routed to INT1 - * 1: FIFO threshold interrupt routed to INT1 - */ -#define INT_SOURCE0_FIFO_THS_INT1_EN_POS 0x02 -#define INT_SOURCE0_FIFO_THS_INT1_EN_MASK (0x01 << INT_SOURCE0_FIFO_THS_INT1_EN_POS) - -/* - * fifo_full_int1_en - * 0: FIFO full interrupt not routed to INT1 - * 1: FIFO full interrupt routed to INT1 - * To avoid FIFO FULL interrupts while reading FIFO, this bit should be disabled while reading FIFO - */ -#define INT_SOURCE0_FIFO_FULL_INT1_EN_POS 0x01 -#define INT_SOURCE0_FIFO_FULL_INT1_EN_MASK (0x01 << INT_SOURCE0_FIFO_FULL_INT1_EN_POS) - -/* - * agc_rdy_int1_en - * 0: UI AGC ready interrupt not routed to INT1 - * 1: UI AGC ready interrupt routed to INT1 - */ -#define INT_SOURCE0_AGC_RDY_INT1_EN_POS 0x00 -#define INT_SOURCE0_AGC_RDY_INT1_EN_MASK 0x01 - - - -/* - * INT_SOURCE1 - * Register Name : INT_SOURCE1 - */ - -/* - * i3c_protocol_error_int1_en - * 0: I3CSM protocol error interrupt not routed to INT1 - * 1: I3CSM protocol error interrupt routed to INT1 - */ -#define INT_SOURCE1_I3C_PROTOCOL_ERROR_INT1_EN_POS 0x06 -#define INT_SOURCE1_I3C_PROTOCOL_ERROR_INT1_EN_MASK (0x01 << INT_SOURCE1_I3C_PROTOCOL_ERROR_INT1_EN_POS) - -/* - * smd_int1_en - * 0: SMD interrupt not routed to INT1 - * 1: SMD interrupt routed to INT1 - */ -#define INT_SOURCE1_SMD_INT1_EN_POS 0x03 -#define INT_SOURCE1_SMD_INT1_EN_MASK (0x01 << INT_SOURCE1_SMD_INT1_EN_POS) - -/* - * wom_z_int1_en - * 0: Z-axis WOM interrupt not routed to INT1 - * 1: Z-axis WOM interrupt routed to INT1 - */ -#define INT_SOURCE1_WOM_Z_INT1_EN_POS 0x02 -#define INT_SOURCE1_WOM_Z_INT1_EN_MASK (0x01 << INT_SOURCE1_WOM_Z_INT1_EN_POS) - -/* - * wom_y_int1_en - * 0: Y-axis WOM interrupt not routed to INT1 - * 1: Y-axis WOM interrupt routed to INT1 - */ -#define INT_SOURCE1_WOM_Y_INT1_EN_POS 0x01 -#define INT_SOURCE1_WOM_Y_INT1_EN_MASK (0x01 << INT_SOURCE1_WOM_Y_INT1_EN_POS) - -/* - * wom_x_int1_en - * 0: X-axis WOM interrupt not routed to INT1 - * 1: X-axis WOM interrupt routed to INT1 - */ -#define INT_SOURCE1_WOM_X_INT1_EN_POS 0x00 -#define INT_SOURCE1_WOM_X_INT1_EN_MASK 0x01 - - - -/* - * INT_SOURCE3 - * Register Name : INT_SOURCE3 - */ - -/* - * st_int2_en - * 0: Self-Test Done interrupt not routed to INT2 - * 1: Self-Test Done interrupt routed to INT2 - */ -#define INT_SOURCE3_ST_INT2_EN_POS 0x07 -#define INT_SOURCE3_ST_INT2_EN_MASK (0x01 << INT_SOURCE3_ST_INT2_EN_POS) - -/* - * fsync_int2_en - * 0: FSYNC interrupt not routed to INT2 - * 1: FSYNC interrupt routed to INT2 - */ -#define INT_SOURCE3_FSYNC_INT2_EN_POS 0x06 -#define INT_SOURCE3_FSYNC_INT2_EN_MASK (0x01 << INT_SOURCE3_FSYNC_INT2_EN_POS) - -/* - * pll_rdy_int2_en - * 0: PLL ready interrupt not routed to INT2 - * 1: PLL ready interrupt routed to INT2 - */ -#define INT_SOURCE3_PLL_RDY_INT2_EN_POS 0x05 -#define INT_SOURCE3_PLL_RDY_INT2_EN_MASK (0x01 << INT_SOURCE3_PLL_RDY_INT2_EN_POS) - -/* - * reset_done_int2_en - * 0: Reset done interrupt not routed to INT2 - * 1: Reset done interrupt routed to INT2 - */ -#define INT_SOURCE3_RESET_DONE_INT2_EN_POS 0x04 -#define INT_SOURCE3_RESET_DONE_INT2_EN_MASK (0x01 << INT_SOURCE3_RESET_DONE_INT2_EN_POS) - -/* - * drdy_int2_en - * 0: Data Ready interrupt not routed to INT2 - * 1: Data Ready interrupt routed to INT2 - */ -#define INT_SOURCE3_DRDY_INT2_EN_POS 0x03 -#define INT_SOURCE3_DRDY_INT2_EN_MASK (0x01 << INT_SOURCE3_DRDY_INT2_EN_POS) - -/* - * fifo_ths_int2_en - * 0: FIFO threshold interrupt not routed to INT2 - * 1: FIFO threshold interrupt routed to INT2 - */ -#define INT_SOURCE3_FIFO_THS_INT2_EN_POS 0x02 -#define INT_SOURCE3_FIFO_THS_INT2_EN_MASK (0x01 << INT_SOURCE3_FIFO_THS_INT2_EN_POS) - -/* - * fifo_full_int2_en - * 0: FIFO full interrupt not routed to INT2 - * 1: FIFO full interrupt routed to INT2 - */ -#define INT_SOURCE3_FIFO_FULL_INT2_EN_POS 0x01 -#define INT_SOURCE3_FIFO_FULL_INT2_EN_MASK (0x01 << INT_SOURCE3_FIFO_FULL_INT2_EN_POS) - -/* - * agc_rdy_int2_en - * 0: AGC ready interrupt not routed to INT2 - * 1: AGC ready interrupt routed to INT2 - */ -#define INT_SOURCE3_AGC_RDY_INT2_EN_POS 0x00 -#define INT_SOURCE3_AGC_RDY_INT2_EN_MASK 0x01 - - - -/* - * INT_SOURCE4 - * Register Name : INT_SOURCE4 - */ - -/* - * i3c_protocol_error_int2_en - * 0: I3CSM protocol error interrupt not routed to INT2 - * 1: I3CSM protocol error interrupt routed to INT2 - */ -#define INT_SOURCE4_I3C_PROTOCOL_ERROR_INT2_EN_POS 0x06 -#define INT_SOURCE4_I3C_PROTOCOL_ERROR_INT2_EN_MASK (0x01 << INT_SOURCE4_I3C_PROTOCOL_ERROR_INT2_EN_POS) - -/* - * smd_int2_en - * 0: SMD interrupt not routed to INT2 - * 1: SMD interrupt routed to INT2 - */ -#define INT_SOURCE4_SMD_INT2_EN_POS 0x03 -#define INT_SOURCE4_SMD_INT2_EN_MASK (0x01 << INT_SOURCE4_SMD_INT2_EN_POS) - -/* - * wom_z_int2_en - * 0: Z-axis WOM interrupt not routed to INT2 - * 1: Z-axis WOM interrupt routed to INT2 - */ -#define INT_SOURCE4_WOM_Z_INT2_EN_POS 0x02 -#define INT_SOURCE4_WOM_Z_INT2_EN_MASK (0x01 << INT_SOURCE4_WOM_Z_INT2_EN_POS) - -/* - * wom_y_int2_en - * 0: Y-axis WOM interrupt not routed to INT2 - * 1: Y-axis WOM interrupt routed to INT2 - */ -#define INT_SOURCE4_WOM_Y_INT2_EN_POS 0x01 -#define INT_SOURCE4_WOM_Y_INT2_EN_MASK (0x01 << INT_SOURCE4_WOM_Y_INT2_EN_POS) - -/* - * wom_x_int2_en - * 0: X-axis WOM interrupt not routed to INT2 - * 1: X-axis WOM interrupt routed to INT2 - */ -#define INT_SOURCE4_WOM_X_INT2_EN_POS 0x00 -#define INT_SOURCE4_WOM_X_INT2_EN_MASK 0x01 - - - -/* - * FIFO_LOST_PKT0 - * Register Name : FIFO_LOST_PKT0 - */ - -/* - * fifo_lost_pkt_cnt - * Stores the number of packets lost in the FIFO - */ -#define FIFO_LOST_PKT0_FIFO_LOST_PKT_CNT_POS 0x00 -#define FIFO_LOST_PKT0_FIFO_LOST_PKT_CNT_MASK 0xff - - - -/* - * FIFO_LOST_PKT1 - * Register Name : FIFO_LOST_PKT1 - */ - -/* - * fifo_lost_pkt_cnt - * Stores the number of packets lost in the FIFO - */ -#define FIFO_LOST_PKT1_FIFO_LOST_PKT_CNT_POS 0x00 -#define FIFO_LOST_PKT1_FIFO_LOST_PKT_CNT_MASK 0xff - - - -/* - * APEX_DATA0 - * Register Name : APEX_DATA0 - */ - -/* - * step_cnt - * This status register indicates number of step taken. - */ -#define APEX_DATA0_STEP_CNT_POS 0x00 -#define APEX_DATA0_STEP_CNT_MASK 0xff - - - -/* - * APEX_DATA1 - * Register Name : APEX_DATA1 - */ - -/* - * step_cnt - * This status register indicates number of step taken. - */ -#define APEX_DATA1_STEP_CNT_POS 0x00 -#define APEX_DATA1_STEP_CNT_MASK 0xff - - - -/* - * APEX_DATA2 - * Register Name : APEX_DATA2 - */ - -/* - * step_cadence - * Pedometer step cadence.Walk/run cadency in number of samples. Format is u6.2. - * E.g, At 50Hz and 2Hz walk frequency, the cadency is 25 samples. The register will output 100. - */ -#define APEX_DATA2_STEP_CADENCE_POS 0x00 -#define APEX_DATA2_STEP_CADENCE_MASK 0xff - - - -/* - * APEX_DATA3 - * Register Name : APEX_DATA3 - */ - -/* - * dmp_idle - * 0: Indicates DMP is running - * 1: Indicates DMP is idle - */ -#define APEX_DATA3_DMP_IDLE_POS 0x02 -#define APEX_DATA3_DMP_IDLE_MASK (0x01 << APEX_DATA3_DMP_IDLE_POS) - -/* - * activity_class - * Pedometer Output: Detected activity - * - * 00: Unknown - * 01: Walk - * 10: Run - * 11: Reserved - */ -#define APEX_DATA3_ACTIVITY_CLASS_POS 0x00 -#define APEX_DATA3_ACTIVITY_CLASS_MASK 0x03 - - - -/* - * INTF_CONFIG0 - * Register Name : INTF_CONFIG0 - */ - -/* - * fifo_count_format - * 0: FIFO count is reported in bytes - * 1: FIFO count is reported in records (1 record = 16 bytes for header + gyro + accel + temp sensor data + time stamp, or 8 bytes for header + gyro/accel + temp sensor data) - */ -#define INTF_CONFIG0_FIFO_COUNT_FORMAT_POS 0x06 -#define INTF_CONFIG0_FIFO_COUNT_FORMAT_MASK (0x01 << INTF_CONFIG0_FIFO_COUNT_FORMAT_POS) - -/* - * fifo_count_endian - * This bit applies to both fifo_count and lost_pkt_count - * 0 : Little Endian (The LSByte data is read first, followed by MSByte data). - * 1 : Big Endian (The MSByte data is read first, followed by LSByte data). - */ -#define INTF_CONFIG0_FIFO_COUNT_ENDIAN_POS 0x05 -#define INTF_CONFIG0_FIFO_COUNT_ENDIAN_MASK (0x01 << INTF_CONFIG0_FIFO_COUNT_ENDIAN_POS) - -/* - * sensor_data_endian - * This bit applies to sensor data to AP, and fifo data. - * 0 : Little Endian (The LSByte data is read first, followed by MSByte data). - * 1 : Big Endian (The MSByte data is read first, followed by LSByte data). - */ -#define INTF_CONFIG0_SENSOR_DATA_ENDIAN_POS 0x04 -#define INTF_CONFIG0_SENSOR_DATA_ENDIAN_MASK (0x01 << INTF_CONFIG0_SENSOR_DATA_ENDIAN_POS) - -/* - * INTF_CONFIG1 - * Register Name : INTF_CONFIG1 - */ - -/* - * i3c_sdr_en - * 0: I3CSM SDR mode not enabled - * 1: I3CSM SDR mode enabled - * - * Device will be in pure I2C mode if {I3C_SDR_EN, I3C_DDR_EN} = 00 - */ -#define INTF_CONFIG1_I3C_SDR_EN_POS 0x03 -#define INTF_CONFIG1_I3C_SDR_EN_MASK (0x01 << INTF_CONFIG1_I3C_SDR_EN_POS) - -/* - * i3c_ddr_en - * 0: I3CSM DDR mode not enabled - * 1: I3CSM DDR mode enabled - * - * This bit will not take effect unless I3C_SDR_EN = 1. - */ -#define INTF_CONFIG1_I3C_DDR_EN_POS 0x02 -#define INTF_CONFIG1_I3C_DDR_EN_MASK (0x01 << INTF_CONFIG1_I3C_DDR_EN_POS) - -/* - * clksel - * 00 Alway select internal RC oscillator - * 01 Select PLL when available, else select RC oscillator (default) - * 10 (Reserved) - * 11 Disable all clocks - */ -#define INTF_CONFIG1_CLKSEL_POS 0x00 -#define INTF_CONFIG1_CLKSEL_MASK 0x03 - - - -/* - * INT_STATUS_DRDY - * Register Name : INT_STATUS_DRDY - */ - -/* - * data_rdy_int - * This bit automatically sets to 1 when a Data Ready interrupt is generated. The bit clears to 0 after the register has been read. - */ -#define INT_STATUS_DRDY_DATA_RDY_INT_POS 0x00 -#define INT_STATUS_DRDY_DATA_RDY_INT_MASK 0x01 - - - -/* - * INT_STATUS - * Register Name : INT_STATUS - */ - -/* - * st_int - * This bit automatically sets to 1 when a Self Test done interrupt is generated. The bit clears to 0 after the register has been read. - */ -#define INT_STATUS_ST_INT_POS 0x07 -#define INT_STATUS_ST_INT_MASK (0x01 << INT_STATUS_ST_INT_POS) - -/* - * fsync_int - * This bit automatically sets to 1 when an FSYNC interrupt is generated. The bit clears to 0 after the register has been read. - */ -#define INT_STATUS_FSYNC_INT_POS 0x06 -#define INT_STATUS_FSYNC_INT_MASK (0x01 << INT_STATUS_FSYNC_INT_POS) - -/* - * pll_rdy_int - * This bit automatically sets to 1 when a PLL Ready interrupt is generated. The bit clears to 0 after the register has been read. - */ -#define INT_STATUS_PLL_RDY_INT_POS 0x05 -#define INT_STATUS_PLL_RDY_INT_MASK (0x01 << INT_STATUS_PLL_RDY_INT_POS) - -/* - * reset_done_int - * This bit automatically sets to 1 when software reset is complete. The bit clears to 0 after the register has been read. - */ -#define INT_STATUS_RESET_DONE_INT_POS 0x04 -#define INT_STATUS_RESET_DONE_INT_MASK (0x01 << INT_STATUS_RESET_DONE_INT_POS) - -/* - * fifo_ths_int - * This bit automatically sets to 1 when the FIFO buffer reaches the threshold value. The bit clears to 0 after the register has been read. - */ -#define INT_STATUS_FIFO_THS_INT_POS 0x02 -#define INT_STATUS_FIFO_THS_INT_MASK (0x01 << INT_STATUS_FIFO_THS_INT_POS) - -/* - * fifo_full_int - * This bit automatically sets to 1 when the FIFO buffer is full. The bit clears to 0 after the register has been read. - */ -#define INT_STATUS_FIFO_FULL_INT_POS 0x01 -#define INT_STATUS_FIFO_FULL_INT_MASK (0x01 << INT_STATUS_FIFO_FULL_INT_POS) - -/* - * agc_rdy_int - * This bit automatically sets to 1 when an AGC Ready interrupt is generated. The bit clears to 0 after the register has been read. - */ -#define INT_STATUS_AGC_RDY_INT_POS 0x00 -#define INT_STATUS_AGC_RDY_INT_MASK 0x01 - - - -/* - * INT_STATUS2 - * Register Name : INT_STATUS2 - */ - -/* - * smd_int - * Significant Motion Detection Interrupt, clears on read - */ -#define INT_STATUS2_SMD_INT_POS 0x03 -#define INT_STATUS2_SMD_INT_MASK (0x01 << INT_STATUS2_SMD_INT_POS) - -/* - * wom_x_int - * Wake on Motion Interrupt on X-axis, clears on read - */ -#define INT_STATUS2_WOM_X_INT_POS 0x02 -#define INT_STATUS2_WOM_X_INT_MASK (0x01 << INT_STATUS2_WOM_X_INT_POS) - -/* - * wom_y_int - * Wake on Motion Interrupt on Y-axis, clears on read - */ -#define INT_STATUS2_WOM_Y_INT_POS 0x01 -#define INT_STATUS2_WOM_Y_INT_MASK (0x01 << INT_STATUS2_WOM_Y_INT_POS) - -/* - * wom_z_int - * Wake on Motion Interrupt on Z-axis, clears on read - */ -#define INT_STATUS2_WOM_Z_INT_POS 0x00 -#define INT_STATUS2_WOM_Z_INT_MASK 0x01 - - - -/* - * INT_STATUS3 - * Register Name : INT_STATUS3 - */ - -/* - * step_det_int - * Step Detection Interrupt, clears on read - */ -#define INT_STATUS3_STEP_DET_INT_POS 0x05 -#define INT_STATUS3_STEP_DET_INT_MASK (0x01 << INT_STATUS3_STEP_DET_INT_POS) - -/* - * step_cnt_ovf_int - * Step Count Overflow Interrupt, clears on read - */ -#define INT_STATUS3_STEP_CNT_OVF_INT_POS 0x04 -#define INT_STATUS3_STEP_CNT_OVF_INT_MASK (0x01 << INT_STATUS3_STEP_CNT_OVF_INT_POS) - -/* - * tilt_det_int - * Tilt Detection Interrupt, clears on read - */ -#define INT_STATUS3_TILT_DET_INT_POS 0x03 -#define INT_STATUS3_TILT_DET_INT_MASK (0x01 << INT_STATUS3_TILT_DET_INT_POS) - -/* - * ff_det_int - * Freefall Interrupt, clears on read - */ -#define INT_STATUS3_FF_DET_INT_POS 0x02 -#define INT_STATUS3_FF_DET_INT_MASK (0x01 << INT_STATUS3_FF_DET_INT_POS) - -/* - * lowg_det_int - * LowG Interrupt, clears on read - */ -#define INT_STATUS3_LOWG_DET_INT_POS 0x01 -#define INT_STATUS3_LOWG_DET_INT_MASK (0x01 << INT_STATUS3_LOWG_DET_INT_POS) - - - -/* - * FIFO_COUNTH - * Register Name : FIFO_COUNTH - */ - -/* - * fifo_count - * Number of bytes in FIFO when fifo_count_format=0. - * Number of records in FIFO when fifo_count_format=1. - */ -#define FIFO_COUNTH_FIFO_COUNT_POS 0x00 -#define FIFO_COUNTH_FIFO_COUNT_MASK 0xff - - - -/* - * FIFO_COUNTL - * Register Name : FIFO_COUNTL - */ - -/* - * fifo_count - * Number of bytes in FIFO when fifo_count_format=0. - * Number of records in FIFO when fifo_count_format=1. - */ -#define FIFO_COUNTL_FIFO_COUNT_POS 0x00 -#define FIFO_COUNTL_FIFO_COUNT_MASK 0xff - - - -/* - * FIFO_DATA - * Register Name : FIFO_DATA - */ - -/* - * fifo_data - * FIFO data port - */ -#define FIFO_DATA_FIFO_DATA_POS 0x00 -#define FIFO_DATA_FIFO_DATA_MASK 0xff - - - -/* - * WHO_AM_I - * Register Name : WHO_AM_I - */ - -/* - * whoami - * Register to indicate to user which device is being accessed - */ -#define WHO_AM_I_WHOAMI_POS 0x00 -#define WHO_AM_I_WHOAMI_MASK 0xff - - - -/* - * BLK_SEL_W - * Register Name : BLK_SEL_W - */ - -/* - * blk_sel_w - * For write operation, select a 256-byte MCLK space, or 128-byte SCLK space. - * Automatically reset when OTP copy operation is triggered. - */ -#define BLK_SEL_W_BLK_SEL_W_POS 0x00 -#define BLK_SEL_W_BLK_SEL_W_MASK 0xff - - - -/* - * MADDR_W - * Register Name : MADDR_W - */ - -/* - * maddr_w - * For MREG write operation, the lower 8-bit address for accessing MCLK domain registers. - */ -#define MADDR_W_MADDR_W_POS 0x00 -#define MADDR_W_MADDR_W_MASK 0xff - - - -/* - * M_W - * Register Name : M_W - */ - -/* - * m_w - * For MREG write operation, the write port for accessing MCLK domain registers. - */ -#define M_W_M_W_POS 0x00 -#define M_W_M_W_MASK 0xff - - - -/* - * BLK_SEL_R - * Register Name : BLK_SEL_R - */ - -/* - * blk_sel_r - * For read operation, select a 256-byte MCLK space, or 128-byte SCLK space. - * Automatically reset when OTP copy operation is triggered. - */ -#define BLK_SEL_R_BLK_SEL_R_POS 0x00 -#define BLK_SEL_R_BLK_SEL_R_MASK 0xff - - - -/* - * MADDR_R - * Register Name : MADDR_R - */ - -/* - * maddr_r - * For MREG read operation, the lower 8-bit address for accessing MCLK domain registers. - */ -#define MADDR_R_MADDR_R_POS 0x00 -#define MADDR_R_MADDR_R_MASK 0xff - - - -/* - * M_R - * Register Name : M_R - */ - -/* - * m_r - * For MREG read operation, the read port for accessing MCLK domain registers. - */ -#define M_R_M_R_POS 0x00 -#define M_R_M_R_MASK 0xff - - -/* --------------------------------------------------------------------------- - * register MREG1 - * ---------------------------------------------------------------------------*/ - -/* - * TMST_CONFIG1 - * Register Name : TMST_CONFIG1 - */ - -/* - * tmst_res - * Time Stamp resolution; When set to 0 (default), time stamp resolution is 1 us. When set to 1, resolution is 16us - */ -#define TMST_CONFIG1_TMST_RES_POS 0x03 -#define TMST_CONFIG1_TMST_RES_MASK (0x01 << TMST_CONFIG1_TMST_RES_POS) - -/* - * tmst_delta_en - * Time Stamp delta Enable : When set to 1, the Time stamp field contains the measurement of time since the last occurrence of ODR. - */ -#define TMST_CONFIG1_TMST_DELTA_EN_POS 0x02 -#define TMST_CONFIG1_TMST_DELTA_EN_MASK (0x01 << TMST_CONFIG1_TMST_DELTA_EN_POS) - -/* - * tmst_fsync_en - * Time Stamp register Fsync Enable . When set to 1, the contents of the Timestamp feature of FSYNC is enabled. The user also needs to select fifo_tmst_fsync_en in order to propagate the timestamp value to the FIFO - */ -#define TMST_CONFIG1_TMST_FSYNC_EN_POS 0x01 -#define TMST_CONFIG1_TMST_FSYNC_EN_MASK (0x01 << TMST_CONFIG1_TMST_FSYNC_EN_POS) - -/* - * tmst_en - * Time Stamp register Enable - */ -#define TMST_CONFIG1_TMST_EN_POS 0x00 -#define TMST_CONFIG1_TMST_EN_MASK 0x01 - - - -/* - * FIFO_CONFIG5 - * Register Name : FIFO_CONFIG5 - */ - -/* - * fifo_wm_gt_th - * 1: trigger FIFO-Watermark interrupt on every ODR(DMA Write) if FIFO_COUNT: =FIFO_WM - * - * 0: Trigger FIFO-Watermark interrupt when FIFO_COUNT == FIFO_WM - */ -#define FIFO_CONFIG5_FIFO_WM_GT_TH_POS 0x05 -#define FIFO_CONFIG5_FIFO_WM_GT_TH_MASK (0x01 << FIFO_CONFIG5_FIFO_WM_GT_TH_POS) - -/* - * fifo_resume_partial_rd - * 0: FIFO is read in packets. If a partial packet is read, then the subsequent read will start from the beginning of the un-read packet. - * 1: FIFO can be read partially. When read is resumed, FIFO bytes will continue from last read point. The SW driver is responsible for cascading previous read and present read and maintain frame boundaries. - */ -#define FIFO_CONFIG5_FIFO_RESUME_PARTIAL_RD_POS 0x04 -#define FIFO_CONFIG5_FIFO_RESUME_PARTIAL_RD_MASK (0x01 << FIFO_CONFIG5_FIFO_RESUME_PARTIAL_RD_POS) - -/* - * fifo_hires_en - * Allows 20 bit resolution in the FIFO packet readout - */ -#define FIFO_CONFIG5_FIFO_HIRES_EN_POS 0x03 -#define FIFO_CONFIG5_FIFO_HIRES_EN_MASK (0x01 << FIFO_CONFIG5_FIFO_HIRES_EN_POS) - -/* - * fifo_tmst_fsync_en - * Allows the TMST in the FIFO to be replaced by the FSYNC timestamp - */ -#define FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_POS 0x02 -#define FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_MASK (0x01 << FIFO_CONFIG5_FIFO_TMST_FSYNC_EN_POS) - -/* - * fifo_gyro_en - * Enables Gyro Packets to go to FIFO - */ -#define FIFO_CONFIG5_FIFO_GYRO_EN_POS 0x01 -#define FIFO_CONFIG5_FIFO_GYRO_EN_MASK (0x01 << FIFO_CONFIG5_FIFO_GYRO_EN_POS) - -/* - * fifo_accel_en - * Enable Accel Packets to go to FIFO - */ -#define FIFO_CONFIG5_FIFO_ACCEL_EN_POS 0x00 -#define FIFO_CONFIG5_FIFO_ACCEL_EN_MASK 0x01 - - - -/* - * FIFO_CONFIG6 - * Register Name : FIFO_CONFIG6 - */ - -/* - * fifo_empty_indicator_dis - * 0: xFF is sent out as FIFO data when FIFO is empty. - * 1: The last FIFO data is sent out when FIFO is empty. - */ -#define FIFO_CONFIG6_FIFO_EMPTY_INDICATOR_DIS_POS 0x04 -#define FIFO_CONFIG6_FIFO_EMPTY_INDICATOR_DIS_MASK (0x01 << FIFO_CONFIG6_FIFO_EMPTY_INDICATOR_DIS_POS) - -/* - * rcosc_req_on_fifo_ths_dis - * 0: When the FIFO is operating in ALP+WUOSC mode and the watermark (WM) interrupt is enabled, the FIFO wakes up the system oscillator (RCOSC) as soon as the watermark level is reached. The system oscillator remains enabled until a Host FIFO read operation happens. This will temporarily cause a small increase in the power consumption due to the enabling of the system oscillator. - * 1: The system oscillator is not automatically woken-up by the FIFO/INT when the WM interrupt is triggered. The side effect is that the host can receive invalid packets until the system oscillator is off after it has been turned on for other reasons not related to a WM interrupt. - * - * The recommended setting of this bit is ‘1’ before entering and during all power modes excluding ALP with WUOSC. This is in order to avoid having to do a FIFO access/flush before entering sleep mode. During ALP with WUOSC it is recommended to set this bit to ‘0’. It is recommended to reset this bit back to ‘1’ before exiting ALP+WUOSC with a wait time of 1 ODR or higher. - */ -#define FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_POS 0x00 -#define FIFO_CONFIG6_RCOSC_REQ_ON_FIFO_THS_DIS_MASK 0x01 - - - -/* - * FSYNC_CONFIG - * Register Name : FSYNC_CONFIG - */ - -/* - * fsync_ui_sel - * this register was called (ext_sync_sel) - * 0 Do not tag Fsync flag - * 1 Tag Fsync flag to TEMP_OUT’s LSB - * 2 Tag Fsync flag to GYRO_XOUT’s LSB - * 3 Tag Fsync flag to GYRO_YOUT’s LSB - * 4 Tag Fsync flag to GYRO_ZOUT’s LSB - * 5 Tag Fsync flag to ACCEL_XOUT’s LSB - * 6 Tag Fsync flag to ACCEL_YOUT’s LSB - * 7 Tag Fsync flag to ACCEL_ZOUT’s LSB - */ -#define FSYNC_CONFIG_FSYNC_UI_SEL_POS 0x04 -#define FSYNC_CONFIG_FSYNC_UI_SEL_MASK (0x07 << FSYNC_CONFIG_FSYNC_UI_SEL_POS) - -/* - * fsync_ui_flag_clear_sel - * 0 means the FSYNC flag is cleared when UI sensor reg is updated - * 1 means the FSYNC flag is cleared when UI interface reads the sensor register LSB of FSYNC tagged axis - */ -#define FSYNC_CONFIG_FSYNC_UI_FLAG_CLEAR_SEL_POS 0x01 -#define FSYNC_CONFIG_FSYNC_UI_FLAG_CLEAR_SEL_MASK (0x01 << FSYNC_CONFIG_FSYNC_UI_FLAG_CLEAR_SEL_POS) - -/* - * fsync_polarity - * 0: Start from Rising edge of FSYNC pulse to measure FSYNC interval - * 1: Start from Falling edge of FSYNC pulse to measure FSYNC interval - */ -#define FSYNC_CONFIG_FSYNC_POLARITY_POS 0x00 -#define FSYNC_CONFIG_FSYNC_POLARITY_MASK 0x01 - - - -/* - * INT_CONFIG0 - * Register Name : INT_CONFIG0 - */ - -/* - * ui_drdy_int_clear - * Data Ready Interrupt Clear Option (latched mode) - * 00: Clear on Status Bit Read - * 01: Clear on Status Bit Read - * 10: Clear on Sensor Register Read - * 11: Clear on Status Bit Read OR on Sensor Register read - */ -#define INT_CONFIG0_UI_DRDY_INT_CLEAR_POS 0x04 -#define INT_CONFIG0_UI_DRDY_INT_CLEAR_MASK (0x03 << INT_CONFIG0_UI_DRDY_INT_CLEAR_POS) - -/* - * fifo_ths_int_clear - * FIFO Threshold Interrupt Clear Option (latched mode) - * 00: Clear on Status Bit Read - * 01: Clear on Status Bit Read - * 10: Clear on FIFO data 1Byte Read - * 11: Clear on Status Bit Read OR on FIFO data 1 byte read - */ -#define INT_CONFIG0_FIFO_THS_INT_CLEAR_POS 0x02 -#define INT_CONFIG0_FIFO_THS_INT_CLEAR_MASK (0x03 << INT_CONFIG0_FIFO_THS_INT_CLEAR_POS) - -/* - * fifo_full_int_clear - * FIFO Full Interrupt Clear Option (latched mode) - * 00: Clear on Status Bit Read - * 01: Clear on Status Bit Read - * 10: Clear on FIFO data 1Byte Read - * 11: Clear on Status Bit Read OR on FIFO data 1 byte read - */ -#define INT_CONFIG0_FIFO_FULL_INT_CLEAR_POS 0x00 -#define INT_CONFIG0_FIFO_FULL_INT_CLEAR_MASK 0x03 - - - -/* - * INT_CONFIG1 - * Register Name : INT_CONFIG1 - */ - -/* - * int_tpulse_duration - * 0 - (Default) Interrupt pulse duration is 100us - * 1- Interrupt pulse duration is 8 us - */ -#define INT_CONFIG1_INT_TPULSE_DURATION_POS 0x06 -#define INT_CONFIG1_INT_TPULSE_DURATION_MASK (0x01 << INT_CONFIG1_INT_TPULSE_DURATION_POS) - -/* - * int_async_reset - * 0: The interrupt pulse is reset as soon as the interrupt status register is read if the pulse is still active. - * 1: The interrupt pulse remains high for the intended duration independent of when the interrupt status register is read. This is the default and recommended setting. In this case, when in ALP with the WUOSC clock, the clearing of the interrupt status register requires up to one ODR period after reading. - */ -#define INT_CONFIG1_INT_ASYNC_RESET_POS 0x04 -#define INT_CONFIG1_INT_ASYNC_RESET_MASK (0x01 << INT_CONFIG1_INT_ASYNC_RESET_POS) - - - -/* - * SENSOR_CONFIG3 - * Register Name : SENSOR_CONFIG3 - */ - -/* - * apex_disable - * 1: Disable APEX features to extend FIFO size to 2.25 Kbytes - */ -#define SENSOR_CONFIG3_APEX_DISABLE_POS 0x06 -#define SENSOR_CONFIG3_APEX_DISABLE_MASK (0x01 << SENSOR_CONFIG3_APEX_DISABLE_POS) - -/* - * ST_CONFIG - * Register Name : ST_CONFIG - */ - -/* - * accel_st_reg - * User must set this bit to 1 when enabling accelerometer self-test and clear it to 0 when self-test procedure has completed. - */ -#define ST_CONFIG_ACCEL_ST_REG_POS 0x07 -#define ST_CONFIG_ACCEL_ST_REG_MASK (0x01 << ST_CONFIG_ACCEL_ST_REG_POS) - -/* - * st_number_sample - * This bit selects the number of sensor samples that should be used to process self-test - * 0: 16 samples - * 1: 200 samples - */ -#define ST_CONFIG_ST_NUMBER_SAMPLE_POS 0x06 -#define ST_CONFIG_ST_NUMBER_SAMPLE_MASK (0x01 << ST_CONFIG_ST_NUMBER_SAMPLE_POS) - -/* - * accel_st_lim - * These bits control the tolerated ratio between self-test processed values and reference (fused) ones for accelerometer. - * 0 : 5% - * 1: 10% - * 2: 15% - * 3: 20% - * 4: 25% - * 5: 30% - * 6: 40% - * 7: 50% - */ -#define ST_CONFIG_ACCEL_ST_LIM_POS 0x03 -#define ST_CONFIG_ACCEL_ST_LIM_MASK (0x07 << ST_CONFIG_ACCEL_ST_LIM_POS) - -/* - * gyro_st_lim - * These bits control the tolerated ratio between self-test processed values and reference (fused) ones for gyro. - * 0 : 5% - * 1: 10% - * 2: 15% - * 3: 20% - * 4: 25% - * 5: 30% - * 6: 40% - * 7: 50% - */ -#define ST_CONFIG_GYRO_ST_LIM_POS 0x00 -#define ST_CONFIG_GYRO_ST_LIM_MASK 0x07 - - - -/* - * SELFTEST - * Register Name : SELFTEST - */ - -/* - * gyro_st_en - * 1: enable gyro self test operation. Host needs to program this bit to 0 to move chip out of self test mode. If host programs this bit to 0 while st_busy = 1 and st_done =0, the current running self-test operation is terminated by host. - */ -#define SELFTEST_GYRO_ST_EN_POS 0x07 -#define SELFTEST_GYRO_ST_EN_MASK (0x01 << SELFTEST_GYRO_ST_EN_POS) - -/* - * accel_st_en - * 1: enable accel self test operation. Host needs to program this bit to 0 to move chip out of self test mode. If host programs this bit to 0 while st_busy = 1 and st_done =0, the current running self-test operation is terminated by host. - */ -#define SELFTEST_ACCEL_ST_EN_POS 0x06 -#define SELFTEST_ACCEL_ST_EN_MASK (0x01 << SELFTEST_ACCEL_ST_EN_POS) - -/* - * en_gz_st - * Enable Gyro Z-axis self test - */ -#define SELFTEST_EN_GZ_ST_POS 0x05 -#define SELFTEST_EN_GZ_ST_MASK (0x01 << SELFTEST_EN_GZ_ST_POS) - -/* - * en_gy_st - * Enable Gyro Y-axis self test - */ -#define SELFTEST_EN_GY_ST_POS 0x04 -#define SELFTEST_EN_GY_ST_MASK (0x01 << SELFTEST_EN_GY_ST_POS) - -/* - * en_gx_st - * Enable Gyro X-axis self test - */ -#define SELFTEST_EN_GX_ST_POS 0x03 -#define SELFTEST_EN_GX_ST_MASK (0x01 << SELFTEST_EN_GX_ST_POS) - -/* - * en_az_st - * Enable Accel Z-axis self test - */ -#define SELFTEST_EN_AZ_ST_POS 0x02 -#define SELFTEST_EN_AZ_ST_MASK (0x01 << SELFTEST_EN_AZ_ST_POS) - -/* - * en_ay_st - * Enable Accel Y-axis self test - */ -#define SELFTEST_EN_AY_ST_POS 0x01 -#define SELFTEST_EN_AY_ST_MASK (0x01 << SELFTEST_EN_AY_ST_POS) - -/* - * en_ax_st - * Enable Accel X-axis self test - */ -#define SELFTEST_EN_AX_ST_POS 0x00 -#define SELFTEST_EN_AX_ST_MASK 0x01 - - - -/* - * INTF_CONFIG6 - * Register Name : INTF_CONFIG6 - */ - -/* - * i3c_timeout_en - * Value of 1 to enable i2c/i3c timeout function - */ -#define INTF_CONFIG6_I3C_TIMEOUT_EN_POS 0x04 -#define INTF_CONFIG6_I3C_TIMEOUT_EN_MASK (0x01 << INTF_CONFIG6_I3C_TIMEOUT_EN_POS) - -/* - * i3c_ibi_byte_en - * I3C Enable IBI-payload function. - */ -#define INTF_CONFIG6_I3C_IBI_BYTE_EN_POS 0x03 -#define INTF_CONFIG6_I3C_IBI_BYTE_EN_MASK (0x01 << INTF_CONFIG6_I3C_IBI_BYTE_EN_POS) - -/* - * i3c_ibi_en - * I3C Enable IBI function. - */ -#define INTF_CONFIG6_I3C_IBI_EN_POS 0x02 -#define INTF_CONFIG6_I3C_IBI_EN_MASK (0x01 << INTF_CONFIG6_I3C_IBI_EN_POS) - - - -/* - * INTF_CONFIG10 - * Register Name : INTF_CONFIG10 - */ - -/* - * asynctime0_dis - * 1: Disable asynchronous timing control mode 0 operation. - */ -#define INTF_CONFIG10_ASYNCTIME0_DIS_POS 0x07 -#define INTF_CONFIG10_ASYNCTIME0_DIS_MASK (0x01 << INTF_CONFIG10_ASYNCTIME0_DIS_POS) - -/* - * INTF_CONFIG7 - * Register Name : INTF_CONFIG7 - */ - -/* - * i3c_ddr_wr_mode - * This bit controls how I3C slave treats the 1st 2-byte data from - * host in a DDR write operation. - * - * 0: (a) The 1st-byte in DDR-WR configures the starting register - * address where the write operation should occur. - * (b) The 2nd-byte in DDR-WR is ignored and dropped. - * (c) The 3rd-byte in DDR-WR will be written into the register - * with address specified by the 1st-byte. - * Or, the next DDR-RD will be starting from the address - * specified by the 1st-byte of previous DDR-WR. - * - * 1: (a) The 1st-byte in DDR-WR configures the starting register - * address where the write operation should occur. - * (b) The 2nd-byte in DDR-WR will be written into the register - * with address specified by the 1st-byte. - */ -#define INTF_CONFIG7_I3C_DDR_WR_MODE_POS 0x03 -#define INTF_CONFIG7_I3C_DDR_WR_MODE_MASK (0x01 << INTF_CONFIG7_I3C_DDR_WR_MODE_POS) - -/* - * OTP_CONFIG - * Register Name : OTP_CONFIG - */ - -/* - * otp_copy_mode - * 00: Reserved - * 01: Enable copying OTP block to SRAM - * 10: Reserved - * 11: Enable copying self-test data from OTP memory to SRAM - */ -#define OTP_CONFIG_OTP_COPY_MODE_POS 0x02 -#define OTP_CONFIG_OTP_COPY_MODE_MASK (0x03 << OTP_CONFIG_OTP_COPY_MODE_POS) - -/* - * INT_SOURCE6 - * Register Name : INT_SOURCE6 - */ - -/* - * ff_int1_en - * 0: Freefall interrupt not routed to INT1 - * 1: Freefall interrupt routed to INT1 - */ -#define INT_SOURCE6_FF_INT1_EN_POS 0x07 -#define INT_SOURCE6_FF_INT1_EN_MASK (0x01 << INT_SOURCE6_FF_INT1_EN_POS) - -/* - * lowg_int1_en - * 0: Low-g interrupt not routed to INT1 - * 1: Low-g interrupt routed to INT1 - */ -#define INT_SOURCE6_LOWG_INT1_EN_POS 0x06 -#define INT_SOURCE6_LOWG_INT1_EN_MASK (0x01 << INT_SOURCE6_LOWG_INT1_EN_POS) - -/* - * step_det_int1_en - * 0: Step detect interrupt not routed to INT1 - * 1: Step detect interrupt routed to INT1 - */ -#define INT_SOURCE6_STEP_DET_INT1_EN_POS 0x05 -#define INT_SOURCE6_STEP_DET_INT1_EN_MASK (0x01 << INT_SOURCE6_STEP_DET_INT1_EN_POS) - -/* - * step_cnt_ofl_int1_en - * 0: Step count overflow interrupt not routed to INT1 - * 1: Step count overflow interrupt routed to INT1 - */ -#define INT_SOURCE6_STEP_CNT_OFL_INT1_EN_POS 0x04 -#define INT_SOURCE6_STEP_CNT_OFL_INT1_EN_MASK (0x01 << INT_SOURCE6_STEP_CNT_OFL_INT1_EN_POS) - -/* - * tilt_det_int1_en - * 0: Tilt detect interrupt not routed to INT1 - * 1: Tile detect interrupt routed to INT1 - */ -#define INT_SOURCE6_TILT_DET_INT1_EN_POS 0x03 -#define INT_SOURCE6_TILT_DET_INT1_EN_MASK (0x01 << INT_SOURCE6_TILT_DET_INT1_EN_POS) - - - -/* - * INT_SOURCE7 - * Register Name : INT_SOURCE7 - */ - -/* - * ff_int2_en - * 0: Freefall interrupt not routed to INT2 - * 1: Freefall interrupt routed to INT2 - */ -#define INT_SOURCE7_FF_INT2_EN_POS 0x07 -#define INT_SOURCE7_FF_INT2_EN_MASK (0x01 << INT_SOURCE7_FF_INT2_EN_POS) - -/* - * lowg_int2_en - * 0: Low-g interrupt not routed to INT2 - * 1: Low-g interrupt routed to INT2 - */ -#define INT_SOURCE7_LOWG_INT2_EN_POS 0x06 -#define INT_SOURCE7_LOWG_INT2_EN_MASK (0x01 << INT_SOURCE7_LOWG_INT2_EN_POS) - -/* - * step_det_int2_en - * 0: Step detect interrupt not routed to INT2 - * 1: Step detect interrupt routed to INT2 - */ -#define INT_SOURCE7_STEP_DET_INT2_EN_POS 0x05 -#define INT_SOURCE7_STEP_DET_INT2_EN_MASK (0x01 << INT_SOURCE7_STEP_DET_INT2_EN_POS) - -/* - * step_cnt_ofl_int2_en - * 0: Step count overflow interrupt not routed to INT2 - * 1: Step count overflow interrupt routed to INT2 - */ -#define INT_SOURCE7_STEP_CNT_OFL_INT2_EN_POS 0x04 -#define INT_SOURCE7_STEP_CNT_OFL_INT2_EN_MASK (0x01 << INT_SOURCE7_STEP_CNT_OFL_INT2_EN_POS) - -/* - * tilt_det_int2_en - * 0: Tilt detect interrupt not routed to INT2 - * 1: Tile detect interrupt routed to INT2 - */ -#define INT_SOURCE7_TILT_DET_INT2_EN_POS 0x03 -#define INT_SOURCE7_TILT_DET_INT2_EN_MASK (0x01 << INT_SOURCE7_TILT_DET_INT2_EN_POS) - - - -/* - * INT_SOURCE8 - * Register Name : INT_SOURCE8 - */ - -/* - * fsync_ibi_en - * 0: FSYNC interrupt not routed to IBI - * 1: FSYNC interrupt routed to IBI - */ -#define INT_SOURCE8_FSYNC_IBI_EN_POS 0x05 -#define INT_SOURCE8_FSYNC_IBI_EN_MASK (0x01 << INT_SOURCE8_FSYNC_IBI_EN_POS) - -/* - * pll_rdy_ibi_en - * 0: PLL ready interrupt not routed to IBI - * 1: PLL ready interrupt routed to IBI - */ -#define INT_SOURCE8_PLL_RDY_IBI_EN_POS 0x04 -#define INT_SOURCE8_PLL_RDY_IBI_EN_MASK (0x01 << INT_SOURCE8_PLL_RDY_IBI_EN_POS) - -/* - * ui_drdy_ibi_en - * 0: UI data ready interrupt not routed to IBI - * 1: UI data ready interrupt routed to IBI - */ -#define INT_SOURCE8_UI_DRDY_IBI_EN_POS 0x03 -#define INT_SOURCE8_UI_DRDY_IBI_EN_MASK (0x01 << INT_SOURCE8_UI_DRDY_IBI_EN_POS) - -/* - * fifo_ths_ibi_en - * 0: FIFO threshold interrupt not routed to IBI - * 1: FIFO threshold interrupt routed to IBI - */ -#define INT_SOURCE8_FIFO_THS_IBI_EN_POS 0x02 -#define INT_SOURCE8_FIFO_THS_IBI_EN_MASK (0x01 << INT_SOURCE8_FIFO_THS_IBI_EN_POS) - -/* - * fifo_full_ibi_en - * 0: FIFO full interrupt not routed to IBI - * 1: FIFO full interrupt routed to IBI - */ -#define INT_SOURCE8_FIFO_FULL_IBI_EN_POS 0x01 -#define INT_SOURCE8_FIFO_FULL_IBI_EN_MASK (0x01 << INT_SOURCE8_FIFO_FULL_IBI_EN_POS) - -/* - * agc_rdy_ibi_en - * 0: AGC ready interrupt not routed to IBI - * 1: AGC ready interrupt routed to IBI - */ -#define INT_SOURCE8_AGC_RDY_IBI_EN_POS 0x00 -#define INT_SOURCE8_AGC_RDY_IBI_EN_MASK 0x01 - - - -/* - * INT_SOURCE9 - * Register Name : INT_SOURCE9 - */ - -/* - * i3c_protocol_error_ibi_en - * 0: I3CSM protocol error interrupt not routed to IBI - * 1: I3CSM protocol error interrupt routed to IBI - */ -#define INT_SOURCE9_I3C_PROTOCOL_ERROR_IBI_EN_POS 0x07 -#define INT_SOURCE9_I3C_PROTOCOL_ERROR_IBI_EN_MASK (0x01 << INT_SOURCE9_I3C_PROTOCOL_ERROR_IBI_EN_POS) - -/* - * ff_ibi_en - * 0: Freefall interrupt not routed to IBI - * 1: Freefall interrupt routed to IBI - */ -#define INT_SOURCE9_FF_IBI_EN_POS 0x06 -#define INT_SOURCE9_FF_IBI_EN_MASK (0x01 << INT_SOURCE9_FF_IBI_EN_POS) - -/* - * lowg_ibi_en - * 0: Low-g interrupt not routed to IBI - * 1: Low-g interrupt routed to IBI - */ -#define INT_SOURCE9_LOWG_IBI_EN_POS 0x05 -#define INT_SOURCE9_LOWG_IBI_EN_MASK (0x01 << INT_SOURCE9_LOWG_IBI_EN_POS) - -/* - * smd_ibi_en - * 0: SMD interrupt not routed to IBI - * 1: SMD interrupt routed to IBI - */ -#define INT_SOURCE9_SMD_IBI_EN_POS 0x04 -#define INT_SOURCE9_SMD_IBI_EN_MASK (0x01 << INT_SOURCE9_SMD_IBI_EN_POS) - -/* - * wom_z_ibi_en - * 0: Z-axis WOM interrupt not routed to IBI - * 1: Z-axis WOM interrupt routed to IBI - */ -#define INT_SOURCE9_WOM_Z_IBI_EN_POS 0x03 -#define INT_SOURCE9_WOM_Z_IBI_EN_MASK (0x01 << INT_SOURCE9_WOM_Z_IBI_EN_POS) - -/* - * wom_y_ibi_en - * 0: Y-axis WOM interrupt not routed to IBI - * 1: Y-axis WOM interrupt routed to IBI - */ -#define INT_SOURCE9_WOM_Y_IBI_EN_POS 0x02 -#define INT_SOURCE9_WOM_Y_IBI_EN_MASK (0x01 << INT_SOURCE9_WOM_Y_IBI_EN_POS) - -/* - * wom_x_ibi_en - * 0: X-axis WOM interrupt not routed to IBI - * 1: X-axis WOM interrupt routed to IBI - */ -#define INT_SOURCE9_WOM_X_IBI_EN_POS 0x01 -#define INT_SOURCE9_WOM_X_IBI_EN_MASK (0x01 << INT_SOURCE9_WOM_X_IBI_EN_POS) - -/* - * st_done_ibi_en - * 0: Self-test done interrupt not routed to IBI - * 1: Self-test done interrupt routed to IBI - */ -#define INT_SOURCE9_ST_DONE_IBI_EN_POS 0x00 -#define INT_SOURCE9_ST_DONE_IBI_EN_MASK 0x01 - - - -/* - * INT_SOURCE10 - * Register Name : INT_SOURCE10 - */ - -/* - * step_det_ibi_en - * 0: Step detect interrupt not routed to IBI - * 1: Step detect interrupt routed to IBI - */ -#define INT_SOURCE10_STEP_DET_IBI_EN_POS 0x05 -#define INT_SOURCE10_STEP_DET_IBI_EN_MASK (0x01 << INT_SOURCE10_STEP_DET_IBI_EN_POS) - -/* - * step_cnt_ofl_ibi_en - * 0: Step count overflow interrupt not routed to IBI - * 1: Step count overflow interrupt routed to IBI - */ -#define INT_SOURCE10_STEP_CNT_OFL_IBI_EN_POS 0x04 -#define INT_SOURCE10_STEP_CNT_OFL_IBI_EN_MASK (0x01 << INT_SOURCE10_STEP_CNT_OFL_IBI_EN_POS) - -/* - * tilt_det_ibi_en - * 0: Tilt detect interrupt not routed to IBI - * 1: Tile detect interrupt routed to IBI - */ -#define INT_SOURCE10_TILT_DET_IBI_EN_POS 0x03 -#define INT_SOURCE10_TILT_DET_IBI_EN_MASK (0x01 << INT_SOURCE10_TILT_DET_IBI_EN_POS) - - - -/* - * APEX_CONFIG2 - * Register Name : APEX_CONFIG2 - */ - -/* - * low_energy_amp_th_sel - * Threshold to select a valid step. Used to increase step detection for slow walk use case. - * - * 0000: 30 mg - * 0001: 35 mg - * 0010: 40 mg - * 0011: 45 mg - * 0100: 50 mg - * 0101: 55 mg - * 0110: 60 mg - * 0111: 65 mg - * 1000: 70 mg - * 1001: 75 mg - * 1010: 80 mg (default) - * 1011: 85 mg - * 1100: 90 mg - * 1101: 95 mg - * 1110: 100 mg - * 1111: 105 mg - */ -#define APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS 0x04 -#define APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_MASK (0x0f << APEX_CONFIG2_LOW_ENERGY_AMP_TH_SEL_POS) - -/* - * dmp_power_save_time_sel - * Duration of the period while the DMP stays awake after receiving a WOM event. - * - * 0000: 0 seconds - * 0001: 4 seconds - * 0010: 8 seconds (default) - * 0011: 12 seconds - * 0100: 16 seconds - * 0101: 20 seconds - * 0110: 24 seconds - * 0111: 28 seconds - * 1000: 32 seconds - * 1001: 36 seconds - * 1010: 40 seconds - * 1011: 44 seconds - * 1100: 48 seconds - * 1101: 52 seconds - * 1110: 56 seconds - * 1111: 60 seconds - */ -#define APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_POS 0x00 -#define APEX_CONFIG2_DMP_POWER_SAVE_TIME_SEL_MASK 0x0f - - - -/* - * APEX_CONFIG3 - * Register Name : APEX_CONFIG3 - */ - -/* - * ped_amp_th_sel - * Threshold of step detection sensitivity. - * - * Low values increase detection sensitivity: reduce miss-detection. - * High values reduce detection sensitivity: reduce false-positive. - * - * 0000: 30 mg - * 0001: 34 mg - * 0010: 38 mg - * 0011: 42 mg - * 0100: 46 mg - * 0101: 50 mg - * 0110: 54 mg - * 0111: 58 mg - * 1000: 62 mg (default) - * 1001: 66 mg - * 1010: 70 mg - * 1011: 74 mg - * 1100: 78 mg - * 1101: 82 mg - * 1110: 86 mg - * 1111: 90 mg - */ -#define APEX_CONFIG3_PED_AMP_TH_SEL_POS 0x04 -#define APEX_CONFIG3_PED_AMP_TH_SEL_MASK (0x0f << APEX_CONFIG3_PED_AMP_TH_SEL_POS) - -/* - * ped_step_cnt_th_sel - * Minimum number of steps that must be detected before step count is incremented. - * - * Low values reduce latency but increase false positives. - * High values increase step count accuracy but increase latency. - * - * 0000: 0 steps - * 0001: 1 step - * 0010: 2 steps - * 0011: 3 steps - * 0100: 4 steps - * 0101: 5 steps (default) - * 0110: 6 steps - * 0111: 7 steps - * 1000: 8 steps - * 1001: 9 steps - * 1010: 10 steps - * 1011: 11 steps - * 1100: 12 steps - * 1101: 13 steps - * 1110: 14 steps - * 1111: 15 steps - */ -#define APEX_CONFIG3_PED_STEP_CNT_TH_SEL_POS 0x00 -#define APEX_CONFIG3_PED_STEP_CNT_TH_SEL_MASK 0x0f - - - -/* - * APEX_CONFIG4 - * Register Name : APEX_CONFIG4 - */ - -/* - * ped_step_det_th_sel - * Minimum number of steps that must be detected before step event is signaled. - * - * Low values reduce latency but increase false positives. - * High values increase step event validity but increase latency. - * - * 000: 0 steps - * 001: 1 step - * 010: 2 steps (default) - * 011: 3 steps - * 100: 4 steps - * 101: 5 steps - * 110: 6 steps - * 111: 7 steps - */ -#define APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS 0x05 -#define APEX_CONFIG4_PED_STEP_DET_TH_SEL_MASK (0x07 << APEX_CONFIG4_PED_STEP_DET_TH_SEL_POS) - -/* - * ped_sb_timer_th_sel - * Duration before algorithm considers that user has stopped taking steps. - * - * 000: 50 samples - * 001: 75 sample - * 010: 100 samples - * 011: 125 samples - * 100: 150 samples (default) - * 101: 175 samples - * 110: 200 samples - * 111: 225 samples - */ -#define APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS 0x02 -#define APEX_CONFIG4_PED_SB_TIMER_TH_SEL_MASK (0x07 << APEX_CONFIG4_PED_SB_TIMER_TH_SEL_POS) - -/* - * ped_hi_en_th_sel - * Threshold to classify acceleration signal as motion not due to steps. - * - * High values improve vibration rejection. - * Low values improve detection. - * - * 00: 87.89 mg - * 01: 104.49 mg (default) - * 10: 132.81 mg - * 11: 155.27 mg - */ -#define APEX_CONFIG4_PED_HI_EN_TH_SEL_POS 0x00 -#define APEX_CONFIG4_PED_HI_EN_TH_SEL_MASK 0x03 - - - -/* - * APEX_CONFIG5 - * Register Name : APEX_CONFIG5 - */ - -/* - * tilt_wait_time_sel - * Minimum duration for which the device should be tilted before signaling event. - * - * 00: 0s - * 01: 2s - * 10: 4s (default) - * 11: 6s - */ -#define APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS 0x06 -#define APEX_CONFIG5_TILT_WAIT_TIME_SEL_MASK (0x03 << APEX_CONFIG5_TILT_WAIT_TIME_SEL_POS) - -/* - * lowg_peak_th_hyst_sel - * Hysteresis value added to the low-g threshold after exceeding it. - * - * 000: 31 mg (default) - * 001: 63 mg - * 010: 94 mg - * 011: 125 mg - * 100: 156 mg - * 101: 188 mg - * 110: 219 mg - * 111: 250 mg - */ -#define APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS 0x03 -#define APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_MASK (0x07 << APEX_CONFIG5_LOWG_PEAK_TH_HYST_SEL_POS) - -/* - * highg_peak_th_hyst_sel - * Hysteresis value subtracted from the high-g threshold after exceeding it. - * - * 000: 31 mg (default) - * 001: 63 mg - * 010: 94 mg - * 011: 125 mg - * 100: 156 mg - * 101: 188 mg - * 110: 219 mg - * 111: 250 mg - */ -#define APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_POS 0x00 -#define APEX_CONFIG5_HIGHG_PEAK_TH_HYST_SEL_MASK 0x07 - - - -/* - * APEX_CONFIG9 - * Register Name : APEX_CONFIG9 - */ - -/* - * ff_debounce_duration_sel - * Period after a freefall is signaled during which a new freefall will not be detected. Prevents false detection due to bounces. - * - * 0000: 0 ms - * 0001: 1250 ms - * 0010: 1375 ms - * 0011: 1500 ms - * 0100: 1625 ms - * 0101: 1750 ms - * 0110: 1875 ms - * 0111: 2000 ms - * 1000: 2125 ms (default) - * 1001: 2250 ms - * 1010: 2375 ms - * 1011: 2500 ms - * 1100: 2625 ms - * 1101: 2750 ms - * 1110: 2875 ms - * 1111: 3000 ms - */ -#define APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS 0x04 -#define APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_MASK (0x0f << APEX_CONFIG9_FF_DEBOUNCE_DURATION_SEL_POS) - -/* - * smd_sensitivity_sel - * Parameter to tune SMD algorithm robustness to rejection, ranging from 0 to 4 (values higher than 4 are reserved). - * - * Low values increase detection rate but increase false positives. - * High values reduce false positives but reduce detection rate (especially for transport use cases). - * - * Default value is 0. - */ -#define APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS 0x01 -#define APEX_CONFIG9_SMD_SENSITIVITY_SEL_MASK (0x07 << APEX_CONFIG9_SMD_SENSITIVITY_SEL_POS) - -/* - * sensitivity_mode - * Pedometer sensitivity mode - * 0: Normal (default) - * 1: Slow walk - * - * Slow walk mode improves slow walk detection (<1Hz) but the number of false positives may increase. - */ -#define APEX_CONFIG9_SENSITIVITY_MODE_POS 0x00 -#define APEX_CONFIG9_SENSITIVITY_MODE_MASK 0x01 - - - -/* - * APEX_CONFIG10 - * Register Name : APEX_CONFIG10 - */ - -/* - * lowg_peak_th_sel - * Threshold for accel values below which low-g state is detected. - * - * 00000: 31 mg (default) - * 00001: 63 mg - * 00010: 94 mg - * 00011: 125 mg - * 00100: 156 mg - * 00101: 188 mg - * 00110: 219 mg - * 00111: 250 mg - * 01000: 281 mg - * 01001: 313 mg - * 01010: 344 mg - * 01011: 375 mg - * 01100: 406 mg - * 01101: 438 mg - * 01110: 469 mg - * 01111: 500 mg - * 10000: 531 mg - * 10001: 563 mg - * 10010: 594 mg - * 10011: 625 mg - * 10100: 656 mg - * 10101: 688 mg - * 10110: 719 mg - * 10111: 750 mg - * 11000: 781 mg - * 11001: 813 mg - * 11010: 844 mg - * 11011: 875 mg - * 11100: 906 mg - * 11101: 938 mg - * 11110: 969 mg - * 11111: 1000 mg - */ -#define APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS 0x03 -#define APEX_CONFIG10_LOWG_PEAK_TH_SEL_MASK (0x1f << APEX_CONFIG10_LOWG_PEAK_TH_SEL_POS) - -/* - * lowg_time_th_sel - * Number of samples required to enter low-g state. - * - * 000: 1 sample (default) - * 001: 2 samples - * 010: 3 samples - * 011: 4 samples - * 100: 5 samples - * 101: 6 samples - * 110: 7 samples - * 111: 8 samples - */ -#define APEX_CONFIG10_LOWG_TIME_TH_SEL_POS 0x00 -#define APEX_CONFIG10_LOWG_TIME_TH_SEL_MASK 0x07 - - - -/* - * APEX_CONFIG11 - * Register Name : APEX_CONFIG11 - */ - -/* - * highg_peak_th_sel - * Threshold for accel values above which high-g state is detected. - * - * 00000: 250 mg (default) - * 00001: 500 mg - * 00010: 750 mg - * 00011: 1000 mg - * 00100: 1250 mg - * 00101: 1500 mg - * 00110: 1750 mg - * 00111: 2000 mg - * 01000: 2250 mg - * 01001: 2500 mg - * 01010: 2750 mg - * 01011: 3000 mg - * 01100: 3250 mg - * 01101: 3500 mg - * 01110: 3750 mg - * 01111: 4000 mg - * 10000: 4250 mg - * 10001: 4500 mg - * 10010: 4750 mg - * 10011: 5000 mg - * 10100: 5250 mg - * 10101: 5500 mg - * 10110: 5750 mg - * 10111: 6000 mg - * 11000: 6250 mg - * 11001: 6500 mg - * 11010: 6750 mg - * 11011: 7000 mg - * 11100: 7250 mg - * 11101: 7500 mg - * 11110: 7750 mg - * 11111: 8000 mg - */ -#define APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS 0x03 -#define APEX_CONFIG11_HIGHG_PEAK_TH_SEL_MASK (0x1f << APEX_CONFIG11_HIGHG_PEAK_TH_SEL_POS) - -/* - * highg_time_th_sel - * Number of samples required to enter high-g state. - * - * 000: 1 sample (default) - * 001: 2 samples - * 010: 3 samples - * 011: 4 samples - * 100: 5 samples - * 101: 6 samples - * 110: 7 samples - * 111: 8 samples - */ -#define APEX_CONFIG11_HIGHG_TIME_TH_SEL_POS 0x00 -#define APEX_CONFIG11_HIGHG_TIME_TH_SEL_MASK 0x07 - - - -/* - * ACCEL_WOM_X_THR - * Register Name : ACCEL_WOM_X_THR - */ - -/* - * wom_x_th - * Threshold value for the Wake on Motion Interrupt for X-axis accelerometer - * WoM thresholds are expressed in fixed “mg” independent of the selected Range [0g : 1g]; Resolution 1g/256=~3.9mg - */ -#define ACCEL_WOM_X_THR_WOM_X_TH_POS 0x00 -#define ACCEL_WOM_X_THR_WOM_X_TH_MASK 0xff - - - -/* - * ACCEL_WOM_Y_THR - * Register Name : ACCEL_WOM_Y_THR - */ - -/* - * wom_y_th - * Threshold value for the Wake on Motion Interrupt for Y-axis accelerometer - * WoM thresholds are expressed in fixed “mg” independent of the selected Range [0g : 1g]; Resolution 1g/256=~3.9mg - */ -#define ACCEL_WOM_Y_THR_WOM_Y_TH_POS 0x00 -#define ACCEL_WOM_Y_THR_WOM_Y_TH_MASK 0xff - - - -/* - * ACCEL_WOM_Z_THR - * Register Name : ACCEL_WOM_Z_THR - */ - -/* - * wom_z_th - * Threshold value for the Wake on Motion Interrupt for Z-axis accelerometer - * WoM thresholds are expressed in fixed “mg” independent of the selected Range [0g : 1g]; Resolution 1g/256=~3.9mg - */ -#define ACCEL_WOM_Z_THR_WOM_Z_TH_POS 0x00 -#define ACCEL_WOM_Z_THR_WOM_Z_TH_MASK 0xff - - - -/* - * OFFSET_USER0 - * Register Name : OFFSET_USER0 - */ - -/* - * gyro_x_offuser - * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps - */ -#define OFFSET_USER0_GYRO_X_OFFUSER_POS 0x00 -#define OFFSET_USER0_GYRO_X_OFFUSER_MASK 0xff - - - -/* - * OFFSET_USER1 - * Register Name : OFFSET_USER1 - */ - -/* - * gyro_x_offuser - * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps - */ -#define OFFSET_USER1_GYRO_X_OFFUSER_POS 0x00 -#define OFFSET_USER1_GYRO_X_OFFUSER_MASK 0x0f - -/* - * gyro_y_offuser - * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps - */ -#define OFFSET_USER1_GYRO_Y_OFFUSER_POS 0x04 -#define OFFSET_USER1_GYRO_Y_OFFUSER_MASK (0x0f << OFFSET_USER1_GYRO_Y_OFFUSER_POS) - - - -/* - * OFFSET_USER2 - * Register Name : OFFSET_USER2 - */ - -/* - * gyro_y_offuser - * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps - */ -#define OFFSET_USER2_GYRO_Y_OFFUSER_POS 0x00 -#define OFFSET_USER2_GYRO_Y_OFFUSER_MASK 0xff - - - -/* - * OFFSET_USER3 - * Register Name : OFFSET_USER3 - */ - -/* - * gyro_z_offuser - * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps - */ -#define OFFSET_USER3_GYRO_Z_OFFUSER_POS 0x00 -#define OFFSET_USER3_GYRO_Z_OFFUSER_MASK 0xff - - - -/* - * OFFSET_USER4 - * Register Name : OFFSET_USER4 - */ - -/* - * gyro_z_offuser - * Gyro offset programmed by user. Max value is +/-64 dps, resolution is 1/32 dps - */ -#define OFFSET_USER4_GYRO_Z_OFFUSER_POS 0x00 -#define OFFSET_USER4_GYRO_Z_OFFUSER_MASK 0x0f - -/* - * accel_x_offuser - * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee - */ -#define OFFSET_USER4_ACCEL_X_OFFUSER_POS 0x04 -#define OFFSET_USER4_ACCEL_X_OFFUSER_MASK (0x0f << OFFSET_USER4_ACCEL_X_OFFUSER_POS) - - - -/* - * OFFSET_USER5 - * Register Name : OFFSET_USER5 - */ - -/* - * accel_x_offuser - * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee - */ -#define OFFSET_USER5_ACCEL_X_OFFUSER_POS 0x00 -#define OFFSET_USER5_ACCEL_X_OFFUSER_MASK 0xff - - - -/* - * OFFSET_USER6 - * Register Name : OFFSET_USER6 - */ - -/* - * accel_y_offuser - * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee - */ -#define OFFSET_USER6_ACCEL_Y_OFFUSER_POS 0x00 -#define OFFSET_USER6_ACCEL_Y_OFFUSER_MASK 0xff - - - -/* - * OFFSET_USER7 - * Register Name : OFFSET_USER7 - */ - -/* - * accel_y_offuser - * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee - */ -#define OFFSET_USER7_ACCEL_Y_OFFUSER_POS 0x00 -#define OFFSET_USER7_ACCEL_Y_OFFUSER_MASK 0x0f - -/* - * accel_z_offuser - * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee - */ -#define OFFSET_USER7_ACCEL_Z_OFFUSER_POS 0x04 -#define OFFSET_USER7_ACCEL_Z_OFFUSER_MASK (0x0f << OFFSET_USER7_ACCEL_Z_OFFUSER_POS) - - - -/* - * OFFSET_USER8 - * Register Name : OFFSET_USER8 - */ - -/* - * accel_z_offuser - * Accel offset programmed by user. Max value is +/-1 gee, resolution is 0.5 mgee - */ -#define OFFSET_USER8_ACCEL_Z_OFFUSER_POS 0x00 -#define OFFSET_USER8_ACCEL_Z_OFFUSER_MASK 0xff - - - -/* - * ST_STATUS1 - * Register Name : ST_STATUS1 - */ - -/* - * accel_st_pass - * 1: Accel self-test passed for all the 3 axes - */ -#define ST_STATUS1_ACCEL_ST_PASS_POS 0x05 -#define ST_STATUS1_ACCEL_ST_PASS_MASK (0x01 << ST_STATUS1_ACCEL_ST_PASS_POS) - -/* - * accel_st_done - * 1: Accel self-test done for all the 3 axes - */ -#define ST_STATUS1_ACCEL_ST_DONE_POS 0x04 -#define ST_STATUS1_ACCEL_ST_DONE_MASK (0x01 << ST_STATUS1_ACCEL_ST_DONE_POS) - -/* - * az_st_pass - * 1: Accel Z-axis self-test passed - */ -#define ST_STATUS1_AZ_ST_PASS_POS 0x03 -#define ST_STATUS1_AZ_ST_PASS_MASK (0x01 << ST_STATUS1_AZ_ST_PASS_POS) - -/* - * ay_st_pass - * 1: Accel Y-axis self-test passed - */ -#define ST_STATUS1_AY_ST_PASS_POS 0x02 -#define ST_STATUS1_AY_ST_PASS_MASK (0x01 << ST_STATUS1_AY_ST_PASS_POS) - -/* - * ax_st_pass - * 1: Accel X-axis self-test passed - */ -#define ST_STATUS1_AX_ST_PASS_POS 0x01 -#define ST_STATUS1_AX_ST_PASS_MASK (0x01 << ST_STATUS1_AX_ST_PASS_POS) - - - -/* - * ST_STATUS2 - * Register Name : ST_STATUS2 - */ - -/* - * st_incomplete - * 1: Self-test is incomplete. - * This bit is set to 1 if the self-test was aborted. - * One possible cause of aborting the self-test may be the detection of significant movement in the gyro when the self-test for gyro and/or accel is being executed. - */ -#define ST_STATUS2_ST_INCOMPLETE_POS 0x06 -#define ST_STATUS2_ST_INCOMPLETE_MASK (0x01 << ST_STATUS2_ST_INCOMPLETE_POS) - -/* - * gyro_st_pass - * 1: Gyro self-test passed for all the 3 axes - */ -#define ST_STATUS2_GYRO_ST_PASS_POS 0x05 -#define ST_STATUS2_GYRO_ST_PASS_MASK (0x01 << ST_STATUS2_GYRO_ST_PASS_POS) - -/* - * gyro_st_done - * 1: Gyro self-test done for all the 3 axes - */ -#define ST_STATUS2_GYRO_ST_DONE_POS 0x04 -#define ST_STATUS2_GYRO_ST_DONE_MASK (0x01 << ST_STATUS2_GYRO_ST_DONE_POS) - -/* - * gz_st_pass - * 1: Gyro Z-axis self-test passed - */ -#define ST_STATUS2_GZ_ST_PASS_POS 0x03 -#define ST_STATUS2_GZ_ST_PASS_MASK (0x01 << ST_STATUS2_GZ_ST_PASS_POS) - -/* - * gy_st_pass - * 1: Gyro Y-axis self-test passed - */ -#define ST_STATUS2_GY_ST_PASS_POS 0x02 -#define ST_STATUS2_GY_ST_PASS_MASK (0x01 << ST_STATUS2_GY_ST_PASS_POS) - -/* - * gx_st_pass - * 1: Gyro X-axis self-test passed - */ -#define ST_STATUS2_GX_ST_PASS_POS 0x01 -#define ST_STATUS2_GX_ST_PASS_MASK (0x01 << ST_STATUS2_GX_ST_PASS_POS) - - - -/* - * FDR_CONFIG - * Register Name : FDR_CONFIG - */ - -/* - * fdr_sel - * [7:4] Reserved - * [3:0] FIFO packet rate decimation factor. Sets the number of discarded FIFO packets. Valid range is 0 to 127. User must disable sensors when initializing FDR_SEL value or making changes to it. - * - * 0xxx: Decimation is disabled, all packets are sent to FIFO - * 1000: 1 packet out of 2 is sent to FIFO - * 1001: 1 packet out of 4 is sent to FIFO - * 1010: 1 packet out of 8 is sent to FIFO - * 1011: 1 packet out of 16 is sent to FIFO - * 1100: 1 packet out of 32 is sent to FIFO - * 1101: 1 packet out of 64 is sent to FIFO - * 1110: 1 packet out of 128 is sent to FIFO - * 1111: 1 packet out of 256 is sent to FIFO - */ -#define FDR_CONFIG_FDR_SEL_POS 0x00 -#define FDR_CONFIG_FDR_SEL_MASK 0xff - - - -/* - * APEX_CONFIG12 - * Register Name : APEX_CONFIG12 - */ - -/* - * ff_max_duration_sel - * Maximum freefall length. Longer freefalls are ignored. - * - * 0000: 102 cm (default) - * 0001: 120 cm - * 0010: 139 cm - * 0011: 159 cm - * 0100: 181 cm - * 0101: 204 cm - * 0110: 228 cm - * 0111: 254 cm - * 1000: 281 cm - * 1001: 310 cm - * 1010: 339 cm - * 1011: 371 cm - * 1100: 403 cm - * 1101: 438 cm - * 1110: 473 cm - * 1111: 510 cm - */ -#define APEX_CONFIG12_FF_MAX_DURATION_SEL_POS 0x04 -#define APEX_CONFIG12_FF_MAX_DURATION_SEL_MASK (0x0f << APEX_CONFIG12_FF_MAX_DURATION_SEL_POS) - -/* - * ff_min_duration_sel - * Minimum freefall length. Shorter freefalls are ignored. - * - * 0000: 10 cm (default) - * 0001: 12 cm - * 0010: 13 cm - * 0011: 16 cm - * 0100: 18 cm - * 0101: 20 cm - * 0110: 23 cm - * 0111: 25 cm - * 1000: 28 cm - * 1001: 31 cm - * 1010: 34 cm - * 1011: 38 cm - * 1100: 41 cm - * 1101: 45 cm - * 1110: 48 cm - * 1111: 52 cm - */ -#define APEX_CONFIG12_FF_MIN_DURATION_SEL_POS 0x00 -#define APEX_CONFIG12_FF_MIN_DURATION_SEL_MASK 0x0f - - -/* --------------------------------------------------------------------------- - * register MREG3 - * ---------------------------------------------------------------------------*/ - -/* - * XA_ST_DATA - * Register Name : XA_ST_DATA - */ - -/* - * xa_st_data - * Accel X-axis self test data converted to 8 bit code. - */ -#define XA_ST_DATA_XA_ST_DATA_POS 0x00 -#define XA_ST_DATA_XA_ST_DATA_MASK 0xff - - - -/* - * YA_ST_DATA - * Register Name : YA_ST_DATA - */ - -/* - * ya_st_data - * Accel Y-axis self test data converted to 8 bit code. - */ -#define YA_ST_DATA_YA_ST_DATA_POS 0x00 -#define YA_ST_DATA_YA_ST_DATA_MASK 0xff - - - -/* - * ZA_ST_DATA - * Register Name : ZA_ST_DATA - */ - -/* - * za_st_data - * Accel Z-axis self test data converted to 8 bit code. - */ -#define ZA_ST_DATA_ZA_ST_DATA_POS 0x00 -#define ZA_ST_DATA_ZA_ST_DATA_MASK 0xff - - - -/* - * XG_ST_DATA - * Register Name : XG_ST_DATA - */ - -/* - * xg_st_data - * Gyro X-axis self test data converted to 8 bit code. - */ -#define XG_ST_DATA_XG_ST_DATA_POS 0x00 -#define XG_ST_DATA_XG_ST_DATA_MASK 0xff - - - -/* - * YG_ST_DATA - * Register Name : YG_ST_DATA - */ - -/* - * yg_st_data - * Gyro Y-axis self test data converted to 8 bit code. - */ -#define YG_ST_DATA_YG_ST_DATA_POS 0x00 -#define YG_ST_DATA_YG_ST_DATA_MASK 0xff - - - -/* - * ZG_ST_DATA - * Register Name : ZG_ST_DATA - */ - -/* - * zg_st_data - * Gyro Z-axis self test data converted to 8 bit code. - */ -#define ZG_ST_DATA_ZG_ST_DATA_POS 0x00 -#define ZG_ST_DATA_ZG_ST_DATA_MASK 0xff - - -/* --------------------------------------------------------------------------- - * register MREG2 - * ---------------------------------------------------------------------------*/ - -/* - * OTP_CTRL7 - * Register Name : OTP_CTRL7 - */ - -/* - * otp_reload - * 1: to trigger OTP copy operation. This bit is cleared to 0 after OTP copy is done. - * - * With otp_copy_mode[1:0] = 2'b01, it takes 280us to complete the OTP reloading operation. - * With otp_copy_mode[1:0] = 2'b11, it takes 20us to complete the OTP reloading operation. - */ -#define OTP_CTRL7_OTP_RELOAD_POS 0x03 -#define OTP_CTRL7_OTP_RELOAD_MASK (0x01 << OTP_CTRL7_OTP_RELOAD_POS) - -/* - * otp_pwr_down - * 0: Power up OTP to copy from OTP to SRAM - * 1: Power down OTP - * - * This bit is automatically set to 1 when OTP copy operation is complete. - */ -#define OTP_CTRL7_OTP_PWR_DOWN_POS 0x01 -#define OTP_CTRL7_OTP_PWR_DOWN_MASK (0x01 << OTP_CTRL7_OTP_PWR_DOWN_POS) - -#ifdef __cplusplus -} -#endif - -#endif /*#ifndef _INV_IMU_REGMAP_H_*/ diff --git a/lib/ICM42670P/src/imu/inv_imu_transport.c b/lib/ICM42670P/src/imu/inv_imu_transport.c deleted file mode 100644 index 2b16ac1..0000000 --- a/lib/ICM42670P/src/imu/inv_imu_transport.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * ________________________________________________________________________________________________________ - * Copyright (c) 2015-2015 InvenSense Inc. All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * ________________________________________________________________________________________________________ - */ - -#include "imu/inv_imu_extfunc.h" -#include "imu/inv_imu_transport.h" -#include "imu/inv_imu_regmap.h" - -#include "Invn/InvError.h" - -/* Function definition */ -static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg); -static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf); -static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf); -static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, - const uint8_t *buf); -static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf); - -int inv_imu_init_transport(struct inv_imu_device *s) -{ - int status = 0; - struct inv_imu_transport *t = (struct inv_imu_transport *)s; - - status |= read_sreg(s, (uint8_t)PWR_MGMT0, 1, &(t->register_cache.pwr_mgmt0_reg)); - status |= read_sreg(s, (uint8_t)GYRO_CONFIG0, 1, &(t->register_cache.gyro_config0_reg)); - status |= read_sreg(s, (uint8_t)ACCEL_CONFIG0, 1, &(t->register_cache.accel_config0_reg)); - - status |= - read_mclk_reg(s, (TMST_CONFIG1_MREG1 & 0xFFFF), 1, &(t->register_cache.tmst_config1_reg)); - - t->need_mclk_cnt = 0; - - return status; -} - -int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf) -{ - uint32_t i; - int rc = 0; - - for (i = 0; i < len; i++) { - uint8_t *cache_addr = get_register_cache_addr(s, reg + i); - - if (cache_addr) { - buf[i] = *cache_addr; - } else { - if (!(reg & 0x10000)) { - rc |= read_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]); - } else { - rc |= read_sreg(s, (uint8_t)reg + i, len - i, &buf[i]); - break; - } - } - } - - return rc; -} - -int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf) -{ - uint32_t i; - int rc = 0; - - for (i = 0; i < len; i++) { - uint8_t *cache_addr = get_register_cache_addr(s, reg + i); - - if (cache_addr) - *cache_addr = buf[i]; - - if (!(reg & 0x10000)) - rc |= write_mclk_reg(s, ((reg + i) & 0xFFFF), 1, &buf[i]); - } - - if (reg & 0x10000) - rc |= write_sreg(s, (uint8_t)reg, len, buf); - - return rc; -} - -int inv_imu_switch_on_mclk(struct inv_imu_device *s) -{ - int status = 0; - uint8_t data; - struct inv_imu_transport *t = (struct inv_imu_transport *)s; - uint64_t timeout_us = 1000000; /* 1 sec */ - uint64_t start; - uint64_t current; - - /* set IDLE bit only if it is not set yet */ - if (t->need_mclk_cnt == 0) { - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); - data |= PWR_MGMT0_IDLE_MASK; - status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data); - - if (status) - return status; - - /* Check if MCLK is ready */ - start = inv_imu_get_time_us(); - do { - status = inv_imu_read_reg(s, MCLK_RDY, 1, &data); - - if (status) - return status; - - /* Timeout */ - current = inv_imu_get_time_us(); - if (current - start > timeout_us) - return INV_ERROR_TIMEOUT; - - } while (!(data & MCLK_RDY_MCLK_RDY_MASK)); - } else { - /* Make sure it is already on */ - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); - if (0 == (data &= PWR_MGMT0_IDLE_MASK)) - status |= INV_ERROR; - } - - /* Increment the counter to keep track of number of MCLK requesters */ - t->need_mclk_cnt++; - - return status; -} - -int inv_imu_switch_off_mclk(struct inv_imu_device *s) -{ - int status = 0; - uint8_t data; - struct inv_imu_transport *t = (struct inv_imu_transport *)s; - - /* Reset the IDLE but only if there is one requester left */ - if (t->need_mclk_cnt == 1) { - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); - data &= ~PWR_MGMT0_IDLE_MASK; - status |= inv_imu_write_reg(s, PWR_MGMT0, 1, &data); - } else { - /* Make sure it is still on */ - status |= inv_imu_read_reg(s, PWR_MGMT0, 1, &data); - if (0 == (data &= PWR_MGMT0_IDLE_MASK)) - status |= INV_ERROR; - } - - /* Decrement the counter */ - t->need_mclk_cnt--; - - return status; -} - -/* Static function */ - -static uint8_t *get_register_cache_addr(struct inv_imu_device *s, uint32_t reg) -{ - struct inv_imu_transport *t = (struct inv_imu_transport *)s; - - switch (reg) { - case PWR_MGMT0: - return &(t->register_cache.pwr_mgmt0_reg); - case GYRO_CONFIG0: - return &(t->register_cache.gyro_config0_reg); - case ACCEL_CONFIG0: - return &(t->register_cache.accel_config0_reg); - case TMST_CONFIG1_MREG1: - return &(t->register_cache.tmst_config1_reg); - default: - return (uint8_t *)0; // Not found - } -} - -static int read_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, uint8_t *buf) -{ - struct inv_imu_serif *serif = (struct inv_imu_serif *)s; - - if (len > serif->max_read) - return INV_ERROR_SIZE; - - if (serif->read_reg(serif, reg, buf, len) != 0) - return INV_ERROR_TRANSPORT; - - return 0; -} - -static int write_sreg(struct inv_imu_device *s, uint8_t reg, uint32_t len, const uint8_t *buf) -{ - struct inv_imu_serif *serif = (struct inv_imu_serif *)s; - - if (len > serif->max_write) - return INV_ERROR_SIZE; - - if (serif->write_reg(serif, reg, buf, len) != 0) - return INV_ERROR_TRANSPORT; - - return 0; -} - -static int read_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t rd_cnt, uint8_t *buf) -{ - uint8_t data; - uint8_t blk_sel = (regaddr & 0xFF00) >> 8; - int status = 0; - - // Have IMU not in IDLE mode to access MCLK domain - status |= inv_imu_switch_on_mclk(s); - - // optimize by changing BLK_SEL only if not NULL - if (blk_sel) - status |= write_sreg(s, (uint8_t)BLK_SEL_R & 0xff, 1, &blk_sel); - - data = (regaddr & 0x00FF); - status |= write_sreg(s, (uint8_t)MADDR_R, 1, &data); - inv_imu_sleep_us(10); - status |= read_sreg(s, (uint8_t)M_R, rd_cnt, buf); - inv_imu_sleep_us(10); - - if (blk_sel) { - data = 0; - status |= write_sreg(s, (uint8_t)BLK_SEL_R, 1, &data); - } - - // switch OFF MCLK if needed - status |= inv_imu_switch_off_mclk(s); - - return status; -} - -static int write_mclk_reg(struct inv_imu_device *s, uint16_t regaddr, uint8_t wr_cnt, - const uint8_t *buf) -{ - uint8_t data; - uint8_t blk_sel = (regaddr & 0xFF00) >> 8; - int status = 0; - - // Have IMU not in IDLE mode to access MCLK domain - status |= inv_imu_switch_on_mclk(s); - - // optimize by changing BLK_SEL only if not NULL - if (blk_sel) - status |= write_sreg(s, (uint8_t)BLK_SEL_W, 1, &blk_sel); - - data = (regaddr & 0x00FF); - status |= write_sreg(s, (uint8_t)MADDR_W, 1, &data); - for (uint8_t i = 0; i < wr_cnt; i++) { - status |= write_sreg(s, (uint8_t)M_W, 1, &buf[i]); - inv_imu_sleep_us(10); - } - - if (blk_sel) { - data = 0; - status = write_sreg(s, (uint8_t)BLK_SEL_W, 1, &data); - } - - status |= inv_imu_switch_off_mclk(s); - - return status; -} diff --git a/lib/ICM42670P/src/imu/inv_imu_transport.h b/lib/ICM42670P/src/imu/inv_imu_transport.h deleted file mode 100644 index dd0845b..0000000 --- a/lib/ICM42670P/src/imu/inv_imu_transport.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * ________________________________________________________________________________________________________ - * Copyright (c) 2015-2015 InvenSense Inc. All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * ________________________________________________________________________________________________________ - */ - -/** @defgroup Transport Transport - * @brief Abstraction layer to access device's registers - * @{ - */ - -/** @file inv_imu_transport.h */ - -#ifndef _INV_IMU_TRANSPORT_H_ -#define _INV_IMU_TRANSPORT_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* forward declaration */ -struct inv_imu_device; - -/** Available serial interface type. */ -typedef enum { - UI_I2C, - UI_SPI4, - UI_SPI3 -} SERIAL_IF_TYPE_t; - -/** Serial interface definition */ -struct inv_imu_serif { - void *context; - int (*read_reg)(struct inv_imu_serif *serif, uint8_t reg, uint8_t *buf, uint32_t len); - int (*write_reg)(struct inv_imu_serif *serif, uint8_t reg, const uint8_t *buf, uint32_t len); - uint32_t max_read; - uint32_t max_write; - SERIAL_IF_TYPE_t serif_type; -}; - -/** Transport interface definition. */ -struct inv_imu_transport { - /** Serial interface object. - * @warning Must be the first object in this structure. - */ - struct inv_imu_serif serif; - - /** Contains mirrored values of some IP registers. */ - struct register_cache { - uint8_t pwr_mgmt0_reg; - uint8_t gyro_config0_reg; - uint8_t accel_config0_reg; - uint8_t tmst_config1_reg; - } register_cache; - - /** Internal counter for MCLK requests. */ - uint8_t need_mclk_cnt; -}; - -/** @brief Init cache variable. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_init_transport(struct inv_imu_device *s); - -/** @brief Reads data from a register on IMU. - * @param[in] s Pointer to device. - * @param[in] reg Register address to be read. - * @param[in] len Number of byte to be read. - * @param[out] buf Output data from the register. - * @return 0 on success, negative value on error. - */ -int inv_imu_read_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, uint8_t *buf); - -/** @brief Writes data to a register on IMU. - * @param[in] s Pointer to device. - * @param[in] reg Register address to be written. - * @param[in] len Number of byte to be written. - * @param[in] buf Input data to write. - * @return 0 on success, negative value on error. - */ -int inv_imu_write_reg(struct inv_imu_device *s, uint32_t reg, uint32_t len, const uint8_t *buf); - -/** @brief Enable MCLK. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_switch_on_mclk(struct inv_imu_device *s); - -/** @brief Disable MCLK. - * @param[in] s Pointer to device. - * @return 0 on success, negative value on error. - */ -int inv_imu_switch_off_mclk(struct inv_imu_device *s); - -#ifdef __cplusplus -} -#endif - -#endif /* _INV_IMU_TRANSPORT_H_ */ - -/** @} */ diff --git a/lib/ICM42670P/src/imu/inv_imu_version.h b/lib/ICM42670P/src/imu/inv_imu_version.h deleted file mode 100644 index a3fe291..0000000 --- a/lib/ICM42670P/src/imu/inv_imu_version.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * ________________________________________________________________________________________________________ - * Copyright (c) 2019 InvenSense Inc. All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively “Software”) is subject - * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * ________________________________________________________________________________________________________ - */ - -#ifndef _INV_IMU_VERSION_H_ -#define _INV_IMU_VERSION_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#define INV_IMU_VERSION_STRING "2.1.1" - -#ifdef __cplusplus -} -#endif - -#endif /* _INV_IMU_VERSION_H_ */ diff --git a/lib/ICM42670P/src/inv_time.c b/lib/ICM42670P/src/inv_time.c deleted file mode 100644 index 9f87bad..0000000 --- a/lib/ICM42670P/src/inv_time.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * ------------------------------------------------------------------------------------------------------------ - * Copyright (c) 2022 InvenSense, Inc All rights reserved. - * - * This software, related documentation and any modifications thereto (collectively "Software") is subject - * to InvenSense, Inc and its licencors' intellectual property rights under U.S. and international copyright - * and other intellectual property rights laws. - * - * InvenSense, Inc and its licencors retain all intellectual property and proprietary rights in and to the Software - * and any use, reproduction, disclosure or distribution of the Software without an express license agreement - * from InvenSense, Inc is strictly prohibited. - * - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS - * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. - * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL - * InvenSense, Inc BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY - * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THE SOFTWARE. - * - * ------------------------------------------------------------------------------------------------------------ - */ - -#include "Arduino.h" - -void inv_imu_sleep_us(uint32_t us) -{ - delayMicroseconds(us); -} - -uint64_t inv_imu_get_time_us(void) -{ - return (uint64_t)micros(); -} \ No newline at end of file From cd6af09e4969c7290b9499bb1acd6c82ccd89e14 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Tue, 10 Sep 2024 15:14:06 +1200 Subject: [PATCH 28/38] Common base class for IMUs --- lib/HeadTracker/ICMSeries.cpp | 29 ----------------------------- lib/HeadTracker/ICMSeries.h | 10 ++++------ lib/HeadTracker/IMUBase.cpp | 31 +++++++++++++++++++++++++++++++ lib/HeadTracker/IMUBase.h | 22 ++++++++++++++++++++++ lib/HeadTracker/MPU6050.cpp | 28 +++++----------------------- lib/HeadTracker/MPU6050.h | 5 ++++- lib/HeadTracker/QMI8658C.cpp | 34 +--------------------------------- lib/HeadTracker/QMI8658C.h | 13 +++++-------- 8 files changed, 72 insertions(+), 100 deletions(-) create mode 100644 lib/HeadTracker/IMUBase.cpp create mode 100644 lib/HeadTracker/IMUBase.h diff --git a/lib/HeadTracker/ICMSeries.cpp b/lib/HeadTracker/ICMSeries.cpp index 9446ff8..ab0dfcc 100644 --- a/lib/HeadTracker/ICMSeries.cpp +++ b/lib/HeadTracker/ICMSeries.cpp @@ -3,7 +3,6 @@ #include "Wire.h" #include "ICMSeries.h" -#define ADDR 0x68 #define WHO_AM_I 0x75 #define REVISION_ID 0x01 #define ACCEL_X1 0x0B @@ -12,13 +11,6 @@ #define ARES (16.0 / 32768) #define GRES (2000.0 / 32768) -void ICMSeries::writeRegister(uint8_t reg, uint8_t val) { - Wire.beginTransmission(ADDR); - Wire.write(reg); - Wire.write(val); - Wire.endTransmission(); -} - void ICMSeries::writeMem1Register(uint8_t reg, uint8_t val) { uint32_t t1 = millis(); writeRegister(0x1F, 0x10); @@ -28,27 +20,6 @@ void ICMSeries::writeMem1Register(uint8_t reg, uint8_t val) { writeRegister(0x1F, 0x00); } -uint8_t ICMSeries::readRegister(uint8_t reg) { - Wire.beginTransmission(ADDR); - Wire.write(reg); - Wire.endTransmission(); - Wire.requestFrom(ADDR, 1); - return Wire.read(); -} - -uint8_t ICMSeries::readBuffer(uint8_t reg, uint8_t *buffer, int length) { - uint32_t t1 = millis(); - Wire.beginTransmission(ADDR); - Wire.write(reg); - Wire.endTransmission(); - Wire.requestFrom(ADDR, length); - int count = 0; - while (Wire.available() && (millis() - t1 < 1000) && count < length) { - buffer[count++] = Wire.read(); - } - return count; -} - bool ICMSeries::initialize() { Wire.setTimeout(1000); Wire.setClock(1000000); diff --git a/lib/HeadTracker/ICMSeries.h b/lib/HeadTracker/ICMSeries.h index 4dab4b1..8153e67 100644 --- a/lib/HeadTracker/ICMSeries.h +++ b/lib/HeadTracker/ICMSeries.h @@ -2,18 +2,16 @@ #define BACKPACK_ICM_H #include "FusionMath.h" +#include "IMUBase.h" -class ICMSeries { +class ICMSeries : IMUBase { public: + ICMSeries() : IMUBase(0x68) {} bool initialize(); bool getDataFromRegisters(FusionVector &accel, FusionVector &gyro); private: - static void writeRegister(uint8_t reg, uint8_t val); - static uint8_t readRegister(uint8_t reg); - static uint8_t readBuffer(uint8_t reg, uint8_t *buffer, int length); - - static void writeMem1Register(uint8_t reg, uint8_t val); + void writeMem1Register(uint8_t reg, uint8_t val); }; #endif //BACKPACK_ICM_H diff --git a/lib/HeadTracker/IMUBase.cpp b/lib/HeadTracker/IMUBase.cpp new file mode 100644 index 0000000..beaa93a --- /dev/null +++ b/lib/HeadTracker/IMUBase.cpp @@ -0,0 +1,31 @@ +#include "IMUBase.h" + +#include "Wire.h" + +void IMUBase::writeRegister(uint8_t reg, uint8_t val) { + Wire.beginTransmission(address); + Wire.write(reg); + Wire.write(val); + Wire.endTransmission(); +} + +uint8_t IMUBase::readRegister(uint8_t reg) { + Wire.beginTransmission(address); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom(address, 1); + return Wire.read(); +} + +uint8_t IMUBase::readBuffer(uint8_t reg, uint8_t *buffer, int length) { + uint32_t t1 = millis(); + Wire.beginTransmission(address); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom(address, length); + int count = 0; + while (Wire.available() && (millis() - t1 < 1000) && count < length) { + buffer[count++] = Wire.read(); + } + return count; +} diff --git a/lib/HeadTracker/IMUBase.h b/lib/HeadTracker/IMUBase.h new file mode 100644 index 0000000..0025b1e --- /dev/null +++ b/lib/HeadTracker/IMUBase.h @@ -0,0 +1,22 @@ +#ifndef BACKPACK_IMUBASE_H +#define BACKPACK_IMUBASE_H + +#include "FusionMath.h" + +class IMUBase { +public: + explicit IMUBase(int _address) : address(_address) {} + + virtual bool initialize() = 0; + virtual bool getDataFromRegisters(FusionVector &accel, FusionVector &gyro) = 0; + +protected: + int address; + + void writeRegister(uint8_t reg, uint8_t val); + uint8_t readRegister(uint8_t reg); + + uint8_t readBuffer(uint8_t reg, uint8_t *buffer, int length); +}; + +#endif //BACKPACK_IMUBASE_H diff --git a/lib/HeadTracker/MPU6050.cpp b/lib/HeadTracker/MPU6050.cpp index f3be7a9..61866b0 100644 --- a/lib/HeadTracker/MPU6050.cpp +++ b/lib/HeadTracker/MPU6050.cpp @@ -1,9 +1,7 @@ #include "Wire.h" #include "MPU6050.h" -#define ADDR 0x68 #define WHO_AM_I 0x75 -#define INT_STATUS 0x3A #define ACCEL_X_H 0x3B #define ARES (16.0 / 32768) @@ -21,21 +19,13 @@ bool MPU6050::initialize() { 0x23, 0x78 }; Wire.setTimeout(1000); - Wire.beginTransmission(ADDR); - Wire.write(WHO_AM_I); - Wire.endTransmission(); - Wire.requestFrom(ADDR, 1); - if (((Wire.read() >> 1) & 0x3F) != 0x34) { + Wire.setClock(400000); + + if (((readRegister(WHO_AM_I) >> 1) & 0x3F) != 0x34) { return false; } for (int i=0 ; i Date: Tue, 10 Sep 2024 15:57:10 +1200 Subject: [PATCH 29/38] Refactoring --- lib/HeadTracker/ICMSeries.cpp | 4 + lib/HeadTracker/ICMSeries.h | 4 +- lib/HeadTracker/IMU.cpp | 203 ----------------------------- lib/HeadTracker/IMU.h | 23 ---- lib/HeadTracker/IMUBase.cpp | 117 +++++++++++++++++ lib/HeadTracker/IMUBase.h | 24 +++- lib/HeadTracker/MPU6050.cpp | 8 +- lib/HeadTracker/MPU6050.h | 4 +- lib/HeadTracker/QMI8658C.cpp | 5 + lib/HeadTracker/QMI8658C.h | 2 + lib/HeadTracker/devHeadTracker.cpp | 38 ++++-- 11 files changed, 189 insertions(+), 243 deletions(-) delete mode 100644 lib/HeadTracker/IMU.cpp delete mode 100644 lib/HeadTracker/IMU.h diff --git a/lib/HeadTracker/ICMSeries.cpp b/lib/HeadTracker/ICMSeries.cpp index ab0dfcc..691b7d0 100644 --- a/lib/HeadTracker/ICMSeries.cpp +++ b/lib/HeadTracker/ICMSeries.cpp @@ -60,6 +60,10 @@ bool ICMSeries::initialize() { writeRegister(0x20, 9); // 2000dps, 100Hz writeRegister(0x1F, 0x0F); // Low noise mode for Accel/Gyro + setInterruptHandler(PIN_INT); + gyroRange = 2000.0; + gRes = 2000.0 / 32768; + return true; } diff --git a/lib/HeadTracker/ICMSeries.h b/lib/HeadTracker/ICMSeries.h index 8153e67..f28a76f 100644 --- a/lib/HeadTracker/ICMSeries.h +++ b/lib/HeadTracker/ICMSeries.h @@ -4,10 +4,12 @@ #include "FusionMath.h" #include "IMUBase.h" -class ICMSeries : IMUBase { +class ICMSeries : public IMUBase { public: ICMSeries() : IMUBase(0x68) {} bool initialize(); + +protected: bool getDataFromRegisters(FusionVector &accel, FusionVector &gyro); private: diff --git a/lib/HeadTracker/IMU.cpp b/lib/HeadTracker/IMU.cpp deleted file mode 100644 index c41023c..0000000 --- a/lib/HeadTracker/IMU.cpp +++ /dev/null @@ -1,203 +0,0 @@ -#if defined(HAS_HEADTRACKING) -#include "Arduino.h" -#include "Wire.h" -#include "logging.h" - -#include "IMU.h" - -#include "ICMSeries.h" -#include "MPU6050.h" -#include "QMI8658C.h" - -static enum { - IMU_ICM42670P, - IMU_MPU6050, - IMU_QMI8658C -} deviceType; - -static void *device; -static float aRes; -static float gRes; - -// Calibration data -constexpr int Loops = 20; -static float imuCalibration[3]; -static double kP, kI; -static int16_t eSample; -static float ITerm[3] = {0 ,0, 0}; -static int c, L; - -static volatile bool irq_received; - -static IRAM_ATTR void irq_handler() { - irq_received = true; -} - -bool IMU::initialize() { - Wire.setClock(400000); - DBGLN("Try MPU6050"); - auto *mpu6050 = new MPU6050(); - if (mpu6050->initialize()) { - DBGLN("Found MPU6050"); - pinMode(PIN_INT, INPUT_PULLUP); - attachInterrupt(PIN_INT, irq_handler, RISING); - sampleRate = 100; - - aRes = 16.0/32768; - gRes = 2000.0/32768; - - device = mpu6050; - deviceType = IMU_MPU6050; - return true; - } - delete mpu6050; - - DBGLN("Try QMI8658C"); - auto *qmi8658c = new QMI8658C(); - if (qmi8658c->initialize()) { - DBGLN("Found QMI8658C"); - pinMode(PIN_INT, INPUT_PULLUP); - attachInterrupt(PIN_INT, irq_handler, RISING); - sampleRate = 100; - - aRes = 16.0/32768; - gRes = 2000.0/32768; - - device = qmi8658c; - deviceType = IMU_QMI8658C; - return true; - } - delete qmi8658c; - - DBGLN("Try ICM42670P"); - auto *imu = new ICMSeries(); - if (imu->initialize()) { - DBGLN("Found ICM42670P"); - pinMode(PIN_INT, INPUT_PULLUP); - attachInterrupt(PIN_INT, irq_handler, RISING); - sampleRate = 100; - - aRes = 16.0/32768; - gRes = 2000.0/32768; - - device = imu; - deviceType = IMU_ICM42670P; - return true; - } - delete imu; - - DBGLN("IMU initialization failed: No IMU"); - return false; -} - -bool IMU::readIMUData(FusionVector &accel, FusionVector &gyro) { - bool hasData = irq_received; - if (!hasData) return false; - - switch (deviceType) { - case IMU_ICM42670P: { - if (!((ICMSeries *) device)->getDataFromRegisters(accel, gyro)) - return false; - break; - } - - case IMU_MPU6050: { - if (!((MPU6050 *) device)->getDataFromRegisters(accel, gyro)) - return false; - break; - } - - case IMU_QMI8658C: { - if (!((QMI8658C *) device)->getDataFromRegisters(accel, gyro)) - return false; - break; - } - } - gyro.axis.x += imuCalibration[0]; - gyro.axis.y += imuCalibration[1]; - gyro.axis.z += imuCalibration[2]; - irq_received = false; - return true; -} - -void IMU::BeginCalibration() -{ - kP = 0.5; - kI = 150; - float x = (100 - map(Loops, 1, 5, 20, 0)) * .01; - kP *= x; - kI *= x; - - c = 0; - L = 0; - eSample = 0; - ITerm[0] = 0; - ITerm[1] = 0; - ITerm[2] = 0; -} - -bool IMU::UpdateCalibration(FusionVector &g) -{ - float eSum = 0; - float Data, Error, PTerm; - - ITerm[0] = imuCalibration[0]; - ITerm[1] = imuCalibration[1]; - ITerm[2] = imuCalibration[2]; - - Error = -g.axis.x; - eSum += abs(g.axis.x); - PTerm = kP * Error; - ITerm[0] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 - Data = PTerm + ITerm[0]; //Compute PID Output - imuCalibration[0] = Data; - - Error = -g.axis.y; - eSum += abs(g.axis.y); - PTerm = kP * Error; - ITerm[1] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 - Data = PTerm + ITerm[1]; //Compute PID Output - imuCalibration[1] = Data; - - Error = -g.axis.z; - eSum += abs(g.axis.z); - PTerm = kP * Error; - ITerm[2] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 - Data = PTerm + ITerm[2]; //Compute PID Output - imuCalibration[2] = Data; - - c++; - if((c == 99) && eSum > 1000 * gRes) { // Error is still to great to continue - c = 0; - DBGLN("Calibration retry %d", (int)eSum); - } - if(eSum < 5 * gRes) eSample++; // Successfully found offsets prepare to advance - if(c == 100 || ((eSum < 100 * gRes) && (c > 10) && (eSample >= 10))) { // Advance to next Loop - imuCalibration[0] = ITerm[0]; - imuCalibration[1] = ITerm[1]; - imuCalibration[2] = ITerm[2]; - kP *= .75; - kI *= .75; - c = 0; - eSample = 0; - L++; - DBGLN("Finished loop iteration %d", L); - Serial.printf("%6.2f %6.2f %6.2f\r\n", imuCalibration[0], imuCalibration[1], imuCalibration[2]); - } - return L == Loops; -} - -void IMU::SetCalibration(float (*calibration)[3]) -{ - memcpy(imuCalibration, calibration, sizeof(imuCalibration)); - for (float & i : imuCalibration) { - if (i > 1000 || i < -1000 || isnan(i)) { - i = 0; - } - } -} - -const float *IMU::GetCalibration() { - return imuCalibration; -} -#endif \ No newline at end of file diff --git a/lib/HeadTracker/IMU.h b/lib/HeadTracker/IMU.h deleted file mode 100644 index b7882d3..0000000 --- a/lib/HeadTracker/IMU.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef BACKPACK_IMU_H -#define BACKPACK_IMU_H - -#include "Fusion.h" - -class IMU { -public: - bool initialize(); - static bool readIMUData(FusionVector &accel, FusionVector &gyro); - int getSampleRate() const { return sampleRate; } - - static void BeginCalibration(); - static bool UpdateCalibration(FusionVector &g); - - static void SetCalibration(float (*calibration)[3]); - - static const float *GetCalibration(); - -private: - int sampleRate = 0; -}; - -#endif //BACKPACK_IMU_H diff --git a/lib/HeadTracker/IMUBase.cpp b/lib/HeadTracker/IMUBase.cpp index beaa93a..c47bdb0 100644 --- a/lib/HeadTracker/IMUBase.cpp +++ b/lib/HeadTracker/IMUBase.cpp @@ -1,7 +1,29 @@ +#include "Arduino.h" +#include "logging.h" + #include "IMUBase.h" #include "Wire.h" +// Calibration data +constexpr int Loops = 20; +static float imuCalibration[3]; +static double kP, kI; +static int16_t eSample; +static float ITerm[3] = {0 ,0, 0}; +static int c, L; + +static volatile bool irq_received; + +static IRAM_ATTR void irq_handler() { + irq_received = true; +} + +void IMUBase::setInterruptHandler(int pin) { + pinMode(pin, INPUT_PULLUP); + attachInterrupt(pin, irq_handler, RISING); +} + void IMUBase::writeRegister(uint8_t reg, uint8_t val) { Wire.beginTransmission(address); Wire.write(reg); @@ -29,3 +51,98 @@ uint8_t IMUBase::readBuffer(uint8_t reg, uint8_t *buffer, int length) { } return count; } + + +bool IMUBase::readIMUData(FusionVector &accel, FusionVector &gyro) { + bool hasData = irq_received; + if (!hasData) return false; + if (!getDataFromRegisters(accel, gyro)) { + return false; + } + gyro.axis.x += imuCalibration[0]; + gyro.axis.y += imuCalibration[1]; + gyro.axis.z += imuCalibration[2]; + irq_received = false; + return true; +} + +void IMUBase::beginCalibration() +{ + kP = 0.5; + kI = 150; + float x = (100 - map(Loops, 1, 5, 20, 0)) * .01; + kP *= x; + kI *= x; + + c = 0; + L = 0; + eSample = 0; + ITerm[0] = 0; + ITerm[1] = 0; + ITerm[2] = 0; +} + +bool IMUBase::updateCalibration(FusionVector &g) +{ + float eSum = 0; + float Data, Error, PTerm; + + ITerm[0] = imuCalibration[0]; + ITerm[1] = imuCalibration[1]; + ITerm[2] = imuCalibration[2]; + + Error = -g.axis.x; + eSum += abs(g.axis.x); + PTerm = kP * Error; + ITerm[0] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 + Data = PTerm + ITerm[0]; //Compute PID Output + imuCalibration[0] = Data; + + Error = -g.axis.y; + eSum += abs(g.axis.y); + PTerm = kP * Error; + ITerm[1] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 + Data = PTerm + ITerm[1]; //Compute PID Output + imuCalibration[1] = Data; + + Error = -g.axis.z; + eSum += abs(g.axis.z); + PTerm = kP * Error; + ITerm[2] += (Error * 0.001) * kI; // Integral term 1000 Calculations a second = 0.001 + Data = PTerm + ITerm[2]; //Compute PID Output + imuCalibration[2] = Data; + + c++; + if((c == 99) && eSum > 1000 * gRes) { // Error is still to great to continue + c = 0; + DBGLN("Calibration retry %d", (int)eSum); + } + if(eSum < 5 * gRes) eSample++; // Successfully found offsets prepare to advance + if(c == 100 || ((eSum < 100 * gRes) && (c > 10) && (eSample >= 10))) { // Advance to next Loop + imuCalibration[0] = ITerm[0]; + imuCalibration[1] = ITerm[1]; + imuCalibration[2] = ITerm[2]; + kP *= .75; + kI *= .75; + c = 0; + eSample = 0; + L++; + DBGLN("Finished loop iteration %d", L); + Serial.printf("%6.2f %6.2f %6.2f\r\n", imuCalibration[0], imuCalibration[1], imuCalibration[2]); + } + return L == Loops; +} + +void IMUBase::setCalibration(float (*calibration)[3]) +{ + memcpy(imuCalibration, calibration, sizeof(imuCalibration)); + for (float & i : imuCalibration) { + if (i > 1000 || i < -1000 || isnan(i)) { + i = 0; + } + } +} + +const float *IMUBase::getCalibration() { + return imuCalibration; +} diff --git a/lib/HeadTracker/IMUBase.h b/lib/HeadTracker/IMUBase.h index 0025b1e..eaa867f 100644 --- a/lib/HeadTracker/IMUBase.h +++ b/lib/HeadTracker/IMUBase.h @@ -5,18 +5,38 @@ class IMUBase { public: - explicit IMUBase(int _address) : address(_address) {} + virtual ~IMUBase() = default; virtual bool initialize() = 0; - virtual bool getDataFromRegisters(FusionVector &accel, FusionVector &gyro) = 0; + + bool readIMUData(FusionVector &accel, FusionVector &gyro); + + void beginCalibration(); + bool updateCalibration(FusionVector &g); + + void setCalibration(float (*calibration)[3]); + + const float *getCalibration(); + + int getSampleRate() const { return sampleRate; } + float getGyroRange() const { return gyroRange; } protected: int address; + int sampleRate = 0; + float gyroRange; + float gRes = 0; + + explicit IMUBase(int _address) : address(_address) {} void writeRegister(uint8_t reg, uint8_t val); uint8_t readRegister(uint8_t reg); uint8_t readBuffer(uint8_t reg, uint8_t *buffer, int length); + + virtual bool getDataFromRegisters(FusionVector &accel, FusionVector &gyro) = 0; + + void setInterruptHandler(int pin); }; #endif //BACKPACK_IMUBASE_H diff --git a/lib/HeadTracker/MPU6050.cpp b/lib/HeadTracker/MPU6050.cpp index 61866b0..365f70c 100644 --- a/lib/HeadTracker/MPU6050.cpp +++ b/lib/HeadTracker/MPU6050.cpp @@ -1,3 +1,4 @@ +#include "Arduino.h" #include "Wire.h" #include "MPU6050.h" @@ -24,9 +25,14 @@ bool MPU6050::initialize() { if (((readRegister(WHO_AM_I) >> 1) & 0x3F) != 0x34) { return false; } - for (int i=0 ; iinitialize()) { - ht_state = STATE_ERROR; - return; + delete imu; + imu = new MPU6050(); + if (!imu->initialize()) { + delete imu; + imu = new QMI8658C(); + if (!imu->initialize()) { + delete imu; + ht_state = STATE_ERROR; + return; + } + } } FusionAhrsInitialise(&ahrs); @@ -45,10 +59,10 @@ static void initialize() const FusionAhrsSettings settings = { .convention = FusionConventionNwu, .gain = 0.5f, - .gyroscopeRange = 2000.0f, /* replace this with actual gyroscope range in degrees/s */ + .gyroscopeRange = imu->getGyroRange(), /* replace this with actual gyroscope range in degrees/s */ .accelerationRejection = 10.0f, .magneticRejection = 10.0f, - .recoveryTriggerPeriod = 5U * imu.getSampleRate(), /* 5 seconds */ + .recoveryTriggerPeriod = 5U * imu->getSampleRate(), /* 5 seconds */ }; FusionAhrsSetSettings(&ahrs, &settings); DBGLN("starting head tracker"); @@ -66,7 +80,7 @@ static int start() int (*cal)[3][2] = config.GetCompassCalibration(); compass.setCalibration((*cal)[0][0],(*cal)[0][1],(*cal)[1][0],(*cal)[1][1],(*cal)[2][0],(*cal)[2][1]); } - imu.SetCalibration(config.GetIMUCalibration()); + imu->setCalibration(config.GetIMUCalibration()); memcpy(orientation, *config.GetBoardOrientation(), sizeof(orientation)); #ifdef DEBUG_LOG @@ -125,7 +139,7 @@ static int timeout() FusionVector a; FusionVector g; - if (!imu.readIMUData(a, g)) + if (!imu->readIMUData(a, g)) return DURATION_IMMEDIATELY; switch (ht_state) { @@ -204,8 +218,8 @@ static int timeout() } case STATE_IMU_CALIBRATING: { - if (imu.UpdateCalibration(g)) { - config.SetIMUCalibration(imu.GetCalibration()); + if (imu->updateCalibration(g)) { + config.SetIMUCalibration(imu->getCalibration()); config.Commit(); ht_state = STATE_RUNNING; } @@ -230,7 +244,7 @@ void startCompassCalibration() void startIMUCalibration() { - imu.BeginCalibration(); + imu->beginCalibration(); cal_started = millis(); ht_state = STATE_IMU_CALIBRATING; } From f82ec266dd90b5a86730b5e8be5bb9b0a9ab6180 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Tue, 10 Sep 2024 16:54:31 +1200 Subject: [PATCH 30/38] I2C common settings outside the IMU code --- lib/HeadTracker/ICMSeries.cpp | 4 ---- lib/HeadTracker/MPU6050.cpp | 2 -- lib/HeadTracker/QMI8658C.cpp | 3 --- lib/HeadTracker/devHeadTracker.cpp | 25 +++++++++++++++++-------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/HeadTracker/ICMSeries.cpp b/lib/HeadTracker/ICMSeries.cpp index 691b7d0..761af3f 100644 --- a/lib/HeadTracker/ICMSeries.cpp +++ b/lib/HeadTracker/ICMSeries.cpp @@ -21,11 +21,7 @@ void ICMSeries::writeMem1Register(uint8_t reg, uint8_t val) { } bool ICMSeries::initialize() { - Wire.setTimeout(1000); - Wire.setClock(1000000); - int whoami = readRegister(WHO_AM_I); - DBGLN("WHO_AM_I %x", whoami); if (whoami != 0x60 && whoami != 0x61 && whoami != 0x67 && whoami != 0x69 && whoami != 0x64) { return false; } diff --git a/lib/HeadTracker/MPU6050.cpp b/lib/HeadTracker/MPU6050.cpp index 365f70c..6b3c24b 100644 --- a/lib/HeadTracker/MPU6050.cpp +++ b/lib/HeadTracker/MPU6050.cpp @@ -19,8 +19,6 @@ bool MPU6050::initialize() { 0x38, 0x01, 0x23, 0x78 }; - Wire.setTimeout(1000); - Wire.setClock(400000); if (((readRegister(WHO_AM_I) >> 1) & 0x3F) != 0x34) { return false; diff --git a/lib/HeadTracker/QMI8658C.cpp b/lib/HeadTracker/QMI8658C.cpp index 54ccb9a..9cf69cf 100644 --- a/lib/HeadTracker/QMI8658C.cpp +++ b/lib/HeadTracker/QMI8658C.cpp @@ -40,9 +40,6 @@ int QMI8658C::writeCommand(uint8_t cmd) } bool QMI8658C::initialize() { - Wire.setTimeout(1000); - Wire.setClock(400000); - writeRegister(0x60, 0xB0); // Reset while(!(readRegister(0x4D) & 0x80)); diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index da57dd4..246b7ab 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -29,8 +29,9 @@ static uint32_t cal_started; static void initialize() { - Wire.setClock(400000); Wire.begin(PIN_SDA, PIN_SCL); + Wire.setClock(400000); + Wire.setTimeout(1000); // Compass init first compass.init(); @@ -38,15 +39,23 @@ static void initialize() if (hasCompass) compass.setMode(0x01,0x08,0x10,0X00); // continuous, 100Hz, 8G, 512 over sample // Initializing the IMU - imu = new ICMSeries(); - if (!imu->initialize()) - { + imu = new MPU6050(); + if (imu->initialize()) { + DBGLN("Found MPU6050 IMU") + } + else { delete imu; - imu = new MPU6050(); - if (!imu->initialize()) { + imu = new QMI8658C(); + if (imu->initialize()) { + DBGLN("Found QMI8658C IMU") + } + else { delete imu; - imu = new QMI8658C(); - if (!imu->initialize()) { + imu = new ICMSeries(); + if (imu->initialize()) { + DBGLN("Found ICM Series IMU") + } + else { delete imu; ht_state = STATE_ERROR; return; From 1c8d50d0ee29434e80832b8f18011a3b2bf3789c Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Tue, 10 Sep 2024 19:35:41 +1200 Subject: [PATCH 31/38] Code docs and simplification --- lib/HeadTracker/ICMSeries.cpp | 52 +++++++++--------------------- lib/HeadTracker/IMUBase.h | 3 +- lib/HeadTracker/MPU6050.cpp | 29 +++++++---------- lib/HeadTracker/QMI8658C.cpp | 23 +++++++------ lib/HeadTracker/devHeadTracker.cpp | 2 +- 5 files changed, 40 insertions(+), 69 deletions(-) diff --git a/lib/HeadTracker/ICMSeries.cpp b/lib/HeadTracker/ICMSeries.cpp index 761af3f..bd1deca 100644 --- a/lib/HeadTracker/ICMSeries.cpp +++ b/lib/HeadTracker/ICMSeries.cpp @@ -1,5 +1,4 @@ #include "Arduino.h" -#include "logging.h" #include "Wire.h" #include "ICMSeries.h" @@ -11,54 +10,33 @@ #define ARES (16.0 / 32768) #define GRES (2000.0 / 32768) -void ICMSeries::writeMem1Register(uint8_t reg, uint8_t val) { - uint32_t t1 = millis(); - writeRegister(0x1F, 0x10); - while (!(readRegister(0x00) & 0x08) && (millis() - t1 < 1000)); - writeRegister(0x7A, reg); - writeRegister(0x7B, val); - writeRegister(0x1F, 0x00); -} - bool ICMSeries::initialize() { + // Make sure it's one of the supported ICM IMUs + // ICM42607P, ICM42607C, ICM42670P, ICM42670S, ICM42670T int whoami = readRegister(WHO_AM_I); if (whoami != 0x60 && whoami != 0x61 && whoami != 0x67 && whoami != 0x69 && whoami != 0x64) { return false; } /* Reset device */ - /* Ensure BLK_SEL_R and BLK_SEL_W are set to 0 */ - writeRegister(0x7C, 0); - writeRegister(0x79, 0); - writeRegister(0x02, 0x10); + writeRegister(0x7C, 0); // BLK_SEL_R + writeRegister(0x79, 0); // BLK_SEL_W + writeRegister(0x02, 0x10); // SIGNAL_PATH_RESET (soft reset device) delay(1); - writeRegister(0x01, 0x04); - writeRegister(0x36, 0x41); - - /* Clear the reset done interrupt */ - readRegister(0x3A); - - writeMem1Register(0x03, 0x00); - - writeRegister(0x06, 0x03); - writeRegister(0x35, 0x50); - writeMem1Register(0x00, 0x09); - writeRegister(0x29, 0x01); + writeRegister(0x01, 0x04); // DEVICE_CONFIG + writeRegister(0x36, 0x41); // INF_CONFIG1, i2c mode, PLL clock - writeMem1Register(0x02, 0x01); - writeMem1Register(0x01, 0x24); + readRegister(0x3A); // Clear the reset done interrupt - writeRegister(0x2B, 0x1C); - writeRegister(0x2C, 0x0F); - writeMem1Register(0x2F, 0xF8); + writeRegister(0x06, 0x03); // INT_CONFIG, interrupt push-pull, active high + writeRegister(0x2B, 0x08); // INT_SOURCE0, drdy - writeRegister(0x21, 9); // 16G, 100Hz - writeRegister(0x20, 9); // 2000dps, 100Hz - writeRegister(0x1F, 0x0F); // Low noise mode for Accel/Gyro + writeRegister(0x21, 9); // 16G, 100Hz + writeRegister(0x20, 9); // 2000dps, 100Hz + writeRegister(0x1F, 0x0F); // Low noise mode for Accel/Gyro - setInterruptHandler(PIN_INT); gyroRange = 2000.0; - gRes = 2000.0 / 32768; + gRes = GRES; return true; } @@ -71,7 +49,7 @@ bool ICMSeries::getDataFromRegisters(FusionVector &accel, FusionVector &gyro) { return false; } - // read teh accel and gyro data + // read the accel and gyro data readBuffer(ACCEL_X1, values, 12); // map into Gs and dps diff --git a/lib/HeadTracker/IMUBase.h b/lib/HeadTracker/IMUBase.h index eaa867f..b0b33e2 100644 --- a/lib/HeadTracker/IMUBase.h +++ b/lib/HeadTracker/IMUBase.h @@ -8,6 +8,7 @@ class IMUBase { virtual ~IMUBase() = default; virtual bool initialize() = 0; + void setInterruptHandler(int pin); bool readIMUData(FusionVector &accel, FusionVector &gyro); @@ -35,8 +36,6 @@ class IMUBase { uint8_t readBuffer(uint8_t reg, uint8_t *buffer, int length); virtual bool getDataFromRegisters(FusionVector &accel, FusionVector &gyro) = 0; - - void setInterruptHandler(int pin); }; #endif //BACKPACK_IMUBASE_H diff --git a/lib/HeadTracker/MPU6050.cpp b/lib/HeadTracker/MPU6050.cpp index 6b3c24b..8e89462 100644 --- a/lib/HeadTracker/MPU6050.cpp +++ b/lib/HeadTracker/MPU6050.cpp @@ -1,5 +1,4 @@ #include "Arduino.h" -#include "Wire.h" #include "MPU6050.h" #define WHO_AM_I 0x75 @@ -9,27 +8,22 @@ #define GRES (2000.0 / 32768) bool MPU6050::initialize() { - static uint8_t INIT_DATA[] = { - 0x6B, 0x01, - 0x1B, 0x18, - 0x1C, 0x18, - 0x1A, 0x01, - 0x19, 0x09, - 0x37, 0x02, - 0x38, 0x01, - 0x23, 0x78 - }; - + // First check if this an MPU6050 if (((readRegister(WHO_AM_I) >> 1) & 0x3F) != 0x34) { return false; } - for (uint32_t i=0 ; isetInterruptHandler(PIN_INT); FusionAhrsInitialise(&ahrs); // Set AHRS algorithm settings From 2e1f907ae2f8f8fa6eb769ee0dbc4d5c267752c2 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Wed, 11 Sep 2024 17:12:58 +1200 Subject: [PATCH 32/38] HDZero goggle support for headtracking view page --- html/scan.js | 7 +++- html/vrx_index.html | 4 +- lib/HeadTracker/devHeadTracker.cpp | 35 ++++++++++++++-- lib/WIFI/devWIFI.cpp | 64 +++++++++++++++++------------- src/Vrx_main.cpp | 1 - src/module_base.cpp | 4 ++ targets/debug.ini | 14 +++---- targets/hdzero.ini | 1 + 8 files changed, 86 insertions(+), 44 deletions(-) diff --git a/html/scan.js b/html/scan.js index 7dfcd86..60c3bf7 100644 --- a/html/scan.js +++ b/html/scan.js @@ -44,6 +44,8 @@ function updateConfig(data) { _('stamode').style.display = 'block'; if (!config['aat']) { if (_('rtctab')) _('rtctab').style.display = 'table-cell'; + } + if (config['head-tracking']) { if (_('httab')) _('httab').style.display = 'table-cell'; } _('ssid').textContent = config.ssid; @@ -607,7 +609,10 @@ function start() { _('label-x').textContent = d.pitch; _('label-y').textContent = d.roll; _('label-z').textContent = d.heading; - } + if (!d.hasIMU) { + show(document.querySelectorAll('.hasIMU'), 'none'); + } + } if (d['heading']) { Euler = d; _('angle-x').textContent = Euler.pitch; diff --git a/html/vrx_index.html b/html/vrx_index.html index c20c5d0..5d5bbf3 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -110,13 +110,13 @@

RTC Update via NTP

-
+
-
+
To set the board orientation, you need to think about the transformations required to take the board from flat and level and pointing away from you using the right-hand rule to its mounted orientation. diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index 7f0927d..29a99d9 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -1,12 +1,15 @@ #include -#if defined(HAS_HEADTRACKING) #include -#include "config.h" -#include "logging.h" + +#include "crsf_protocol.h" #include "devHeadTracker.h" +#if defined(HAS_HEADTRACKING) +#include "config.h" +#include "logging.h" + #include "QMC5883LCompass.h" #include "IMUBase.h" #include "ICMSeries.h" @@ -316,4 +319,30 @@ device_t HeadTracker_device = { .timeout = timeout }; +#endif +#if defined(SUPPORT_HEADTRACKING) +extern int16_t ptrChannelData[3]; + +HeadTrackerState getHeadTrackerState() +{ + return STATE_RUNNING; +} + +float fmap(float x, float in_min, float in_max, float out_min, float out_max) { + const float run = in_max - in_min; + const float rise = out_max - out_min; + const float delta = x - in_min; + return (delta * rise) / run + out_min; +} + +void resetCenter() +{ +} + +void getEuler(float *yaw, float *pitch, float *roll) +{ + *yaw = fmap(ptrChannelData[0], CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000, -180.0, 180.0); + *pitch = fmap(ptrChannelData[2], CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000, -180.0, 180.0); + *roll = -fmap(ptrChannelData[1], CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000, -180.0, 180.0); +} #endif \ No newline at end of file diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index 04b5fac..d92db5a 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -39,8 +39,10 @@ #include #endif #if defined(TARGET_VRX_BACKPACK) -#if defined(HAS_HEADTRACKING) +#if defined(HAS_HEADTRACKING)|| defined(SUPPORT_HEADTRACKING) #include "devHeadTracker.h" +#include "crsf_protocol.h" + #endif extern VrxBackpackConfig config; extern bool sendRTCChangesToVrx; @@ -91,8 +93,10 @@ static IPAddress apBroadcast(10, 0, 0, 255); static DNSServer dnsServer; static AsyncWebServer server(80); -#if defined(HAS_HEADTRACKING) +#if defined(HAS_HEADTRACKING)|| defined(SUPPORT_HEADTRACKING) static AsyncWebSocket ws("/ws"); +extern bool sendHeadTrackingChangesToVrx; +extern bool headTrackingEnabled; #endif static bool servicesStarted = false; @@ -162,36 +166,36 @@ static struct { {"/mui.js", "text/javascript", (uint8_t *)MUI_JS, sizeof(MUI_JS)}, {"/scan.js", "text/javascript", (uint8_t *)SCAN_JS, sizeof(SCAN_JS)}, {"/logo.svg", "image/svg+xml", (uint8_t *)LOGO_SVG, sizeof(LOGO_SVG)}, -#if defined(HAS_HEADTRACKING) +#if defined(HAS_HEADTRACKING) || defined(SUPPORT_HEADTRACKING) {"/airplane.obj", "text/plain", (uint8_t *)PLANE_OBJ, sizeof(PLANE_OBJ)}, {"/texture.gif", "image/gif", (uint8_t *)TEXTURE_GIF, sizeof(TEXTURE_GIF)}, {"/p5.js", "text/javascript", (uint8_t *)P5_JS, sizeof(P5_JS)}, #endif }; -#if defined(HAS_HEADTRACKING) +#if defined(HAS_HEADTRACKING) || defined(SUPPORT_HEADTRACKING) static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { if (type == WS_EVT_CONNECT) { - static const char IMU_JSON[] PROGMEM = R"=====({"orientation":true,"heading":%f,"pitch":%f,"roll":%f})====="; + static const char IMU_JSON[] PROGMEM = R"=====({"orientation":true,"heading":%f,"pitch":%f,"roll":%f,"hasIMU":%s})====="; // Send JSON over websocket - char payload[80]; + char payload[128]; +#if defined(HAS_HEADTRACKING) float (*o)[3] = config.GetBoardOrientation(); - snprintf_P(payload, sizeof(payload), IMU_JSON, (*o)[2] * RAD_TO_DEG, (*o)[0] * RAD_TO_DEG, (*o)[1] * RAD_TO_DEG); + snprintf_P(payload, sizeof(payload), IMU_JSON, (*o)[2] * RAD_TO_DEG, (*o)[0] * RAD_TO_DEG, (*o)[1] * RAD_TO_DEG, "true"); +#else + snprintf_P(payload, sizeof(payload), IMU_JSON, 0.0, 0.0, 0.0, "false"); +#endif ws.text(client->id(), payload, strlen(payload)); + headTrackingEnabled = true; + sendHeadTrackingChangesToVrx = true; } if (type == WS_EVT_DATA) { - if (memcmp(data, "cc", 2) == 0) { - startCompassCalibration(); - } else if (memcmp(data, "ci", 2) == 0) { - startIMUCalibration(); - } else if (memcmp(data, "sc", 2) == 0) { + if (memcmp(data, "sc", 2) == 0) { resetCenter(); - } else if (memcmp(data, "ro", 2) == 0) { - resetBoardOrientation(); - } else if (memcmp(data, "sv", 2) == 0) { - saveBoardOrientation(); - } else if (memcmp(data, "o:", 2) == 0) { + } +#if defined(HAS_HEADTRACKING) + if (memcmp(data, "o:", 2) == 0) { char buf[64]; memcpy(buf, data+2, len-2); buf[len-2] = 0; @@ -203,7 +207,16 @@ static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, Aw int y = atoi(colon+1); int z = atoi(colon2+1); setBoardOrientation(x, y, z); + } else if (memcmp(data, "cc", 2) == 0) { + startCompassCalibration(); + } else if (memcmp(data, "ci", 2) == 0) { + startIMUCalibration(); + } else if (memcmp(data, "ro", 2) == 0) { + resetBoardOrientation(); + } else if (memcmp(data, "sv", 2) == 0) { + saveBoardOrientation(); } +#endif } } #endif @@ -248,6 +261,9 @@ static void GetConfiguration(AsyncWebServerRequest *request) #if defined(STM32_TX_BACKPACK) json["stm32"] = "yes"; #endif +#if defined(HAS_HEADTRACKING) || defined(SUPPORT_HEADTRACKING) + json["config"]["head-tracking"] = true; +#endif #if defined(AAT_BACKPACK) WebAatAppendConfig(json); #endif @@ -682,7 +698,7 @@ static void startServices() server.on(files[i].url, WebUpdateSendContent); } - #if defined(HAS_HEADTRACKING) + #if defined(HAS_HEADTRACKING)|| defined(SUPPORT_HEADTRACKING) ws.onEvent(onWsEvent); server.addHandler(&ws); #endif @@ -865,14 +881,13 @@ static void HandleWebUpdate() rebootTime = millis() + 200; } -#if defined(HAS_HEADTRACKING) +#if defined(HAS_HEADTRACKING)|| defined(SUPPORT_HEADTRACKING) static long lastCall = 0; static HeadTrackerState last_state = STATE_ERROR; if (now - lastCall > 50) { auto current_state = getHeadTrackerState(); - switch(current_state) + if (current_state == STATE_RUNNING) { - case STATE_RUNNING: if (last_state == STATE_IMU_CALIBRATING || last_state == STATE_COMPASS_CALIBRATING) { ws.textAll("{\"done\": true}"); @@ -890,13 +905,6 @@ static void HandleWebUpdate() ws.textAll(payload, strlen(payload)); } } - break; - - case STATE_COMPASS_CALIBRATING: - break; - - case STATE_IMU_CALIBRATING: - break; } lastCall = now; last_state = current_state; diff --git a/src/Vrx_main.cpp b/src/Vrx_main.cpp index c9c7172..e57bb8c 100644 --- a/src/Vrx_main.cpp +++ b/src/Vrx_main.cpp @@ -510,7 +510,6 @@ void loop() sendRTCChangesToVrx = false; vrxModule.SetRTC(); } - return; } if (BindingExpired(now)) diff --git a/src/module_base.cpp b/src/module_base.cpp index 012f265..489acb1 100644 --- a/src/module_base.cpp +++ b/src/module_base.cpp @@ -12,6 +12,7 @@ #include "crsf_protocol.h" #endif +int16_t ptrChannelData[3]; void RebootIntoWifi(wifi_service_t service = WIFI_SERVICE_UPDATE); bool BindingExpired(uint32_t now); extern uint8_t backpackVersion[]; @@ -153,6 +154,9 @@ MSPModuleBase::Loop(uint32_t now) } else if (packet->function == MSP_ELRS_BACKPACK_SET_PTR && headTrackingEnabled) { + ptrChannelData[0] = packet->payload[0] + (packet->payload[1] << 8); + ptrChannelData[1] = packet->payload[2] + (packet->payload[3] << 8); + ptrChannelData[2] = packet->payload[4] + (packet->payload[5] << 8); sendMSPViaEspnow(packet); } msp.markPacketReceived(); diff --git a/targets/debug.ini b/targets/debug.ini index 3f5e94d..3ac4bb6 100644 --- a/targets/debug.ini +++ b/targets/debug.ini @@ -130,17 +130,13 @@ board_upload.flash_size=4MB extends = env:DEBUG_S3_HT_Backpack_via_UART [env:DEBUG_32_HT_Backpack_via_UART] -extends = env_common_esp32, rapidfire_vrx_backpack_common +extends = env_common_esp32, hdzero_vrx_backpack_common build_flags = ${env_common_esp32.build_flags} - ${rapidfire_vrx_backpack_common.build_flags} - -D HAS_HEADTRACKING - -D PIN_SDA=22 - -D PIN_SCL=19 - -D PIN_INT=15 - -D PIN_MOSI=12 - -D PIN_CLK=13 - -D PIN_CS=14 + ${hdzero_vrx_backpack_common.build_flags} + -D SUPPORT_HEADTRACKING + -D DEBUG_LOG + -D PIN_BUTTON=0 monitor_filters = esp32_exception_decoder monitor_speed = 460800 diff --git a/targets/hdzero.ini b/targets/hdzero.ini index 83996a5..7797bfa 100644 --- a/targets/hdzero.ini +++ b/targets/hdzero.ini @@ -47,6 +47,7 @@ build_flags = ${hdzero_vrx_backpack_common.build_flags} -D PIN_LED=4 -D NO_AUTOBIND=1 + -D SUPPORT_HEADTRACKING [env:HDZero_Goggle_ESP32_Backpack_via_WIFI] extends = env:HDZero_Goggle_ESP32_Backpack_via_UART From 298aca470b167992ae1025a8af1572ef2cc7b41d Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Sun, 15 Sep 2024 20:19:08 +1200 Subject: [PATCH 33/38] Fixup the angles --- html/scan.js | 4 ++-- lib/HeadTracker/devHeadTracker.cpp | 10 ++++----- src/Vrx_main.cpp | 35 +++++++++++++++--------------- src/module_base.cpp | 16 +++++++------- 4 files changed, 33 insertions(+), 32 deletions(-) diff --git a/html/scan.js b/html/scan.js index 60c3bf7..75f7a86 100644 --- a/html/scan.js +++ b/html/scan.js @@ -640,9 +640,9 @@ function setup() { function draw() { background(192); - rotateY(radians(-Euler.heading+180)); + rotateY(radians(Euler.heading + 180)); // Add 180 degrees so the plane is facing away at zero rotateZ(radians(Euler.pitch)); - rotateX(radians(Euler.roll)); + rotateX(radians(-Euler.roll)); // Invert the about the pitch axis (i.e. roll is opposite) push(); stroke('#CCC'); diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index 29a99d9..aa7dea0 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -307,9 +307,9 @@ void resetCenter() void getEuler(float *yaw, float *pitch, float *roll) { - *yaw = -euler.angle.yaw; - *pitch = euler.angle.pitch; - *roll = -euler.angle.roll; + *yaw = normalize(euler.angle.yaw, -180.0, 180.0); + *pitch = normalize(euler.angle.pitch, -180.0, 180.0); + *roll = normalize(euler.angle.roll, -180.0, 180.0); } device_t HeadTracker_device = { @@ -341,8 +341,8 @@ void resetCenter() void getEuler(float *yaw, float *pitch, float *roll) { - *yaw = fmap(ptrChannelData[0], CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000, -180.0, 180.0); + *yaw = -fmap(ptrChannelData[0], CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000, -180.0, 180.0); *pitch = fmap(ptrChannelData[2], CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000, -180.0, 180.0); - *roll = -fmap(ptrChannelData[1], CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000, -180.0, 180.0); + *roll = fmap(ptrChannelData[1], CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000, -180.0, 180.0); } #endif \ No newline at end of file diff --git a/src/Vrx_main.cpp b/src/Vrx_main.cpp index e57bb8c..7fbd022 100644 --- a/src/Vrx_main.cpp +++ b/src/Vrx_main.cpp @@ -503,15 +503,6 @@ void loop() } #endif - if (connectionState == wifiUpdate) - { - if (sendRTCChangesToVrx) - { - sendRTCChangesToVrx = false; - vrxModule.SetRTC(); - } - } - if (BindingExpired(now)) { DBGLN("Binding expired"); @@ -533,14 +524,6 @@ void loop() vrxModule.SendHeadTrackingEnableCmd(headTrackingEnabled); } - // spam out a bunch of requests for the desired band/channel for the first 5s - if (!gotInitialPacket && now - VRX_BOOT_DELAY < 5000 && now - lastSentRequest > 1000 && connectionState != binding) - { - DBGLN("RequestVTXPacket..."); - RequestVTXPacket(); - lastSentRequest = now; - } - #if !defined(NO_AUTOBIND) // Power cycle must be done within 30s. Long timeout to allow goggles to boot and shutdown correctly e.g. Orqa. if (now > BINDING_TIMEOUT && config.GetBootCount() > 0) @@ -550,6 +533,24 @@ void loop() } #endif + if (connectionState == wifiUpdate) + { + if (sendRTCChangesToVrx) + { + sendRTCChangesToVrx = false; + vrxModule.SetRTC(); + } + return; + } + + // spam out a bunch of requests for the desired band/channel for the first 5s + if (!gotInitialPacket && now - VRX_BOOT_DELAY < 5000 && now - lastSentRequest > 1000 && connectionState != binding) + { + DBGLN("RequestVTXPacket..."); + RequestVTXPacket(); + lastSentRequest = now; + } + #if defined(PLATFORM_ESP32) if (uxQueueMessagesWaiting(rxqueue) > 0 && Serial.availableForWrite() == 128) { diff --git a/src/module_base.cpp b/src/module_base.cpp index 489acb1..702416e 100644 --- a/src/module_base.cpp +++ b/src/module_base.cpp @@ -64,29 +64,29 @@ ModuleBase::Loop(uint32_t now) { #if defined(HAS_HEADTRACKING) static uint32_t lastSend = 0; - if (headTrackingEnabled && now - lastSend > 20) + if (connectionState != wifiUpdate && headTrackingEnabled && now - lastSend > 20) { float fyaw, fpitch, froll; getEuler(&fyaw, &fpitch, &froll); // convert from degrees to servo positions - int pan = map((fyaw + 90)*1000, 0, 180*1000, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); - int tilt = map((fpitch + 90)*1000, 0, 180*1000, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); - int roll = map((froll + 90)*1000, 0, 180*1000, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); - + int pan = -map(fyaw*100, -180*100, 180*100, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); + int tilt = map(fpitch*100, -180*100, 180*100, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); + int roll = map(froll*100, -180*100, 180*100, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); mspPacket_t packet; packet.reset(); packet.makeCommand(); packet.function = MSP_ELRS_BACKPACK_SET_PTR; packet.addByte(pan & 0xFF); packet.addByte(pan >> 8); - packet.addByte(tilt & 0xFF); + packet.addByte(roll & 0xFF); // rotating about roll is pitch axis + packet.addByte(roll>> 8); + packet.addByte(tilt & 0xFF); // rotating about pitch is roll axis packet.addByte(tilt >> 8); - packet.addByte(roll & 0xFF); - packet.addByte(roll >> 8); sendMSPViaEspnow(&packet); lastSend = now; +Serial.printf("%6.2f, %d\r\n",fpitch, tilt); } #endif } From 16711f618fed9d96c354689667d4f03c49451f04 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Mon, 16 Sep 2024 08:25:18 +1200 Subject: [PATCH 34/38] Update fusion library --- lib/Fusion/FusionAhrs.c | 12 ++++++------ lib/Fusion/FusionOffset.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Fusion/FusionAhrs.c b/lib/Fusion/FusionAhrs.c index 0417b76..661e6e8 100644 --- a/lib/Fusion/FusionAhrs.c +++ b/lib/Fusion/FusionAhrs.c @@ -10,7 +10,7 @@ #include // FLT_MAX #include "FusionAhrs.h" -#include // atan2f, cosf, powf, sinf +#include // atan2f, cosf, fabsf, powf, sinf //------------------------------------------------------------------------------ // Definitions @@ -117,7 +117,7 @@ void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, cons ahrs->accelerometer = accelerometer; // Reinitialise if gyroscope range exceeded - if ((fabs(gyroscope.axis.x) > ahrs->settings.gyroscopeRange) || (fabs(gyroscope.axis.y) > ahrs->settings.gyroscopeRange) || (fabs(gyroscope.axis.z) > ahrs->settings.gyroscopeRange)) { + if ((fabsf(gyroscope.axis.x) > ahrs->settings.gyroscopeRange) || (fabsf(gyroscope.axis.y) > ahrs->settings.gyroscopeRange) || (fabsf(gyroscope.axis.z) > ahrs->settings.gyroscopeRange)) { const FusionQuaternion quaternion = ahrs->quaternion; FusionAhrsReset(ahrs); ahrs->quaternion = quaternion; @@ -125,7 +125,7 @@ void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, cons } // Ramp down gain during initialisation - if (ahrs->initialising == true) { + if (ahrs->initialising) { ahrs->rampedGain -= ahrs->rampedGainStep * deltaTime; if ((ahrs->rampedGain < ahrs->settings.gain) || (ahrs->settings.gain == 0.0f)) { ahrs->rampedGain = ahrs->settings.gain; @@ -146,7 +146,7 @@ void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, cons ahrs->halfAccelerometerFeedback = Feedback(FusionVectorNormalise(accelerometer), halfGravity); // Don't ignore accelerometer if acceleration error below threshold - if ((ahrs->initialising == true) || ((FusionVectorMagnitudeSquared(ahrs->halfAccelerometerFeedback) <= ahrs->settings.accelerationRejection))) { + if (ahrs->initialising || ((FusionVectorMagnitudeSquared(ahrs->halfAccelerometerFeedback) <= ahrs->settings.accelerationRejection))) { ahrs->accelerometerIgnored = false; ahrs->accelerationRecoveryTrigger -= 9; } else { @@ -180,7 +180,7 @@ void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, cons ahrs->halfMagnetometerFeedback = Feedback(FusionVectorNormalise(FusionVectorCrossProduct(halfGravity, magnetometer)), halfMagnetic); // Don't ignore magnetometer if magnetic error below threshold - if ((ahrs->initialising == true) || ((FusionVectorMagnitudeSquared(ahrs->halfMagnetometerFeedback) <= ahrs->settings.magneticRejection))) { + if (ahrs->initialising || ((FusionVectorMagnitudeSquared(ahrs->halfMagnetometerFeedback) <= ahrs->settings.magneticRejection))) { ahrs->magnetometerIgnored = false; ahrs->magneticRecoveryTrigger -= 9; } else { @@ -327,7 +327,7 @@ void FusionAhrsUpdateNoMagnetometer(FusionAhrs *const ahrs, const FusionVector g FusionAhrsUpdate(ahrs, gyroscope, accelerometer, FUSION_VECTOR_ZERO, deltaTime); // Zero heading during initialisation - if (ahrs->initialising == true) { + if (ahrs->initialising) { FusionAhrsSetHeading(ahrs, 0.0f); } } diff --git a/lib/Fusion/FusionOffset.c b/lib/Fusion/FusionOffset.c index b21794d..a037e1e 100644 --- a/lib/Fusion/FusionOffset.c +++ b/lib/Fusion/FusionOffset.c @@ -9,7 +9,7 @@ // Includes #include "FusionOffset.h" -#include // fabs +#include // fabsf //------------------------------------------------------------------------------ // Definitions @@ -57,7 +57,7 @@ FusionVector FusionOffsetUpdate(FusionOffset *const offset, FusionVector gyrosco gyroscope = FusionVectorSubtract(gyroscope, offset->gyroscopeOffset); // Reset timer if gyroscope not stationary - if ((fabs(gyroscope.axis.x) > THRESHOLD) || (fabs(gyroscope.axis.y) > THRESHOLD) || (fabs(gyroscope.axis.z) > THRESHOLD)) { + if ((fabsf(gyroscope.axis.x) > THRESHOLD) || (fabsf(gyroscope.axis.y) > THRESHOLD) || (fabsf(gyroscope.axis.z) > THRESHOLD)) { offset->timer = 0; return gyroscope; } From 792dacdfcefcda506dd3bed9a19bdfcb7e0d13f6 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Mon, 16 Sep 2024 09:20:15 +1200 Subject: [PATCH 35/38] Remove compass from head-tracker --- html/scan.js | 18 +------ html/vrx_index.html | 1 - lib/HeadTracker/devHeadTracker.cpp | 82 +----------------------------- lib/HeadTracker/devHeadTracker.h | 2 - lib/WIFI/devWIFI.cpp | 4 +- 5 files changed, 4 insertions(+), 103 deletions(-) diff --git a/html/scan.js b/html/scan.js index 75f7a86..31feb86 100644 --- a/html/scan.js +++ b/html/scan.js @@ -615,8 +615,8 @@ function start() { } if (d['heading']) { Euler = d; - _('angle-x').textContent = Euler.pitch; - _('angle-y').textContent = Euler.roll; + _('angle-x').textContent = Euler.roll; + _('angle-y').textContent = Euler.pitch; _('angle-z').textContent = Euler.heading; } }; @@ -665,7 +665,6 @@ function draw() { } if (_('set-center')) _('set-center').addEventListener('click', () => {websock.send('sc');}); -if (_('cal-compass')) _('cal-compass').addEventListener('click', calibrateCompass); if (_('cal-gyro')) _('cal-gyro').addEventListener('click', calibrateIMU); if (_('reset-board')) _('reset-board').addEventListener('click', (e) => { _('x-angle').value = 0; @@ -679,19 +678,6 @@ if (_('x-angle')) _('x-angle').addEventListener('input', setOrientation); if (_('y-angle')) _('y-angle').addEventListener('input', setOrientation); if (_('z-angle')) _('z-angle').addEventListener('input', setOrientation); -async function calibrateCompass() { - await cuteAlert({ - type: 'info', - title: "Calibrate Compass", - message: "Rotate the board in all directions for 10 seconds until the succeeded popup appears", - confirmText: "Calibrate", - cancelText: "Cancel" - }).then((e)=>{ - websock.send('cc'); - calibrationOn(); - }); -} - async function calibrateIMU() { await cuteAlert({ type: 'info', diff --git a/html/vrx_index.html b/html/vrx_index.html index 5d5bbf3..020821c 100644 --- a/html/vrx_index.html +++ b/html/vrx_index.html @@ -112,7 +112,6 @@

RTC Update via NTP

-
diff --git a/lib/HeadTracker/devHeadTracker.cpp b/lib/HeadTracker/devHeadTracker.cpp index aa7dea0..b1c69a1 100644 --- a/lib/HeadTracker/devHeadTracker.cpp +++ b/lib/HeadTracker/devHeadTracker.cpp @@ -10,7 +10,6 @@ #include "config.h" #include "logging.h" -#include "QMC5883LCompass.h" #include "IMUBase.h" #include "ICMSeries.h" #include "MPU6050.h" @@ -19,7 +18,6 @@ static HeadTrackerState ht_state = STATE_ERROR; static IMUBase *imu; -static QMC5883LCompass compass; static bool hasCompass = false; static FusionAhrs ahrs; @@ -35,11 +33,6 @@ static void initialize() Wire.setClock(400000); Wire.setTimeout(1000); - // Compass init first - compass.init(); - hasCompass = compass.readChipId() == 0xFF; - if (hasCompass) compass.setMode(0x01,0x08,0x10,0X00); // continuous, 100Hz, 8G, 512 over sample - // Initializing the IMU imu = new MPU6050(); if (imu->initialize()) { @@ -87,11 +80,6 @@ static int start() { return DURATION_NEVER; } - if (hasCompass) - { - int (*cal)[3][2] = config.GetCompassCalibration(); - compass.setCalibration((*cal)[0][0],(*cal)[0][1],(*cal)[1][0],(*cal)[1][1],(*cal)[2][0],(*cal)[2][1]); - } imu->setCalibration(config.GetIMUCalibration()); memcpy(orientation, *config.GetBoardOrientation(), sizeof(orientation)); @@ -159,24 +147,13 @@ static int timeout() rotate(a.array, orientation); rotate(g.array, orientation); - FusionVector m FUSION_VECTOR_ZERO; - if (hasCompass) - { - compass.read(); - - m.axis.x = compass.getX(); - m.axis.y = compass.getY(); - m.axis.z = compass.getZ(); - rotate(m.array, orientation); - } - // Calculate delta time (in seconds) to account for gyroscope sample clock error const clock_t timestamp = micros(); static clock_t previousTimestamp; const float deltaTime = (float) (timestamp - previousTimestamp) / (float) 1000000; previousTimestamp = timestamp; - FusionAhrsUpdate(&ahrs, g, a, m, deltaTime); + FusionAhrsUpdate(&ahrs, g, a, FUSION_VECTOR_ZERO, deltaTime); euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs)); euler.angle.roll -= rollHome; @@ -185,50 +162,6 @@ static int timeout() break; } - case STATE_COMPASS_CALIBRATING: { - if ((millis() - cal_started) < 10000) { - compass.read(); - - int x = compass.getX(); - int y = compass.getY(); - int z = compass.getZ(); - - if (x < calibrationData[0][0]) { - calibrationData[0][0] = x; - } - if (x > calibrationData[0][1]) { - calibrationData[0][1] = x; - } - - if (y < calibrationData[1][0]) { - calibrationData[1][0] = y; - } - if (y > calibrationData[1][1]) { - calibrationData[1][1] = y; - } - - if (z < calibrationData[2][0]) { - calibrationData[2][0] = z; - } - if (z > calibrationData[2][1]) { - calibrationData[2][1] = z; - } - } else { - compass.setCalibration( - calibrationData[0][0], - calibrationData[0][1], - calibrationData[1][0], - calibrationData[1][1], - calibrationData[2][0], - calibrationData[2][1] - ); - config.SetCompassCalibration(calibrationData); - config.Commit(); - ht_state = STATE_RUNNING; - } - break; - } - case STATE_IMU_CALIBRATING: { if (imu->updateCalibration(g)) { config.SetIMUCalibration(imu->getCalibration()); @@ -241,19 +174,6 @@ static int timeout() return DURATION_IMMEDIATELY; } -void startCompassCalibration() -{ - if (hasCompass) { - compass.clearCalibration(); - calibrationData[0][0] = calibrationData[0][1] = compass.getX(); - calibrationData[1][0] = calibrationData[1][1] = compass.getY(); - calibrationData[2][0] = calibrationData[2][1] = compass.getZ(); - - cal_started = millis(); - ht_state = STATE_COMPASS_CALIBRATING; - } -} - void startIMUCalibration() { imu->beginCalibration(); diff --git a/lib/HeadTracker/devHeadTracker.h b/lib/HeadTracker/devHeadTracker.h index 3a35345..00bcee6 100644 --- a/lib/HeadTracker/devHeadTracker.h +++ b/lib/HeadTracker/devHeadTracker.h @@ -7,11 +7,9 @@ extern device_t HeadTracker_device; typedef enum { STATE_ERROR, STATE_RUNNING, - STATE_COMPASS_CALIBRATING, STATE_IMU_CALIBRATING } HeadTrackerState; -void startCompassCalibration(); void startIMUCalibration(); void resetBoardOrientation(); void saveBoardOrientation(); diff --git a/lib/WIFI/devWIFI.cpp b/lib/WIFI/devWIFI.cpp index d92db5a..1947067 100644 --- a/lib/WIFI/devWIFI.cpp +++ b/lib/WIFI/devWIFI.cpp @@ -207,8 +207,6 @@ static void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, Aw int y = atoi(colon+1); int z = atoi(colon2+1); setBoardOrientation(x, y, z); - } else if (memcmp(data, "cc", 2) == 0) { - startCompassCalibration(); } else if (memcmp(data, "ci", 2) == 0) { startIMUCalibration(); } else if (memcmp(data, "ro", 2) == 0) { @@ -888,7 +886,7 @@ static void HandleWebUpdate() auto current_state = getHeadTrackerState(); if (current_state == STATE_RUNNING) { - if (last_state == STATE_IMU_CALIBRATING || last_state == STATE_COMPASS_CALIBRATING) + if (last_state == STATE_IMU_CALIBRATING) { ws.textAll("{\"done\": true}"); } From 50d73f533740c333c475cb1e7da701f153c8197b Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Wed, 18 Sep 2024 16:06:42 +1200 Subject: [PATCH 36/38] Update ESP01F based targets --- targets/orqa.ini | 2 +- targets/rapidfire.ini | 2 +- targets/rx5808.ini | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/targets/orqa.ini b/targets/orqa.ini index be7407c..e8434d8 100644 --- a/targets/orqa.ini +++ b/targets/orqa.ini @@ -36,7 +36,7 @@ build_flags = -D HAS_HEADTRACKING -D PIN_SDA=2 -D PIN_SCL=4 - -D PIN_INT=9 + -D PIN_INT=15 [env:Orqa_HT_Backpack_via_WIFI] extends = env:Orqa_HT_Backpack_via_UART diff --git a/targets/rapidfire.ini b/targets/rapidfire.ini index affafdc..3b122e7 100644 --- a/targets/rapidfire.ini +++ b/targets/rapidfire.ini @@ -60,7 +60,7 @@ build_flags = -D HAS_HEADTRACKING -D PIN_SDA=2 -D PIN_SCL=4 - -D PIN_INT=9 + -D PIN_INT=12 [env:Rapidfire_HT_Backpack_via_WIFI] extends = env:Rapidfire_HT_Backpack_via_UART diff --git a/targets/rx5808.ini b/targets/rx5808.ini index b04d318..3c16db9 100644 --- a/targets/rx5808.ini +++ b/targets/rx5808.ini @@ -68,7 +68,7 @@ build_flags = -D HAS_HEADTRACKING -D PIN_SDA=2 -D PIN_SCL=4 - -D PIN_INT=9 + -D PIN_INT=12 [env:RX5808_HT_Backpack_via_WIFI] extends = env:RX5808_HT_Backpack_via_UART From 3e54fc36624027b8d18859cdaad934daaad1407f Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Thu, 14 Nov 2024 07:07:06 +1300 Subject: [PATCH 37/38] Change interrupt pin for test HT backpack --- targets/rapidfire.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/rapidfire.ini b/targets/rapidfire.ini index 3b122e7..affafdc 100644 --- a/targets/rapidfire.ini +++ b/targets/rapidfire.ini @@ -60,7 +60,7 @@ build_flags = -D HAS_HEADTRACKING -D PIN_SDA=2 -D PIN_SCL=4 - -D PIN_INT=12 + -D PIN_INT=9 [env:Rapidfire_HT_Backpack_via_WIFI] extends = env:Rapidfire_HT_Backpack_via_UART From 476a6ade1a105a8d1bb367222777a74583dfdd1e Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Fri, 20 Dec 2024 11:35:38 +1300 Subject: [PATCH 38/38] Fix PAN and remove some debugging --- lib/HeadTracker/IMUBase.cpp | 2 ++ src/module_base.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/HeadTracker/IMUBase.cpp b/lib/HeadTracker/IMUBase.cpp index c47bdb0..6c6280d 100644 --- a/lib/HeadTracker/IMUBase.cpp +++ b/lib/HeadTracker/IMUBase.cpp @@ -127,8 +127,10 @@ bool IMUBase::updateCalibration(FusionVector &g) c = 0; eSample = 0; L++; +#ifdef DEBUG_LOG DBGLN("Finished loop iteration %d", L); Serial.printf("%6.2f %6.2f %6.2f\r\n", imuCalibration[0], imuCalibration[1], imuCalibration[2]); +#endif } return L == Loops; } diff --git a/src/module_base.cpp b/src/module_base.cpp index 702416e..989eb4e 100644 --- a/src/module_base.cpp +++ b/src/module_base.cpp @@ -71,7 +71,7 @@ ModuleBase::Loop(uint32_t now) // convert from degrees to servo positions - int pan = -map(fyaw*100, -180*100, 180*100, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); + int pan = map(-fyaw*100, -180*100, 180*100, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); int tilt = map(fpitch*100, -180*100, 180*100, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); int roll = map(froll*100, -180*100, 180*100, CRSF_CHANNEL_VALUE_1000, CRSF_CHANNEL_VALUE_2000); mspPacket_t packet; @@ -86,7 +86,7 @@ ModuleBase::Loop(uint32_t now) packet.addByte(tilt >> 8); sendMSPViaEspnow(&packet); lastSend = now; -Serial.printf("%6.2f, %d\r\n",fpitch, tilt); +// Serial.printf("%d, %d, %d\r\n",pan, tilt, roll); } #endif }